@@ -3,6 +3,79 @@ import {CdkRadioButton, CdkRadioGroup} from './radio';
3
3
import { ComponentFixture , TestBed } from '@angular/core/testing' ;
4
4
import { By } from '@angular/platform-browser' ;
5
5
import { BidiModule , Direction , Directionality } from '@angular/cdk/bidi' ;
6
+ import axe from 'axe-core' ;
7
+
8
+ // Basic ANSI color functions because chalk has issues with unit tests.
9
+ const colors = {
10
+ red : ( text : string ) => `\x1b[31m${ text } \x1b[0m` ,
11
+ yellow : ( text : string ) => `\x1b[33m${ text } \x1b[0m` ,
12
+ blue : ( text : string ) => `\x1b[34m${ text } \x1b[0m` ,
13
+ magenta : ( text : string ) => `\x1b[35m${ text } \x1b[0m` ,
14
+ cyan : ( text : string ) => `\x1b[36m${ text } \x1b[0m` ,
15
+ gray : ( text : string ) => `\x1b[90m${ text } \x1b[0m` ,
16
+ underline : ( text : string ) => `\x1b[4m${ text } \x1b[0m` ,
17
+ default : ( text : string ) => `\x1b[0m${ text } \x1b[0m` ,
18
+ } ;
19
+
20
+ // TODO: Move this to a separate folder/file so it can be reused across components.
21
+ async function getAccessibilityViolationsReport ( root : HTMLElement ) : Promise < string | null > {
22
+ const results = await axe . run ( root ) ;
23
+
24
+ if ( ! results . violations . length ) {
25
+ return null ;
26
+ }
27
+
28
+ const reportLines : string [ ] = [ ] ;
29
+ const append = ( text : string ) => reportLines . push ( colors . default ( text ) ) ;
30
+ append ( colors . red ( `Found ${ results . violations . length } accessibility violation(s):` ) ) ;
31
+
32
+ results . violations . forEach ( ( violation , index ) => {
33
+ append ( '' ) ;
34
+ append ( colors . red ( `Violation ${ index + 1 } : ${ violation . id } \n` ) ) ;
35
+
36
+ let impactText = violation . impact || 'unknown' ;
37
+ switch ( violation . impact ) {
38
+ case 'critical' :
39
+ impactText = colors . red ( impactText ) ;
40
+ break ;
41
+ case 'serious' :
42
+ impactText = colors . yellow ( impactText ) ;
43
+ break ;
44
+ case 'moderate' :
45
+ impactText = colors . blue ( impactText ) ;
46
+ break ;
47
+ case 'minor' :
48
+ impactText = colors . gray ( impactText ) ;
49
+ break ;
50
+ default :
51
+ impactText = colors . default ( impactText ) ;
52
+ break ;
53
+ }
54
+
55
+ append ( ` Impact: ${ impactText } ` ) ;
56
+ append ( ` Description: ${ violation . description } ` ) ;
57
+ append ( ` Help: ${ violation . help } ` ) ;
58
+ append ( ` Help URL: ${ colors . underline ( colors . blue ( violation . helpUrl ) ) } \n` ) ;
59
+
60
+ if ( violation . nodes && violation . nodes . length > 0 ) {
61
+ append ( ' Failing Elements:' ) ;
62
+ violation . nodes . forEach ( ( node , nodeIndex ) => {
63
+ append ( colors . cyan ( ` Node ${ nodeIndex + 1 } :` ) ) ;
64
+ if ( node . target && node . target . length > 0 ) {
65
+ append ( ` Selector: ${ colors . magenta ( node . target . join ( ', ' ) ) } ` ) ;
66
+ }
67
+ if ( node . failureSummary ) {
68
+ append ( ' Failure Summary:' ) ;
69
+ node . failureSummary
70
+ . split ( '\n' )
71
+ . forEach ( line => append ( colors . yellow ( ` ${ line . trim ( ) } ` ) ) ) ;
72
+ }
73
+ } ) ;
74
+ }
75
+ } ) ;
76
+
77
+ return reportLines . join ( '\n' ) ;
78
+ }
6
79
7
80
describe ( 'CdkRadioGroup' , ( ) => {
8
81
let fixture : ComponentFixture < RadioGroupExample > ;
@@ -46,14 +119,16 @@ describe('CdkRadioGroup', () => {
46
119
47
120
const fixture = TestBed . createComponent < T > ( component ) ;
48
121
fixture . detectChanges ( ) ;
122
+ defineTestVariables ( fixture ) ;
123
+ return fixture ;
124
+ }
49
125
126
+ function defineTestVariables ( fixture : ComponentFixture < unknown > ) {
50
127
radioGroup = fixture . debugElement . query ( By . directive ( CdkRadioGroup ) ) ;
51
- radioButtons = radioGroup . queryAll ( By . directive ( CdkRadioButton ) ) ;
128
+ radioButtons = fixture . debugElement . queryAll ( By . directive ( CdkRadioButton ) ) ;
52
129
radioGroupInstance = radioGroup . injector . get < CdkRadioGroup < number > > ( CdkRadioGroup ) ;
53
130
radioGroupElement = radioGroup . nativeElement ;
54
131
radioButtonElements = radioButtons . map ( radioButton => radioButton . nativeElement ) ;
55
-
56
- return fixture ;
57
132
}
58
133
59
134
function setupRadioGroup ( opts ?: {
@@ -99,8 +174,17 @@ describe('CdkRadioGroup', () => {
99
174
textDirection . emit ( opts . textDirection ) ;
100
175
}
101
176
fixture . detectChanges ( ) ;
177
+ defineTestVariables ( fixture ) ; // Ensure env vars are up-to-date with the dom.
102
178
}
103
179
180
+ afterEach ( async ( ) => {
181
+ const report = await getAccessibilityViolationsReport ( radioGroupElement ) ;
182
+
183
+ if ( report ) {
184
+ fail ( report ) ;
185
+ }
186
+ } ) ;
187
+
104
188
describe ( 'ARIA attributes and roles' , ( ) => {
105
189
describe ( 'default configuration' , ( ) => {
106
190
beforeEach ( ( ) => {
@@ -490,10 +574,15 @@ describe('CdkRadioGroup', () => {
490
574
return radioGroupElement . getAttribute ( 'aria-activedescendant' ) === radioButtonElements [ i ] . id ;
491
575
} ) ;
492
576
493
- it ( 'should handle an empty set of radio buttons gracefully' , ( ) => {
494
- setupRadioGroup ( { options : [ ] } ) ;
495
- radioButtons = fixture . debugElement . queryAll ( By . directive ( CdkRadioButton ) ) ;
496
- expect ( radioButtons . length ) . toBe ( 0 ) ;
577
+ describe ( 'failure cases' , ( ) => {
578
+ beforeEach ( ( ) => {
579
+ fixture = setupTestEnvironment ( RadioGroupExample ) ;
580
+ } ) ;
581
+
582
+ it ( 'should handle an empty set of radio buttons gracefully' , ( ) => {
583
+ setupRadioGroup ( { options : [ ] } ) ;
584
+ expect ( radioButtons . length ) . toBe ( 0 ) ;
585
+ } ) ;
497
586
} ) ;
498
587
} ) ;
499
588
0 commit comments