testilo 13.6.0 → 13.6.2

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "testilo",
3
- "version": "13.6.0",
4
- "description": "Client that scores and digests Testaro reports",
3
+ "version": "13.6.2",
4
+ "description": "Prepares and processes Testaro reports",
5
5
  "main": "aim.js",
6
6
  "scripts": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -21,8 +21,7 @@ const innerJoiner = '\n ';
21
21
  const htmlEscape = textOrNumber => textOrNumber
22
22
  .toString()
23
23
  .replace(/&/g, '&')
24
- .replace(/</g, '&lt;')
25
- .replace(/"/g, '&quot;');
24
+ .replace(/</g, '&lt;');
26
25
  // Gets a row of the score-summary table.
27
26
  const getScoreRow = (componentName, score) => `<tr><th>${componentName}</th><td>${score}</td></tr>`;
28
27
  // Adds parameters to a query for a digest.
@@ -71,13 +70,13 @@ const populateQuery = (report, query) => {
71
70
  issueDetailRows.push(`<h4>Complaints by <code>${toolID}</code></h5>`);
72
71
  const ruleIDs = Object.keys(issueData.tools[toolID]);
73
72
  ruleIDs.forEach(ruleID => {
73
+ const ruleData = issueData.tools[toolID][ruleID];
74
74
  issueDetailRows.push(`<h5>Rule <code>${ruleID}</code></h5>`);
75
- issueDetailRows.push(
76
- `<p>Count of instances: ${issueData.tools[toolID][ruleID].complaints.countTotal}</p>`
77
- );
75
+ issueDetailRows.push(`<p>Description: ${ruleData.what}</p>`);
76
+ issueDetailRows.push(`<p>Count of instances: ${ruleData.complaints.countTotal}</p>`);
78
77
  issueDetailRows.push('<h6>Complaint specifics</h6>');
79
78
  issueDetailRows.push('<ul>');
80
- issueData.tools[toolID][ruleID].complaints.texts.forEach(text => {
79
+ ruleData.complaints.texts.forEach(text => {
81
80
  issueDetailRows.push(` <li>${htmlEscape(text || '')}</li>`);
82
81
  });
83
82
  issueDetailRows.push('</ul>');
@@ -237,17 +237,17 @@ exports.issueClasses = {
237
237
  507: {
238
238
  variable: false,
239
239
  quality: 1,
240
- what: 'element with a radio role has no mechanism that allows an accessible name to be calculated'
240
+ what: 'Element with a radio role has no mechanism that allows an accessible name to be calculated'
241
241
  },
242
242
  509: {
243
243
  variable: false,
244
244
  quality: 1,
245
- what: 'element with a textbox role has no mechanism that allows an accessible name to be calculated'
245
+ what: 'Element with a textbox role has no mechanism that allows an accessible name to be calculated'
246
246
  },
247
247
  510: {
248
248
  variable: false,
249
249
  quality: 1,
250
- what: 'element with a combobox role has no mechanism that allows an accessible name to be calculated'
250
+ what: 'Element with a combobox role has no mechanism that allows an accessible name to be calculated'
251
251
  }
252
252
  },
253
253
  htmlcs: {
@@ -379,6 +379,11 @@ exports.issueClasses = {
379
379
  variable: false,
380
380
  quality: 1,
381
381
  what: 'figure element has no associated label'
382
+ },
383
+ figure_label_exists: {
384
+ variable: false,
385
+ quality: 1,
386
+ what: 'figure element has no associated label'
382
387
  }
383
388
  }
384
389
  }
@@ -410,7 +415,7 @@ exports.issueClasses = {
410
415
  87: {
411
416
  variable: false,
412
417
  quality: 1,
413
- what: 'element with an image, graphics-symbol, or graphics-document role has no mechanism to calculate an accessible name'
418
+ what: 'Element with an image, graphics-symbol, or graphics-document role has no mechanism to calculate an accessible name'
414
419
  },
415
420
  89: {
416
421
  variable: false,
@@ -431,10 +436,20 @@ exports.issueClasses = {
431
436
  quality: 1,
432
437
  what: 'Element with an img role has no non-empty label'
433
438
  },
439
+ aria_img_labelled: {
440
+ variable: false,
441
+ quality: 1,
442
+ what: 'Element with an img role has no label or an empty label'
443
+ },
434
444
  WCAG20_Img_HasAlt: {
435
445
  variable: false,
436
446
  quality: 1,
437
447
  what: 'Image has no alt attribute conveying its meaning, or alt="" if decorative'
448
+ },
449
+ img_alt_valid: {
450
+ variable: false,
451
+ quality: 1,
452
+ what: 'Image has neither an alt attribute nor an ARIA label or title'
438
453
  }
439
454
  },
440
455
  nuVal: {
@@ -643,7 +658,7 @@ exports.issueClasses = {
643
658
  235: {
644
659
  variable: false,
645
660
  quality: 1,
646
- what: 'element with an img, graphics-symbol, or graphics-document role has a suspicious calculated accessible name'
661
+ what: 'Element with an img, graphics-symbol, or graphics-document role has a suspicious calculated accessible name'
647
662
  }
648
663
  },
649
664
  qualWeb: {
@@ -744,6 +759,11 @@ exports.issueClasses = {
744
759
  variable: false,
745
760
  quality: 1,
746
761
  what: 'Page detected as HTML, but has no lang attribute'
762
+ },
763
+ html_lang_exists: {
764
+ variable: false,
765
+ quality: 1,
766
+ what: 'Page detected as HTML, but has no lang attribute'
747
767
  }
748
768
  },
749
769
  nuVal: {
@@ -1266,6 +1286,11 @@ exports.issueClasses = {
1266
1286
  variable: false,
1267
1287
  quality: 1,
1268
1288
  what: 'Checkbox or radio button label precedes the input control'
1289
+ },
1290
+ input_label_after: {
1291
+ variable: false,
1292
+ quality: 1,
1293
+ what: 'Label text is located before its associated checkbox or radio button element'
1269
1294
  }
1270
1295
  },
1271
1296
  qualWeb: {
@@ -1285,7 +1310,7 @@ exports.issueClasses = {
1285
1310
  95: {
1286
1311
  variable: false,
1287
1312
  quality: 1,
1288
- what: 'element has an aria-labelledby value that includes an invalid or duplicate id'
1313
+ what: 'Element has an aria-labelledby value that includes an invalid or duplicate id'
1289
1314
  }
1290
1315
  },
1291
1316
  htmlcs: {
@@ -1892,14 +1917,14 @@ exports.issueClasses = {
1892
1917
  r94: {
1893
1918
  variable: false,
1894
1919
  quality: 1,
1895
- what: 'element with a menuitem role has no accessible name'
1920
+ what: 'Element with a menuitem role has no accessible name'
1896
1921
  }
1897
1922
  },
1898
1923
  qualWeb: {
1899
1924
  'QW-ACT-R66': {
1900
1925
  variable: false,
1901
1926
  quality: 1,
1902
- what: 'Menuitem has no non-empty accessible name'
1927
+ what: 'menuitem element has no non-empty accessible name'
1903
1928
  }
1904
1929
  }
1905
1930
  }
@@ -2060,7 +2085,7 @@ exports.issueClasses = {
2060
2085
  r47: {
2061
2086
  variable: false,
2062
2087
  quality: 1,
2063
- what: 'Meta element restricts zooming'
2088
+ what: 'meta element restricts zooming'
2064
2089
  }
2065
2090
  },
2066
2091
  axe: {
@@ -2243,12 +2268,18 @@ exports.issueClasses = {
2243
2268
  quality: 1,
2244
2269
  what: 'Overflow is hidden or clipped if the text is enlarged'
2245
2270
  }
2246
- },
2271
+ }
2272
+ }
2273
+ },
2274
+ overflowHiddenRisk: {
2275
+ wcag: '1.4.4',
2276
+ weight: 1,
2277
+ tools: {
2247
2278
  qualWeb: {
2248
2279
  'QW-ACT-R40': {
2249
2280
  variable: false,
2250
2281
  quality: 1,
2251
- what: 'Zoomed text node is clipped by a CSS overflow declaration'
2282
+ what: 'Zoomed text node may be clipped by a CSS overflow declaration'
2252
2283
  }
2253
2284
  }
2254
2285
  }
@@ -2310,6 +2341,11 @@ exports.issueClasses = {
2310
2341
  variable: false,
2311
2342
  quality: 1,
2312
2343
  what: 'link element with an as attribute has no rel attribute with preload, modulepreload, or prefetch as its value'
2344
+ },
2345
+ 'A link element with an as attribute must have a rel attribute that contains the value preload or the value modulepreload.': {
2346
+ variable: false,
2347
+ quality: 1,
2348
+ what: 'link element with an as attribute has no rel attribute with preload or modulepreload as its value'
2313
2349
  }
2314
2350
  }
2315
2351
  }
@@ -2362,7 +2398,7 @@ exports.issueClasses = {
2362
2398
  '^Bad value .+ for attribute .+ on element meta.*$': {
2363
2399
  variable: true,
2364
2400
  quality: 1,
2365
- what: 'attribute of a meta element has an invalid value'
2401
+ what: 'Attribute of a meta element has an invalid value'
2366
2402
  }
2367
2403
  }
2368
2404
  }
@@ -2538,7 +2574,7 @@ exports.issueClasses = {
2538
2574
  412: {
2539
2575
  variable: false,
2540
2576
  quality: 1,
2541
- what: 'element has a role attribute set to an invalid ARIA role value'
2577
+ what: 'Element has a role attribute set to an invalid ARIA role value'
2542
2578
  }
2543
2579
  },
2544
2580
  ibm: {
@@ -2562,6 +2598,11 @@ exports.issueClasses = {
2562
2598
  quality: 1,
2563
2599
  what: 'Element has a contentinfo role when no element has a main role'
2564
2600
  },
2601
+ aria_contentinfo_misuse: {
2602
+ variable: false,
2603
+ quality: 1,
2604
+ what: 'Element with a contentinfo role is present without an element with a main role'
2605
+ },
2565
2606
  Rpt_Aria_ValidRole: {
2566
2607
  variable: false,
2567
2608
  quality: 1,
@@ -2691,17 +2732,17 @@ exports.issueClasses = {
2691
2732
  '^Attribute .+ not allowed on element .+ at this point.*$': {
2692
2733
  variable: true,
2693
2734
  quality: 1,
2694
- what: 'attribute not allowed on this element'
2735
+ what: 'Attribute not allowed on this element'
2695
2736
  },
2696
2737
  '^Bad value .* for attribute .+ on element .+$': {
2697
2738
  variable: true,
2698
2739
  quality: 1,
2699
- what: 'attribute on this element has an invalid value'
2740
+ what: 'Attribute on this element has an invalid value'
2700
2741
  },
2701
2742
  '^Bad value .+ for the attribute .+$': {
2702
2743
  variable: true,
2703
2744
  quality: 1,
2704
- what: 'attribute has an invalid value'
2745
+ what: 'Attribute has an invalid value'
2705
2746
  },
2706
2747
  '^Attribute .+ not allowed here.*$': {
2707
2748
  variable: true,
@@ -2736,17 +2777,17 @@ exports.issueClasses = {
2736
2777
  'The sizes attribute may be specified only if the srcset attribute is also present.': {
2737
2778
  variable: false,
2738
2779
  quality: 1,
2739
- what: 'element has a sizes attribute but no srcset attribute'
2780
+ what: 'Element has a sizes attribute but no srcset attribute'
2740
2781
  },
2741
2782
  '^Bad value for attribute (?:width|height) on element img: The empty string is not a valid non-negative integer.*$': {
2742
2783
  variable: true,
2743
2784
  quality: 1,
2744
- what: 'attribute has an empty value'
2785
+ what: 'Attribute has an empty value'
2745
2786
  },
2746
2787
  '^.+ in an unquoted attribute value. Probable causes: Attributes running together or a URL query string in an unquoted attribute value.*$': {
2747
2788
  variable: true,
2748
2789
  quality: 1,
2749
- what: 'attribute has a value containing invalid punctuation'
2790
+ what: 'Attribute has a value containing invalid punctuation'
2750
2791
  }
2751
2792
  }
2752
2793
  }
@@ -2970,12 +3011,12 @@ exports.issueClasses = {
2970
3011
  279: {
2971
3012
  variable: false,
2972
3013
  quality: 1,
2973
- what: 'element has an aria-posinset attribute without having a compatible role'
3014
+ what: 'Element has an aria-posinset attribute without having a compatible role'
2974
3015
  },
2975
3016
  280: {
2976
3017
  variable: false,
2977
3018
  quality: 1,
2978
- what: 'element has aria-posinset and aria-setsize attributes without having a compatible role'
3019
+ what: 'Element has aria-posinset and aria-setsize attributes without having a compatible role'
2979
3020
  },
2980
3021
  281: {
2981
3022
  variable: false,
@@ -3015,7 +3056,7 @@ exports.issueClasses = {
3015
3056
  610: {
3016
3057
  variable: false,
3017
3058
  quality: 1,
3018
- what: 'element has an aria-setsize attribute without having a compatible role'
3059
+ what: 'Element has an aria-setsize attribute without having a compatible role'
3019
3060
  },
3020
3061
  1066: {
3021
3062
  variable: false,
@@ -3054,17 +3095,17 @@ exports.issueClasses = {
3054
3095
  'Attribute aria-activedescendant value should either refer to a descendant element, or should be accompanied by attribute aria-owns.': {
3055
3096
  variable: false,
3056
3097
  quality: 1,
3057
- what: 'element has no aria-owns attribute but its aria-activedescendant attribute references a non-descendant'
3098
+ what: 'Element has no aria-owns attribute but its aria-activedescendant attribute references a non-descendant'
3058
3099
  },
3059
3100
  '^Bad value for attribute .+ on element .+: Must be non-empty.*$': {
3060
3101
  variable: true,
3061
3102
  quality: 1,
3062
- what: 'attribute value is empty'
3103
+ what: 'Attribute value is empty'
3063
3104
  },
3064
3105
  '^Bad value for attribute aria-hidden on element .+$': {
3065
3106
  variable: true,
3066
3107
  quality: 1,
3067
- what: 'attribute aria-hidden has an empty value'
3108
+ what: 'aria-hidden attribute has an empty value'
3068
3109
  },
3069
3110
  'The form attribute must refer to a form element.': {
3070
3111
  variable: false,
@@ -3811,7 +3852,7 @@ exports.issueClasses = {
3811
3852
  71: {
3812
3853
  variable: false,
3813
3854
  quality: 1,
3814
- what: 'element with a heading role has no aria-level attribute'
3855
+ what: 'Element with a heading role has no aria-level attribute'
3815
3856
  }
3816
3857
  }
3817
3858
  }
@@ -4146,7 +4187,14 @@ exports.issueClasses = {
4146
4187
  'Element dl is missing a required child element.': {
4147
4188
  variable: false,
4148
4189
  quality: 1,
4149
- what: 'dl element has no child element.'
4190
+ what: 'dl element has no child element'
4191
+ }
4192
+ },
4193
+ qualWeb: {
4194
+ 'QW-BP24': {
4195
+ variable: false,
4196
+ quality: 1,
4197
+ what: 'ul or ol element has a child other than li, script, or template'
4150
4198
  }
4151
4199
  }
4152
4200
  }
@@ -4294,6 +4342,19 @@ exports.issueClasses = {
4294
4342
  }
4295
4343
  }
4296
4344
  },
4345
+ noOptionFocusable: {
4346
+ wcag: '2.1.1',
4347
+ weight: 1,
4348
+ tools: {
4349
+ ibm: {
4350
+ aria_child_tabbable: {
4351
+ variable: false,
4352
+ quality: 1,
4353
+ what: 'No descendent element with an option role is tabbable'
4354
+ }
4355
+ }
4356
+ }
4357
+ },
4297
4358
  accessKeyDuplicate: {
4298
4359
  wcag: '1.3.1',
4299
4360
  weight: 3,
@@ -4754,6 +4815,11 @@ exports.issueClasses = {
4754
4815
  variable: false,
4755
4816
  quality: 1,
4756
4817
  what: 'Form control has no associated label'
4818
+ },
4819
+ input_label_exists: {
4820
+ variable: false,
4821
+ quality: 1,
4822
+ what: 'Element with the role of a form control has no associated label'
4757
4823
  }
4758
4824
  },
4759
4825
  qualWeb: {
@@ -5156,6 +5222,13 @@ exports.issueClasses = {
5156
5222
  quality: 1,
5157
5223
  what: 'main landmark is contained in another landmark'
5158
5224
  }
5225
+ },
5226
+ qualWeb: {
5227
+ 'QW-BP27': {
5228
+ variable: false,
5229
+ quality: 1,
5230
+ what: 'main landmark is not at the top level'
5231
+ }
5159
5232
  }
5160
5233
  }
5161
5234
  },
@@ -5351,6 +5424,11 @@ exports.issueClasses = {
5351
5424
  variable: false,
5352
5425
  quality: 1,
5353
5426
  what: 'Element with a form role has no unique purpose label among the form-role elements'
5427
+ },
5428
+ aria_form_label_unique: {
5429
+ variable: false,
5430
+ quality: 1,
5431
+ what: 'Multiple elements with a form role do not have unique labels'
5354
5432
  }
5355
5433
  }
5356
5434
  }
@@ -5435,6 +5513,11 @@ exports.issueClasses = {
5435
5513
  variable: false,
5436
5514
  quality: 1,
5437
5515
  what: 'Element with a region role has no unique label among the region-role elements'
5516
+ },
5517
+ aria_region_label_unique: {
5518
+ variable: false,
5519
+ quality: 1,
5520
+ what: 'Multiple elements with a region role do not have unique labels'
5438
5521
  }
5439
5522
  }
5440
5523
  }
@@ -5744,20 +5827,7 @@ exports.issueClasses = {
5744
5827
  hover: {
5745
5828
  variable: false,
5746
5829
  quality: 1,
5747
- what: 'Hovering is mis-indicated or changes content'
5748
- }
5749
- }
5750
- }
5751
- },
5752
- hoverSurprise: {
5753
- wcag: '1.4.13',
5754
- weight: 3,
5755
- tools: {
5756
- testaro: {
5757
- hover: {
5758
- variable: false,
5759
- quality: 1,
5760
- what: 'Hovering over element has unexpected effects'
5830
+ what: 'Hovering is impossible or mis-indicated or changes content'
5761
5831
  }
5762
5832
  }
5763
5833
  }
@@ -6886,6 +6956,16 @@ exports.issueClasses = {
6886
6956
  }
6887
6957
  },
