testaro 60.7.4 → 60.8.1

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.
@@ -4,28 +4,30 @@ Quick notes to run the Testaro validation tests locally (Windows PowerShell):
4
4
 
5
5
  1. Install project dependencies
6
6
 
7
- ```powershell
8
- npm install
9
- ```
7
+ ```powershell
8
+ npm install
9
+ ```
10
10
 
11
- 2. Install Playwright browsers (required)
11
+ 1. Install Playwright browsers (required)
12
12
 
13
- ```powershell
14
- npx playwright install
15
- ```
13
+ ```powershell
14
+ npx playwright install
15
+ ```
16
16
 
17
- 3. Run a validation for a specific rule (example: `altScheme`)
17
+ 1. Run a validation for a specific rule (example: `altScheme`)
18
18
 
19
19
  ```powershell
20
20
  npm test altScheme
21
21
  ```
22
22
 
23
23
  Notes:
24
+
24
25
  - If a validator job is stored under `validation/tests/jobProperties/pending`, copy it to `validation/tests/jobProperties/` or run the validator via the provided filenames. `altScheme` was copied already.
25
26
  - If a test fails its expectations, read the JSON output printed by the validation harness for `standardResult` and `expectations` to identify missing instances.
26
27
  - After making changes to rule implementations in `testaro/`, re-run the specific `npm test <ruleID>` until the validator reports success.
27
28
 
28
29
  Preparing a PR:
30
+
29
31
  - Create a branch (example `feature/add-training-rules`), commit your changes, push to remote, and open a PR describing which rules are training vs clean-room.
30
32
 
31
33
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testaro",
3
- "version": "60.7.4",
3
+ "version": "60.8.1",
4
4
  "description": "Run 1000 web accessibility tests from 11 tools and get a standardized report",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/run.js CHANGED
