@@ -5,60 +5,293 @@ test.describe('Form', () => {
55 await page . goto ( '/lazy/form' ) ;
66 } ) ;
77
8- test ( 'should have form control initial value' , async ( { page } ) => {
9- await expect ( page . locator ( 'ion-input.required input' ) ) . toHaveValue ( '' ) ;
8+ test . describe ( 'status updates' , ( ) => {
9+ test ( 'should update Ionic form classes when calling form methods programmatically' , async ( { page } ) => {
10+ await page . locator ( '#input-touched' ) . click ( ) ;
11+ await expect ( page . locator ( '#touched-input-test' ) ) . toHaveClass ( / i o n - t o u c h e d / ) ;
12+ await page . locator ( '#input-otp-touched' ) . click ( ) ;
13+ await expect ( page . locator ( '#touched-input-otp-number-test' ) ) . toHaveClass ( / i o n - t o u c h e d / ) ;
14+ } ) ;
15+
16+ test ( 'markAllAsTouched should apply .ion-touched to nearest ion-item' , async ( { page } ) => {
17+ await page . locator ( '#mark-all-touched-button' ) . click ( ) ;
18+ const items = page . locator ( 'form ion-item' ) ;
19+ const count = await items . count ( ) ;
20+ for ( let i = 0 ; i < count ; i ++ ) {
21+ await expect ( items . nth ( i ) ) . toHaveClass ( / i o n - t o u c h e d / ) ;
22+ }
23+ } ) ;
1024 } ) ;
1125
12- test ( 'should reflect Ionic form control status classes' , async ( { page } ) => {
13- // Control is initially invalid
14- await expect ( page . locator ( 'ion-input.required' ) ) . toHaveClass ( / i o n - i n v a l i d / ) ;
15- await expect ( page . locator ( 'ion-input.required' ) ) . toHaveClass ( / i o n - p r i s t i n e / ) ;
16- await expect ( page . locator ( 'ion-input.required' ) ) . toHaveClass ( / i o n - u n t o u c h e d / ) ;
26+ test . describe ( 'change' , ( ) => {
27+ test ( 'should have default values' , async ( { page } ) => {
28+ await testStatus ( page , 'INVALID' ) ;
29+ await expect ( page . locator ( '#submit' ) ) . toHaveText ( 'false' ) ;
30+ await testData ( page , {
31+ datetime : '2010-08-20' ,
32+ select : null ,
33+ toggle : false ,
34+ input : '' ,
35+ input2 : 'Default Value' ,
36+ inputMin : 1 ,
37+ inputMax : 1 ,
38+ inputOtp : null ,
39+ inputOtpText : '' ,
40+ inputOtp2 : 1234 ,
41+ checkbox : false ,
42+ radio : null
43+ } ) ;
44+ } ) ;
1745
18- // Fill the input to make it valid
19- await page . locator ( 'ion-input.required input' ) . fill ( 'Some value' ) ;
20- await page . locator ( 'ion-input.required input' ) . blur ( ) ;
46+ test ( 'should become valid' , async ( { page } ) => {
47+ await page . locator ( 'ion-input.required input' ) . fill ( 'Some value' ) ;
48+ await page . locator ( 'ion-input.required input' ) . blur ( ) ;
2149
22- await expect ( page . locator ( 'ion-input.required' ) ) . toHaveClass ( / i o n - v a l i d / ) ;
23- await expect ( page . locator ( 'ion-input.required' ) ) . toHaveClass ( / i o n - d i r t y / ) ;
24- await expect ( page . locator ( 'ion-input.required' ) ) . toHaveClass ( / i o n - t o u c h e d / ) ;
25- } ) ;
50+ // Test number OTP input
51+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 0 ) . fill ( '5' ) ;
52+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 1 ) . fill ( '6' ) ;
53+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 2 ) . fill ( '7' ) ;
54+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 3 ) . fill ( '8' ) ;
55+ await page . locator ( '#touched-input-otp-number-test input' ) . last ( ) . focus ( ) ;
56+ await page . locator ( '#touched-input-otp-number-test input' ) . last ( ) . blur ( ) ;
2657
27- test ( 'should become valid when filled' , async ( { page } ) => {
28- await page . locator ( 'ion-input.required input' ) . fill ( 'Some value' ) ;
29- await page . locator ( 'ion-input.required input' ) . blur ( ) ;
58+ // Test text OTP input
59+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 0 ) . fill ( 'A' ) ;
60+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 1 ) . fill ( 'B' ) ;
61+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 2 ) . fill ( 'C' ) ;
62+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 3 ) . fill ( 'D' ) ;
63+ await page . locator ( '#touched-input-otp-text-test input' ) . last ( ) . focus ( ) ;
64+ await page . locator ( '#touched-input-otp-text-test input' ) . last ( ) . blur ( ) ;
3065
31- // Test number OTP input
32- await page . locator ( 'ion-input-otp input' ) . nth ( 0 ) . fill ( '1' ) ;
33- await page . locator ( 'ion-input-otp input' ) . nth ( 1 ) . fill ( '2' ) ;
34- await page . locator ( 'ion-input-otp input' ) . nth ( 2 ) . fill ( '3' ) ;
35- await page . locator ( 'ion-input-otp input' ) . nth ( 3 ) . fill ( '4' ) ;
66+ await testStatus ( page , 'INVALID' ) ;
3667
37- // Check that the OTP input is valid
38- await expect ( page . locator ( '#touched-input-otp-number-test' ) ) . toHaveClass ( / i o n - v a l i d / ) ;
39- } ) ;
68+ await page . locator ( 'ion-select' ) . click ( ) ;
69+ await expect ( page . locator ( 'ion-alert' ) ) . toBeVisible ( ) ;
70+ // NES option
71+ await page . locator ( 'ion-alert .alert-radio-button' ) . nth ( 1 ) . click ( ) ;
72+ // Click confirm button
73+ await page . locator ( 'ion-alert .alert-button:not(.alert-button-role-cancel)' ) . click ( ) ;
74+
75+ await testStatus ( page , 'VALID' ) ;
76+
77+ await testData ( page , {
78+ datetime : '2010-08-20' ,
79+ select : 'nes' ,
80+ toggle : false ,
81+ input : 'Some value' ,
82+ input2 : 'Default Value' ,
83+ inputMin : 1 ,
84+ inputMax : 1 ,
85+ inputOtp : 5678 ,
86+ inputOtpText : 'ABCD' ,
87+ inputOtp2 : 1234 ,
88+ checkbox : false ,
89+ radio : null
90+ } ) ;
91+ } ) ;
92+
93+ test ( 'ion-toggle should change' , async ( { page } ) => {
94+ await page . locator ( 'form ion-toggle' ) . click ( ) ;
95+ await testData ( page , {
96+ datetime : '2010-08-20' ,
97+ select : null ,
98+ toggle : true ,
99+ input : '' ,
100+ input2 : 'Default Value' ,
101+ inputMin : 1 ,
102+ inputMax : 1 ,
103+ inputOtp : null ,
104+ inputOtpText : '' ,
105+ inputOtp2 : 1234 ,
106+ checkbox : false ,
107+ radio : null
108+ } ) ;
109+ } ) ;
110+
111+ test ( 'ion-checkbox should change' , async ( { page } ) => {
112+ await page . locator ( 'ion-checkbox' ) . click ( ) ;
113+ await testData ( page , {
114+ datetime : '2010-08-20' ,
115+ select : null ,
116+ toggle : false ,
117+ input : '' ,
118+ input2 : 'Default Value' ,
119+ inputMin : 1 ,
120+ inputMax : 1 ,
121+ inputOtp : null ,
122+ inputOtpText : '' ,
123+ inputOtp2 : 1234 ,
124+ checkbox : true ,
125+ radio : null
126+ } ) ;
127+ } ) ;
128+
129+ test ( 'ion-radio should change' , async ( { page } ) => {
130+ await page . locator ( 'ion-radio' ) . click ( ) ;
131+ await testData ( page , {
132+ datetime : '2010-08-20' ,
133+ select : null ,
134+ toggle : false ,
135+ input : '' ,
136+ input2 : 'Default Value' ,
137+ inputMin : 1 ,
138+ inputMax : 1 ,
139+ inputOtp : null ,
140+ inputOtpText : '' ,
141+ inputOtp2 : 1234 ,
142+ checkbox : false ,
143+ radio : 'nes' ,
144+ } ) ;
145+ } ) ;
146+
147+ test ( 'ion-input-otp should change' , async ( { page } ) => {
148+ // Test number OTP input
149+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 0 ) . fill ( '5' ) ;
150+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 1 ) . fill ( '6' ) ;
151+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 2 ) . fill ( '7' ) ;
152+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 3 ) . fill ( '8' ) ;
153+ await page . locator ( '#touched-input-otp-number-test input' ) . last ( ) . focus ( ) ;
154+ await page . locator ( '#touched-input-otp-number-test input' ) . last ( ) . blur ( ) ;
155+
156+ // Test text OTP input
157+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 0 ) . fill ( 'A' ) ;
158+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 1 ) . fill ( 'B' ) ;
159+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 2 ) . fill ( 'C' ) ;
160+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 3 ) . fill ( 'D' ) ;
161+ await page . locator ( '#touched-input-otp-text-test input' ) . last ( ) . focus ( ) ;
162+ await page . locator ( '#touched-input-otp-text-test input' ) . last ( ) . blur ( ) ;
163+
164+ await testData ( page , {
165+ datetime : '2010-08-20' ,
166+ select : null ,
167+ toggle : false ,
168+ input : '' ,
169+ input2 : 'Default Value' ,
170+ inputMin : 1 ,
171+ inputMax : 1 ,
172+ inputOtp : 5678 ,
173+ inputOtpText : 'ABCD' ,
174+ inputOtp2 : 1234 ,
175+ checkbox : false ,
176+ radio : null
177+ } ) ;
178+ } ) ;
40179
41- test ( 'ion-input should error with min set' , async ( { page } ) => {
42- const control = page . locator ( 'form ion-input[formControlName="inputMin"]' ) ;
180+ test ( 'should submit' , async ( { page } ) => {
181+ await page . locator ( '#set-values' ) . click ( ) ;
182+ await page . locator ( '#submit-button' ) . click ( ) ;
183+ await expect ( page . locator ( '#submit' ) ) . toHaveText ( 'true' ) ;
184+ } ) ;
43185
44- // Control is initially valid
45- await expect ( control ) . toHaveClass ( / n g - v a l i d / ) ;
186+ test ( 'ion-input-otp should validate both number and text types' , async ( { page } ) => {
187+ // Test number OTP validation
188+ await expect ( page . locator ( '#touched-input-otp-number-test' ) ) . toHaveClass ( / n g - i n v a l i d / ) ;
189+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 0 ) . fill ( '5' ) ;
190+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 1 ) . fill ( '6' ) ;
191+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 2 ) . fill ( '7' ) ;
192+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 3 ) . fill ( '8' ) ;
193+ await page . locator ( '#touched-input-otp-number-test input' ) . last ( ) . focus ( ) ;
194+ await page . locator ( '#touched-input-otp-number-test input' ) . last ( ) . blur ( ) ;
195+ await expect ( page . locator ( '#touched-input-otp-number-test' ) ) . toHaveClass ( / n g - v a l i d / ) ;
46196
47- await control . locator ( 'input' ) . fill ( '0' ) ;
48- await control . locator ( 'input' ) . blur ( ) ;
197+ // Test text OTP validation
198+ await expect ( page . locator ( '#touched-input-otp-text-test' ) ) . toHaveClass ( / n g - i n v a l i d / ) ;
199+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 0 ) . fill ( 'A' ) ;
200+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 1 ) . fill ( 'B' ) ;
201+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 2 ) . fill ( 'C' ) ;
202+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 3 ) . fill ( 'D' ) ;
203+ await page . locator ( '#touched-input-otp-text-test input' ) . last ( ) . focus ( ) ;
204+ await page . locator ( '#touched-input-otp-text-test input' ) . last ( ) . blur ( ) ;
205+ await expect ( page . locator ( '#touched-input-otp-text-test' ) ) . toHaveClass ( / n g - v a l i d / ) ;
206+ } ) ;
49207
50- await expect ( control ) . toHaveClass ( / n g - i n v a l i d / ) ;
208+ test ( 'ion-input-otp should remain invalid when partially filled' , async ( { page } ) => {
209+ // Test number OTP with only first digit
210+ await expect ( page . locator ( '#touched-input-otp-number-test' ) ) . toHaveClass ( / n g - i n v a l i d / ) ;
211+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 0 ) . fill ( '5' ) ;
212+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 1 ) . focus ( ) ;
213+ await page . locator ( '#touched-input-otp-number-test input' ) . nth ( 1 ) . blur ( ) ;
214+ await expect ( page . locator ( '#touched-input-otp-number-test' ) ) . toHaveClass ( / n g - i n v a l i d / ) ;
215+
216+ // Test text OTP with only first character
217+ await expect ( page . locator ( '#touched-input-otp-text-test' ) ) . toHaveClass ( / n g - i n v a l i d / ) ;
218+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 0 ) . fill ( 'A' ) ;
219+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 1 ) . focus ( ) ;
220+ await page . locator ( '#touched-input-otp-text-test input' ) . nth ( 1 ) . blur ( ) ;
221+ await expect ( page . locator ( '#touched-input-otp-text-test' ) ) . toHaveClass ( / n g - i n v a l i d / ) ;
222+
223+ // Verify form status is still invalid
224+ await testStatus ( page , 'INVALID' ) ;
225+ } ) ;
51226 } ) ;
52227
53- test ( 'ion-input should error with max set' , async ( { page } ) => {
54- const control = page . locator ( 'form ion-input[formControlName="inputMax"]' ) ;
228+ test . describe ( 'blur' , ( ) => {
229+ test ( 'ion-toggle should change only after blur' , async ( { page } ) => {
230+ await page . locator ( 'form ion-toggle' ) . click ( ) ;
231+ await testData ( page , {
232+ datetime : '2010-08-20' ,
233+ select : null ,
234+ toggle : true ,
235+ input : '' ,
236+ input2 : 'Default Value' ,
237+ inputMin : 1 ,
238+ inputMax : 1 ,
239+ inputOtp : null ,
240+ inputOtpText : '' ,
241+ inputOtp2 : 1234 ,
242+ checkbox : false ,
243+ radio : null
244+ } ) ;
245+ await page . locator ( 'ion-checkbox' ) . click ( ) ;
246+ await testData ( page , {
247+ datetime : '2010-08-20' ,
248+ select : null ,
249+ toggle : true ,
250+ input : '' ,
251+ input2 : 'Default Value' ,
252+ inputMin : 1 ,
253+ inputMax : 1 ,
254+ inputOtp : null ,
255+ inputOtpText : '' ,
256+ inputOtp2 : 1234 ,
257+ checkbox : true ,
258+ radio : null
259+ } ) ;
260+ } ) ;
261+ } ) ;
262+
263+ test . describe ( 'validators' , ( ) => {
264+ test ( 'ion-input should error with min set' , async ( { page } ) => {
265+ const control = page . locator ( 'form ion-input[formControlName="inputMin"]' ) ;
266+
267+ await expect ( control ) . toHaveClass ( / n g - v a l i d / ) ;
55268
56- // Control is initially valid
57- await expect ( control ) . toHaveClass ( / n g - v a l i d / ) ;
269+ await control . locator ( 'input' ) . fill ( '0' ) ;
270+ await control . locator ( 'input' ) . blur ( ) ;
58271
59- await control . locator ( 'input' ) . fill ( '2' ) ;
60- await control . locator ( 'input' ) . blur ( ) ;
272+ await expect ( control ) . toHaveClass ( / n g - i n v a l i d / ) ;
273+ } ) ;
61274
62- await expect ( control ) . toHaveClass ( / n g - i n v a l i d / ) ;
275+ test ( 'ion-input should error with max set' , async ( { page } ) => {
276+ const control = page . locator ( 'form ion-input[formControlName="inputMax"]' ) ;
277+
278+ await expect ( control ) . toHaveClass ( / n g - v a l i d / ) ;
279+
280+ await control . locator ( 'input' ) . fill ( '2' ) ;
281+ await control . locator ( 'input' ) . blur ( ) ;
282+
283+ await expect ( control ) . toHaveClass ( / n g - i n v a l i d / ) ;
284+ } ) ;
63285 } ) ;
286+
287+ // Helper functions
288+ async function testStatus ( page : any , status : string ) {
289+ await expect ( page . locator ( '#status' ) ) . toHaveText ( status ) ;
290+ }
291+
292+ async function testData ( page : any , data : any ) {
293+ const text = await page . locator ( '#data' ) . textContent ( ) ;
294+ const value = JSON . parse ( text ! ) ;
295+ expect ( value ) . toEqual ( data ) ;
296+ }
64297} ) ;
0 commit comments