mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-24 02:59:58 +08:00
feat: add a11y test for components demo (#51414)
* test: add accessibility test utilities * refactor(tests): replace polling with Promise chain for axe test queue management * test: add a11y test for components demo * feat: add a11y test utils * test: add a11y test for components demo * test: add a11y test for components demo * chore: remove unnecessary code * feat: add polyfill for test environment * test: add a11y test for components demo * test: add a11y test for components demo * chore: adjust code style
This commit is contained in:
parent
a796037a46
commit
08a9325182
@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
import Affix from '..';
|
import Affix from '..';
|
||||||
import accessibilityTest from '../../../tests/shared/accessibilityTest';
|
import { accessibilityTest } from '../../../tests/shared/accessibilityTest';
|
||||||
import rtlTest from '../../../tests/shared/rtlTest';
|
import rtlTest from '../../../tests/shared/rtlTest';
|
||||||
import { render, triggerResize, waitFakeTimer } from '../../../tests/utils';
|
import { render, triggerResize, waitFakeTimer } from '../../../tests/utils';
|
||||||
import Button from '../../button';
|
import Button from '../../button';
|
||||||
|
5
components/affix/__tests__/a11y.test.ts
Normal file
5
components/affix/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
describe('affix demo a11y', () => {
|
||||||
|
accessibilityDemoTest('affix');
|
||||||
|
});
|
5
components/alert/__tests__/a11y.test.ts
Normal file
5
components/alert/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
describe('alert demo a11y', () => {
|
||||||
|
accessibilityDemoTest('alert', { disabledRules: ['button-name'] });
|
||||||
|
});
|
@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event';
|
|||||||
import { resetWarned } from 'rc-util/lib/warning';
|
import { resetWarned } from 'rc-util/lib/warning';
|
||||||
|
|
||||||
import Alert from '..';
|
import Alert from '..';
|
||||||
import accessibilityTest from '../../../tests/shared/accessibilityTest';
|
import { accessibilityTest } from '../../../tests/shared/accessibilityTest';
|
||||||
import rtlTest from '../../../tests/shared/rtlTest';
|
import rtlTest from '../../../tests/shared/rtlTest';
|
||||||
import { act, render, screen, waitFakeTimer } from '../../../tests/utils';
|
import { act, render, screen, waitFakeTimer } from '../../../tests/utils';
|
||||||
import Button from '../../button';
|
import Button from '../../button';
|
||||||
|
5
components/anchor/__tests__/a11y.test.ts
Normal file
5
components/anchor/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
describe('anchor demo a11y', () => {
|
||||||
|
accessibilityDemoTest('anchor');
|
||||||
|
});
|
5
components/app/__tests__/a11y.test.ts
Normal file
5
components/app/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
describe('app demo a11y', () => {
|
||||||
|
accessibilityDemoTest('app');
|
||||||
|
});
|
5
components/avatar/__tests__/a11y.test.ts
Normal file
5
components/avatar/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
describe('avatar demo a11y', () => {
|
||||||
|
accessibilityDemoTest('avatar', { disabledRules: ['image-alt'] });
|
||||||
|
});
|
5
components/back-top/__tests__/a11y.test.ts
Normal file
5
components/back-top/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
describe('back-top demo a11y', () => {
|
||||||
|
accessibilityDemoTest('back-top');
|
||||||
|
});
|
5
components/badge/__tests__/a11y.test.ts
Normal file
5
components/badge/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
describe('badge demo a11y', () => {
|
||||||
|
accessibilityDemoTest('badge', { disabledRules: ['button-name'] });
|
||||||
|
});
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { resetWarned } from '../../_util/warning';
|
import { resetWarned } from '../../_util/warning';
|
||||||
import accessibilityTest from '../../../tests/shared/accessibilityTest';
|
import { accessibilityTest } from '../../../tests/shared/accessibilityTest';
|
||||||
import mountTest from '../../../tests/shared/mountTest';
|
import mountTest from '../../../tests/shared/mountTest';
|
||||||
import rtlTest from '../../../tests/shared/rtlTest';
|
import rtlTest from '../../../tests/shared/rtlTest';
|
||||||
import { render } from '../../../tests/utils';
|
import { render } from '../../../tests/utils';
|
||||||
|
5
components/breadcrumb/__tests__/a11y.test.ts
Normal file
5
components/breadcrumb/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
describe('breadcrumb demo a11y', () => {
|
||||||
|
accessibilityDemoTest('breadcrumb');
|
||||||
|
});
|
5
components/button/__tests__/a11y.test.ts
Normal file
5
components/button/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
describe('button demo a11y', () => {
|
||||||
|
accessibilityDemoTest('button');
|
||||||
|
});
|
4
components/color-picker/__tests__/a11y.test.ts
Normal file
4
components/color-picker/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
// skip _InternalPanelDoNotUseOrYouWillBeFired
|
||||||
|
accessibilityDemoTest('color-picker', { skip: ['pure-panel.tsx'] });
|
3
components/descriptions/__tests__/a11y.test.ts
Normal file
3
components/descriptions/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('descriptions');
|
3
components/divider/__tests__/a11y.test.ts
Normal file
3
components/divider/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('divider');
|
3
components/drawer/__tests__/a11y.test.ts
Normal file
3
components/drawer/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('drawer', { disabledRules: ['image-alt'] });
|
3
components/dropdown/__tests__/a11y.test.ts
Normal file
3
components/dropdown/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('dropdown');
|
3
components/empty/__tests__/a11y.test.ts
Normal file
3
components/empty/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('empty', { disabledRules: ['label'] });
|
3
components/float-button/__tests__/a11y.test.ts
Normal file
3
components/float-button/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('float-button', { disabledRules: ['button-name'] });
|
3
components/icon/__tests__/a11y.test.ts
Normal file
3
components/icon/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('icon', { disabledRules: ['role-img-alt'] });
|
3
components/image/__tests__/a11y.test.ts
Normal file
3
components/image/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('image', { disabledRules: ['image-alt', 'label'] });
|
3
components/input-number/__tests__/a11y.test.ts
Normal file
3
components/input-number/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('input-number', { disabledRules: ['label'] });
|
3
components/input/__tests__/a11y.test.ts
Normal file
3
components/input/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('input', { disabledRules: ['label'] });
|
3
components/mentions/__tests__/a11y.test.ts
Normal file
3
components/mentions/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('mentions', { disabledRules: ['label'] });
|
3
components/menu/__tests__/a11y.test.ts
Normal file
3
components/menu/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('menu', { disabledRules: ['button-name'] });
|
3
components/message/__tests__/a11y.test.ts
Normal file
3
components/message/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('message');
|
4
components/modal/__tests__/a11y.test.ts
Normal file
4
components/modal/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
// skip debug components
|
||||||
|
accessibilityDemoTest('modal', { skip: ['wireframe.tsx', 'render-panel.tsx'] });
|
3
components/notification/__tests__/a11y.test.ts
Normal file
3
components/notification/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('notification', { disabledRules: ['button-name', 'label'] });
|
3
components/popconfirm/__tests__/a11y.test.ts
Normal file
3
components/popconfirm/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('popconfirm', { disabledRules: ['button-name'] });
|
3
components/radio/__tests__/a11y.test.ts
Normal file
3
components/radio/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('radio');
|
3
components/result/__tests__/a11y.test.ts
Normal file
3
components/result/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('result');
|
3
components/select/__tests__/a11y.test.ts
Normal file
3
components/select/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('select', { disabledRules: ['label', 'button-name'] });
|
3
components/space/__tests__/a11y.test.ts
Normal file
3
components/space/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('space', { disabledRules: ['label', 'button-name'] });
|
3
components/spin/__tests__/a11y.test.ts
Normal file
3
components/spin/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('spin', { disabledRules: ['button-name'] });
|
3
components/splitter/__tests__/a11y.test.ts
Normal file
3
components/splitter/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('splitter');
|
3
components/switch/__tests__/a11y.test.ts
Normal file
3
components/switch/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('switch', { disabledRules: ['button-name'] });
|
3
components/tag/__tests__/a11y.test.ts
Normal file
3
components/tag/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('tag');
|
3
components/timeline/__tests__/a11y.test.ts
Normal file
3
components/timeline/__tests__/a11y.test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import accessibilityDemoTest from '../../../tests/shared/accessibilityTest';
|
||||||
|
|
||||||
|
accessibilityDemoTest('timeline');
|
@ -1,15 +1,145 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
|
import { globSync } from 'glob';
|
||||||
import { axe } from 'jest-axe';
|
import { axe } from 'jest-axe';
|
||||||
|
|
||||||
|
class AxeQueueManager {
|
||||||
|
private queue: Promise<any> = Promise.resolve();
|
||||||
|
private isProcessing = false;
|
||||||
|
|
||||||
|
async enqueue<T>(task: () => Promise<T>): Promise<T> {
|
||||||
|
const currentQueue = this.queue;
|
||||||
|
|
||||||
|
const newTask = async () => {
|
||||||
|
try {
|
||||||
|
await currentQueue;
|
||||||
|
this.isProcessing = true;
|
||||||
|
return await task();
|
||||||
|
} finally {
|
||||||
|
this.isProcessing = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.queue = this.queue.then(newTask, newTask);
|
||||||
|
|
||||||
|
return this.queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
isRunning(): boolean {
|
||||||
|
return this.isProcessing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const axeQueueManager = new AxeQueueManager();
|
||||||
|
|
||||||
|
const runAxe = async (...args: Parameters<typeof axe>): Promise<ReturnType<typeof axe>> => {
|
||||||
|
return axeQueueManager.enqueue(async () => {
|
||||||
|
try {
|
||||||
|
return await axe(...args);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Axe test failed:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
type Rules = {
|
||||||
|
[key: string]: {
|
||||||
|
enabled: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const convertRulesToAxeFormat = (rules: string[]): Rules => {
|
||||||
|
return rules.reduce(
|
||||||
|
(acc, rule) => ({
|
||||||
|
...acc,
|
||||||
|
[rule]: { enabled: false },
|
||||||
|
}),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line jest/no-export
|
// eslint-disable-next-line jest/no-export
|
||||||
export default function accessibilityTest(Component: React.ComponentType) {
|
export function accessibilityTest(Component: React.ComponentType, disabledRules?: string[]) {
|
||||||
|
beforeAll(() => {
|
||||||
|
// Fake ResizeObserver
|
||||||
|
global.ResizeObserver = jest.fn(() => {
|
||||||
|
return {
|
||||||
|
observe() {},
|
||||||
|
unobserve() {},
|
||||||
|
disconnect() {},
|
||||||
|
};
|
||||||
|
}) as jest.Mock;
|
||||||
|
|
||||||
|
// fake fetch
|
||||||
|
global.fetch = jest.fn(() => {
|
||||||
|
return {
|
||||||
|
then() {
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
catch() {
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
finally() {
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}) as jest.Mock;
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Reset all mocks
|
||||||
|
if (global.fetch) {
|
||||||
|
(global.fetch as jest.Mock).mockClear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// Clear all mocks
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
describe(`accessibility`, () => {
|
describe(`accessibility`, () => {
|
||||||
it(`component does not have any violations`, async () => {
|
it(`component does not have any violations`, async () => {
|
||||||
jest.useRealTimers();
|
jest.useRealTimers();
|
||||||
const { container } = render(<Component />);
|
const { container } = render(<Component />);
|
||||||
const results = await axe(container);
|
|
||||||
|
const rules = convertRulesToAxeFormat(disabledRules || []);
|
||||||
|
|
||||||
|
const results = await runAxe(container, { rules });
|
||||||
expect(results).toHaveNoViolations();
|
expect(results).toHaveNoViolations();
|
||||||
|
}, 30000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
skip?: boolean | string[];
|
||||||
|
disabledRules?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line jest/no-export
|
||||||
|
export default function accessibilityDemoTest(component: string, options: Options = {}) {
|
||||||
|
// If skip is true, return immediately without executing any tests
|
||||||
|
if (options.skip === true) {
|
||||||
|
describe.skip(`${component} demo a11y`, () => {
|
||||||
|
it('skipped', () => {});
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe(`${component} demo a11y`, () => {
|
||||||
|
const files = globSync(`./components/${component}/demo/*.tsx`).filter(
|
||||||
|
(file) =>
|
||||||
|
!file.includes('_semantic') && !file.includes('debug') && !file.includes('component-token'),
|
||||||
|
);
|
||||||
|
|
||||||
|
files.forEach((file) => {
|
||||||
|
const shouldSkip = Array.isArray(options.skip) && options.skip.some((c) => file.endsWith(c));
|
||||||
|
const testMethod = shouldSkip ? describe.skip : describe;
|
||||||
|
|
||||||
|
testMethod(`Test ${file} accessibility`, () => {
|
||||||
|
const Demo = require(`../../${file}`).default;
|
||||||
|
accessibilityTest(Demo, options.disabledRules);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user