@@ -372,121 +372,6 @@ const launch = exports.launch = async (
372
372
  browserContext.setDefaultTimeout(0);
373
373
  // When a page (i.e. tab) is added to the browser context (i.e. browser window):
374
374
  browserContext.on('page', async page => {
375
- const isTestaroTest = act.type === 'test' && act.which === 'testaro';
376
- // Mask automation detection.
377
- await page.addInitScript(isTestaroTest => {
378
- Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
379
- window.chrome = {runtime: {}};
380
- Object.defineProperty(navigator, 'plugins', {
381
- get: () => [1, 2, 3, 4, 5]
382
- });
383
- Object.defineProperty(navigator, 'languages', {
384
- get: () => ['en-US', 'en']
385
- });
386
- // If the act is a testaro test act:
387
- if (isTestaroTest) {
388
- // Add a window method to return an instance.
389
- window.getInstance = (
390
- element, ruleID, what, count = 1, ordinalSeverity, summaryTagName = ''
391
- ) => {
392
- // If the element exists:
393
- if (element) {
394
- // Get its properties.
395
- const boxData = element.getBoundingClientRect();
396
- ['x', 'y', 'width', 'height'].forEach(dimension => {
397
- boxData[dimension] = Math.round(boxData[dimension]);
398
- });
399
- const {x, y, width, height} = boxData;
400
- const {tagName, id = ''} = element;
401
- // Return an itemized instance.
402
- return {
403
- ruleID,
404
- what,
405
- count,
406
- ordinalSeverity,
407
- tagName,
408
- id,
409
- location: {
410
- doc: 'dom',
411
- type: 'box',
412
- spec: {
413
- x,
414
- y,
415
- width,
416
- height
417
- }
418
- },
419
- excerpt: element.textContent.trim().replace(/\s+/g, ' ').slice(0, 100),
420
- boxID: [x, y, width, height].join(':'),
421
- pathID: window.getXPath(element)
422
- };
423
- }
424
- // Otherwise, i.e. if no element exists, return a summary instance.
425
- return {
426
- ruleID,
427
- what,
428
- count,
429
- ordinalSeverity,
430
- tagName: summaryTagName,
431
- id: '',
432
- location: {
433
- doc: '',
434
- type: '',
435
- spec: ''
436
- },
437
- excerpt: '',
438
- boxID: '',
439
- pathID: ''
440
- };
441
- };
442
- // Add a window method to get the XPath of an element.
443
- window.getXPath = element => {
444
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
445
- return '';
446
- }
447
- const segments = [];
448
- let el = element;
449
- // As long as the current node is an element:
450
- while (el && el.nodeType === Node.ELEMENT_NODE) {
451
- const tag = el.tagName.toLowerCase();
452
- // If it is the html element:
453
- if (el === document.documentElement) {
454
- // Prepend it to the segment array
455
- segments.unshift('html');
456
- // Stop traversing.
457
- break;
458
- }
459
- // Otherwise, get its parent node.
460
- const parent = el.parentNode;
461
- // If (abnormally) the parent node is not an element:
462
- if (!parent || parent.nodeType !== Node.ELEMENT_NODE) {
463
- // Prepend it to the segment array.
464
- segments.unshift(tag);
465
- // Stop traversing, leaving the segment array partial.
466
- break;
467
- }
468
- let subscript = '';
469
- let sameTagCount = 0;
470
- // Get the subscript of the element.
471
- const cohort = Array
472
- .from(parent.childNodes)
473
- .filter(
474
- childNode => childNode.nodeType === Node.ELEMENT_NODE
475
- && childNode.tagName === el.tagName
476
- );
477
- if (cohort.length > 1) {
478
- subscript = `[${cohort.indexOf(el) + 1}]`;
479
- }
480
- // Prepend the element identifier to the segment array.
481
- segments.unshift(`${tag}${subscript}`);
482
- // Continue the traversal with the parent of the current element.
483
- el = parent;
484
- }
485
- // Return the XPath.
486
- return `/${segments.join('/')}`;
487
- };
488
- }
489
- }, isTestaroTest);
490
375
  // Ensure the report has a jobData property.
491
376
  report.jobData ??= {};
492
377
  const {jobData} = report;
@@ -550,6 +435,122 @@ const launch = exports.launch = async (
550
435
  page = await browserContext.newPage();
551
436
  // Wait until it is stable.
552
437
  await page.waitForLoadState('domcontentloaded', {timeout: 5000});
438
+ const isTestaroTest = act.type === 'test' && act.which === 'testaro';
439
+ // Add a script to the page to:
440
+ await page.addInitScript(isTestaroTest => {
441
+ // Mask automation detection.
442
+ Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
443
+ window.chrome = {runtime: {}};
444
+ Object.defineProperty(navigator, 'plugins', {
445
+ get: () => [1, 2, 3, 4, 5]
446
+ });
447
+ Object.defineProperty(navigator, 'languages', {
448
+ get: () => ['en-US', 'en']
449
+ });
450
+ // If the act is a testaro test act:
451
+ if (isTestaroTest) {
452
+ // Add a window method to return an instance.
453
+ window.getInstance = (
454
+ element, ruleID, what, count = 1, ordinalSeverity, summaryTagName = ''
455
+ ) => {
456
+ // If the element exists:
457
+ if (element) {
458
+ // Get its properties.
459
+ const boxData = element.getBoundingClientRect();
460
+ ['x', 'y', 'width', 'height'].forEach(dimension => {
461
+ boxData[dimension] = Math.round(boxData[dimension]);
462
+ });
463
+ const {x, y, width, height} = boxData;
464
+ const {tagName, id = ''} = element;
465
+ // Return an itemized instance.
466
+ return {
467
+ ruleID,
468
+ what,
469
+ count,
470
+ ordinalSeverity,
471
+ tagName,
472
+ id,
473
+ location: {
474
+ doc: 'dom',
475
+ type: 'box',
476
+ spec: {
477
+ x,
478
+ y,
479
+ width,
480
+ height
481
+ }
482
+ },
483
+ excerpt: element.textContent.trim().replace(/\s+/g, ' ').slice(0, 100),
484
+ boxID: [x, y, width, height].join(':'),
485
+ pathID: window.getXPath(element)
486
+ };
487
+ }
488
+ // Otherwise, i.e. if no element exists, return a summary instance.
489
+ return {
490
+ ruleID,
491
+ what,
492
+ count,
493
+ ordinalSeverity,
494
+ tagName: summaryTagName,
495
+ id: '',
496
+ location: {
497
+ doc: '',
498
+ type: '',
499
+ spec: ''
500
+ },
501
+ excerpt: '',
502
+ boxID: '',
503
+ pathID: ''
504
+ };
505
+ };
506
+ // Add a window method to get the XPath of an element.
507
+ window.getXPath = element => {
508
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) {
509
+ return '';
510
+ }
511
+ const segments = [];
512
+ let el = element;
513
+ // As long as the current node is an element:
514
+ while (el && el.nodeType === Node.ELEMENT_NODE) {
515
+ const tag = el.tagName.toLowerCase();
516
+ // If it is the html element:
517
+ if (el === document.documentElement) {
518
+ // Prepend it to the segment array
519
+ segments.unshift('html');
520
+ // Stop traversing.
521
+ break;
522
+ }
523
+ // Otherwise, get its parent node.
524
+ const parent = el.parentNode;
525
+ // If (abnormally) the parent node is not an element:
526
+ if (!parent || parent.nodeType !== Node.ELEMENT_NODE) {
527
+ // Prepend it to the segment array.
528
+ segments.unshift(tag);
529
+ // Stop traversing, leaving the segment array partial.
530
+ break;
531
+ }
532
+ let subscript = '';
533
+ let sameTagCount = 0;
534
+ // Get the subscript of the element.
535
+ const cohort = Array
536
+ .from(parent.childNodes)
537
+ .filter(
538
+ childNode => childNode.nodeType === Node.ELEMENT_NODE
539
+ && childNode.tagName === el.tagName
540
+ );
541
+ if (cohort.length > 1) {
542
+ subscript = `[${cohort.indexOf(el) + 1}]`;
543
+ }
544
+ // Prepend the element identifier to the segment array.
545
+ segments.unshift(`${tag}${subscript}`);
546
+ // Continue the traversal with the parent of the current element.
547
+ el = parent;
548
+ }
549
+ // Return the XPath.
550
+ return `/${segments.join('/')}`;
551
+ };
552
+ }
553
+ }, isTestaroTest);
553
554
  // Navigate to the specified URL.
554
555
  const navResult = await goTo(report, page, url, 15000, 'domcontentloaded');
555
556
  // If the navigation succeeded:
package/testaro/adbID.js CHANGED
@@ -1,5 +1,6 @@
1
1
  /*
2
2
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2025 Juan S. Casado. All rights reserved.
3
4
  © 2025 Jonathan Robert Pool. All rights reserved.
4
5
 
5
6
  MIT License
@@ -25,7 +26,7 @@
25
26
 
26
27
  /*
27
28
  adbID
28
- This test reports elements referencing aria-describedby targets that are missing
29
+ Clean-room rule:This test reports elements referencing aria-describedby targets that are missing
29
30
  or, because of duplicate IDs, ambiguous. An earlier version of this test was
30
31
  originally developed under a clean-room procedure to ensure its independence from
31
32
  the implementation of a test for a similar rule in the Tenon tool.
@@ -0,0 +1,98 @@
1
+ /*
2
+ © 2023 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2025 Jonathan Robert Pool. All rights reserved.
4
+
5
+ MIT License
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
24
+ */
25
+
26
+ /*
27
+ allHidden
28
+ This test reports a page that is entirely or mainly hidden.
29
+ */
30
+
31
+ // FUNCTIONS
32
+
33
+ // Runs the test and returns the result.
34
+ exports.reporter = async page => {
35
+ // Get data on violations of the rule.
36
+ const violationData = await page.evaluate(() => {
37
+ // Get all candidates, i.e. the regions that should never be hidden.
38
+ const regions = {
39
+ html: {
40
+ element: document.documentElement,
41
+ severity: 3
42
+ },
43
+ body: {
44
+ element: document.body,
45
+ severity: 2
46
+ },
47
+ main: {
48
+ element: document.querySelector('main, [role=main]'),
49
+ severity: 1
50
+ }
51
+ };
52
+ let violationCounts = [0, 0, 0, 0];
53
+ const instances = [];
54
+ // For each candidate:
55
+ Object.keys(regions).forEach(regionName => {
56
+ const region = regions[regionName];
57
+ const {element, severity} = region;
58
+ console.log(`XXX ${regionName}`);
59
+ const tagName = regionName.toUpperCase();
60
+ // If it is not main and does not exist:
61
+ if (! element && regionName !== 'main') {
62
+ console.log(`XXX ${regionName} is not main anddoes not exist`);
63
+ // Increment the violation count.
64
+ violationCounts[3]++;
65
+ // Add an instance to the instances.
66
+ const what = `The ${regionName} region does not exist`;
67
+ // Add a summary instance to the instances.
68
+ instances.push(window.getInstance(null, 'allHidden', what, 1, 3, tagName));
69
+ }
70
+ // Otherwise, if it exists:
71
+ else if (element) {
72
+ console.log(`XXX ${regionName} exists`);
73
+ const styleDec = window.getComputedStyle(element);
74
+ const {display, visibility} = styleDec;
75
+ // If it is hidden:
76
+ if (display === 'none' || visibility === 'hidden' || element.ariaHidden === 'true') {
77
+ // Increment the violation count.
78
+ violationCounts[severity]++;
79
+ // Add an instance to the instances.
80
+ const what = `The ${regionName} region is hidden`;
81
+ // Add an instance to the instances.
82
+ instances.push(window.getInstance(element, 'allHidden', what, 1, severity, tagName));
83
+ }
84
+ }
85
+ });
86
+ return {
87
+ violationCounts,
88
+ instances
89
+ };
90
+ });
91
+ const {violationCounts, instances} = violationData;
92
+ // Return the result.
93
+ return {
94
+ data: {},
95
+ totals: violationCounts,
96
+ standardInstances: instances
97
+ };
98
+ };
@@ -1,5 +1,6 @@
1
1
  /*
2
2
  © 2023 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2025 Jonathan Robert Pool. All rights reserved.
3
4
 
4
5
  MIT License
5
6
 
@@ -26,52 +27,35 @@
26
27
  allHidden
27
28
  This test reports a page that is entirely or mainly hidden.
28
29
  */
30
+
31
+ // FUNCTIONS
32
+
33
+ // Runs the test and returns the result.
29
34
  exports.reporter = async page => {
30
- // Gets the hiddennesses of the document, body, and main region.
31
- const data = await page.evaluate(() => {
32
- // For each region:
33
- const {body} = document;
34
- const main = body && body.querySelector('main, [role=main]');
35
- const data = [];
36
- [document.documentElement, body, main].forEach(region => {
37
- if (! region) {
38
- data.push(null);
39
- }
40
- else if (region.hidden || region.ariaHidden) {
41
- data.push(true);
42
- }
43
- else {
44
- const styleDec = window.getComputedStyle(region);
45
- const {display, visibility} = styleDec;
46
- data.push(display === 'none' || visibility === 'hidden');
47
- }
48
- });
49
- return data;
50
- });
51
- // Get the severity totals.
52
- const totals = [0, data[2] ? 1 : 0, data[1] ? 1 : 0, data[0] ? 1 : 0];
53
- const standardInstances = [];
54
- data.forEach((isHidden, index) => {
55
- const region = [['Document', 'HTML'], ['Body', 'BODY'], ['Main region', 'MAIN']][index];
56
- if (isHidden) {
57
- standardInstances.push({
58
- ruleID: 'allHidden',
59
- what: `${region[0]} is hidden`,
60
- ordinalSeverity: 3 - index,
61
- tagName: region[1],
62
- id: '',
63
- location: {
64
- doc: '',
65
- type: '',
66
- spec: ''
67
- },
68
- excerpt: ''
69
- });
35
+ // Get a count of elements deemed visible by Playwright.
36
+ const visibleElementCount = await page.locator('body :visible').count();
37
+ // Get a violation count and an instance.
38
+ const violationData = await page.evaluate(visibleElementCount => {
39
+ let violationCount = 0;
40
+ const instances = [];
41
+ // If no element is visible:
42
+ if (! visibleElementCount) {
43
+ // Increment the violation count.
44
+ violationCount = 1;
45
+ const what = `The entire page body is hidden or empty`;
46
+ // Add a summary instance to the instances.
47
+ instances.push(window.getInstance(null, 'allHidden', what, 1, 3));
70
48
  }
71
- });
49
+ return {
50
+ violationCount,
51
+ instances
52
+ };
53
+ }, visibleElementCount);
54
+ const {violationCount, instances} = violationData;
55
+ // Return the result.
72
56
  return {
73
- data,
74
- totals,
75
- standardInstances
57
+ data: {},
58
+ totals: [0, 0, 0, violationCount],
59
+ standardInstances: instances
76
60
  };
77
61
  };
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool. All rights reserved.
3
+ © 2025 Juan S. Casado. All rights reserved.
4
4
 
5
5
  MIT License
6
6
 
package/testaro/bulk.js CHANGED
@@ -1,5 +1,6 @@
1
1
  /*
2
2
  © 2021–2023 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2025 Jonathan Robert Pool. All rights reserved.
3
4
 
4
5
  MIT License
5
6
 
@@ -32,37 +33,31 @@
32
33
  if the page is cluttered with content.
33
34
  */
34
35
  exports.reporter = async page => {
35
- const data = {};
36
- await page.waitForSelector('body', {timeout: 10000})
37
- .catch(error => {
38
- console.log(`ERROR (${error.message})`);
39
- data.prevented = true;
40
- data.error = 'ERROR: bulk timed out';
41
- return {result: data};
42
- });
43
- const visiblesLoc = await page.locator('body :visible');
44
- const visibleLocs = await visiblesLoc.all();
45
- data.visibleElements = visibleLocs.length;
46
- const severity = Math.min(4, Math.round(data.visibleElements / 400));
47
- const totals = [0, 0, 0, 0];
48
- if (severity) {
49
- totals[severity - 1] = 1;
50
- }
36
+ // Get a count of elements deemed visible by Playwright.
37
+ const visibleElementCount = await page.locator(':visible').count();
38
+ // Get totals and an instance.
39
+ const violationData = await page.evaluate(visibleElementCount => {
40
+ // Convert the count to a severity level, treating up to 400 as non-reportable.
41
+ const severity = Math.min(4, Math.round(visibleElementCount / 400)) - 1;
42
+ const totals = [0, 0, 0, 0];
43
+ const instances = [];
44
+ // If the severity is reportable:
45
+ if (severity > -1) {
46
+ totals[severity] = 1;
47
+ const what = `Page contains ${visibleElementCount} visible elements`;
48
+ // Create an instance reporting it.
49
+ instances.push(window.getInstance(document.documentElement, 'bulk', what, 1, severity));
50
+ }
51
+ return {
52
+ totals,
53
+ instances
54
+ };
55
+ }, visibleElementCount);
56
+ const {totals, instances} = violationData;
57
+ // Return the result.
51
58
  return {
52
- data,
59
+ data: {},
53
60
  totals,
54
- standardInstances: data.visibleElements < 200 ? [] : [{
55
- ruleID: 'bulk',
56
- what: 'Page contains a large number of visible elements',
57
- ordinalSeverity: severity - 1,
58
- tagName: 'HTML',
59
- id: '',
60
- location: {
61
- doc: '',
62
- type: '',
63
- spec: ''
64
- },
65
- excerpt: ''
66
- }]
61
+ standardInstances: instances
67
62
  };
68
63
  };
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool. All rights reserved.
3
+ © 2025 Juan S. Casado. All rights reserved.
4
4
 
5
5
  MIT License
6
6
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool. All rights reserved.
3
+ © 2025 Juan S. Casado. All rights reserved.
4
4
 
5
5
  MIT License
6
6
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool. All rights reserved.
3
+ © 2025 Juan S. Casado. All rights reserved.
4
4
 
5
5
  MIT License
6
6
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool. All rights reserved.
3
+ © 2025 Juan S. Casado. All rights reserved.
4
4
 
5
5
  MIT License
6
6
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool. All rights reserved.
3
+ © 2025 Juan S. Casado. All rights reserved.
4
4
 
5
5
  MIT License
6
6
 
package/testaro/phOnly.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool. All rights reserved.
3
+ © 2025 Juan S. Casado. All rights reserved.
4
4
 
5
5
  MIT License
6
6
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool. All rights reserved.
3
+ © 2025 Juan S. Casado. All rights reserved.
4
4
 
5
5
  MIT License
6
6
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool. All rights reserved.
3
+ © 2025 Juan S. Casado. All rights reserved.
4
4
 
5
5
  MIT License
6
6
 
package/tests/testaro.js CHANGED
@@ -448,9 +448,6 @@ const allRules = [
448
448
  defaultOn: false
449
449
  }
450
450
  ];
451
- const headedBrowser = process.env.HEADED_BROWSER === 'true';
452
- const debug = process.env.DEBUG === 'true';
453
- const waits = Number.parseInt(process.env.WAITS) || 0;
454
451
  const timeoutMultiplier = Number.parseFloat(process.env.TIMEOUT_MULTIPLIER) || 1;
455
452
 
456
453
  // ERROR HANDLER
@@ -607,7 +604,7 @@ exports.reporter = async (page, report, actIndex) => {
607
604
  // Add data about the test, including its prevention, to the result.
608
605
  const endTime = Date.now();
609
606
  testTimes.push([rule, Math.round((endTime - startTime) / 1000)]);
610
- data.rulePreventions.push(rule);
607
+ data.rulePreventions.push(ruleID);
611
608
  data.rulePreventionMessages[ruleID] = 'Timeout';
612
609
  result[ruleID].totals = [0, 0, 0, 0];
613
610
  result[ruleID].standardInstances = [];
@@ -29,7 +29,7 @@
29
29
  <meta name="description" content="tester">
30
30
  <meta name="viewport" content="width=device-width, initial-scale=1">
31
31
  </head>
32
- <body aria-hidden="true">
32
+ <body aria-hidden="true">
33
33
  <main>
34
34
  <h1>Page with an ARIA-hidden body</h1>
35
35
  <p>This page has an ARIA-hidden body.</p>
@@ -22,7 +22,7 @@
22
22
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
23
  SOFTWARE.
24
24
  -->
25
- <html lang="en-US" aria-hidden="true">
25
+ <html lang="en-US" aria-hidden="true">
26
26
  <head>
27
27
  <meta charset="utf-8">
28
28
  <title>Page with mixture of hiddenness</title>
@@ -1,6 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <!--
3
3
  © 2023 CVS Health and/or one of its affiliates. All rights reserved.
4
+ © 2025 Jonathan Robert Pool. All rights reserved.
4
5
 
5
6
  MIT License
6
7
 
@@ -30,5 +31,5 @@
30
31
  <meta name="viewport" content="width=device-width, initial-scale=1">
31
32
  </head>
32
33
  <h1>Page with no body</h1>
33
- <p>The content is direct children of the <code>html</code> element.</p>
34
+ <p>The content is direct children of the <code>html</code> element. However, the browser will supply the missing <code>body</code> element, allowing the page to pass the test.</p>
34
35
  </html>
@@ -1,6 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <!--
3
3
  © 2023 CVS Health and/or one of its affiliates. All rights reserved.
4
+ © 2025 Jonathan Robert Pool. All rights reserved.
4
5
 
5
6
  MIT License
6
7
 
@@ -31,6 +32,6 @@
31
32
  </head>
32
33
  <body>
33
34
  <h1>Page with no main landmark</h1>
34
- <p>This page has no main landmark.</p>
35
+ <p>This page has no main landmark. But the <code>body</code> element has visible descendants, so this page will pass the test.</p>
35
36
  </body>
36
37
  </html>
@@ -1,51 +0,0 @@
1
- /*
2
- reproInitScripts.js
3
- Minimal example to demonstrate an apparent Playwright bug reported as issue 38442
4
- (https://github.com/microsoft/playwright/issues/38442).
5
- */
6
-
7
- // save as e.g. reproInitScripts.js
8
- // run with: node reproInitScripts.js
9
-
10
- const {chromium} = require('playwright');
11
-
12
- (async () => {
13
-
14
- const browser = await chromium.launch({headless: true});
15
- const context = await browser.newContext();
16
-
17
- context.on('page', async page => {
18
- console.log('context.on("page") fired');
19
-
20
- // First init script
21
- await page.addInitScript(() => {
22
- window.helperOne = () => 'one';
23
- });
24
-
25
- // Second init script
26
- await page.addInitScript(() => {
27
- window.helperTwo = () => 'two';
28
- });
29
-
30
- // Third init script
31
- await page.addInitScript(() => {
32
- window.helperThree = () => 'three';
33
- });
34
-
35
- });
36
-
37
- const page = await context.newPage();
38
-
39
- await page.goto('https://example.com', {waitUntil: 'domcontentloaded'});
40
-
41
- const result = await page.evaluate(() => ({
42
- helperOne: typeof window.helperOne,
43
- helperTwo: typeof window.helperTwo,
44
- helperThree: typeof window.helperThree
45
- }));
46
-
47
- console.log('Helper types:', result);
48
-
49
- await browser.close();
50
-
51
- })();