test(refactor): Provide SVG selection in jsdomIt function

This commit is contained in:
quilicicf
2025-06-22 13:45:24 +02:00
parent 4145879003
commit 28840ebd84
2 changed files with 59 additions and 66 deletions

View File

@@ -1,27 +1,23 @@
import { addSVGa11yTitleDescription, setA11yDiagramInfo } from './accessibility.js'; import { addSVGa11yTitleDescription, setA11yDiagramInfo } from './accessibility.js';
import { ensureNodeFromSelector, jsdomIt } from './tests/util.js'; import { ensureNodeFromSelector, jsdomIt } from './tests/util.js';
import { select } from 'd3';
import { expect } from 'vitest'; import { expect } from 'vitest';
describe('accessibility', () => { describe('accessibility', () => {
describe('setA11yDiagramInfo', () => { describe('setA11yDiagramInfo', () => {
jsdomIt('should set svg element role to "graphics-document document"', () => { jsdomIt('should set svg element role to "graphics-document document"', ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); setA11yDiagramInfo(svg, 'flowchart');
setA11yDiagramInfo(svgSelection, 'flowchart');
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
expect(svgNode.getAttribute('role')).toBe('graphics-document document'); expect(svgNode.getAttribute('role')).toBe('graphics-document document');
}); });
jsdomIt('should set aria-roledescription to the diagram type', () => { jsdomIt('should set aria-roledescription to the diagram type', ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); setA11yDiagramInfo(svg, 'flowchart');
setA11yDiagramInfo(svgSelection, 'flowchart');
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
expect(svgNode.getAttribute('aria-roledescription')).toBe('flowchart'); expect(svgNode.getAttribute('aria-roledescription')).toBe('flowchart');
}); });
jsdomIt('should not set aria-roledescription if the diagram type is empty', () => { jsdomIt('should not set aria-roledescription if the diagram type is empty', ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); setA11yDiagramInfo(svg, '');
setA11yDiagramInfo(svgSelection, '');
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
expect(svgNode.getAttribute('aria-roledescription')).toBeNull(); expect(svgNode.getAttribute('aria-roledescription')).toBeNull();
}); });
@@ -46,25 +42,25 @@ describe('accessibility', () => {
describe('with a11y description', () => { describe('with a11y description', () => {
const a11yDesc = 'a11y description'; const a11yDesc = 'a11y description';
jsdomIt('should set aria-labelledby to the title id inserted as a child', () => { jsdomIt('should set aria-labelledby to the title id inserted as a child', ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
expect(svgNode.getAttribute('aria-labelledby')).toBe(`chart-title-${givenId}`); expect(svgNode.getAttribute('aria-labelledby')).toBe(`chart-title-${givenId}`);
}); });
jsdomIt('should set aria-describedby to the description id inserted as a child', () => { jsdomIt(
const svgSelection = select<SVGSVGElement, never>('svg'); 'should set aria-describedby to the description id inserted as a child',
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId); ({ svg }) => {
addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
expect(svgNode.getAttribute('aria-describedby')).toBe(`chart-desc-${givenId}`); expect(svgNode.getAttribute('aria-describedby')).toBe(`chart-desc-${givenId}`);
}); }
);
jsdomIt( jsdomIt(
'should insert title tag as the first child with the text set to the accTitle given', 'should insert title tag as the first child with the text set to the accTitle given',
() => { ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
const titleNode = ensureNodeFromSelector('title', svgNode); const titleNode = ensureNodeFromSelector('title', svgNode);
expect(titleNode?.innerHTML).toBe(a11yTitle); expect(titleNode?.innerHTML).toBe(a11yTitle);
@@ -73,9 +69,8 @@ describe('accessibility', () => {
jsdomIt( jsdomIt(
'should insert desc tag as the 2nd child with the text set to accDescription given', 'should insert desc tag as the 2nd child with the text set to accDescription given',
() => { ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
const descNode = ensureNodeFromSelector('desc', svgNode); const descNode = ensureNodeFromSelector('desc', svgNode);
expect(descNode?.innerHTML).toBe(a11yDesc); expect(descNode?.innerHTML).toBe(a11yDesc);
@@ -86,34 +81,30 @@ describe('accessibility', () => {
describe(`without a11y description`, {}, () => { describe(`without a11y description`, {}, () => {
const a11yDesc = undefined; const a11yDesc = undefined;
jsdomIt('should set aria-labelledby to the title id inserted as a child', () => { jsdomIt('should set aria-labelledby to the title id inserted as a child', ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
expect(svgNode.getAttribute('aria-labelledby')).toBe(`chart-title-${givenId}`); expect(svgNode.getAttribute('aria-labelledby')).toBe(`chart-title-${givenId}`);
}); });
jsdomIt('should not set aria-describedby', () => { jsdomIt('should not set aria-describedby', ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
expect(svgNode.getAttribute('aria-describedby')).toBeNull(); expect(svgNode.getAttribute('aria-describedby')).toBeNull();
}); });
jsdomIt( jsdomIt(
'should insert title tag as the first child with the text set to the accTitle given', 'should insert title tag as the first child with the text set to the accTitle given',
() => { ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
const titleNode = ensureNodeFromSelector('title', svgNode); const titleNode = ensureNodeFromSelector('title', svgNode);
expect(titleNode?.innerHTML).toBe(a11yTitle); expect(titleNode?.innerHTML).toBe(a11yTitle);
} }
); );
jsdomIt('should not insert description tag', () => { jsdomIt('should not insert description tag', ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
const descNode = svgNode.querySelector('desc'); const descNode = svgNode.querySelector('desc');
expect(descNode).toBeNull(); expect(descNode).toBeNull();
@@ -127,33 +118,32 @@ describe('accessibility', () => {
describe('with a11y description', () => { describe('with a11y description', () => {
const a11yDesc = 'a11y description'; const a11yDesc = 'a11y description';
jsdomIt('should not set aria-labelledby', () => { jsdomIt('should not set aria-labelledby', ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
expect(svgNode.getAttribute('aria-labelledby')).toBeNull(); expect(svgNode.getAttribute('aria-labelledby')).toBeNull();
}); });
jsdomIt('should not insert title tag', () => { jsdomIt('should not insert title tag', ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
const titleNode = svgNode.querySelector('title'); const titleNode = svgNode.querySelector('title');
expect(titleNode).toBeNull(); expect(titleNode).toBeNull();
}); });
jsdomIt('should set aria-describedby to the description id inserted as a child', () => { jsdomIt(
const svgSelection = select<SVGSVGElement, never>('svg'); 'should set aria-describedby to the description id inserted as a child',
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId); ({ svg }) => {
addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
expect(svgNode.getAttribute('aria-describedby')).toBe(`chart-desc-${givenId}`); expect(svgNode.getAttribute('aria-describedby')).toBe(`chart-desc-${givenId}`);
}); }
);
jsdomIt( jsdomIt(
'should insert desc tag as the 2nd child with the text set to accDescription given', 'should insert desc tag as the 2nd child with the text set to accDescription given',
() => { ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
const descNode = ensureNodeFromSelector('desc', svgNode); const descNode = ensureNodeFromSelector('desc', svgNode);
expect(descNode?.innerHTML).toBe(a11yDesc); expect(descNode?.innerHTML).toBe(a11yDesc);
@@ -164,31 +154,27 @@ describe('accessibility', () => {
describe('without a11y description', () => { describe('without a11y description', () => {
const a11yDesc = undefined; const a11yDesc = undefined;
jsdomIt('should not set aria-labelledby', () => { jsdomIt('should not set aria-labelledby', ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
expect(svgNode.getAttribute('aria-labelledby')).toBeNull(); expect(svgNode.getAttribute('aria-labelledby')).toBeNull();
}); });
jsdomIt('should not set aria-describedby', () => { jsdomIt('should not set aria-describedby', ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
expect(svgNode.getAttribute('aria-describedby')).toBeNull(); expect(svgNode.getAttribute('aria-describedby')).toBeNull();
}); });
jsdomIt('should not insert title tag', () => { jsdomIt('should not insert title tag', ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
const titleNode = svgNode.querySelector('title'); const titleNode = svgNode.querySelector('title');
expect(titleNode).toBeNull(); expect(titleNode).toBeNull();
}); });
jsdomIt('should not insert description tag', () => { jsdomIt('should not insert description tag', ({ svg }) => {
const svgSelection = select<SVGSVGElement, never>('svg'); addSVGa11yTitleDescription(svg, a11yTitle, a11yDesc, givenId);
addSVGa11yTitleDescription(svgSelection, a11yTitle, a11yDesc, givenId);
const svgNode = ensureNodeFromSelector('svg'); const svgNode = ensureNodeFromSelector('svg');
const descNode = svgNode.querySelector('desc'); const descNode = svgNode.querySelector('desc');
expect(descNode).toBeNull(); expect(descNode).toBeNull();

View File

@@ -28,6 +28,7 @@ ${'2w'} | ${dayjs.duration(2, 'w')}
import { JSDOM } from 'jsdom'; import { JSDOM } from 'jsdom';
import { expect, it } from 'vitest'; import { expect, it } from 'vitest';
import { select, type Selection } from 'd3';
export const convert = (template: TemplateStringsArray, ...params: unknown[]) => { export const convert = (template: TemplateStringsArray, ...params: unknown[]) => {
const header = template[0] const header = template[0]
@@ -61,6 +62,11 @@ export const MOCKED_BBOX = {
height: 666, height: 666,
}; };
interface JsdomItInput {
// eslint-disable-next-line
svg: Selection<SVGSVGElement, never, HTMLElement, any>; // The `any` here comes from D3'as API.
}
/** /**
* Test method borrowed from d3 : https://github.com/d3/d3-selection/blob/v3.0.0/test/jsdom.js * Test method borrowed from d3 : https://github.com/d3/d3-selection/blob/v3.0.0/test/jsdom.js
* *
@@ -74,7 +80,7 @@ export const MOCKED_BBOX = {
* *
* This makes it possible to make structural tests instead of mocking everything. * This makes it possible to make structural tests instead of mocking everything.
*/ */
export function jsdomIt(message: string, run: () => void | Promise<void>) { export function jsdomIt(message: string, run: (input: JsdomItInput) => void | Promise<void>) {
return it(message, async (): Promise<void> => { return it(message, async (): Promise<void> => {
const oldWindow = global.window; const oldWindow = global.window;
const oldDocument = global.document; const oldDocument = global.document;
@@ -99,7 +105,8 @@ export function jsdomIt(message: string, run: () => void | Promise<void>) {
setOnProtectedConstant(global, 'document', dom.window.document); // Fool D3 into thinking it's in a browser setOnProtectedConstant(global, 'document', dom.window.document); // Fool D3 into thinking it's in a browser
setOnProtectedConstant(global, 'MutationObserver', undefined); // JSDOM doesn't like cytoscape elements setOnProtectedConstant(global, 'MutationObserver', undefined); // JSDOM doesn't like cytoscape elements
await run(); const svgSelection = select<SVGSVGElement, never>('svg');
await run({ svg: svgSelection });
} finally { } finally {
setOnProtectedConstant(global, 'window', oldWindow); setOnProtectedConstant(global, 'window', oldWindow);
setOnProtectedConstant(global, 'document', oldDocument); setOnProtectedConstant(global, 'document', oldDocument);