@axe-core/webdriverjs 4.3.3-alpha.243 → 4.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +13 -0
- package/README.md +2 -24
- package/dist/axe-injector.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +9 -11
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +2 -3
- package/dist/utils/index.d.ts +1 -2
- package/dist/utils/index.js +11 -9
- package/dist/utils/index.js.map +1 -1
- package/error-handling.md +52 -0
- package/example-with-callback.js +15 -0
- package/example.js +13 -0
- package/package.json +10 -13
- package/src/axe-injector.ts +191 -0
- package/src/browser.ts +142 -0
- package/src/index.ts +262 -0
- package/src/types.ts +27 -0
- package/src/utils/index.ts +23 -0
- package/tests/axe-webdriverjs.spec.ts +676 -0
- package/tests/fixtures/context.html +11 -0
- package/tests/test-utils.ts +49 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
import 'mocha';
|
|
2
|
+
import { Spec } from 'axe-core';
|
|
3
|
+
import { WebDriver } from 'selenium-webdriver';
|
|
4
|
+
import * as express from 'express';
|
|
5
|
+
import * as chromedriver from 'chromedriver';
|
|
6
|
+
import testListen = require('test-listen');
|
|
7
|
+
import delay from 'delay';
|
|
8
|
+
import { assert } from 'chai';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import * as fs from 'fs';
|
|
11
|
+
import { Server, createServer } from 'http';
|
|
12
|
+
import { Webdriver, connectToChromeDriver } from './test-utils';
|
|
13
|
+
import AxeBuilder from '../src';
|
|
14
|
+
import { axeRunPartial } from '../src/browser';
|
|
15
|
+
const dylangConfig = require('./fixtures/external/dylang-config.json') as Spec;
|
|
16
|
+
|
|
17
|
+
describe('@axe-core/webdriverjs', () => {
|
|
18
|
+
const port = 9515;
|
|
19
|
+
let driver: WebDriver;
|
|
20
|
+
let server: Server;
|
|
21
|
+
let addr: string;
|
|
22
|
+
let axeSource: string;
|
|
23
|
+
let axeCrasherSource: string;
|
|
24
|
+
let axeForceLegacy: string;
|
|
25
|
+
|
|
26
|
+
before(async () => {
|
|
27
|
+
const axePath = require.resolve('axe-core');
|
|
28
|
+
axeSource = fs.readFileSync(axePath, 'utf8');
|
|
29
|
+
const externalPath = path.resolve(__dirname, 'fixtures', 'external');
|
|
30
|
+
axeCrasherSource = fs.readFileSync(
|
|
31
|
+
path.join(externalPath, 'axe-crasher.js'),
|
|
32
|
+
'utf8'
|
|
33
|
+
);
|
|
34
|
+
axeForceLegacy = fs.readFileSync(
|
|
35
|
+
path.join(externalPath, 'axe-force-legacy.js'),
|
|
36
|
+
'utf8'
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
chromedriver.start([`--port=${port}`]);
|
|
40
|
+
await delay(500);
|
|
41
|
+
await connectToChromeDriver(port);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
after(() => {
|
|
45
|
+
chromedriver.stop();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
beforeEach(async () => {
|
|
49
|
+
const app = express();
|
|
50
|
+
app.use(express.static(path.resolve(__dirname, 'fixtures')));
|
|
51
|
+
server = createServer(app);
|
|
52
|
+
addr = await testListen(server);
|
|
53
|
+
driver = Webdriver();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
afterEach(async () => {
|
|
57
|
+
await driver.close();
|
|
58
|
+
server.close();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('analyze', () => {
|
|
62
|
+
it('returns results', async () => {
|
|
63
|
+
await driver.get(`${addr}/external/index.html`);
|
|
64
|
+
const results = await new AxeBuilder(driver).analyze();
|
|
65
|
+
assert.isNotNull(results);
|
|
66
|
+
assert.isArray(results.violations);
|
|
67
|
+
assert.isArray(results.incomplete);
|
|
68
|
+
assert.isArray(results.passes);
|
|
69
|
+
assert.isArray(results.inapplicable);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('handles undefineds', async () => {
|
|
73
|
+
await driver.get(`${addr}/external/index.html`);
|
|
74
|
+
const results = await new AxeBuilder(driver).analyze();
|
|
75
|
+
assert.isNotNull(results);
|
|
76
|
+
assert.isArray(results.violations);
|
|
77
|
+
assert.isArray(results.incomplete);
|
|
78
|
+
assert.isArray(results.passes);
|
|
79
|
+
assert.isArray(results.inapplicable);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('returns correct results metadata', async () => {
|
|
83
|
+
await driver.get(`${addr}/index.html`);
|
|
84
|
+
const results = await new AxeBuilder(driver).analyze();
|
|
85
|
+
assert.isDefined(results.testEngine.name);
|
|
86
|
+
assert.isDefined(results.testEngine.version);
|
|
87
|
+
assert.isDefined(results.testEnvironment.orientationAngle);
|
|
88
|
+
assert.isDefined(results.testEnvironment.orientationType);
|
|
89
|
+
assert.isDefined(results.testEnvironment.userAgent);
|
|
90
|
+
assert.isDefined(results.testEnvironment.windowHeight);
|
|
91
|
+
assert.isDefined(results.testEnvironment.windowWidth);
|
|
92
|
+
assert.isDefined(results.testRunner.name);
|
|
93
|
+
assert.isDefined(results.toolOptions.reporter);
|
|
94
|
+
assert.equal(results.url, `${addr}/index.html`);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('properly isolates the call to axe.finishRun', async () => {
|
|
98
|
+
let err;
|
|
99
|
+
await driver.get(`${addr}/external/isolated-finish.html`);
|
|
100
|
+
try {
|
|
101
|
+
await new AxeBuilder(driver).analyze();
|
|
102
|
+
} catch (e) {
|
|
103
|
+
err = e;
|
|
104
|
+
}
|
|
105
|
+
assert.isUndefined(err);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('throws if axe errors out on the top window', done => {
|
|
109
|
+
driver
|
|
110
|
+
.get(`${addr}/external/crash.html`)
|
|
111
|
+
.then(() => {
|
|
112
|
+
return new AxeBuilder(driver, axeSource + axeCrasherSource).analyze();
|
|
113
|
+
})
|
|
114
|
+
.then(
|
|
115
|
+
() => done(new Error('Expect async function to throw')),
|
|
116
|
+
() => done()
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('throws when injecting a problematic source', done => {
|
|
121
|
+
driver
|
|
122
|
+
.get(`${addr}/external/crash-me.html`)
|
|
123
|
+
.then(() => {
|
|
124
|
+
return new AxeBuilder(driver, 'throw new Error()').analyze();
|
|
125
|
+
})
|
|
126
|
+
.then(
|
|
127
|
+
() => done(new Error('Expect async function to throw')),
|
|
128
|
+
() => done()
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('throws when a setup fails', done => {
|
|
133
|
+
const brokenSource = axeSource + `;window.axe.utils = {}`;
|
|
134
|
+
driver
|
|
135
|
+
.get(`${addr}/external/index.html`)
|
|
136
|
+
.then(() => {
|
|
137
|
+
return new AxeBuilder(driver, brokenSource)
|
|
138
|
+
.withRules('label')
|
|
139
|
+
.analyze();
|
|
140
|
+
})
|
|
141
|
+
.then(
|
|
142
|
+
() => done(new Error(`Expect async function to throw`)),
|
|
143
|
+
() => done()
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe('configure', () => {
|
|
149
|
+
it('should find configured violations in all iframes', async () => {
|
|
150
|
+
await driver.get(`${addr}/external/nested-iframes.html`);
|
|
151
|
+
const results = await new AxeBuilder(driver)
|
|
152
|
+
.configure(dylangConfig)
|
|
153
|
+
.analyze();
|
|
154
|
+
|
|
155
|
+
assert.equal(results.violations[0].id, 'dylang');
|
|
156
|
+
// the second violation is in a iframe
|
|
157
|
+
assert.equal(results.violations[0].nodes.length, 8);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should find configured violations in all framesets', async () => {
|
|
161
|
+
await driver.get(`${addr}/external/nested-frameset.html`);
|
|
162
|
+
const results = await new AxeBuilder(driver)
|
|
163
|
+
.configure(dylangConfig)
|
|
164
|
+
.analyze();
|
|
165
|
+
|
|
166
|
+
assert.equal(results.violations[0].id, 'dylang');
|
|
167
|
+
// the second violation is in a frame
|
|
168
|
+
assert.equal(results.violations[0].nodes.length, 8);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('throws when passed a non-object', () => {
|
|
172
|
+
assert.throws(() => {
|
|
173
|
+
/* @ts-expect-error */
|
|
174
|
+
new AxeBuilder(driver, axe403Source).configure('abc123');
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('disableRules', () => {
|
|
180
|
+
it('disables the given rules(s) as array', async () => {
|
|
181
|
+
await driver.get(`${addr}/external/index.html`);
|
|
182
|
+
const results = await new AxeBuilder(driver)
|
|
183
|
+
.disableRules(['region'])
|
|
184
|
+
.analyze();
|
|
185
|
+
const all = [
|
|
186
|
+
...results.passes,
|
|
187
|
+
...results.inapplicable,
|
|
188
|
+
...results.violations,
|
|
189
|
+
...results.incomplete
|
|
190
|
+
];
|
|
191
|
+
assert.isTrue(!all.find(r => r.id === 'region'));
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('disables the given rules(s) as string', async () => {
|
|
195
|
+
await driver.get(`${addr}/external/index.html`);
|
|
196
|
+
const results = await new AxeBuilder(driver)
|
|
197
|
+
.disableRules('region')
|
|
198
|
+
.analyze();
|
|
199
|
+
const all = [
|
|
200
|
+
...results.passes,
|
|
201
|
+
...results.inapplicable,
|
|
202
|
+
...results.violations,
|
|
203
|
+
...results.incomplete
|
|
204
|
+
];
|
|
205
|
+
assert.isTrue(!all.find(r => r.id === 'region'));
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe('frame tests', () => {
|
|
210
|
+
it('injects into nested iframes', async () => {
|
|
211
|
+
await driver.get(`${addr}/external/nested-iframes.html`);
|
|
212
|
+
const { violations } = await new AxeBuilder(driver)
|
|
213
|
+
.options({ runOnly: 'label' })
|
|
214
|
+
.analyze();
|
|
215
|
+
|
|
216
|
+
assert.equal(violations[0].id, 'label');
|
|
217
|
+
const nodes = violations[0].nodes;
|
|
218
|
+
assert.lengthOf(nodes, 4);
|
|
219
|
+
assert.deepEqual(nodes[0].target, [
|
|
220
|
+
'#ifr-foo',
|
|
221
|
+
'#foo-bar',
|
|
222
|
+
'#bar-baz',
|
|
223
|
+
'input'
|
|
224
|
+
]);
|
|
225
|
+
assert.deepEqual(nodes[1].target, ['#ifr-foo', '#foo-baz', 'input']);
|
|
226
|
+
assert.deepEqual(nodes[2].target, ['#ifr-bar', '#bar-baz', 'input']);
|
|
227
|
+
assert.deepEqual(nodes[3].target, ['#ifr-baz', 'input']);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('injects into nested frameset', async () => {
|
|
231
|
+
await driver.get(`${addr}/external/nested-frameset.html`);
|
|
232
|
+
const { violations } = await new AxeBuilder(driver)
|
|
233
|
+
.options({ runOnly: 'label' })
|
|
234
|
+
.analyze();
|
|
235
|
+
|
|
236
|
+
assert.equal(violations[0].id, 'label');
|
|
237
|
+
assert.lengthOf(violations[0].nodes, 4);
|
|
238
|
+
|
|
239
|
+
const nodes = violations[0].nodes;
|
|
240
|
+
assert.deepEqual(nodes[0].target, [
|
|
241
|
+
'#frm-foo',
|
|
242
|
+
'#foo-bar',
|
|
243
|
+
'#bar-baz',
|
|
244
|
+
'input'
|
|
245
|
+
]);
|
|
246
|
+
assert.deepEqual(nodes[1].target, ['#frm-foo', '#foo-baz', 'input']);
|
|
247
|
+
assert.deepEqual(nodes[2].target, ['#frm-bar', '#bar-baz', 'input']);
|
|
248
|
+
assert.deepEqual(nodes[3].target, ['#frm-baz', 'input']);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should work on shadow DOM iframes', async () => {
|
|
252
|
+
await driver.get(`${addr}/external/shadow-frames.html`);
|
|
253
|
+
const { violations } = await new AxeBuilder(driver)
|
|
254
|
+
.options({ runOnly: 'label' })
|
|
255
|
+
.analyze();
|
|
256
|
+
|
|
257
|
+
assert.equal(violations[0].id, 'label');
|
|
258
|
+
assert.lengthOf(violations[0].nodes, 3);
|
|
259
|
+
|
|
260
|
+
const nodes = violations[0].nodes;
|
|
261
|
+
assert.deepEqual(nodes[0].target, ['#light-frame', 'input']);
|
|
262
|
+
assert.deepEqual(nodes[1].target, [
|
|
263
|
+
['#shadow-root', '#shadow-frame'],
|
|
264
|
+
'input'
|
|
265
|
+
]);
|
|
266
|
+
assert.deepEqual(nodes[2].target, ['#slotted-frame', 'input']);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('reports erroring frames in frame-tested', async () => {
|
|
270
|
+
await driver.get(`${addr}/external/crash-parent.html`);
|
|
271
|
+
const results = await new AxeBuilder(driver, axeSource + axeCrasherSource)
|
|
272
|
+
.options({ runOnly: ['label', 'frame-tested'] })
|
|
273
|
+
.analyze();
|
|
274
|
+
|
|
275
|
+
assert.equal(results.incomplete[0].id, 'frame-tested');
|
|
276
|
+
assert.lengthOf(results.incomplete[0].nodes, 1);
|
|
277
|
+
assert.deepEqual(results.incomplete[0].nodes[0].target, ['#ifr-crash']);
|
|
278
|
+
assert.equal(results.violations[0].id, 'label');
|
|
279
|
+
assert.lengthOf(results.violations[0].nodes, 2);
|
|
280
|
+
assert.deepEqual(results.violations[0].nodes[0].target, [
|
|
281
|
+
'#ifr-bar',
|
|
282
|
+
'#bar-baz',
|
|
283
|
+
'input'
|
|
284
|
+
]);
|
|
285
|
+
assert.deepEqual(results.violations[0].nodes[1].target, [
|
|
286
|
+
'#ifr-baz',
|
|
287
|
+
'input'
|
|
288
|
+
]);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('returns the same results from runPartial as from legacy mode', async () => {
|
|
292
|
+
await driver.get(`${addr}/nested-iframes.html`);
|
|
293
|
+
const legacyResults = await new AxeBuilder(
|
|
294
|
+
driver,
|
|
295
|
+
axeSource + axeForceLegacy
|
|
296
|
+
).analyze();
|
|
297
|
+
assert.equal(legacyResults.testEngine.name, 'axe-legacy');
|
|
298
|
+
|
|
299
|
+
const normalResults = await new AxeBuilder(driver, axeSource).analyze();
|
|
300
|
+
normalResults.timestamp = legacyResults.timestamp;
|
|
301
|
+
normalResults.testEngine.name = legacyResults.testEngine.name;
|
|
302
|
+
assert.deepEqual(normalResults, legacyResults);
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
describe('withRules', () => {
|
|
307
|
+
it('only runs the provided rules as an array', async () => {
|
|
308
|
+
await driver.get(`${addr}/external/index.html`);
|
|
309
|
+
const results = await new AxeBuilder(driver)
|
|
310
|
+
.withRules(['region'])
|
|
311
|
+
.analyze();
|
|
312
|
+
const all = [
|
|
313
|
+
...results.passes,
|
|
314
|
+
...results.inapplicable,
|
|
315
|
+
...results.violations,
|
|
316
|
+
...results.incomplete
|
|
317
|
+
];
|
|
318
|
+
assert.strictEqual(all.length, 1);
|
|
319
|
+
assert.strictEqual(all[0].id, 'region');
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('only runs the provided rules as a string', async () => {
|
|
323
|
+
await driver.get(`${addr}/external/index.html`);
|
|
324
|
+
const results = await new AxeBuilder(driver)
|
|
325
|
+
.withRules('region')
|
|
326
|
+
.analyze();
|
|
327
|
+
const all = [
|
|
328
|
+
...results.passes,
|
|
329
|
+
...results.inapplicable,
|
|
330
|
+
...results.violations,
|
|
331
|
+
...results.incomplete
|
|
332
|
+
];
|
|
333
|
+
assert.strictEqual(all.length, 1);
|
|
334
|
+
assert.strictEqual(all[0].id, 'region');
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
describe('options', () => {
|
|
339
|
+
it('passes options to axe-core', async () => {
|
|
340
|
+
await driver.get(`${addr}/external/index.html`);
|
|
341
|
+
const results = await new AxeBuilder(driver)
|
|
342
|
+
.options({ rules: { region: { enabled: false } } })
|
|
343
|
+
.analyze();
|
|
344
|
+
const all = [
|
|
345
|
+
...results.passes,
|
|
346
|
+
...results.inapplicable,
|
|
347
|
+
...results.violations,
|
|
348
|
+
...results.incomplete
|
|
349
|
+
];
|
|
350
|
+
assert.isTrue(!all.find(r => r.id === 'region'));
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
describe('withTags', () => {
|
|
355
|
+
it('only rules rules with the given tag(s) as an array', async () => {
|
|
356
|
+
await driver.get(`${addr}/external/index.html`);
|
|
357
|
+
const results = await new AxeBuilder(driver)
|
|
358
|
+
.withTags(['best-practice'])
|
|
359
|
+
.analyze();
|
|
360
|
+
const all = [
|
|
361
|
+
...results.passes,
|
|
362
|
+
...results.inapplicable,
|
|
363
|
+
...results.violations,
|
|
364
|
+
...results.incomplete
|
|
365
|
+
];
|
|
366
|
+
assert.isOk(all);
|
|
367
|
+
for (const rule of all) {
|
|
368
|
+
assert.include(rule.tags, 'best-practice');
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('only rules rules with the given tag(s) as a string', async () => {
|
|
373
|
+
await driver.get(`${addr}/external/index.html`);
|
|
374
|
+
const results = await new AxeBuilder(driver)
|
|
375
|
+
.withTags('best-practice')
|
|
376
|
+
.analyze();
|
|
377
|
+
const all = [
|
|
378
|
+
...results.passes,
|
|
379
|
+
...results.inapplicable,
|
|
380
|
+
...results.violations,
|
|
381
|
+
...results.incomplete
|
|
382
|
+
];
|
|
383
|
+
assert.isOk(all);
|
|
384
|
+
for (const rule of all) {
|
|
385
|
+
assert.include(rule.tags, 'best-practice');
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('No results provided when the given tag(s) is invalid', async () => {
|
|
390
|
+
await driver.get(`${addr}/external/index.html`);
|
|
391
|
+
const results = await new AxeBuilder(driver)
|
|
392
|
+
.withTags(['foobar'])
|
|
393
|
+
.analyze();
|
|
394
|
+
|
|
395
|
+
const all = [
|
|
396
|
+
...results.passes,
|
|
397
|
+
...results.inapplicable,
|
|
398
|
+
...results.violations,
|
|
399
|
+
...results.incomplete
|
|
400
|
+
];
|
|
401
|
+
// Ensure all run rules had the "foobar" tag
|
|
402
|
+
assert.deepStrictEqual(0, all.length);
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
describe('include/exclude', () => {
|
|
407
|
+
it('with include and exclude', async () => {
|
|
408
|
+
let error: Error | null = null;
|
|
409
|
+
await driver.get(`${addr}/context.html`);
|
|
410
|
+
const builder = new AxeBuilder(driver)
|
|
411
|
+
.include('.include')
|
|
412
|
+
.exclude('.exclude');
|
|
413
|
+
|
|
414
|
+
try {
|
|
415
|
+
await builder.analyze();
|
|
416
|
+
} catch (e) {
|
|
417
|
+
error = e;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
assert.strictEqual(error, null);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('with only include', async () => {
|
|
424
|
+
let error: Error | null = null;
|
|
425
|
+
await driver.get(`${addr}/context.html`);
|
|
426
|
+
const builder = new AxeBuilder(driver).include('.include');
|
|
427
|
+
|
|
428
|
+
try {
|
|
429
|
+
await builder.analyze();
|
|
430
|
+
} catch (e) {
|
|
431
|
+
error = e;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
assert.strictEqual(error, null);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('with only exclude', async () => {
|
|
438
|
+
let error: Error | null = null;
|
|
439
|
+
await driver.get(`${addr}/context.html`);
|
|
440
|
+
const builder = new AxeBuilder(driver).exclude('.exclude');
|
|
441
|
+
|
|
442
|
+
try {
|
|
443
|
+
await builder.analyze();
|
|
444
|
+
} catch (e) {
|
|
445
|
+
error = e;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
assert.strictEqual(error, null);
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
describe('callback()', () => {
|
|
453
|
+
it('returns an error as the first argument', done => {
|
|
454
|
+
driver.get(`${addr}/external/index.html`).then(() => {
|
|
455
|
+
new AxeBuilder(driver, 'throw new Error()').analyze((err, results) => {
|
|
456
|
+
try {
|
|
457
|
+
assert.isNull(results);
|
|
458
|
+
assert.isNotNull(err);
|
|
459
|
+
done();
|
|
460
|
+
} catch (e) {
|
|
461
|
+
done(e);
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('returns as the second argument', done => {
|
|
468
|
+
driver.get(`${addr}/external/index.html`).then(() => {
|
|
469
|
+
new AxeBuilder(driver).analyze((err, results) => {
|
|
470
|
+
try {
|
|
471
|
+
assert.isNull(err);
|
|
472
|
+
assert.isNotNull(results);
|
|
473
|
+
assert.isArray(results?.violations);
|
|
474
|
+
assert.isArray(results?.incomplete);
|
|
475
|
+
assert.isArray(results?.passes);
|
|
476
|
+
assert.isArray(results?.inapplicable);
|
|
477
|
+
done();
|
|
478
|
+
} catch (e) {
|
|
479
|
+
done(e);
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
describe('axe.finishRun errors', () => {
|
|
487
|
+
const finishRunThrows = `;axe.finishRun = () => { throw new Error("No finishRun")}`;
|
|
488
|
+
const windowOpenThrows = `;window.open = () => { throw new Error("No window.open")}`;
|
|
489
|
+
|
|
490
|
+
it('throws an error if window.open throws', async () => {
|
|
491
|
+
const source = axeSource + windowOpenThrows;
|
|
492
|
+
await driver.get(`${addr}/external/index.html`);
|
|
493
|
+
|
|
494
|
+
try {
|
|
495
|
+
await new AxeBuilder(driver, source).analyze();
|
|
496
|
+
assert.fail('Should have thrown');
|
|
497
|
+
} catch (err) {
|
|
498
|
+
assert.match(err.message, /switchTo failed./);
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it('throws an error if axe.finishRun throws', async () => {
|
|
503
|
+
const source = axeSource + finishRunThrows;
|
|
504
|
+
await driver.get(`${addr}/external/index.html`);
|
|
505
|
+
|
|
506
|
+
try {
|
|
507
|
+
await new AxeBuilder(driver, source).analyze();
|
|
508
|
+
assert.fail('Should have thrown');
|
|
509
|
+
} catch (err) {
|
|
510
|
+
assert.match(err.message, /Please check out/);
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
describe('setLegacyMode', () => {
|
|
516
|
+
const runPartialThrows = `;axe.runPartial = () => { throw new Error("No runPartial")}`;
|
|
517
|
+
it('runs legacy mode when used', async () => {
|
|
518
|
+
await driver.get(`${addr}/external/index.html`);
|
|
519
|
+
const results = await new AxeBuilder(driver, axeSource + runPartialThrows)
|
|
520
|
+
.setLegacyMode()
|
|
521
|
+
.analyze();
|
|
522
|
+
assert.isNotNull(results);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it('prevents cross-origin frame testing', async () => {
|
|
526
|
+
await driver.get(`${addr}/external/cross-origin.html`);
|
|
527
|
+
const results = await new AxeBuilder(driver, axeSource + runPartialThrows)
|
|
528
|
+
.withRules(['frame-tested'])
|
|
529
|
+
.setLegacyMode()
|
|
530
|
+
.analyze();
|
|
531
|
+
|
|
532
|
+
const frameTested = results.incomplete.find(
|
|
533
|
+
({ id }) => id === 'frame-tested'
|
|
534
|
+
);
|
|
535
|
+
assert.ok(frameTested);
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it('can be disabled again', async () => {
|
|
539
|
+
await driver.get(`${addr}/external/cross-origin.html`);
|
|
540
|
+
const results = await new AxeBuilder(driver)
|
|
541
|
+
.withRules(['frame-tested'])
|
|
542
|
+
.setLegacyMode()
|
|
543
|
+
.setLegacyMode(false)
|
|
544
|
+
.analyze();
|
|
545
|
+
|
|
546
|
+
const frameTested = results.incomplete.find(
|
|
547
|
+
({ id }) => id === 'frame-tested'
|
|
548
|
+
);
|
|
549
|
+
assert.isUndefined(frameTested);
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
describe('browser functions', () => {
|
|
554
|
+
it('serializes results', async () => {
|
|
555
|
+
await driver.get(`${addr}/external/nested-iframes.html`);
|
|
556
|
+
await driver.executeScript(`
|
|
557
|
+
window.axe = {
|
|
558
|
+
runPartial: (c, o) => Promise.resolve({ violations: [], passes: [] })
|
|
559
|
+
};
|
|
560
|
+
`);
|
|
561
|
+
const res = await axeRunPartial(driver, null as any, null as any);
|
|
562
|
+
assert.equal(typeof res, 'string');
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
describe('for versions without axe.runPartial', () => {
|
|
566
|
+
let axe403Source: string;
|
|
567
|
+
before(() => {
|
|
568
|
+
const axe403Path = path.resolve(
|
|
569
|
+
__dirname,
|
|
570
|
+
'fixtures',
|
|
571
|
+
'external',
|
|
572
|
+
'axe-core@legacy.js'
|
|
573
|
+
);
|
|
574
|
+
axe403Source = fs.readFileSync(axe403Path, 'utf8');
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
it('can run', async () => {
|
|
578
|
+
await driver.get(`${addr}/external/nested-iframes.html`);
|
|
579
|
+
const results = await new AxeBuilder(driver, axe403Source)
|
|
580
|
+
.options({ runOnly: ['label'] })
|
|
581
|
+
.analyze();
|
|
582
|
+
|
|
583
|
+
assert.equal(results.violations[0].id, 'label');
|
|
584
|
+
assert.lengthOf(results.violations[0].nodes, 4);
|
|
585
|
+
assert.equal(results.testEngine.version, '4.2.3');
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it('throws if the top level errors', done => {
|
|
589
|
+
driver
|
|
590
|
+
.get(`${addr}/external/crash.html`)
|
|
591
|
+
.then(() => {
|
|
592
|
+
return new AxeBuilder(
|
|
593
|
+
driver,
|
|
594
|
+
axe403Source + axeCrasherSource
|
|
595
|
+
).analyze();
|
|
596
|
+
})
|
|
597
|
+
.then(
|
|
598
|
+
() => done(new Error('Expect async function to throw')),
|
|
599
|
+
() => done()
|
|
600
|
+
);
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
it('can be configured', async () => {
|
|
604
|
+
await driver.get(`${addr}/external/nested-iframes.html`);
|
|
605
|
+
const results = await new AxeBuilder(driver, axe403Source)
|
|
606
|
+
.configure(dylangConfig)
|
|
607
|
+
.analyze();
|
|
608
|
+
assert.equal(results.violations[0].id, 'dylang');
|
|
609
|
+
assert.equal(results.violations[0].nodes.length, 8);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
it('reports frame-tested', async () => {
|
|
613
|
+
await driver.get(`${addr}/external/crash-parent.html`);
|
|
614
|
+
const results = await new AxeBuilder(
|
|
615
|
+
driver,
|
|
616
|
+
axe403Source + axeCrasherSource
|
|
617
|
+
)
|
|
618
|
+
.options({ runOnly: ['label', 'frame-tested'] })
|
|
619
|
+
.analyze();
|
|
620
|
+
assert.equal(results.incomplete[0].id, 'frame-tested');
|
|
621
|
+
assert.lengthOf(results.incomplete[0].nodes, 1);
|
|
622
|
+
assert.equal(results.violations[0].id, 'label');
|
|
623
|
+
assert.lengthOf(results.violations[0].nodes, 2);
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
it('tests cross-origin pages', async () => {
|
|
627
|
+
await driver.get(`${addr}/external/cross-origin.html`);
|
|
628
|
+
const results = await new AxeBuilder(driver, axe403Source)
|
|
629
|
+
.withRules(['frame-tested'])
|
|
630
|
+
.analyze();
|
|
631
|
+
|
|
632
|
+
const frameTested = results.incomplete.find(
|
|
633
|
+
({ id }) => id === 'frame-tested'
|
|
634
|
+
);
|
|
635
|
+
assert.isUndefined(frameTested);
|
|
636
|
+
});
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
describe('allowedOrigins', () => {
|
|
640
|
+
const getAllowedOrigins = async (): Promise<string[]> => {
|
|
641
|
+
return await driver.executeScript('return axe._audit.allowedOrigins');
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
it('should not set when running runPartial and not legacy mode', async () => {
|
|
645
|
+
await driver.get(`${addr}/index.html`);
|
|
646
|
+
await new AxeBuilder(driver).analyze();
|
|
647
|
+
const allowedOrigins = await getAllowedOrigins();
|
|
648
|
+
assert.deepEqual(allowedOrigins, [addr]);
|
|
649
|
+
assert.lengthOf(allowedOrigins, 1);
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it('should not set when running runPartial and legacy mode', async () => {
|
|
653
|
+
await driver.get(`${addr}/index.html`);
|
|
654
|
+
await new AxeBuilder(driver).setLegacyMode(true).analyze();
|
|
655
|
+
const allowedOrigins = await getAllowedOrigins();
|
|
656
|
+
assert.deepEqual(allowedOrigins, [addr]);
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
it('should not set when running legacy source and legacy mode', async () => {
|
|
660
|
+
await driver.get(`${addr}/index.html`);
|
|
661
|
+
await new AxeBuilder(driver, axeSource + axeForceLegacy)
|
|
662
|
+
.setLegacyMode(true)
|
|
663
|
+
.analyze();
|
|
664
|
+
const allowedOrigins = await getAllowedOrigins();
|
|
665
|
+
assert.deepEqual(allowedOrigins, [addr]);
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it('should set when running legacy source and not legacy mode', async () => {
|
|
669
|
+
await driver.get(`${addr}/index.html`);
|
|
670
|
+
await new AxeBuilder(driver, axeSource + axeForceLegacy).analyze();
|
|
671
|
+
const allowedOrigins = await getAllowedOrigins();
|
|
672
|
+
assert.deepEqual(allowedOrigins, ['*']);
|
|
673
|
+
assert.lengthOf(allowedOrigins, 1);
|
|
674
|
+
});
|
|
675
|
+
});
|
|
676
|
+
});
|