GraphSidebar.test.tsx3.8 KBView on GitHub /**
* The canvas's left rail.
*
* Mounted directly rather than through the canvas: what is being asserted is which glyphs the
* rail offers for a given set of callbacks and what each one is CALLED, and a React Flow
* harness would add a provider and a graph read around that.
*
* The recurring assertion is `getByRole('button', { name })` — an icon-only control whose
* accessible name is missing is invisible to a screen reader and unnameable by a test, which
* is the same failure wearing two hats.
*/
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import type { GraphSchema } from '@zero/server/graph';
import { GraphSidebar } from '@/modules/graph/GraphSidebar';
const SCHEMA: GraphSchema = {
version: 1,
visibility: 'team',
nodeSchema: {
fields: [
{ key=[redacted], label: 'Stance', type: 'select', options: ['supporter', 'skeptic'] },
],
},
edgeKinds: [{ key=[redacted], label: 'Reports to' }],
view: { colorField: 'stance' },
};
describe('GraphSidebar', () => {
it('offers the two actions as named, icon-only buttons', async () => {
const onAddNode = jest.fn();
const onAutoFormat = jest.fn();
render(<GraphSidebar schema={SCHEMA} onAddNode={onAddNode} onAutoFormat={onAutoFormat} />);
const add = screen.getByRole('button', { name: 'Add node' });
const format = screen.getByRole('button', { name: 'Auto-format' });
// Icon-only: the label is the accessible name and the tooltip, never text on the canvas.
expect(add).toHaveTextContent('');
expect(format).toHaveTextContent('');
await userEvent.click(add);
expect(onAddNode).toHaveBeenCalledTimes(1);
await userEvent.click(format);
expect(onAutoFormat).toHaveBeenCalledTimes(1);
});
it('is one vertical toolbar, not three loose buttons', () => {
render(<GraphSidebar schema={SCHEMA} onAddNode={jest.fn()} />);
const rail = screen.getByRole('toolbar', { name: 'Graph actions' });
expect(rail).toHaveAttribute('aria-orientation', 'vertical');
});
it('drops an action it was given no handler for', () => {
render(<GraphSidebar schema={SCHEMA} onAddNode={jest.fn()} />);
expect(screen.queryByRole('button', { name: 'Auto-format' })).toBeNull();
expect(screen.queryByRole('button', { name: 'Schema' })).toBeNull();
});
/**
* The read-only case. A raised strip on the canvas that holds nothing is a control that does
* nothing, which is worse than no control — so the rail removes itself entirely.
*/
it('renders nothing at all when it can offer nothing', () => {
const { container } = render(<GraphSidebar schema={SCHEMA} />);
expect(container).toBeEmptyDOMElement();
});
/**
* The two schema writes travel together. Half an editor — one that can repoint the colour
* field but not tick a field onto the node face — is a form with a dead half, and nothing on
* screen would say which half.
*/
it('hides Schema unless it can perform both schema writes', () => {
const { rerender } = render(<GraphSidebar schema={SCHEMA} onSetView={jest.fn()} />);
expect(screen.queryByRole('button', { name: 'Schema' })).toBeNull();
rerender(<GraphSidebar schema={SCHEMA} onSetFields={jest.fn()} />);
expect(screen.queryByRole('button', { name: 'Schema' })).toBeNull();
rerender(<GraphSidebar schema={SCHEMA} onSetView={jest.fn()} onSetFields={jest.fn()} />);
expect(screen.getByRole('button', { name: 'Schema' })).toBeInTheDocument();
});
it('opens the schema editor from the Schema glyph', async () => {
render(<GraphSidebar schema={SCHEMA} onSetView={jest.fn()} onSetFields={jest.fn()} />);
await userEvent.click(screen.getByRole('button', { name: 'Schema' }));
expect(await screen.findByText('Background')).toBeInTheDocument();
expect(screen.getByText('On the node face')).toBeInTheDocument();
});
});