test(action-sheet): improve test performance (#27336)
Issue number: N/A --------- <!-- Please refer to our contributing documentation for any questions on submitting a pull request, or let us know here if you need any help: https://ionicframework.com/docs/building/contributing --> <!-- Some docs updates need to be made in the `ionic-docs` repo, in a separate PR. See https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#modifying-documentation for details. --> <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> I noticed some test slowdowns with the introduction of action sheet tests, so I wanted to try and find a way to speed these tests up. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> - htmlAttributes test was ported to a spec test - the functionality/behavioral tests were being run per-mode when they did not need to be, so I removed the per-mode checks - Moved screenshot tests to a separate file so they can be better paralellelized ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. -->
@ -0,0 +1,42 @@
|
|||||||
|
import { configs, test } from '@utils/test/playwright';
|
||||||
|
|
||||||
|
import { ActionSheetFixture } from './fixture';
|
||||||
|
|
||||||
|
configs().forEach(({ config, screenshot, title }) => {
|
||||||
|
test.describe(title('action sheet: variant rendering'), () => {
|
||||||
|
let actionSheetFixture!: ActionSheetFixture;
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
actionSheetFixture = new ActionSheetFixture(page, screenshot);
|
||||||
|
|
||||||
|
await page.goto(`/src/components/action-sheet/test/basic`, config);
|
||||||
|
});
|
||||||
|
test('should open basic action sheet', async () => {
|
||||||
|
await actionSheetFixture.open('#basic');
|
||||||
|
await actionSheetFixture.screenshot('basic');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We want to test that the dismiss method
|
||||||
|
* actually works, but we do not need to test
|
||||||
|
* it every time. As a result, we only
|
||||||
|
* call dismiss in this test.
|
||||||
|
*/
|
||||||
|
await actionSheetFixture.dismiss();
|
||||||
|
});
|
||||||
|
test('should open cancel only action sheet', async () => {
|
||||||
|
await actionSheetFixture.open('#cancelOnly');
|
||||||
|
await actionSheetFixture.screenshot('cancel-only');
|
||||||
|
});
|
||||||
|
test('should open custom action sheet', async () => {
|
||||||
|
await actionSheetFixture.open('#custom');
|
||||||
|
await actionSheetFixture.screenshot('custom');
|
||||||
|
});
|
||||||
|
test('should open scrollable action sheet', async () => {
|
||||||
|
await actionSheetFixture.open('#scrollableOptions');
|
||||||
|
await actionSheetFixture.screenshot('scrollable-options');
|
||||||
|
});
|
||||||
|
test('should open scrollable action sheet without cancel', async () => {
|
||||||
|
await actionSheetFixture.open('#scrollWithoutCancel');
|
||||||
|
await actionSheetFixture.screenshot('scroll-without-cancel');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@ -1,9 +1,12 @@
|
|||||||
import { expect } from '@playwright/test';
|
import { expect } from '@playwright/test';
|
||||||
import type { Locator } from '@playwright/test';
|
|
||||||
import { configs, test } from '@utils/test/playwright';
|
import { configs, test } from '@utils/test/playwright';
|
||||||
import type { E2EPage } from '@utils/test/playwright';
|
|
||||||
|
|
||||||
configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
import { ActionSheetFixture } from './fixture';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This behavior does not vary across modes/directions
|
||||||
|
*/
|
||||||
|
configs({ mode: ['ios'], directions: ['ltr'] }).forEach(({ config, title }) => {
|
||||||
test.describe(title('action sheet: data'), () => {
|
test.describe(title('action sheet: data'), () => {
|
||||||
let actionSheetFixture!: ActionSheetFixture;
|
let actionSheetFixture!: ActionSheetFixture;
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
@ -35,58 +38,11 @@ configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
|
||||||
test.describe(title('action sheet: attributes'), () => {
|
|
||||||
test('should set htmlAttributes', async ({ page }) => {
|
|
||||||
await page.goto(`/src/components/action-sheet/test/basic`, config);
|
|
||||||
const actionSheetFixture = new ActionSheetFixture(page);
|
|
||||||
|
|
||||||
await actionSheetFixture.open('#basic');
|
/**
|
||||||
|
* This behavior does not vary across modes/directions
|
||||||
const actionSheet = page.locator('ion-action-sheet');
|
*/
|
||||||
await expect(actionSheet).toHaveAttribute('data-testid', 'basic-action-sheet');
|
configs({ mode: ['ios'], directions: ['ltr'] }).forEach(({ config, title }) => {
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
configs().forEach(({ config, screenshot, title }) => {
|
|
||||||
test.describe(title('action sheet: variant rendering'), () => {
|
|
||||||
let actionSheetFixture!: ActionSheetFixture;
|
|
||||||
test.beforeEach(async ({ page }) => {
|
|
||||||
actionSheetFixture = new ActionSheetFixture(page, screenshot);
|
|
||||||
|
|
||||||
await page.goto(`/src/components/action-sheet/test/basic`, config);
|
|
||||||
});
|
|
||||||
test('should open basic action sheet', async () => {
|
|
||||||
await actionSheetFixture.open('#basic');
|
|
||||||
await actionSheetFixture.screenshot('basic');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We want to test that the dismiss method
|
|
||||||
* actually works, but we do not need to test
|
|
||||||
* it every time. As a result, we only
|
|
||||||
* call dismiss in this test.
|
|
||||||
*/
|
|
||||||
await actionSheetFixture.dismiss();
|
|
||||||
});
|
|
||||||
test('should open cancel only action sheet', async () => {
|
|
||||||
await actionSheetFixture.open('#cancelOnly');
|
|
||||||
await actionSheetFixture.screenshot('cancel-only');
|
|
||||||
});
|
|
||||||
test('should open custom action sheet', async () => {
|
|
||||||
await actionSheetFixture.open('#custom');
|
|
||||||
await actionSheetFixture.screenshot('custom');
|
|
||||||
});
|
|
||||||
test('should open scrollable action sheet', async () => {
|
|
||||||
await actionSheetFixture.open('#scrollableOptions');
|
|
||||||
await actionSheetFixture.screenshot('scrollable-options');
|
|
||||||
});
|
|
||||||
test('should open scrollable action sheet without cancel', async () => {
|
|
||||||
await actionSheetFixture.open('#scrollWithoutCancel');
|
|
||||||
await actionSheetFixture.screenshot('scroll-without-cancel');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
|
||||||
test.describe(title('action sheet: variant functionality'), () => {
|
test.describe(title('action sheet: variant functionality'), () => {
|
||||||
let actionSheetFixture!: ActionSheetFixture;
|
let actionSheetFixture!: ActionSheetFixture;
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
@ -118,7 +74,11 @@ configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
|
||||||
|
/**
|
||||||
|
* This behavior does not vary across modes/directions
|
||||||
|
*/
|
||||||
|
configs({ mode: ['ios'], directions: ['ltr'] }).forEach(({ config, title }) => {
|
||||||
test.describe(title('action sheet: focus trap'), () => {
|
test.describe(title('action sheet: focus trap'), () => {
|
||||||
test('it should trap focus in action sheet', async ({ page, browserName }) => {
|
test('it should trap focus in action sheet', async ({ page, browserName }) => {
|
||||||
await page.goto(`/src/components/action-sheet/test/basic`, config);
|
await page.goto(`/src/components/action-sheet/test/basic`, config);
|
||||||
@ -140,42 +100,3 @@ configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
class ActionSheetFixture {
|
|
||||||
readonly page: E2EPage;
|
|
||||||
readonly screenshotFn?: (file: string) => string;
|
|
||||||
|
|
||||||
private actionSheet!: Locator;
|
|
||||||
|
|
||||||
constructor(page: E2EPage, screenshot?: (file: string) => string) {
|
|
||||||
this.page = page;
|
|
||||||
this.screenshotFn = screenshot;
|
|
||||||
}
|
|
||||||
|
|
||||||
async open(selector: string) {
|
|
||||||
const ionActionSheetDidPresent = await this.page.spyOnEvent('ionActionSheetDidPresent');
|
|
||||||
await this.page.locator(selector).click();
|
|
||||||
await ionActionSheetDidPresent.next();
|
|
||||||
this.actionSheet = this.page.locator('ion-action-sheet');
|
|
||||||
await expect(this.actionSheet).toBeVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
async dismiss() {
|
|
||||||
const ionActionSheetDidDismiss = await this.page.spyOnEvent('ionActionSheetDidDismiss');
|
|
||||||
await this.actionSheet.evaluate((el: HTMLIonActionSheetElement) => el.dismiss());
|
|
||||||
await ionActionSheetDidDismiss.next();
|
|
||||||
await expect(this.actionSheet).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.actionSheet).toHaveScreenshot(screenshotFn(`action-sheet-${modifier}-diff`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
import { h } from '@stencil/core';
|
||||||
|
import { newSpecPage } from '@stencil/core/testing';
|
||||||
|
|
||||||
|
import { ActionSheet } from '../../action-sheet';
|
||||||
|
|
||||||
|
describe('action sheet: htmlAttributes inheritance', () => {
|
||||||
|
it('should correctly inherit attributes on host', async () => {
|
||||||
|
const page = await newSpecPage({
|
||||||
|
components: [ActionSheet],
|
||||||
|
template: () => <ion-action-sheet htmlAttributes={{ 'data-testid': 'basic-action-sheet' }}></ion-action-sheet>,
|
||||||
|
});
|
||||||
|
|
||||||
|
const actionSheet = page.body.querySelector('ion-action-sheet');
|
||||||
|
|
||||||
|
await expect(actionSheet.getAttribute('data-testid')).toBe('basic-action-sheet');
|
||||||
|
});
|
||||||
|
});
|
42
core/src/components/action-sheet/test/basic/fixture.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import type { Locator } from '@playwright/test';
|
||||||
|
import { expect } from '@playwright/test';
|
||||||
|
import type { E2EPage } from '@utils/test/playwright';
|
||||||
|
|
||||||
|
export class ActionSheetFixture {
|
||||||
|
readonly page: E2EPage;
|
||||||
|
readonly screenshotFn?: (file: string) => string;
|
||||||
|
|
||||||
|
private actionSheet!: Locator;
|
||||||
|
|
||||||
|
constructor(page: E2EPage, screenshot?: (file: string) => string) {
|
||||||
|
this.page = page;
|
||||||
|
this.screenshotFn = screenshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
async open(selector: string) {
|
||||||
|
const ionActionSheetDidPresent = await this.page.spyOnEvent('ionActionSheetDidPresent');
|
||||||
|
await this.page.locator(selector).click();
|
||||||
|
await ionActionSheetDidPresent.next();
|
||||||
|
this.actionSheet = this.page.locator('ion-action-sheet');
|
||||||
|
await expect(this.actionSheet).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
async dismiss() {
|
||||||
|
const ionActionSheetDidDismiss = await this.page.spyOnEvent('ionActionSheetDidDismiss');
|
||||||
|
await this.actionSheet.evaluate((el: HTMLIonActionSheetElement) => el.dismiss());
|
||||||
|
await ionActionSheetDidDismiss.next();
|
||||||
|
await expect(this.actionSheet).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.actionSheet).toHaveScreenshot(screenshotFn(`action-sheet-${modifier}-diff`));
|
||||||
|
}
|
||||||
|
}
|