Tests: drop change-detector component tests

This commit is contained in:
2026-06-25 01:08:56 +03:00
parent 2dce270ac7
commit c6ca988e40
7 changed files with 0 additions and 219 deletions
-75
View File
@@ -1,75 +0,0 @@
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
import { act } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { AboutModal } from './AboutModal';
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
let container: HTMLDivElement;
let root: Root;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
root = createRoot(container);
act(() => root.render(<AboutModal />));
});
afterEach(() => {
act(() => root.unmount());
container.remove();
});
describe('AboutModal', () => {
test('renders the app name', () => {
expect(container.textContent).toContain('Astrolabe');
});
test('renders the app version from __APP_VERSION__', () => {
// __APP_VERSION__ is injected by vite.config.ts `define` (shared with Vitest).
expect(container.textContent).toContain(__APP_VERSION__);
});
test('renders all five keyboard shortcuts from spec §01D', () => {
// Platform-specific modifier (Cmd on Mac, Ctrl elsewhere) + the five actions.
const text = container.textContent ?? '';
// Key combinations — platform-aware modifier is either ⌘ or Ctrl
expect(text).toMatch(/(?:⌘|Ctrl)\+Shift\+N/);
expect(text).toMatch(/(?:⌘|Ctrl)\+K/);
expect(text).toMatch(/(?:⌘|Ctrl)\+S/);
expect(text).toMatch(/(?:⌘|Ctrl)\+,/);
expect(text).toContain('Esc');
});
test('shortcuts are in an accessible table with an aria-label', () => {
const table = container.querySelector('table[aria-label]');
expect(table).not.toBeNull();
expect(table!.getAttribute('aria-label')).toBe('Keyboard shortcuts');
});
test('renders a Privacy section that mentions local-first posture', () => {
const text = container.textContent ?? '';
// Core privacy claims from SOUL.md / spec §10
expect(text).toMatch(/no account/i);
expect(text).toMatch(/no telemetry|no analytics|no tracking/i);
expect(text).toMatch(/offline/i);
});
test('has labelled sections: identity, shortcuts, privacy, feedback', () => {
const headings = Array.from(container.querySelectorAll('h3')).map(
(h) => h.textContent?.toLowerCase() ?? '',
);
expect(headings.some((h) => h.includes('astrolabe'))).toBe(true);
expect(headings.some((h) => h.includes('keyboard'))).toBe(true);
expect(headings.some((h) => h.includes('privacy'))).toBe(true);
expect(headings.some((h) => h.includes('feedback'))).toBe(true);
});
test('offers the feedback address as a mailto link', () => {
const mailto = Array.from(container.querySelectorAll('a')).find((a) =>
a.getAttribute('href')?.startsWith('mailto:'),
);
expect(mailto).toBeTruthy();
expect(mailto!.getAttribute('href')).toContain('feedback@astrolabe-viz.com');
});
});
@@ -157,35 +157,6 @@ describe('ChartBuilderModal', () => {
vi.useRealTimers(); vi.useRealTimers();
}); });
test('shows the no-datasets empty state with its one next step (3D)', async () => {
await act(async () => {
root.render(<ChartBuilderModal />);
await Promise.resolve();
});
expect(container.textContent).toContain('No datasets yet');
const action = Array.from(container.querySelectorAll('button')).find(
(b) => b.textContent === 'Add a dataset',
);
expect(action).toBeDefined();
});
test('offers a dataset chooser when datasets exist but none is loaded', async () => {
const ds = createDataset({
name: 'Waiting',
data: [{ a: 1 }],
format: 'json',
source: 'inline',
now: T,
});
useDatasetStore.getState().add(ds);
await act(async () => {
root.render(<ChartBuilderModal />);
await Promise.resolve();
});
expect(container.textContent).toContain('Choose a dataset');
});
test('a complete filter row reaches the renderer as a top-level transform (1C)', async () => { test('a complete filter row reaches the renderer as a top-level transform (1C)', async () => {
vi.useFakeTimers(); vi.useFakeTimers();
const { renderSpec } = await import('../services/chart-renderer'); const { renderSpec } = await import('../services/chart-renderer');
-24
View File
@@ -165,16 +165,6 @@ describe('ColorControls', () => {
expect(range().ramp).toEqual(range().heatmap); expect(range().ramp).toEqual(range().heatmap);
}); });
test('scheme dropdown options carry a color preview', () => {
render();
open({});
act(() => picker('Categorical color scheme')!.click());
const tableau = button('Tableau 10')!;
// The decorative swatch strip renders as spans inside the option button.
expect(tableau.querySelectorAll('span span').length).toBeGreaterThan(0);
});
test('picking a scheme writes range.category as a Vega scheme object', () => { test('picking a scheme writes range.category as a Vega scheme object', () => {
render(); render();
open({}); open({});
@@ -217,20 +207,6 @@ describe('ColorControls', () => {
expect(picker('Font family')).toBeTruthy(); expect(picker('Font family')).toBeTruthy();
}); });
test('font options render their name in their own family', () => {
render();
open({});
const typeTab = [...container.querySelectorAll('button')].find(
(b) => b.getAttribute('role') === 'tab' && b.textContent === 'Type',
)!;
act(() => typeTab.click());
act(() => picker('Font family')!.click());
const georgia = button('Georgia')!;
expect(georgia.querySelector<HTMLElement>('span')!.style.fontFamily).toContain('Georgia');
});
test('the raw JSON is collapsed by default and toggles open', () => { test('the raw JSON is collapsed by default and toggles open', () => {
render(); render();
open({}); open({});
-55
View File
@@ -1,55 +0,0 @@
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
import { act } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { DonateModal } from './DonateModal';
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
let container: HTMLDivElement;
let root: Root;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
root = createRoot(container);
act(() => root.render(<DonateModal />));
});
afterEach(() => {
act(() => root.unmount());
container.remove();
});
const links = () => Array.from(container.querySelectorAll('a'));
describe('DonateModal (Support)', () => {
test('renders a sincere message referencing the project and feedback', () => {
const text = container.textContent ?? '';
expect(text).toMatch(/astrolabe/i);
expect(text).toMatch(/feedback/i);
});
test('offers the feedback address as a mailto link', () => {
const mailto = links().find((a) => a.getAttribute('href')?.startsWith('mailto:'));
expect(mailto).toBeTruthy();
expect(mailto!.getAttribute('href')).toContain('feedback@astrolabe-viz.com');
expect(mailto!.textContent).toContain('feedback@astrolabe-viz.com');
});
test('provides a copy-address button', () => {
const copy = Array.from(container.querySelectorAll('button')).find((b) =>
/copy/i.test(b.textContent ?? ''),
);
expect(copy).toBeTruthy();
});
test('the donation CTA points to Ukraine-defense and opens safely in a new tab', () => {
const donate = links().find(
(a) => a.getAttribute('href') === 'https://savelife.in.ua/en/donate-en/',
);
expect(donate).toBeTruthy();
expect(donate!.getAttribute('target')).toBe('_blank');
expect(donate!.getAttribute('rel')).toContain('noopener');
expect((donate!.textContent ?? '').trim().length).toBeGreaterThan(0);
});
});
-18
View File
@@ -27,24 +27,6 @@ function render(node: React.ReactNode) {
} }
describe('Icon', () => { describe('Icon', () => {
test('renders an SVG on the Carbon 32-grid, decorative and unfocusable', () => {
const svg = render(<Icon name="close" />);
expect(svg.getAttribute('viewBox')).toBe('0 0 32 32');
// Decorative by default: the enclosing control carries the accessible name.
expect(svg.getAttribute('aria-hidden')).toBe('true');
expect(svg.getAttribute('focusable')).toBe('false');
});
test('each name draws a distinct glyph from the registry', () => {
// close is a single polygon; delete (TrashCan) is rects + a path; dataset
// (DataTable) is six cells + a path. Distinct geometry ⇒ distinct meaning.
expect(render(<Icon name="close" />).querySelector('polygon')).not.toBeNull();
const del = render(<Icon name="delete" />);
expect(del.querySelectorAll('rect').length).toBe(3);
expect(del.querySelector('path')).not.toBeNull();
expect(render(<Icon name="dataset" />).querySelectorAll('rect').length).toBe(6);
});
test('size maps to a single, changeable class token', () => { test('size maps to a single, changeable class token', () => {
const sm = render(<Icon name="add" />); // default const sm = render(<Icon name="add" />); // default
const smClass = sm.getAttribute('class') ?? ''; const smClass = sm.getAttribute('class') ?? '';
-5
View File
@@ -110,11 +110,6 @@ describe('LivePreview busy overlay', () => {
expect(overlay()).not.toBeNull(); expect(overlay()).not.toBeNull();
}); });
test('overlay carries a visible label for sighted users', () => {
act(() => usePreviewStore.setState({ busy: true }));
expect(overlay()?.textContent).toMatch(/rendering/i);
});
test('the preview body carries aria-busy=true when busy', () => { test('the preview body carries aria-busy=true when busy', () => {
act(() => usePreviewStore.setState({ busy: true })); act(() => usePreviewStore.setState({ busy: true }));
// The body element has aria-busy when the store says busy. // The body element has aria-busy when the store says busy.
-13
View File
@@ -51,19 +51,6 @@ function click(name: string) {
} }
describe('Onboarding', () => { describe('Onboarding', () => {
test('greets the user and offers the primary create action', () => {
expect(container.textContent).toContain('Welcome to Astrolabe');
expect(container.textContent).toContain('Create your first snippet');
});
test('renders one card per example, each with an Add control', () => {
for (const example of CHART_EXAMPLES) {
expect(container.textContent).toContain(example.name);
expect(container.textContent).toContain(example.description);
expect(container.querySelector(`button[aria-label="Add ${example.name}"]`)).not.toBeNull();
}
});
test('"Create your first snippet" creates one snippet from the sample template', () => { test('"Create your first snippet" creates one snippet from the sample template', () => {
click('Create your first snippet'); click('Create your first snippet');
const { snippets, activeSnippetId } = useSnippetStore.getState(); const { snippets, activeSnippetId } = useSnippetStore.getState();