mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-23 18:50:06 +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 Affix from '..';
|
||||
import accessibilityTest from '../../../tests/shared/accessibilityTest';
|
||||
import { accessibilityTest } from '../../../tests/shared/accessibilityTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { render, triggerResize, waitFakeTimer } from '../../../tests/utils';
|
||||
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 Alert from '..';
|
||||
import accessibilityTest from '../../../tests/shared/accessibilityTest';
|
||||
import { accessibilityTest } from '../../../tests/shared/accessibilityTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { act, render, screen, waitFakeTimer } from '../../../tests/utils';
|
||||
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 { resetWarned } from '../../_util/warning';
|
||||
import accessibilityTest from '../../../tests/shared/accessibilityTest';
|
||||
import { accessibilityTest } from '../../../tests/shared/accessibilityTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
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 { render } from '@testing-library/react';
|
||||
import { globSync } from 'glob';
|
||||
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
|
||||
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`, () => {
|
||||
it(`component does not have any violations`, async () => {
|
||||
jest.useRealTimers();
|
||||
const { container } = render(<Component />);
|
||||
const results = await axe(container);
|
||||
|
||||
const rules = convertRulesToAxeFormat(disabledRules || []);
|
||||
|
||||
const results = await runAxe(container, { rules });
|
||||
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