testaro 21.0.0 → 22.0.0

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/README.md CHANGED
@@ -587,6 +587,10 @@ The `testaro` tool (like the `ibm` tool) has a `withItems` property. If you set
587
587
 
588
588
  Unlike any other tool, the `testaro` tools requires a `stopOnFail` property, which specifies whether a failure to conform to any rule (i.e. any value of `totals` other than `[0, 0, 0, 0]`) should terminate the execution of tests for the remaining rules.
589
589
 
590
+ Warnings in the `testaro/hover.js`, `testaro/motion.js`, and `procs/visChange.js` files advise you to avoid launching particular browser types for the performance of particular Testaro tests.
591
+
592
+ Several Testaro tests make use of the `init()` function in the `procs/testaro` module. That function samples elements if the population of elements to be tested is larger than 100. The purpose is to achieve reasonable performance. The sampling overweights elements near the beginning of a page, because of the tendency of that location to have important and atypical elements.
593
+
590
594
  You can add custom rules to the rules of any tool. Testaro provides a template, `data/template.js`, for the definition of a rule to be added. Once you have created a copy of the template with revisions, you can move the copy into the `testaro` directory and add an entry for your custom rule to the `evalRules` object in the `tests/testaro.js` file. Then your custom rule will act as a Testaro rule.
591
595
 
592
596
  ###### WAVE
package/data/template.js CHANGED
@@ -13,12 +13,12 @@ const {init, report} = require('../procs/testaro');
13
13
  // Runs the test and returns the result.
