testaro 17.0.0 → 18.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 +7 -51
- package/actSpecs.js +0 -14
- package/package.json +1 -1
- package/run.js +1 -120
- package/standardize.js +0 -34
- package/testaro/hovInd.js +100 -74
- package/tests/testaro.js +0 -1
- package/validation/tests/jobs/allHidden.json +0 -2
- package/validation/tests/jobs/attVal.json +0 -3
- package/validation/tests/jobs/bulk.json +0 -2
- package/validation/tests/jobs/docType.json +0 -2
- package/validation/tests/jobs/dupAtt.json +0 -3
- package/validation/tests/jobs/elements.json +0 -3
- package/validation/tests/jobs/embAc.json +0 -3
- package/validation/tests/jobs/filter.json +0 -3
- package/validation/tests/jobs/focAll.json +0 -3
- package/validation/tests/jobs/focInd.json +0 -2
- package/validation/tests/jobs/focOp.json +2 -5
- package/validation/tests/jobs/focVis.json +0 -1
- package/validation/tests/jobs/labClash.json +0 -2
- package/validation/tests/jobs/linkTo.json +0 -2
- package/validation/tests/jobs/linkUl.json +0 -3
- package/validation/tests/jobs/miniText.json +0 -2
- package/validation/tests/jobs/motion.json +0 -1
- package/validation/tests/jobs/nonTable.json +0 -1
- package/validation/tests/jobs/radioSet.json +0 -2
- package/validation/tests/jobs/role.json +0 -2
- package/validation/tests/jobs/styleDiff.json +0 -2
- package/validation/tests/jobs/tabNav.json +0 -3
- package/validation/tests/jobs/textNodes.json +0 -5
- package/validation/tests/jobs/title.json +0 -2
- package/validation/tests/jobs/titledEl.json +0 -2
- package/validation/tests/jobs/zIndex.json +0 -3
- package/tests/tenon.js +0 -144
package/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Ensemble testing for web accessibility
|
|
4
4
|
|
|
5
|
+
## Notice: tool change
|
|
6
|
+
|
|
7
|
+
As of this version 18.0.0, the 10 tools integrated by Testaro have been changed to 9 tools. One tool, Tenon, was withdrawn by its owner on 2023-08-06, so it was removed from Testaro.
|
|
8
|
+
|
|
5
9
|
## Introduction
|
|
6
10
|
|
|
7
11
|
Testaro is an application for automated web accessibility testing.
|
|
@@ -48,13 +52,10 @@ Testaro performs tests of these tools:
|
|
|
48
52
|
- [HTML CodeSniffer](https://www.npmjs.com/package/html_codesniffer) (Squiz Labs)
|
|
49
53
|
- [Nu Html Checker](https://github.com/validator/validator) (World Wide Web Consortium)
|
|
50
54
|
- [QualWeb core](https://www.npmjs.com/package/@qualweb/core) (University of Lisbon)
|
|
51
|
-
- [Tenon](https://tenon.io/documentation/what-tenon-tests.php) (Tenon)
|
|
52
55
|
- [Testaro](https://www.npmjs.com/package/testaro) (Testaro)
|
|
53
56
|
- [WAVE API](https://wave.webaim.org/api/) (WebAIM)
|
|
54
57
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Level Access has acquired Tenon and has announced that it will retire Tenon in August 2023. Thereafter one should expect that Testaro will report failures to perform Tenon tests. In anticipation of the retirement of Tenon, tests have been added to Testaro that approximate those Tenon tests that, empirically, have been found to report issues not reported by other tools.
|
|
58
|
+
Some of the rules tested by Testaro are based on rules of the [BBC Accessibility Standards Checker](https://github.com/bbc/bbc-a11y) and of Tenon, a service that existed until August 2023.
|
|
58
59
|
|
|
59
60
|
## Rules
|
|
60
61
|
|
|
@@ -71,7 +72,6 @@ When you ask Testaro to run tests of a tool, you may specify a subset of the rul
|
|
|
71
72
|
These tools always perform a fixed set of tests, and Testaro disregards irrelevant results when you specify a set of rules:
|
|
72
73
|
- ibm
|
|
73
74
|
- nuVal
|
|
74
|
-
- tenon
|
|
75
75
|
- wave
|
|
76
76
|
|
|
77
77
|
## Job data
|
|
@@ -126,7 +126,7 @@ To run Testaro after installation, provide the environment variables described b
|
|
|
126
126
|
|
|
127
127
|
## Payment
|
|
128
128
|
|
|
129
|
-
All of the tests that Testaro can perform are free of cost, except those performed by the
|
|
129
|
+
All of the tests that Testaro can perform are free of cost, except those performed by the WAVE tool. The owner of that tool gives new registrants a free allowance of credits before it becomes necessary to pay for use of the API of the tool. The required environment variable for authentication and payment is described below under “Environment variables”.
|
|
130
130
|
|
|
131
131
|
## Process objects
|
|
132
132
|
|
|
@@ -402,7 +402,7 @@ That means that a test act (i.e. an act with a `type` property having the value
|
|
|
402
402
|
|
|
403
403
|
If a particular test act either must have or may have any other properties, those properties are specified in the `tests` property in `actSpecs.js`.
|
|
404
404
|
|
|
405
|
-
When you include a `rules` property, you limit the tests of the tool that are performed or reported. For some tools (`alfa`, `axe`, `continuum`, `htmlcs`, `qualWeb`, and `testaro`), only the specified tests are performed. Other tools (`ibm`, `nuVal`,
|
|
405
|
+
When you include a `rules` property, you limit the tests of the tool that are performed or reported. For some tools (`alfa`, `axe`, `continuum`, `htmlcs`, `qualWeb`, and `testaro`), only the specified tests are performed. Other tools (`ibm`, `nuVal`, and `wave`) do not allow such a limitation, so, for those tools, all tests are performed but results are reported from only the specified tests.
|
|
406
406
|
|
|
407
407
|
The `nuVal` and `testaro` tools require specific formats for the `rules` property. Those formats are described below in the sections about those tools.
|
|
408
408
|
|
|
@@ -555,48 +555,6 @@ Its `rules` argument is **not** an array of rule IDs, but instead is an array of
|
|
|
555
555
|
|
|
556
556
|
The `qualWeb` tool performs the ACT rules, WCAG Techniques, and best-practices tests of QualWeb. Only failures and warnings are included in the report. The EARL report of QualWeb is not generated, because it is equivalent to the report of the ACT rules tests.
|
|
557
557
|
|
|
558
|
-
###### Tenon
|
|
559
|
-
|
|
560
|
-
Most tools require only one act, but the `tenon` tool requires two acts:
|
|
561
|
-
- An act of type `tenonRequest`.
|
|
562
|
-
- An act of type `test` with `tenon` as the value of `which`.
|
|
563
|
-
|
|
564
|
-
Example:
|
|
565
|
-
|
|
566
|
-
```json
|
|
567
|
-
{
|
|
568
|
-
"type": "tenonRequest",
|
|
569
|
-
"id": "a",
|
|
570
|
-
"withNewContent": true,
|
|
571
|
-
"what": "Tenon API version 2 test request"
|
|
572
|
-
}
|
|
573
|
-
```
|
|
574
|
-
|
|
575
|
-
followed by
|
|
576
|
-
|
|
577
|
-
```json
|
|
578
|
-
{
|
|
579
|
-
"type": "test",
|
|
580
|
-
"which": "tenon",
|
|
581
|
-
"id": "a",
|
|
582
|
-
"what": "Tenon API version 2 result retrieval"
|
|
583
|
-
}
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
The reason is that the Tenon API operates asynchronously. You ask it to perform a test, and it puts your request into a queue. To learn whether Tenon has completed your test, you make a status request. You can continue making status requests until Tenon replies that your test has been completed. Then you submit a request for the test result, and Tenon replies with the result. (As of May 2022, however, status requests were observed to misreport still-running tests as completed. The `tenon` test act works around that by requesting only the result and using the response to determine whether the tests have been completed.)
|
|
587
|
-
|
|
588
|
-
Tenon says that tests are typically completed in 3 to 6 seconds but that the latency can be longer, depending on demand.
|
|
589
|
-
|
|
590
|
-
Therefore, you can include a `tenonRequest` act early in your job, and a `tenon` test act late in your job. Tenon will move your request through its queue while Testaro is processing your job. When Testaro reaches your `tenon` test act, Tenon will most likely have completed your test. If not, the `tenon` test will wait and then make a second request before giving up.
|
|
591
|
-
|
|
592
|
-
Thus, a `tenon` test act actually does not perform any test; it merely collects the result. The page that was active when the `tenonRequest` act was performed is the one that Tenon tests.
|
|
593
|
-
|
|
594
|
-
In case you want to perform the Tenon tests more than once in the same job, you can do so. Just give each pair of acts a distinct `id` property, so each `tenon` test act will request the correct result.
|
|
595
|
-
|
|
596
|
-
Tenon recommends giving it a public URL rather than giving it the content of a page, if possible. So, it is best to give the `withNewContent` property of the `tenonRequest` act the value `true`, unless the page is not public.
|
|
597
|
-
|
|
598
|
-
If a `tenon` test act is included in a job, environment variables named `TENON_USER` and `TENON_PASSWORD` must exist, with your Tenon username and password, respectively, as their values. These could be obtained from [Tenon](https://tenon.io/documentation/overview) until Tenon was closed to new subscribers in or about October 2022.
|
|
599
|
-
|
|
600
558
|
###### Testaro
|
|
601
559
|
|
|
602
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.
|
|
@@ -805,8 +763,6 @@ You may store environment variables in an untracked `.env` file if you wish, and
|
|
|
805
763
|
|
|
806
764
|
```conf
|
|
807
765
|
URL_INJECT=yes
|
|
808
|
-
TENON_USER=you@yourdomain.tld
|
|
809
|
-
TENON_PASSWORD=yourTenonPassword
|
|
810
766
|
WAVE_KEY=yourwavekey
|
|
811
767
|
PROTOCOL=https
|
|
812
768
|
JOB_URL=yourserver.tld/job
|
package/actSpecs.js
CHANGED
|
@@ -113,14 +113,6 @@ exports.actSpecs = {
|
|
|
113
113
|
what: [false, 'string', 'hasLength', 'comment']
|
|
114
114
|
}
|
|
115
115
|
],
|
|
116
|
-
tenonRequest: [
|
|
117
|
-
'Request a Tenon test',
|
|
118
|
-
{
|
|
119
|
-
id: [true, 'string', 'hasLength', 'ID for this test instance'],
|
|
120
|
-
withNewContent: [true, 'boolean', '', 'true: use a URL; false: use page content'],
|
|
121
|
-
what: [false, 'string', 'hasLength', 'comment']
|
|
122
|
-
}
|
|
123
|
-
],
|
|
124
116
|
test: [
|
|
125
117
|
'Perform tests of a tool',
|
|
126
118
|
{
|
|
@@ -174,12 +166,6 @@ exports.actSpecs = {
|
|
|
174
166
|
withNewContent: [true, 'boolean', '', 'whether to use a URL instead of page content']
|
|
175
167
|
}
|
|
176
168
|
],
|
|
177
|
-
tenon: [
|
|
178
|
-
'Perform Tenon tests',
|
|
179
|
-
{
|
|
180
|
-
id: [true, 'string', 'hasLength', 'ID of the requested test instance']
|
|
181
|
-
}
|
|
182
|
-
],
|
|
183
169
|
testaro: [
|
|
184
170
|
'Perform Testaro tests',
|
|
185
171
|
{
|
package/package.json
CHANGED
package/run.js
CHANGED
|
@@ -42,7 +42,6 @@ const tests = {
|
|
|
42
42
|
ibm: 'IBM Accessibility Checker',
|
|
43
43
|
nuVal: 'Nu Html Checker',
|
|
44
44
|
qualWeb: 'QualWeb',
|
|
45
|
-
tenon: 'Tenon',
|
|
46
45
|
testaro: 'Testaro',
|
|
47
46
|
wave: 'WAVE',
|
|
48
47
|
};
|
|
@@ -54,11 +53,6 @@ const browserTypeNames = {
|
|
|
54
53
|
};
|
|
55
54
|
// Items that may be waited for.
|
|
56
55
|
const waitables = ['url', 'title', 'body'];
|
|
57
|
-
// Tenon data.
|
|
58
|
-
const tenonData = {
|
|
59
|
-
accessToken: '',
|
|
60
|
-
requestIDs: {}
|
|
61
|
-
};
|
|
62
56
|
// Strings in log messages indicating errors.
|
|
63
57
|
const errorWords = [
|
|
64
58
|
'but not used',
|
|
@@ -864,119 +858,6 @@ const doActs = async (report, actIndex, page) => {
|
|
|
864
858
|
success: true
|
|
865
859
|
};
|
|
866
860
|
}
|
|
867
|
-
// Otherwise, if the act is a tenon request:
|
|
868
|
-
else if (act.type === 'tenonRequest') {
|
|
869
|
-
const {id, withNewContent} = act;
|
|
870
|
-
const https = require('https');
|
|
871
|
-
// If a Tenon access token has not yet been obtained:
|
|
872
|
-
if (! tenonData.accessToken) {
|
|
873
|
-
// Authenticate with the Tenon API.
|
|
874
|
-
const authData = await new Promise(resolve => {
|
|
875
|
-
const request = https.request(
|
|
876
|
-
{
|
|
877
|
-
host: 'tenon.io',
|
|
878
|
-
path: '/api/v2/auth',
|
|
879
|
-
port: 443,
|
|
880
|
-
protocol: 'https:',
|
|
881
|
-
method: 'POST',
|
|
882
|
-
headers: {
|
|
883
|
-
'Content-Type': 'application/json',
|
|
884
|
-
'Cache-Control': 'no-cache'
|
|
885
|
-
}
|
|
886
|
-
},
|
|
887
|
-
response => {
|
|
888
|
-
let responseData = '';
|
|
889
|
-
response.on('data', chunk => {
|
|
890
|
-
responseData += chunk;
|
|
891
|
-
});
|
|
892
|
-
response.on('end', () => {
|
|
893
|
-
try {
|
|
894
|
-
const responseJSON = JSON.parse(responseData);
|
|
895
|
-
return resolve(responseJSON);
|
|
896
|
-
}
|
|
897
|
-
catch(error) {
|
|
898
|
-
return resolve({
|
|
899
|
-
error: 'Tenon did not return JSON authentication data.',
|
|
900
|
-
responseData
|
|
901
|
-
});
|
|
902
|
-
}
|
|
903
|
-
});
|
|
904
|
-
}
|
|
905
|
-
);
|
|
906
|
-
const tenonUser = process.env.TENON_USER;
|
|
907
|
-
const tenonPassword = process.env.TENON_PASSWORD;
|
|
908
|
-
const postData = JSON.stringify({
|
|
909
|
-
username: tenonUser,
|
|
910
|
-
password: tenonPassword
|
|
911
|
-
});
|
|
912
|
-
request.write(postData);
|
|
913
|
-
request.end();
|
|
914
|
-
});
|
|
915
|
-
// If the authentication succeeded:
|
|
916
|
-
if (authData.access_token) {
|
|
917
|
-
// Record the access token.
|
|
918
|
-
tenonData.accessToken = authData.access_token;
|
|
919
|
-
}
|
|
920
|
-
// Otherwise, i.e. if the authentication failed:
|
|
921
|
-
else {
|
|
922
|
-
console.log('ERROR: tenon authentication failed');
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
// If a Tenon access token exists:
|
|
926
|
-
if (tenonData.accessToken) {
|
|
927
|
-
// Request a Tenon test of the page and get a response ID.
|
|
928
|
-
const option = {};
|
|
929
|
-
// If Tenon is to be given the URL and not the content of the page:
|
|
930
|
-
if (withNewContent) {
|
|
931
|
-
// Specify this.
|
|
932
|
-
option.url = page.url();
|
|
933
|
-
}
|
|
934
|
-
// Otherwise, i.e. if Tenon is to be given the page content:
|
|
935
|
-
else {
|
|
936
|
-
// Specify this.
|
|
937
|
-
option.src = await page.content();
|
|
938
|
-
}
|
|
939
|
-
// Request a Tenon test and get a response ID.
|
|
940
|
-
const responseID = await new Promise(resolve => {
|
|
941
|
-
const request = https.request(
|
|
942
|
-
{
|
|
943
|
-
host: 'tenon.io',
|
|
944
|
-
path: '/api/v2/',
|
|
945
|
-
port: 443,
|
|
946
|
-
protocol: 'https:',
|
|
947
|
-
method: 'POST',
|
|
948
|
-
headers: {
|
|
949
|
-
'Content-Type': 'application/json',
|
|
950
|
-
'Cache-Control': 'no-cache',
|
|
951
|
-
Authorization: `Bearer ${tenonData.accessToken}`
|
|
952
|
-
}
|
|
953
|
-
},
|
|
954
|
-
response => {
|
|
955
|
-
let resultJSON = '';
|
|
956
|
-
response.on('data', chunk => {
|
|
957
|
-
resultJSON += chunk;
|
|
958
|
-
});
|
|
959
|
-
// When the data arrive, return them as an object.
|
|
960
|
-
response.on('end', () => {
|
|
961
|
-
try {
|
|
962
|
-
const result = JSON.parse(resultJSON);
|
|
963
|
-
resolve(result.data.responseID || '');
|
|
964
|
-
}
|
|
965
|
-
catch (error) {
|
|
966
|
-
console.log('ERROR: Tenon did not return JSON.');
|
|
967
|
-
resolve('');
|
|
968
|
-
}
|
|
969
|
-
});
|
|
970
|
-
}
|
|
971
|
-
);
|
|
972
|
-
const postData = JSON.stringify(option);
|
|
973
|
-
request.write(postData);
|
|
974
|
-
request.end();
|
|
975
|
-
});
|
|
976
|
-
// Record the response ID.
|
|
977
|
-
tenonData.requestIDs[id] = responseID || '';
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
861
|
// Otherwise, if the act performs the tests of a tool:
|
|
981
862
|
else if (act.type === 'test') {
|
|
982
863
|
// Add a description of the test to the act.
|
|
@@ -997,7 +878,7 @@ const doActs = async (report, actIndex, page) => {
|
|
|
997
878
|
}
|
|
998
879
|
};
|
|
999
880
|
try {
|
|
1000
|
-
const args = [
|
|
881
|
+
const args = [page, options];
|
|
1001
882
|
testReport = await require(`./tests/${act.which}`).reporter(... args);
|
|
1002
883
|
testReport.result.success = true;
|
|
1003
884
|
}
|
package/standardize.js
CHANGED
|
@@ -381,40 +381,6 @@ const convert = (toolName, result, standardResult) => {
|
|
|
381
381
|
doQualWeb(result, standardResult, 'best-practices');
|
|
382
382
|
}
|
|
383
383
|
}
|
|
384
|
-
// tenon
|
|
385
|
-
else if (toolName === 'tenon' && result.data && result.data.resultSet) {
|
|
386
|
-
result.data.resultSet.forEach(item => {
|
|
387
|
-
const identifiers = getIdentifiers(
|
|
388
|
-
item.errorSnippet.replace(/</g, '<').replace(/>/g, '>')
|
|
389
|
-
);
|
|
390
|
-
if (! identifiers[0] && item.xpath) {
|
|
391
|
-
const tagNameArray = item.xpath.match(/^.+\/([^/[]+)/);
|
|
392
|
-
if (tagNameArray && tagNameArray.length === 2) {
|
|
393
|
-
identifiers[0] = tagNameArray[1].toUpperCase();
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
const instance = {
|
|
397
|
-
ruleID: item.tID ? item.tID.toString() : '',
|
|
398
|
-
what: item.errorTitle || '',
|
|
399
|
-
ordinalSeverity: Math.min(
|
|
400
|
-
3, Math.max(0, Math.round((item.certainty || 0) * (item.priority || 0) / 3333))
|
|
401
|
-
),
|
|
402
|
-
tagName: identifiers[0],
|
|
403
|
-
id: identifiers[1],
|
|
404
|
-
location: {
|
|
405
|
-
doc: 'dom',
|
|
406
|
-
type: 'xpath',
|
|
407
|
-
spec: item.xpath || ''
|
|
408
|
-
},
|
|
409
|
-
excerpt: cap(item.errorSnippet || '').replace(/</g, '<').replace(/>/g, '>')
|
|
410
|
-
};
|
|
411
|
-
standardResult.instances.push(instance);
|
|
412
|
-
});
|
|
413
|
-
standardResult.totals = [0, 0, 0, 0];
|
|
414
|
-
standardResult.instances.forEach(instance => {
|
|
415
|
-
standardResult.totals[instance.ordinalSeverity]++;
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
384
|
// testaro
|
|
419
385
|
else if (toolName === 'testaro') {
|
|
420
386
|
const rules = result.rules ? Object.keys(result.rules) : [];
|
package/testaro/hovInd.js
CHANGED
|
@@ -140,88 +140,114 @@ exports.reporter = async (page, withItems, sampleSize = 20) => {
|
|
|
140
140
|
for (const loc of sample) {
|
|
141
141
|
// Get its style properties.
|
|
142
142
|
const preStyles = await getHoverStyles(loc);
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
143
|
+
// Try to focus it.
|
|
144
|
+
try {
|
|
145
|
+
await loc.focus({timeout: 500});
|
|
146
|
+
// If focusing succeeds, get its style properties.
|
|
147
|
+
const focStyles = await getHoverStyles(loc);
|
|
148
|
+
// Try to hover over it.
|
|
149
|
+
try {
|
|
150
|
+
await loc.hover({timeout: 500});
|
|
151
|
+
// If hovering succeeds, get its style properties.
|
|
152
|
+
const fhStyles = await getHoverStyles(loc);
|
|
153
|
+
// Try to blur it.
|
|
154
|
+
try {
|
|
155
|
+
await loc.blur({timeout: 500});
|
|
156
|
+
// If blurring succeeds, get its style properties.
|
|
157
|
+
const hovStyles = await getHoverStyles(loc);
|
|
158
|
+
// If all 4 style declarations belong to the same element:
|
|
159
|
+
if ([focStyles, fhStyles, hovStyles].every(style => style.code === preStyles.code)) {
|
|
160
|
+
// Get data on the element if itemization is required.
|
|
161
|
+
const elData = withItems ? await getLocatorData(loc) : null;
|
|
162
|
+
// If the hover cursor is nonstandard:
|
|
163
|
+
const cursorData = getCursorData(hovStyles);
|
|
164
|
+
if (! cursorData.ok) {
|
|
165
|
+
// Add to the totals.
|
|
166
|
+
totals[2] += psRatio;
|
|
167
|
+
data.typeTotals.badCursor += psRatio;
|
|
168
|
+
// If itemization is required:
|
|
169
|
+
if (withItems) {
|
|
170
|
+
// Add an instance to the result.
|
|
171
|
+
standardInstances.push({
|
|
172
|
+
ruleID: 'hovInd',
|
|
173
|
+
what: `Element has a nonstandard hover cursor (${cursorData.cursor})`,
|
|
174
|
+
ordinalSeverity: 2,
|
|
175
|
+
tagName: elData.tagName,
|
|
176
|
+
id: elData.id,
|
|
177
|
+
location: elData.location,
|
|
178
|
+
excerpt: elData.excerpt
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// If the element is a button and the hover and default states are not distinct:
|
|
183
|
+
if (hovStyles.tagName === 'BUTTON' && areAlike(preStyles, hovStyles)) {
|
|
184
|
+
// Add to the totals.
|
|
185
|
+
totals[1] += psRatio;
|
|
186
|
+
data.typeTotals.hoverLikeDefault += psRatio;
|
|
187
|
+
// If itemization is required:
|
|
188
|
+
if (withItems) {
|
|
189
|
+
// Add an instance to the result.
|
|
190
|
+
standardInstances.push({
|
|
191
|
+
ruleID: 'hovInd',
|
|
192
|
+
what: 'Element border, outline, and background color do not change when hovered over',
|
|
193
|
+
ordinalSeverity: 1,
|
|
194
|
+
tagName: elData.tagName,
|
|
195
|
+
id: elData.id,
|
|
196
|
+
location: elData.location,
|
|
197
|
+
excerpt: elData.excerpt
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// If the hover and focus-hover states are indistinct but differ from the default state:
|
|
202
|
+
if (areAlike(hovStyles, focStyles) && ! areAlike(hovStyles, preStyles)) {
|
|
203
|
+
// Add to the totals.
|
|
204
|
+
totals[1] += psRatio;
|
|
205
|
+
data.typeTotals.hoverLikeFocus += psRatio;
|
|
206
|
+
// If itemization is required:
|
|
207
|
+
if (withItems) {
|
|
208
|
+
// Add an instance to the result.
|
|
209
|
+
standardInstances.push({
|
|
210
|
+
ruleID: 'hovInd',
|
|
211
|
+
what: 'Element border, outline, and background color are alike on hover and focus',
|
|
212
|
+
ordinalSeverity: 1,
|
|
213
|
+
tagName: elData.tagName,
|
|
214
|
+
id: elData.id,
|
|
215
|
+
location: elData.location,
|
|
216
|
+
excerpt: elData.excerpt
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Otherwise, i.e. if the style properties do not all belong to the same element:
|
|
222
|
+
else {
|
|
223
|
+
// Report this and quit.
|
|
224
|
+
data.prevented = true;
|
|
225
|
+
data.error = 'ERROR: Page changes on focus or hover prevent test';
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
179
228
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
// If itemization is required:
|
|
187
|
-
if (withItems) {
|
|
188
|
-
// Add an instance to the result.
|
|
189
|
-
standardInstances.push({
|
|
190
|
-
ruleID: 'hovInd',
|
|
191
|
-
what: 'Element border, outline, and background color do not change when hovered over',
|
|
192
|
-
ordinalSeverity: 1,
|
|
193
|
-
tagName: elData.tagName,
|
|
194
|
-
id: elData.id,
|
|
195
|
-
location: elData.location,
|
|
196
|
-
excerpt: elData.excerpt
|
|
197
|
-
});
|
|
229
|
+
// If blurring fails:
|
|
230
|
+
catch(error) {
|
|
231
|
+
// Report this.
|
|
232
|
+
data.prevented = true;
|
|
233
|
+
data.error = 'ERROR: Page changes on focus or hover prevent test';
|
|
234
|
+
break;
|
|
198
235
|
}
|
|
199
236
|
}
|
|
200
|
-
// If
|
|
201
|
-
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
data.
|
|
205
|
-
|
|
206
|
-
if (withItems) {
|
|
207
|
-
// Add an instance to the result.
|
|
208
|
-
standardInstances.push({
|
|
209
|
-
ruleID: 'hovInd',
|
|
210
|
-
what: 'Element border, outline, and background color are alike on hover and focus',
|
|
211
|
-
ordinalSeverity: 1,
|
|
212
|
-
tagName: elData.tagName,
|
|
213
|
-
id: elData.id,
|
|
214
|
-
location: elData.location,
|
|
215
|
-
excerpt: elData.excerpt
|
|
216
|
-
});
|
|
217
|
-
}
|
|
237
|
+
// If hovering fails:
|
|
238
|
+
catch(error) {
|
|
239
|
+
// Report this.
|
|
240
|
+
data.prevented = true;
|
|
241
|
+
data.error = 'ERROR: Page changes on focus or hover prevent test';
|
|
242
|
+
break;
|
|
218
243
|
}
|
|
219
244
|
}
|
|
220
|
-
//
|
|
221
|
-
|
|
245
|
+
// If focusing fails:
|
|
246
|
+
catch(error) {
|
|
222
247
|
// Report this.
|
|
223
248
|
data.prevented = true;
|
|
224
249
|
data.error = 'ERROR: Page changes on focus or hover prevent test';
|
|
250
|
+
break;
|
|
225
251
|
}
|
|
226
252
|
}
|
|
227
253
|
// Round the totals.
|
package/tests/testaro.js
CHANGED
|
@@ -30,7 +30,6 @@ const evalRules = {
|
|
|
30
30
|
linkTitle: 'links with title attributes repeating text content',
|
|
31
31
|
linkTo: 'links without destinations',
|
|
32
32
|
linkUl: 'missing underlines on inline links',
|
|
33
|
-
menuNav: 'nonstandard keyboard navigation between focusable menu items',
|
|
34
33
|
miniText: 'text smaller than 11 pixels',
|
|
35
34
|
motion: 'motion without user request',
|
|
36
35
|
nonTable: 'table elements used for layout',
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "entirely or mainly hidden page",
|
|
21
20
|
"stopOnFail": true,
|
|
22
21
|
"expect": [
|
|
23
22
|
[
|
|
@@ -60,7 +59,6 @@
|
|
|
60
59
|
{
|
|
61
60
|
"type": "test",
|
|
62
61
|
"which": "testaro",
|
|
63
|
-
"what": "entirely or mainly hidden page",
|
|
64
62
|
"stopOnFail": true,
|
|
65
63
|
"expect": [
|
|
66
64
|
[
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "attribute values",
|
|
21
20
|
"withItems": true,
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
@@ -43,7 +42,6 @@
|
|
|
43
42
|
{
|
|
44
43
|
"type": "test",
|
|
45
44
|
"which": "testaro",
|
|
46
|
-
"what": "attribute values",
|
|
47
45
|
"withItems": false,
|
|
48
46
|
"stopOnFail": true,
|
|
49
47
|
"expect": [
|
|
@@ -149,7 +147,6 @@
|
|
|
149
147
|
{
|
|
150
148
|
"type": "test",
|
|
151
149
|
"which": "testaro",
|
|
152
|
-
"what": "attribute values",
|
|
153
150
|
"withItems": false,
|
|
154
151
|
"stopOnFail": true,
|
|
155
152
|
"expect": [
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "visible element count",
|
|
21
20
|
"stopOnFail": true,
|
|
22
21
|
"expect": [
|
|
23
22
|
[
|
|
@@ -45,7 +44,6 @@
|
|
|
45
44
|
{
|
|
46
45
|
"type": "test",
|
|
47
46
|
"which": "testaro",
|
|
48
|
-
"what": "visible element count",
|
|
49
47
|
"stopOnFail": true,
|
|
50
48
|
"expect": [
|
|
51
49
|
[
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "elements with duplicate attributes",
|
|
21
20
|
"withItems": true,
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
@@ -45,7 +44,6 @@
|
|
|
45
44
|
{
|
|
46
45
|
"type": "test",
|
|
47
46
|
"which": "testaro",
|
|
48
|
-
"what": "elements with duplicate attributes",
|
|
49
47
|
"withItems": true,
|
|
50
48
|
"stopOnFail": true,
|
|
51
49
|
"expect": [
|
|
@@ -108,7 +106,6 @@
|
|
|
108
106
|
{
|
|
109
107
|
"type": "test",
|
|
110
108
|
"which": "testaro",
|
|
111
|
-
"what": "elements with duplicate attributes",
|
|
112
109
|
"withItems": false,
|
|
113
110
|
"stopOnFail": true,
|
|
114
111
|
"expect": [
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "elements",
|
|
21
20
|
"stopOnFail": true,
|
|
22
21
|
"expect": [
|
|
23
22
|
[
|
|
@@ -38,7 +37,6 @@
|
|
|
38
37
|
{
|
|
39
38
|
"type": "test",
|
|
40
39
|
"which": "testaro",
|
|
41
|
-
"what": "elements",
|
|
42
40
|
"stopOnFail": true,
|
|
43
41
|
"expect": [
|
|
44
42
|
[
|
|
@@ -59,7 +57,6 @@
|
|
|
59
57
|
{
|
|
60
58
|
"type": "test",
|
|
61
59
|
"which": "testaro",
|
|
62
|
-
"what": "elements",
|
|
63
60
|
"stopOnFail": true,
|
|
64
61
|
"expect": [
|
|
65
62
|
[
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "embedded active elements",
|
|
21
20
|
"withItems": true,
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
@@ -45,7 +44,6 @@
|
|
|
45
44
|
{
|
|
46
45
|
"type": "test",
|
|
47
46
|
"which": "testaro",
|
|
48
|
-
"what": "embedded active elements",
|
|
49
47
|
"withItems": true,
|
|
50
48
|
"stopOnFail": true,
|
|
51
49
|
"expect": [
|
|
@@ -108,7 +106,6 @@
|
|
|
108
106
|
{
|
|
109
107
|
"type": "test",
|
|
110
108
|
"which": "testaro",
|
|
111
|
-
"what": "embedded active elements",
|
|
112
109
|
"withItems": false,
|
|
113
110
|
"stopOnFail": true,
|
|
114
111
|
"expect": [
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "filter",
|
|
21
20
|
"withItems": true,
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
@@ -45,7 +44,6 @@
|
|
|
45
44
|
{
|
|
46
45
|
"type": "test",
|
|
47
46
|
"which": "testaro",
|
|
48
|
-
"what": "filter",
|
|
49
47
|
"withItems": true,
|
|
50
48
|
"stopOnFail": true,
|
|
51
49
|
"expect": [
|
|
@@ -118,7 +116,6 @@
|
|
|
118
116
|
{
|
|
119
117
|
"type": "test",
|
|
120
118
|
"which": "testaro",
|
|
121
|
-
"what": "filter",
|
|
122
119
|
"withItems": false,
|
|
123
120
|
"stopOnFail": true,
|
|
124
121
|
"expect": [
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "Tab-focusability",
|
|
21
20
|
"withItems": true,
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
@@ -45,7 +44,6 @@
|
|
|
45
44
|
{
|
|
46
45
|
"type": "test",
|
|
47
46
|
"which": "testaro",
|
|
48
|
-
"what": "Tab-focusability",
|
|
49
47
|
"withItems": true,
|
|
50
48
|
"stopOnFail": true,
|
|
51
49
|
"expect": [
|
|
@@ -93,7 +91,6 @@
|
|
|
93
91
|
{
|
|
94
92
|
"type": "test",
|
|
95
93
|
"which": "testaro",
|
|
96
|
-
"what": "Tab-focusability",
|
|
97
94
|
"withItems": true,
|
|
98
95
|
"stopOnFail": true,
|
|
99
96
|
"expect": [
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
20
|
"withItems": true,
|
|
21
|
-
"what": "focus indication",
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
24
23
|
[
|
|
@@ -134,7 +133,6 @@
|
|
|
134
133
|
"type": "test",
|
|
135
134
|
"which": "testaro",
|
|
136
135
|
"withItems": false,
|
|
137
|
-
"what": "focus indication",
|
|
138
136
|
"stopOnFail": true,
|
|
139
137
|
"expect": [
|
|
140
138
|
[
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
"acts": [
|
|
7
7
|
{
|
|
8
8
|
"type": "launch",
|
|
9
|
-
"which": "
|
|
10
|
-
"what": "
|
|
9
|
+
"which": "chromium",
|
|
10
|
+
"what": "usual browser"
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"type": "url",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "focusability and operability",
|
|
21
20
|
"withItems": true,
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
@@ -50,7 +49,6 @@
|
|
|
50
49
|
{
|
|
51
50
|
"type": "test",
|
|
52
51
|
"which": "testaro",
|
|
53
|
-
"what": "focusability and operability",
|
|
54
52
|
"withItems": true,
|
|
55
53
|
"stopOnFail": true,
|
|
56
54
|
"expect": [
|
|
@@ -158,7 +156,6 @@
|
|
|
158
156
|
{
|
|
159
157
|
"type": "test",
|
|
160
158
|
"which": "testaro",
|
|
161
|
-
"what": "focusability and operability",
|
|
162
159
|
"withItems": false,
|
|
163
160
|
"stopOnFail": true,
|
|
164
161
|
"expect": [
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "Labeling",
|
|
21
20
|
"withItems": true,
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
@@ -50,7 +49,6 @@
|
|
|
50
49
|
{
|
|
51
50
|
"type": "test",
|
|
52
51
|
"which": "testaro",
|
|
53
|
-
"what": "Labeling",
|
|
54
52
|
"withItems": true,
|
|
55
53
|
"stopOnFail": true,
|
|
56
54
|
"expect": [
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "links without destinations",
|
|
21
20
|
"withItems": true,
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
@@ -80,7 +79,6 @@
|
|
|
80
79
|
{
|
|
81
80
|
"type": "test",
|
|
82
81
|
"which": "testaro",
|
|
83
|
-
"what": "links without destinations",
|
|
84
82
|
"withItems": false,
|
|
85
83
|
"stopOnFail": true,
|
|
86
84
|
"expect": [
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "adjacent-link underlining",
|
|
21
20
|
"withItems": true,
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
@@ -45,7 +44,6 @@
|
|
|
45
44
|
{
|
|
46
45
|
"type": "test",
|
|
47
46
|
"which": "testaro",
|
|
48
|
-
"what": "adjacent-link underlining",
|
|
49
47
|
"withItems": true,
|
|
50
48
|
"stopOnFail": true,
|
|
51
49
|
"expect": [
|
|
@@ -78,7 +76,6 @@
|
|
|
78
76
|
{
|
|
79
77
|
"type": "test",
|
|
80
78
|
"which": "testaro",
|
|
81
|
-
"what": "inline-link underlining",
|
|
82
79
|
"withItems": true,
|
|
83
80
|
"stopOnFail": true,
|
|
84
81
|
"expect": [
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "small text",
|
|
21
20
|
"withItems": true,
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
@@ -95,7 +94,6 @@
|
|
|
95
94
|
{
|
|
96
95
|
"type": "test",
|
|
97
96
|
"which": "testaro",
|
|
98
|
-
"what": "small text",
|
|
99
97
|
"withItems": false,
|
|
100
98
|
"stopOnFail": true,
|
|
101
99
|
"expect": [
|
|
@@ -45,7 +45,6 @@
|
|
|
45
45
|
{
|
|
46
46
|
"type": "test",
|
|
47
47
|
"which": "testaro",
|
|
48
|
-
"what": "radioSet",
|
|
49
48
|
"withItems": true,
|
|
50
49
|
"stopOnFail": true,
|
|
51
50
|
"expect": [
|
|
@@ -148,7 +147,6 @@
|
|
|
148
147
|
{
|
|
149
148
|
"type": "test",
|
|
150
149
|
"which": "testaro",
|
|
151
|
-
"what": "radioSet",
|
|
152
150
|
"withItems": false,
|
|
153
151
|
"stopOnFail": true,
|
|
154
152
|
"expect": [
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
20
|
"withItems": true,
|
|
21
|
-
"what": "styleDiff",
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
24
23
|
[
|
|
@@ -51,7 +50,6 @@
|
|
|
51
50
|
"type": "test",
|
|
52
51
|
"which": "testaro",
|
|
53
52
|
"withItems": true,
|
|
54
|
-
"what": "styleDiff",
|
|
55
53
|
"stopOnFail": true,
|
|
56
54
|
"expect": [
|
|
57
55
|
[
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "tab-list navigation",
|
|
21
20
|
"withItems": true,
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
@@ -50,7 +49,6 @@
|
|
|
50
49
|
{
|
|
51
50
|
"type": "test",
|
|
52
51
|
"which": "testaro",
|
|
53
|
-
"what": "tab-list navigation",
|
|
54
52
|
"withItems": true,
|
|
55
53
|
"stopOnFail": true,
|
|
56
54
|
"expect": [
|
|
@@ -138,7 +136,6 @@
|
|
|
138
136
|
{
|
|
139
137
|
"type": "test",
|
|
140
138
|
"which": "testaro",
|
|
141
|
-
"what": "tab-list navigation",
|
|
142
139
|
"withItems": false,
|
|
143
140
|
"stopOnFail": true,
|
|
144
141
|
"expect": [
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "text nodes",
|
|
21
20
|
"stopOnFail": true,
|
|
22
21
|
"expect": [
|
|
23
22
|
[
|
|
@@ -38,7 +37,6 @@
|
|
|
38
37
|
{
|
|
39
38
|
"type": "test",
|
|
40
39
|
"which": "testaro",
|
|
41
|
-
"what": "text nodes",
|
|
42
40
|
"stopOnFail": true,
|
|
43
41
|
"expect": [
|
|
44
42
|
[
|
|
@@ -59,7 +57,6 @@
|
|
|
59
57
|
{
|
|
60
58
|
"type": "test",
|
|
61
59
|
"which": "testaro",
|
|
62
|
-
"what": "text nodes",
|
|
63
60
|
"stopOnFail": true,
|
|
64
61
|
"expect": [
|
|
65
62
|
[
|
|
@@ -85,7 +82,6 @@
|
|
|
85
82
|
{
|
|
86
83
|
"type": "test",
|
|
87
84
|
"which": "testaro",
|
|
88
|
-
"what": "text nodes",
|
|
89
85
|
"stopOnFail": true,
|
|
90
86
|
"expect": [
|
|
91
87
|
[
|
|
@@ -170,7 +166,6 @@
|
|
|
170
166
|
{
|
|
171
167
|
"type": "test",
|
|
172
168
|
"which": "testaro",
|
|
173
|
-
"what": "text nodes",
|
|
174
169
|
"stopOnFail": true,
|
|
175
170
|
"expect": [
|
|
176
171
|
[
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "document title",
|
|
21
20
|
"stopOnFail": true,
|
|
22
21
|
"expect": [
|
|
23
22
|
[
|
|
@@ -40,7 +39,6 @@
|
|
|
40
39
|
{
|
|
41
40
|
"type": "test",
|
|
42
41
|
"which": "testaro",
|
|
43
|
-
"what": "document title",
|
|
44
42
|
"stopOnFail": true,
|
|
45
43
|
"expect": [
|
|
46
44
|
[
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "title attributes on inappropriate elements",
|
|
21
20
|
"withItems": true,
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
@@ -85,7 +84,6 @@
|
|
|
85
84
|
{
|
|
86
85
|
"type": "test",
|
|
87
86
|
"which": "testaro",
|
|
88
|
-
"what": "title attributes on inappropriate elements",
|
|
89
87
|
"withItems": false,
|
|
90
88
|
"stopOnFail": true,
|
|
91
89
|
"expect": [
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "test",
|
|
19
19
|
"which": "testaro",
|
|
20
|
-
"what": "zIndex",
|
|
21
20
|
"withItems": true,
|
|
22
21
|
"stopOnFail": true,
|
|
23
22
|
"expect": [
|
|
@@ -50,7 +49,6 @@
|
|
|
50
49
|
{
|
|
51
50
|
"type": "test",
|
|
52
51
|
"which": "testaro",
|
|
53
|
-
"what": "zIndex",
|
|
54
52
|
"withItems": true,
|
|
55
53
|
"stopOnFail": true,
|
|
56
54
|
"expect": [
|
|
@@ -108,7 +106,6 @@
|
|
|
108
106
|
{
|
|
109
107
|
"type": "test",
|
|
110
108
|
"which": "testaro",
|
|
111
|
-
"what": "zIndex",
|
|
112
109
|
"withItems": false,
|
|
113
110
|
"stopOnFail": true,
|
|
114
111
|
"expect": [
|
package/tests/tenon.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
tenon
|
|
3
|
-
This test processes a previously requested test by the Tenon API.
|
|
4
|
-
*/
|
|
5
|
-
const https = require('https');
|
|
6
|
-
// Wait until a time limit in seconds expires.
|
|
7
|
-
const wait = timeLimit => new Promise(resolve => setTimeout(resolve, 1000 * timeLimit));
|
|
8
|
-
exports.reporter = async (tenonData, options) => {
|
|
9
|
-
const {id, rules} = options;
|
|
10
|
-
if (tenonData && tenonData.accessToken && tenonData.requestIDs && tenonData.requestIDs[id]) {
|
|
11
|
-
// Shared request options.
|
|
12
|
-
const requestOptions = {
|
|
13
|
-
host: 'tenon.io',
|
|
14
|
-
path: `/api/v2/${tenonData.requestIDs[id]}`,
|
|
15
|
-
port: 443,
|
|
16
|
-
protocol: 'https:',
|
|
17
|
-
headers: {
|
|
18
|
-
Authorization: `Bearer ${tenonData.accessToken}`,
|
|
19
|
-
'Cache-Control': 'no-cache'
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
// Gets the test status.
|
|
23
|
-
const getStatus = async () => {
|
|
24
|
-
const testStatus = await new Promise((resolve, reject) => {
|
|
25
|
-
requestOptions.method = 'HEAD';
|
|
26
|
-
const statusRequest = https.request(requestOptions, statusResponse => {
|
|
27
|
-
const {statusCode} = statusResponse;
|
|
28
|
-
resolve(statusCode);
|
|
29
|
-
});
|
|
30
|
-
statusRequest.on('error', error => {
|
|
31
|
-
console.log(`ERROR getting Tenon test status (${error.message})`);
|
|
32
|
-
reject(`ERROR getting Tenon test status (${error.message})`);
|
|
33
|
-
});
|
|
34
|
-
statusRequest.end();
|
|
35
|
-
});
|
|
36
|
-
return testStatus;
|
|
37
|
-
};
|
|
38
|
-
// Gets the test result.
|
|
39
|
-
const getResult = async () => {
|
|
40
|
-
const testResult = await new Promise(resolve => {
|
|
41
|
-
requestOptions.method = 'GET';
|
|
42
|
-
const resultRequest = https.request(requestOptions, resultResponse => {
|
|
43
|
-
let resultJSON = '';
|
|
44
|
-
resultResponse.on('data', chunk => {
|
|
45
|
-
resultJSON += chunk;
|
|
46
|
-
});
|
|
47
|
-
resultResponse.on('end', () => {
|
|
48
|
-
try {
|
|
49
|
-
const result = JSON.parse(resultJSON);
|
|
50
|
-
resolve(result);
|
|
51
|
-
}
|
|
52
|
-
catch(error) {
|
|
53
|
-
console.log(`ERROR getting Tenon test result (${resultJSON.slice(0, 80)} …)`);
|
|
54
|
-
resolve({
|
|
55
|
-
error: 'ERROR getting Tenon test result',
|
|
56
|
-
resultStart: resultJSON.slice(0, 80)
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
resultRequest.end();
|
|
62
|
-
});
|
|
63
|
-
// If any rules were specified and any test results exist:
|
|
64
|
-
if (
|
|
65
|
-
rules
|
|
66
|
-
&& rules.length
|
|
67
|
-
&& testResult.data
|
|
68
|
-
&& testResult.data.resultSet
|
|
69
|
-
&& testResult.data.resultSet.length
|
|
70
|
-
) {
|
|
71
|
-
// Delete the results of tests of rules not specified.
|
|
72
|
-
testResult.data.resultSet = testResult.data.resultSet.filter(
|
|
73
|
-
result => rules.includes(result.tID.toString())
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
return testResult;
|
|
77
|
-
};
|
|
78
|
-
// Get the test status (not reliable: may say 200 instead of 202).
|
|
79
|
-
let testStatus = await getStatus();
|
|
80
|
-
// If the test is still in the Tenon queue:
|
|
81
|
-
if (testStatus === 202) {
|
|
82
|
-
// Wait 5 seconds.
|
|
83
|
-
await wait(5);
|
|
84
|
-
// Get the test status again.
|
|
85
|
-
testStatus = await getStatus();
|
|
86
|
-
}
|
|
87
|
-
// If the test has allegedly been completed:
|
|
88
|
-
if (testStatus === 200) {
|
|
89
|
-
// Get the test result.
|
|
90
|
-
let testResult = await getResult();
|
|
91
|
-
// If the test is still in the Tenon queue:
|
|
92
|
-
let {status} = testResult;
|
|
93
|
-
if (status === 202) {
|
|
94
|
-
// Wait 5 seconds.
|
|
95
|
-
await wait(5);
|
|
96
|
-
// Get the test result again.
|
|
97
|
-
testResult = await getResult();
|
|
98
|
-
// If the test is still in the Tenon queue:
|
|
99
|
-
status = testResult.status;
|
|
100
|
-
if (status === 202) {
|
|
101
|
-
// Wait 15 more seconds.
|
|
102
|
-
await wait(15);
|
|
103
|
-
// Get the test result again.
|
|
104
|
-
testResult = await getResult();
|
|
105
|
-
status = testResult.status;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
// If the test has really been completed:
|
|
109
|
-
if (status === 200) {
|
|
110
|
-
// Return its result.
|
|
111
|
-
return {result: testResult};
|
|
112
|
-
}
|
|
113
|
-
// Otherwise, i.e. if the test is still running or failed:
|
|
114
|
-
else {
|
|
115
|
-
return {
|
|
116
|
-
result: {
|
|
117
|
-
prevented: true,
|
|
118
|
-
error: 'ERROR: Tenon test stalled or crashed',
|
|
119
|
-
status
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
// Otherwise, if the test is still running after a wait for its status:
|
|
125
|
-
else {
|
|
126
|
-
// Report the test status.
|
|
127
|
-
return {
|
|
128
|
-
result: {
|
|
129
|
-
prevented: true,
|
|
130
|
-
error: 'ERROR: test status not completed',
|
|
131
|
-
testStatus
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
return {
|
|
138
|
-
result: {
|
|
139
|
-
prevented: true,
|
|
140
|
-
error: 'ERROR: tenon authorization and test data incomplete'
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
};
|