mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-10 00:27:41 +08:00
Merge remote-tracking branch 'origin/main' into chore/sync-with-main-5-3
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import { expect } from '@playwright/test';
|
||||
import type { E2EPage } from '@utils/test/playwright';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
const testAria = async (
|
||||
page: E2EPage,
|
||||
@ -28,36 +28,36 @@ const testAria = async (
|
||||
expect(ariaDescribedBy).toBe(expectedAriaDescribedBy);
|
||||
};
|
||||
|
||||
test.describe('alert: a11y', () => {
|
||||
test.beforeEach(async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
await page.goto(`/src/components/alert/test/a11y`);
|
||||
});
|
||||
|
||||
test('should not have accessibility violations when header and message are defined', async ({ page }) => {
|
||||
const didPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const button = page.locator('#bothHeaders');
|
||||
await button.click();
|
||||
|
||||
await didPresent.next();
|
||||
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
|
||||
test('should have aria-labelledby when header is set', async ({ page }) => {
|
||||
await testAria(page, 'noMessage', 'alert-1-hdr', null);
|
||||
});
|
||||
|
||||
test('should have aria-describedby when message is set', async ({ page }) => {
|
||||
await testAria(page, 'noHeaders', null, 'alert-1-msg');
|
||||
});
|
||||
|
||||
test('should fall back to subHeader for aria-labelledby if header is not defined', async ({ page }) => {
|
||||
await testAria(page, 'subHeaderOnly', 'alert-1-sub-hdr', 'alert-1-msg');
|
||||
});
|
||||
|
||||
test('should allow for manually specifying aria attributes', async ({ page }) => {
|
||||
await testAria(page, 'customAria', 'Custom title', 'Custom description');
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('alert: a11y'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(`/src/components/alert/test/a11y`, config);
|
||||
});
|
||||
test('should not have accessibility violations when header and message are defined', async ({ page }) => {
|
||||
const didPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const button = page.locator('#bothHeaders');
|
||||
await button.click();
|
||||
|
||||
await didPresent.next();
|
||||
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
|
||||
test('should have aria-labelledby when header is set', async ({ page }) => {
|
||||
await testAria(page, 'noMessage', 'alert-1-hdr', null);
|
||||
});
|
||||
|
||||
test('should have aria-describedby when message is set', async ({ page }) => {
|
||||
await testAria(page, 'noHeaders', null, 'alert-1-msg');
|
||||
});
|
||||
|
||||
test('should fall back to subHeader for aria-labelledby if header is not defined', async ({ page }) => {
|
||||
await testAria(page, 'subHeaderOnly', 'alert-1-sub-hdr', 'alert-1-msg');
|
||||
});
|
||||
|
||||
test('should allow for manually specifying aria attributes', async ({ page }) => {
|
||||
await testAria(page, 'customAria', 'Custom title', 'Custom description');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,113 +1,147 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import type { Locator } from '@playwright/test';
|
||||
import type { E2EPage } from '@utils/test/playwright';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
const openAlert = async (page: E2EPage, buttonID: string) => {
|
||||
const didPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
|
||||
await page.click(`#${buttonID}`);
|
||||
await didPresent.next();
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('alert: basic'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/alert/test/basic', config);
|
||||
});
|
||||
test('focus trap should work correctly', async ({ page, browserName }) => {
|
||||
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
|
||||
|
||||
return page.locator('ion-alert');
|
||||
};
|
||||
const alertFixture = new AlertFixture(page, screenshot);
|
||||
|
||||
const testAlert = async (page: E2EPage, buttonID: string) => {
|
||||
await page.goto(`/src/components/alert/test/basic`);
|
||||
const alert = await alertFixture.open('#multipleButtons');
|
||||
const alertBtns = alert.locator('button');
|
||||
|
||||
const didDismiss = await page.spyOnEvent('ionAlertDidDismiss');
|
||||
const alert = await openAlert(page, buttonID);
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(alertBtns.nth(0)).toBeFocused();
|
||||
|
||||
await expect(alert).toBeVisible();
|
||||
await expect(alert).toHaveScreenshot(`alert-${buttonID}-${page.getSnapshotSettings()}.png`);
|
||||
await page.keyboard.press(`Shift+${tabKey}`);
|
||||
await expect(alertBtns.nth(2)).toBeFocused();
|
||||
|
||||
await alert.evaluate((el: HTMLIonAlertElement) => el.dismiss());
|
||||
await didDismiss.next();
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(alertBtns.nth(0)).toBeFocused();
|
||||
});
|
||||
|
||||
await expect(alert).toHaveCount(0);
|
||||
};
|
||||
test('should set custom attributes', async ({ page }) => {
|
||||
const alertFixture = new AlertFixture(page, screenshot);
|
||||
|
||||
test.describe('alert: basic', () => {
|
||||
test('focus trap should work correctly', async ({ page, skip, browserName }) => {
|
||||
skip.rtl();
|
||||
await page.goto(`/src/components/alert/test/basic`);
|
||||
const alert = await alertFixture.open('#basic');
|
||||
await expect(alert).toHaveAttribute('data-testid', 'basic-alert');
|
||||
});
|
||||
|
||||
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
|
||||
test('should dismiss when async handler resolves', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const ionAlertDidDismiss = await page.spyOnEvent('ionAlertDidDismiss');
|
||||
const ionLoadingDidDismiss = await page.spyOnEvent('ionLoadingDidDismiss');
|
||||
|
||||
const alert = await openAlert(page, 'multipleButtons');
|
||||
const alertBtns = alert.locator('button');
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(alertBtns.nth(0)).toBeFocused();
|
||||
await page.click('#asyncHandler');
|
||||
await ionAlertDidPresent.next();
|
||||
|
||||
await page.keyboard.press(`Shift+${tabKey}`);
|
||||
await expect(alertBtns.nth(2)).toBeFocused();
|
||||
await page.click('.alert-button');
|
||||
|
||||
await page.keyboard.press(tabKey);
|
||||
await expect(alertBtns.nth(0)).toBeFocused();
|
||||
});
|
||||
await expect(alert).toBeVisible();
|
||||
|
||||
test('should set custom attributes', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
await page.goto(`/src/components/alert/test/basic`);
|
||||
await ionLoadingDidDismiss.next();
|
||||
await ionAlertDidDismiss.next();
|
||||
|
||||
const alert = await openAlert(page, 'basic');
|
||||
await expect(alert).toHaveAttribute('data-testid', 'basic-alert');
|
||||
await expect(alert).toBeHidden();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('should dismiss when async handler resolves', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
await page.goto(`/src/components/alert/test/basic`);
|
||||
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const ionAlertDidDismiss = await page.spyOnEvent('ionAlertDidDismiss');
|
||||
const ionLoadingDidDismiss = await page.spyOnEvent('ionLoadingDidDismiss');
|
||||
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
await page.click('#asyncHandler');
|
||||
await ionAlertDidPresent.next();
|
||||
|
||||
await page.click('.alert-button');
|
||||
|
||||
await expect(alert).toBeVisible();
|
||||
|
||||
await ionLoadingDidDismiss.next();
|
||||
await ionAlertDidDismiss.next();
|
||||
|
||||
await expect(alert).toBeHidden();
|
||||
});
|
||||
configs().forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('should not have visual regressions'), () => {
|
||||
let alertFixture!: AlertFixture;
|
||||
|
||||
test.describe('should not have visual regressions', () => {
|
||||
test('header, subheader, message', async ({ page }) => {
|
||||
await testAlert(page, 'basic');
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/alert/test/basic', config);
|
||||
alertFixture = new AlertFixture(page, screenshot);
|
||||
});
|
||||
test('header, subheader, message', async () => {
|
||||
await alertFixture.open('#basic');
|
||||
await alertFixture.screenshot('basic');
|
||||
});
|
||||
|
||||
test('long message', async ({ page }) => {
|
||||
await testAlert(page, 'longMessage');
|
||||
test('long message', async () => {
|
||||
await alertFixture.open('#longMessage');
|
||||
await alertFixture.screenshot('longMessage');
|
||||
});
|
||||
|
||||
test('more than two buttons', async ({ page }) => {
|
||||
await testAlert(page, 'multipleButtons');
|
||||
test('more than two buttons', async () => {
|
||||
await alertFixture.open('#multipleButtons');
|
||||
await alertFixture.screenshot('multipleButtons');
|
||||
});
|
||||
|
||||
test('no message', async ({ page }) => {
|
||||
await testAlert(page, 'noMessage');
|
||||
test('no message', async () => {
|
||||
await alertFixture.open('#noMessage');
|
||||
await alertFixture.screenshot('noMessage');
|
||||
});
|
||||
|
||||
test('two buttons', async ({ page }) => {
|
||||
await testAlert(page, 'confirm');
|
||||
test('two buttons', async () => {
|
||||
await alertFixture.open('#confirm');
|
||||
await alertFixture.screenshot('confirm');
|
||||
});
|
||||
|
||||
test('form prompt', async ({ page }) => {
|
||||
await testAlert(page, 'prompt');
|
||||
test('form prompt', async () => {
|
||||
await alertFixture.open('#prompt');
|
||||
await alertFixture.screenshot('prompt');
|
||||
});
|
||||
|
||||
test('radios', async ({ page }) => {
|
||||
await testAlert(page, 'radio');
|
||||
test('radios', async () => {
|
||||
await alertFixture.open('#radio');
|
||||
await alertFixture.screenshot('radio');
|
||||
});
|
||||
|
||||
test('checkboxes', async ({ page }) => {
|
||||
await testAlert(page, 'checkbox');
|
||||
test('checkboxes', async () => {
|
||||
await alertFixture.open('#checkbox');
|
||||
await alertFixture.screenshot('checkbox');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class AlertFixture {
|
||||
readonly page: E2EPage;
|
||||
readonly screenshotFn?: (file: string) => string;
|
||||
|
||||
private alert!: Locator;
|
||||
|
||||
constructor(page: E2EPage, screenshot?: (file: string) => string) {
|
||||
this.page = page;
|
||||
this.screenshotFn = screenshot;
|
||||
}
|
||||
|
||||
async open(selector: string) {
|
||||
const ionAlertDidPresent = await this.page.spyOnEvent('ionAlertDidPresent');
|
||||
await this.page.locator(selector).click();
|
||||
await ionAlertDidPresent.next();
|
||||
this.alert = this.page.locator('ion-alert');
|
||||
await expect(this.alert).toBeVisible();
|
||||
|
||||
return this.alert;
|
||||
}
|
||||
|
||||
async dismiss() {
|
||||
const ionAlertDidDismiss = await this.page.spyOnEvent('ionAlertDidDismiss');
|
||||
await this.alert.evaluate((el: HTMLIonAlertElement) => el.dismiss());
|
||||
await ionAlertDidDismiss.next();
|
||||
await expect(this.alert).not.toBeVisible();
|
||||
}
|
||||
|
||||
async screenshot(modifier: string) {
|
||||
const { screenshotFn } = this;
|
||||
|
||||
if (!screenshotFn) {
|
||||
throw new Error(
|
||||
'A screenshot function is required to take a screenshot. Pass one in when creating ActionSheetFixture.'
|
||||
);
|
||||
}
|
||||
|
||||
await expect(this.alert).toHaveScreenshot(screenshotFn(`alert-${modifier}`));
|
||||
}
|
||||
}
|
||||
|
||||
33
core/src/components/alert/test/is-open/alert.e2e.ts
Normal file
33
core/src/components/alert/test/is-open/alert.e2e.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('alert: isOpen'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/alert/test/is-open', config);
|
||||
});
|
||||
test('should open the alert', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
await page.click('#default');
|
||||
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
});
|
||||
|
||||
test('should open the alert then close after a timeout', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const ionAlertDidDismiss = await page.spyOnEvent('ionAlertDidDismiss');
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
await page.click('#timeout');
|
||||
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
|
||||
await ionAlertDidDismiss.next();
|
||||
await expect(alert).toBeHidden();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,34 +0,0 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('alert: isOpen', () => {
|
||||
test.beforeEach(async ({ page, skip }) => {
|
||||
skip.rtl('isOpen does not behave differently in RTL');
|
||||
skip.mode('md', 'isOpen does not behave differently in MD');
|
||||
await page.goto('/src/components/alert/test/isOpen');
|
||||
});
|
||||
|
||||
test('should open the alert', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
await page.click('#default');
|
||||
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
});
|
||||
|
||||
test('should open the alert then close after a timeout', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const ionAlertDidDismiss = await page.spyOnEvent('ionAlertDidDismiss');
|
||||
const alert = page.locator('ion-alert');
|
||||
|
||||
await page.click('#timeout');
|
||||
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
|
||||
await ionAlertDidDismiss.next();
|
||||
await expect(alert).toBeHidden();
|
||||
});
|
||||
});
|
||||
@ -1,16 +1,18 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('alert: standalone', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/alert/test/standalone`);
|
||||
configs().forEach(({ config, screenshot, title }) => {
|
||||
test.describe(title('alert: standalone'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/alert/test/standalone`, config);
|
||||
|
||||
const alert = page.locator('ion-alert');
|
||||
const didPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const alert = page.locator('ion-alert');
|
||||
const didPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
|
||||
await page.click('#basic');
|
||||
await didPresent.next();
|
||||
await page.click('#basic');
|
||||
await didPresent.next();
|
||||
|
||||
await expect(alert).toHaveScreenshot(`alert-standalone-${page.getSnapshotSettings()}.png`);
|
||||
await expect(alert).toHaveScreenshot(screenshot('alert-standalone'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,35 +1,34 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('alert: trigger', () => {
|
||||
test.beforeEach(async ({ page, skip }) => {
|
||||
skip.rtl('trigger does not behave differently in RTL');
|
||||
skip.mode('md');
|
||||
await page.goto('/src/components/alert/test/trigger');
|
||||
});
|
||||
|
||||
test('should open the alert', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const alert = page.locator('#default-alert');
|
||||
configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('alert: trigger'), () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/src/components/alert/test/trigger', config);
|
||||
});
|
||||
test('should open the alert', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const alert = page.locator('#default-alert');
|
||||
|
||||
await page.click('#default');
|
||||
await page.click('#default');
|
||||
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
});
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
});
|
||||
|
||||
test('should present a previously presented alert', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const ionAlertDidDismiss = await page.spyOnEvent('ionAlertDidDismiss');
|
||||
const alert = page.locator('#timeout-alert');
|
||||
test('should present a previously presented alert', async ({ page }) => {
|
||||
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
|
||||
const ionAlertDidDismiss = await page.spyOnEvent('ionAlertDidDismiss');
|
||||
const alert = page.locator('#timeout-alert');
|
||||
|
||||
await page.click('#timeout');
|
||||
await page.click('#timeout');
|
||||
|
||||
await ionAlertDidDismiss.next();
|
||||
await ionAlertDidDismiss.next();
|
||||
|
||||
await page.click('#timeout');
|
||||
await page.click('#timeout');
|
||||
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
await ionAlertDidPresent.next();
|
||||
await expect(alert).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user