From 79c65dc382aa7584e138ed3067c5c616905afcd4 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 16 Aug 2022 08:18:42 -0500 Subject: [PATCH] test(playwright): add new utilities for skipping tests (#25758) --- core/playwright.config.ts | 2 + .../accordion/test/a11y/accordion.e2e.ts | 4 +- .../test/basic/action-sheet.e2e.ts | 28 ++++---- .../test/translucent/action-sheet.e2e.ts | 9 +-- .../components/app/test/safe-area/app.e2e.ts | 7 +- .../button/test/basic/button.e2e.ts | 4 +- .../test/basic/datetime-button.e2e.ts | 27 ++++---- .../test/disabled/datetime-button.e2e.ts | 6 +- .../test/overlays/datetime-button.e2e.ts | 12 ++-- .../datetime/test/basic/datetime.e2e.ts | 11 ++- .../test/disable-dates/datetime.e2e.ts | 9 +-- .../datetime/test/multiple/datetime.e2e.ts | 5 +- .../test/presentation/datetime.e2e.ts | 4 +- .../header/test/condense/header.e2e.ts | 6 +- .../components/input/test/a11y/input.e2e.ts | 10 ++- .../input/test/masking/input.e2e.ts | 4 +- .../test/basic/item-sliding.e2e.ts | 6 +- .../test/scroll-target/item-sliding.e2e.ts | 9 +-- .../components/item/test/inputs/item.e2e.ts | 5 +- .../components/modal/test/basic/modal.e2e.ts | 6 +- .../modal/test/canDismiss/modal.e2e.ts | 4 +- .../modal/test/card-nav/modal.e2e.ts | 12 ++-- .../modal/test/card-refresher/modal.e2e.ts | 4 +- .../test/card-scroll-target/modal.e2e.ts | 4 +- .../components/modal/test/card/modal.e2e.ts | 4 +- .../modal/test/custom-dialog/modal.e2e.ts | 4 +- .../test/basic/picker-column-internal.e2e.ts | 12 ++-- .../test/dismissOnSelect/popover.e2e.ts | 6 +- .../radio-group/test/basic/radio-group.e2e.ts | 4 +- .../test/search/radio-group.e2e.ts | 4 +- .../components/radio/test/a11y/radio.e2e.ts | 5 +- .../components/radio/test/basic/radio.e2e.ts | 5 +- .../components/range/test/basic/range.e2e.ts | 6 +- .../range/test/scroll-target/range.e2e.ts | 6 +- .../select/test/async/select.e2e.ts | 4 +- .../select/test/compare-with/select.e2e.ts | 4 +- core/src/utils/test/overlays/overlays.e2e.ts | 4 +- core/src/utils/test/playwright/README.md | 69 +++++++++++++++++++ .../playwright/playwright-declarations.ts | 8 +++ .../utils/test/playwright/playwright-page.ts | 24 ++++++- 40 files changed, 222 insertions(+), 145 deletions(-) create mode 100644 core/src/utils/test/playwright/README.md diff --git a/core/playwright.config.ts b/core/playwright.config.ts index 6fe83347ff..5b15c4e169 100644 --- a/core/playwright.config.ts +++ b/core/playwright.config.ts @@ -33,12 +33,14 @@ const projects = [ { name: 'Mobile Chrome', use: { + browserName: 'chromium', ...devices['Pixel 5'] } }, { name: 'Mobile Safari', use: { + browserName: 'webkit', ...devices['iPhone 12'] } } diff --git a/core/src/components/accordion/test/a11y/accordion.e2e.ts b/core/src/components/accordion/test/a11y/accordion.e2e.ts index 3fdcdeb447..024aab8f84 100644 --- a/core/src/components/accordion/test/a11y/accordion.e2e.ts +++ b/core/src/components/accordion/test/a11y/accordion.e2e.ts @@ -2,9 +2,9 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('accordion: a11y', () => { - test('accordions should be keyboard navigable', async ({ page, browserName }) => { + test('accordions should be keyboard navigable', async ({ page, skip, browserName }) => { // TODO(FW-1764): remove skip once issue is resolved - test.skip(browserName === 'firefox', 'https://github.com/ionic-team/ionic-framework/issues/25529'); + skip.browser('firefox', 'https://github.com/ionic-team/ionic-framework/issues/25529'); await page.goto(`/src/components/accordion/test/a11y`); const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab'; diff --git a/core/src/components/action-sheet/test/basic/action-sheet.e2e.ts b/core/src/components/action-sheet/test/basic/action-sheet.e2e.ts index f7da10aa4b..449a64cd8a 100644 --- a/core/src/components/action-sheet/test/basic/action-sheet.e2e.ts +++ b/core/src/components/action-sheet/test/basic/action-sheet.e2e.ts @@ -10,8 +10,8 @@ test.describe('action sheet: basic', () => { actionSheetFixture = new ActionSheetFixture(page); }); test.describe('action sheet: data', () => { - test('should return data', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs. RTL layout.'); + test('should return data', async ({ page, skip }) => { + skip.rtl(); const ionActionSheetDidDismiss = await page.spyOnEvent('ionActionSheetDidDismiss'); await actionSheetFixture.open('#buttonData'); @@ -22,8 +22,8 @@ test.describe('action sheet: basic', () => { await ionActionSheetDidDismiss.next(); expect(ionActionSheetDidDismiss).toHaveReceivedEventDetail({ data: { type: '1' }, role: undefined }); }); - test('should return cancel button data', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs. RTL layout.'); + test('should return cancel button data', async ({ page, skip }) => { + skip.rtl(); const ionActionSheetDidDismiss = await page.spyOnEvent('ionActionSheetDidDismiss'); await actionSheetFixture.open('#buttonData'); @@ -36,8 +36,8 @@ test.describe('action sheet: basic', () => { }); }); test.describe('action sheet: attributes', () => { - test('should set htmlAttributes', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs. RTL layout.'); + test('should set htmlAttributes', async ({ page, skip }) => { + skip.rtl(); await actionSheetFixture.open('#basic'); const actionSheet = page.locator('ion-action-sheet'); @@ -73,15 +73,15 @@ test.describe('action sheet: basic', () => { await actionSheetFixture.open('#scrollWithoutCancel'); await actionSheetFixture.screenshot('scroll-without-cancel'); }); - test('should open custom backdrop action sheet', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs. RTL layout.'); + test('should open custom backdrop action sheet', async ({ page, skip }) => { + skip.rtl(); await actionSheetFixture.open('#customBackdrop'); const backdrop = page.locator('ion-action-sheet ion-backdrop'); expect(backdrop).toHaveCSS('opacity', '1'); }); - test('should open alert from action sheet', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs. RTL layout.'); + test('should open alert from action sheet', async ({ page, skip }) => { + skip.rtl(); const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent'); await actionSheetFixture.open('#alertFromActionSheet'); @@ -89,8 +89,8 @@ test.describe('action sheet: basic', () => { await ionAlertDidPresent.next(); }); - test('should not dismiss action sheet when backdropDismiss: false', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs. RTL layout.'); + test('should not dismiss action sheet when backdropDismiss: false', async ({ page, skip }) => { + skip.rtl(); await actionSheetFixture.open('#noBackdropDismiss'); const actionSheet = page.locator('ion-action-sheet'); @@ -100,8 +100,8 @@ test.describe('action sheet: basic', () => { }); }); test.describe('action sheet: focus trap', () => { - test('it should trap focus in action sheet', async ({ page, browserName }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs. RTL layout.'); + test('it should trap focus in action sheet', async ({ page, skip, browserName }) => { + skip.rtl(); const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab'; await actionSheetFixture.open('#basic'); diff --git a/core/src/components/action-sheet/test/translucent/action-sheet.e2e.ts b/core/src/components/action-sheet/test/translucent/action-sheet.e2e.ts index c32529090a..ac67992358 100644 --- a/core/src/components/action-sheet/test/translucent/action-sheet.e2e.ts +++ b/core/src/components/action-sheet/test/translucent/action-sheet.e2e.ts @@ -2,12 +2,9 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('action sheet: translucent', () => { - test('should not have visual regressions', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.mode === 'md', 'Translucent effect is only active on iOS mode'); - test.skip( - testInfo.project.metadata.rtl === true, - 'This tests how the component is painted, not layout. RTL tests are not needed here' - ); + test('should not have visual regressions', async ({ page, skip }) => { + skip.mode('md', 'Translucent effect is only active on iOS mode'); + skip.rtl('This tests how the component is painted, not layout. RTL tests are not needed here'); await page.goto(`/src/components/action-sheet/test/translucent`); diff --git a/core/src/components/app/test/safe-area/app.e2e.ts b/core/src/components/app/test/safe-area/app.e2e.ts index d754398d54..22a2d30f22 100644 --- a/core/src/components/app/test/safe-area/app.e2e.ts +++ b/core/src/components/app/test/safe-area/app.e2e.ts @@ -14,11 +14,8 @@ test.describe('app: safe-area', () => { expect(await page.screenshot()).toMatchSnapshot(`app-${screenshotModifier}-diff-${page.getSnapshotSettings()}.png`); }; - test.beforeEach(async ({ page }, testInfo) => { - test.skip( - testInfo.project.metadata.rtl === true, - 'Safe area tests only check top and bottom edges. RTL checks are not required here.' - ); + test.beforeEach(async ({ page, skip }) => { + skip.rtl('Safe area tests only check top and bottom edges. RTL checks are not required here.'); await page.goto(`/src/components/app/test/safe-area`); }); diff --git a/core/src/components/button/test/basic/button.e2e.ts b/core/src/components/button/test/basic/button.e2e.ts index dee4aa6526..6e54bc8ad0 100644 --- a/core/src/components/button/test/basic/button.e2e.ts +++ b/core/src/components/button/test/basic/button.e2e.ts @@ -12,8 +12,8 @@ test.describe('button: basic', () => { }); test.describe('button: ripple effect', () => { - test('should not have visual regressions', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.mode !== 'md', 'Ripple effect is only available in MD mode.'); + test('should not have visual regressions', async ({ page, skip }) => { + skip.mode('ios', 'Ripple effect is only available in MD mode.'); await page.goto(`/src/components/button/test/basic?ionic:_testing=false`); diff --git a/core/src/components/datetime-button/test/basic/datetime-button.e2e.ts b/core/src/components/datetime-button/test/basic/datetime-button.e2e.ts index 6654ddf87b..c236fc8d2b 100644 --- a/core/src/components/datetime-button/test/basic/datetime-button.e2e.ts +++ b/core/src/components/datetime-button/test/basic/datetime-button.e2e.ts @@ -2,9 +2,9 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('datetime-button: switching to correct view', () => { - test.beforeEach(async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === 'rtl', 'No layout tests'); - test.skip(testInfo.project.metadata.mode === 'ios', 'No mode-specific logic'); + test.beforeEach(async ({ page, skip }) => { + skip.rtl(); + skip.mode('ios', 'No mode-specific logic'); await page.setContent(` @@ -31,10 +31,9 @@ test.describe('datetime-button: switching to correct view', () => { }); test.describe('datetime-button: labels', () => { - // eslint-disable-next-line no-empty-pattern - test.beforeEach(({}, testInfo) => { - test.skip(testInfo.project.metadata.rtl === 'rtl', 'No layout tests'); - test.skip(testInfo.project.metadata.mode === 'ios', 'No mode-specific logic'); + test.beforeEach(({ skip }) => { + skip.rtl(); + skip.mode('ios', 'No mode-specific logic'); }); test('should set date and time labels in separate buttons', async ({ page }) => { await page.setContent(` @@ -106,10 +105,9 @@ test.describe('datetime-button: labels', () => { }); test.describe('datetime-button: locale', () => { - // eslint-disable-next-line no-empty-pattern - test.beforeEach(({}, testInfo) => { - test.skip(testInfo.project.metadata.rtl === 'rtl', 'No layout tests'); - test.skip(testInfo.project.metadata.mode === 'ios', 'No mode-specific logic'); + test.beforeEach(({ skip }) => { + skip.rtl(); + skip.mode('ios', 'No mode-specific logic'); }); test('should use the same locale as datetime', async ({ page }) => { await page.setContent(` @@ -154,10 +152,9 @@ test.describe('datetime-button: locale', () => { }); test.describe('datetime-button: wheel', () => { - // eslint-disable-next-line no-empty-pattern - test.beforeEach(({}, testInfo) => { - test.skip(testInfo.project.metadata.rtl === 'rtl', 'No layout tests'); - test.skip(testInfo.project.metadata.mode === 'ios', 'No mode-specific logic'); + test.beforeEach(({ skip }) => { + skip.rtl(); + skip.mode('ios', 'No mode-specific logic'); }); test('should only show a single date button when presentation="date-time" and prefer-wheel="true"', async ({ page, diff --git a/core/src/components/datetime-button/test/disabled/datetime-button.e2e.ts b/core/src/components/datetime-button/test/disabled/datetime-button.e2e.ts index c9e23913a6..9ec8a083ac 100644 --- a/core/src/components/datetime-button/test/disabled/datetime-button.e2e.ts +++ b/core/src/components/datetime-button/test/disabled/datetime-button.e2e.ts @@ -2,9 +2,9 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('datetime-button: disabled buttons', () => { - test('buttons should not be enabled when component is disabled', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === 'rtl', 'No layout tests'); - test.skip(testInfo.project.metadata.mode === 'ios', 'No mode-specific logic'); + test('buttons should not be enabled when component is disabled', async ({ page, skip }) => { + skip.rtl(); + skip.mode('ios', 'No mode-specific logic'); await page.setContent(` diff --git a/core/src/components/datetime-button/test/overlays/datetime-button.e2e.ts b/core/src/components/datetime-button/test/overlays/datetime-button.e2e.ts index 49d18c5637..7fdab33f00 100644 --- a/core/src/components/datetime-button/test/overlays/datetime-button.e2e.ts +++ b/core/src/components/datetime-button/test/overlays/datetime-button.e2e.ts @@ -42,9 +42,9 @@ test.describe('datetime-button: popover', () => { let popover: Locator; let ionPopoverDidPresent: EventSpy; let ionPopoverDidDismiss: EventSpy; - test.beforeEach(async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === 'rtl', 'No layout tests'); - test.skip(testInfo.project.metadata.mode === 'ios', 'No mode-specific logic'); + test.beforeEach(async ({ page, skip }) => { + skip.rtl(); + skip.mode('ios', 'No mode-specific logic'); await page.setContent(` @@ -104,9 +104,9 @@ test.describe('datetime-button: modal', () => { let modal: Locator; let ionModalDidPresent: EventSpy; let ionModalDidDismiss: EventSpy; - test.beforeEach(async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === 'rtl', 'No layout tests'); - test.skip(testInfo.project.metadata.mode === 'ios', 'No mode-specific logic'); + test.beforeEach(async ({ page, skip }) => { + skip.rtl(); + skip.mode('ios', 'No mode-specific logic'); await page.setContent(` diff --git a/core/src/components/datetime/test/basic/datetime.e2e.ts b/core/src/components/datetime/test/basic/datetime.e2e.ts index 5f4a41fafe..324c9b64c1 100644 --- a/core/src/components/datetime/test/basic/datetime.e2e.ts +++ b/core/src/components/datetime/test/basic/datetime.e2e.ts @@ -185,10 +185,9 @@ test.describe('datetime: footer', () => { }); test.describe('datetime: swiping', () => { - // eslint-disable-next-line no-empty-pattern - test.beforeEach(({}, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs RTL layouts.'); - test.skip(testInfo.project.metadata.mode === 'ios', 'This does not have mode-specific logic.'); + test.beforeEach(({ skip }) => { + skip.rtl(); + skip.mode('ios', 'This does not have mode-specific logic.'); }); test('should move to prev month by swiping', async ({ page }) => { await page.setContent(` @@ -224,8 +223,8 @@ test.describe('datetime: swiping', () => { await expect(calendarHeader).toHaveText(/June 2022/); }); - test('should not re-render if swipe is in progress', async ({ page, browserName }) => { - test.skip(browserName === 'webkit', 'Wheel is not available in WebKit'); + test('should not re-render if swipe is in progress', async ({ page, skip }) => { + skip.browser('webkit', 'Wheel is not available in WebKit'); await page.setContent(` diff --git a/core/src/components/datetime/test/disable-dates/datetime.e2e.ts b/core/src/components/datetime/test/disable-dates/datetime.e2e.ts index 23bc14787e..bbcb3aff66 100644 --- a/core/src/components/datetime/test/disable-dates/datetime.e2e.ts +++ b/core/src/components/datetime/test/disable-dates/datetime.e2e.ts @@ -13,13 +13,8 @@ const queryAllWorkingMonthDisabledDays = (page: E2EPage, datetimeSelector = 'ion }; test.describe('datetime: disable dates', () => { - /** - * We need to access testInfo, but Playwright - * requires that we destructure the first parameter. - */ - // eslint-disable-next-line no-empty-pattern - test.beforeEach(({}, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'These tests do not check layout rendering functionality.'); + test.beforeEach(({ skip }) => { + skip.rtl(); }); test.describe('check return values', () => { test.beforeEach(async ({ page }) => { diff --git a/core/src/components/datetime/test/multiple/datetime.e2e.ts b/core/src/components/datetime/test/multiple/datetime.e2e.ts index 98d7b60442..3244efecf1 100644 --- a/core/src/components/datetime/test/multiple/datetime.e2e.ts +++ b/core/src/components/datetime/test/multiple/datetime.e2e.ts @@ -32,9 +32,8 @@ test.describe('datetime: multiple date selection (visual regressions)', () => { }); test.describe('datetime: multiple date selection (functionality)', () => { - // eslint-disable-next-line no-empty-pattern - test.beforeEach(async ({}, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'Does not test LTR vs. RTL layout.'); + test.beforeEach(async ({ skip }) => { + skip.rtl(); }); test('clicking unselected days should select them', async ({ page }) => { diff --git a/core/src/components/datetime/test/presentation/datetime.e2e.ts b/core/src/components/datetime/test/presentation/datetime.e2e.ts index 137c625fad..050caa720e 100644 --- a/core/src/components/datetime/test/presentation/datetime.e2e.ts +++ b/core/src/components/datetime/test/presentation/datetime.e2e.ts @@ -84,8 +84,8 @@ test.describe('datetime: presentation', () => { expect(ionChangeSpy.length).toBe(1); }); - test('switching presentation should close month/year picker', async ({ page }, testInfo) => { - await test.skip(testInfo.project.metadata.rtl === true, 'This feature does not have RTL specific behaviors.'); + test('switching presentation should close month/year picker', async ({ page, skip }) => { + await skip.rtl(); await page.setContent(` diff --git a/core/src/components/header/test/condense/header.e2e.ts b/core/src/components/header/test/condense/header.e2e.ts index 48ea676ce0..3312a06ead 100644 --- a/core/src/components/header/test/condense/header.e2e.ts +++ b/core/src/components/header/test/condense/header.e2e.ts @@ -2,9 +2,9 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('header: condense', () => { - test('should be hidden from screen readers when collapsed', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.mode === 'md', 'Logic only applies to iOS mode'); - test.skip(testInfo.project.metadata.rtl === true, 'No RTL-specific logic'); + test('should be hidden from screen readers when collapsed', async ({ page, skip }) => { + skip.mode('md'); + skip.rtl(); await page.goto('/src/components/header/test/condense'); const header = page.locator('#collapsibleHeader'); diff --git a/core/src/components/input/test/a11y/input.e2e.ts b/core/src/components/input/test/a11y/input.e2e.ts index 6d8365fdd6..f705d0cb45 100644 --- a/core/src/components/input/test/a11y/input.e2e.ts +++ b/core/src/components/input/test/a11y/input.e2e.ts @@ -2,10 +2,8 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('input: a11y', () => { - test('does not set a default aria-labelledby when there is not a neighboring ion-label', async ({ - page, - }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'Does not test LTR vs. RTL layout.'); + test('does not set a default aria-labelledby when there is not a neighboring ion-label', async ({ page, skip }) => { + skip.rtl(); await page.setContent(''); @@ -15,8 +13,8 @@ test.describe('input: a11y', () => { await expect(ariaLabelledBy).toBe(null); }); - test('set a default aria-labelledby when a neighboring ion-label exists', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'Does not test LTR vs. RTL layout.'); + test('set a default aria-labelledby when a neighboring ion-label exists', async ({ page, skip }) => { + skip.rtl(); await page.setContent( ` diff --git a/core/src/components/input/test/masking/input.e2e.ts b/core/src/components/input/test/masking/input.e2e.ts index 2bbd36864a..88e2d6746d 100644 --- a/core/src/components/input/test/masking/input.e2e.ts +++ b/core/src/components/input/test/masking/input.e2e.ts @@ -6,8 +6,8 @@ test.describe('input: masking', () => { await page.goto('/src/components/input/test/masking'); }); - test('should filter out spaces', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'Does not test LTR vs. RTL layout.'); + test('should filter out spaces', async ({ page, skip }) => { + skip.rtl(); const input = page.locator('#inputTrimmed'); diff --git a/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts b/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts index 33c0511151..3f8baae470 100644 --- a/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts +++ b/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts @@ -2,9 +2,9 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('item-sliding: basic', () => { - test('should not scroll when the item-sliding is swiped', async ({ page, browserName }, testInfo) => { - test.skip(browserName === 'webkit', 'mouse.wheel is not available in WebKit'); - test.skip(testInfo.project.metadata.rtl === true, 'This feature does not have RTL-specific behaviors'); + test('should not scroll when the item-sliding is swiped', async ({ page, skip }) => { + skip.browser('webkit', 'mouse.wheel is not available in WebKit'); + skip.rtl(); await page.goto(`/src/components/item-sliding/test/basic`); diff --git a/core/src/components/item-sliding/test/scroll-target/item-sliding.e2e.ts b/core/src/components/item-sliding/test/scroll-target/item-sliding.e2e.ts index 162ed9105a..9f8f1d7803 100644 --- a/core/src/components/item-sliding/test/scroll-target/item-sliding.e2e.ts +++ b/core/src/components/item-sliding/test/scroll-target/item-sliding.e2e.ts @@ -2,12 +2,9 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('item-sliding: scroll-target', () => { - test('should not scroll when the item-sliding is swiped in custom scroll target', async ({ - page, - browserName, - }, testInfo) => { - test.skip(browserName === 'webkit', 'mouse.wheel is not available in WebKit'); - test.skip(testInfo.project.metadata.rtl === true, 'This feature does not have RTL-specific behaviors'); + test('should not scroll when the item-sliding is swiped in custom scroll target', async ({ page, skip }) => { + skip.browser('webkit', 'mouse.wheel is not available in WebKit'); + skip.rtl(); await page.goto(`/src/components/item-sliding/test/scroll-target`); diff --git a/core/src/components/item/test/inputs/item.e2e.ts b/core/src/components/item/test/inputs/item.e2e.ts index 442852d6ab..47cc6d9912 100644 --- a/core/src/components/item/test/inputs/item.e2e.ts +++ b/core/src/components/item/test/inputs/item.e2e.ts @@ -41,9 +41,8 @@ test.describe('item: inputs', () => { }); test.describe('form data', () => { - // eslint-disable-next-line no-empty-pattern - test.beforeEach(async ({}, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'Does not test LTR vs. RTL layout.'); + test.beforeEach(async ({ skip }) => { + skip.rtl(); }); test('initial form data should be empty', async ({ page }) => { diff --git a/core/src/components/modal/test/basic/modal.e2e.ts b/core/src/components/modal/test/basic/modal.e2e.ts index 29e6727093..aa86ab3821 100644 --- a/core/src/components/modal/test/basic/modal.e2e.ts +++ b/core/src/components/modal/test/basic/modal.e2e.ts @@ -3,9 +3,9 @@ import { test, Viewports } from '@utils/test/playwright'; import type { E2EPage } from '@utils/test/playwright'; test.describe('modal: focus trapping', () => { - test.beforeEach(async ({ browserName }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs. RTL layout.'); - test.skip(browserName === 'firefox', 'Firefox incorrectly allows keyboard focus to move to ion-content'); + test.beforeEach(async ({ skip }) => { + skip.rtl(); + skip.browser('firefox', 'Firefox incorrectly allows keyboard focus to move to ion-content'); }); test('focus should be trapped inside of modal', async ({ page, browserName }) => { /** diff --git a/core/src/components/modal/test/canDismiss/modal.e2e.ts b/core/src/components/modal/test/canDismiss/modal.e2e.ts index 2bae0c7c36..81d9519f94 100644 --- a/core/src/components/modal/test/canDismiss/modal.e2e.ts +++ b/core/src/components/modal/test/canDismiss/modal.e2e.ts @@ -134,8 +134,8 @@ test.describe('modal: canDismiss', () => { }); test.describe('card modal - iOS swiping', () => { - test.beforeEach(async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.mode !== 'ios', 'Swipe to close on a modal is only available in iOS mode.'); + test.beforeEach(async ({ page, skip }) => { + skip.mode('md'); await page.click('#radio-card'); }); diff --git a/core/src/components/modal/test/card-nav/modal.e2e.ts b/core/src/components/modal/test/card-nav/modal.e2e.ts index 2c3466d329..76f081a143 100644 --- a/core/src/components/modal/test/card-nav/modal.e2e.ts +++ b/core/src/components/modal/test/card-nav/modal.e2e.ts @@ -5,13 +5,13 @@ import { CardModalPage } from '../fixtures'; test.describe('card modal - nav', () => { let cardModalPage: CardModalPage; - test.beforeEach(async ({ page, browserName }, testInfo) => { - test.skip(testInfo.project.metadata.mode !== 'ios', 'Card style modal is only available on iOS'); - test.skip( - testInfo.project.metadata.rtl === true, - 'This test only verifies that the gesture activates inside of a modal.' + test.beforeEach(async ({ page, skip }) => { + skip.mode('md'); + skip.rtl('This test only verifies that the gesture activates inside of a modal.'); + skip.browser( + (browserName: string) => browserName !== 'chromium', + 'dragElementBy is flaky outside of Chrome browsers.' ); - test.skip(browserName !== 'chromium', 'dragElementBy is flaky outside of Chrome browsers.'); cardModalPage = new CardModalPage(page); await cardModalPage.navigate('/src/components/modal/test/card-nav?ionic:_testing=false'); diff --git a/core/src/components/modal/test/card-refresher/modal.e2e.ts b/core/src/components/modal/test/card-refresher/modal.e2e.ts index 9567d15889..776c74c8f0 100644 --- a/core/src/components/modal/test/card-refresher/modal.e2e.ts +++ b/core/src/components/modal/test/card-refresher/modal.e2e.ts @@ -2,8 +2,8 @@ import { expect } from '@playwright/test'; import { dragElementBy, test } from '@utils/test/playwright'; test.describe('card modal - with refresher', () => { - test.beforeEach(async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.mode !== 'ios', 'Card style modal is only available on iOS'); + test.beforeEach(async ({ page, skip }) => { + skip.mode('md'); await page.goto('/src/components/modal/test/card-refresher'); }); diff --git a/core/src/components/modal/test/card-scroll-target/modal.e2e.ts b/core/src/components/modal/test/card-scroll-target/modal.e2e.ts index 2d3eb50097..35290325b8 100644 --- a/core/src/components/modal/test/card-scroll-target/modal.e2e.ts +++ b/core/src/components/modal/test/card-scroll-target/modal.e2e.ts @@ -2,8 +2,8 @@ import { expect } from '@playwright/test'; import { dragElementBy, test } from '@utils/test/playwright'; test.describe('card modal - scroll target', () => { - test.beforeEach(async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.mode !== 'ios', 'Card style modal is only available on iOS'); + test.beforeEach(async ({ page, skip }) => { + skip.mode('md'); await page.goto('/src/components/modal/test/card-scroll-target'); }); diff --git a/core/src/components/modal/test/card/modal.e2e.ts b/core/src/components/modal/test/card/modal.e2e.ts index 048211f748..0e24f5eed6 100644 --- a/core/src/components/modal/test/card/modal.e2e.ts +++ b/core/src/components/modal/test/card/modal.e2e.ts @@ -5,8 +5,8 @@ import { CardModalPage } from '../fixtures'; test.describe('card modal', () => { let cardModalPage: CardModalPage; - test.beforeEach(async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.mode !== 'ios', 'Card style modal is only available on iOS'); + test.beforeEach(async ({ page, skip }) => { + skip.mode('md'); cardModalPage = new CardModalPage(page); await cardModalPage.navigate('/src/components/modal/test/card'); diff --git a/core/src/components/modal/test/custom-dialog/modal.e2e.ts b/core/src/components/modal/test/custom-dialog/modal.e2e.ts index 2b167abd0b..f5d89b279d 100644 --- a/core/src/components/modal/test/custom-dialog/modal.e2e.ts +++ b/core/src/components/modal/test/custom-dialog/modal.e2e.ts @@ -2,8 +2,8 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('modal: custom dialog', () => { - test('should size custom modal correctly', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs. RTL layout.'); + test('should size custom modal correctly', async ({ page, skip }) => { + skip.rtl(); test.info().annotations.push({ type: 'issue', description: 'https://github.com/ionic-team/ionic-framework/issues/24080', diff --git a/core/src/components/picker-column-internal/test/basic/picker-column-internal.e2e.ts b/core/src/components/picker-column-internal/test/basic/picker-column-internal.e2e.ts index c0d188d540..455a0a25a9 100644 --- a/core/src/components/picker-column-internal/test/basic/picker-column-internal.e2e.ts +++ b/core/src/components/picker-column-internal/test/basic/picker-column-internal.e2e.ts @@ -32,8 +32,8 @@ test.describe('picker-column-internal', () => { expect(activeColumn).not.toBeNull(); }); - test('scrolling should change the active item', async ({ page, browserName }) => { - test.skip(browserName === 'firefox', 'https://bugzilla.mozilla.org/show_bug.cgi?id=1766890'); + test('scrolling should change the active item', async ({ page, skip }) => { + skip.browser('firefox', 'https://bugzilla.mozilla.org/show_bug.cgi?id=1766890'); await page.locator('#default').evaluate((el: HTMLIonPickerColumnInternalElement) => { el.scrollTop = 801; @@ -45,8 +45,8 @@ test.describe('picker-column-internal', () => { expect(await activeColumn?.innerText()).toEqual('23'); }); - test('should not emit ionChange when the value is modified externally', async ({ page, browserName }) => { - test.skip(browserName === 'firefox', 'https://bugzilla.mozilla.org/show_bug.cgi?id=1766890'); + test('should not emit ionChange when the value is modified externally', async ({ page, skip }) => { + skip.browser('firefox', 'https://bugzilla.mozilla.org/show_bug.cgi?id=1766890'); const ionChangeSpy = await page.spyOnEvent('ionChange'); @@ -57,8 +57,8 @@ test.describe('picker-column-internal', () => { expect(ionChangeSpy).not.toHaveReceivedEvent(); }); - test('should emit ionChange when the picker is scrolled', async ({ page, browserName }) => { - test.skip(browserName === 'firefox', 'https://bugzilla.mozilla.org/show_bug.cgi?id=1766890'); + test('should emit ionChange when the picker is scrolled', async ({ page, skip }) => { + skip.browser('firefox', 'https://bugzilla.mozilla.org/show_bug.cgi?id=1766890'); const ionChangeSpy = await page.spyOnEvent('ionChange'); diff --git a/core/src/components/popover/test/dismissOnSelect/popover.e2e.ts b/core/src/components/popover/test/dismissOnSelect/popover.e2e.ts index e3b814cfd5..4824740bff 100644 --- a/core/src/components/popover/test/dismissOnSelect/popover.e2e.ts +++ b/core/src/components/popover/test/dismissOnSelect/popover.e2e.ts @@ -23,10 +23,10 @@ test.describe('popover: dismissOnSelect', async () => { await expect(popover).toBeVisible(); }); - test('should not dismiss a popover when clicking a click trigger', async ({ page, browserName }) => { + test('should not dismiss a popover when clicking a click trigger', async ({ page, skip }) => { // TODO FW-1486 - test.skip( - browserName === 'firefox', + skip.browser( + 'firefox', 'Parent popover disappears when click trigger is clicked. Cannot replicate locally. Needs further investigation.' ); diff --git a/core/src/components/radio-group/test/basic/radio-group.e2e.ts b/core/src/components/radio-group/test/basic/radio-group.e2e.ts index 3916a5e280..106ea7dcb2 100644 --- a/core/src/components/radio-group/test/basic/radio-group.e2e.ts +++ b/core/src/components/radio-group/test/basic/radio-group.e2e.ts @@ -16,8 +16,8 @@ test.describe('radio-group: basic', () => { test.describe('radio-group: interaction', () => { let radioFixture: RadioFixture; - test.beforeEach(({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs RTL logic.'); + test.beforeEach(({ page, skip }) => { + skip.rtl(); radioFixture = new RadioFixture(page); }); diff --git a/core/src/components/radio-group/test/search/radio-group.e2e.ts b/core/src/components/radio-group/test/search/radio-group.e2e.ts index ef031bb4a4..9acba673e3 100644 --- a/core/src/components/radio-group/test/search/radio-group.e2e.ts +++ b/core/src/components/radio-group/test/search/radio-group.e2e.ts @@ -2,8 +2,8 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('radio-group', () => { - test.beforeEach(async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs RTL logic.'); + test.beforeEach(async ({ page, skip }) => { + skip.rtl(); await page.goto('/src/components/radio-group/test/search'); }); diff --git a/core/src/components/radio/test/a11y/radio.e2e.ts b/core/src/components/radio/test/a11y/radio.e2e.ts index ba44ca68d4..81c7bd84cc 100644 --- a/core/src/components/radio/test/a11y/radio.e2e.ts +++ b/core/src/components/radio/test/a11y/radio.e2e.ts @@ -2,9 +2,8 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('radio: a11y', () => { - // eslint-disable-next-line no-empty-pattern - test.beforeEach(({}, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs RTL logic.'); + test.beforeEach(({ skip }) => { + skip.rtl(); }); test('tabbing should switch between radio groups', async ({ page, browserName }) => { const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab'; diff --git a/core/src/components/radio/test/basic/radio.e2e.ts b/core/src/components/radio/test/basic/radio.e2e.ts index 396077abd5..31542286dd 100644 --- a/core/src/components/radio/test/basic/radio.e2e.ts +++ b/core/src/components/radio/test/basic/radio.e2e.ts @@ -97,9 +97,8 @@ test.describe('radio: rendering', () => { }); test.describe('radio: interaction', () => { - // eslint-disable-next-line no-empty-pattern - test.beforeEach(({}, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs RTL logic.'); + test.beforeEach(({ skip }) => { + skip.rtl(); }); test('radio should be checked when activated', async ({ page }) => { await page.setContent(` diff --git a/core/src/components/range/test/basic/range.e2e.ts b/core/src/components/range/test/basic/range.e2e.ts index 2700b0aceb..1792cec0c1 100644 --- a/core/src/components/range/test/basic/range.e2e.ts +++ b/core/src/components/range/test/basic/range.e2e.ts @@ -54,9 +54,9 @@ test.describe('range: basic', () => { expect(rangeEnd).toHaveReceivedEventDetail({ value: 21 }); }); - test('should not scroll when the knob is swiped', async ({ page, browserName }, testInfo) => { - test.skip(browserName === 'webkit', 'mouse.wheel is not available in WebKit'); - test.skip(testInfo.project.metadata.rtl === true, 'This feature does not have RTL-specific behaviors'); + test('should not scroll when the knob is swiped', async ({ page, skip }) => { + skip.browser('webkit', 'mouse.wheel is not available in WebKit'); + skip.rtl(); await page.goto(`/src/components/range/test/basic`); diff --git a/core/src/components/range/test/scroll-target/range.e2e.ts b/core/src/components/range/test/scroll-target/range.e2e.ts index 26dd5049a4..ff7723f1db 100644 --- a/core/src/components/range/test/scroll-target/range.e2e.ts +++ b/core/src/components/range/test/scroll-target/range.e2e.ts @@ -2,9 +2,9 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('range: scroll-target', () => { - test('should not scroll when the knob is swiped in custom scroll target', async ({ page, browserName }, testInfo) => { - test.skip(browserName === 'webkit', 'mouse.wheel is not available in WebKit'); - test.skip(testInfo.project.metadata.rtl === true, 'This feature does not have RTL-specific behaviors'); + test('should not scroll when the knob is swiped in custom scroll target', async ({ page, skip }) => { + skip.browser('webkit', 'mouse.wheel is not available in WebKit'); + skip.rtl(); await page.goto(`/src/components/range/test/scroll-target`); diff --git a/core/src/components/select/test/async/select.e2e.ts b/core/src/components/select/test/async/select.e2e.ts index 783c857300..8db1aa8966 100644 --- a/core/src/components/select/test/async/select.e2e.ts +++ b/core/src/components/select/test/async/select.e2e.ts @@ -2,8 +2,8 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('select: async', () => { - test('should correctly set the value after a delay', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This is checking internal logic. RTL tests are not needed'); + test('should correctly set the value after a delay', async ({ page, skip }) => { + skip.rtl('This is checking internal logic. RTL tests are not needed'); await page.goto(`/src/components/select/test/async`); const selectValueSet = await page.spyOnEvent('selectValueSet'); diff --git a/core/src/components/select/test/compare-with/select.e2e.ts b/core/src/components/select/test/compare-with/select.e2e.ts index 09534ca8d1..9c2dd55685 100644 --- a/core/src/components/select/test/compare-with/select.e2e.ts +++ b/core/src/components/select/test/compare-with/select.e2e.ts @@ -2,8 +2,8 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('select: compare-with', () => { - test('should correctly set value when using compareWith property', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'This is checking internal logic. RTL tests are not needed'); + test('should correctly set value when using compareWith property', async ({ page, skip }) => { + skip.rtl('This is checking internal logic. RTL tests are not needed'); await page.goto('/src/components/select/test/compare-with'); diff --git a/core/src/utils/test/overlays/overlays.e2e.ts b/core/src/utils/test/overlays/overlays.e2e.ts index 5c125f324c..51d1fb2737 100644 --- a/core/src/utils/test/overlays/overlays.e2e.ts +++ b/core/src/utils/test/overlays/overlays.e2e.ts @@ -2,8 +2,8 @@ import { expect } from '@playwright/test'; import { test } from '@utils/test/playwright'; test.describe('overlays: focus', () => { - test('should not focus the overlay container if element inside of overlay is focused', async ({ page }, testInfo) => { - test.skip(testInfo.project.metadata.rtl === true, 'RTL tests are not needed as layout is not checked'); + test('should not focus the overlay container if element inside of overlay is focused', async ({ page, skip }) => { + skip.rtl(); await page.setContent(` Show Modal diff --git a/core/src/utils/test/playwright/README.md b/core/src/utils/test/playwright/README.md new file mode 100644 index 0000000000..ac650fa5e2 --- /dev/null +++ b/core/src/utils/test/playwright/README.md @@ -0,0 +1,69 @@ +# Playwright Test Utils + +This directory contains utilities that can be used to more easily test Stencil projects with Playwright. + +## Test Function + +The default `test` function has been extended to provide two custom options. + +| Fixture | Type | Description | +| ------- | ---- | ----------- | +| page | [E2EPage](https://github.com/ionic-team/ionic-framework/blob/main/core/src/utils/test/playwright/playwright-declarations.ts) | An extension of the base `page` test fixture within Playwright | +| skip | [E2ESkip](https://github.com/ionic-team/ionic-framework/blob/main/core/src/utils/test/playwright/playwright-declarations.ts) | Used to skip tests based on text direction, mode, or browser | + +### Usage + +**`page`** + +```typescript +import { test } from '@utils/test/playwright'; + +test('my custom test', ({ page }) => { + await page.goto('path/to/file'); +}); +``` + +**`skip.mode`** + +```typescript +import { test } from '@utils/test/playwright'; + +test('my custom test', ({ page, skip }) => { + skip.mode('md', 'This test is iOS-specific.'); + + await page.goto('path/to/file'); +}); +``` + +**`skip.rtl`** +```typescript +import { test } from '@utils/test/playwright'; + +test('my custom test', ({ page, skip }) => { + skip.rtl('This test does not have RTL-specific behaviors.'); + + await page.goto('path/to/file'); +}); +``` + +**`skip.browser`** +```typescript +import { test } from '@utils/test/playwright'; + +test('my custom test', ({ page, skip }) => { + skip.browser('webkit', 'This test does not work in WebKit yet.'); + + await page.goto('path/to/file'); +}); +``` + +**`skip.browser` with callback** +```typescript +import { test } from '@utils/test/playwright'; + +test('my custom test', ({ page, skip }) => { + skip.browser((browserName: string) => browserName !== 'webkit', 'This tests a WebKit-specific behavior.'); + + await page.goto('path/to/file'); +}); +``` \ No newline at end of file diff --git a/core/src/utils/test/playwright/playwright-declarations.ts b/core/src/utils/test/playwright/playwright-declarations.ts index f033fb6fe2..6260f40647 100644 --- a/core/src/utils/test/playwright/playwright-declarations.ts +++ b/core/src/utils/test/playwright/playwright-declarations.ts @@ -96,6 +96,14 @@ export interface E2EPage extends Page { _e2eEvents: Map; } +export type BrowserNameOrCallback = string | ((browserName: string) => boolean); + +export interface E2ESkip { + rtl: (reason?: string) => void; + browser: (browserNameOrCallback: BrowserNameOrCallback, reason?: string) => void; + mode: (mode: string, reason?: string) => void; +} + export interface SetIonViewportOptions { /** * `true` if the viewport should be scaled to match the `ion-content` diff --git a/core/src/utils/test/playwright/playwright-page.ts b/core/src/utils/test/playwright/playwright-page.ts index cc6bfecff5..3e16a75237 100644 --- a/core/src/utils/test/playwright/playwright-page.ts +++ b/core/src/utils/test/playwright/playwright-page.ts @@ -18,7 +18,7 @@ import { locator, } from './page/utils'; import type { LocatorOptions } from './page/utils'; -import type { E2EPage, SetIonViewportOptions } from './playwright-declarations'; +import type { E2EPage, E2ESkip, BrowserNameOrCallback, SetIonViewportOptions } from './playwright-declarations'; type CustomTestArgs = PlaywrightTestArgs & PlaywrightTestOptions & @@ -29,6 +29,7 @@ type CustomTestArgs = PlaywrightTestArgs & type CustomFixtures = { page: E2EPage; + skip: E2ESkip; }; /** @@ -63,4 +64,25 @@ export const test = base.extend({ page = await extendPageFixture(page, testInfo); await use(page); }, + skip: { + rtl: (reason = 'The functionality that is being tested is not applicable to RTL layouts.') => { + const testInfo: TestInfo = base.info(); + base.skip(testInfo.project.metadata.rtl === true, reason); + }, + browser: ( + browserNameOrFunction: BrowserNameOrCallback, + reason = `The functionality that is being tested is not applicable to this browser.` + ) => { + const browserName = base.info().project.use.browserName!; + + if (typeof browserNameOrFunction === 'function') { + base.skip(browserNameOrFunction(browserName), reason); + } else { + base.skip(browserName === browserNameOrFunction, reason); + } + }, + mode: (mode: string, reason = `The functionality that is being tested is not applicable to ${mode} mode`) => { + base.skip(base.info().project.metadata.mode === mode, reason); + }, + }, });