6888
6958
  nuVal: {
6959
+ 'No space between attributes.': {
6960
+ variable: false,
6961
+ quality: 1,
6962
+ what: 'No space between attributes'
6963
+ },
6964
+ 'Saw <?. Probable cause: Attempt to use an XML processing instruction in HTML. (XML processing instructions are not supported in HTML.)': {
6965
+ variable: false,
6966
+ quality: 1,
6967
+ what: 'Left angle bracket is followed by a question mark'
6968
+ },
6889
6969
  'Almost standards mode doctype. Expected <!DOCTYPE html>.': {
6890
6970
  variable: false,
6891
6971
  quality: 1,
@@ -7009,7 +7089,7 @@ exports.issueClasses = {
7009
7089
  'When the srcset attribute has any image candidate string with a width descriptor, the sizes attribute must also be present.': {
7010
7090
  variable: false,
7011
7091
  quality: 1,
7012
- what: 'element with a srcset attribute with a width has no sizes attribute'
7092
+ what: 'Element with a srcset attribute with a width has no sizes attribute'
7013
7093
  },
7014
7094
  '^The text content of element .+ was not in the required format: Expected .+ but found .+ instead.*$': {
7015
7095
  variable: true,
@@ -120,8 +120,10 @@ exports.scorer = report => {
120
120
  details.issue[issueID].tools[which] = {};
121
121
  }
122
122
  if (! details.issue[issueID].tools[which][ruleID]) {
123
+ const ruleData = issueClasses[issueID].tools[which][ruleID];
123
124
  details.issue[issueID].tools[which][ruleID] = {
124
- quality: issueClasses[issueID].tools[which][ruleID].quality,
125
+ quality: ruleData.quality,
126
+ what: ruleData.what,
125
127
  complaints: {
126
128
  countTotal: 0,
127
129
  texts: []
@@ -172,7 +174,7 @@ exports.scorer = report => {
172
174
  }
173
175
  });
174
176
  // Get the score for the issue.
175
- issueData.score = issueData.weight * issueData.maxCount;
177
+ issueData.score = Math.round(issueData.weight * issueData.maxCount);
176
178
  });
177
179
  // Add the severity detail totals to the score.
178
180
  details.severity.total = Object.keys(details.severity.byTool).reduce((severityTotals, toolID) => {