@@ -17,7 +17,7 @@ import {
17
17
html ,
18
18
nextFrame ,
19
19
oneEvent ,
20
- waitUntil
20
+ waitUntil ,
21
21
} from '@open-wc/testing' ;
22
22
import { testForLitDevWarnings } from '../../../test/testing-helpers' ;
23
23
@@ -43,7 +43,8 @@ import {
43
43
a11ySnapshot ,
44
44
findAccessibilityNode ,
45
45
sendKeys ,
46
- setViewport } from '@web/test-runner-commands' ;
46
+ setViewport ,
47
+ } from '@web/test-runner-commands' ;
47
48
import { TemplateResult } from '@spectrum-web-components/base' ;
48
49
import { isWebKit } from '@spectrum-web-components/shared' ;
49
50
import { SAFARI_FOCUS_RING_CLASS } from '@spectrum-web-components/picker/src/InteractionController.js' ;
@@ -107,6 +108,120 @@ export const testActionMenu = (mode: 'sync' | 'async'): void => {
107
108
108
109
await expect ( el ) . to . be . accessible ( ) ;
109
110
} ) ;
111
+
112
+ describe ( 'accessibility warnings' , ( ) => {
113
+ let warnSpy : sinon . SinonSpy ;
114
+
115
+ let originalWarn : typeof window . __swc . warn ;
116
+
117
+ beforeEach ( ( ) => {
118
+ // Create __swc if it doesn't exist
119
+ window . __swc = window . __swc || { warn : ( ) => { } } ;
120
+ // Store original warn function
121
+ originalWarn = window . __swc . warn ;
122
+ // Create spy
123
+ warnSpy = spy ( ) ;
124
+ window . __swc . warn = warnSpy ;
125
+ } ) ;
126
+
127
+ afterEach ( ( ) => {
128
+ // Restore original warn function
129
+ window . __swc . warn = originalWarn ;
130
+ } ) ;
131
+
132
+ it ( 'warns when no accessible label is provided' , async ( ) => {
133
+ const el = await fixture < ActionMenu > ( html `
134
+ < sp-action-menu >
135
+ < sp-menu-item > Deselect</ sp-menu-item >
136
+ </ sp-action-menu >
137
+ ` ) ;
138
+
139
+ await elementUpdated ( el ) ;
140
+ await nextFrame ( ) ;
141
+ await nextFrame ( ) ;
142
+
143
+ expect ( warnSpy . called ) . to . be . true ;
144
+ expect ( warnSpy . firstCall . args [ 0 ] ) . to . equal ( el ) ;
145
+ expect ( warnSpy . firstCall . args [ 1 ] ) . to . equal (
146
+ '<sp-action-menu> needs one of the following to be accessible:'
147
+ ) ;
148
+ expect ( warnSpy . firstCall . args [ 2 ] ) . to . equal (
149
+ 'https://opensource.adobe.com/spectrum-web-components/components/action-menu/#accessibility'
150
+ ) ;
151
+ expect ( warnSpy . firstCall . args [ 3 ] ) . to . deep . equal ( {
152
+ type : 'accessibility' ,
153
+ issues : [
154
+ 'an <sp-field-label> element with a `for` attribute referencing the `id` of the `<sp-action-menu>`, or' ,
155
+ 'value supplied to the "label" attribute, which will be displayed visually as placeholder text' ,
156
+ 'text content supplied in a <span> with slot="label", or, text content supplied in a <span> with slot="label-only"' ,
157
+ 'which will also be displayed visually as placeholder text.' ,
158
+ ] ,
159
+ } ) ;
160
+ } ) ;
161
+
162
+ it ( 'does not warn when label attribute is provided' , async ( ) => {
163
+ const el = await fixture < ActionMenu > ( html `
164
+ < sp-action-menu label ="More Actions ">
165
+ < sp-menu-item > Deselect</ sp-menu-item >
166
+ </ sp-action-menu >
167
+ ` ) ;
168
+
169
+ await elementUpdated ( el ) ;
170
+
171
+ expect ( warnSpy . called ) . to . be . false ;
172
+ } ) ;
173
+
174
+ it ( 'does not warn when label slot is provided' , async ( ) => {
175
+ const el = await fixture < ActionMenu > ( html `
176
+ < sp-action-menu >
177
+ < span slot ="label "> More Actions</ span >
178
+ < sp-menu-item > Deselect</ sp-menu-item >
179
+ </ sp-action-menu >
180
+ ` ) ;
181
+
182
+ await elementUpdated ( el ) ;
183
+
184
+ expect ( warnSpy . called ) . to . be . false ;
185
+ } ) ;
186
+
187
+ it ( 'does not warn when label-only slot is provided' , async ( ) => {
188
+ const el = await fixture < ActionMenu > ( html `
189
+ < sp-action-menu >
190
+ < span slot ="label-only "> More Actions</ span >
191
+ < sp-menu-item > Deselect</ sp-menu-item >
192
+ </ sp-action-menu >
193
+ ` ) ;
194
+
195
+ await elementUpdated ( el ) ;
196
+
197
+ expect ( warnSpy . called ) . to . be . false ;
198
+ } ) ;
199
+
200
+ it ( 'does not warn when aria-label is provided' , async ( ) => {
201
+ const el = await fixture < ActionMenu > ( html `
202
+ < sp-action-menu aria-label ="More Actions ">
203
+ < sp-menu-item > Deselect</ sp-menu-item >
204
+ </ sp-action-menu >
205
+ ` ) ;
206
+
207
+ await elementUpdated ( el ) ;
208
+
209
+ expect ( warnSpy . called ) . to . be . false ;
210
+ } ) ;
211
+
212
+ it ( 'does not warn when aria-labelledby is provided' , async ( ) => {
213
+ const el = await fixture < ActionMenu > ( html `
214
+ < div id ="label-el "> More Actions</ div >
215
+ < sp-action-menu aria-labelledby ="label-el ">
216
+ < sp-menu-item > Deselect</ sp-menu-item >
217
+ </ sp-action-menu >
218
+ ` ) ;
219
+
220
+ await elementUpdated ( el ) ;
221
+
222
+ expect ( warnSpy . called ) . to . be . false ;
223
+ } ) ;
224
+ } ) ;
110
225
it ( 'loads - [slot="label"]' , async ( ) => {
111
226
const el = await fixture < ActionMenu > ( html `
112
227
< sp-action-menu >
@@ -147,10 +262,9 @@ export const testActionMenu = (mode: 'sync' | 'async'): void => {
147
262
148
263
await expect ( el ) . to . be . accessible ( ) ;
149
264
} ) ;
150
- it ( 'has menuitems in accessibility tree' , async ( ) => {
265
+ it ( 'has menuitems in accessibility tree' , async ( ) => {
151
266
const el = await fixture < ActionMenu > ( html `
152
- < sp-action-menu
153
- label ="More Actions ">
267
+ < sp-action-menu label ="More Actions ">
154
268
< sp-menu-item > Deselect</ sp-menu-item >
155
269
< sp-menu-item disabled > Make Work Path</ sp-menu-item >
156
270
</ sp-action-menu >
@@ -161,15 +275,31 @@ export const testActionMenu = (mode: 'sync' | 'async'): void => {
161
275
await opened ;
162
276
await nextFrame ( ) ;
163
277
164
-
165
- type NamedNode = { name : string , role : string , disabled : boolean } ;
166
- const snapshot = ( await a11ySnapshot ( { } ) ) as unknown as NamedNode & {
278
+ type NamedNode = { name : string ; role : string ; disabled : boolean } ;
279
+ const snapshot = ( await a11ySnapshot (
280
+ { }
281
+ ) ) as unknown as NamedNode & {
167
282
children : NamedNode [ ] ;
168
283
} ;
169
- const button = findAccessibilityNode < NamedNode > ( snapshot , ( node ) => node . name === 'More Actions' ) ;
170
- const menu = findAccessibilityNode < NamedNode > ( snapshot , ( node ) => node . role === 'menu' ) ;
171
- const deselect = findAccessibilityNode < NamedNode > ( snapshot , ( node ) => node . role === 'menuitem' && node . name === 'Deselect' ) ;
172
- const workPath = findAccessibilityNode < NamedNode > ( snapshot , ( node ) => node . role === 'menuitem' && node . name === 'Make Work Path' && node . disabled ) ;
284
+ const button = findAccessibilityNode < NamedNode > (
285
+ snapshot ,
286
+ ( node ) => node . name === 'More Actions'
287
+ ) ;
288
+ const menu = findAccessibilityNode < NamedNode > (
289
+ snapshot ,
290
+ ( node ) => node . role === 'menu'
291
+ ) ;
292
+ const deselect = findAccessibilityNode < NamedNode > (
293
+ snapshot ,
294
+ ( node ) => node . role === 'menuitem' && node . name === 'Deselect'
295
+ ) ;
296
+ const workPath = findAccessibilityNode < NamedNode > (
297
+ snapshot ,
298
+ ( node ) =>
299
+ node . role === 'menuitem' &&
300
+ node . name === 'Make Work Path' &&
301
+ node . disabled
302
+ ) ;
173
303
expect ( button , 'button' ) . to . not . be . null ;
174
304
expect ( menu , 'menu' ) . to . not . be . null ;
175
305
expect ( deselect , 'first menuitem' ) . to . not . be . null ;
@@ -526,14 +656,13 @@ export const testActionMenu = (mode: 'sync' | 'async'): void => {
526
656
expect ( el . value ) . to . not . equal ( thirdItem . value ) ;
527
657
const opened = oneEvent ( el , 'sp-opened' ) ;
528
658
el . focus ( ) ;
529
- await sendKeys ( { press : 'Enter' } )
659
+ await sendKeys ( { press : 'Enter' } ) ;
530
660
await opened ;
531
661
532
- await sendKeys ( { press : 'Escape' } ) ;
533
- await waitUntil (
534
- ( ) => document . activeElement === el ,
535
- 'focused' , { timeout : 300 }
536
- ) ;
662
+ await sendKeys ( { press : 'Escape' } ) ;
663
+ await waitUntil ( ( ) => document . activeElement === el , 'focused' , {
664
+ timeout : 300 ,
665
+ } ) ;
537
666
expect ( el . open ) . to . be . false ;
538
667
} ) ;
539
668
it ( 'returns focus on select' , async ( ) => {
@@ -545,14 +674,13 @@ export const testActionMenu = (mode: 'sync' | 'async'): void => {
545
674
expect ( el . value ) . to . not . equal ( thirdItem . value ) ;
546
675
const opened = oneEvent ( el , 'sp-opened' ) ;
547
676
el . focus ( ) ;
548
- await sendKeys ( { press : 'Enter' } )
677
+ await sendKeys ( { press : 'Enter' } ) ;
549
678
await opened ;
550
679
551
680
thirdItem . click ( ) ;
552
- await waitUntil (
553
- ( ) => document . activeElement === el ,
554
- 'focused' , { timeout : 300 }
555
- ) ;
681
+ await waitUntil ( ( ) => document . activeElement === el , 'focused' , {
682
+ timeout : 300 ,
683
+ } ) ;
556
684
expect ( el . open ) . to . be . false ;
557
685
} ) ;
558
686
it ( 'has attribute aria-describedby' , async ( ) => {
0 commit comments