Skip to content

Commit d147f89

Browse files
committed
test(select): add e2e test for bottom content
1 parent 5acf8ab commit d147f89

File tree

2 files changed

+327
-0
lines changed

2 files changed

+327
-0
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<!DOCTYPE html>
2+
<html lang="en" dir="ltr">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>Select - Bottom Content</title>
6+
<meta
7+
name="viewport"
8+
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
9+
/>
10+
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
11+
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
12+
<script src="../../../../../scripts/testing/scripts.js"></script>
13+
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
14+
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
15+
<style>
16+
.grid {
17+
display: grid;
18+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
19+
grid-row-gap: 20px;
20+
grid-column-gap: 20px;
21+
}
22+
23+
h2 {
24+
font-size: 12px;
25+
font-weight: normal;
26+
27+
color: #6f7378;
28+
29+
margin-top: 10px;
30+
}
31+
32+
ion-select.custom-error-color {
33+
--highlight-color-invalid: purple;
34+
}
35+
</style>
36+
</head>
37+
38+
<body>
39+
<ion-app>
40+
<ion-header>
41+
<ion-toolbar>
42+
<ion-title>Select - Bottom Content</ion-title>
43+
</ion-toolbar>
44+
</ion-header>
45+
46+
<ion-content id="content" class="ion-padding">
47+
<div class="grid">
48+
<div class="grid-item">
49+
<h2>No Hint</h2>
50+
<ion-select label="Label">
51+
<ion-select-option>Option</ion-select-option>
52+
</ion-select>
53+
</div>
54+
55+
<div class="grid-item">
56+
<h2>No Hint: Stacked</h2>
57+
<ion-select label="Label" label-placement="stacked">
58+
<ion-select-option>Option</ion-select-option>
59+
</ion-select>
60+
</div>
61+
62+
<div class="grid-item">
63+
<h2>Helper Text</h2>
64+
<ion-select label="Label" helper-text="Helper text">
65+
<ion-select-option>Option</ion-select-option>
66+
</ion-select>
67+
</div>
68+
69+
<div class="grid-item">
70+
<h2>Helper Text: Stacked</h2>
71+
<ion-select label="Label" label-placement="stacked" helper-text="Helper text"
72+
>Label
73+
<ion-select-option>Option</ion-select-option>
74+
</ion-select>
75+
</div>
76+
77+
<div class="grid-item">
78+
<h2>Error Text</h2>
79+
<ion-select class="ion-touched ion-invalid" label="Label" error-text="Error text">
80+
<ion-select-option>Option</ion-select-option></ion-select
81+
>
82+
</div>
83+
84+
<div class="grid-item">
85+
<h2>Error Text: Stacked</h2>
86+
<ion-select class="ion-touched ion-invalid" label="Label" label-placement="stacked" error-text="Error text">
87+
<ion-select-option>Option</ion-select-option></ion-select
88+
>
89+
</div>
90+
91+
<div class="grid-item">
92+
<h2>Error Text: Custom Color</h2>
93+
<ion-select class="ion-touched ion-invalid custom-error-color" label="Label" error-text="Error text">
94+
<ion-select-option>Option</ion-select-option></ion-select
95+
>
96+
</div>
97+
98+
<div class="grid-item">
99+
<h2>Helper Text: Wrapping</h2>
100+
<ion-select
101+
label="Label"
102+
helper-text="Helper text helper text helper text helper text helper text helper text helper text helper text helper text"
103+
>
104+
<ion-select-option>Option</ion-select-option>
105+
</ion-select>
106+
</div>
107+
</div>
108+
109+
<button class="expand" onclick="toggleFill()">Toggle Fill</button>
110+
</ion-content>
111+
</ion-app>
112+
113+
<script>
114+
const selects = document.querySelectorAll('ion-select');
115+
116+
function toggleFill() {
117+
selects.forEach((select) => {
118+
switch (select.fill) {
119+
case 'outline':
120+
select.fill = 'solid';
121+
break;
122+
case 'solid':
123+
select.fill = undefined;
124+
break;
125+
default:
126+
select.fill = 'outline';
127+
}
128+
});
129+
}
130+
</script>
131+
</body>
132+
</html>
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
import { expect } from '@playwright/test';
2+
import { configs, test } from '@utils/test/playwright';
3+
4+
/**
5+
* Functionality is the same across modes & directions
6+
*/
7+
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
8+
test.describe(title('select: bottom content functionality'), () => {
9+
test('should not render bottom content if no hint is enabled', async ({ page }) => {
10+
await page.setContent(`<ion-select label="Label"></ion-select>`, config);
11+
12+
const bottomEl = page.locator('ion-select .select-bottom');
13+
await expect(bottomEl).toHaveCount(0);
14+
});
15+
test('helper text should be visible initially', async ({ page }) => {
16+
await page.setContent(
17+
`<ion-select label="Label" helper-text="Helper text" error-text="Error text"></ion-select>`,
18+
config
19+
);
20+
21+
const helperText = page.locator('ion-select .helper-text');
22+
const errorText = page.locator('ion-select .error-text');
23+
await expect(helperText).toBeVisible();
24+
await expect(helperText).toHaveText('Helper text');
25+
await expect(errorText).toBeHidden();
26+
});
27+
test('input should have an aria-describedby attribute when helper text is present', async ({ page }) => {
28+
await page.setContent(
29+
`<ion-select label="Label" helper-text="Helper text" error-text="Error text"></ion-select>`,
30+
config
31+
);
32+
33+
const input = page.locator('ion-select button');
34+
const helperText = page.locator('ion-select .helper-text');
35+
const helperTextId = await helperText.getAttribute('id');
36+
const ariaDescribedBy = await input.getAttribute('aria-describedby');
37+
38+
expect(ariaDescribedBy).toBe(helperTextId);
39+
});
40+
test('error text should be visible when select is invalid', async ({ page }) => {
41+
await page.setContent(
42+
`<ion-select label="Label" class="ion-invalid ion-touched" helper-text="Helper text" error-text="Error text"></ion-select>`,
43+
config
44+
);
45+
46+
const helperText = page.locator('ion-select .helper-text');
47+
const errorText = page.locator('ion-select .error-text');
48+
await expect(helperText).toBeHidden();
49+
await expect(errorText).toBeVisible();
50+
await expect(errorText).toHaveText('Error text');
51+
});
52+
53+
test('input should have an aria-describedby attribute when error text is present', async ({ page }) => {
54+
await page.setContent(
55+
`<ion-select label="Label" class="ion-invalid ion-touched" helper-text="Helper text" error-text="Error text"></ion-select>`,
56+
config
57+
);
58+
59+
const input = page.locator('ion-select button');
60+
const errorText = page.locator('ion-select .error-text');
61+
const errorTextId = await errorText.getAttribute('id');
62+
const ariaDescribedBy = await input.getAttribute('aria-describedby');
63+
64+
expect(ariaDescribedBy).toBe(errorTextId);
65+
});
66+
test('input should have aria-invalid attribute when input is invalid', async ({ page }) => {
67+
await page.setContent(
68+
`<ion-select label="Label" class="ion-invalid ion-touched" helper-text="Helper text" error-text="Error text"></ion-select>`,
69+
config
70+
);
71+
72+
const input = page.locator('ion-select button');
73+
74+
await expect(input).toHaveAttribute('aria-invalid');
75+
});
76+
test('input should not have aria-invalid attribute when input is valid', async ({ page }) => {
77+
await page.setContent(
78+
`<ion-select label="Label" helper-text="Helper text" error-text="Error text"></ion-select>`,
79+
config
80+
);
81+
82+
const input = page.locator('ion-select button');
83+
84+
await expect(input).not.toHaveAttribute('aria-invalid');
85+
});
86+
test('input should not have aria-describedby attribute when no hint or error text is present', async ({ page }) => {
87+
await page.setContent(`<ion-select label="Label"></ion-select>`, config);
88+
89+
const input = page.locator('ion-select button');
90+
91+
await expect(input).not.toHaveAttribute('aria-describedby');
92+
});
93+
});
94+
});
95+
96+
/**
97+
* Rendering is different across modes
98+
*/
99+
configs({ modes: ['ios', 'md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
100+
test.describe(title('select: helper text rendering'), () => {
101+
test('should not have visual regressions when rendering helper text', async ({ page }) => {
102+
await page.setContent(`<ion-select label="Label" helper-text="Helper text"></ion-select>`, config);
103+
104+
const bottomEl = page.locator('ion-select');
105+
await expect(bottomEl).toHaveScreenshot(screenshot(`select-helper-text`));
106+
});
107+
test('should not have visual regressions when rendering helper text with wrapping text', async ({ page }) => {
108+
await page.setContent(
109+
`<ion-select label="Label" helper-text="Helper text helper text helper text helper text helper text helper text helper text helper text helper text"></ion-select>`,
110+
config
111+
);
112+
113+
const bottomEl = page.locator('ion-select');
114+
await expect(bottomEl).toHaveScreenshot(screenshot(`select-helper-text-wrapping`));
115+
});
116+
test('should not have visual regressions when rendering helper text with a stacked label', async ({ page }) => {
117+
await page.setContent(
118+
`<ion-select label="Label" label-placement="stacked" helper-text="Helper text"></ion-select>`,
119+
config
120+
);
121+
122+
const bottomEl = page.locator('ion-select');
123+
await expect(bottomEl).toHaveScreenshot(screenshot(`select-helper-text-stacked-label`));
124+
});
125+
});
126+
127+
test.describe(title('select: error text rendering'), () => {
128+
test('should not have visual regressions when rendering error text', async ({ page }) => {
129+
await page.setContent(
130+
`<ion-select label="Label" class="ion-invalid ion-touched" error-text="Error text"></ion-select>`,
131+
config
132+
);
133+
134+
const bottomEl = page.locator('ion-select');
135+
await expect(bottomEl).toHaveScreenshot(screenshot(`select-error-text`));
136+
});
137+
test('should not have visual regressions when rendering error text with a stacked label', async ({ page }) => {
138+
await page.setContent(
139+
`<ion-select label="Label" class="ion-invalid ion-touched" error-text="Error text" label-placement="stacked"></ion-select>`,
140+
config
141+
);
142+
143+
const bottomEl = page.locator('ion-select');
144+
await expect(bottomEl).toHaveScreenshot(screenshot(`select-error-text-stacked-label`));
145+
});
146+
});
147+
});
148+
149+
/**
150+
* Customizing supporting text is the same across modes and directions
151+
*/
152+
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
153+
test.describe(title('select: supporting text customization'), () => {
154+
test('should not have visual regressions when rendering helper text with a custom color', async ({ page }) => {
155+
await page.setContent(
156+
`
157+
<style>
158+
ion-select.custom-select::part(supporting-text) {
159+
font-size: 20px;
160+
}
161+
162+
ion-select.custom-select::part(helper-text) {
163+
color: green;
164+
}
165+
</style>
166+
<ion-select label="Label" class="custom-select" helper-text="Helper text"></ion-select>
167+
`,
168+
config
169+
);
170+
171+
const errorText = page.locator('ion-select');
172+
await expect(errorText).toHaveScreenshot(screenshot(`select-helper-text-custom`));
173+
});
174+
test('should not have visual regressions when rendering error text with a custom color', async ({ page }) => {
175+
await page.setContent(
176+
`
177+
<style>
178+
ion-select.custom-select::part(supporting-text) {
179+
font-size: 20px;
180+
}
181+
182+
ion-select.custom-select::part(error-text) {
183+
color: purple;
184+
}
185+
</style>
186+
<ion-select label="Label" class="ion-invalid ion-touched custom-select" error-text="Error text"></ion-select>
187+
`,
188+
config
189+
);
190+
191+
const errorText = page.locator('ion-select');
192+
await expect(errorText).toHaveScreenshot(screenshot(`select-error-text-custom`));
193+
});
194+
});
195+
});

0 commit comments

Comments
 (0)