testilo 9.0.3 → 10.0.1

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.
Files changed (51) hide show
  1. package/README.md +31 -7
  2. package/package.json +1 -1
  3. package/procs/score/{sp20b.js → tic21.js} +1252 -991
  4. package/procs/score/tsp21.js +720 -0
  5. package/specs/batches/orgs.json +43 -0
  6. package/specs/scripts/ts21.json +213 -0
  7. package/toolRules/qualWeb.tsv +121 -0
  8. package/procs/score/sp10a.js +0 -591
  9. package/procs/score/sp10b.js +0 -507
  10. package/procs/score/sp10c.js +0 -465
  11. package/procs/score/sp11a.js +0 -1968
  12. package/procs/score/sp12a.js +0 -3000
  13. package/procs/score/sp12b.js +0 -3315
  14. package/procs/score/sp14a.js +0 -4101
  15. package/procs/score/sp15a.js +0 -4899
  16. package/procs/score/sp15b.js +0 -6279
  17. package/procs/score/sp15c.js +0 -6282
  18. package/procs/score/sp16a.js +0 -6362
  19. package/procs/score/sp16b.js +0 -6370
  20. package/procs/score/sp16c.js +0 -6364
  21. package/procs/score/sp17a.js +0 -6099
  22. package/procs/score/sp18a.js +0 -6378
  23. package/procs/score/sp20a.js +0 -6379
  24. package/procs/score/spHover.js +0 -328
  25. package/procs/score/tsp09a.js +0 -564
  26. package/scoring/data/correlations.json +0 -327
  27. package/scoring/data/data.json +0 -26021
  28. package/scoring/data/dupCounts.json +0 -112
  29. package/scoring/data/duplications.json +0 -253
  30. package/scoring/data/groups.json +0 -335
  31. package/scoring/data/issues.json +0 -304
  32. package/scoring/data/rulesetData.json +0 -15
  33. package/scoring/data/testGroups.json +0 -1067
  34. package/scoring/procs/asp09.js +0 -555
  35. package/scoring/procs/asp09NoWAVE.js +0 -508
  36. package/scoring/procs/correlation.js +0 -76
  37. package/scoring/procs/dupCounts.js +0 -39
  38. package/scoring/procs/groups.js +0 -61
  39. package/scoring/procs/packageData.js +0 -171
  40. package/scoring/procs/packageIssues.js +0 -34
  41. package/scoring/procs/regroup.js +0 -39
  42. package/scoring/procs/tempgroup.js +0 -96
  43. /package/procs/score/{spA11yMessage.js → tspA11yMessage.js} +0 -0
  44. /package/procs/score/{spProductPrice.js → tspProductPrice.js} +0 -0
  45. /package/{scoring/data/packageRules → toolRules}/alfa.tsv +0 -0
  46. /package/{scoring/data/packageRules → toolRules}/axe.js +0 -0
  47. /package/{scoring/data/packageRules → toolRules}/htmlcs.js +0 -0
  48. /package/{scoring/data/packageRules → toolRules}/ibm.tsv +0 -0
  49. /package/{scoring/data/packageRules → toolRules}/tenon.tsv +0 -0
  50. /package/{scoring/data/packageRules → toolRules}/wave.json +0 -0
  51. /package/{scoring/data/packageRules → toolRules}/wave.tsv +0 -0
@@ -1,88 +1,56 @@
1
1
  /*
2
- sp20b
3
- Testilo score proc 20b
2
+ tic21
3
+ Testilo issue classification 21
4
4
 
5
- Computes scores from Testaro script ts20 and adds them to a report. Differs from sp20a by
6
- including wcag property in group summary.
5
+ Classifies tests of Testaro and its dependent tools into issues.
7
6
 
8
- This proc applies specified weights to the component scores before summing them. An issue reported
9
- by a test is given a score. That score is determined by:
10
- Whether the issue is reported as an error or a warning.
11
- How important the issue is, if the test package is pre-weighted (axe, tenon, and testaro)
12
- Whether the test belongs to a group or is a solo test.
13
- How heavily the group is weighted, if the test package is not pre-weighted and the test belongs
14
- to a group
7
+ Issue properties:
8
+ wcag: WCAG Principle, Guideline, Success Criterion, or Technique, if any, most closely
9
+ associated with the issue.
10
+ weight: weight of the issue in score computation.
11
+ tools: tools (including Testaro) providing tests in the issue.
15
12
 
16
- The scores of solo tests are added together, multiplied by the soloWeight multiplier, and
17
- contributed to the total score.
18
-
19
- The scores of grouped tests are aggregated into a group score before being contributed to the
20
- total score. The group score is the sum of (1) an absolute score, assigned because the group has
21
- at least one test with a non-zero score, (2) the largest score among the tests of the group
22
- multiplied by a multiplier, and (3) the sum of the scores from the other tests of the group
23
- multiplied by a smaller multiplier. These three amounts are given by the groupWeights object.
24
-
25
- Browser logging and navigation statistics produce a log score, and the prevention of tests
26
- produces a prevention score. They, too, are added to the total score.
27
-
28
- Each grouped test has a quality property, typically set to 1. The value of this property can be
29
- modified when the test is found to be higher or lower in quality than usual.
13
+ Each property of tools specifies a test of that tool. Its key is the identifier of the test. Its value is an object with these properties:
14
+ - variable: whether the identifier is a regular expression.
15
+ - quality: the estimated quality of the test (normally 1).
16
+ - what: description of the test.
30
17
  */
31
18
 
