1+ import type { ConsoleMessage } from '@playwright/test' ;
12import { expect } from '@playwright/test' ;
3+ import type { E2EPage } from '@utils/test/playwright' ;
24import { configs , test } from '@utils/test/playwright' ;
35
6+ const DEFAULT_INPUT_LENGTH = 4 ;
7+ const VALID_SIZES = [ 'small' , 'medium' , 'large' ] ;
8+
9+ /**
10+ * Helper function to check if the next sibling after
11+ * the input box is a separator
12+ */
13+ const hasSeparatorAfter = async ( page : E2EPage , index : number ) : Promise < boolean > => {
14+ const wrappers = page . locator ( '.input-otp-group > .native-wrapper' ) ;
15+ return await wrappers
16+ . nth ( index )
17+ . evaluate ( ( el : Element ) => el . nextElementSibling ?. classList . contains ( 'input-otp-separator' ) ?? false ) ;
18+ } ;
19+
20+ /**
21+ * Helper function to collect console warnings
22+ */
23+ const collectWarnings = async ( page : E2EPage ) : Promise < string [ ] > => {
24+ const warnings : string [ ] = [ ] ;
25+ page . on ( 'console' , ( ev : ConsoleMessage ) => {
26+ if ( ev . type ( ) === 'warning' ) {
27+ warnings . push ( ev . text ( ) ) ;
28+ }
29+ } ) ;
30+ return warnings ;
31+ } ;
32+
433configs ( { directions : [ 'ltr' ] } ) . forEach ( ( { title, screenshot, config } ) => {
534 test . describe ( title ( 'input-otp: separators' ) , ( ) => {
635 // Test separators with all sizes
7- [ 'small' , 'medium' , 'large' ] . forEach ( ( size ) => {
36+ VALID_SIZES . forEach ( ( size ) => {
837 test ( `one separator with ${ size } size should not have visual regressions` , async ( { page } ) => {
938 await page . setContent (
1039 `
@@ -52,17 +81,10 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
5281 test ( 'should render separators after the first and third input box' , async ( { page } ) => {
5382 await page . setContent ( `<ion-input-otp separators="1,3">Description</ion-input-otp>` , config ) ;
5483
55- const group = page . locator ( '.input-otp-group' ) ;
56- const wrappers = group . locator ( '> .native-wrapper' ) ;
57-
58- // Helper to check if the next sibling is a separator
59- const hasSeparatorAfter = async ( index : number ) =>
60- wrappers . nth ( index ) . evaluate ( ( el ) => el . nextElementSibling ?. classList . contains ( 'input-otp-separator' ) ?? false ) ;
61-
62- await expect ( await hasSeparatorAfter ( 0 ) ) . toBe ( true ) ;
63- await expect ( await hasSeparatorAfter ( 1 ) ) . toBe ( false ) ;
64- await expect ( await hasSeparatorAfter ( 2 ) ) . toBe ( true ) ;
65- await expect ( await hasSeparatorAfter ( 3 ) ) . toBe ( false ) ;
84+ await expect ( await hasSeparatorAfter ( page , 0 ) ) . toBe ( true ) ;
85+ await expect ( await hasSeparatorAfter ( page , 1 ) ) . toBe ( false ) ;
86+ await expect ( await hasSeparatorAfter ( page , 2 ) ) . toBe ( true ) ;
87+ await expect ( await hasSeparatorAfter ( page , 3 ) ) . toBe ( false ) ;
6688 } ) ;
6789
6890 test ( 'should render separators after the second and third input box' , async ( { page } ) => {
@@ -73,61 +95,42 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
7395 el . separators = [ 2 , 3 ] ;
7496 } ) ;
7597
76- const group = page . locator ( '.input-otp-group' ) ;
77- const wrappers = group . locator ( '> .native-wrapper' ) ;
78-
79- // Helper to check if the next sibling is a separator
80- const hasSeparatorAfter = async ( index : number ) =>
81- wrappers . nth ( index ) . evaluate ( ( el ) => el . nextElementSibling ?. classList . contains ( 'input-otp-separator' ) ?? false ) ;
82-
83- await expect ( await hasSeparatorAfter ( 0 ) ) . toBe ( false ) ;
84- await expect ( await hasSeparatorAfter ( 1 ) ) . toBe ( true ) ;
85- await expect ( await hasSeparatorAfter ( 2 ) ) . toBe ( true ) ;
86- await expect ( await hasSeparatorAfter ( 3 ) ) . toBe ( false ) ;
98+ await expect ( await hasSeparatorAfter ( page , 0 ) ) . toBe ( false ) ;
99+ await expect ( await hasSeparatorAfter ( page , 1 ) ) . toBe ( true ) ;
100+ await expect ( await hasSeparatorAfter ( page , 2 ) ) . toBe ( true ) ;
101+ await expect ( await hasSeparatorAfter ( page , 3 ) ) . toBe ( false ) ;
87102 } ) ;
88103
89104 test ( 'should render all separators' , async ( { page } ) => {
90105 await page . setContent ( `<ion-input-otp separators="all">Description</ion-input-otp>` , config ) ;
91106
92- const group = page . locator ( '.input-otp-group' ) ;
93- const wrappers = group . locator ( '> .native-wrapper' ) ;
107+ await expect ( await hasSeparatorAfter ( page , 0 ) ) . toBe ( true ) ;
108+ await expect ( await hasSeparatorAfter ( page , 1 ) ) . toBe ( true ) ;
109+ await expect ( await hasSeparatorAfter ( page , 2 ) ) . toBe ( true ) ;
110+ await expect ( await hasSeparatorAfter ( page , 3 ) ) . toBe ( false ) ;
111+ } ) ;
94112
95- // Helper to check if the next sibling is a separator
96- const hasSeparatorAfter = async ( index : number ) =>
97- wrappers . nth ( index ) . evaluate ( ( el ) => el . nextElementSibling ?. classList . contains ( 'input-otp-separator' ) ?? false ) ;
113+ test ( 'should handle empty separators string' , async ( { page } ) => {
114+ await page . setContent ( `<ion-input-otp separators="">Description</ion-input-otp>` , config ) ;
98115
99- await expect ( await hasSeparatorAfter ( 0 ) ) . toBe ( true ) ;
100- await expect ( await hasSeparatorAfter ( 1 ) ) . toBe ( true ) ;
101- await expect ( await hasSeparatorAfter ( 2 ) ) . toBe ( true ) ;
102- await expect ( await hasSeparatorAfter ( 3 ) ) . toBe ( false ) ;
116+ await expect ( await hasSeparatorAfter ( page , 0 ) ) . toBe ( false ) ;
117+ await expect ( await hasSeparatorAfter ( page , 1 ) ) . toBe ( false ) ;
118+ await expect ( await hasSeparatorAfter ( page , 2 ) ) . toBe ( false ) ;
119+ await expect ( await hasSeparatorAfter ( page , 3 ) ) . toBe ( false ) ;
103120 } ) ;
104121
105122 test ( 'should warn when setting separators to a position greater than the input length' , async ( { page } ) => {
106- const warnings : string [ ] = [ ] ;
107-
108- page . on ( 'console' , ( ev ) => {
109- if ( ev . type ( ) === 'warning' ) {
110- warnings . push ( ev . text ( ) ) ;
111- }
112- } ) ;
113-
123+ const warnings = await collectWarnings ( page ) ;
114124 await page . setContent ( `<ion-input-otp separators="1,3,5,6,7">Description</ion-input-otp>` , config ) ;
115125
116126 expect ( warnings . length ) . toBe ( 1 ) ;
117127 expect ( warnings [ 0 ] ) . toContain (
118- ' [Ionic Warning]: [ion-input-otp] - The following separator positions are greater than the input length (4 ): 5, 6, 7. These separators will be ignored.'
128+ ` [Ionic Warning]: [ion-input-otp] - The following separator positions are greater than the input length (${ DEFAULT_INPUT_LENGTH } ): 5, 6, 7. These separators will be ignored.`
119129 ) ;
120130 } ) ;
121131
122132 test ( 'should warn when setting separators to an invalid space-separated string' , async ( { page } ) => {
123- const warnings : string [ ] = [ ] ;
124-
125- page . on ( 'console' , ( ev ) => {
126- if ( ev . type ( ) === 'warning' ) {
127- warnings . push ( ev . text ( ) ) ;
128- }
129- } ) ;
130-
133+ const warnings = await collectWarnings ( page ) ;
131134 const invalidSeparators = '1 2 3' ;
132135
133136 await page . setContent ( `<ion-input-otp separators="${ invalidSeparators } ">Description</ion-input-otp>` , config ) ;
@@ -139,15 +142,20 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
139142 } ) ;
140143
141144 test ( 'should warn when setting separators to an invalid comma-separated string' , async ( { page } ) => {
142- const warnings : string [ ] = [ ] ;
145+ const warnings = await collectWarnings ( page ) ;
146+ const invalidSeparators = '1,d,3' ;
143147
144- page . on ( 'console' , ( ev ) => {
145- if ( ev . type ( ) === 'warning' ) {
146- warnings . push ( ev . text ( ) ) ;
147- }
148- } ) ;
148+ await page . setContent ( `<ion-input-otp separators="${ invalidSeparators } ">Description</ion-input-otp>` , config ) ;
149149
150- const invalidSeparators = '1,d,3' ;
150+ expect ( warnings . length ) . toBe ( 1 ) ;
151+ expect ( warnings [ 0 ] ) . toContain (
152+ `[Ionic Warning]: [ion-input-otp] - Invalid separators format. Expected a comma-separated list of numbers, an array of numbers, or "all". Received: ${ invalidSeparators } `
153+ ) ;
154+ } ) ;
155+
156+ test ( 'should warn when setting separators to negative numbers' , async ( { page } ) => {
157+ const warnings = await collectWarnings ( page ) ;
158+ const invalidSeparators = '-1,2,3' ;
151159
152160 await page . setContent ( `<ion-input-otp separators="${ invalidSeparators } ">Description</ion-input-otp>` , config ) ;
153161
@@ -156,5 +164,17 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
156164 `[Ionic Warning]: [ion-input-otp] - Invalid separators format. Expected a comma-separated list of numbers, an array of numbers, or "all". Received: ${ invalidSeparators } `
157165 ) ;
158166 } ) ;
167+
168+ test ( 'should warn when setting separators to duplicate positions' , async ( { page } ) => {
169+ const warnings = await collectWarnings ( page ) ;
170+ const invalidSeparators = '1,1,2' ;
171+
172+ await page . setContent ( `<ion-input-otp separators="${ invalidSeparators } ">Description</ion-input-otp>` , config ) ;
173+
174+ expect ( warnings . length ) . toBe ( 1 ) ;
175+ expect ( warnings [ 0 ] ) . toContain (
176+ `[Ionic Warning]: [ion-input-otp] - Duplicate separator positions are not allowed. Received: ${ invalidSeparators } `
177+ ) ;
178+ } ) ;
159179 } ) ;
160180} ) ;
0 commit comments