testaro 12.2.7 → 12.4.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 +7 -6
- package/actSpecs.js +12 -0
- package/dupAtt/temp.html +19 -0
- package/package.json +2 -1
- package/run.js +10 -6
- package/tests/attVal.js +2 -2
- package/tests/autocomplete.js +75 -0
- package/tests/dupAtt.js +54 -0
- package/validation/tests/jobs/allHidden.json +1 -1
- package/validation/tests/jobs/attVal.json +60 -0
- package/validation/tests/jobs/autocomplete.json +51 -0
- package/validation/tests/jobs/bulk.json +1 -1
- package/validation/tests/jobs/docType.json +1 -1
- package/validation/tests/jobs/dupAtt.json +51 -0
- package/validation/tests/jobs/elements.json +1 -1
- package/validation/tests/jobs/embAc.json +1 -1
- package/validation/tests/jobs/filter.json +1 -1
- package/validation/tests/jobs/focAll.json +1 -1
- package/validation/tests/jobs/focInd.json +1 -1
- package/validation/tests/jobs/focOp.json +1 -1
- package/validation/tests/jobs/focVis.json +1 -1
- package/validation/tests/jobs/hover.json +1 -1
- package/validation/tests/jobs/labClash.json +1 -1
- package/validation/tests/jobs/linkTo.json +1 -1
- package/validation/tests/jobs/linkUl.json +1 -1
- package/validation/tests/jobs/menuNav.json +1 -1
- package/validation/tests/jobs/miniText.json +1 -1
- package/validation/tests/jobs/motion.json +1 -1
- package/validation/tests/jobs/nonTable.json +1 -1
- package/validation/tests/jobs/radioSet.json +1 -1
- package/validation/tests/jobs/role.json +1 -1
- package/validation/tests/jobs/styleDiff.json +1 -1
- package/validation/tests/jobs/tabNav.json +1 -1
- package/validation/tests/jobs/textNodes.json +1 -1
- package/validation/tests/jobs/title.json +1 -1
- package/validation/tests/jobs/titledEl.json +1 -1
- package/validation/tests/jobs/zIndex.json +1 -1
- package/validation/tests/targets/attVal/bad.html +15 -0
- package/validation/tests/targets/attVal/good.html +15 -0
- package/validation/tests/targets/autocomplete/bad.html +20 -0
- package/validation/tests/targets/autocomplete/good.html +22 -0
- package/validation/tests/targets/dupAtt/bad.html +19 -0
- package/validation/tests/targets/dupAtt/good.html +16 -0
package/README.md
CHANGED
|
@@ -31,7 +31,9 @@ Testaro uses:
|
|
|
31
31
|
- [Playwright](https://playwright.dev/) to launch browsers, perform user actions in them, and perform tests
|
|
32
32
|
- [pixelmatch](https://www.npmjs.com/package/pixelmatch) to measure motion
|
|
33
33
|
|
|
34
|
-
Testaro includes some of its own accessibility tests.
|
|
34
|
+
Testaro includes some of its own accessibility tests. Some of them are derived from tests performed by the [BBC Accessibility Standards Checker](https://github.com/bbc/bbc-a11y).
|
|
35
|
+
|
|
36
|
+
In addition, Testaro performs tests of these tools:
|
|
35
37
|
- [accessibility-checker](https://www.npmjs.com/package/accessibility-checker) (the IBM Equal Access Accessibility Checker)
|
|
36
38
|
- [alfa](https://alfa.siteimprove.com/) (Siteimprove alfa)
|
|
37
39
|
- [axe-playwright](https://www.npmjs.com/package/axe-playwright) (Deque Axe-core)
|
|
@@ -42,8 +44,6 @@ Testaro includes some of its own accessibility tests. In addition, it performs t
|
|
|
42
44
|
- [Tenon](https://tenon.io/documentation/what-tenon-tests.php) (Level Access)
|
|
43
45
|
- [WAVE API](https://wave.webaim.org/api/) (WebAIM WAVE)
|
|
44
46
|
|
|
45
|
-
Some of the Testaro tests are derived from tests performed by the [BBC Accessibility Standards Checker](https://github.com/bbc/bbc-a11y).
|
|
46
|
-
|
|
47
47
|
As of this version, the counts of tests of the tools referenced above were:
|
|
48
48
|
- Alfa: 103
|
|
49
49
|
- Axe-core: 138
|
|
@@ -54,9 +54,8 @@ As of this version, the counts of tests of the tools referenced above were:
|
|
|
54
54
|
- QualWeb core: 121
|
|
55
55
|
- Tenon: 180
|
|
56
56
|
- WAVE: 110
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
- grand total: 1351
|
|
57
|
+
- Testaro: 29
|
|
58
|
+
- total: 1356
|
|
60
59
|
|
|
61
60
|
## Quasi-tests
|
|
62
61
|
|
|
@@ -537,6 +536,8 @@ The third item in each array, if there are 3 items in the array, is the criterio
|
|
|
537
536
|
|
|
538
537
|
A typical use for an `expect` property is checking the correctness of a Testaro test. Thus, the validation jobs in the `validation/tests/jobs` directory all contain `test` acts with `expect` properties. See the “Validation” section below.
|
|
539
538
|
|
|
539
|
+
When a `test` act has an `expect` property, the result for that act has an `expectations` property reporting whether the expectations were satisfied. The value of `expectations` is an array of objects, one object per expectation. Each object includes a `property` property identifying the expectation, and a `passed` property with `true` or `false` value reporting whether the expectation was satisfied. If applicable, it also has other properties identifying what was expected and what was actually reported.
|
|
540
|
+
|
|
540
541
|
## Execution
|
|
541
542
|
|
|
542
543
|
### Introduction
|
package/actSpecs.js
CHANGED
|
@@ -168,6 +168,12 @@ exports.actSpecs = {
|
|
|
168
168
|
withItems: [true, 'boolean', '', 'itemize']
|
|
169
169
|
}
|
|
170
170
|
],
|
|
171
|
+
autocomplete: [
|
|
172
|
+
'Perform an autocomplete test',
|
|
173
|
+
{
|
|
174
|
+
withItems: [true, 'boolean', '', 'itemize']
|
|
175
|
+
}
|
|
176
|
+
],
|
|
171
177
|
axe: [
|
|
172
178
|
'Perform an Axe test',
|
|
173
179
|
{
|
|
@@ -181,6 +187,12 @@ exports.actSpecs = {
|
|
|
181
187
|
rules: [false, 'array', 'areNumbers', 'rule numbers (e.g., 25), if not all']
|
|
182
188
|
}
|
|
183
189
|
],
|
|
190
|
+
dupAtt: [
|
|
191
|
+
'Perform a dupAtt test',
|
|
192
|
+
{
|
|
193
|
+
withItems: [true, 'boolean', '', 'itemize']
|
|
194
|
+
}
|
|
195
|
+
],
|
|
184
196
|
elements: [
|
|
185
197
|
'Perform an elements test',
|
|
186
198
|
{
|
package/dupAtt/temp.html
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en-US">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Test page</title>
|
|
6
|
+
<meta name="description" content="tester">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<main>
|
|
11
|
+
<h1>Test page</h1>
|
|
12
|
+
<p><button id="violator" width="4rem" width="10rem">Submit</button></p>
|
|
13
|
+
<p
|
|
14
|
+
aria-label="large"
|
|
15
|
+
aria-label="small"
|
|
16
|
+
>A paragraph</p>
|
|
17
|
+
</main>
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testaro",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.4.0",
|
|
4
4
|
"description": "Automation of accessibility testing",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"accessibility-checker": "*",
|
|
36
36
|
"axe-playwright": "*",
|
|
37
37
|
"dotenv": "*",
|
|
38
|
+
"node-fetch": "*",
|
|
38
39
|
"pixelmatch": "*",
|
|
39
40
|
"playwright": "*"
|
|
40
41
|
},
|
package/run.js
CHANGED
|
@@ -34,10 +34,12 @@ const tests = {
|
|
|
34
34
|
alfa: 'alfa',
|
|
35
35
|
allHidden: 'page that is entirely or mostly hidden',
|
|
36
36
|
attVal: 'elements with attributes having illicit values',
|
|
37
|
+
autocomplete: 'name and email inputs without autocomplete attributes',
|
|
37
38
|
axe: 'Axe',
|
|
38
39
|
bulk: 'count of visible elements',
|
|
39
40
|
continuum: 'Level Access Continuum, community edition',
|
|
40
41
|
docType: 'document without a doctype property',
|
|
42
|
+
dupAtt: 'elements with duplicate attributes',
|
|
41
43
|
elements: 'data on specified elements',
|
|
42
44
|
embAc: 'active elements embedded in links or buttons',
|
|
43
45
|
filter: 'filter styles on elements',
|
|
@@ -264,8 +266,9 @@ const isValidReport = report => {
|
|
|
264
266
|
if (! acts[1].which || typeof acts[1].which !== 'string' || ! isURL(acts[1].which)) {
|
|
265
267
|
return 'Second act which not a URL';
|
|
266
268
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
+
const invalidAct = acts.find(act => ! isValidAct(act));
|
|
270
|
+
if (invalidAct) {
|
|
271
|
+
return `Invalid act:\n${JSON.stringify(invalidAct, null, 2)}`;
|
|
269
272
|
}
|
|
270
273
|
if (! sources || typeof sources !== 'object') {
|
|
271
274
|
return 'Bad report sources';
|
|
@@ -496,7 +499,7 @@ const isTrue = (object, specs) => {
|
|
|
496
499
|
}
|
|
497
500
|
// Otherwise, i.e. if the expectation is of a property value:
|
|
498
501
|
else if (specs.length === 3) {
|
|
499
|
-
//
|
|
502
|
+
// Return whether the expectation was fulfilled.
|
|
500
503
|
const relation = specs[1];
|
|
501
504
|
const criterion = specs[2];
|
|
502
505
|
let satisfied;
|
|
@@ -516,16 +519,17 @@ const isTrue = (object, specs) => {
|
|
|
516
519
|
satisfied = actual !== criterion;
|
|
517
520
|
}
|
|
518
521
|
else if (relation === 'i') {
|
|
519
|
-
satisfied = actual.includes(criterion);
|
|
522
|
+
satisfied = typeof actual === 'string' && actual.includes(criterion);
|
|
520
523
|
}
|
|
521
524
|
else if (relation === '!i') {
|
|
522
|
-
satisfied = ! actual.includes(criterion);
|
|
525
|
+
satisfied = typeof actual === 'string' && ! actual.includes(criterion);
|
|
523
526
|
}
|
|
524
527
|
return [actual, satisfied];
|
|
525
528
|
}
|
|
526
529
|
// Otherwise, i.e. if the specifications are invalid:
|
|
527
530
|
else {
|
|
528
|
-
//
|
|
531
|
+
// Return this.
|
|
532
|
+
return [null, false];
|
|
529
533
|
}
|
|
530
534
|
};
|
|
531
535
|
// Adds a wait error result to an act.
|
package/tests/attVal.js
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
exports.reporter = async (page, attributeName, areLicit, values, withItems) => {
|
|
6
6
|
// Identify the elements that have the specified attribute with illicit values.
|
|
7
7
|
const badAttributeData = await page.evaluate(
|
|
8
|
-
args => {
|
|
8
|
+
async args => {
|
|
9
9
|
const attributeName = args[0];
|
|
10
10
|
// Whether the values are the licit or the illicit ones.
|
|
11
11
|
const areLicit = args[1];
|
|
12
12
|
const values = args[2];
|
|
13
13
|
// Returns the text of an element.
|
|
14
|
-
const textOf =
|
|
14
|
+
const textOf = (element, limit) => {
|
|
15
15
|
let text = element.textContent;
|
|
16
16
|
text = text.trim() || element.innerHTML;
|
|
17
17
|
return text.replace(/\s+/sg, ' ').replace(/<>&/g, '').slice(0, limit);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/*
|
|
2
|
+
autocomplete
|
|
3
|
+
This test reports failures to equip name and email inputs with correct autocomplete attributes.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ########## IMPORTS
|
|
7
|
+
|
|
8
|
+
// Returns text associated with an element.
|
|
9
|
+
const {allText} = require('../procs/allText');
|
|
10
|
+
|
|
11
|
+
// ########## FUNCTIONS
|
|
12
|
+
|
|
13
|
+
// Adds a failure, if any, to the data.
|
|
14
|
+
const addFailure = async (withItems, input, inputText, autocomplete, data) => {
|
|
15
|
+
// If it does not have the required autocomplete attribute:
|
|
16
|
+
const autoValue = await input.getAttribute('autocomplete');
|
|
17
|
+
if (autoValue !== autocomplete) {
|
|
18
|
+
// Add this to the total.
|
|
19
|
+
data.total++;
|
|
20
|
+
// If itemization is required:
|
|
21
|
+
if (withItems) {
|
|
22
|
+
// Add the item to the data.
|
|
23
|
+
data.items.push([autocomplete, inputText.slice(0, 100)]);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
// Reports failures.
|
|
28
|
+
exports.reporter = async (page, withItems) => {
|
|
29
|
+
const data = {total: 0};
|
|
30
|
+
if (withItems) {
|
|
31
|
+
data.items = [];
|
|
32
|
+
}
|
|
33
|
+
// Identify the inputs.
|
|
34
|
+
const inputs = await page.$$('input');
|
|
35
|
+
// If there are any:
|
|
36
|
+
if (inputs.length) {
|
|
37
|
+
// For each one:
|
|
38
|
+
for (const input of inputs) {
|
|
39
|
+
const inputText = await allText(page, input);
|
|
40
|
+
// If it is a text input:
|
|
41
|
+
const inputType = await input.getAttribute('type');
|
|
42
|
+
if (inputType === 'text' || ! inputType) {
|
|
43
|
+
const inputTextLC = inputText.toLowerCase();
|
|
44
|
+
// If it requests a given name:
|
|
45
|
+
if (
|
|
46
|
+
inputTextLC === 'first'
|
|
47
|
+
|| ['first name', 'given name'].some(phrase => inputTextLC.includes(phrase))
|
|
48
|
+
) {
|
|
49
|
+
// Add any failure to the data.
|
|
50
|
+
await addFailure(withItems, input, inputText, 'given-name', data);
|
|
51
|
+
}
|
|
52
|
+
// Otherwise, if it requests a family name:
|
|
53
|
+
else if (
|
|
54
|
+
inputTextLC === 'last'
|
|
55
|
+
|| ['last name', 'family name'].some(phrase => inputTextLC.includes(phrase))
|
|
56
|
+
) {
|
|
57
|
+
// Add any failure to the data.
|
|
58
|
+
await addFailure(withItems, input, inputText, 'family-name', data);
|
|
59
|
+
}
|
|
60
|
+
// Otherwise, if it requests an email address:
|
|
61
|
+
else if (inputTextLC.includes('email')) {
|
|
62
|
+
// Add any failure to the data.
|
|
63
|
+
await addFailure(withItems, input, inputText, 'email', data);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Otherwise, if it is an email input:
|
|
67
|
+
else if (inputType === 'email') {
|
|
68
|
+
// Add any failure to the data.
|
|
69
|
+
await addFailure(withItems, input, inputText, 'email', data);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Return the data.
|
|
74
|
+
return {result: data};
|
|
75
|
+
};
|
package/tests/dupAtt.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
dupAtt.js
|
|
3
|
+
This test reports duplicate attributes in the source of a document.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ########## IMPORTS
|
|
7
|
+
|
|
8
|
+
// Module to make HTTP requests.
|
|
9
|
+
const fetch = require('node-fetch');
|
|
10
|
+
// Module to process files.
|
|
11
|
+
const fs = require('fs/promises');
|
|
12
|
+
|
|
13
|
+
// ########## FUNCTIONS
|
|
14
|
+
|
|
15
|
+
// Reports failures.
|
|
16
|
+
exports.reporter = async (page, withItems) => {
|
|
17
|
+
// Initialize the data.
|
|
18
|
+
const data = {total: 0};
|
|
19
|
+
if (withItems) {
|
|
20
|
+
data.items = [];
|
|
21
|
+
}
|
|
22
|
+
// Get the page.
|
|
23
|
+
const url = page.url();
|
|
24
|
+
const scheme = url.replace(/:.+/, '');
|
|
25
|
+
let rawPage;
|
|
26
|
+
if (scheme === 'file') {
|
|
27
|
+
const filePath = url.slice(7);
|
|
28
|
+
rawPage = await fs.readFile(filePath, 'utf8');
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
rawPage = await fetch(url);
|
|
32
|
+
}
|
|
33
|
+
// Extract its elements, in a uniform format.
|
|
34
|
+
const elements = rawPage
|
|
35
|
+
.match(/<[^/<>]+>/g)
|
|
36
|
+
.map(element => element.slice(1, -1).trim().replace(/\s*=\s*/g, '='))
|
|
37
|
+
.map(element => element.replace(/\s+/g, ' '));
|
|
38
|
+
// For each element:
|
|
39
|
+
elements.forEach(element => {
|
|
40
|
+
// Identify its attributes.
|
|
41
|
+
const attributes = element.split(' ').slice(1).map(attVal => attVal.replace(/=.+/, ''));
|
|
42
|
+
// If any is duplicated:
|
|
43
|
+
const attSet = new Set(attributes);
|
|
44
|
+
if (attSet.size < attributes.length) {
|
|
45
|
+
// Add this to the data.
|
|
46
|
+
data.total++;
|
|
47
|
+
if (withItems) {
|
|
48
|
+
data.items.push(element);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
// Return the data.
|
|
53
|
+
return {result: data};
|
|
54
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "attVal",
|
|
3
|
+
"what": "validation of attVal test",
|
|
4
|
+
"strict": true,
|
|
5
|
+
"timeLimit": 20,
|
|
6
|
+
"acts": [
|
|
7
|
+
{
|
|
8
|
+
"type": "launch",
|
|
9
|
+
"which": "chromium",
|
|
10
|
+
"what": "usual browser"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"type": "url",
|
|
14
|
+
"which": "__targets__/attVal/good.html",
|
|
15
|
+
"what": "page with permitted attribute values"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"type": "test",
|
|
19
|
+
"which": "attVal",
|
|
20
|
+
"attributeName": "lang",
|
|
21
|
+
"areLicit": true,
|
|
22
|
+
"values": ["en-US", "de-CH"],
|
|
23
|
+
"what": "attribute values",
|
|
24
|
+
"withItems": true,
|
|
25
|
+
"expect": [
|
|
26
|
+
["total", "=", 0],
|
|
27
|
+
["items.1"]
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"type": "url",
|
|
32
|
+
"which": "__targets__/attVal/bad.html",
|
|
33
|
+
"what": "page with permitted attribute values"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"type": "test",
|
|
37
|
+
"which": "attVal",
|
|
38
|
+
"attributeName": "lang",
|
|
39
|
+
"areLicit": false,
|
|
40
|
+
"values": ["en", "de"],
|
|
41
|
+
"what": "attribute values",
|
|
42
|
+
"withItems": true,
|
|
43
|
+
"expect": [
|
|
44
|
+
["total", "=", 2],
|
|
45
|
+
["items.0.tagName", "=", "HTML"],
|
|
46
|
+
["items.0.attributeValue", "=", "en"],
|
|
47
|
+
["items.1.tagName", "=", "SPAN"],
|
|
48
|
+
["items.1.attributeValue", "=", "de"],
|
|
49
|
+
["items.1.textStart", "i", "Veloparkieren"]
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"sources": {
|
|
54
|
+
"script": "",
|
|
55
|
+
"host": {},
|
|
56
|
+
"requester": ""
|
|
57
|
+
},
|
|
58
|
+
"creationTime": "2023-04-19T12:34:00",
|
|
59
|
+
"timeStamp": "00000"
|
|
60
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "autocomplete",
|
|
3
|
+
"what": "validation of autocomplete test",
|
|
4
|
+
"strict": true,
|
|
5
|
+
"timeLimit": 20,
|
|
6
|
+
"acts": [
|
|
7
|
+
{
|
|
8
|
+
"type": "launch",
|
|
9
|
+
"which": "chromium",
|
|
10
|
+
"what": "usual browser"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"type": "url",
|
|
14
|
+
"which": "__targets__/autocomplete/good.html",
|
|
15
|
+
"what": "page with correct autocomplete attributes"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"type": "test",
|
|
19
|
+
"which": "autocomplete",
|
|
20
|
+
"what": "autocomplete attributes for name and email",
|
|
21
|
+
"withItems": true,
|
|
22
|
+
"expect": [
|
|
23
|
+
["total", "=", 0],
|
|
24
|
+
["items.1"]
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"type": "url",
|
|
29
|
+
"which": "__targets__/autocomplete/bad.html",
|
|
30
|
+
"what": "page with incorrect autocomplete attributes"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"type": "test",
|
|
34
|
+
"which": "autocomplete",
|
|
35
|
+
"what": "autocomplete attributes for name and email",
|
|
36
|
+
"withItems": true,
|
|
37
|
+
"expect": [
|
|
38
|
+
["total", "=", 4],
|
|
39
|
+
["items.1.0", "=", "family-name"],
|
|
40
|
+
["items.2.1", "=", "Your email address"]
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"sources": {
|
|
45
|
+
"script": "",
|
|
46
|
+
"host": {},
|
|
47
|
+
"requester": ""
|
|
48
|
+
},
|
|
49
|
+
"creationTime": "2023-04-16T21:06:00",
|
|
50
|
+
"timeStamp": "00000"
|
|
51
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "dupAtt",
|
|
3
|
+
"what": "validation of dupAtt test",
|
|
4
|
+
"strict": true,
|
|
5
|
+
"timeLimit": 20,
|
|
6
|
+
"acts": [
|
|
7
|
+
{
|
|
8
|
+
"type": "launch",
|
|
9
|
+
"which": "chromium",
|
|
10
|
+
"what": "usual browser"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"type": "url",
|
|
14
|
+
"which": "__targets__/dupAtt/good.html",
|
|
15
|
+
"what": "page without duplicate attributes"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"type": "test",
|
|
19
|
+
"which": "dupAtt",
|
|
20
|
+
"what": "elements with duplicate attributes",
|
|
21
|
+
"withItems": true,
|
|
22
|
+
"expect": [
|
|
23
|
+
["total", "=", 0],
|
|
24
|
+
["items.1"]
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"type": "url",
|
|
29
|
+
"which": "__targets__/dupAtt/bad.html",
|
|
30
|
+
"what": "page with duplicate attributes"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"type": "test",
|
|
34
|
+
"which": "dupAtt",
|
|
35
|
+
"what": "elements with duplicate attributes",
|
|
36
|
+
"withItems": true,
|
|
37
|
+
"expect": [
|
|
38
|
+
["total", "=", 2],
|
|
39
|
+
["items.0", "=", "p class=\"narrow\" id=\"daParagraph\" class=\"wide\""],
|
|
40
|
+
["items.1", "i", "large"]
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"sources": {
|
|
45
|
+
"script": "",
|
|
46
|
+
"host": {},
|
|
47
|
+
"requester": ""
|
|
48
|
+
},
|
|
49
|
+
"creationTime": "2023-04-18T11:02:00",
|
|
50
|
+
"timeStamp": "00000"
|
|
51
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Page with illicit attribute values</title>
|
|
6
|
+
<meta name="description" content="tester">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<main>
|
|
11
|
+
<h1>Page with illicit attribute values</h1>
|
|
12
|
+
<p><q><span lang="de">Veloparkieren nicht gestattet</span></q> is Swiss German, so it should be marked up with the <code>de-ch</code> language tag.</p>
|
|
13
|
+
</main>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en-US">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Page with licit attribute values</title>
|
|
6
|
+
<meta name="description" content="tester">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<main>
|
|
11
|
+
<h1>Page with illicit attribute values</h1>
|
|
12
|
+
<p><q><span>lang="de-CH">Veloparkieren nicht gestattet</span></q> is Swiss German, so it is marked up with the <code>de-CH</code> language tag.</p>
|
|
13
|
+
</main>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en-US">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Page with incorrect autocomplete attributes</title>
|
|
6
|
+
<meta name="description" content="tester">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<main>
|
|
11
|
+
<h1>Page with incorrect autocomplete attributes</h1>
|
|
12
|
+
<p><label>Your first name <input width="40rem" autocomplete="first-name"></label></p>
|
|
13
|
+
<p><label>Your last name <input type="text" width="40rem"></label></p>
|
|
14
|
+
<p><label>Your email address <input width="40rem"></label></p>
|
|
15
|
+
<p><label>
|
|
16
|
+
Your email address again <input type="email" width="40rem" autocomplete="email-address">
|
|
17
|
+
</label></p>
|
|
18
|
+
</main>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en-US">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Page with correct autocomplete attributes</title>
|
|
6
|
+
<meta name="description" content="tester">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<main>
|
|
11
|
+
<h1>Page with correct autocomplete attributes</h1>
|
|
12
|
+
<p><label>Your first name <input width="40rem" autocomplete="given-name"></label></p>
|
|
13
|
+
<p><label>
|
|
14
|
+
Your last name <input type="text" width="40rem" autocomplete="family-name">
|
|
15
|
+
</label></p>
|
|
16
|
+
<p><label>Your email address <input width="40rem" autocomplete="email"></label></p>
|
|
17
|
+
<p><label>
|
|
18
|
+
Your email address again <input type="email" width="40rem" autocomplete="email">
|
|
19
|
+
</label></p>
|
|
20
|
+
</main>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en-US">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Page with duplicate attributes</title>
|
|
6
|
+
<meta name="description" content="tester">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<main>
|
|
11
|
+
<h1>Page with duplicate attributes</h1>
|
|
12
|
+
<p class="narrow" id="daParagraph" class="wide">Submit</p>
|
|
13
|
+
<p><button
|
|
14
|
+
aria-label="large"
|
|
15
|
+
aria-label="small"
|
|
16
|
+
>A paragraph</button></p>
|
|
17
|
+
</main>
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en-US">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Page without duplicate attributes</title>
|
|
6
|
+
<meta name="description" content="tester">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<main>
|
|
11
|
+
<h1>Page without duplicate attributes</h1>
|
|
12
|
+
<p class="narrow" id="okParagraph" lang="es-US">Buscar</p>
|
|
13
|
+
<p><button id="p" aria-label="A body of text">A paragraph</button></p>
|
|
14
|
+
</main>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|