32
- // CONSTANTS
33
-
34
- // ID of this proc.
35
- const scoreProcID = 'sp20b';
36
- // Configuration disclosures.
37
- const logWeights = {
38
- logCount: 0.5,
39
- logSize: 0.01,
40
- errorLogCount: 1,
41
- errorLogSize: 0.02,
42
- prohibitedCount: 15,
43
- visitTimeoutCount: 10,
44
- visitRejectionCount: 10,
45
- visitLatency: 1
46
- };
47
- // Normal latency (1 second per visit).
48
- const normalLatency = 13;
49
- // How much each solo issue adds to the score.
50
- const soloWeight = 2;
51
- // How much grouped issues add to the score.
52
- const groupWeights = {
53
- // Added per issue group.
54
- absolute: 2,
55
- // Added per issue reported by the package with the largest count in the group.
56
- largest: 1,
57
- // Added per issue in the group reported by each other package.
58
- smaller: 0.4
59
- };
60
- // How much each prevention adds to the score.
61
- const preventionWeights = {
62
- testaro: 50,
63
- other: 100
64
- };
65
- // Non-Testaro packages.
66
- const otherPackages = ['alfa', 'axe', 'continuum', 'htmlcs', 'ibm', 'nuVal', 'tenon', 'wave'];
67
- // Test groups.
68
- const groups = {
19
+ exports.issues = {
69
20
  ignorable: {
70
21
  wcag: '',
71
22
  weight: 0,
72
- packages: {
23
+ tools: {
73
24
  nuVal: {
74
25
  'Element mediaelementwrapper not allowed as child of element div in this context. (Suppressing further errors from this subtree.)': {
75
26
  variable: false,
76
27
  quality: 0,
77
28
  what: 'Bug in nuVal'
78
29
  }
30
+ },
31
+ qualWeb: {
32
+ 'QW-ACT-R52': {
33
+ variable: false,
34
+ quality: 1,
35
+ what: 'video element visual-only content has no description track (description tracks and this ACT rule have been deprecated)'
36
+ },
37
+ 'QW-ACT-R57': {
38
+ variable: false,
39
+ quality: 1,
40
+ what: 'video element visual content has no description track (description tracks and this ACT rule have been deprecated)'
41
+ },
42
+ 'QW-WCAG-T20': {
43
+ variable: false,
44
+ quality: 1,
45
+ what: 'Link text is not supplemented with a title attribute'
46
+ }
79
47
  }
80
48
  }
81
49
  },
82
50
  duplicateID: {
83
51
  wcag: '4.1.1',
84
52
  weight: 4,
85
- packages: {
53
+ tools: {
86
54
  alfa: {
87
55
  r3: {
88
56
  variable: false,
@@ -111,7 +79,7 @@ const groups = {
111
79
  94: {
112
80
  variable: false,
113
81
  quality: 1,
114
- what: 'Elements contains an id attribute set to a value that is not unique in the DOM'
82
+ what: 'Element contains an id attribute set to a value that is not unique in the DOM'
115
83
  }
116
84
  },
117
85
  htmlcs: {
@@ -134,13 +102,20 @@ const groups = {
134
102
  quality: 1,
135
103
  what: 'Duplicate id'
136
104
  }
105
+ },
106
+ qualWeb: {
107
+ 'QW-ACT-R18': {
108
+ variable: false,
109
+ quality: 1,
110
+ what: 'id attribute value is not unique'
111
+ }
137
112
  }
138
113
  }
139
114
  },
140
115
  componentNoText: {
141
116
  wcag: '4.1.2',
142
117
  weight: 4,
143
- packages: {
118
+ tools: {
144
119
  ibm: {
145
120
  Rpt_Aria_WidgetLabels_Implicit: {
146
121
  variable: false,
@@ -153,7 +128,7 @@ const groups = {
153
128
  regionNoText: {
154
129
  wcag: '4.1.2',
155
130
  weight: 4,
156
- packages: {
131
+ tools: {
157
132
  alfa: {
158
133
  r40: {
159
134
  variable: false,
@@ -177,10 +152,23 @@ const groups = {
177
152
  }
178
153
  }
179
154
  },
155
+ headingNoText: {
156
+ wcag: '1.1.1',
157
+ weight: 4,
158
+ tools: {
159
+ qualWeb: {
160
+ 'QW-BP8': {
161
+ variable: false,
162
+ quality: 1,
163
+ what: 'Heading with an image has no accessible name'
164
+ }
165
+ }
166
+ }
167
+ },
180
168
  formFieldNoText: {
181
169
  wcag: '4.1.2',
182
170
  weight: 4,
183
- packages: {
171
+ tools: {
184
172
  alfa: {
185
173
  r8: {
186
174
  variable: false,
@@ -193,7 +181,7 @@ const groups = {
193
181
  inputNoText: {
194
182
  wcag: '4.1.2',
195
183
  weight: 4,
196
- packages: {
184
+ tools: {
197
185
  axe: {
198
186
  'aria-input-field-name': {
199
187
  variable: false,
@@ -300,7 +288,7 @@ const groups = {
300
288
  inputOnlyPlaceholder: {
301
289
  wcag: '4.1.2',
302
290
  weight: 3,
303
- packages: {
291
+ tools: {
304
292
  continuum: {
305
293
  863: {
306
294
  variable: false,
@@ -313,7 +301,7 @@ const groups = {
313
301
  imageInputNoText: {
314
302
  wcag: '1.1.1',
315
303
  weight: 4,
316
- packages: {
304
+ tools: {
317
305
  alfa: {
318
306
  r28: {
319
307
  variable: false,
@@ -342,6 +330,18 @@ const groups = {
342
330
  what: 'Input element of type image has no text alternative'
343
331
  }
344
332
  },
333
+ qualWeb: {
334
+ 'QW-ACT-R6': {
335
+ variable: false,
336
+ quality: 1,
337
+ what: 'Image button has no accessible name'
338
+ },
339
+ 'QW-WCAG-T5': {
340
+ variable: false,
341
+ quality: 1,
342
+ what: 'alt attribute not used on an image used as a submit button'
343
+ }
344
+ },
345
345
  wave: {
346
346
  'e:alt_input_missing': {
347
347
  variable: false,
@@ -354,7 +354,7 @@ const groups = {
354
354
  figureNoText: {
355
355
  wcag: '1.1.1',
356
356
  weight: 4,
357
- packages: {
357
+ tools: {
358
358
  ibm: {
359
359
  HAAC_Figure_label: {
360
360
  variable: false,
@@ -367,7 +367,7 @@ const groups = {
367
367
  imageNoText: {
368
368
  wcag: '1.1.1',
369
369
  weight: 4,
370
- packages: {
370
+ tools: {
371
371
  alfa: {
372
372
  r2: {
373
373
  variable: false,
@@ -425,6 +425,13 @@ const groups = {
425
425
  what: 'img element has no alt attribute'
426
426
  }
427
427
  },
428
+ qualWeb: {
429
+ 'QW-ACT-R17': {
430
+ variable: false,
431
+ quality: 1,
432
+ what: 'Image has no accessible name'
433
+ }
434
+ },
428
435
  wave: {
429
436
  'e:alt_missing': {
430
437
  variable: false,
@@ -442,7 +449,7 @@ const groups = {
442
449
  decorativeImageAltBad: {
443
450
  wcag: '1.1.1',
444
451
  weight: 4,
445
- packages: {
452
+ tools: {
446
453
  ibm: {
447
454
  WCAG20_Img_PresentationImgHasNonNullAlt: {
448
455
  variable: false,
@@ -455,7 +462,7 @@ const groups = {
455
462
  imageTextBad: {
456
463
  wcag: '1.1.1',
457
464
  weight: 3,
458
- packages: {
465
+ tools: {
459
466
  alfa: {
460
467
  r39: {
461
468
  variable: false,
@@ -468,7 +475,7 @@ const groups = {
468
475
  imageNoSource: {
469
476
  wcag: '1.3.1',
470
477
  weight: 4,
471
- packages: {
478
+ tools: {
472
479
  nuVal: {
473
480
  'Element img is missing required attribute src.': {
474
481
  variable: false,
@@ -481,7 +488,7 @@ const groups = {
481
488
  sourceEmpty: {
482
489
  wcag: '1.3.1',
483
490
  weight: 4,
484
- packages: {
491
+ tools: {
485
492
  nuVal: {
486
493
  '^Bad value for attribute src on element .+: Must be non-empty.*$': {
487
494
  variable: true,
@@ -494,7 +501,7 @@ const groups = {
494
501
  backgroundBad: {
495
502
  wcag: '4.1',
496
503
  weight: 4,
497
- packages: {
504
+ tools: {
498
505
  nuVal: {
499
506
  '^CSS: background: .+ is not a color value.*$': {
500
507
  variable: true,
@@ -517,7 +524,7 @@ const groups = {
517
524
  backgroundImageBad: {
518
525
  wcag: '4.1',
519
526
  weight: 4,
520
- packages: {
527
+ tools: {
521
528
  nuVal: {
522
529
  '^CSS: background-image: .+ is not a background-image value.*$': {
523
530
  variable: true,
@@ -535,7 +542,7 @@ const groups = {
535
542
  inputAlt: {
536
543
  wcag: '4.1',
537
544
  weight: 4,
538
- packages: {
545
+ tools: {
539
546
  continuum: {
540
547
  15: {
541
548
  variable: false,
@@ -548,7 +555,7 @@ const groups = {
548
555
  imagesSameAlt: {
549
556
  wcag: '1.1.1',
550
557
  weight: 1,
551
- packages: {
558
+ tools: {
552
559
  wave: {
553
560
  'a:alt_duplicate': {
554
561
  variable: false,
@@ -561,7 +568,14 @@ const groups = {
561
568
  imageTextLong: {
562
569
  wcag: '1.1.1',
563
570
  weight: 2,
564
- packages: {
571
+ tools: {
572
+ qualWeb: {
573
+ 'QW-BP2': {
574
+ variable: false,
575
+ quality: 1,
576
+ what: 'image text alternative is not concise'
577
+ }
578
+ },
565
579
  wave: {
566
580
  'a:alt_long': {
567
581
  variable: false,
@@ -571,10 +585,36 @@ const groups = {
571
585
  }
572
586
  }
573
587
  },
574
- imageTextRisk: {
575
- wcag: '1.1.1',
588
+ titleLong: {
589
+ wcag: '3.1.5',
590
+ weight: 2,
591
+ tools: {
592
+ qualWeb: {
593
+ 'QW-BP6': {
594
+ variable: false,
595
+ quality: 1,
596
+ what: 'title element is too long (more than 64 characters)'
597
+ }
598
+ }
599
+ }
600
+ },
601
+ titleNotText: {
602
+ wcag: '3.1.5',
603
+ weight: 2,
604
+ tools: {
605
+ qualWeb: {
606
+ 'QW-BP7': {
607
+ variable: false,
608
+ quality: 1,
609
+ what: 'Title element contains ASCII-art'
610
+ }
611
+ }
612
+ }
613
+ },
614
+ textAlternativeRisk: {
615
+ wcag: '1',
576
616
  weight: 1,
577
- packages: {
617
+ tools: {
578
618
  continuum: {
579
619
  234: {
580
620
  variable: false,
@@ -587,6 +627,13 @@ const groups = {
587
627
  what: 'element with an img, graphics-symbol, or graphics-document role has a suspicious calculated accessible name'
588
628
  }
589
629
  },
630
+ qualWeb: {
631
+ 'QW-WCAG-T8': {
632
+ variable: false,
633
+ quality: 1,
634
+ what: 'Text alternative is not an alternative'
635
+ }
636
+ },
590
637
  wave: {
591
638
  'a:alt_suspicious': {
592
639
  variable: false,
@@ -599,7 +646,7 @@ const groups = {
599
646
  decorativeImageRisk: {
600
647
  wcag: '1.1.1',
601
648
  weight: 1,
602
- packages: {
649
+ tools: {
603
650
  htmlcs: {
604
651
  'w:AA.1_1_1.H67.2': {
605
652
  variable: false,
@@ -612,7 +659,7 @@ const groups = {
612
659
  decorativeElementExposed: {
613
660
  wcag: '1.3.1',
614
661
  weight: 2,
615
- packages: {
662
+ tools: {
616
663
  alfa: {
617
664
  r67: {
618
665
  variable: false,
@@ -631,13 +678,20 @@ const groups = {
631
678
  quality: 1,
632
679
  what: 'img element with alt="" has a role attribute'
633
680
  }
681
+ },
682
+ qualWeb: {
683
+ 'QW-ACT-R48': {
684
+ variable: false,
685
+ quality: 1,
686
+ what: 'Element marked as decorative is exposed'
687
+ }
634
688
  }
635
689
  }
636
690
  },
637
691
  pageLanguage: {
638
692
  wcag: '3.1.1',
639
693
  weight: 4,
640
- packages: {
694
+ tools: {
641
695
  alfa: {
642
696
  r4: {
643
697
  variable: false,
@@ -685,6 +739,13 @@ const groups = {
685
739
  what: 'html start tag has no lang attribute to declare the language of the page'
686
740
  }
687
741
  },
742
+ qualWeb: {
743
+ 'QW-ACT-R2': {
744
+ variable: false,
745
+ quality: 1,
746
+ what: 'HTML page has no lang attribute'
747
+ }
748
+ },
688
749
  wave: {
689
750
  'e:language_missing': {
690
751
  variable: false,
@@ -697,7 +758,7 @@ const groups = {
697
758
  pageLanguageBad: {
698
759
  wcag: '3.1.1',
699
760
  weight: 4,
700
- packages: {
761
+ tools: {
701
762
  alfa: {
702
763
  r5: {
703
764
  variable: false,
@@ -732,13 +793,25 @@ const groups = {
732
793
  quality: 1,
733
794
  what: 'lang attribute does not include a valid primary language'
734
795
  }
796
+ },
797
+ qualWeb: {
798
+ 'QW-ACT-R5': {
799
+ variable: false,
800
+ quality: 1,
801
+ what: 'HTML lang attribute is invalid'
802
+ },
803
+ 'QW-ACT-R3': {
804
+ variable: false,
805
+ quality: 1,
806
+ what: 'HTML lang and xml:lang do not match'
807
+ }
735
808
  }
736
809
  }
737
810
  },
738
811
  elementLanguageBad: {
739
812
  wcag: '3.1.2',
740
813
  weight: 4,
741
- packages: {
814
+ tools: {
742
815
  htmlcs: {
743
816
  'e:AA.3_1_2.H58.1.Lang': {
744
817
  variable: false,
@@ -752,13 +825,20 @@ const groups = {
752
825
  quality: 1,
753
826
  what: 'Element has no lang attrbute matching its xml:lang attribute'
754
827
  }
828
+ },
829
+ qualWeb: {
830
+ 'QW-ACT-R22': {
831
+ variable: false,
832
+ quality: 1,
833
+ what: 'Element within the body has no valid lang attribute'
834
+ }
755
835
  }
756
836
  }
757
837
  },
758
838
  languageChange: {
759
839
  wcag: '3.1.2',
760
840
  weight: 3,
761
- packages: {
841
+ tools: {
762
842
  alfa: {
763
843
  r7: {
764
844
  variable: false,
@@ -785,7 +865,7 @@ const groups = {
785
865
  dialogNoText: {
786
866
  wcag: '4.1.2',
787
867
  weight: 4,
788
- packages: {
868
+ tools: {
789
869
  axe: {
790
870
  'aria-dialog-name': {
791
871
  variable: false,
@@ -805,7 +885,7 @@ const groups = {
805
885
  applicationNoText: {
806
886
  wcag: '4.1.2',
807
887
  weight: 4,
808
- packages: {
888
+ tools: {
809
889
  ibm: {
810
890
  Rpt_Aria_ApplicationLandmarkLabel: {
811
891
  variable: false,
@@ -818,19 +898,19 @@ const groups = {
818
898
  objectNoText: {
819
899
  wcag: '1.1.1',
820
900
  weight: 4,
821
- packages: {
901
+ tools: {
822
902
  alfa: {
823
903
  r63: {
824
904
  variable: false,
825
905
  quality: 1,
826
- what: 'Object element has no accessible name'
906
+ what: 'object element has no accessible name'
827
907
  }
828
908
  },
829
909
  axe: {
830
910
  'object-alt': {
831
911
  variable: false,
832
912
  quality: 1,
833
- what: 'Object element has no text alternative'
913
+ what: 'object element has no text alternative'
834
914
  }
835
915
  },
836
916
  continuum: {
@@ -844,14 +924,21 @@ const groups = {
844
924
  'e:ARIA6+H53': {
845
925
  variable: false,
846
926
  quality: 1,
847
- what: 'Object element contains no text alternative'
927
+ what: 'object element contains no text alternative'
848
928
  }
849
929
  },
850
930
  ibm: {
851
931
  WCAG20_Object_HasText: {
852
932
  variable: false,
853
933
  quality: 1,
854
- what: 'Object element has no text alternative'
934
+ what: 'object element has no text alternative'
935
+ }
936
+ },
937
+ qualWeb: {
938
+ 'QW-ACT-R42': {
939
+ variable: false,
940
+ quality: 1,
941
+ what: 'Object element has no non-empty accessible name'
855
942
  }
856
943
  },
857
944
  wave: {
@@ -863,10 +950,23 @@ const groups = {
863
950
  }
864
951
  }
865
952
  },
953
+ appletNoText: {
954
+ wcag: '1.1.1',
955
+ weight: 4,
956
+ tools: {
957
+ qualWeb: {
958
+ 'QW-WCAG-T11': {
959
+ variable: false,
960
+ quality: 1,
961
+ what: 'Text alternative not provided on an applet element'
962
+ }
963
+ }
964
+ }
965
+ },
866
966
  videoNoText: {
867
967
  wcag: '1.1.1',
868
968
  weight: 4,
869
- packages: {
969
+ tools: {
870
970
  continuum: {
871
971
  252: {
872
972
  variable: false,
@@ -879,7 +979,7 @@ const groups = {
879
979
  imageMapNoText: {
880
980
  wcag: '1.1.1',
881
981
  weight: 4,
882
- packages: {
982
+ tools: {
883
983
  wave: {
884
984
  'e:alt_map_missing': {
885
985
  variable: false,
@@ -892,7 +992,7 @@ const groups = {
892
992
  imageMapAreaNoText: {
893
993
  wcag: '1.1.1',
894
994
  weight: 4,
895
- packages: {
995
+ tools: {
896
996
  axe: {
897
997
  'area-alt': {
898
998
  variable: false,
@@ -919,6 +1019,13 @@ const groups = {
919
1019
  what: 'Area element in an image map has no text alternative'
920
1020
  }
921
1021
  },
1022
+ qualWeb: {
1023
+ 'QW-WCAG-T1': {
1024
+ variable: false,
1025
+ quality: 1,
1026
+ what: 'Text alternative for an area element of an image map is not provided'
1027
+ }
1028
+ },
922
1029
  wave: {
923
1030
  'e:alt_area_missing': {
924
1031
  variable: false,
@@ -931,7 +1038,7 @@ const groups = {
931
1038
  objectBlurKeyboardRisk: {
932
1039
  wcag: '2.1.1',
933
1040
  weight: 1,
934
- packages: {
1041
+ tools: {
935
1042
  htmlcs: {
936
1043
  'w:AA.2_1_2.F10': {
937
1044
  variable: false,
@@ -944,7 +1051,7 @@ const groups = {
944
1051
  keyboardAccess: {
945
1052
  wcag: '2.1.1',
946
1053
  weight: 4,
947
- packages: {
1054
+ tools: {
948
1055
  tenon: {
949
1056
  180: {
950
1057
  variable: false,
@@ -957,7 +1064,7 @@ const groups = {
957
1064
  eventKeyboardRisk: {
958
1065
  wcag: '2.1.1',
959
1066
  weight: 1,
960
- packages: {
1067
+ tools: {
961
1068
  htmlcs: {
962
1069
  'w:AA.2_1_1.G90': {
963
1070
  variable: false,
@@ -985,6 +1092,13 @@ const groups = {
985
1092
  what: 'Mousing-up functionality may not be available by keyboard'
986
1093
  }
987
1094
  },
1095
+ qualWeb: {
1096
+ 'QW-WCAG-T6': {
1097
+ variable: false,
1098
+ quality: 1,
1099
+ what: 'Both keyboard and other device-specific functions are not used'
1100
+ }
1101
+ },
988
1102
  wave: {
989
1103
  'a:event_handler': {
990
1104
  variable: false,
@@ -997,7 +1111,7 @@ const groups = {
997
1111
  internalLinkBroken: {
998
1112
  wcag: '1.3.1',
999
1113
  weight: 4,
1000
- packages: {
1114
+ tools: {
1001
1115
  htmlcs: {
1002
1116
  'e:AA.2_4_1.G1,G123,G124.NoSuchID': {
1003
1117
  variable: false,
@@ -1017,7 +1131,7 @@ const groups = {
1017
1131
  labelForBad: {
1018
1132
  wcag: '1.3.1',
1019
1133
  weight: 3,
1020
- packages: {
1134
+ tools: {
1021
1135
  htmlcs: {
1022
1136
  'w:AA.1_3_1.H44.NotFormControl': {
1023
1137
  variable: false,
@@ -1037,7 +1151,7 @@ const groups = {
1037
1151
  ariaLabelWrongRisk: {
1038
1152
  wcag: '1.3.1',
1039
1153
  weight: 1,
1040
- packages: {
1154
+ tools: {
1041
1155
  nuVal: {
1042
1156
  'Possible misuse of aria-label. (If you disagree with this warning, file an issue report or send e-mail to www-validator@w3.org.)': {
1043
1157
  variable: false,
@@ -1050,7 +1164,7 @@ const groups = {
1050
1164
  activeDescendantBadID: {
1051
1165
  wcag: '1.3.1',
1052
1166
  weight: 4,
1053
- packages: {
1167
+ tools: {
1054
1168
  continuum: {
1055
1169
  290: {
1056
1170
  variable: false,
@@ -1070,7 +1184,7 @@ const groups = {
1070
1184
  governedBadID: {
1071
1185
  wcag: '1.3.1',
1072
1186
  weight: 4,
1073
- packages: {
1187
+ tools: {
1074
1188
  continuum: {
1075
1189
  85: {
1076
1190
  variable: false,
@@ -1102,7 +1216,7 @@ const groups = {
1102
1216
  descriptionBadID: {
1103
1217
  wcag: '1.3.1',
1104
1218
  weight: 4,
1105
- packages: {
1219
+ tools: {
1106
1220
  continuum: {
1107
1221
  83: {
1108
1222
  variable: false,
@@ -1115,7 +1229,7 @@ const groups = {
1115
1229
  labelConfusionRisk: {
1116
1230
  wcag: '3.3.2',
1117
1231
  weight: 1,
1118
- packages: {
1232
+ tools: {
1119
1233
  ibm: {
1120
1234
  WCAG20_Input_LabelBefore: {
1121
1235
  variable: false,
@@ -1127,13 +1241,20 @@ const groups = {
1127
1241
  quality: 1,
1128
1242
  what: 'Checkbox or radio button label precedes the input control'
1129
1243
  }
1244
+ },
1245
+ qualWeb: {
1246
+ 'QW-WCAG-T17': {
1247
+ variable: false,
1248
+ quality: 1,
1249
+ what: 'label not positioned to maximize the predictability of the relationship'
1250
+ }
1130
1251
  }
1131
1252
  }
1132
1253
  },
1133
1254
  labelBadID: {
1134
1255
  wcag: '1.3.1',
1135
1256
  weight: 4,
1136
- packages: {
1257
+ tools: {
1137
1258
  continuum: {
1138
1259
  95: {
1139
1260
  variable: false,
@@ -1194,7 +1315,7 @@ const groups = {
1194
1315
  haspopupBad: {
1195
1316
  wcag: '1.3.1',
1196
1317
  weight: 4,
1197
- packages: {
1318
+ tools: {
1198
1319
  ibm: {
1199
1320
  combobox_haspopup: {
1200
1321
  variable: false,
@@ -1207,7 +1328,7 @@ const groups = {
1207
1328
  ownerConflict: {
1208
1329
  wcag: '1.3.1',
1209
1330
  weight: 4,
1210
- packages: {
1331
+ tools: {
1211
1332
  continuum: {
1212
1333
  360: {
1213
1334
  variable: false,
@@ -1220,7 +1341,7 @@ const groups = {
1220
1341
  linkNoText: {
1221
1342
  wcag: '2.4.4',
1222
1343
  weight: 4,
1223
- packages: {
1344
+ tools: {
1224
1345
  alfa: {
1225
1346
  r11: {
1226
1347
  variable: false,
@@ -1283,6 +1404,18 @@ const groups = {
1283
1404
  what: 'link element has an empty href attribute'
1284
1405
  }
1285
1406
  },
1407
+ qualWeb: {
1408
+ 'QW-ACT-R12': {
1409
+ variable: false,
1410
+ quality: 1,
1411
+ what: 'Link has no accessible name'
1412
+ },
1413
+ 'QW-WCAG-T21': {
1414
+ variable: false,
1415
+ quality: 1,
1416
+ what: 'Accessible name is not provided for an image which is the only content in a link'
1417
+ }
1418
+ },
1286
1419
  tenon: {
1287
1420
  57: {
1288
1421
  variable: false,
@@ -1312,7 +1445,7 @@ const groups = {
1312
1445
  linkBrokenRisk: {
1313
1446
  wcag: '1.3.1',
1314
1447
  weight: 2,
1315
- packages: {
1448
+ tools: {
1316
1449
  htmlcs: {
1317
1450
  'w:AA.4_1_2.H91.A.Placeholder': {
1318
1451
  variable: false,
@@ -1325,7 +1458,7 @@ const groups = {
1325
1458
  acronymNoTitle: {
1326
1459
  wcag: '3.1.4',
1327
1460
  weight: 4,
1328
- packages: {
1461
+ tools: {
1329
1462
  tenon: {
1330
1463
  117: {
1331
1464
  variable: false,
@@ -1338,20 +1471,27 @@ const groups = {
1338
1471
  abbreviationNoTitle: {
1339
1472
  wcag: '3.1.4',
1340
1473
  weight: 4,
1341
- packages: {
1474
+ tools: {
1342
1475
  tenon: {
1343
1476
  233: {
1344
1477
  variable: false,
1345
1478
  quality: 1,
1346
1479
  what: 'abbr element is first for its abbreviation but has no useful title value'
1347
1480
  }
1481
+ },
1482
+ qualWeb: {
1483
+ 'QW-WCAG-T7': {
1484
+ variable: false,
1485
+ quality: 1,
1486
+ what: 'Definition for an abbreviation not provided with an abbr element'
1487
+ }
1348
1488
  }
1349
1489
  }
1350
1490
  },
1351
1491
  pdfLink: {
1352
1492
  wcag: '1.3.3',
1353
1493
  weight: 1,
1354
- packages: {
1494
+ tools: {
1355
1495
  wave: {
1356
1496
  'a:link_pdf': {
1357
1497
  variable: false,
@@ -1364,7 +1504,7 @@ const groups = {
1364
1504
  destinationLink: {
1365
1505
  wcag: '1.3.1',
1366
1506
  weight: 2,
1367
- packages: {
1507
+ tools: {
1368
1508
  htmlcs: {
1369
1509
  'w:AA.4_1_2.H91.A.NoHref': {
1370
1510
  variable: false,
@@ -1391,7 +1531,7 @@ const groups = {
1391
1531
  textAreaNoText: {
1392
1532
  wcag: '1.3.1',
1393
1533
  weight: 4,
1394
- packages: {
1534
+ tools: {
1395
1535
  continuum: {
1396
1536
  872: {
1397
1537
  variable: false,
@@ -1408,16 +1548,34 @@ const groups = {
1408
1548
  }
1409
1549
  }
1410
1550
  },
1411
- linkTextsSame: {
1412
- wcag: '2.4.4',
1551
+ linkAltSame: {
1552
+ wcag: '1.1.1',
1413
1553
  weight: 2,
1414
- packages: {
1554
+ tools: {
1415
1555
  htmlcs: {
1416
1556
  'e:AA.1_1_1.H2.EG3': {
1417
1557
  variable: false,
1418
1558
  quality: 1,
1419
1559
  what: 'alt value of the link img element duplicates the text of a link beside it'
1420
1560
  }
1561
+ }
1562
+ }
1563
+ },
1564
+ linkTextsSame: {
1565
+ wcag: '2.4.4',
1566
+ weight: 2,
1567
+ tools: {
1568
+ qualWeb: {
1569
+ 'QW-ACT-R9': {
1570
+ variable: false,
1571
+ quality: 1,
1572
+ what: 'Links with identical accessible names have different purposes'
1573
+ },
1574
+ 'QW-ACT-R44': {
1575
+ variable: false,
1576
+ quality: 1,
1577
+ what: 'Links with identical accessible names and context serve different purposes'
1578
+ }
1421
1579
  },
1422
1580
  tenon: {
1423
1581
  98: {
@@ -1428,10 +1586,10 @@ const groups = {
1428
1586
  }
1429
1587
  }
1430
1588
  },
1431
- linkDestinationsSame: {
1589
+ areaDestinationsSame: {
1432
1590
  wcag: '2.4.4',
1433
1591
  weight: 2,
1434
- packages: {
1592
+ tools: {
1435
1593
  tenon: {
1436
1594
  132: {
1437
1595
  variable: false,
@@ -1441,10 +1599,23 @@ const groups = {
1441
1599
  }
1442
1600
  }
1443
1601
  },
1602
+ linksNoNav: {
1603
+ wcag: '1.3.1',
1604
+ weight: 2,
1605
+ tools: {
1606
+ qualWeb: {
1607
+ 'QW-BP4': {
1608
+ variable: false,
1609
+ quality: 1,
1610
+ what: 'Grouped links are not within a nav element'
1611
+ }
1612
+ }
1613
+ }
1614
+ },
1444
1615
  linkConfusionRisk: {
1445
1616
  wcag: '2.4.4',
1446
1617
  weight: 1,
1447
- packages: {
1618
+ tools: {
1448
1619
  axe: {
1449
1620
  'identical-links-same-purpose': {
1450
1621
  variable: false,
@@ -1457,7 +1628,19 @@ const groups = {
1457
1628
  linkPair: {
1458
1629
  wcag: '2.4.4',
1459
1630
  weight: 2,
1460
- packages: {
1631
+ tools: {
1632
+ qualWeb: {
1633
+ 'QW-WCAG-T10': {
1634
+ variable: false,
1635
+ quality: 1,
1636
+ what: 'Adjacent image and text links for the same resource are not combined'
1637
+ },
1638
+ 'QW-BP13': {
1639
+ variable: false,
1640
+ quality: 1,
1641
+ what: 'Consecutive links have the same href and one contains an image'
1642
+ }
1643
+ },
1461
1644
  tenon: {
1462
1645
  184: {
1463
1646
  variable: false,
@@ -1474,10 +1657,23 @@ const groups = {
1474
1657
  }
1475
1658
  }
1476
1659
  },
1660
+ pageNewWindow: {
1661
+ wcag: '3.2.5',
1662
+ weight: 3,
1663
+ tools: {
1664
+ qualWeb: {
1665
+ 'QW-WCAG-T22': {
1666
+ variable: false,
1667
+ quality: 1,
1668
+ what: 'New window opens as soon as a new page is loaded'
1669
+ }
1670
+ }
1671
+ }
1672
+ },
1477
1673
  formNewWindow: {
1478
1674
  wcag: '3.2.5',
1479
1675
  weight: 2,
1480
- packages: {
1676
+ tools: {
1481
1677
  tenon: {
1482
1678
  214: {
1483
1679
  variable: false,
@@ -1490,7 +1686,7 @@ const groups = {
1490
1686
  linkForcesNewWindow: {
1491
1687
  wcag: '3.2.5',
1492
1688
  weight: 3,
1493
- packages: {
1689
+ tools: {
1494
1690
  tenon: {
1495
1691
  218: {
1496
1692
  variable: false,
@@ -1503,7 +1699,7 @@ const groups = {
1503
1699
  linkWindowSurpriseRisk: {
1504
1700
  wcag: '3.2.5',
1505
1701
  weight: 1,
1506
- packages: {
1702
+ tools: {
1507
1703
  htmlcs: {
1508
1704
  'w:WCAG2AAA.Principle3.Guideline3_2.3_2_5.H83.3': {
1509
1705
  variable: false,
@@ -1516,7 +1712,7 @@ const groups = {
1516
1712
  selectNavSurpriseRisk: {
1517
1713
  wcag: '3.2.5',
1518
1714
  weight: 1,
1519
- packages: {
1715
+ tools: {
1520
1716
  wave: {
1521
1717
  'a:javascript_jumpmenu': {
1522
1718
  variable: false,
@@ -1529,7 +1725,7 @@ const groups = {
1529
1725
  buttonAlt: {
1530
1726
  wcag: '4.1.2',
1531
1727
  weight: 4,
1532
- packages: {
1728
+ tools: {
1533
1729
  nuVal: {
1534
1730
  'Attribute alt not allowed on element button at this point.': {
1535
1731
  variable: false,
@@ -1542,7 +1738,7 @@ const groups = {
1542
1738
  buttonNoText: {
1543
1739
  wcag: '4.1.2',
1544
1740
  weight: 4,
1545
- packages: {
1741
+ tools: {
1546
1742
  alfa: {
1547
1743
  r12: {
1548
1744
  variable: false,
@@ -1623,6 +1819,13 @@ const groups = {
1623
1819
  what: 'input element with type=button has no nonempty value attribute'
1624
1820
  }
1625
1821
  },
1822
+ qualWeb: {
1823
+ 'QW-ACT-R11': {
1824
+ variable: false,
1825
+ quality: 1,
1826
+ what: 'Button has no accessible name'
1827
+ }
1828
+ },
1626
1829
  wave: {
1627
1830
  'e:button_empty': {
1628
1831
  variable: false,
@@ -1635,20 +1838,27 @@ const groups = {
1635
1838
  menuItemNoText: {
1636
1839
  wcag: '4.1.2',
1637
1840
  weight: 4,
1638
- packages: {
1841
+ tools: {
1639
1842
  alfa: {
1640
1843
  r94: {
1641
1844
  variable: false,
1642
1845
  quality: 1,
1643
1846
  what: 'element with a menuitem role has no accessible name'
1644
1847
  }
1848
+ },
1849
+ qualWeb: {
1850
+ 'QW-ACT-R66': {
1851
+ variable: false,
1852
+ quality: 1,
1853
+ what: 'Menuitem has no non-empty accessible name'
1854
+ }
1645
1855
  }
1646
1856
  }
1647
1857
  },
1648
1858
  parentMissing: {
1649
1859
  wcag: '1.3.1',
1650
1860
  weight: 4,
1651
- packages: {
1861
+ tools: {
1652
1862
  alfa: {
1653
1863
  r42: {
1654
1864
  variable: false,
@@ -1669,13 +1879,67 @@ const groups = {
1669
1879
  quality: 1,
1670
1880
  what: 'Element is not contained within a role-valid element'
1671
1881
  }
1882
+ },
1883
+ nuVal: {
1884
+ '^An element with role=.+ must be contained in, or owned by, an element with role=.+$': {
1885
+ variable: true,
1886
+ quality: 1,
1887
+ what: 'Element has no required container or owner'
1888
+ }
1889
+ },
1890
+ qualWeb: {
1891
+ 'QW-ACT-R33': {
1892
+ variable: false,
1893
+ quality: 1,
1894
+ what: 'Element has no ARIA required context role'
1895
+ }
1896
+ }
1897
+ }
1898
+ },
1899
+ descendantMissing: {
1900
+ wcag: '1.3.1',
1901
+ weight: 4,
1902
+ tools: {
1903
+ alfa: {
1904
+ r68: {
1905
+ variable: false,
1906
+ quality: 1,
1907
+ what: 'Element does not own an element required by its semantic role'
1908
+ }
1909
+ },
1910
+ axe: {
1911
+ 'aria-required-children': {
1912
+ variable: false,
1913
+ quality: 1,
1914
+ what: 'ARIA role does not contain a required child'
1915
+ }
1916
+ },
1917
+ qualWeb: {
1918
+ 'QW-ACT-R38': {
1919
+ variable: false,
1920
+ quality: 1,
1921
+ what: 'Element has no ARIA required owned element'
1922
+ }
1923
+ }
1924
+ }
1925
+ },
1926
+ presentationChild: {
1927
+ wcag: '1.3.1',
1928
+ weight: 4,
1929
+ tools: {
1930
+ htmlcs: {
1931
+ 'e:AA.1_3_1.F92,ARIA4': {
1932
+ variable: false,
1933
+ quality: 1,
1934
+ what: 'Element has presentation role but semantic child'
1935
+ }
1672
1936
  }
1673
1937
  }
1674
1938
  },
1675
1939
  svgImageNoText: {
1676
1940
  wcag: '1.1.1',
1677
1941
  weight: 4,
1678
- packages: {
1942
+ tools: {
1679
1943
  alfa: {
1680
1944
  r43: {
1681
1945
  variable: false,
@@ -1696,26 +1960,40 @@ const groups = {
1696
1960
  quality: 1,
1697
1961
  what: 'svg element has no mechanism that allows an accessible name to be calculated'
1698
1962
  }
1963
+ },
1964
+ qualWeb: {
1965
+ 'QW-ACT-R21': {
1966
+ variable: false,
1967
+ quality: 1,
1968
+ what: 'svg element with an explicit role has no accessible name'
1969
+ }
1699
1970
  }
1700
1971
  }
1701
1972
  },
1702
1973
  cssBansRotate: {
1703
1974
  wcag: '1.3.4',
1704
1975
  weight: 4,
1705
- packages: {
1976
+ tools: {
1706
1977
  axe: {
1707
1978
  'css-orientation-lock': {
1708
1979
  variable: false,
1709
1980
  quality: 1,
1710
1981
  what: 'CSS media query locks display orientation'
1711
1982
  }
1983
+ },
1984
+ qualWeb: {
1985
+ 'QW-ACT-R7': {
1986
+ variable: false,
1987
+ quality: 1,
1988
+ what: 'Orientation of the page is restricted by a CSS transform property'
1989
+ }
1712
1990
  }
1713
1991
  }
1714
1992
  },
1715
1993
  textRotated: {
1716
1994
  wcag: '1.4.8',
1717
1995
  weight: 2,
1718
- packages: {
1996
+ tools: {
1719
1997
  tenon: {
1720
1998
  271: {
1721
1999
  variable: false,
@@ -1728,7 +2006,7 @@ const groups = {
1728
2006
  metaBansZoom: {
1729
2007
  wcag: '1.4.4',
1730
2008
  weight: 4,
1731
- packages: {
2009
+ tools: {
1732
2010
  alfa: {
1733
2011
  r47: {
1734
2012
  variable: false,
@@ -1766,38 +2044,12 @@ const groups = {
1766
2044
  quality: 1,
1767
2045
  what: 'viewport value prevents users from resizing the document'
1768
2046
  }
1769
- }
1770
- }
1771
- },
1772
- childMissing: {
1773
- wcag: '1.3.1',
1774
- weight: 4,
1775
- packages: {
1776
- alfa: {
1777
- r68: {
1778
- variable: false,
1779
- quality: 1,
1780
- what: 'Element does not own an element required by its semantic role'
1781
- }
1782
2047
  },
1783
- axe: {
1784
- 'aria-required-children': {
1785
- variable: false,
1786
- quality: 1,
1787
- what: 'ARIA role does not contain a required child'
1788
- }
1789
- }
1790
- }
1791
- },
1792
- presentationChild: {
1793
- wcag: '1.3.1',
1794
- weight: 4,
1795
- packages: {
1796
- htmlcs: {
1797
- 'e:AA.1_3_1.F92,ARIA4': {
2048
+ qualWeb: {
2049
+ 'QW-ACT-R14': {
1798
2050
  variable: false,
1799
2051
  quality: 1,
1800
- what: 'Element has presentation role but semantic child'
2052
+ what: 'meta viewport prevents zoom'
1801
2053
  }
1802
2054
  }
1803
2055
  }
@@ -1805,20 +2057,27 @@ const groups = {
1805
2057
  fontSizeAbsolute: {
1806
2058
  wcag: '1.4.4',
1807
2059
  weight: 2,
1808
- packages: {
2060
+ tools: {
1809
2061
  alfa: {
1810
2062
  r74: {
1811
2063
  variable: false,
1812
2064
  quality: 1,
1813
2065
  what: 'Paragraph text has an absolute font size'
1814
2066
  }
2067
+ },
2068
+ qualWeb: {
2069
+ 'QW-WCAG-T28': {
2070
+ variable: false,
2071
+ quality: 1,
2072
+ what: 'Percent, em, or name is used for a font size'
2073
+ }
1815
2074
  }
1816
2075
  }
1817
2076
  },
1818
2077
  fontSmall: {
1819
2078
  wcag: '1.4',
1820
2079
  weight: 3,
1821
- packages: {
2080
+ tools: {
1822
2081
  alfa: {
1823
2082
  r75: {
1824
2083
  variable: false,
@@ -1849,10 +2108,10 @@ const groups = {
1849
2108
  }
1850
2109
  }
1851
2110
  },
1852
- leadingFrozen: {
2111
+ textSpacingFrozen: {
1853
2112
  wcag: '1.4.12',
1854
2113
  weight: 4,
1855
- packages: {
2114
+ tools: {
1856
2115
  alfa: {
1857
2116
  r93: {
1858
2117
  variable: false,
@@ -1866,13 +2125,30 @@ const groups = {
1866
2125
  quality: 1,
1867
2126
  what: 'Inline text spacing is not adjustable with a custom stylesheet'
1868
2127
  }
2128
+ },
2129
+ qualWeb: {
2130
+ 'QW-ACT-R67': {
2131
+ variable: false,
2132
+ quality: 1,
2133
+ what: 'Letter spacing in a style attribute is !important'
2134
+ },
2135
+ 'QW-ACT-R68': {
2136
+ variable: false,
2137
+ quality: 1,
2138
+ what: 'Line height in a style attribute is !important'
2139
+ },
2140
+ 'QW-ACT-R69': {
2141
+ variable: false,
2142
+ quality: 1,
2143
+ what: 'Word spacing in a style attribute is !important'
2144
+ }
1869
2145
  }
1870
2146
  }
1871
2147
  },
1872
2148
  leadingAbsolute: {
1873
2149
  wcag: '1.4.12',
1874
2150
  weight: 2,
1875
- packages: {
2151
+ tools: {
1876
2152
  alfa: {
1877
2153
  r80: {
1878
2154
  variable: false,
@@ -1885,7 +2161,7 @@ const groups = {
1885
2161
  leadingInsufficient: {
1886
2162
  wcag: '1.4.8',
1887
2163
  weight: 2,
1888
- packages: {
2164
+ tools: {
1889
2165
  alfa: {
1890
2166
  r73: {
1891
2167
  variable: false,
@@ -1898,7 +2174,7 @@ const groups = {
1898
2174
  leadingClipsText: {
1899
2175
  wcag: '1.4.8',
1900
2176
  weight: 4,
1901
- packages: {
2177
+ tools: {
1902
2178
  tenon: {
1903
2179
  144: {
1904
2180
  variable: false,
@@ -1911,20 +2187,45 @@ const groups = {
1911
2187
  overflowHidden: {
1912
2188
  wcag: '1.4.4',
1913
2189
  weight: 4,
1914
- packages: {
2190
+ tools: {
1915
2191
  alfa: {
1916
2192
  r83: {
1917
2193
  variable: false,
1918
2194
  quality: 1,
1919
2195
  what: 'Overflow is hidden or clipped if the text is enlarged'
1920
2196
  }
2197
+ },
2198
+ qualWeb: {
2199
+ 'QW-ACT-R40': {
2200
+ variable: false,
2201
+ quality: 1,
2202
+ what: 'Zoomed text node is clipped by a CSS overflow declaration'
2203
+ }
2204
+ }
2205
+ }
2206
+ },
2207
+ boxSizeAbsolute: {
2208
+ wcag: '1.4.4',
2209
+ weight: 3,
2210
+ tools: {
2211
+ qualWeb: {
2212
+ 'QW-BP14': {
2213
+ variable: false,
2214
+ quality: 1,
2215
+ what: 'Container width is specified in px'
2216
+ },
2217
+ 'QW-BP15': {
2218
+ variable: false,
2219
+ quality: 1,
2220
+ what: 'Element width is specified in an absolute value'
2221
+ }
1921
2222
  }
1922
2223
  }
1923
2224
  },
1924
2225
  titleBad: {
1925
2226
  wcag: '1.3.1',
1926
2227
  weight: 4,
1927
- packages: {
2228
+ tools: {
1928
2229
  nuVal: {
1929
2230
  'Element title not allowed as child of element body in this context. (Suppressing further errors from this subtree.)': {
1930
2231
  variable: false,
@@ -1944,7 +2245,7 @@ const groups = {
1944
2245
  linkElementBad: {
1945
2246
  wcag: '1.3.1',
1946
2247
  weight: 4,
1947
- packages: {
2248
+ tools: {
1948
2249
  nuVal: {
1949
2250
  'A link element must not appear as a descendant of a body element unless the link element has an itemprop attribute or has a rel attribute whose value contains dns-prefetch, modulepreload, pingback, preconnect, prefetch, preload, prerender, or stylesheet.': {
1950
2251
  variable: false,
@@ -1962,7 +2263,7 @@ const groups = {
1962
2263
  metaBad: {
1963
2264
  wcag: '1.3.1',
1964
2265
  weight: 3,
1965
- packages: {
2266
+ tools: {
1966
2267
  nuVal: {
1967
2268
  '^Attribute .+ not allowed on element meta at this point.*$': {
1968
2269
  variable: true,
@@ -2015,7 +2316,7 @@ const groups = {
2015
2316
  scriptElementBad: {
2016
2317
  wcag: '1.3.1',
2017
2318
  weight: 4,
2018
- packages: {
2319
+ tools: {
2019
2320
  nuVal: {
2020
2321
  'Element script must not have attribute defer unless attribute src is also specified.': {
2021
2322
  variable: false,
@@ -2033,7 +2334,7 @@ const groups = {
2033
2334
  itemTypeBad: {
2034
2335
  wcag: '1.3.1',
2035
2336
  weight: 4,
2036
- packages: {
2337
+ tools: {
2037
2338
  nuVal: {
2038
2339
  'The itemtype attribute must not be specified on elements that do not have an itemscope attribute specified.': {
2039
2340
  variable: false,
@@ -2046,7 +2347,7 @@ const groups = {
2046
2347
  iframeTitleBad: {
2047
2348
  wcag: '4.1.2',
2048
2349
  weight: 4,
2049
- packages: {
2350
+ tools: {
2050
2351
  alfa: {
2051
2352
  r13: {
2052
2353
  variable: false,
@@ -2086,13 +2387,25 @@ const groups = {
2086
2387
  quality: 1,
2087
2388
  what: 'Inline frame has an empty or nonunique title attribute'
2088
2389
  }
2390
+ },
2391
+ qualWeb: {
2392
+ 'QW-ACT-R10': {
2393
+ variable: false,
2394
+ quality: 1,
2395
+ what: 'iframe elements with identical accessible names have different purposes'
2396
+ },
2397
+ 'QW-ACT-R19': {
2398
+ variable: false,
2399
+ quality: 1,
2400
+ what: 'iframe element has no accessible name'
2401
+ }
2089
2402
  }
2090
2403
  }
2091
2404
  },
2092
2405
  roleBad: {
2093
2406
  wcag: '4.1.2',
2094
2407
  weight: 3,
2095
- packages: {
2408
+ tools: {
2096
2409
  alfa: {
2097
2410
  r21: {
2098
2411
  variable: false,
@@ -2233,6 +2546,13 @@ const groups = {
2233
2546
  what: 'Table cell has a role attribute'
2234
2547
  }
2235
2548
  },
2549
+ qualWeb: {
2550
+ 'QW-ACT-R20': {
2551
+ variable: false,
2552
+ quality: 1,
2553
+ what: 'role attribute has an invalid value'
2554
+ }
2555
+ },
2236
2556
  testaro: {
2237
2557
  role: {
2238
2558
  variable: false,
@@ -2245,7 +2565,7 @@ const groups = {
2245
2565
  roleRedundant: {
2246
2566
  wcag: '4.1.2',
2247
2567
  weight: 1,
2248
- packages: {
2568
+ tools: {
2249
2569
  ibm: {
2250
2570
  aria_role_redundant: {
2251
2571
  variable: false,
@@ -2280,7 +2600,7 @@ const groups = {
2280
2600
  attributeBad: {
2281
2601
  wcag: '1.3.1',
2282
2602
  weight: 4,
2283
- packages: {
2603
+ tools: {
2284
2604
  ibm: {
2285
2605
  Valerie_Elem_DirValid: {
2286
2606
  variable: false,
@@ -2365,7 +2685,7 @@ const groups = {
2365
2685
  attributeMissing: {
2366
2686
  wcag: '4.1.2',
2367
2687
  weight: 4,
2368
- packages: {
2688
+ tools: {
2369
2689
  axe: {
2370
2690
  'aria-required-attr': {
2371
2691
  variable: false,
@@ -2412,7 +2732,7 @@ const groups = {
2412
2732
  roleMissing: {
2413
2733
  wcag: '4.1.2',
2414
2734
  weight: 3,
2415
- packages: {
2735
+ tools: {
2416
2736
  nuVal: {
2417
2737
  '^Element .+ is missing required attribute role.*$': {
2418
2738
  variable: true,
@@ -2425,7 +2745,7 @@ const groups = {
2425
2745
  roleMissingRisk: {
2426
2746
  wcag: '4.1.2',
2427
2747
  weight: 1,
2428
- packages: {
2748
+ tools: {
2429
2749
  nuVal: {
2430
2750
  '^Element .+ is missing one or more of the following attributes: role.*$': {
2431
2751
  variable: true,
@@ -2438,7 +2758,7 @@ const groups = {
2438
2758
  ariaMissing: {
2439
2759
  wcag: '4.1.2',
2440
2760
  weight: 4,
2441
- packages: {
2761
+ tools: {
2442
2762
  alfa: {
2443
2763
  r16: {
2444
2764
  variable: false,
@@ -2470,6 +2790,13 @@ const groups = {
2470
2790
  what: 'Element is missing a required ARIA attribute'
2471
2791
  }
2472
2792
  },
2793
+ qualWeb: {
2794
+ 'QW-ACT-R28': {
2795
+ variable: false,
2796
+ quality: 1,
2797
+ what: 'Element with a role attribute does not have the required states and properties'
2798
+ }
2799
+ },
2473
2800
  wave: {
2474
2801
  'e:aria_reference_broken': {
2475
2802
  variable: false,
@@ -2482,7 +2809,7 @@ const groups = {
2482
2809
  ariaBadAttribute: {
2483
2810
  wcag: '4.1.2',
2484
2811
  weight: 4,
2485
- packages: {
2812
+ tools: {
2486
2813
  alfa: {
2487
2814
  r18: {
2488
2815
  variable: false,
@@ -2687,13 +3014,30 @@ const groups = {
2687
3014
  quality: 1,
2688
3015
  what: 'input element with a button role and type="checkbox" has no aria-pressed="true"'
2689
3016
  }
3017
+ },
3018
+ qualWeb: {
3019
+ 'QW-ACT-R25': {
3020
+ variable: false,
3021
+ quality: 1,
3022
+ what: 'ARIA state or property is not permitted'
3023
+ },
3024
+ 'QW-ACT-R27': {
3025
+ variable: false,
3026
+ quality: 1,
3027
+ what: 'aria- attribute is not defined in ARIA 1.1'
3028
+ },
3029
+ 'QW-ACT-R34': {
3030
+ variable: false,
3031
+ quality: 1,
3032
+ what: 'ARIA state or property has an invalid value'
3033
+ }
2690
3034
  }
2691
3035
  }
2692
3036
  },
2693
3037
  ariaRedundant: {
2694
3038
  wcag: '4.1.2',
2695
3039
  weight: 1,
2696
- packages: {
3040
+ tools: {
2697
3041
  continuum: {
2698
3042
  205: {
2699
3043
  variable: false,
@@ -2720,7 +3064,7 @@ const groups = {
2720
3064
  ariaReferenceBad: {
2721
3065
  wcag: '1.3.1',
2722
3066
  weight: 4,
2723
- packages: {
3067
+ tools: {
2724
3068
  ibm: {
2725
3069
  Rpt_Aria_ValidIdRef: {
2726
3070
  variable: false,
@@ -2740,7 +3084,7 @@ const groups = {
2740
3084
  autocompleteBad: {
2741
3085
  wcag: '1.3.5',
2742
3086
  weight: 3,
2743
- packages: {
3087
+ tools: {
2744
3088
  alfa: {
2745
3089
  r10: {
2746
3090
  variable: false,
@@ -2775,13 +3119,20 @@ const groups = {
2775
3119
  quality: 1,
2776
3120
  what: 'autocomplete attribute has an empty value'
2777
3121
  }
3122
+ },
3123
+ qualWeb: {
3124
+ 'QW-ACT-R24': {
3125
+ variable: false,
3126
+ quality: 1,
3127
+ what: 'autocomplete attribute has no valid value'
3128
+ }
2778
3129
  }
2779
3130
  }
2780
3131
  },
2781
3132
  autocompleteRisk: {
2782
3133
  wcag: '1.3.5',
2783
3134
  weight: 1,
2784
- packages: {
3135
+ tools: {
2785
3136
  htmlcs: {
2786
3137
  'w:AA.1_3_5.H98': {
2787
3138
  variable: false,
@@ -2791,10 +3142,10 @@ const groups = {
2791
3142
  }
2792
3143
  }
2793
3144
  },
2794
- contrastAA: {
3145
+ textContrastAA: {
2795
3146
  wcag: '1.4.3',
2796
3147
  weight: 4,
2797
- packages: {
3148
+ tools: {
2798
3149
  alfa: {
2799
3150
  r69: {
2800
3151
  variable: false,
@@ -2828,6 +3179,13 @@ const groups = {
2828
3179
  what: 'Contrast ratio of text with background does not meet WCAG 2.1 AA'
2829
3180
  }
2830
3181
  },
3182
+ qualWeb: {
3183
+ 'QW-ACT-R37': {
3184
+ variable: false,
3185
+ quality: 1,
3186
+ what: 'Text has less than the minimum contrast'
3187
+ }
3188
+ },
2831
3189
  wave: {
2832
3190
  'c:contrast': {
2833
3191
  variable: false,
@@ -2837,10 +3195,23 @@ const groups = {
2837
3195
  }
2838
3196
  }
2839
3197
  },
3198
+ colorMissing: {
3199
+ wcag: '1.4.3',
3200
+ weight: 4,
3201
+ tools: {
3202
+ qualWeb: {
3203
+ 'QW-WCAG-T31': {
3204
+ variable: false,
3205
+ quality: 1,
3206
+ what: 'Foreground or background color is specified but not both'
3207
+ }
3208
+ }
3209
+ }
3210
+ },
2840
3211
  contrastAAA: {
2841
3212
  wcag: '1.4.6',
2842
3213
  weight: 1,
2843
- packages: {
3214
+ tools: {
2844
3215
  alfa: {
2845
3216
  r66: {
2846
3217
  variable: false,
@@ -2862,6 +3233,13 @@ const groups = {
2862
3233
  what: 'Insufficient contrast'
2863
3234
  }
2864
3235
  },
3236
+ qualWeb: {
3237
+ 'QW-ACT-R76': {
3238
+ variable: false,
3239
+ quality: 1,
3240
+ what: 'Text has less than the enhanced minimum contrast'
3241
+ }
3242
+ },
2865
3243
  tenon: {
2866
3244
  95: {
2867
3245
  variable: false,
@@ -2874,7 +3252,7 @@ const groups = {
2874
3252
  contrastRisk: {
2875
3253
  wcag: '1.4.3',
2876
3254
  weight: 1,
2877
- packages: {
3255
+ tools: {
2878
3256
  htmlcs: {
2879
3257
  'w:AA.1_4_3_F24.F24.BGColour': {
2880
3258
  variable: false,
@@ -2922,7 +3300,7 @@ const groups = {
2922
3300
  idEmpty: {
2923
3301
  wcag: '1.3.1',
2924
3302
  weight: 4,
2925
- packages: {
3303
+ tools: {
2926
3304
  nuVal: {
2927
3305
  '^Bad value for attribute .+ on element .+: An ID must not be the empty string.+$': {
2928
3306
  variable: true,
@@ -2940,7 +3318,7 @@ const groups = {
2940
3318
  targetEmpty: {
2941
3319
  wcag: '1.3.1',
2942
3320
  weight: 4,
2943
- packages: {
3321
+ tools: {
2944
3322
  nuVal: {
2945
3323
  'Bad value for attribute target on element a: Browsing context name must be at least one character long.': {
2946
3324
  variable: false,
@@ -2953,7 +3331,7 @@ const groups = {
2953
3331
  headingEmpty: {
2954
3332
  wcag: '2.4.6',
2955
3333
  weight: 3,
2956
- packages: {
3334
+ tools: {
2957
3335
  alfa: {
2958
3336
  r64: {
2959
3337
  variable: false,
@@ -2989,6 +3367,13 @@ const groups = {
2989
3367
  what: 'Empty heading'
2990
3368
  }
2991
3369
  },
3370
+ qualWeb: {
3371
+ 'QW-ACT-R33': {
3372
+ variable: false,
3373
+ quality: 1,
3374
+ what: 'Heading has no accessible name'
3375
+ }
3376
+ },
2992
3377
  wave: {
2993
3378
  'e:heading_empty': {
2994
3379
  variable: false,
@@ -3001,7 +3386,7 @@ const groups = {
3001
3386
  headingOfNothing: {
3002
3387
  wcag: '1.3.1',
3003
3388
  weight: 2,
3004
- packages: {
3389
+ tools: {
3005
3390
  alfa: {
3006
3391
  r78: {
3007
3392
  variable: false,
@@ -3014,7 +3399,7 @@ const groups = {
3014
3399
  typeRedundant: {
3015
3400
  wcag: '1.3.1',
3016
3401
  weight: 1,
3017
- packages: {
3402
+ tools: {
3018
3403
  nuVal: {
3019
3404
  'The type attribute is unnecessary for JavaScript resources.': {
3020
3405
  variable: false,
@@ -3032,7 +3417,7 @@ const groups = {
3032
3417
  imageTextRedundant: {
3033
3418
  wcag: '1.1.1',
3034
3419
  weight: 1,
3035
- packages: {
3420
+ tools: {
3036
3421
  axe: {
3037
3422
  'image-redundant-alt': {
3038
3423
  variable: false,
@@ -3066,7 +3451,7 @@ const groups = {
3066
3451
  decorativeTitle: {
3067
3452
  wcag: '1.3.1',
3068
3453
  weight: 1,
3069
- packages: {
3454
+ tools: {
3070
3455
  htmlcs: {
3071
3456
  'e:AA.1_1_1.H67.1': {
3072
3457
  variable: false,
@@ -3093,7 +3478,14 @@ const groups = {
3093
3478
  titleRedundant: {
3094
3479
  wcag: '1.3.1',
3095
3480
  weight: 1,
3096
- packages: {
3481
+ tools: {
3482
+ qualWeb: {
3483
+ 'QW-BP3': {
3484
+ variable: false,
3485
+ quality: 1,
3486
+ what: 'Link text content is equal to the title attribute'
3487
+ }
3488
+ },
3097
3489
  tenon: {
3098
3490
  79: {
3099
3491
  variable: false,
@@ -3113,7 +3505,7 @@ const groups = {
3113
3505
  titleEmpty: {
3114
3506
  wcag: '1.3.1',
3115
3507
  weight: 1,
3116
- packages: {
3508
+ tools: {
3117
3509
  continuum: {
3118
3510
  152: {
3119
3511
  variable: false,
@@ -3145,7 +3537,7 @@ const groups = {
3145
3537
  docType: {
3146
3538
  wcag: '1.3.1',
3147
3539
  weight: 10,
3148
- packages: {
3540
+ tools: {
3149
3541
  nuVal: {
3150
3542
  'Start tag seen without seeing a doctype first. Expected <!DOCTYPE html>.': {
3151
3543
  variable: false,
@@ -3165,7 +3557,7 @@ const groups = {
3165
3557
  pageTitle: {
3166
3558
  wcag: '2.4.2',
3167
3559
  weight: 10,
3168
- packages: {
3560
+ tools: {
3169
3561
  alfa: {
3170
3562
  r1: {
3171
3563
  variable: false,
@@ -3213,6 +3605,13 @@ const groups = {
3213
3605
  what: 'head element has no child title element'
3214
3606
  }
3215
3607
  },
3608
+ qualWeb: {
3609
+ 'QW-ACT-R1': {
3610
+ variable: false,
3611
+ quality: 1,
3612
+ what: 'HTML page has no title'
3613
+ }
3614
+ },
3216
3615
  wave: {
3217
3616
  'e:title_invalid': {
3218
3617
  variable: false,
@@ -3225,7 +3624,7 @@ const groups = {
3225
3624
  headingStructure: {
3226
3625
  wcag: '1.3.1',
3227
3626
  weight: 2,
3228
- packages: {
3627
+ tools: {
3229
3628
  alfa: {
3230
3629
  r53: {
3231
3630
  variable: false,
@@ -3254,6 +3653,13 @@ const groups = {
3254
3653
  what: 'Page contains more than 1 h1 element'
3255
3654
  }
3256
3655
  },
3656
+ qualWeb: {
3657
+ 'QW-WCAG-T9': {
3658
+ variable: false,
3659
+ quality: 1,
3660
+ what: 'Page is not organized using headings'
3661
+ }
3662
+ },
3257
3663
  tenon: {
3258
3664
  155: {
3259
3665
  variable: false,
@@ -3273,7 +3679,7 @@ const groups = {
3273
3679
  headingLevelless: {
3274
3680
  wcag: '1.3.1',
3275
3681
  weight: 1,
3276
- packages: {
3682
+ tools: {
3277
3683
  continuum: {
3278
3684
  71: {
3279
3685
  variable: false,
@@ -3286,7 +3692,7 @@ const groups = {
3286
3692
  noHeading: {
3287
3693
  wcag: '1.3.1',
3288
3694
  weight: 3,
3289
- packages: {
3695
+ tools: {
3290
3696
  alfa: {
3291
3697
  r59: {
3292
3698
  variable: false,
@@ -3294,6 +3700,13 @@ const groups = {
3294
3700
  what: 'Document has no headings'
3295
3701
  }
3296
3702
  },
3703
+ qualWeb: {
3704
+ 'QW-BP1': {
3705
+ variable: false,
3706
+ quality: 1,
3707
+ what: 'h1-h6 not used to identify headings'
3708
+ }
3709
+ },
3297
3710
  wave: {
3298
3711
  'a:heading_missing': {
3299
3712
  variable: false,
@@ -3306,7 +3719,7 @@ const groups = {
3306
3719
  h1Missing: {
3307
3720
  wcag: '1.3.1',
3308
3721
  weight: 3,
3309
- packages: {
3722
+ tools: {
3310
3723
  alfa: {
3311
3724
  r61: {
3312
3725
  variable: false,
@@ -3333,7 +3746,7 @@ const groups = {
3333
3746
  articleHeadingless: {
3334
3747
  wcag: '1.3.1',
3335
3748
  weight: 1,
3336
- packages: {
3749
+ tools: {
3337
3750
  nuVal: {
3338
3751
  'Article lacks heading. Consider using h2-h6 elements to add identifying headings to all articles.': {
3339
3752
  variable: false,
@@ -3346,7 +3759,7 @@ const groups = {
3346
3759
  sectionHeadingless: {
3347
3760
  wcag: '1.3.1',
3348
3761
  weight: 1,
3349
- packages: {
3762
+ tools: {
3350
3763
  nuVal: {
3351
3764
  'Section lacks heading. Consider using h2-h6 elements to add identifying headings to all sections.': {
3352
3765
  variable: false,
@@ -3363,8 +3776,8 @@ const groups = {
3363
3776
  },
3364
3777
  justification: {
3365
3778
  wcag: '1.4.8',
3366
- weight: 1,
3367
- packages: {
3779
+ weight: 2,
3780
+ tools: {
3368
3781
  alfa: {
3369
3782
  r71: {
3370
3783
  variable: false,
@@ -3379,6 +3792,13 @@ const groups = {
3379
3792
  what: 'Text is fully justified'
3380
3793
  }
3381
3794
  },
3795
+ qualWeb: {
3796
+ 'QW-WCAG-T27': {
3797
+ variable: false,
3798
+ quality: 1,
3799
+ what: 'Text is justified (aligned to both the left and the right margins)'
3800
+ }
3801
+ },
3382
3802
  wave: {
3383
3803
  'a:text_justified': {
3384
3804
  variable: false,
@@ -3388,10 +3808,23 @@ const groups = {
3388
3808
  }
3389
3809
  }
3390
3810
  },
3811
+ justificationRisk: {
3812
+ wcag: '1.4.8',
3813
+ weight: 1,
3814
+ tools: {
3815
+ qualWeb: {
3816
+ 'QW-WCAG-T29': {
3817
+ variable: false,
3818
+ quality: 1,
3819
+ what: 'Alignment either to the left or right is not specified in CSS'
3820
+ }
3821
+ }
3822
+ }
3823
+ },
3391
3824
  nonSemanticText: {
3392
3825
  wcag: '1.3.1',
3393
3826
  weight: 2,
3394
- packages: {
3827
+ tools: {
3395
3828
  htmlcs: {
3396
3829
  'w:AA.1_3_1.H49.AlignAttr': {
3397
3830
  variable: false,
@@ -3439,7 +3872,7 @@ const groups = {
3439
3872
  pseudoParagraphRisk: {
3440
3873
  wcag: '1.3.1',
3441
3874
  weight: 1,
3442
- packages: {
3875
+ tools: {
3443
3876
  tenon: {
3444
3877
  242: {
3445
3878
  variable: false,
@@ -3449,10 +3882,23 @@ const groups = {
3449
3882
  }
3450
3883
  }
3451
3884
  },
3885
+ pseudoList: {
3886
+ wcag: '1.3.1',
3887
+ weight: 1,
3888
+ tools: {
3889
+ qualWeb: {
3890
+ 'QW-BP11': {
3891
+ variable: false,
3892
+ quality: 1,
3893
+ what: 'br is used to make a list'
3894
+ }
3895
+ }
3896
+ }
3897
+ },
3452
3898
  pseudoCodeRisk: {
3453
3899
  wcag: '1.3.1',
3454
3900
  weight: 1,
3455
- packages: {
3901
+ tools: {
3456
3902
  alfa: {
3457
3903
  r79: {
3458
3904
  variable: false,
@@ -3465,7 +3911,7 @@ const groups = {
3465
3911
  pseudoHeadingRisk: {
3466
3912
  wcag: '1.3.1',
3467
3913
  weight: 1,
3468
- packages: {
3914
+ tools: {
3469
3915
  axe: {
3470
3916
  'p-as-heading': {
3471
3917
  variable: false,
@@ -3492,7 +3938,7 @@ const groups = {
3492
3938
  pseudoLinkRisk: {
3493
3939
  wcag: '1.3.1',
3494
3940
  weight: 1,
3495
- packages: {
3941
+ tools: {
3496
3942
  tenon: {
3497
3943
  129: {
3498
3944
  variable: false,
@@ -3512,7 +3958,7 @@ const groups = {
3512
3958
  listChild: {
3513
3959
  wcag: '1.3.1',
3514
3960
  weight: 4,
3515
- packages: {
3961
+ tools: {
3516
3962
  axe: {
3517
3963
  list: {
3518
3964
  variable: false,
@@ -3561,7 +4007,7 @@ const groups = {
3561
4007
  listItemOrphan: {
3562
4008
  wcag: '1.3.1',
3563
4009
  weight: 4,
3564
- packages: {
4010
+ tools: {
3565
4011
  axe: {
3566
4012
  listitem: {
3567
4013
  variable: false,
@@ -3593,7 +4039,7 @@ const groups = {
3593
4039
  pseudoListRisk: {
3594
4040
  wcag: '1.3.1',
3595
4041
  weight: 1,
3596
- packages: {
4042
+ tools: {
3597
4043
  wave: {
3598
4044
  'a:list_possible': {
3599
4045
  variable: false,
@@ -3606,7 +4052,7 @@ const groups = {
3606
4052
  pseudoOrderedListRisk: {
3607
4053
  wcag: '1.3.1',
3608
4054
  weight: 1,
3609
- packages: {
4055
+ tools: {
3610
4056
  htmlcs: {
3611
4057
  'w:AA.1_3_1.H48.2': {
3612
4058
  variable: false,
@@ -3616,23 +4062,30 @@ const groups = {
3616
4062
  }
3617
4063
  }
3618
4064
  },
3619
- pseudoNavListRisk: {
4065
+ pseudoNavList: {
3620
4066
  wcag: '1.3.1',
3621
- weight: 1,
3622
- packages: {
4067
+ weight: 2,
4068
+ tools: {
3623
4069
  htmlcs: {
3624
4070
  'w:AA.1_3_1.H48': {
3625
4071
  variable: false,
3626
4072
  quality: 1,
3627
4073
  what: 'Navigation links are not coded as a list'
3628
4074
  }
4075
+ },
4076
+ qualWeb: {
4077
+ 'QW-WCAG-T32': {
4078
+ variable: false,
4079
+ quality: 1,
4080
+ what: 'ol, ul or dl is not used for a list or group of links'
4081
+ }
3629
4082
  }
3630
4083
  }
3631
4084
  },
3632
4085
  selectNoText: {
3633
4086
  wcag: '4.1.2',
3634
4087
  weight: 3,
3635
- packages: {
4088
+ tools: {
3636
4089
  axe: {
3637
4090
  'select-name': {
3638
4091
  variable: false,
@@ -3671,7 +4124,7 @@ const groups = {
3671
4124
  optionNoText: {
3672
4125
  wcag: '4.1.2',
3673
4126
  weight: 4,
3674
- packages: {
4127
+ tools: {
3675
4128
  nuVal: {
3676
4129
  'Element option without attribute label must not be empty.': {
3677
4130
  variable: false,
@@ -3684,7 +4137,7 @@ const groups = {
3684
4137
  selectFlatRisk: {
3685
4138
  wcag: '1.3.1',
3686
4139
  weight: 1,
3687
- packages: {
4140
+ tools: {
3688
4141
  htmlcs: {
3689
4142
  'w:AA.1_3_1.H85.2': {
3690
4143
  variable: false,
@@ -3697,7 +4150,7 @@ const groups = {
3697
4150
  accessKeyDuplicate: {
3698
4151
  wcag: '1.3.1',
3699
4152
  weight: 3,
3700
- packages: {
4153
+ tools: {
3701
4154
  axe: {
3702
4155
  accesskeys: {
3703
4156
  variable: false,
@@ -3731,7 +4184,7 @@ const groups = {
3731
4184
  fieldSetMissing: {
3732
4185
  wcag: '1.3.1',
3733
4186
  weight: 2,
3734
- packages: {
4187
+ tools: {
3735
4188
  ibm: {
3736
4189
  WCAG20_Input_RadioChkInFieldSet: {
3737
4190
  variable: false,
@@ -3758,7 +4211,7 @@ const groups = {
3758
4211
  fieldSetRisk: {
3759
4212
  wcag: '1.3.1',
3760
4213
  weight: 1,
3761
- packages: {
4214
+ tools: {
3762
4215
  htmlcs: {
3763
4216
  'w:AA.1_3_1.H71.SameName': {
3764
4217
  variable: false,
@@ -3771,7 +4224,7 @@ const groups = {
3771
4224
  legendMisplaced: {
3772
4225
  wcag: '4.1.2',
3773
4226
  weight: 4,
3774
- packages: {
4227
+ tools: {
3775
4228
  continuum: {
3776
4229
  738: {
3777
4230
  variable: false,
@@ -3784,7 +4237,7 @@ const groups = {
3784
4237
  legendMissing: {
3785
4238
  wcag: '4.1.2',
3786
4239
  weight: 2,
3787
- packages: {
4240
+ tools: {
3788
4241
  continuum: {
3789
4242
  221: {
3790
4243
  variable: false,
@@ -3796,7 +4249,7 @@ const groups = {
3796
4249
  'e:AA.1_3_1.H71.NoLegend': {
3797
4250
  variable: false,
3798
4251
  quality: 1,
3799
- what: 'Fieldset has no legend element'
4252
+ what: 'fieldset has no legend element'
3800
4253
  }
3801
4254
  },
3802
4255
  ibm: {
@@ -3806,11 +4259,18 @@ const groups = {
3806
4259
  what: 'fieldset element has no single, non-empty legend as a label'
3807
4260
  }
3808
4261
  },
4262
+ qualWeb: {
4263
+ 'QW-WCAG-T3': {
4264
+ variable: false,
4265
+ quality: 1,
4266
+ what: 'Description for a group of form controls using fieldset and legend elements is not provided'
4267
+ }
4268
+ },
3809
4269
  wave: {
3810
4270
  'a:legend_missing': {
3811
4271
  variable: false,
3812
4272
  quality: 1,
3813
- what: 'Fieldset has no legend element'
4273
+ what: 'fieldset has no legend element'
3814
4274
  }
3815
4275
  }
3816
4276
  }
@@ -3818,7 +4278,7 @@ const groups = {
3818
4278
  groupName: {
3819
4279
  wcag: '4.1.2',
3820
4280
  weight: 3,
3821
- packages: {
4281
+ tools: {
3822
4282
  alfa: {
3823
4283
  r60: {
3824
4284
  variable: false,
@@ -3845,7 +4305,19 @@ const groups = {
3845
4305
  layoutTable: {
3846
4306
  wcag: '1.3.1',
3847
4307
  weight: 2,
3848
- packages: {
4308
+ tools: {
4309
+ qualWeb: {
4310
+ 'QW-WCAG-T12': {
4311
+ variable: false,
4312
+ quality: 1,
4313
+ what: 'th or caption element or non-empty summary attribute used in a layout table'
4314
+ },
4315
+ 'QW-BP9': {
4316
+ variable: false,
4317
+ quality: 1,
4318
+ what: 'Table element without header cells has a caption'
4319
+ }
4320
+ },
3849
4321
  testaro: {
3850
4322
  nonTable: {
3851
4323
  variable: false,
@@ -3862,10 +4334,36 @@ const groups = {
3862
4334
  }
3863
4335
  }
3864
4336
  },
4337
+ tabularTableless: {
4338
+ wcag: '1.3.1',
4339
+ weight: 3,
4340
+ tools: {
4341
+ qualWeb: {
4342
+ 'QW-WCAG-T18': {
4343
+ variable: false,
4344
+ quality: 1,
4345
+ what: 'Table markup not used to present tabular information'
4346
+ }
4347
+ }
4348
+ }
4349
+ },
4350
+ tableNoSummary: {
4351
+ wcag: '1.3.1',
4352
+ weight: 2,
4353
+ tools: {
4354
+ qualWeb: {
4355
+ 'QW-WCAG-T4': {
4356
+ variable: false,
4357
+ quality: 1,
4358
+ what: 'summary attribute is not used to give an overview of a data table'
4359
+ }
4360
+ }
4361
+ }
4362
+ },
3865
4363
  tableColumnsVary: {
3866
4364
  wcag: '1.3.1',
3867
4365
  weight: 3,
3868
- packages: {
4366
+ tools: {
3869
4367
  nuVal: {
3870
4368
  '^A table row was .+ columns wide, which is less than the column count established by the first row.*$': {
3871
4369
  variable: true,
@@ -3883,7 +4381,7 @@ const groups = {
3883
4381
  tableCaption: {
3884
4382
  wcag: '1.3.1',
3885
4383
  weight: 1,
3886
- packages: {
4384
+ tools: {
3887
4385
  axe: {
3888
4386
  'table-fake-caption': {
3889
4387
  variable: false,
@@ -3897,13 +4395,20 @@ const groups = {
3897
4395
  quality: 1,
3898
4396
  what: 'Table has no caption element'
3899
4397
  }
4398
+ },
4399
+ qualWeb: {
4400
+ 'QW-WCAG-T2': {
4401
+ variable: false,
4402
+ quality: 1,
4403
+ what: 'caption element not used to associate a caption with a data table'
4404
+ }
3900
4405
  }
3901
4406
  }
3902
4407
  },
3903
4408
  cellHeadersNotInferrable: {
3904
4409
  wcag: '1.3.1',
3905
4410
  weight: 4,
3906
- packages: {
4411
+ tools: {
3907
4412
  htmlcs: {
3908
4413
  'e:AA.1_3_1.H43.HeadersRequired': {
3909
4414
  variable: false,
@@ -3920,23 +4425,53 @@ const groups = {
3920
4425
  }
3921
4426
  }
3922
4427
  },
4428
+ cellHeadersOutsideTable: {
4429
+ wcag: '1.3.1',
4430
+ weight: 4,
4431
+ tools: {
4432
+ qualWeb: {
4433
+ 'QW-ACT-R36': {
4434
+ variable: false,
4435
+ quality: 1,
4436
+ what: 'Headers attribute does not refer to a cell in the same table element'
4437
+ }
4438
+ },
4439
+ }
4440
+ },
3923
4441
  cellHeadersAmbiguityRisk: {
3924
4442
  wcag: '1.3.1',
3925
4443
  weight: 2,
3926
- packages: {
4444
+ tools: {
3927
4445
  htmlcs: {
3928
4446
  'w:AA.1_3_1.H43.ScopeAmbiguous': {
3929
4447
  variable: false,
3930
4448
  quality: 1,
3931
4449
  what: 'Complex table requires headers attributes of cells instead of header scopes'
3932
4450
  }
4451
+ },
4452
+ qualWeb: {
4453
+ 'QW-WCAG-T14': {
4454
+ variable: false,
4455
+ quality: 1,
4456
+ what: 'id and headers attributes not used to associate data cells with header cells in a data table'
4457
+ },
4458
+ 'QW-WCAG-T25': {
4459
+ variable: false,
4460
+ quality: 1,
4461
+ what: 'scope attribute not used to associate header cells and data cells in a data table'
4462
+ },
4463
+ 'QW-BP12': {
4464
+ variable: false,
4465
+ quality: 1,
4466
+ what: 'scope col and row are not used'
4467
+ }
3933
4468
  }
3934
4469
  }
3935
4470
  },
3936
4471
  tableHeaderless: {
3937
4472
  wcag: '1.3.1',
3938
4473
  weight: 3,
3939
- packages: {
4474
+ tools: {
3940
4475
  continuum: {
3941
4476
  387: {
3942
4477
  variable: false,
@@ -3956,7 +4491,7 @@ const groups = {
3956
4491
  tableCellHeaderless: {
3957
4492
  wcag: '1.3.1',
3958
4493
  weight: 3,
3959
- packages: {
4494
+ tools: {
3960
4495
  alfa: {
3961
4496
  r77: {
3962
4497
  variable: false,
@@ -3976,7 +4511,7 @@ const groups = {
3976
4511
  tableHeaderCelless: {
3977
4512
  wcag: '1.3.1',
3978
4513
  weight: 4,
3979
- packages: {
4514
+ tools: {
3980
4515
  alfa: {
3981
4516
  r46: {
3982
4517
  variable: false,
@@ -3984,11 +4519,18 @@ const groups = {
3984
4519
  what: 'Header cell is not assigned to any cell'
3985
4520
  }
3986
4521
  },
3987
- axe: {
3988
- 'th-has-data-cells': {
4522
+ axe: {
4523
+ 'th-has-data-cells': {
4524
+ variable: false,
4525
+ quality: 1,
4526
+ what: 'Table header refers to no cell'
4527
+ }
4528
+ },
4529
+ qualWeb: {
4530
+ 'QW-ACT-R39': {
3989
4531
  variable: false,
3990
4532
  quality: 1,
3991
- what: 'Table header refers to no cell'
4533
+ what: 'Table header cell has no assigned data cell'
3992
4534
  }
3993
4535
  }
3994
4536
  }
@@ -3996,7 +4538,7 @@ const groups = {
3996
4538
  TableHeaderScopeRisk: {
3997
4539
  wcag: '1.3.1',
3998
4540
  weight: 1,
3999
- packages: {
4541
+ tools: {
4000
4542
  htmlcs: {
4001
4543
  'e:AA.1_3_1.H63.1': {
4002
4544
  variable: false,
@@ -4009,7 +4551,7 @@ const groups = {
4009
4551
  tableHeaderEmpty: {
4010
4552
  wcag: '1.3.1',
4011
4553
  weight: 2,
4012
- packages: {
4554
+ tools: {
4013
4555
  wave: {
4014
4556
  'e:th_empty': {
4015
4557
  variable: false,
@@ -4019,10 +4561,23 @@ const groups = {
4019
4561
  }
4020
4562
  }
4021
4563
  },
4564
+ tableEmbedded: {
4565
+ wcag: '1.3.1',
4566
+ weight: 2,
4567
+ tools: {
4568
+ qualWeb: {
4569
+ 'QW-BP5': {
4570
+ variable: false,
4571
+ quality: 1,
4572
+ what: 'table element is inside another table element'
4573
+ }
4574
+ }
4575
+ }
4576
+ },
4022
4577
  controlNoText: {
4023
4578
  wcag: '4.1.2',
4024
4579
  weight: 4,
4025
- packages: {
4580
+ tools: {
4026
4581
  axe: {
4027
4582
  label: {
4028
4583
  variable: false,
@@ -4044,6 +4599,13 @@ const groups = {
4044
4599
  what: 'Form control has no associated label'
4045
4600
  }
4046
4601
  },
4602
+ qualWeb: {
4603
+ 'QW-ACT-R16': {
4604
+ variable: false,
4605
+ quality: 1,
4606
+ what: 'Form control has no accessible name'
4607
+ }
4608
+ },
4047
4609
  wave: {
4048
4610
  'e:label_missing': {
4049
4611
  variable: false,
@@ -4056,7 +4618,7 @@ const groups = {
4056
4618
  controlLabelInvisible: {
4057
4619
  wcag: '2.4.6',
4058
4620
  weight: 4,
4059
- packages: {
4621
+ tools: {
4060
4622
  axe: {
4061
4623
  'label-title-only': {
4062
4624
  variable: false,
@@ -4069,7 +4631,7 @@ const groups = {
4069
4631
  titleAsLabel: {
4070
4632
  wcag: '2.4.6',
4071
4633
  weight: 3,
4072
- packages: {
4634
+ tools: {
4073
4635
  wave: {
4074
4636
  'a:label_title': {
4075
4637
  variable: false,
@@ -4082,7 +4644,7 @@ const groups = {
4082
4644
  visibleLabelNotName: {
4083
4645
  wcag: '2.5.3',
4084
4646
  weight: 3,
4085
- packages: {
4647
+ tools: {
4086
4648
  alfa: {
4087
4649
  r14: {
4088
4650
  variable: false,
@@ -4110,13 +4672,20 @@ const groups = {
4110
4672
  quality: 1,
4111
4673
  what: 'Accessible name does not match or contain the visible label text'
4112
4674
  }
4675
+ },
4676
+ qualWeb: {
4677
+ 'QW-ACT-R30': {
4678
+ variable: false,
4679
+ quality: 1,
4680
+ what: 'Visible label is not part of the accessible name'
4681
+ }
4113
4682
  }
4114
4683
  }
4115
4684
  },
4116
4685
  targetSize: {
4117
4686
  wcag: '2.5.5',
4118
4687
  weight: 3,
4119
- packages: {
4688
+ tools: {
4120
4689
  tenon: {
4121
4690
  152: {
4122
4691
  variable: false,
@@ -4129,7 +4698,7 @@ const groups = {
4129
4698
  visibleBulk: {
4130
4699
  wcag: '2.4',
4131
4700
  weight: 1,
4132
- packages: {
4701
+ tools: {
4133
4702
  testaro: {
4134
4703
  bulk: {
4135
4704
  variable: false,
@@ -4142,7 +4711,7 @@ const groups = {
4142
4711
  activeEmbedding: {
4143
4712
  wcag: '2.5.5',
4144
4713
  weight: 3,
4145
- packages: {
4714
+ tools: {
4146
4715
  axe: {
4147
4716
  'nested-interactive': {
4148
4717
  variable: false,
@@ -4241,7 +4810,7 @@ const groups = {
4241
4810
  tabFocusability: {
4242
4811
  wcag: '2.1.1',
4243
4812
  weight: 4,
4244
- packages: {
4813
+ tools: {
4245
4814
  alfa: {
4246
4815
  r95: {
4247
4816
  variable: false,
@@ -4256,6 +4825,18 @@ const groups = {
4256
4825
  what: 'UI component has no focusable child element for keyboard access'
4257
4826
  }
4258
4827
  },
4828
+ qualWeb: {
4829
+ 'QW-ACT-R70': {
4830
+ variable: false,
4831
+ quality: 1,
4832
+ what: 'iframe with negative tabindex has interactive elements'
4833
+ },
4834
+ 'QW-WCAG-T24': {
4835
+ variable: false,
4836
+ quality: 1,
4837
+ what: 'Script removes the focus when focus is received'
4838
+ }
4839
+ },
4259
4840
  testaro: {
4260
4841
  focAll: {
4261
4842
  variable: false,
@@ -4268,19 +4849,26 @@ const groups = {
4268
4849
  focusIndication: {
4269
4850
  wcag: '2.4.7',
4270
4851
  weight: 4,
4271
- packages: {
4852
+ tools: {
4272
4853
  alfa: {
4273
4854
  r65: {
4274
4855
  variable: false,
4275
4856
  quality: 1,
4276
- what: 'Element in sequential focus order has no visible focus'
4857
+ what: 'Element in the sequential focus order has no visible focus'
4858
+ }
4859
+ },
4860
+ qualWeb: {
4861
+ 'QW-ACT-R62': {
4862
+ variable: false,
4863
+ quality: 1,
4864
+ what: 'Element in the sequential focus order has no visible focus'
4277
4865
  }
4278
4866
  },
4279
4867
  testaro: {
4280
4868
  focInd: {
4281
4869
  variable: false,
4282
4870
  quality: 1,
4283
- what: 'Focused element displaying no or nostandard focus indicator'
4871
+ what: 'Focused element displays no or a nostandard focus indicator'
4284
4872
  }
4285
4873
  }
4286
4874
  }
@@ -4288,7 +4876,7 @@ const groups = {
4288
4876
  allCaps: {
4289
4877
  wcag: '3.1.5',
4290
4878
  weight: 1,
4291
- packages: {
4879
+ tools: {
4292
4880
  alfa: {
4293
4881
  r72: {
4294
4882
  variable: false,
@@ -4308,7 +4896,7 @@ const groups = {
4308
4896
  allItalics: {
4309
4897
  wcag: '3.1.5',
4310
4898
  weight: 1,
4311
- packages: {
4899
+ tools: {
4312
4900
  alfa: {
4313
4901
  r85: {
4314
4902
  variable: false,
@@ -4328,7 +4916,7 @@ const groups = {
4328
4916
  noLandmarks: {
4329
4917
  wcag: '1.3.6',
4330
4918
  weight: 2,
4331
- packages: {
4919
+ tools: {
4332
4920
  wave: {
4333
4921
  'a:region_missing': {
4334
4922
  variable: false,
@@ -4341,7 +4929,7 @@ const groups = {
4341
4929
  contentBeyondLandmarks: {
4342
4930
  wcag: '1.3.6',
4343
4931
  weight: 2,
4344
- packages: {
4932
+ tools: {
4345
4933
  alfa: {
4346
4934
  r57: {
4347
4935
  variable: false,
@@ -4368,7 +4956,7 @@ const groups = {
4368
4956
  footerTopLandmark: {
4369
4957
  wcag: '1.3.6',
4370
4958
  weight: 1,
4371
- packages: {
4959
+ tools: {
4372
4960
  axe: {
4373
4961
  'landmark-contentinfo-is-top-level': {
4374
4962
  variable: false,
@@ -4381,7 +4969,7 @@ const groups = {
4381
4969
  asideNotTop: {
4382
4970
  wcag: '1.3.6',
4383
4971
  weight: 2,
4384
- packages: {
4972
+ tools: {
4385
4973
  axe: {
4386
4974
  'landmark-complementary-is-top-level': {
4387
4975
  variable: false,
@@ -4394,7 +4982,7 @@ const groups = {
4394
4982
  mainNotTop: {
4395
4983
  wcag: '1.3.6',
4396
4984
  weight: 2,
4397
- packages: {
4985
+ tools: {
4398
4986
  axe: {
4399
4987
  'landmark-main-is-top-level': {
4400
4988
  variable: false,
@@ -4407,7 +4995,7 @@ const groups = {
4407
4995
  mainConfusion: {
4408
4996
  wcag: '1.3.6',
4409
4997
  weight: 3,
4410
- packages: {
4998
+ tools: {
4411
4999
  ibm: {
4412
5000
  Rpt_Aria_MultipleMainsRequireLabel_Implicit_2: {
4413
5001
  variable: false,
@@ -4422,16 +5010,31 @@ const groups = {
4422
5010
  }
4423
5011
  }
4424
5012
  },
4425
- mainNot1: {
5013
+ mainNone: {
4426
5014
  wcag: '1.3.6',
4427
5015
  weight: 2,
4428
- packages: {
5016
+ tools: {
4429
5017
  axe: {
4430
5018
  'landmark-one-main': {
4431
5019
  variable: false,
4432
5020
  quality: 1,
4433
5021
  what: 'page has no main landmark'
4434
- },
5022
+ }
5023
+ },
5024
+ qualWeb: {
5025
+ 'QW-ACT-R63': {
5026
+ variable: false,
5027
+ quality: 1,
5028
+ what: 'Document has no landmark with non-repeated content'
5029
+ }
5030
+ }
5031
+ }
5032
+ },
5033
+ mainMultiple: {
5034
+ wcag: '1.3.6',
5035
+ weight: 2,
5036
+ tools: {
5037
+ axe: {
4435
5038
  'landmark-no-duplicate-main': {
4436
5039
  variable: false,
4437
5040
  quality: 1,
@@ -4457,7 +5060,7 @@ const groups = {
4457
5060
  bannerNot1: {
4458
5061
  wcag: '1.3.6',
4459
5062
  weight: 2,
4460
- packages: {
5063
+ tools: {
4461
5064
  axe: {
4462
5065
  'landmark-no-duplicate-banner': {
4463
5066
  variable: false,
@@ -4477,7 +5080,7 @@ const groups = {
4477
5080
  bannerNotTop: {
4478
5081
  wcag: '1.3.6',
4479
5082
  weight: 2,
4480
- packages: {
5083
+ tools: {
4481
5084
  axe: {
4482
5085
  'landmark-banner-is-top-level': {
4483
5086
  variable: false,
@@ -4490,7 +5093,7 @@ const groups = {
4490
5093
  footerConfusion: {
4491
5094
  wcag: '1.3.6',
4492
5095
  weight: 3,
4493
- packages: {
5096
+ tools: {
4494
5097
  ibm: {
4495
5098
  Rpt_Aria_MultipleContentinfoLandmarks_Implicit: {
4496
5099
  variable: false,
@@ -4503,7 +5106,7 @@ const groups = {
4503
5106
  footerNot1: {
4504
5107
  wcag: '1.3.6',
4505
5108
  weight: 2,
4506
- packages: {
5109
+ tools: {
4507
5110
  axe: {
4508
5111
  'landmark-no-duplicate-contentinfo': {
4509
5112
  variable: false,
@@ -4523,7 +5126,7 @@ const groups = {
4523
5126
  landmarkConfusion: {
4524
5127
  wcag: '1.3.6',
4525
5128
  weight: 3,
4526
- packages: {
5129
+ tools: {
4527
5130
  axe: {
4528
5131
  'landmark-unique': {
4529
5132
  variable: false,
@@ -4543,7 +5146,7 @@ const groups = {
4543
5146
  articleConfusion: {
4544
5147
  wcag: '1.3.6',
4545
5148
  weight: 3,
4546
- packages: {
5149
+ tools: {
4547
5150
  ibm: {
4548
5151
  Rpt_Aria_MultipleArticleRoles_Implicit: {
4549
5152
  variable: false,
@@ -4556,7 +5159,7 @@ const groups = {
4556
5159
  formConfusion: {
4557
5160
  wcag: '1.3.6',
4558
5161
  weight: 3,
4559
- packages: {
5162
+ tools: {
4560
5163
  ibm: {
4561
5164
  Rpt_Aria_MultipleFormLandmarks_Implicit: {
4562
5165
  variable: false,
@@ -4569,7 +5172,7 @@ const groups = {
4569
5172
  applicationConfusion: {
4570
5173
  wcag: '1.3.6',
4571
5174
  weight: 3,
4572
- packages: {
5175
+ tools: {
4573
5176
  ibm: {
4574
5177
  Rpt_Aria_MultipleApplicationLandmarks: {
4575
5178
  variable: false,
@@ -4582,7 +5185,7 @@ const groups = {
4582
5185
  asideConfusion: {
4583
5186
  wcag: '1.3.6',
4584
5187
  weight: 3,
4585
- packages: {
5188
+ tools: {
4586
5189
  continuum: {
4587
5190
  527: {
4588
5191
  variable: false,
@@ -4602,7 +5205,7 @@ const groups = {
4602
5205
  bannerConfusion: {
4603
5206
  wcag: '1.3.6',
4604
5207
  weight: 3,
4605
- packages: {
5208
+ tools: {
4606
5209
  ibm: {
4607
5210
  Rpt_Aria_MultipleBannerLandmarks_Implicit: {
4608
5211
  variable: false,
@@ -4615,7 +5218,7 @@ const groups = {
4615
5218
  navConfusion: {
4616
5219
  wcag: '1.3.6',
4617
5220
  weight: 3,
4618
- packages: {
5221
+ tools: {
4619
5222
  continuum: {
4620
5223
  531: {
4621
5224
  variable: false,
@@ -4635,7 +5238,7 @@ const groups = {
4635
5238
  regionConfusion: {
4636
5239
  wcag: '1.3.6',
4637
5240
  weight: 3,
4638
- packages: {
5241
+ tools: {
4639
5242
  ibm: {
4640
5243
  Rpt_Aria_MultipleRegionsUniqueLabel_Implicit: {
4641
5244
  variable: false,
@@ -4648,7 +5251,7 @@ const groups = {
4648
5251
  searchConfusion: {
4649
5252
  wcag: '1.3.6',
4650
5253
  weight: 3,
4651
- packages: {
5254
+ tools: {
4652
5255
  ibm: {
4653
5256
  Rpt_Aria_MultipleSearchLandmarks: {
4654
5257
  variable: false,
@@ -4661,7 +5264,7 @@ const groups = {
4661
5264
  asideNoText: {
4662
5265
  wcag: '1.3.6',
4663
5266
  weight: 3,
4664
- packages: {
5267
+ tools: {
4665
5268
  continuum: {
4666
5269
  532: {
4667
5270
  variable: false,
@@ -4674,7 +5277,7 @@ const groups = {
4674
5277
  complementaryNoText: {
4675
5278
  wcag: '1.3.6',
4676
5279
  weight: 1,
4677
- packages: {
5280
+ tools: {
4678
5281
  ibm: {
4679
5282
  Rpt_Aria_ComplementaryRequiredLabel_Implicit: {
4680
5283
  variable: false,
@@ -4692,7 +5295,7 @@ const groups = {
4692
5295
  navNoText: {
4693
5296
  wcag: '1.3.6',
4694
5297
  weight: 3,
4695
- packages: {
5298
+ tools: {
4696
5299
  continuum: {
4697
5300
  533: {
4698
5301
  variable: false,
@@ -4705,7 +5308,7 @@ const groups = {
4705
5308
  labelNoText: {
4706
5309
  wcag: '1.3.1',
4707
5310
  weight: 4,
4708
- packages: {
5311
+ tools: {
4709
5312
  ibm: {
4710
5313
  Valerie_Label_HasContent: {
4711
5314
  variable: false,
@@ -4718,7 +5321,7 @@ const groups = {
4718
5321
  focusableOperable: {
4719
5322
  wcag: '2.1.1',
4720
5323
  weight: 3,
4721
- packages: {
5324
+ tools: {
4722
5325
  testaro: {
4723
5326
  focOp: {
4724
5327
  variable: false,
@@ -4731,20 +5334,27 @@ const groups = {
4731
5334
  focusableRole: {
4732
5335
  wcag: '4.1.2',
4733
5336
  weight: 3,
4734
- packages: {
5337
+ tools: {
4735
5338
  axe: {
4736
5339
  'focus-order-semantics': {
4737
5340
  variable: false,
4738
5341
  quality: 1,
4739
5342
  what: 'Focusable element has no active role'
4740
5343
  }
5344
+ },
5345
+ qualWeb: {
5346
+ 'QW-WCAG-T26': {
5347
+ variable: false,
5348
+ quality: 1,
5349
+ what: 'Script makes a div or span a user interface control without providing a role for the control'
5350
+ }
4741
5351
  }
4742
5352
  }
4743
5353
  },
4744
5354
  focusableHidden: {
4745
5355
  wcag: '4.1.2',
4746
5356
  weight: 4,
4747
- packages: {
5357
+ tools: {
4748
5358
  alfa: {
4749
5359
  r17: {
4750
5360
  variable: false,
@@ -4778,6 +5388,13 @@ const groups = {
4778
5388
  what: 'Focusable element is within the subtree of an element with aria-hidden set to true'
4779
5389
  }
4780
5390
  },
5391
+ qualWeb: {
5392
+ 'QW-ACT-R13': {
5393
+ variable: false,
5394
+ quality: 1,
5395
+ what: 'Element with aria-hidden has focusable content'
5396
+ }
5397
+ },
4781
5398
  tenon: {
4782
5399
  189: {
4783
5400
  variable: false,
@@ -4795,7 +5412,7 @@ const groups = {
4795
5412
  focusedAway: {
4796
5413
  wcag: '1.4.10',
4797
5414
  weight: 3,
4798
- packages: {
5415
+ tools: {
4799
5416
  testaro: {
4800
5417
  focVis: {
4801
5418
  variable: false,
@@ -4808,20 +5425,27 @@ const groups = {
4808
5425
  focusableDescendants: {
4809
5426
  wcag: '4.1.2',
4810
5427
  weight: 4,
4811
- packages: {
5428
+ tools: {
4812
5429
  alfa: {
4813
5430
  r90: {
4814
5431
  variable: false,
4815
5432
  quality: 1,
4816
5433
  what: 'Element has a role making its children presentational but contains a focusable element'
4817
5434
  }
5435
+ },
5436
+ qualWeb: {
5437
+ 'QW-ACT-R65': {
5438
+ variable: false,
5439
+ quality: 1,
5440
+ what: 'Element with presentational children has focusable content'
5441
+ }
4818
5442
  }
4819
5443
  }
4820
5444
  },
4821
5445
  labeledHidden: {
4822
5446
  wcag: '1.3.1',
4823
5447
  weight: 2,
4824
- packages: {
5448
+ tools: {
4825
5449
  htmlcs: {
4826
5450
  'w:AA.1_3_1.F68.Hidden': {
4827
5451
  variable: false,
@@ -4839,7 +5463,7 @@ const groups = {
4839
5463
  contentHidden: {
4840
5464
  wcag: '2.4.7',
4841
5465
  weight: 10,
4842
- packages: {
5466
+ tools: {
4843
5467
  testaro: {
4844
5468
  allHidden: {
4845
5469
  variable: false,
@@ -4852,7 +5476,7 @@ const groups = {
4852
5476
  hiddenContentRisk: {
4853
5477
  wcag: '4.1',
4854
5478
  weight: 1,
4855
- packages: {
5479
+ tools: {
4856
5480
  axe: {
4857
5481
  'hidden-content': {
4858
5482
  variable: false,
@@ -4865,7 +5489,7 @@ const groups = {
4865
5489
  frameContentRisk: {
4866
5490
  wcag: '4.1',
4867
5491
  weight: 1,
4868
- packages: {
5492
+ tools: {
4869
5493
  axe: {
4870
5494
  'frame-tested': {
4871
5495
  variable: false,
@@ -4878,7 +5502,7 @@ const groups = {
4878
5502
  frameSandboxRisk: {
4879
5503
  wcag: '4.1',
4880
5504
  weight: 2,
4881
- packages: {
5505
+ tools: {
4882
5506
  nuVal: {
4883
5507
  '^Potentially bad value .+ for attribute sandbox on element iframe: Setting both allow-scripts and allow-same-origin is not recommended, because it effectively enables an embedded page to break out of all sandboxing.*$': {
4884
5508
  variable: true,
@@ -4891,7 +5515,7 @@ const groups = {
4891
5515
  hoverSurprise: {
4892
5516
  wcag: '1.4.13',
4893
5517
  weight: 2,
4894
- packages: {
5518
+ tools: {
4895
5519
  testaro: {
4896
5520
  hover: {
4897
5521
  variable: false,
@@ -4904,7 +5528,7 @@ const groups = {
4904
5528
  labelClash: {
4905
5529
  wcag: '1.3.1',
4906
5530
  weight: 2,
4907
- packages: {
5531
+ tools: {
4908
5532
  axe: {
4909
5533
  'form-field-multiple-labels': {
4910
5534
  variable: false,
@@ -4938,7 +5562,7 @@ const groups = {
4938
5562
  labelEmpty: {
4939
5563
  wcag: '1.3.1',
4940
5564
  weight: 3,
4941
- packages: {
5565
+ tools: {
4942
5566
  htmlcs: {
4943
5567
  'w:AA.1_3_1.ARIA6': {
4944
5568
  variable: false,
@@ -4963,7 +5587,7 @@ const groups = {
4963
5587
  linkComprehensionRisk: {
4964
5588
  wcag: '2.4.4',
4965
5589
  weight: 1,
4966
- packages: {
5590
+ tools: {
4967
5591
  wave: {
4968
5592
  'a:link_suspicious': {
4969
5593
  variable: false,
@@ -4976,7 +5600,7 @@ const groups = {
4976
5600
  nonWebLink: {
4977
5601
  wcag: '1.3.3',
4978
5602
  weight: 1,
4979
- packages: {
5603
+ tools: {
4980
5604
  continuum: {
4981
5605
  141: {
4982
5606
  variable: false,
@@ -5001,7 +5625,7 @@ const groups = {
5001
5625
  linkVague: {
5002
5626
  wcag: '2.4.4',
5003
5627
  weight: 3,
5004
- packages: {
5628
+ tools: {
5005
5629
  tenon: {
5006
5630
  73: {
5007
5631
  variable: false,
@@ -5014,7 +5638,7 @@ const groups = {
5014
5638
  linkIndication: {
5015
5639
  wcag: '1.3.3',
5016
5640
  weight: 2,
5017
- packages: {
5641
+ tools: {
5018
5642
  alfa: {
5019
5643
  r62: {
5020
5644
  variable: false,
@@ -5041,7 +5665,7 @@ const groups = {
5041
5665
  menuNavigation: {
5042
5666
  wcag: '2.1.1',
5043
5667
  weight: 2,
5044
- packages: {
5668
+ tools: {
5045
5669
  testaro: {
5046
5670
  menuNav: {
5047
5671
  variable: false,
@@ -5054,7 +5678,7 @@ const groups = {
5054
5678
  menuItemless: {
5055
5679
  wcag: '1.3.1',
5056
5680
  weight: 4,
5057
- packages: {
5681
+ tools: {
5058
5682
  wave: {
5059
5683
  'e:aria_menu_broken': {
5060
5684
  variable: false,
@@ -5067,7 +5691,7 @@ const groups = {
5067
5691
  tabNavigation: {
5068
5692
  wcag: '2.1.1',
5069
5693
  weight: 2,
5070
- packages: {
5694
+ tools: {
5071
5695
  testaro: {
5072
5696
  tabNav: {
5073
5697
  variable: false,
@@ -5077,10 +5701,23 @@ const groups = {
5077
5701
  }
5078
5702
  }
5079
5703
  },
5704
+ siteNavigation: {
5705
+ wcag: '2.4',
5706
+ weight: 1,
5707
+ tools: {
5708
+ qualWeb: {
5709
+ 'QW-WCAG-T15': {
5710
+ variable: false,
5711
+ quality: 1,
5712
+ what: 'link element and navigation tools not used'
5713
+ }
5714
+ }
5715
+ }
5716
+ },
5080
5717
  spontaneousMotion: {
5081
5718
  wcag: '2.2.2',
5082
5719
  weight: 2,
5083
- packages: {
5720
+ tools: {
5084
5721
  testaro: {
5085
5722
  motion: {
5086
5723
  variable: false,
@@ -5090,23 +5727,92 @@ const groups = {
5090
5727
  }
5091
5728
  }
5092
5729
  },
5730
+ blink: {
5731
+ wcag: '2.2.2',
5732
+ weight: 4,
5733
+ tools: {
5734
+ qualWeb: {
5735
+ 'QW-WCAG-T13': {
5736
+ variable: false,
5737
+ quality: 1,
5738
+ what: 'blink element used'
5739
+ },
5740
+ 'QW-WCAG-T30': {
5741
+ variable: false,
5742
+ quality: 1,
5743
+ what: 'text-decoration:blink is used without a mechanism to stop it in less than five seconds'
5744
+ }
5745
+ }
5746
+ }
5747
+ },
5093
5748
  autoplay: {
5094
5749
  wcag: '1.4.2',
5095
5750
  weight: 2,
5096
- packages: {
5751
+ tools: {
5097
5752
  axe: {
5098
5753
  'no-autoplay-audio': {
5099
5754
  variable: false,
5100
5755
  quality: 1,
5101
5756
  what: 'video or audio element plays automatically'
5102
5757
  }
5758
+ },
5759
+ qualWeb: {
5760
+ 'QW-ACT-R15': {
5761
+ variable: false,
5762
+ quality: 1,
5763
+ what: 'audio or video has audio that plays automatically'
5764
+ }
5765
+ }
5766
+ }
5767
+ },
5768
+ autoplayLong: {
5769
+ wcag: '1.4.2',
5770
+ weight: 2,
5771
+ tools: {
5772
+ qualWeb: {
5773
+ 'QW-ACT-R49': {
5774
+ variable: false,
5775
+ quality: 1,
5776
+ what: 'audio or video that plays automatically has audio lasting more than 3 seconds'
5777
+ }
5778
+ }
5779
+ }
5780
+ },
5781
+ autoplayControl: {
5782
+ wcag: '1.4.2',
5783
+ weight: 2,
5784
+ tools: {
5785
+ qualWeb: {
5786
+ 'QW-ACT-R50': {
5787
+ variable: false,
5788
+ quality: 1,
5789
+ what: 'audio or video that plays automatically has no control mechanism'
5790
+ }
5791
+ }
5792
+ }
5793
+ },
5794
+ refresh: {
5795
+ wcag: '2.2.1',
5796
+ weight: 3,
5797
+ tools: {
5798
+ qualWeb: {
5799
+ 'QW-ACT-R4': {
5800
+ variable: false,
5801
+ quality: 1,
5802
+ what: 'meta element refreshes or redirects with delay'
5803
+ },
5804
+ 'QW-ACT-R71': {
5805
+ variable: false,
5806
+ quality: 1,
5807
+ what: 'meta element has a refresh delay (no exception)'
5808
+ }
5103
5809
  }
5104
5810
  }
5105
5811
  },
5106
5812
  divParentBad: {
5107
5813
  wcag: '1.3.1',
5108
5814
  weight: 4,
5109
- packages: {
5815
+ tools: {
5110
5816
  nuVal: {
5111
5817
  'Element div not allowed as child of element button in this context. (Suppressing further errors from this subtree.)': {
5112
5818
  variable: false,
@@ -5119,7 +5825,7 @@ const groups = {
5119
5825
  pParentBad: {
5120
5826
  wcag: '1.3.1',
5121
5827
  weight: 4,
5122
- packages: {
5828
+ tools: {
5123
5829
  nuVal: {
5124
5830
  'Element p not allowed as child of element strong in this context. (Suppressing further errors from this subtree.)': {
5125
5831
  variable: false,
@@ -5132,7 +5838,7 @@ const groups = {
5132
5838
  styleParentBad: {
5133
5839
  wcag: '1.3.1',
5134
5840
  weight: 4,
5135
- packages: {
5841
+ tools: {
5136
5842
  nuVal: {
5137
5843
  'Element style not allowed as child of element body in this context. (Suppressing further errors from this subtree.)': {
5138
5844
  variable: false,
@@ -5160,7 +5866,7 @@ const groups = {
5160
5866
  inconsistentStyles: {
5161
5867
  wcag: '3.2.4',
5162
5868
  weight: 1,
5163
- packages: {
5869
+ tools: {
5164
5870
  testaro: {
5165
5871
  styleDiff: {
5166
5872
  variable: false,
@@ -5173,7 +5879,7 @@ const groups = {
5173
5879
  zIndexNotZero: {
5174
5880
  wcag: '1.4',
5175
5881
  weight: 1,
5176
- packages: {
5882
+ tools: {
5177
5883
  testaro: {
5178
5884
  zIndex: {
5179
5885
  variable: false,
@@ -5186,7 +5892,7 @@ const groups = {
5186
5892
  tabIndexPositive: {
5187
5893
  wcag: '2.4.3',
5188
5894
  weight: 1,
5189
- packages: {
5895
+ tools: {
5190
5896
  axe: {
5191
5897
  tabindex: {
5192
5898
  variable: false,
@@ -5206,7 +5912,7 @@ const groups = {
5206
5912
  tabIndexBad: {
5207
5913
  wcag: '1.3.1',
5208
5914
  weight: 4,
5209
- packages: {
5915
+ tools: {
5210
5916
  nuVal: {
5211
5917
  '^Bad value for attribute tabindex on element .+: The empty string is not a valid integer.*$': {
5212
5918
  variable: true,
@@ -5219,7 +5925,7 @@ const groups = {
5219
5925
  tabIndexMissing: {
5220
5926
  wcag: '2.1.1',
5221
5927
  weight: 4,
5222
- packages: {
5928
+ tools: {
5223
5929
  continuum: {
5224
5930
  337: {
5225
5931
  variable: false,
@@ -5244,7 +5950,7 @@ const groups = {
5244
5950
  trackNoLabel: {
5245
5951
  wcag: '4.1.2',
5246
5952
  weight: 4,
5247
- packages: {
5953
+ tools: {
5248
5954
  continuum: {
5249
5955
  40: {
5250
5956
  variable: false,
@@ -5262,7 +5968,7 @@ const groups = {
5262
5968
  trackNoSource: {
5263
5969
  wcag: '1.3.1',
5264
5970
  weight: 4,
5265
- packages: {
5971
+ tools: {
5266
5972
  continuum: {
5267
5973
  485: {
5268
5974
  variable: false,
@@ -5272,36 +5978,108 @@ const groups = {
5272
5978
  }
5273
5979
  }
5274
5980
  },
5275
- audioCaptionMissing: {
5981
+ audioContentNoText: {
5276
5982
  wcag: '1.2.1',
5277
5983
  weight: 4,
5278
- packages: {
5984
+ tools: {
5279
5985
  axe: {
5280
5986
  'audio-caption': {
5281
5987
  variable: false,
5282
5988
  quality: 1,
5283
5989
  what: 'audio element has no captions track'
5284
5990
  }
5991
+ },
5992
+ qualWeb: {
5993
+ 'QW-ACT-R26': {
5994
+ variable: false,
5995
+ quality: 1,
5996
+ what: 'video element auditory content has no accessible alternative'
5997
+ },
5998
+ 'QW-ACT-R29': {
5999
+ variable: false,
6000
+ quality: 1,
6001
+ what: 'audio element content has no text alternative'
6002
+ },
6003
+ 'QW-ACT-R58': {
6004
+ variable: false,
6005
+ quality: 1,
6006
+ what: 'audio element content has no transcript'
6007
+ },
6008
+ 'QW-ACT-R59': {
6009
+ variable: false,
6010
+ quality: 1,
6011
+ what: 'audio element content is not a media alternative for text'
6012
+ },
6013
+ 'QW-ACT-R60': {
6014
+ variable: false,
6015
+ quality: 1,
6016
+ what: 'video element auditory content has no captions'
6017
+ }
5285
6018
  }
5286
6019
  }
5287
6020
  },
5288
- videoCaptionMissing: {
6021
+ videoContentNoText: {
5289
6022
  wcag: '1.2.2',
5290
6023
  weight: 4,
5291
- packages: {
6024
+ tools: {
5292
6025
  axe: {
5293
6026
  'video-caption': {
5294
6027
  variable: false,
5295
6028
  quality: 1,
5296
6029
  what: 'video element has no captions'
5297
6030
  }
6031
+ },
6032
+ qualWeb: {
6033
+ 'QW-ACT-R23': {
6034
+ variable: false,
6035
+ quality: 1,
6036
+ what: 'video element visual content has no accessible alternative'
6037
+ },
6038
+ 'QW-ACT-R31': {
6039
+ variable: false,
6040
+ quality: 1,
6041
+ what: 'video element visual-only content has no accessible alternative'
6042
+ },
6043
+ 'QW-ACT-R32': {
6044
+ variable: false,
6045
+ quality: 1,
6046
+ what: 'video element visual-only content has no strict accessible alternative'
6047
+ },
6048
+ 'QW-ACT-R51': {
6049
+ variable: false,
6050
+ quality: 1,
6051
+ what: 'video element visual-only content is not a media alternative for text'
6052
+ },
6053
+ 'QW-ACT-R56': {
6054
+ variable: false,
6055
+ quality: 1,
6056
+ what: 'video element content is not a media alternative for text'
6057
+ },
6058
+ 'QW-ACT-R61': {
6059
+ variable: false,
6060
+ quality: 1,
6061
+ what: 'video element visual content has no transcript'
6062
+ }
6063
+ }
6064
+ }
6065
+ },
6066
+ videoContentNoTranscript: {
6067
+ wcag: '1.2.2',
6068
+ weight: 4,
6069
+ tools: {
6070
+ qualWeb: {
6071
+ 'QW-ACT-R53': {
6072
+ variable: false,
6073
+ quality: 1,
6074
+ what: 'video element visual-only content has no transcript'
6075
+ }
5298
6076
  }
5299
6077
  }
5300
6078
  },
5301
6079
  videoCaptionRisk: {
5302
6080
  wcag: '1.2.2',
5303
6081
  weight: 1,
5304
- packages: {
6082
+ tools: {
5305
6083
  wave: {
5306
6084
  'a:html5_video_audio': {
5307
6085
  variable: false,
@@ -5321,10 +6099,36 @@ const groups = {
5321
6099
  }
5322
6100
  }
5323
6101
  },
6102
+ videoNoAudioDescription: {
6103
+ wcag: '1.2.1',
6104
+ weight: 4,
6105
+ tools: {
6106
+ qualWeb: {
6107
+ 'QW-ACT-R55': {
6108
+ variable: false,
6109
+ quality: 1,
6110
+ what: 'video element visual content has no audio description'
6111
+ }
6112
+ }
6113
+ }
6114
+ },
6115
+ videoNoAudioTrack: {
6116
+ wcag: '1.2.1',
6117
+ weight: 4,
6118
+ tools: {
6119
+ qualWeb: {
6120
+ 'QW-ACT-R54': {
6121
+ variable: false,
6122
+ quality: 1,
6123
+ what: 'video element visual-only content has no audio track alternative'
6124
+ }
6125
+ }
6126
+ }
6127
+ },
5324
6128
  notKeyboardScrollable: {
5325
6129
  wcag: '2.1.1',
5326
6130
  weight: 4,
5327
- packages: {
6131
+ tools: {
5328
6132
  alfa: {
5329
6133
  r84: {
5330
6134
  variable: false,
@@ -5338,13 +6142,20 @@ const groups = {
5338
6142
  quality: 1,
5339
6143
  what: 'Element is scrollable but has no keyboard access'
5340
6144
  }
6145
+ },
6146
+ qualWeb: {
6147
+ 'QW-ACT-R43': {
6148
+ variable: false,
6149
+ quality: 1,
6150
+ what: 'Scrollable element is not keyboard accessible'
6151
+ }
5341
6152
  }
5342
6153
  }
5343
6154
  },
5344
6155
  horizontalScrolling: {
5345
6156
  wcag: '1.4.10',
5346
6157
  weight: 3,
5347
- packages: {
6158
+ tools: {
5348
6159
  tenon: {
5349
6160
  28: {
5350
6161
  variable: false,
@@ -5357,27 +6168,27 @@ const groups = {
5357
6168
  scrollRisk: {
5358
6169
  wcag: '1.4.10',
5359
6170
  weight: 1,
5360
- packages: {
6171
+ tools: {
5361
6172
  htmlcs: {
5362
6173
  'w:AA.1_4_10.C32,C31,C33,C38,SCR34,G206': {
5363
6174
  variable: false,
5364
6175
  quality: 1,
5365
6176
  what: 'Fixed-position element may force bidirectional scrolling'
5366
6177
  }
6178
+ },
6179
+ qualWeb: {
6180
+ 'QW-BP18': {
6181
+ variable: false,
6182
+ quality: 1,
6183
+ what: 'Percentage is not used in CSS for a container size'
6184
+ }
5367
6185
  }
5368
6186
  }
5369
6187
  },
5370
6188
  skipRepeatedContent: {
5371
6189
  wcag: '2.4.1',
5372
6190
  weight: 3,
5373
- packages: {
5374
- alfa: {
5375
- 'r87': {
5376
- variable: false,
5377
- quality: 0.5,
5378
- what: 'First focusable element is not a link to the main content'
5379
- }
5380
- },
6191
+ tools: {
5381
6192
  axe: {
5382
6193
  'bypass': {
5383
6194
  variable: false,
@@ -5397,6 +6208,18 @@ const groups = {
5397
6208
  what: 'Page provides no way to skip directly to the main content'
5398
6209
  }
5399
6210
  },
6211
+ qualWeb: {
6212
+ 'QW-ACT-R64': {
6213
+ variable: false,
6214
+ quality: 1,
6215
+ what: 'Document has no heading for non-repeated content'
6216
+ },
6217
+ 'QW-ACT-R75': {
6218
+ variable: false,
6219
+ quality: 1,
6220
+ what: 'Blocks of repeated content cannot be bypassed'
6221
+ }
6222
+ },
5400
6223
  wave: {
5401
6224
  'e:link_skip_broken': {
5402
6225
  variable: false,
@@ -5406,23 +6229,70 @@ const groups = {
5406
6229
  }
5407
6230
  }
5408
6231
  },
6232
+ repeatedContentRisk: {
6233
+ wcag: '2.4.1',
6234
+ weight: 1,
6235
+ tools: {
6236
+ alfa: {
6237
+ 'r87': {
6238
+ variable: false,
6239
+ quality: 0.5,
6240
+ what: 'First focusable element is not a link to the main content'
6241
+ }
6242
+ },
6243
+ qualWeb: {
6244
+ 'QW-ACT-R72': {
6245
+ variable: false,
6246
+ quality: 1,
6247
+ what: 'First focusable element is not a link to the non-repeated content'
6248
+ },
6249
+ 'QW-ACT-R73': {
6250
+ variable: false,
6251
+ quality: 1,
6252
+ what: 'Block of repeated content is not collapsible'
6253
+ },
6254
+ 'QW-ACT-R74': {
6255
+ variable: false,
6256
+ quality: 1,
6257
+ what: 'Document has no instrument to move focus to non-repeated content'
6258
+ },
6259
+ 'QW-WCAG-T23': {
6260
+ variable: false,
6261
+ quality: 1,
6262
+ what: 'No link at the top of the page that goes directly to the main content area'
6263
+ },
6264
+ 'QW-BP17': {
6265
+ variable: false,
6266
+ quality: 1,
6267
+ what: 'No link at the beginning of a block of repeated content goes to the end of the block'
6268
+ }
6269
+ }
6270
+ }
6271
+ },
5409
6272
  submitButton: {
5410
6273
  wcag: '2.5.6',
5411
6274
  weight: 3,
5412
- packages: {
6275
+ tools: {
5413
6276
  htmlcs: {
5414
6277
  'e:AA.3_2_2.H32.2': {
5415
6278
  variable: false,
5416
6279
  quality: 1,
5417
6280
  what: 'Form has no submit button'
5418
6281
  }
6282
+ },
6283
+ qualWeb: {
6284
+ 'QW-WCAG-T19': {
6285
+ variable: false,
6286
+ quality: 1,
6287
+ what: 'Submit button not provided'
6288
+ }
5419
6289
  }
5420
6290
  }
5421
6291
  },
5422
6292
  fragmentaryNoticeRisk: {
5423
6293
  wcag: '4.1.3',
5424
6294
  weight: 2,
5425
- packages: {
6295
+ tools: {
5426
6296
  alfa: {
5427
6297
  r54: {
5428
6298
  variable: false,
@@ -5432,10 +6302,23 @@ const groups = {
5432
6302
  }
5433
6303
  }
5434
6304
  },
6305
+ errorReferenceBad: {
6306
+ wcag: '3.3.1',
6307
+ weight: 4,
6308
+ tools: {
6309
+ qualWeb: {
6310
+ 'QW-ACT-R41': {
6311
+ variable: false,
6312
+ quality: 1,
6313
+ what: 'Error message describes no invalid form field value'
6314
+ }
6315
+ }
6316
+ }
6317
+ },
5435
6318
  noScriptRisk: {
5436
6319
  wcag: '4.1',
5437
6320
  weight: 1,
5438
- packages: {
6321
+ tools: {
5439
6322
  wave: {
5440
6323
  'a:noscript': {
5441
6324
  variable: false,
@@ -5446,9 +6329,9 @@ const groups = {
5446
6329
  }
5447
6330
  },
5448
6331
  browserSupportRisk: {
5449
- wcag: '1.3.1',
6332
+ wcag: '4.1',
5450
6333
  weight: 1,
5451
- packages: {
6334
+ tools: {
5452
6335
  nuVal: {
5453
6336
  'The inputmode attribute is not supported in all browsers. Please be sure to test, and consider using a polyfill.': {
5454
6337
  variable: false,
@@ -5459,9 +6342,9 @@ const groups = {
5459
6342
  }
5460
6343
  },
5461
6344
  obsolete: {
5462
- wcag: '1.3.1',
6345
+ wcag: '4.1',
5463
6346
  weight: 3,
5464
- packages: {
6347
+ tools: {
5465
6348
  alfa: {
5466
6349
  r70: {
5467
6350
  variable: false,
@@ -5560,6 +6443,13 @@ const groups = {
5560
6443
  what: 'Attribute value is a deprecated language subtag'
5561
6444
  }
5562
6445
  },
6446
+ qualWeb: {
6447
+ 'QW-BP10': {
6448
+ variable: false,
6449
+ quality: 1,
6450
+ what: 'HTML element is used to control the visual presentation of content'
6451
+ }
6452
+ },
5563
6453
  wave: {
5564
6454
  'a:longdesc': {
5565
6455
  variable: false,
@@ -5575,9 +6465,9 @@ const groups = {
5575
6465
  }
5576
6466
  },
5577
6467
  cssInvalid: {
5578
- wcag: '1.3.1',
6468
+ wcag: '4.1',
5579
6469
  weight: 3,
5580
- packages: {
6470
+ tools: {
5581
6471
  nuVal: {
5582
6472
  'CSS: Parse Error. Style sheets should not include HTML syntax.': {
5583
6473
  variable: false,
@@ -5708,9 +6598,9 @@ const groups = {
5708
6598
  }
5709
6599
  },
5710
6600
  parseError: {
5711
- wcag: '1.3.1',
6601
+ wcag: '4.1',
5712
6602
  weight: 3,
5713
- packages: {
6603
+ tools: {
5714
6604
  ibm: {
5715
6605
  'Rpt_Aria_InvalidTabindexForActivedescendant': {
5716
6606
  variable: false,
@@ -5884,11 +6774,6 @@ const groups = {
5884
6774
  quality: 1,
5885
6775
  what: 'element with a srcset attribute with a width has no sizes attribute'
5886
6776
  },
5887
- '^An element with role=.+ must be contained in, or owned by, an element with role=.+$': {
5888
- variable: true,
5889
- quality: 1,
5890
- what: 'Element has no required container or owner'
5891
- },
5892
6777
  '^The text content of element .+ was not in the required format: Expected .+ but found .+ instead.*$': {
5893
6778
  variable: true,
5894
6779
  quality: 1,
@@ -5920,6 +6805,13 @@ const groups = {
5920
6805
  what: 'Idle timeout expired'
5921
6806
  }
5922
6807
  },
6808
+ qualWeb: {
6809
+ 'QW-WCAG-T16': {
6810
+ variable: false,
6811
+ quality: 1,
6812
+ what: 'HTML is not used according to spec'
6813
+ }
6814
+ },
5923
6815
  wave: {
5924
6816
  'e:longdesc_invalid': {
5925
6817
  variable: false,
@@ -5932,7 +6824,7 @@ const groups = {
5932
6824
  encodingBad: {
5933
6825
  wcag: '3.1.3',
5934
6826
  weight: 4,
5935
- packages: {
6827
+ tools: {
5936
6828
  nuVal: {
5937
6829
  'Document uses the Unicode Private Use Area(s), which should not be used in publicly exchanged documents. (Charmod C073)': {
5938
6830
  variable: false,
@@ -5945,7 +6837,7 @@ const groups = {
5945
6837
  fatalError: {
5946
6838
  wcag: '4.1',
5947
6839
  weight: 50,
5948
- packages: {
6840
+ tools: {
5949
6841
  nuVal: {
5950
6842
  'Cannot recover after last error. Any further errors will be ignored.': {
5951
6843
  variable: false,
@@ -5963,7 +6855,7 @@ const groups = {
5963
6855
  notValidatable: {
5964
6856
  wcag: '4.1',
5965
6857
  weight: 1,
5966
- packages: {
6858
+ tools: {
5967
6859
  nuVal: {
5968
6860
  'Unsupported SVG version specified. This validator only supports SVG 1.1. The recommended way to suppress this warning is to remove the version attribute altogether.': {
5969
6861
  variable: false,
@@ -5974,634 +6866,3 @@ const groups = {
5974
6866
  }
5975
6867
  }
5976
6868
  };
5977
-
5978
- // VARIABLES
5979
-
5980
- let packageDetails = {};
5981
- let groupDetails = {};
5982
- let summary = {};
5983
- let preventionScores = {};
5984
-
5985
- // FUNCTIONS
5986
-
5987
- // Initialize the variables.
5988
- const init = () => {
5989
- packageDetails = {};
5990
- groupDetails = {
5991
- groups: {},
5992
- solos: {}
5993
- };
5994
- summary = {
5995
- total: 0,
5996
- log: 0,
5997
- preventions: 0,
5998
- solos: 0,
5999
- groups: []
6000
- };
6001
- preventionScores = {};
6002
- };
6003
-
6004
- // Adds a score to the package details.
6005
- const addDetail = (actWhich, testID, addition = 1) => {
6006
- if (addition) {
6007
- if (!packageDetails[actWhich]) {
6008
- packageDetails[actWhich] = {};
6009
- }
6010
- if (!packageDetails[actWhich][testID]) {
6011
- packageDetails[actWhich][testID] = 0;
6012
- }
6013
- packageDetails[actWhich][testID] += Math.round(addition);
6014
- }
6015
- };
6016
- // Scores a report.
6017
- exports.scorer = report => {
6018
- // Initialize the variables.
6019
- init();
6020
- // If there are any acts in the report:
6021
- const {acts} = report;
6022
- if (Array.isArray(acts)) {
6023
- // If any of them are test acts:
6024
- const testActs = acts.filter(act => act.type === 'test');
6025
- if (testActs.length) {
6026
- // For each test act:
6027
- testActs.forEach(test => {
6028
- const {which} = test;
6029
- // Add scores to the package details.
6030
- if (which === 'alfa') {
6031
- const issues = test.result && test.result.items;
6032
- if (issues && Array.isArray(issues)) {
6033
- issues.forEach(issue => {
6034
- const {verdict, rule} = issue;
6035
- if (verdict && rule) {
6036
- const {ruleID} = rule;
6037
- if (ruleID) {
6038
- // Add 4 per failure, 1 per warning (cantTell).
6039
- addDetail(which, ruleID, verdict === 'failed' ? 4 : 1);
6040
- }
6041
- }
6042
- });
6043
- }
6044
- }
6045
- else if (which === 'axe') {
6046
- const impactScores = {
6047
- minor: 1,
6048
- moderate: 2,
6049
- serious: 3,
6050
- critical: 4
6051
- };
6052
- const tests = test.result && test.result.details;
6053
- if (tests) {
6054
- const warnings = tests.incomplete;
6055
- const {violations} = tests;
6056
- [[warnings, 0.25], [violations, 1]].forEach(issueSeverity => {
6057
- if (issueSeverity[0] && Array.isArray(issueSeverity[0])) {
6058
- issueSeverity[0].forEach(issueType => {
6059
- const {id, nodes} = issueType;
6060
- if (id && nodes && Array.isArray(nodes)) {
6061
- nodes.forEach(node => {
6062
- const {impact} = node;
6063
- if (impact) {
6064
- // Add the impact score for a violation or 25% of it for a warning.
6065
- addDetail(which, id, issueSeverity[1] * impactScores[impact]);
6066
- }
6067
- });
6068
- }
6069
- });
6070
- }
6071
- });
6072
- }
6073
- }
6074
- else if (which === 'continuum') {
6075
- const issues = test.result;
6076
- if (issues && Array.isArray(issues)) {
6077
- issues.forEach(issue => {
6078
- // Add 4 per violation.
6079
- addDetail(which, issue.engineTestId, 4);
6080
- });
6081
- }
6082
- }
6083
- else if (which === 'htmlcs') {
6084
- const issues = test.result;
6085
- if (issues) {
6086
- ['Error', 'Warning'].forEach(issueSeverityName => {
6087
- const severityData = issues[issueSeverityName];
6088
- if (severityData) {
6089
- const issueTypes = Object.keys(severityData);
6090
- issueTypes.forEach(issueTypeName => {
6091
- const issueArrays = Object.values(severityData[issueTypeName]);
6092
- const issueCount = issueArrays.reduce((count, array) => count + array.length, 0);
6093
- const severityCode = issueSeverityName[0].toLowerCase();
6094
- const code = `${severityCode}:${issueTypeName}`;
6095
- // Add 4 per error, 1 per warning.
6096
- const weight = severityCode === 'e' ? 4 : 1;
6097
- addDetail(which, code, weight * issueCount);
6098
- });
6099
- }
6100
- });
6101
- }
6102
- }
6103
- else if (which === 'ibm') {
6104
- const {result} = test;
6105
- const {content, url} = result;
6106
- if (content && url) {
6107
- let preferredMode = 'content';
6108
- if (
6109
- content.error ||
6110
- (content.totals &&
6111
- content.totals.violation &&
6112
- url.totals &&
6113
- url.totals.violation &&
6114
- url.totals.violation > content.totals.violation)
6115
- ) {
6116
- preferredMode = 'url';
6117
- }
6118
- const {items} = result[preferredMode];
6119
- if (items && Array.isArray(items)) {
6120
- items.forEach(issue => {
6121
- const {ruleId, level} = issue;
6122
- if (ruleId && level) {
6123
- // Add 4 per violation, 1 per warning (recommendation).
6124
- addDetail(which, ruleId, level === 'violation' ? 4 : 1);
6125
- }
6126
- });
6127
- }
6128
- }
6129
- }
6130
- else if (which === 'nuVal') {
6131
- const issues = test.result && test.result.messages;
6132
- if (issues) {
6133
- issues.forEach(issue => {
6134
- // Add 4 per error, 1 per warning.
6135
- const weight = issue.type === 'error' ? 4 : 1;
6136
- addDetail(which, issue.message, weight);
6137
- });
6138
- }
6139
- }
6140
- else if (which === 'tenon') {
6141
- const issues =
6142
- test.result && test.result.data && test.result.data.resultSet;
6143
- if (issues && Array.isArray(issues)) {
6144
- issues.forEach(issue => {
6145
- const {tID, priority, certainty} = issue;
6146
- if (tID && priority && certainty) {
6147
- // Add 4 per issue if certainty and priority 100, less if less.
6148
- addDetail(which, tID, certainty * priority / 2500);
6149
- }
6150
- });
6151
- }
6152
- }
6153
- else if (which === 'wave') {
6154
- const severityScores = {
6155
- error: 4,
6156
- contrast: 3,
6157
- alert: 1
6158
- };
6159
- const issueSeverities = test.result && test.result.categories;
6160
- if (issueSeverities) {
6161
- ['error', 'contrast', 'alert'].forEach(issueSeverity => {
6162
- const {items} = issueSeverities[issueSeverity];
6163
- if (items) {
6164
- const testIDs = Object.keys(items);
6165
- if (testIDs.length) {
6166
- testIDs.forEach(testID => {
6167
- const {count} = items[testID];
6168
- if (count) {
6169
- // Add 4 per error, 3 per contrast error, 1 per warning (alert).
6170
- addDetail(
6171
- which, `${issueSeverity[0]}:${testID}`, count * severityScores[issueSeverity]
6172
- );
6173
- }
6174
- });
6175
- }
6176
- }
6177
- });
6178
- }
6179
- }
6180
- else if (which === 'allHidden') {
6181
- const {result} = test;
6182
- if (
6183
- result
6184
- && ['hidden', 'reallyHidden', 'visHidden', 'ariaHidden'].every(
6185
- key => result[key]
6186
- && ['document', 'body', 'main'].every(
6187
- element => typeof result[key][element] === 'boolean'
6188
- )
6189
- )
6190
- ) {
6191
- // Get a score for the test.
6192
- const score = 8 * result.hidden.document
6193
- + 8 * result.hidden.body
6194
- + 6 * result.hidden.main
6195
- + 10 * result.reallyHidden.document
6196
- + 10 * result.reallyHidden.body
6197
- + 8 * result.reallyHidden.main
6198
- + 8 * result.visHidden.document
6199
- + 8 * result.visHidden.body
6200
- + 6 * result.visHidden.main
6201
- + 10 * result.ariaHidden.document
6202
- + 10 * result.ariaHidden.body
6203
- + 8 * result.ariaHidden.main;
6204
- // Add the score.
6205
- addDetail('testaro', which, score);
6206
- }
6207
- }
6208
- else if (which === 'bulk') {
6209
- const count = test.result && test.result.visibleElements;
6210
- if (typeof count === 'number') {
6211
- // Add 1 per 300 visible elements beyond 300.
6212
- addDetail('testaro', which, Math.max(0, count / 300 - 1));
6213
- }
6214
- }
6215
- else if (which === 'docType') {
6216
- // If document has no or invalid doctype:
6217
- const hasType = test.result && test.result.docHasType;
6218
- if (typeof hasType === 'boolean' && ! hasType) {
6219
- // Add 10.
6220
- addDetail('testaro', which, 10);
6221
- }
6222
- }
6223
- else if (which === 'embAc') {
6224
- const issueCounts = test.result && test.result.totals;
6225
- if (issueCounts) {
6226
- const counts = Object.values(issueCounts);
6227
- const total = counts.reduce((sum, current) => sum + current);
6228
- // Add 3 per embedded element.
6229
- addDetail('testaro', which, 3 * total);
6230
- }
6231
- }
6232
- else if (which === 'filter') {
6233
- const totals = test.result && test.result.totals;
6234
- if (totals) {
6235
- // Add 2 per filter-styled element, 1 per filter-impacted element.
6236
- addDetail('testaro', which, 2 * totals.elements + totals.impact);
6237
- }
6238
- }
6239
- else if (which === 'focAll') {
6240
- const discrepancy = test.result && test.result.discrepancy;
6241
- if (discrepancy) {
6242
- // Add 2 per discrepancy.
6243
- addDetail('testaro', which, 2 * Math.abs(discrepancy));
6244
- }
6245
- }
6246
- else if (which === 'focInd') {
6247
- const issueTypes =
6248
- test.result && test.result.totals && test.result.totals.types;
6249
- if (issueTypes) {
6250
- const missingCount = issueTypes.indicatorMissing
6251
- && issueTypes.indicatorMissing.total
6252
- || 0;
6253
- const badCount = issueTypes.nonOutlinePresent
6254
- && issueTypes.nonOutlinePresent.total
6255
- || 0;
6256
- // Add 3 per missing, 1 per non-outline focus indicator.
6257
- addDetail('testaro', which, badCount + 3 * missingCount);
6258
- }
6259
- }
6260
- else if (which === 'focOp') {
6261
- const issueTypes =
6262
- test.result && test.result.totals && test.result.totals.types;
6263
- if (issueTypes) {
6264
- const noOpCount = issueTypes.onlyFocusable && issueTypes.onlyFocusable.total || 0;
6265
- const noFocCount = issueTypes.onlyOperable && issueTypes.onlyOperable.total || 0;
6266
- // Add 2 per unfocusable, 0.5 per inoperable element.
6267
- addDetail('testaro', which, 2 * noFocCount + 0.5 * noOpCount);
6268
- }
6269
- }
6270
- else if (which === 'focVis') {
6271
- const count = test.result && test.result.total;
6272
- if (count) {
6273
- // Add 1 per link outside the viewport.
6274
- addDetail('testaro', which, count);
6275
- }
6276
- }
6277
- else if (which === 'hover') {
6278
- const issues = test.result && test.result.totals;
6279
- if (issues) {
6280
- const {
6281
- impactTriggers,
6282
- additions,
6283
- removals,
6284
- opacityChanges,
6285
- opacityImpact,
6286
- unhoverables,
6287
- noCursors,
6288
- badCursors,
6289
- noIndicators,
6290
- badIndicators
6291
- } = issues;
6292
- // Add score with weights on hover-impact types.
6293
- const score = 2 * impactTriggers
6294
- + 0.3 * additions
6295
- + removals
6296
- + 0.2 * opacityChanges
6297
- + 0.1 * opacityImpact
6298
- + unhoverables
6299
- + 3 * noCursors
6300
- + 2 * badCursors
6301
- + noIndicators
6302
- + badIndicators;
6303
- if (score) {
6304
- addDetail('testaro', which, score);
6305
- }
6306
- }
6307
- }
6308
- else if (which === 'labClash') {
6309
- const mislabeledCount = test.result
6310
- && test.result.totals
6311
- && test.result.totals.mislabeled
6312
- || 0;
6313
- // Add 1 per element with conflicting labels (ignoring unlabeled elements).
6314
- addDetail('testaro', which, mislabeledCount);
6315
- }
6316
- else if (which === 'linkTo') {
6317
- const count = test.result && test.result.total;
6318
- if (count) {
6319
- // Add 2 per link with no destination.
6320
- addDetail('testaro', which, count);
6321
- }
6322
- }
6323
- else if (which === 'linkUl') {
6324
- const totals = test.result && test.result.totals && test.result.totals.adjacent;
6325
- if (totals) {
6326
- const nonUl = totals.total - totals.underlined || 0;
6327
- // Add 2 per non-underlined adjacent link.
6328
- addDetail('testaro', which, 2 * nonUl);
6329
- }
6330
- }
6331
- else if (which === 'menuNav') {
6332
- const issueCount = test.result
6333
- && test.result.totals
6334
- && test.result.totals.navigations
6335
- && test.result.totals.navigations.all
6336
- && test.result.totals.navigations.all.incorrect
6337
- || 0;
6338
- // Add 2 per defect.
6339
- addDetail('testaro', which, 2 * issueCount);
6340
- }
6341
- else if (which === 'miniText') {
6342
- const items = test.result && test.result.items;
6343
- if (items && items.length) {
6344
- // Add 1 per 100 characters of small-text.
6345
- const totalLength = items.reduce((total, item) => total + item.length, 0);
6346
- addDetail('testaro', which, Math.floor(totalLength / 100));
6347
- }
6348
- }
6349
- else if (which === 'motion') {
6350
- const data = test.result;
6351
- if (data) {
6352
- const {
6353
- meanLocalRatio,
6354
- maxLocalRatio,
6355
- globalRatio,
6356
- meanPixelChange,
6357
- maxPixelChange,
6358
- changeFrequency
6359
- } = data;
6360
- const score = 2 * (meanLocalRatio - 1)
6361
- + (maxLocalRatio - 1)
6362
- + globalRatio - 1
6363
- + meanPixelChange / 10000
6364
- + maxPixelChange / 25000
6365
- + 3 * changeFrequency
6366
- || 0;
6367
- addDetail('testaro', which, score);
6368
- }
6369
- }
6370
- else if (which === 'nonTable') {
6371
- const total = test.result && test.result.total;
6372
- if (total) {
6373
- // Add 2 per pseudotable.
6374
- addDetail('testaro', which, 2 * total);
6375
- }
6376
- }
6377
- else if (which === 'radioSet') {
6378
- const totals = test.result && test.result.totals;
6379
- if (totals) {
6380
- const {total, inSet} = totals;
6381
- const score = total - inSet || 0;
6382
- // Add 1 per misgrouped radio button.
6383
- addDetail('testaro', which, score);
6384
- }
6385
- }
6386
- else if (which === 'role') {
6387
- const badCount = test.result && test.result.badRoleElements || 0;
6388
- const redundantCount = test.result && test.result.redundantRoleElements || 0;
6389
- // Add 2 per bad role and 1 per redundant role.
6390
- addDetail('testaro', which, 2 * badCount + redundantCount);
6391
- }
6392
- else if (which === 'styleDiff') {
6393
- const totals = test.result && test.result.totals;
6394
- if (totals) {
6395
- let score = 0;
6396
- // For each element type that has any style diversity:
6397
- Object.values(totals).forEach(typeData => {
6398
- const {total, subtotals} = typeData;
6399
- if (subtotals) {
6400
- const styleCount = subtotals.length;
6401
- const plurality = subtotals[0];
6402
- const minorities = total - plurality;
6403
- // Add 1 per style, 0.2 per element with any nonplurality style.
6404
- score += styleCount + 0.2 * minorities;
6405
- }
6406
- });
6407
- addDetail('testaro', which, score);
6408
- }
6409
- }
6410
- else if (which === 'tabNav') {
6411
- const issueCount = test.result
6412
- && test.result.totals
6413
- && test.result.totals.navigations
6414
- && test.result.totals.navigations.all
6415
- && test.result.totals.navigations.all.incorrect
6416
- || 0;
6417
- // Add 2 per defect.
6418
- addDetail('testaro', which, 2 * issueCount);
6419
- }
6420
- else if (which === 'titledEl') {
6421
- const total = test.result && test.result.total;
6422
- if (total) {
6423
- const score = 4 * total;
6424
- // Add 4 per mistitled element.
6425
- addDetail('testaro', which, score);
6426
- }
6427
- }
6428
- else if (which === 'zIndex') {
6429
- const issueCount = test.result && test.result.totals && test.result.totals.total || 0;
6430
- // Add 1 per non-auto zIndex.
6431
- addDetail('testaro', which, issueCount);
6432
- }
6433
- });
6434
- // Get the prevention scores and add them to the summary.
6435
- const actsPrevented = testActs.filter(test => test.result.prevented);
6436
- actsPrevented.forEach(act => {
6437
- if (otherPackages.includes(act.which)) {
6438
- preventionScores[act.which] = preventionWeights.other;
6439
- }
6440
- else {
6441
- preventionScores[`testaro-${act.which}`] = preventionWeights.testaro;
6442
- }
6443
- });
6444
- const preventionScore = Object.values(preventionScores).reduce(
6445
- (sum, current) => sum + current,
6446
- 0
6447
- );
6448
- const roundedPreventionScore = Math.round(preventionScore);
6449
- summary.preventions = roundedPreventionScore;
6450
- summary.total += roundedPreventionScore;
6451
- // Initialize a table of the groups to which tests belong.
6452
- const testGroups = {
6453
- testaro: {},
6454
- alfa: {},
6455
- axe: {},
6456
- continuum: {},
6457
- htmlcs: {},
6458
- ibm: {},
6459
- nuVal: {},
6460
- tenon: {},
6461
- wave: {}
6462
- };
6463
- // Initialize a table of the regular expressions of variably named tests of packages.
6464
- const testMatchers = {};
6465
- Object.keys(groups).forEach(groupName => {
6466
- Object.keys(groups[groupName].packages).forEach(packageName => {
6467
- Object.keys(groups[groupName].packages[packageName]).forEach(testID => {
6468
- // Update the group table.
6469
- testGroups[packageName][testID] = groupName;
6470
- // If the test is variably named:
6471
- if (groups[groupName].packages[packageName][testID].variable) {
6472
- // Add its regular expression, as multiline, to the variably-named-test table.
6473
- if (! testMatchers[packageName]) {
6474
- testMatchers[packageName] = [];
6475
- }
6476
- testMatchers[packageName].push(new RegExp(testID, 's'));
6477
- }
6478
- });
6479
- });
6480
- });
6481
- // For each package with any scores:
6482
- Object.keys(packageDetails).forEach(packageName => {
6483
- const matchers = testMatchers[packageName];
6484
- // For each test with any scores in the package:
6485
- Object.keys(packageDetails[packageName]).forEach(testMessage => {
6486
- // Initialize the test ID as the reported test message.
6487
- let testID = testMessage;
6488
- // Get the group of the test, if it has a fixed name and is in a group.
6489
- let groupName = testGroups[packageName][testMessage];
6490
- // If the test has a variable name or is a solo test:
6491
- if (! groupName) {
6492
- // Determine whether the package has variably named tests and the test is among them.
6493
- testRegExp = matchers && matchers.find(matcher => matcher.test(testMessage));
6494
- // If so:
6495
- if (testRegExp) {
6496
- // Make the matching regular expression the test ID.
6497
- testID = testRegExp.source;
6498
- // Get the group of the test.
6499
- groupName = testGroups[packageName][testID];
6500
- }
6501
- }
6502
- // If the test is in a group:
6503
- if (groupName) {
6504
- // Initialize its score as its score in the package details.
6505
- if (! groupDetails.groups[groupName]) {
6506
- groupDetails.groups[groupName] = {
6507
- wcag: groups[groupName].wcag,
6508
- packages: {}
6509
- };
6510
- }
6511
- if (! groupDetails.groups[groupName].packages[packageName]) {
6512
- groupDetails.groups[groupName].packages[packageName] = {};
6513
- }
6514
- let weightedScore = packageDetails[packageName][testMessage];
6515
- // Weight that by the group weight and normalize it to a 1–4 scale per instance.
6516
- weightedScore *= groups[groupName].weight / 4;
6517
- // Adjust the score for the quality of the test.
6518
- weightedScore *= groups[groupName].packages[packageName][testID].quality;
6519
- // Round the score, but not to less than 1.
6520
- const roundedScore = Math.max(Math.round(weightedScore), 1);
6521
- // Add the rounded score and the test description to the group details.
6522
- groupDetails.groups[groupName].packages[packageName][testID] = {
6523
- score: roundedScore,
6524
- what: groups[groupName].packages[packageName][testID].what
6525
- };
6526
- }
6527
- // Otherwise, i.e. if the test is solo:
6528
- else {
6529
- if (! groupDetails.solos[packageName]) {
6530
- groupDetails.solos[packageName] = {};
6531
- }
6532
- const roundedScore = Math.round(packageDetails[packageName][testID]);
6533
- groupDetails.solos[packageName][testID] = roundedScore;
6534
- }
6535
- });
6536
- });
6537
- // Determine the group scores and add them to the summary.
6538
- const groupNames = Object.keys(groupDetails.groups);
6539
- const {absolute, largest, smaller} = groupWeights;
6540
- // For each group with any scores:
6541
- groupNames.forEach(groupName => {
6542
- const scores = [];
6543
- // For each package with any scores in the group:
6544
- const groupPackageData = Object.values(groupDetails.groups[groupName].packages);
6545
- groupPackageData.forEach(packageObj => {
6546
- // Get the sum of the scores of the tests of the package in the group.
6547
- const scoreSum = Object.values(packageObj).reduce(
6548
- (sum, current) => sum + current.score,
6549
- 0
6550
- );
6551
- // Add the sum to the list of package scores in the group.
6552
- scores.push(scoreSum);
6553
- });
6554
- // Sort the scores in descending order.
6555
- scores.sort((a, b) => b - a);
6556
- // Compute the sum of the absolute score and the weighted largest and other scores.
6557
- const groupScore = absolute
6558
- + largest * scores[0]
6559
- + smaller * scores.slice(1).reduce((sum, current) => sum + current, 0);
6560
- const roundedGroupScore = Math.round(groupScore);
6561
- summary.groups.push({
6562
- groupName,
6563
- score: roundedGroupScore
6564
- });
6565
- summary.total += roundedGroupScore;
6566
- });
6567
- summary.groups.sort((a, b) => b.score - a.score);
6568
- // Determine the solo score and add it to the summary.
6569
- const soloPackageNames = Object.keys(groupDetails.solos);
6570
- soloPackageNames.forEach(packageName => {
6571
- const testIDs = Object.keys(groupDetails.solos[packageName]);
6572
- testIDs.forEach(testID => {
6573
- const score = soloWeight * groupDetails.solos[packageName][testID];
6574
- summary.solos += score;
6575
- summary.total += score;
6576
- });
6577
- });
6578
- summary.solos = Math.round(summary.solos);
6579
- summary.total = Math.round(summary.total);
6580
- }
6581
- }
6582
- // Get the log score.
6583
- const {jobData} = report;
6584
- const logScore = logWeights.logCount * jobData.logCount
6585
- + logWeights.logSize * jobData.logSize +
6586
- + logWeights.errorLogCount * jobData.errorLogCount
6587
- + logWeights.errorLogSize * jobData.errorLogSize
6588
- + logWeights.prohibitedCount * jobData.prohibitedCount +
6589
- + logWeights.visitTimeoutCount * jobData.visitTimeoutCount +
6590
- + logWeights.visitRejectionCount * jobData.visitRejectionCount
6591
- + logWeights.visitLatency * (jobData.visitLatency - normalLatency);
6592
- const roundedLogScore = Math.max(0, Math.round(logScore));
6593
- summary.log = roundedLogScore;
6594
- summary.total += roundedLogScore;
6595
- // Add the score facts to the report.
6596
- report.score = {
6597
- scoreProcID,
6598
- logWeights,
6599
- soloWeight,
6600
- groupWeights,
6601
- preventionWeights,
6602
- packageDetails,
6603
- groupDetails,
6604
- preventionScores,
6605
- summary
6606
- };
6607
- };