14
14
  exports.reporter = async (page, withItems) => {
15
15
  // Initialize the locators and result.
16
- const all = await init(page, 'body *');
16
+ const all = await init(page, 'body a');
17
17
  // For each locator:
18
18
  for (const loc of all.allLocs) {
19
19
  // Get whether its element violates the rule.
20
20
  const isBad = await loc.evaluate(el => {
21
- const isViolator = el.tagName.length > 300;
21
+ const isViolator = ! el.href;
22
22
  return isViolator;
23
23
  });
24
24
  // If it does:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testaro",
3
- "version": "21.0.0",
3
+ "version": "22.0.0",
4
4
  "description": "Run 920 web accessibility tests from 9 tools and get a standardized report",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/procs/sample.js CHANGED
@@ -10,7 +10,7 @@ exports.getSample = (population, sampleSize) => {
10
10
  const popSize = population.length;
11
11
  // If the sample is smaller than the population:
12
12
  if (sampleSize < popSize) {
13
- // Assign to each trigger a priority randomly decreasing with its index.
13
+ // Assign to each individual a priority randomly decreasing with its index.
14
14
  const WeightedPopulation = population.map((item, index) => {
15
15
  const weight = 1 + Math.sin(Math.PI * index / popSize + Math.PI / 2);
16
16
  const priority = weight * Math.random();
@@ -20,7 +20,7 @@ exports.getSample = (population, sampleSize) => {
20
20
  const sortedPopulation = WeightedPopulation.sort((a, b) => b[1] - a[1]);
21
21
  const sample = sortedPopulation.slice(0, sampleSize);
22
22
  const domOrderSample = sample.sort((a, b) => a[0] - b[0]);
23
- return domOrderSample.map(trigger => trigger[0]);
23
+ return domOrderSample.map(individual => individual[0]);
24
24
  }
25
25
  // Otherwise, i.e. if the sample is at least as large as the population:
26
26
  else {
package/procs/testaro.js CHANGED
@@ -2,18 +2,32 @@
2
2
 
3
3
  // ########## IMPORTS
4
4
 
5
+ // Module to sample a population.
6
+ const {getSample} = require('../procs/sample');
5
7
  // Module to get locator data.
6
8
  const {getLocatorData} = require('../procs/getLocatorData');
7
9
 
10
+ // ########## CONSTANTS
11
+
12
+ let sampleSize = 100;
13
+
8
14
  // ########## FUNCTIONS
9
15
 
10
16
  // Initializes locators and a result.
11
17
  exports.init = async (page, locAllSelector, options = {}) => {
12
18
  // Get locators for the specified elements.
13
- const locAll = page.locator(locAllSelector, options);
14
- const allLocs = await locAll.all();
19
+ const locPop = page.locator(locAllSelector, options);
20
+ const locPops = await locPop.all();
21
+ const populationSize = locPops.length;
22
+ sampleSize = Math.min(sampleSize, populationSize);
23
+ const locIndexes = getSample(locPops, sampleSize);
24
+ const allLocs = locIndexes.map(index => locPops[index]);
15
25
  const result = {
16
- data: {},
26
+ data: {
27
+ populationSize,
28
+ sampleSize,
29
+ populationRatio: sampleSize ? populationSize / sampleSize : null
30
+ },
17
31
  totals: [0, 0, 0, 0],
18
32
  standardInstances: []
19
33
  };
@@ -28,7 +42,7 @@ exports.init = async (page, locAllSelector, options = {}) => {
28
42
  // Populates a result.
29
43
  exports.report = async (withItems, all, ruleID, whats, ordinalSeverity, tagName = '') => {
30
44
  const {locs, result} = all;
31
- const {totals, standardInstances} = result;
45
+ const {data, totals, standardInstances} = result;
32
46
  // For each instance locator:
33
47
  for (const locItem of locs) {
34
48
  // Get data on its element.
@@ -42,7 +56,7 @@ exports.report = async (withItems, all, ruleID, whats, ordinalSeverity, tagName
42
56
  }
43
57
  const elData = await getLocatorData(loc);
44
58
  // Add to the totals.
45
- totals[ordinalSeverity]++;
59
+ totals[ordinalSeverity] += data.populationRatio;
46
60
  // If itemization is required:
47
61
  if (withItems) {
48
62
  // Add a standard instance to the result.
@@ -64,7 +78,7 @@ exports.report = async (withItems, all, ruleID, whats, ordinalSeverity, tagName
64
78
  ruleID,
65
79
  what: whats[1],
66
80
  ordinalSeverity,
67
- count: totals[ordinalSeverity],
81
+ count: Math.round(totals[ordinalSeverity]),
68
82
  tagName,
69
83
  id: '',
70
84
  location: {
@@ -23,7 +23,6 @@ const shoot = async (page, exclusion = null) => {
23
23
  timeout: 2000
24
24
  };
25
25
  if (exclusion) {
26
- const exclusionText = await exclusion.textContent();
27
26
  options.mask = [exclusion];
28
27
  }
29
28
  return await page.screenshot(options)
package/standardize.js CHANGED
@@ -400,6 +400,20 @@ const convert = (toolName, result, standardResult) => {
400
400
  console.log(`ERROR: Testaro rule ${rule} result has no standardInstances property`);
401
401
  }
402
402
  });
403
+ const preventionCount = result.preventions && result.preventions.length;
404
+ if (preventionCount) {
405
+ standardResult.totals[3] += preventionCount;
406
+ standardResult.instances.push({
407
+ ruleID: 'testPrevention',
408
+ what: 'Page prevented tests from being performed',
409
+ ordinalSeverity: 3,
410
+ count: preventionCount,
411
+ tagName: '',
412
+ id: '',
413
+ location: '',
414
+ excerpt: ''
415
+ });
416
+ }
403
417
  standardResult.totals = standardResult.totals.map(total => Math.round(total));
404
418
  }
405
419
  // wave
package/testaro/hover.js CHANGED
@@ -2,7 +2,7 @@
2
2
  hover
3
3
  This test reports unexpected impacts of hovering on the visible page. Impacts are measured by
4
4
  pixel changes outside the hovered element and by unhoverability.
5
-
5
+
6
6
  The elements that are subjected to hovering (called “triggers”) are the Playwright-visible
7
7
  elements that have 'A', 'BUTTON', or (if not with role=menuitem) 'LI' tag names or have
8
8
  'onmouseenter' or 'onmouseover' attributes.
@@ -24,8 +24,8 @@
24
24
  action on the location where the center of the element is, rather than some other element with a
25
25
  higher zIndex value in the same location being the target.
26
26
 
27
- WARNING: This test uses the Playwright page.screenshot method, which is not implemented for the
28
- firefox browser type.
27
+ WARNING: This test uses the procs/visChange module. See the warning in that module about browser
28
+ types.
29
29
  */
30
30
 
31
31
  // IMPORTS
@@ -54,7 +54,7 @@ exports.reporter = async (page, withItems, sampleSize = 20) => {
54
54
  const psRatio = Math.max(1, locsAll.length / sampleSize);
55
55
  // Get a sample of the triggers.
56
56
  const sampleIndexes = getSample(locsAll, sampleSize);
57
- const sample = locsAll.filter((loc, index) => sampleIndexes.includes(index));
57
+ const sample = sampleIndexes.map(index => locsAll[index]);
58
58
  // For each trigger in the sample:
59
59
  for (const loc of sample) {
60
60
  // Hover over it and get the fractional pixel change.
package/testaro/motion.js CHANGED
@@ -7,9 +7,9 @@
7
7
  test compares two screen shots of the viewport 2 seconds and 6 seconds after page load. It
8
8
  reports a rule violation if any pixels change. The larger the change fraction, the greater the
9
9
  ordinal severity.
10
-
11
- WARNING: This test uses the Playwright page.screenshot method, which is not implemented for the
12
- firefox browser type.
10
+
11
+ WARNING: This test uses the procs/visChange module. See the warning in that module about browser
12
+ types.
13
13
  */
14
14
 
15
15
  // IMPORTS
package/tests/testaro.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  testaro
3
- This test implements the Testaro evaluative ruleset for accessibility.
3
+ This test implements the Testaro evaluative rules.
4
4
  */
5
5
 
6
6
  // CONSTANTS
@@ -54,7 +54,7 @@ const etcRules = {
54
54
 
55
55
  // FUNCTIONS
56
56
 
57
- // Conducts and reports a Testaro test.
57
+ // Conducts and reports Testaro tests.
58
58
  exports.reporter = async (page, options) => {
59
59
  const {withItems, stopOnFail, args} = options;
60
60
  const argRules = args ? Object.keys(args) : null;
@@ -62,7 +62,8 @@ exports.reporter = async (page, options) => {
62
62
  // Initialize the data.
63
63
  const data = {
64
64
  rules: {},
65
- preventions: []
65
+ preventions: [],
66
+ testTimes: {}
66
67
  };
67
68
  // If the rule specification is valid:
68
69
  if (
@@ -74,6 +75,7 @@ exports.reporter = async (page, options) => {
74
75
  const realRules = rules[0] === 'y'
75
76
  ? rules.slice(1)
76
77
  : Object.keys(evalRules).filter(ruleID => ! rules.slice(1).includes(ruleID));
78
+ const testTimes = [];
77
79
  for (const rule of realRules) {
78
80
  // Initialize an argument array.
79
81
  const ruleArgs = [page, withItems];
@@ -90,9 +92,15 @@ exports.reporter = async (page, options) => {
90
92
  data.rules[rule].what = what;
91
93
  console.log(`>>>>>> ${rule} (${what})`);
92
94
  try {
95
+ const startTime = Date.now();
93
96
  const report = await require(`../testaro/${rule}`).reporter(... ruleArgs);
97
+ const endTime = Date.now();
98
+ testTimes.push([rule, Math.round((endTime - startTime) / 1000)]);
94
99
  Object.keys(report).forEach(key => {
95
100
  data.rules[rule][key] = report[key];
101
+ if (report.prevented) {
102
+ data.preventions.push(rule);
103
+ }
96
104
  });
97
105
  // If testing is to stop after a failure and the page failed the test:
98
106
  if (stopOnFail && report.totals.some(total => total)) {
@@ -107,6 +115,9 @@ exports.reporter = async (page, options) => {
107
115
  console.log(`ERROR: Test of testaro rule ${rule} prevented (${error.message})`);
108
116
  }
109
117
  }
118
+ testTimes.sort((a, b) => b[1] - a[1]).forEach(pair => {
119
+ data.testTimes[pair[0]] = pair[1];
120
+ });
110
121
  }
111
122
  // Otherwise, i.e. if the rule specification is invalid:
112
123
  else {