@@ -133,3 +133,58 @@ describe('input: clear icon', () => {
133133 expect ( icon . getAttribute ( 'icon' ) ) . toBe ( 'foo' ) ;
134134 } ) ;
135135} ) ;
136+
137+ // Regression tests for screen reader accessibility of error messages
138+ describe ( 'input: error text accessibility' , ( ) => {
139+ it ( 'should have aria-live="assertive" and aria-atomic="true" on error text for screen reader announcements' , async ( ) => {
140+ const page = await newSpecPage ( {
141+ components : [ Input ] ,
142+ html : `<ion-input label="Input" error-text="This field is required"></ion-input>` ,
143+ } ) ;
144+
145+ const errorTextEl = page . body . querySelector ( 'ion-input .error-text' ) ;
146+ expect ( errorTextEl ) . not . toBe ( null ) ;
147+
148+ // These attributes ensure screen readers announce errors immediately when they appear
149+ expect ( errorTextEl ! . getAttribute ( 'aria-live' ) ) . toBe ( 'assertive' ) ;
150+ expect ( errorTextEl ! . getAttribute ( 'aria-atomic' ) ) . toBe ( 'true' ) ;
151+ } ) ;
152+
153+ it ( 'should maintain accessibility attributes when error text changes dynamically' , async ( ) => {
154+ const page = await newSpecPage ( {
155+ components : [ Input ] ,
156+ html : `<ion-input label="Input"></ion-input>` ,
157+ } ) ;
158+
159+ const input = page . body . querySelector ( 'ion-input' ) ! ;
160+
161+ // Simulate validation error appearing on blur
162+ input . setAttribute ( 'error-text' , 'Invalid email format' ) ;
163+ await page . waitForChanges ( ) ;
164+
165+ const errorTextEl = page . body . querySelector ( 'ion-input .error-text' ) ;
166+ expect ( errorTextEl ) . not . toBe ( null ) ;
167+ expect ( errorTextEl ! . getAttribute ( 'aria-live' ) ) . toBe ( 'assertive' ) ;
168+ expect ( errorTextEl ! . getAttribute ( 'aria-atomic' ) ) . toBe ( 'true' ) ;
169+ expect ( errorTextEl ! . textContent ) . toBe ( 'Invalid email format' ) ;
170+ } ) ;
171+
172+ it ( 'should properly link error text to input via aria-describedby when invalid' , async ( ) => {
173+ const page = await newSpecPage ( {
174+ components : [ Input ] ,
175+ html : `<ion-input label="Input" error-text="Required field" class="ion-touched ion-invalid"></ion-input>` ,
176+ } ) ;
177+
178+ const nativeInput = page . body . querySelector ( 'ion-input input' ) ! ;
179+ const errorTextEl = page . body . querySelector ( 'ion-input .error-text' ) ! ;
180+
181+ // Verify the input references the error text ID
182+ const describedBy = nativeInput . getAttribute ( 'aria-describedby' ) ;
183+ const errorId = errorTextEl . getAttribute ( 'id' ) ;
184+
185+ expect ( describedBy ) . toBe ( errorId ) ;
186+ expect ( describedBy ) . toContain ( 'error-text' ) ;
187+ // When invalid, aria-invalid should be set
188+ expect ( nativeInput . hasAttribute ( 'aria-invalid' ) ) . toBe ( true ) ;
189+ } ) ;
190+ } ) ;
0 commit comments