testaro 26.2.0 → 26.3.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.
@@ -0,0 +1,73 @@
1
+ # testaro
2
+
3
+ Ensemble testing for web accessibility
4
+
5
+ ## Contributing
6
+
7
+ Contributions to Testaro are welcome.
8
+
9
+ ### Testaro rule specification
10
+
11
+ Testaro integrates 8 accessibility testing tools, and one of them is Testaro itself. The Testaro tool has defined 43 rules, and the other 7 tools have defined about 600 rules in the aggregate. Contributing a new Testaro rule can add value if the new rule will not merely duplicate one of the existing rules. The issue classification (`tic…`) files in the `procs` directory of the [Testilo](https://www.npmjs.com/package/testilo) package can help in this determination.
12
+
13
+ If you determine that a new Testaro rule would be valuable, the first step is to specify it. This specification has the following parts:
14
+ 1. Adding an entry to the `evalRules` or `etcRules` object in the `tests/testaro.js` file.
15
+ 1. Adding a validation target directory to the `validation/tests/targets` directory.
16
+ 1. Adding at least one validation target to the target directory.
17
+ 1. Adding a validation job to the `validation/tests/jobs` directory.
18
+
19
+ ### Testaro rule validation
20
+
21
+ All Testaro rules have validators, as mentioned above.
22
+
23
+ The first step in creating a validator is to create at least one HTML file that will be tested against the new rule. A single `index.html` file may suffice. It should contain cases that will pass the test and cases that will fail the test. If appropriate, you can create multiple targets, such as `good.html` and `bad.html`.
24
+
25
+ The second step is to create a validation job. It launches a browser, navigates to a validation target, and conducts the test. It includes expectations about the results. Typically, the expectations relate to the standard instances included in the results. The `Tests/Expectations` section of the `README.md` file describes the syntax of expectations.
26
+
27
+ When a rule `xyz` has been defined and the `npm test xyz` statement is executed, the validation job will be run.
28
+
29
+ ### Rule creation
30
+
31
+ Once you have specified a new rule, you, or somebody else, can develop the rule in accord with your specification. Developing the rule consists of:
32
+ 1. Creating a rule-definition file.
33
+ 1. Adding the file to the `testaro` directory.
34
+ 1. Validating it.
35
+
36
+ For example, if the new rule has the ID `bigAlt`, it must succeed when the statement `npm test bigAlt` is executed.
37
+
38
+ ### Simple rules
39
+
40
+ Some existing Testaro rules are simple enough to be defined in JSON files. If a new rule is similarly simple, you can define it with a similar JSON file.
41
+
42
+ An example is the `titledEl` rule:
43
+
44
+ ```json
45
+ {
46
+ "ruleID": "titledEl",
47
+ "selector": "[title]:not(input, button, textarea, select, iframe):visible",
48
+ "complaints": {
49
+ "instance": "Ineligible element has a title attribute",
50
+ "summary": "Ineligible elements have title attributes"
51
+ },
52
+ "ordinalSeverity": 2,
53
+ "summaryTagName": ""
54
+ }
55
+ ```
56
+
57
+ To create a simple rule, you can copy one of the existing JSON files and replace the values with values that are appropriate for the new rule. Testaro uses the properties in the file as follows:
58
+ - Every element matching the selector is treated as a violator of the rule.
59
+ - Testaro describes violations with one of the `complaints` values. If the user has requested itemized results, the `instance` value is used; otherwise the `summary` value is used.
60
+ - Testaro assigns the `ordinalSeverity` value as the ordinal severity of each violation. This is a scale of integers, from 0 to 3.
61
+ - When Testaro itemizes results, it reports the tag name of violating elements. When it only summarizes results, it includes the `summaryTagName`. If every violating element necessarily has one particular tag name, then you should make that (e.g., `BUTTON`) the value of `summaryTagName`.
62
+
63
+ ### Complex rules
64
+
65
+ Your new rule may not be quite so simple. For example, it may need to examine the selected elements and identify only some of them as rule violators.
66
+
67
+ You will need to define such a rule in a JavaScript file, rather than a JSON file.
68
+
69
+ An example of such an existing rule is `filter`. The test for that rule initially selects all elements that descend from the `body` element (or a sample of 100 such elements in the page contains more than 100). Then it examines each selected element to determine whether it violates the rule. Specifically, it determines whether the element has a `filter` style property with a value other than `'none'`. It reports those violations.
70
+
71
+ To define such a rule, you can copy an existing rule file and replace parts of its code as needed. Examples include:
72
+ - `allSlanted`, `distortion`, `filter`, `miniText`, `zIndex`: violations based on element styles.
73
+ - `linkTitle`: violations based on attributes and text content.
package/README.md CHANGED
@@ -559,9 +559,9 @@ Thus, when the `rules` argument is omitted, QualWeb will test for all of the rul
559
559
 
560
560
  If you do not specify rules when using the `testaro` tool, Testaro will test for the rules listed in the `evalRules` object of the `tests/testaro.js` file.
561
561
 
562
- The `rules` property for `testaro` is an array whose first item is either `'y'` or `'n'` and whose remaining items are rule IDs. If `'y'`, then only the specified rules’ tests are performed. If `'n'`, then all the evaluative tests are performed, except for the specified rules.
562
+ The `rules` argument for a `testaro` test act is an array whose first item is either `'y'` or `'n'` and whose remaining items are rule IDs. If `'y'`, then only the specified rules’ tests are performed. If `'n'`, then all the evaluative tests are performed, **except** for the specified rules.
563
563
 
564
- The `testaro` tool (like the `ibm` tool) has a `withItems` property. If you set it to `false`, the `standardResult` object of `testaro` will contain an `instances` property with summaries that identify issues and instance counts. If you set it to `true`, some of the instances will be itemized.
564
+ The `testaro` tool (like the `ibm` tool) has a `withItems` property. If you set it to `false`, the `standardResult` object will contain an `instances` property with summaries that identify issues and instance counts. If you set it to `true`, some of the instances will be itemized.
565
565
 
566
566
  Unlike any other tool, the `testaro` tool 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.
567
567
 
@@ -569,7 +569,7 @@ Warnings in the `testaro/hover.js`, `testaro/motion.js`, and `procs/visChange.js
569
569
 
570
570
  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.
571
571
 
572
- 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.
572
+ 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. Some `testaro` rules are simple enough to be fully specified in JSON files. You can use any of those as a template if you want to create a sufficiently simple custom rule, namely a rule whose prohibited elements are all and only the elements matching a CSS selector. More details about rule creation are in the `CONTRIBUTING.md` file.
573
573
 
574
574
  ###### WAVE
575
575
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testaro",
3
- "version": "26.2.0",
3
+ "version": "26.3.0",
4
4
  "description": "Run 650 web accessibility tests from 8 tools and get a standardized report",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -15,7 +15,7 @@ const {init, report} = require('../procs/testaro');
15
15
  // Runs the test and returns the result.
16
16
  exports.reporter = async (page, withItems) => {
17
17
  // Initialize the locators and result.
18
- const all = await init(page, 'body *:not(style):not(script):not(svg)');
18
+ const all = await init(page, 'body *:not(style, script, svg)');
19
19
  // For each locator:
20
20
  for (const loc of all.allLocs) {
21
21
  // Get whether its element violates the rule.
@@ -15,7 +15,7 @@ const {init, report} = require('../procs/testaro');
15
15
  // Runs the test and returns the result.
16
16
  exports.reporter = async (page, withItems) => {
17
17
  // Initialize the locators and result.
18
- const all = await init(page, 'body *:not(style):not(script):not(svg)');
18
+ const all = await init(page, 'body *:not(style, script, svg)');
19
19
  // For each locator:
20
20
  for (const loc of all.allLocs) {
21
21
  // Get whether its element violates the rule.
@@ -130,9 +130,7 @@ exports.reporter = async (page, withItems, trialKeySpecs = []) => {
130
130
  // If they were obtained:
131
131
  if (extraData) {
132
132
  // Get locators for its descendant non-menu menu items.
133
- const miLocAll = menuLoc.locator(
134
- '[role=menuitem]:not([role=menu]), [role=menuitem]:not([role=menubar])'
135
- );
133
+ const miLocAll = menuLoc.locator('[role=menuitem]:not([role=menu], [role=menubar])');
136
134
  // Get which of them are direct descendants.
137
135
  const areDirect = await miLocAll.evaluateAll((els, menuID) => {
138
136
  return els.map(el => {
@@ -0,0 +1,10 @@
1
+ {
2
+ "ruleID": "captionLoc",
3
+ "selector": "*:not(table) > caption, caption:not(:first-child)",
4
+ "complaints": {
5
+ "instance": "Element is not the first child of a table element",
6
+ "summary": "caption elements are not the first children of table elements"
7
+ },
8
+ "ordinalSeverity": 3,
9
+ "summaryTagName": "CAPTION"
10
+ }
package/testaro/hover.js CHANGED
@@ -23,7 +23,7 @@ exports.reporter = async (page, withItems) => {
23
23
  );
24
24
  const allNonTrigger = await init(
25
25
  page,
26
- 'body *:not([aria-controls]):not([aria-expanded]):not([aria-haspopup]):not([onmouseenter]):not([onmouseover])'
26
+ 'body *:not([aria-controls], [aria-expanded], [aria-haspopup], [onmouseenter], [onmouseover])'
27
27
  );
28
28
  const populationSize
29
29
  = allTrigger.result.data.populationSize + allNonTrigger.result.data.populationSize;
@@ -0,0 +1,10 @@
1
+ {
2
+ "ruleID": "hr",
3
+ "selector": "body hr",
4
+ "complaints": {
5
+ "instance": "Element instead of styling is used for vertical segmentation",
6
+ "summary": "Elements instead of styling are used for vertical segmentation"
7
+ },
8
+ "ordinalSeverity": 0,
9
+ "summaryTagName": "HR"
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "ruleID": "linkExt",
3
+ "selector": "a[target=_blank]",
4
+ "complaints": {
5
+ "instance": "Link has a target=_blank attribute",
6
+ "summary": "Links have target=_blank attributes"
7
+ },
8
+ "ordinalSeverity": 0,
9
+ "summaryTagName": "A"
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "ruleID": "linkTo",
3
+ "selector": "a:not([href]):visible",
4
+ "complaints": {
5
+ "instance": "Link has no href attribute",
6
+ "summary": "Links are missing href attributes"
7
+ },
8
+ "ordinalSeverity": 2,
9
+ "summaryTagName": "A"
10
+ }
@@ -40,7 +40,7 @@ exports.reporter = async (page, withItems) => {
40
40
  // Return misuse.
41
41
  return true;
42
42
  }
43
- // Otherwise, if it contains a table-compatible el:
43
+ // Otherwise, if it contains a table-compatible element:
44
44
  else if (
45
45
  el.caption
46
46
  || ['grid', 'treegrid'].includes(role)
@@ -18,8 +18,8 @@ exports.reporter = async (page, withItems) => {
18
18
  const all = await init(page, 'body *', {has: page.locator('br + br')});
19
19
  // For each locator:
20
20
  for (const loc of all.allLocs) {
21
- // Get whether its element violates the rule.
22
21
  const isBad = await loc.evaluate(el => {
22
+ // Get whether it has 2 adjacent br elements with no non-space text between them.
23
23
  const childNodes = Array.from(el.childNodes);
24
24
  const realChildNodes = childNodes.filter(
25
25
  node => node.nodeType !== Node.TEXT_NODE || node.nodeValue.replace(/\s/g, '').length
@@ -0,0 +1,10 @@
1
+ {
2
+ "ruleID": "titledEl",
3
+ "selector": "[title]:not(input, button, textarea, select, iframe):visible",
4
+ "complaints": {
5
+ "instance": "Ineligible element has a title attribute",
6
+ "summary": "Ineligible elements have title attributes"
7
+ },
8
+ "ordinalSeverity": 2,
9
+ "summaryTagName": ""
10
+ }
package/tests/testaro.js CHANGED
@@ -3,6 +3,13 @@
3
3
  This test implements the Testaro evaluative rules.
4
4
  */
5
5
 
6
+ // ######## IMPORTS
7
+
8
+ // Module to perform common operations.
9
+ const {init, report} = require('../procs/testaro');
10
+ // Module to handle files.
11
+ const fs = require('fs/promises');
12
+
6
13
  // ######## CONSTANTS
7
14
 
8
15
  const evalRules = {
@@ -12,6 +19,7 @@ const evalRules = {
12
19
  autocomplete: 'name and email inputs without autocomplete attributes',
13
20
  bulk: 'large count of visible elements',
14
21
  buttonMenu: 'nonstandard keyboard navigation between items of button-controlled menus',
22
+ captionLoc: 'caption elements that are not first children of table elements',
15
23
  distortion: 'distorted text',
16
24
  docType: 'document without a doctype property',
17
25
  dupAtt: 'elements with duplicate attributes',
@@ -27,6 +35,7 @@ const evalRules = {
27
35
  hovInd: 'hover indication nonstandard',
28
36
  hr: 'hr element instead of styles used for vertical segmentation',
29
37
  labClash: 'labeling inconsistencies',
38
+ legendLoc: 'legend elements that are not first children of fieldset elements',
30
39
  lineHeight: 'text with a line height less than 1.5 times its font size',
31
40
  linkExt: 'links that automatically open new windows',
32
41
  linkAmb: 'links with identical texts but different destinations',
@@ -37,6 +46,8 @@ const evalRules = {
37
46
  motion: 'motion without user request',
38
47
  nonTable: 'table elements used for layout',
39
48
  opFoc: 'Operable elements that are not Tab-focusable',
49
+ optRoleSel: 'Non-option elements with option roles that have no aria-selected attributes',
50
+ phOnly: 'input elements with placeholders but no accessible names',
40
51
  pseudoP: 'adjacent br elements suspected of nonsemantically simulating p elements',
41
52
  radioSet: 'radio buttons not grouped into standard field sets',
42
53
  role: 'invalid and native-replacing explicit roles',
@@ -53,8 +64,26 @@ const etcRules = {
53
64
  title: 'page title',
54
65
  };
55
66
 
56
- // FUNCTIONS
67
+ // ######## FUNCTIONS
57
68
 
69
+ // Conducts a JSON-defined test.
70
+ const jsonTest = async (ruleID, ruleArgs) => {
71
+ const [page, withItems] = ruleArgs;
72
+ // Get the rule definition.
73
+ const ruleJSON = await fs.readFile(`${__dirname}/../testaro/${ruleID}.json`, 'utf8');
74
+ const ruleObj = JSON.parse(ruleJSON);
75
+ // Initialize the locators and result.
76
+ const all = await init(page, ruleObj.selector);
77
+ all.locs = all.allLocs;
78
+ // Populate and return the result.
79
+ const whats = [
80
+ ruleObj.complaints.instance,
81
+ ruleObj.complaints.summary
82
+ ];
83
+ return await report(
84
+ withItems, all, ruleObj.ruleID, whats, ruleObj.ordinalSeverity, ruleObj.summaryTagName
85
+ );
86
+ };
58
87
  // Conducts and reports Testaro tests.
59
88
  exports.reporter = async (page, options) => {
60
89
  const {withItems, stopOnFail, args} = options;
@@ -80,42 +109,55 @@ exports.reporter = async (page, options) => {
80
109
  for (const rule of calledRules) {
81
110
  // Initialize an argument array.
82
111
  const ruleArgs = [page, withItems];
83
- // If the rule has extra arguments:
84
- if (argRules && argRules.includes(rule)) {
85
- // Add them to the argument array.
86
- ruleArgs.push(... args[rule]);
87
- }
88
- // Test the page.
89
- const what = evalRules[rule] || etcRules[rule];
90
- if (! data.rules[rule]) {
91
- data.rules[rule] = {};
92
- }
93
- data.rules[rule].what = what;
94
- console.log(`>>>>>> ${rule} (${what})`);
95
- try {
96
- const startTime = Date.now();
97
- const ruleReport = await require(`../testaro/${rule}`).reporter(... ruleArgs);
98
- // Add data from the test to the result.
99
- const endTime = Date.now();
100
- testTimes.push([rule, Math.round((endTime - startTime) / 1000)]);
101
- Object.keys(ruleReport).forEach(key => {
102
- data.rules[rule][key] = ruleReport[key];
103
- });
104
- data.rules[rule].totals = data.rules[rule].totals.map(total => Math.round(total));
105
- if (ruleReport.prevented) {
106
- data.preventions.push(rule);
112
+ // If the rule is defined with JavaScript or JSON but not both:
113
+ const ruleFileNames = await fs.readdir(`${__dirname}/../testaro`);
114
+ const isJS = ruleFileNames.includes(`${rule}.js`);
115
+ const isJSON = ruleFileNames.includes(`${rule}.json`);
116
+ if ((isJS || isJSON) && ! (isJS && isJSON)) {
117
+ // If with JavaScript and it has extra arguments:
118
+ if (isJS && argRules && argRules.includes(rule)) {
119
+ // Add them to the argument array.
120
+ ruleArgs.push(... args[rule]);
121
+ }
122
+ // Test the page.
123
+ const what = evalRules[rule] || etcRules[rule];
124
+ if (! data.rules[rule]) {
125
+ data.rules[rule] = {};
107
126
  }
108
- // If testing is to stop after a failure and the page failed the test:
109
- if (stopOnFail && ruleReport.totals.some(total => total)) {
110
- // Stop testing.
111
- break;
127
+ data.rules[rule].what = what;
128
+ console.log(`>>>>>> ${rule} (${what})`);
129
+ try {
130
+ const startTime = Date.now();
131
+ const ruleReport = isJS
132
+ ? await require(`../testaro/${rule}`).reporter(... ruleArgs)
133
+ : await jsonTest(rule, ruleArgs);
134
+ // Add data from the test to the result.
135
+ const endTime = Date.now();
136
+ testTimes.push([rule, Math.round((endTime - startTime) / 1000)]);
137
+ Object.keys(ruleReport).forEach(key => {
138
+ data.rules[rule][key] = ruleReport[key];
139
+ });
140
+ data.rules[rule].totals = data.rules[rule].totals.map(total => Math.round(total));
141
+ if (ruleReport.prevented) {
142
+ data.preventions.push(rule);
143
+ }
144
+ // If testing is to stop after a failure and the page failed the test:
145
+ if (stopOnFail && ruleReport.totals.some(total => total)) {
146
+ // Stop testing.
147
+ break;
148
+ }
149
+ }
150
+ // If an error is thrown by the test:
151
+ catch(error) {
152
+ // Report this.
153
+ data.preventions.push(rule);
154
+ console.log(`ERROR: Test of testaro rule ${rule} prevented (${error.message})`);
112
155
  }
113
156
  }
114
- // If an error is thrown by the test:
115
- catch(error) {
157
+ // Otherwise, i.e. if the rule is undefined or doubly defined:
158
+ else {
116
159
  // Report this.
117
- data.preventions.push(rule);
118
- console.log(`ERROR: Test of testaro rule ${rule} prevented (${error.message})`);
160
+ console.log(`ERROR: Rule ${rule} not validly defined`);
119
161
  }
120
162
  }
121
163
  testTimes.sort((a, b) => b[1] - a[1]).forEach(pair => {
@@ -0,0 +1,140 @@
1
+ {
2
+ "id": "captionLoc",
3
+ "what": "validation of captionLoc test",
4
+ "strict": true,
5
+ "timeLimit": 20,
6
+ "acts": [
7
+ {
8
+ "type": "launch",
9
+ "which": "chromium",
10
+ "url": "file://validation/tests/targets/captionLoc/index.html",
11
+ "what": "page with standard and nonstandard caption locations"
12
+ },
13
+ {
14
+ "type": "test",
15
+ "which": "testaro",
16
+ "withItems": true,
17
+ "stopOnFail": true,
18
+ "expect": [
19
+ [
20
+ "standardResult.totals.3",
21
+ "=",
22
+ 2
23
+ ],
24
+ [
25
+ "standardResult.totals.1",
26
+ "=",
27
+ 0
28
+ ],
29
+ [
30
+ "standardResult.instances.0.ruleID",
31
+ "=",
32
+ "captionLoc"
33
+ ],
34
+ [
35
+ "standardResult.instances.0.what",
36
+ "=",
37
+ "Element is not the first child of a table element"
38
+ ],
39
+ [
40
+ "standardResult.instances.0.ordinalSeverity",
41
+ "=",
42
+ 3
43
+ ],
44
+ [
45
+ "standardResult.instances.0.tagName",
46
+ "=",
47
+ "CAPTION"
48
+ ],
49
+ [
50
+ "standardResult.instances.0.location.doc",
51
+ "=",
52
+ "dom"
53
+ ],
54
+ [
55
+ "standardResult.instances.0.location.type",
56
+ "=",
57
+ "box"
58
+ ],
59
+ [
60
+ "standardResult.instances.0.location.spec.width",
61
+ ">",
62
+ 0
63
+ ],
64
+ [
65
+ "standardResult.instances.0.excerpt",
66
+ "=",
67
+ "Office personnel"
68
+ ],
69
+ [
70
+ "standardResult.instances.1.excerpt",
71
+ "=",
72
+ "Farm personnel"
73
+ ]
74
+ ],
75
+ "rules": [
76
+ "y",
77
+ "captionLoc"
78
+ ]
79
+ },
80
+ {
81
+ "type": "test",
82
+ "which": "testaro",
83
+ "withItems": false,
84
+ "stopOnFail": true,
85
+ "expect": [
86
+ [
87
+ "standardResult.totals.3",
88
+ "=",
89
+ 2
90
+ ],
91
+ [
92
+ "standardResult.totals.1",
93
+ "=",
94
+ 0
95
+ ],
96
+ [
97
+ "standardResult.instances.length",
98
+ "=",
99
+ 1
100
+ ],
101
+ [
102
+ "standardResult.instances.0.ruleID",
103
+ "=",
104
+ "captionLoc"
105
+ ],
106
+ [
107
+ "standardResult.instances.0.what",
108
+ "=",
109
+ "caption elements are not the first children of table elements"
110
+ ],
111
+ [
112
+ "standardResult.instances.0.ordinalSeverity",
113
+ "=",
114
+ 3
115
+ ],
116
+ [
117
+ "standardResult.instances.0.count",
118
+ "=",
119
+ 2
120
+ ],
121
+ [
122
+ "standardResult.instances.0.tagName",
123
+ "=",
124
+ "CAPTION"
125
+ ]
126
+ ],
127
+ "rules": [
128
+ "y",
129
+ "captionLoc"
130
+ ]
131
+ }
132
+ ],
133
+ "sources": {
134
+ "script": "",
135
+ "host": {},
136
+ "requester": ""
137
+ },
138
+ "creationTime": "2013-05-28T12:00:00",
139
+ "timeStamp": "00000"
140
+ }
@@ -0,0 +1,146 @@
1
+ {
2
+ "id": "legendLoc",
3
+ "what": "validation of legendLoc test",
4
+ "strict": true,
5
+ "timeLimit": 20,
6
+ "acts": [
7
+ {
8
+ "type": "launch",
9
+ "which": "chromium",
10
+ "url": "file://validation/tests/targets/legendLoc/index.html",
11
+ "what": "page with standard and nonstandard legend locations"
12
+ },
13
+ {
14
+ "type": "test",
15
+ "which": "testaro",
16
+ "what": "legendLoc",
17
+ "withItems": true,
18
+ "stopOnFail": true,
19
+ "expect": [
20
+ [
21
+ "standardResult.totals.3",
22
+ "=",
23
+ 2
24
+ ],
25
+ [
26
+ "standardResult.totals.1",
27
+ "=",
28
+ 0
29
+ ],
30
+ [
31
+ "standardResult.instances.0.ruleID",
32
+ "=",
33
+ "legendLoc"
34
+ ],
35
+ [
36
+ "standardResult.instances.0.what",
37
+ "=",
38
+ "Element is not the first child of a fieldset element"
39
+ ],
40
+ [
41
+ "standardResult.instances.0.ordinalSeverity",
42
+ "=",
43
+ 3
44
+ ],
45
+ [
46
+ "standardResult.instances.0.tagName",
47
+ "=",
48
+ "LEGEND"
49
+ ],
50
+ [
51
+ "standardResult.instances.0.location.doc",
52
+ "=",
53
+ "dom"
54
+ ],
55
+ [
56
+ "standardResult.instances.0.location.type",
57
+ "=",
58
+ "box"
59
+ ],
60
+ [
61
+ "standardResult.instances.0.location.spec.width",
62
+ ">",
63
+ 0
64
+ ],
65
+ [
66
+ "standardResult.instances.0.excerpt",
67
+ "=",
68
+ "Choose a food"
69
+ ],
70
+ [
71
+ "standardResult.instances.1.ruleID",
72
+ "=",
73
+ "legendLoc"
74
+ ],
75
+ [
76
+ "standardResult.instances.1.excerpt",
77
+ "=",
78
+ "Choose a gas"
79
+ ]
80
+ ],
81
+ "rules": [
82
+ "y",
83
+ "legendLoc"
84
+ ]
85
+ },
86
+ {
87
+ "type": "test",
88
+ "which": "testaro",
89
+ "withItems": false,
90
+ "stopOnFail": true,
91
+ "expect": [
92
+ [
93
+ "standardResult.totals.3",
94
+ "=",
95
+ 2
96
+ ],
97
+ [
98
+ "standardResult.totals.1",
99
+ "=",
100
+ 0
101
+ ],
102
+ [
103
+ "standardResult.instances.length",
104
+ "=",
105
+ 1
106
+ ],
107
+ [
108
+ "standardResult.instances.0.ruleID",
109
+ "=",
110
+ "legendLoc"
111
+ ],
112
+ [
113
+ "standardResult.instances.0.what",
114
+ "=",
115
+ "legend elements are not the first children of fieldset elements"
116
+ ],
117
+ [
118
+ "standardResult.instances.0.ordinalSeverity",
119
+ "=",
120
+ 3
121
+ ],
122
+ [
123
+ "standardResult.instances.0.count",
124
+ "=",
125
+ 2
126
+ ],
127
+ [
128
+ "standardResult.instances.0.tagName",
129
+ "=",
130
+ "LEGEND"
131
+ ]
132
+ ],
133
+ "rules": [
134
+ "y",
135
+ "legendLoc"
136
+ ]
137
+ }
138
+ ],
139
+ "sources": {
140
+ "script": "",
141
+ "host": {},
142
+ "requester": ""
143
+ },
144
+ "creationTime": "2013-05-28T12:00:00",
145
+ "timeStamp": "00000"
146
+ }