mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-14 23:18:36 +00:00
add editor a11y playwright tests (#255899)
This commit is contained in:
@@ -90,6 +90,7 @@ export class NativeEditContext extends AbstractEditContext {
|
||||
this._imeTextArea.setClassName(`ime-text-area`);
|
||||
this._imeTextArea.setAttribute('readonly', 'true');
|
||||
this._imeTextArea.setAttribute('tabindex', '-1');
|
||||
this._imeTextArea.setAttribute('aria-hidden', 'true');
|
||||
this.domNode.setAttribute('autocorrect', 'off');
|
||||
this.domNode.setAttribute('autocapitalize', 'off');
|
||||
this.domNode.setAttribute('autocomplete', 'off');
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import * as playwright from '@playwright/test';
|
||||
import { assert } from 'chai';
|
||||
import { injectAxe } from 'axe-playwright';
|
||||
|
||||
const PORT = 8563;
|
||||
const TIMEOUT = 20 * 1000;
|
||||
@@ -135,4 +136,127 @@ describe('API Integration Tests', function (): void {
|
||||
'\t\t\'\'\'Make the monkey eat N bananas!\'\'\''
|
||||
]);
|
||||
});
|
||||
describe('Accessibility', function (): void {
|
||||
beforeEach(async () => {
|
||||
await page.goto(APP);
|
||||
await injectAxe(page);
|
||||
await page.evaluate(`
|
||||
(function () {
|
||||
instance.focus();
|
||||
instance.trigger('keyboard', 'cursorHome');
|
||||
instance.trigger('keyboard', 'type', {
|
||||
text: 'a'
|
||||
});
|
||||
})()
|
||||
`);
|
||||
});
|
||||
|
||||
it('Editor should not have critical accessibility violations', async () => {
|
||||
let violationCount = 0;
|
||||
const checkedElements = new Set<string>();
|
||||
|
||||
// Run axe and get all results (passes and violations)
|
||||
const axeResults = await page.evaluate(() => {
|
||||
return window.axe.run(document, {
|
||||
runOnly: {
|
||||
type: 'tag',
|
||||
values: [
|
||||
'wcag2a',
|
||||
'wcag2aa',
|
||||
'wcag21a',
|
||||
'wcag21aa',
|
||||
'best-practice'
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
axeResults.violations.forEach((v: any) => {
|
||||
const isCritical = v.impact === 'critical';
|
||||
const emoji = isCritical ? '❌' : undefined;
|
||||
v.nodes.forEach((node: any) => {
|
||||
const selector = node.target?.join(' ');
|
||||
if (selector && emoji) {
|
||||
checkedElements.add(selector);
|
||||
console.log(`${emoji} FAIL: ${selector} - ${v.id} - ${v.description}`);
|
||||
}
|
||||
});
|
||||
violationCount += isCritical ? 1 : 0;
|
||||
});
|
||||
|
||||
axeResults.passes.forEach((pass: any) => {
|
||||
pass.nodes.forEach((node: any) => {
|
||||
const selector = node.target?.join(' ');
|
||||
if (selector && !checkedElements.has(selector)) {
|
||||
checkedElements.add(selector);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
playwright.expect(violationCount).toBe(0);
|
||||
});
|
||||
|
||||
it('Editor should not have color contrast accessibility violations', async () => {
|
||||
let violationCount = 0;
|
||||
const checkedElements = new Set<string>();
|
||||
|
||||
const axeResults = await page.evaluate(() => {
|
||||
return window.axe.run(document, {
|
||||
runOnly: {
|
||||
type: 'rule',
|
||||
values: ['color-contrast']
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
axeResults.violations.forEach((v: any) => {
|
||||
const isCritical = v.impact === 'critical';
|
||||
const emoji = isCritical ? '❌' : undefined;
|
||||
v.nodes.forEach((node: any) => {
|
||||
const selector = node.target?.join(' ');
|
||||
if (selector && emoji) {
|
||||
checkedElements.add(selector);
|
||||
console.log(`${emoji} FAIL: ${selector} - ${v.id} - ${v.description}`);
|
||||
}
|
||||
});
|
||||
violationCount += 1;
|
||||
});
|
||||
|
||||
axeResults.passes.forEach((pass: any) => {
|
||||
pass.nodes.forEach((node: any) => {
|
||||
const selector = node.target?.join(' ');
|
||||
if (selector && !checkedElements.has(selector)) {
|
||||
checkedElements.add(selector);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
playwright.expect(violationCount).toBe(0);
|
||||
});
|
||||
it('Monaco editor container should have an ARIA role', async () => {
|
||||
const role = await page.evaluate(() => {
|
||||
const container = document.querySelector('.monaco-editor');
|
||||
return container?.getAttribute('role');
|
||||
});
|
||||
assert.isDefined(role, 'Monaco editor container should have a role attribute');
|
||||
});
|
||||
|
||||
it('Monaco editor should have an ARIA label', async () => {
|
||||
const ariaLabel = await page.evaluate(() => {
|
||||
const container = document.querySelector('.monaco-editor');
|
||||
return container?.getAttribute('aria-label');
|
||||
});
|
||||
assert.isDefined(ariaLabel, 'Monaco editor container should have an aria-label attribute');
|
||||
});
|
||||
|
||||
it('All toolbar buttons should have accessible names', async () => {
|
||||
const buttonsWithoutLabel = await page.evaluate(() => {
|
||||
return Array.from(document.querySelectorAll('button')).filter(btn => {
|
||||
const label = btn.getAttribute('aria-label') || btn.textContent?.trim();
|
||||
return !label;
|
||||
}).map(btn => btn.outerHTML);
|
||||
});
|
||||
assert.deepEqual(buttonsWithoutLabel, [], 'All toolbar buttons should have accessible names');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
126
test/monaco/package-lock.json
generated
126
test/monaco/package-lock.json
generated
@@ -10,6 +10,7 @@
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.2.14",
|
||||
"axe-playwright": "^2.1.0",
|
||||
"chai": "^4.2.0",
|
||||
"warnings-to-errors-webpack-plugin": "^2.3.0"
|
||||
}
|
||||
@@ -20,6 +21,13 @@
|
||||
"integrity": "sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/junit-report-builder": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/junit-report-builder/-/junit-report-builder-3.0.2.tgz",
|
||||
"integrity": "sha512-R5M+SYhMbwBeQcNXYWNCZkl09vkVfAtcPIaCGdzIkkbeaTrVbGQ7HVgi4s+EmM/M1K4ZuWQH0jGcvMvNePfxYA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/assertion-error": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
|
||||
@@ -29,6 +37,49 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/axe-core": {
|
||||
"version": "4.10.3",
|
||||
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz",
|
||||
"integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==",
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/axe-html-reporter": {
|
||||
"version": "2.2.11",
|
||||
"resolved": "https://registry.npmjs.org/axe-html-reporter/-/axe-html-reporter-2.2.11.tgz",
|
||||
"integrity": "sha512-WlF+xlNVgNVWiM6IdVrsh+N0Cw7qupe5HT9N6Uyi+aN7f6SSi92RDomiP1noW8OWIV85V6x404m5oKMeqRV3tQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mustache": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"axe-core": ">=3"
|
||||
}
|
||||
},
|
||||
"node_modules/axe-playwright": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/axe-playwright/-/axe-playwright-2.1.0.tgz",
|
||||
"integrity": "sha512-tY48SX56XaAp16oHPyD4DXpybz8Jxdz9P7exTjF/4AV70EGUavk+1fUPWirM0OYBR+YyDx6hUeDvuHVA6fB9YA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/junit-report-builder": "^3.0.2",
|
||||
"axe-core": "^4.10.1",
|
||||
"axe-html-reporter": "2.2.11",
|
||||
"junit-report-builder": "^5.1.1",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"playwright": ">1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chai": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
|
||||
@@ -77,6 +128,54 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/junit-report-builder": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/junit-report-builder/-/junit-report-builder-5.1.1.tgz",
|
||||
"integrity": "sha512-ZNOIIGMzqCGcHQEA2Q4rIQQ3Df6gSIfne+X9Rly9Bc2y55KxAZu8iGv+n2pP0bLf0XAOctJZgeloC54hWzCahQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.21",
|
||||
"make-dir": "^3.1.0",
|
||||
"xmlbuilder": "^15.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/make-dir": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"semver": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/mustache": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
|
||||
"integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mustache": "bin/mustache"
|
||||
}
|
||||
},
|
||||
"node_modules/pathval": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
|
||||
@@ -86,6 +185,23 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/type-detect": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
||||
@@ -103,6 +219,16 @@
|
||||
"peerDependencies": {
|
||||
"webpack": "^2.2.0-rc || ^3 || ^4 || ^5"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlbuilder": {
|
||||
"version": "15.1.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
||||
"integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.2.14",
|
||||
"axe-playwright": "^2.1.0",
|
||||
"chai": "^4.2.0",
|
||||
"warnings-to-errors-webpack-plugin": "^2.3.0"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user