@@ -12,6 +12,9 @@ ruleTester.run('template-require-valid-alt-text', rule, {
1212 '<template><img alt="Company branding" src="/logo.png" /></template>' ,
1313 '<template><img alt="" src="/decorative.png" /></template>' ,
1414 '<template><img hidden alt="" /></template>' ,
15+ // Whitespace-only alt — pin our current behavior. Peer plugins
16+ // (jsx-a11y) accept this; we don't trim before considering "empty alt".
17+ '<template><img alt=" " /></template>' ,
1518
1619 '<template><img alt="hullo"></template>' ,
1720 '<template><img alt={{foo}}></template>' ,
@@ -57,8 +60,89 @@ ruleTester.run('template-require-valid-alt-text', rule, {
5760 '<template><area aria-labelledby="some-alt"></template>' ,
5861 '<template><area aria-label="some-alt"></template>' ,
5962 '<template><img role={{unless this.altText "presentation"}} alt={{this.altText}}></template>' ,
63+ // Mustache-string-literal forms resolve to their static value — a
64+ // non-empty literal supplies an accessible name the same as a text node.
65+ '<template><input type="image" aria-label={{"valid"}} /></template>' ,
6066 ] ,
6167 invalid : [
68+ // Empty-string aria-label / aria-labelledby / alt provides no accessible
69+ // name, so these must flag.
70+ {
71+ code : '<template><input type="image" aria-label="" /></template>' ,
72+ output : null ,
73+ errors : [ { messageId : 'inputImage' } ] ,
74+ } ,
75+ {
76+ code : '<template><input type="image" aria-labelledby="" /></template>' ,
77+ output : null ,
78+ errors : [ { messageId : 'inputImage' } ] ,
79+ } ,
80+ {
81+ code : '<template><input type="image" alt="" /></template>' ,
82+ output : null ,
83+ errors : [ { messageId : 'inputImage' } ] ,
84+ } ,
85+ {
86+ code : '<template><object aria-label=""></object></template>' ,
87+ output : null ,
88+ errors : [ { messageId : 'objectMissing' } ] ,
89+ } ,
90+ {
91+ code : '<template><object aria-labelledby=""></object></template>' ,
92+ output : null ,
93+ errors : [ { messageId : 'objectMissing' } ] ,
94+ } ,
95+ {
96+ code : '<template><object title=""></object></template>' ,
97+ output : null ,
98+ errors : [ { messageId : 'objectMissing' } ] ,
99+ } ,
100+ {
101+ code : '<template><area aria-label=""></template>' ,
102+ output : null ,
103+ errors : [ { messageId : 'areaMissing' } ] ,
104+ } ,
105+ {
106+ code : '<template><area aria-labelledby=""></template>' ,
107+ output : null ,
108+ errors : [ { messageId : 'areaMissing' } ] ,
109+ } ,
110+ {
111+ code : '<template><area alt=""></template>' ,
112+ output : null ,
113+ errors : [ { messageId : 'areaMissing' } ] ,
114+ } ,
115+ // Whitespace-only values are not valid accessible names per ACCNAME 1.2 §4.3.2 step 2D.
116+ {
117+ code : '<template><input type="image" aria-label=" " /></template>' ,
118+ output : null ,
119+ errors : [ { messageId : 'inputImage' } ] ,
120+ } ,
121+ {
122+ code : '<template><object aria-labelledby="\n\t" ></object></template>' ,
123+ output : null ,
124+ errors : [ { messageId : 'objectMissing' } ] ,
125+ } ,
126+ // <area>: title is NOT one of the accepted fallbacks per ACCNAME.
127+ // Only alt / aria-label / aria-labelledby contribute. A whitespace-only
128+ // aria-label should be flagged.
129+ {
130+ code : '<template><area href="/" aria-label=" " /></template>' ,
131+ output : null ,
132+ errors : [ { messageId : 'areaMissing' } ] ,
133+ } ,
134+ // Mustache-string-literal forms that resolve to an empty string are
135+ // treated the same as the text-node empty value — no accessible name.
136+ {
137+ code : '<template><input type="image" aria-label={{""}} /></template>' ,
138+ output : null ,
139+ errors : [ { messageId : 'inputImage' } ] ,
140+ } ,
141+ {
142+ code : '<template><input type="image" aria-label="{{""}}" /></template>' ,
143+ output : null ,
144+ errors : [ { messageId : 'inputImage' } ] ,
145+ } ,
62146 {
63147 code : '<template><img src="/test.jpg" /></template>' ,
64148 output : null ,
@@ -264,6 +348,38 @@ hbsRuleTester.run('template-require-valid-alt-text', rule, {
264348 } ,
265349 ] ,
266350 } ,
351+ // HBS parity: empty / whitespace-only accessible-name fallbacks
352+ // should be flagged, mirroring the GTS cases above.
353+ {
354+ code : '<input type="image" aria-label=" " />' ,
355+ output : null ,
356+ errors : [
357+ {
358+ message :
359+ 'All <input> elements with type="image" must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` attribute.' ,
360+ } ,
361+ ] ,
362+ } ,
363+ {
364+ code : '<object aria-labelledby="\n\t" ></object>' ,
365+ output : null ,
366+ errors : [
367+ {
368+ message :
369+ 'Embedded <object> elements must have alternative text by providing inner text, aria-label or aria-labelledby attributes.' ,
370+ } ,
371+ ] ,
372+ } ,
373+ {
374+ code : '<area href="/" aria-label=" " />' ,
375+ output : null ,
376+ errors : [
377+ {
378+ message :
379+ 'Each area of an image map must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` attribute.' ,
380+ } ,
381+ ] ,
382+ } ,
267383 {
268384 code : '<img alt="picture">' ,
269385 output : null ,
0 commit comments