diff --git a/core/src/utils/animation/test/animationbuilder/animation.e2e-legacy.ts b/core/src/utils/animation/test/animationbuilder/animation.e2e-legacy.ts
deleted file mode 100644
index 5e556d8c1d..0000000000
--- a/core/src/utils/animation/test/animationbuilder/animation.e2e-legacy.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import type { E2EPage } from '@utils/test/playwright';
-import { test } from '@utils/test/playwright';
-
-test.describe('animation: animationbuilder', async () => {
- test.beforeEach(({ skip }) => {
- skip.rtl();
- });
- test('backwards-compatibility animation', async ({ page }) => {
- await page.goto('/src/utils/animation/test/animationbuilder');
- await testNavigation(page);
- });
-
- test('ios-transition web', async ({ page, skip }) => {
- skip.mode('md');
-
- await page.goto('/src/utils/animation/test/animationbuilder');
- await testNavigation(page);
- });
-
- test('ios-transition css', async ({ page, skip }) => {
- skip.mode('md');
-
- await page.goto('/src/utils/animation/test/animationbuilder?ionic:_forceCSSAnimations=true');
- await testNavigation(page);
- });
-});
-
-const testNavigation = async (page: E2EPage) => {
- const ionRouteDidChange = await page.spyOnEvent('ionRouteDidChange');
-
- await page.click('page-root ion-button.next');
- await ionRouteDidChange.next();
- page.click('page-one ion-button.next');
- await ionRouteDidChange.next();
- page.click('page-two ion-button.next');
- await ionRouteDidChange.next();
- page.click('page-three ion-back-button');
- await ionRouteDidChange.next();
- page.click('page-two ion-back-button');
- await ionRouteDidChange.next();
- page.click('page-one ion-back-button');
- await ionRouteDidChange.next();
-};
diff --git a/core/src/utils/animation/test/animationbuilder/animation.e2e.ts b/core/src/utils/animation/test/animationbuilder/animation.e2e.ts
new file mode 100644
index 0000000000..96ea221853
--- /dev/null
+++ b/core/src/utils/animation/test/animationbuilder/animation.e2e.ts
@@ -0,0 +1,38 @@
+import type { E2EPage } from '@utils/test/playwright';
+import { configs, test } from '@utils/test/playwright';
+
+configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
+ test.describe(title('animation: animationbuilder'), async () => {
+ test('backwards-compatibility animation', async ({ page }) => {
+ await page.goto('/src/utils/animation/test/animationbuilder', config);
+ await testNavigation(page);
+ });
+
+ test('ios-transition web', async ({ page }) => {
+ await page.goto('/src/utils/animation/test/animationbuilder', config);
+ await testNavigation(page);
+ });
+
+ test('ios-transition css', async ({ page }) => {
+ await page.goto('/src/utils/animation/test/animationbuilder?ionic:_forceCSSAnimations=true', config);
+ await testNavigation(page);
+ });
+ });
+});
+
+const testNavigation = async (page: E2EPage) => {
+ const ionRouteDidChange = await page.spyOnEvent('ionRouteDidChange');
+
+ await page.click('page-root ion-button.next');
+ await ionRouteDidChange.next();
+ page.click('page-one ion-button.next');
+ await ionRouteDidChange.next();
+ page.click('page-two ion-button.next');
+ await ionRouteDidChange.next();
+ page.click('page-three ion-back-button');
+ await ionRouteDidChange.next();
+ page.click('page-two ion-back-button');
+ await ionRouteDidChange.next();
+ page.click('page-one ion-back-button');
+ await ionRouteDidChange.next();
+};
diff --git a/core/src/utils/animation/test/basic/animation.e2e-legacy.ts b/core/src/utils/animation/test/basic/animation.e2e-legacy.ts
deleted file mode 100644
index 41ae95b366..0000000000
--- a/core/src/utils/animation/test/basic/animation.e2e-legacy.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { test } from '@utils/test/playwright';
-import type { E2EPage } from '@utils/test/playwright';
-
-test.describe('animation: basic', async () => {
- test.beforeEach(({ skip }) => {
- skip.rtl();
- skip.mode('ios');
- });
-
- test(`should resolve using web animations`, async ({ page }) => {
- await page.goto('/src/utils/animation/test/basic');
- await testPage(page);
- });
-
- test(`should resolve using css animations`, async ({ page }) => {
- await page.goto('/src/utils/animation/test/basic?ionic:_forceCSSAnimations=true');
- await testPage(page);
- });
-});
-
-const testPage = async (page: E2EPage) => {
- const ionAnimationFinished = await page.spyOnEvent('ionAnimationFinished');
-
- await page.click('.play');
-
- await ionAnimationFinished.next();
-};
diff --git a/core/src/utils/animation/test/basic/animation.e2e.ts b/core/src/utils/animation/test/basic/animation.e2e.ts
new file mode 100644
index 0000000000..959218b3b2
--- /dev/null
+++ b/core/src/utils/animation/test/basic/animation.e2e.ts
@@ -0,0 +1,24 @@
+import { configs, test } from '@utils/test/playwright';
+import type { E2EPage } from '@utils/test/playwright';
+
+configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
+ test.describe(title('animation: basic'), async () => {
+ test(`should resolve using web animations`, async ({ page }) => {
+ await page.goto('/src/utils/animation/test/basic', config);
+ await testPage(page);
+ });
+
+ test(`should resolve using css animations`, async ({ page }) => {
+ await page.goto('/src/utils/animation/test/basic?ionic:_forceCSSAnimations=true', config);
+ await testPage(page);
+ });
+ });
+});
+
+const testPage = async (page: E2EPage) => {
+ const ionAnimationFinished = await page.spyOnEvent('ionAnimationFinished');
+
+ await page.click('.play');
+
+ await ionAnimationFinished.next();
+};
diff --git a/core/src/utils/animation/test/display/animation.e2e-legacy.ts b/core/src/utils/animation/test/display/animation.e2e.ts
similarity index 55%
rename from core/src/utils/animation/test/display/animation.e2e-legacy.ts
rename to core/src/utils/animation/test/display/animation.e2e.ts
index d5f2b1bd3b..2f123854aa 100644
--- a/core/src/utils/animation/test/display/animation.e2e-legacy.ts
+++ b/core/src/utils/animation/test/display/animation.e2e.ts
@@ -1,21 +1,18 @@
import { expect } from '@playwright/test';
-import { test } from '@utils/test/playwright';
+import { configs, test } from '@utils/test/playwright';
import type { E2EPage } from '@utils/test/playwright';
-test.describe('animation: display', async () => {
- test.beforeEach(({ skip }) => {
- skip.rtl();
- skip.mode('ios');
- });
+configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
+ test.describe(title('animation: display'), async () => {
+ test(`should resolve using web animations`, async ({ page }) => {
+ await page.goto('/src/utils/animation/test/display', config);
+ await testDisplay(page);
+ });
- test(`should resolve using web animations`, async ({ page }) => {
- await page.goto('/src/utils/animation/test/display');
- await testDisplay(page);
- });
-
- test(`should resolve using css animations`, async ({ page }) => {
- await page.goto('/src/utils/animation/test/display?ionic:_forceCSSAnimations=true');
- await testDisplay(page);
+ test(`should resolve using css animations`, async ({ page }) => {
+ await page.goto('/src/utils/animation/test/display?ionic:_forceCSSAnimations=true', config);
+ await testDisplay(page);
+ });
});
});
diff --git a/core/src/utils/animation/test/hooks/animation.e2e-legacy.ts b/core/src/utils/animation/test/hooks/animation.e2e.ts
similarity index 73%
rename from core/src/utils/animation/test/hooks/animation.e2e-legacy.ts
rename to core/src/utils/animation/test/hooks/animation.e2e.ts
index be714b04c0..47c386ef59 100644
--- a/core/src/utils/animation/test/hooks/animation.e2e-legacy.ts
+++ b/core/src/utils/animation/test/hooks/animation.e2e.ts
@@ -1,21 +1,18 @@
import { expect } from '@playwright/test';
-import { test } from '@utils/test/playwright';
+import { configs, test } from '@utils/test/playwright';
import type { E2EPage } from '@utils/test/playwright';
-test.describe('animation: hooks', async () => {
- test.beforeEach(({ skip }) => {
- skip.rtl();
- skip.mode('ios');
- });
+configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
+ test.describe(title('animation: hooks'), async () => {
+ test(`should fire hooks using web animations`, async ({ page }) => {
+ await page.goto('/src/utils/animation/test/hooks', config);
+ await testHooks(page);
+ });
- test(`should fire hooks using web animations`, async ({ page }) => {
- await page.goto('/src/utils/animation/test/hooks');
- await testHooks(page);
- });
-
- test(`should fire hooks using css animations`, async ({ page }) => {
- await page.goto('/src/utils/animation/test/hooks?ionic:_forceCSSAnimations=true');
- await testHooks(page);
+ test(`should fire hooks using css animations`, async ({ page }) => {
+ await page.goto('/src/utils/animation/test/hooks?ionic:_forceCSSAnimations=true', config);
+ await testHooks(page);
+ });
});
});
diff --git a/core/src/utils/animation/test/multiple/animation.e2e-legacy.ts b/core/src/utils/animation/test/multiple/animation.e2e.ts
similarity index 60%
rename from core/src/utils/animation/test/multiple/animation.e2e-legacy.ts
rename to core/src/utils/animation/test/multiple/animation.e2e.ts
index a2772e04ee..5db1fbe38b 100644
--- a/core/src/utils/animation/test/multiple/animation.e2e-legacy.ts
+++ b/core/src/utils/animation/test/multiple/animation.e2e.ts
@@ -1,24 +1,21 @@
import { expect } from '@playwright/test';
-import { test } from '@utils/test/playwright';
+import { configs, test } from '@utils/test/playwright';
import type { E2EPage } from '@utils/test/playwright';
-test.describe('animation: multiple', async () => {
- test.beforeEach(({ skip }) => {
- skip.rtl();
- skip.mode('ios');
- });
+configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
+ test.describe(title('animation: multiple'), async () => {
+ test(`should resolve grouped animations using web animations`, async ({ page }) => {
+ await page.goto('/src/utils/animation/test/multiple', config);
+ await testMultiple(page);
+ });
- test(`should resolve grouped animations using web animations`, async ({ page }) => {
- await page.goto('/src/utils/animation/test/multiple');
- await testMultiple(page);
- });
-
- /**
- * CSS animations will occasionally resolve out of order, so we skip for now
- */
- test.skip(`should resolve grouped animations using css animations`, async ({ page }) => {
- await page.goto('/src/utils/animation/test/multiple?ionic:_forceCSSAnimations=true');
- await testMultiple(page);
+ /**
+ * CSS animations will occasionally resolve out of order, so we skip for now
+ */
+ test.skip(`should resolve grouped animations using css animations`, async ({ page }) => {
+ await page.goto('/src/utils/animation/test/multiple?ionic:_forceCSSAnimations=true', config);
+ await testMultiple(page);
+ });
});
});
diff --git a/core/src/utils/input-shims/hacks/test/scroll-assist.e2e-legacy.ts b/core/src/utils/input-shims/hacks/test/scroll-assist.e2e-legacy.ts
deleted file mode 100644
index 693429e758..0000000000
--- a/core/src/utils/input-shims/hacks/test/scroll-assist.e2e-legacy.ts
+++ /dev/null
@@ -1,180 +0,0 @@
-import { expect } from '@playwright/test';
-import type { Locator } from '@playwright/test';
-import { KeyboardResize } from '@utils/native/keyboard';
-import type { E2EPage } from '@utils/test/playwright';
-import { test } from '@utils/test/playwright';
-
-const getScrollPosition = async (contentEl: Locator) => {
- return await contentEl.evaluate(async (el: HTMLIonContentElement) => {
- const scrollEl = await el.getScrollElement();
-
- return scrollEl.scrollTop;
- });
-};
-
-// TODO FW-3427
-test.describe.skip('scroll-assist', () => {
- let scrollAssistFixture: ScrollAssistFixture;
- test.beforeEach(async ({ page, skip }) => {
- test.slow();
- skip.rtl();
- skip.mode('md', 'Scroll utils are only needed on iOS mode');
- skip.browser('firefox');
- skip.browser('chromium');
-
- scrollAssistFixture = new ScrollAssistFixture(page);
- });
-
- test.describe('scroll-assist: basic functionality', () => {
- test.beforeEach(async () => {
- await scrollAssistFixture.goto();
- });
- test('should not activate when input is above the keyboard', async () => {
- await scrollAssistFixture.expectNotToHaveScrollAssist(
- '#input-above-keyboard',
- '#input-above-keyboard input:not(.cloned-input)'
- );
- });
-
- test('should activate when input is below the keyboard', async () => {
- await scrollAssistFixture.expectToHaveScrollAssist(
- '#input-below-keyboard',
- '#input-below-keyboard input:not(.cloned-input)'
- );
- });
-
- test('should activate even when not explicitly tapping input', async () => {
- await scrollAssistFixture.expectToHaveScrollAssist(
- '#item-below-keyboard ion-label',
- '#input-below-keyboard input:not(.cloned-input)'
- );
- });
- });
- test.describe('scroll-assist: scroll-padding', () => {
- test.describe('scroll-padding: browser/cordova', () => {
- test.beforeEach(async () => {
- await scrollAssistFixture.goto();
- });
- test('should add scroll padding for an input at the bottom of the scroll container', async () => {
- await scrollAssistFixture.expectToHaveScrollPadding(
- '#input-outside-viewport',
- '#input-outside-viewport input:not(.cloned-input)'
- );
- });
-
- test('should keep scroll padding even when switching between inputs', async () => {
- await scrollAssistFixture.expectToHaveScrollPadding(
- '#input-outside-viewport',
- '#input-outside-viewport input:not(.cloned-input)'
- );
-
- await scrollAssistFixture.expectToHaveScrollPadding(
- '#textarea-outside-viewport',
- '#textarea-outside-viewport textarea:not(.cloned-input)'
- );
- });
- });
- test.describe('scroll-padding: webview resizing', () => {
- test('should add scroll padding when webview resizing is "none"', async () => {
- await scrollAssistFixture.goto(KeyboardResize.None);
-
- await scrollAssistFixture.expectToHaveScrollPadding(
- '#input-outside-viewport',
- '#input-outside-viewport input:not(.cloned-input)'
- );
- });
- test('should not add scroll padding when webview resizing is "body"', async () => {
- await scrollAssistFixture.goto(KeyboardResize.Body);
-
- await scrollAssistFixture.expectNotToHaveScrollPadding(
- '#input-outside-viewport',
- '#input-outside-viewport input:not(.cloned-input)'
- );
- });
- test('should not add scroll padding when webview resizing is "ionic"', async () => {
- await scrollAssistFixture.goto(KeyboardResize.Ionic);
-
- await scrollAssistFixture.expectNotToHaveScrollPadding(
- '#input-outside-viewport',
- '#input-outside-viewport input:not(.cloned-input)'
- );
- });
- test('should not add scroll padding when webview resizing is "native"', async () => {
- await scrollAssistFixture.goto(KeyboardResize.Native);
-
- await scrollAssistFixture.expectNotToHaveScrollPadding(
- '#input-outside-viewport',
- '#input-outside-viewport input:not(.cloned-input)'
- );
- });
- });
- });
-});
-
-class ScrollAssistFixture {
- readonly page: E2EPage;
- private content!: Locator;
-
- constructor(page: E2EPage) {
- this.page = page;
- }
-
- async goto(resizeMode?: KeyboardResize) {
- let url = `/src/utils/input-shims/hacks/test`;
- if (resizeMode !== undefined) {
- url += `?resizeMode=${resizeMode}`;
- }
-
- await this.page.goto(url);
-
- this.content = this.page.locator('ion-content');
- }
-
- private async focusInput(interactiveSelector: string, inputSelector: string) {
- const { page } = this;
- const interactive = page.locator(interactiveSelector);
- const input = page.locator(inputSelector);
-
- await interactive.click({ force: true });
- await expect(input).toBeFocused();
- await page.waitForChanges();
- }
-
- private getScrollPosition() {
- const { content } = this;
-
- return getScrollPosition(content);
- }
-
- async expectNotToHaveScrollAssist(interactiveSelector: string, inputSelector: string) {
- await expect(await this.getScrollPosition()).toBe(0);
-
- await this.focusInput(interactiveSelector, inputSelector);
-
- await expect(await this.getScrollPosition()).toBe(0);
- }
-
- async expectToHaveScrollAssist(interactiveSelector: string, inputSelector: string) {
- await expect(await this.getScrollPosition()).toBe(0);
-
- await this.focusInput(interactiveSelector, inputSelector);
-
- await expect(await this.getScrollPosition()).not.toBe(0);
- }
-
- async expectToHaveScrollPadding(interactiveSelector: string, inputSelector: string) {
- const { content } = this;
-
- await this.focusInput(interactiveSelector, inputSelector);
-
- await expect(content).not.toHaveCSS('--keyboard-offset', '0px');
- }
-
- async expectNotToHaveScrollPadding(interactiveSelector: string, inputSelector: string) {
- const { content } = this;
-
- await this.focusInput(interactiveSelector, inputSelector);
-
- await expect(content).toHaveCSS('--keyboard-offset', '0px');
- }
-}
diff --git a/core/src/utils/input-shims/hacks/test/scroll-assist.e2e.ts b/core/src/utils/input-shims/hacks/test/scroll-assist.e2e.ts
new file mode 100644
index 0000000000..9edfc45e7d
--- /dev/null
+++ b/core/src/utils/input-shims/hacks/test/scroll-assist.e2e.ts
@@ -0,0 +1,180 @@
+import { expect } from '@playwright/test';
+import type { Locator } from '@playwright/test';
+import { KeyboardResize } from '@utils/native/keyboard';
+import type { E2EPage, E2EPageOptions } from '@utils/test/playwright';
+import { configs, test } from '@utils/test/playwright';
+
+const getScrollPosition = async (contentEl: Locator) => {
+ return await contentEl.evaluate(async (el: HTMLIonContentElement) => {
+ const scrollEl = await el.getScrollElement();
+
+ return scrollEl.scrollTop;
+ });
+};
+
+// TODO FW-3427
+configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
+ test.describe.skip(title('scroll-assist'), () => {
+ let scrollAssistFixture: ScrollAssistFixture;
+ test.beforeEach(async ({ page, skip }) => {
+ test.slow();
+ skip.browser('firefox');
+ skip.browser('chromium');
+
+ scrollAssistFixture = new ScrollAssistFixture(page);
+ });
+
+ test.describe('scroll-assist: basic functionality', () => {
+ test.beforeEach(async () => {
+ await scrollAssistFixture.goto(config);
+ });
+ test('should not activate when input is above the keyboard', async () => {
+ await scrollAssistFixture.expectNotToHaveScrollAssist(
+ '#input-above-keyboard',
+ '#input-above-keyboard input:not(.cloned-input)'
+ );
+ });
+
+ test('should activate when input is below the keyboard', async () => {
+ await scrollAssistFixture.expectToHaveScrollAssist(
+ '#input-below-keyboard',
+ '#input-below-keyboard input:not(.cloned-input)'
+ );
+ });
+
+ test('should activate even when not explicitly tapping input', async () => {
+ await scrollAssistFixture.expectToHaveScrollAssist(
+ '#item-below-keyboard ion-label',
+ '#input-below-keyboard input:not(.cloned-input)'
+ );
+ });
+ });
+ test.describe('scroll-assist: scroll-padding', () => {
+ test.describe('scroll-padding: browser/cordova', () => {
+ test.beforeEach(async () => {
+ await scrollAssistFixture.goto(config);
+ });
+ test('should add scroll padding for an input at the bottom of the scroll container', async () => {
+ await scrollAssistFixture.expectToHaveScrollPadding(
+ '#input-outside-viewport',
+ '#input-outside-viewport input:not(.cloned-input)'
+ );
+ });
+
+ test('should keep scroll padding even when switching between inputs', async () => {
+ await scrollAssistFixture.expectToHaveScrollPadding(
+ '#input-outside-viewport',
+ '#input-outside-viewport input:not(.cloned-input)'
+ );
+
+ await scrollAssistFixture.expectToHaveScrollPadding(
+ '#textarea-outside-viewport',
+ '#textarea-outside-viewport textarea:not(.cloned-input)'
+ );
+ });
+ });
+ test.describe('scroll-padding: webview resizing', () => {
+ test('should add scroll padding when webview resizing is "none"', async () => {
+ await scrollAssistFixture.goto(config, KeyboardResize.None);
+
+ await scrollAssistFixture.expectToHaveScrollPadding(
+ '#input-outside-viewport',
+ '#input-outside-viewport input:not(.cloned-input)'
+ );
+ });
+ test('should not add scroll padding when webview resizing is "body"', async () => {
+ await scrollAssistFixture.goto(config, KeyboardResize.Body);
+
+ await scrollAssistFixture.expectNotToHaveScrollPadding(
+ '#input-outside-viewport',
+ '#input-outside-viewport input:not(.cloned-input)'
+ );
+ });
+ test('should not add scroll padding when webview resizing is "ionic"', async () => {
+ await scrollAssistFixture.goto(config, KeyboardResize.Ionic);
+
+ await scrollAssistFixture.expectNotToHaveScrollPadding(
+ '#input-outside-viewport',
+ '#input-outside-viewport input:not(.cloned-input)'
+ );
+ });
+ test('should not add scroll padding when webview resizing is "native"', async () => {
+ await scrollAssistFixture.goto(config, KeyboardResize.Native);
+
+ await scrollAssistFixture.expectNotToHaveScrollPadding(
+ '#input-outside-viewport',
+ '#input-outside-viewport input:not(.cloned-input)'
+ );
+ });
+ });
+ });
+ });
+});
+
+class ScrollAssistFixture {
+ readonly page: E2EPage;
+ private content!: Locator;
+
+ constructor(page: E2EPage) {
+ this.page = page;
+ }
+
+ async goto(config: E2EPageOptions, resizeMode?: KeyboardResize) {
+ let url = `/src/utils/input-shims/hacks/test`;
+ if (resizeMode !== undefined) {
+ url += `?resizeMode=${resizeMode}`;
+ }
+
+ await this.page.goto(url, config);
+
+ this.content = this.page.locator('ion-content');
+ }
+
+ private async focusInput(interactiveSelector: string, inputSelector: string) {
+ const { page } = this;
+ const interactive = page.locator(interactiveSelector);
+ const input = page.locator(inputSelector);
+
+ await interactive.click({ force: true });
+ await expect(input).toBeFocused();
+ await page.waitForChanges();
+ }
+
+ private getScrollPosition() {
+ const { content } = this;
+
+ return getScrollPosition(content);
+ }
+
+ async expectNotToHaveScrollAssist(interactiveSelector: string, inputSelector: string) {
+ await expect(await this.getScrollPosition()).toBe(0);
+
+ await this.focusInput(interactiveSelector, inputSelector);
+
+ await expect(await this.getScrollPosition()).toBe(0);
+ }
+
+ async expectToHaveScrollAssist(interactiveSelector: string, inputSelector: string) {
+ await expect(await this.getScrollPosition()).toBe(0);
+
+ await this.focusInput(interactiveSelector, inputSelector);
+
+ await expect(await this.getScrollPosition()).not.toBe(0);
+ }
+
+ async expectToHaveScrollPadding(interactiveSelector: string, inputSelector: string) {
+ const { content } = this;
+
+ await this.focusInput(interactiveSelector, inputSelector);
+
+ await expect(content).not.toHaveCSS('--keyboard-offset', '0px');
+ }
+
+ async expectNotToHaveScrollPadding(interactiveSelector: string, inputSelector: string) {
+ const { content } = this;
+
+ await this.focusInput(interactiveSelector, inputSelector);
+
+ await expect(content).toHaveCSS('--keyboard-offset', '0px');
+ }
+}
diff --git a/core/src/utils/tap-click/test/tap-click.e2e-legacy.ts b/core/src/utils/tap-click/test/tap-click.e2e-legacy.ts
deleted file mode 100644
index e027eec7a9..0000000000
--- a/core/src/utils/tap-click/test/tap-click.e2e-legacy.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { expect } from '@playwright/test';
-import { test } from '@utils/test/playwright';
-
-// TODO FW-3010
-test.describe.skip('tap click utility', () => {
- test.beforeEach(({ skip }) => {
- skip.rtl();
- skip.mode('ios');
- });
- test('it should apply activated class when clicking element', async ({ page }) => {
- await page.setContent(`
-
-
-
- `);
-
- const button = page.locator('button');
- const box = await button.boundingBox()!;
-
- if (box) {
- await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
- await page.mouse.down();
- await page.waitForChanges();
- }
-
- await expect(button).toHaveClass(/ion-activated/);
- });
-});
diff --git a/core/src/utils/tap-click/test/tap-click.e2e.ts b/core/src/utils/tap-click/test/tap-click.e2e.ts
new file mode 100644
index 0000000000..1827e27d6a
--- /dev/null
+++ b/core/src/utils/tap-click/test/tap-click.e2e.ts
@@ -0,0 +1,30 @@
+import { expect } from '@playwright/test';
+import { configs, test } from '@utils/test/playwright';
+
+// TODO FW-3010
+
+configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
+ test.describe.skip(title('tap click utility'), () => {
+ test('it should apply activated class when clicking element', async ({ page }) => {
+ await page.setContent(
+ `
+
+
+
+ `,
+ config
+ );
+
+ const button = page.locator('button');
+ const box = await button.boundingBox()!;
+
+ if (box) {
+ await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
+ await page.mouse.down();
+ await page.waitForChanges();
+ }
+
+ await expect(button).toHaveClass(/ion-activated/);
+ });
+ });
+});
diff --git a/core/src/utils/test/framework-delegate/framework-delegate.e2e-legacy.ts b/core/src/utils/test/framework-delegate/framework-delegate.e2e-legacy.ts
deleted file mode 100644
index 435f7246d0..0000000000
--- a/core/src/utils/test/framework-delegate/framework-delegate.e2e-legacy.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { expect } from '@playwright/test';
-import { test } from '@utils/test/playwright';
-
-test.describe('framework-delegate', () => {
- test.beforeEach(async ({ page, skip }) => {
- skip.rtl();
- skip.mode('ios');
-
- await page.goto('/src/utils/test/framework-delegate');
- });
- test('should present modal already at ion-app root', async ({ page }) => {
- const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
-
- await page.click('#button-inline-root');
-
- const modal = page.locator('#inline-root');
- await ionModalDidPresent.next();
- await expect(modal).toBeVisible();
- });
-
- test('should present modal in content', async ({ page }) => {
- const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
-
- await page.click('#button-inline-content');
-
- const modal = page.locator('#inline-content');
- await ionModalDidPresent.next();
- await expect(modal).toBeVisible();
- });
-
- test('should present modal via controller', async ({ page }) => {
- const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
-
- await page.click('#button-controller');
-
- const modal = page.locator('#controller');
- await ionModalDidPresent.next();
- await expect(modal).toBeVisible();
- });
-});
diff --git a/core/src/utils/test/framework-delegate/framework-delegate.e2e.ts b/core/src/utils/test/framework-delegate/framework-delegate.e2e.ts
new file mode 100644
index 0000000000..32bc44cd1c
--- /dev/null
+++ b/core/src/utils/test/framework-delegate/framework-delegate.e2e.ts
@@ -0,0 +1,39 @@
+import { expect } from '@playwright/test';
+import { configs, test } from '@utils/test/playwright';
+
+configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
+ test.describe(title('framework-delegate'), () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/src/utils/test/framework-delegate', config);
+ });
+ test('should present modal already at ion-app root', async ({ page }) => {
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+
+ await page.click('#button-inline-root');
+
+ const modal = page.locator('#inline-root');
+ await ionModalDidPresent.next();
+ await expect(modal).toBeVisible();
+ });
+
+ test('should present modal in content', async ({ page }) => {
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+
+ await page.click('#button-inline-content');
+
+ const modal = page.locator('#inline-content');
+ await ionModalDidPresent.next();
+ await expect(modal).toBeVisible();
+ });
+
+ test('should present modal via controller', async ({ page }) => {
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+
+ await page.click('#button-controller');
+
+ const modal = page.locator('#controller');
+ await ionModalDidPresent.next();
+ await expect(modal).toBeVisible();
+ });
+ });
+});
diff --git a/core/src/utils/test/overlays/overlays.e2e-legacy.ts b/core/src/utils/test/overlays/overlays.e2e-legacy.ts
deleted file mode 100644
index 440bdaf897..0000000000
--- a/core/src/utils/test/overlays/overlays.e2e-legacy.ts
+++ /dev/null
@@ -1,254 +0,0 @@
-import { expect } from '@playwright/test';
-import { test } from '@utils/test/playwright';
-
-test.describe('overlays: dismiss', () => {
- test.beforeEach(async ({ page, skip }) => {
- skip.rtl();
-
- await page.goto('/src/utils/test/overlays');
- });
- test('hardware back button: should dismiss a presented overlay', async ({ page }) => {
- const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
- const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
-
- await page.click('#create-and-present');
-
- await ionModalDidPresent.next();
-
- await page.click('#modal-simulate');
-
- await ionModalDidDismiss.next();
- });
-
- test('hardware back button: should dismiss the presented overlay, even though another hidden modal was added last', async ({
- page,
- }) => {
- const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
- const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
-
- await page.click('#create-and-present');
-
- await ionModalDidPresent.next();
-
- await page.click('#modal-create');
-
- const modals = page.locator('ion-modal');
- await expect(await modals.count()).toEqual(2);
-
- await expect(await modals.nth(0)).not.toHaveClass(/overlay-hidden/);
- await expect(await modals.nth(1)).toHaveClass(/overlay-hidden/);
-
- await page.click('#modal-simulate');
-
- await ionModalDidDismiss.next();
-
- await expect(await modals.count()).toEqual(1);
- await expect(await modals.nth(0)).toHaveClass(/overlay-hidden/);
- });
-
- test('Esc: should dismiss a presented overlay', async ({ page }) => {
- const createAndPresentButton = page.locator('#create-and-present');
-
- const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
- const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
-
- await createAndPresentButton.click();
-
- await ionModalDidPresent.next();
-
- await page.keyboard.press('Escape');
-
- await ionModalDidDismiss.next();
- });
-
- test('Esc: should dismiss the presented overlay, even though another hidden modal was added last', async ({
- page,
- }) => {
- const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
- const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
-
- await page.click('#create-and-present');
-
- await ionModalDidPresent.next();
-
- await page.click('#modal-create');
-
- const modals = page.locator('ion-modal');
- await expect(await modals.count()).toEqual(2);
-
- await expect(await modals.nth(0)).not.toHaveClass(/overlay-hidden/);
- await expect(await modals.nth(1)).toHaveClass(/overlay-hidden/);
-
- await page.keyboard.press('Escape');
-
- await ionModalDidDismiss.next();
-
- await expect(await modals.count()).toEqual(1);
- await expect(await modals.nth(0)).toHaveClass(/overlay-hidden/);
- });
-
- test('overlays: Nested: should dismiss the top overlay', async ({ page }) => {
- const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
- const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
-
- await page.click('#create-nested');
-
- await ionModalDidPresent.next();
-
- await page.click('#dismiss-modal-nested-overlay');
-
- await ionModalDidDismiss.next();
-
- const modals = page.locator('ion-modal');
- expect(await modals.count()).toEqual(0);
- });
-});
-
-// TODO FW-3536
-test.describe.skip('overlays: focus', () => {
- test.beforeEach(({ skip }) => {
- skip.rtl();
- });
-
- test('should not select a hidden focusable element', async ({ page, browserName }) => {
- await page.setContent(`
-
-
- Show Modal
-
-
- Hidden Button
- Visible Button
-
-
- `);
-
- const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
- const presentButton = page.locator('ion-button#open-modal');
- const visibleButton = page.locator('ion-button#visible');
- const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
-
- await presentButton.click();
- await ionModalDidPresent.next();
-
- await page.keyboard.press(tabKey);
- await expect(visibleButton).toBeFocused();
-
- await page.keyboard.press(tabKey);
- await expect(visibleButton).toBeFocused();
- });
-
- test('should not select a disabled focusable element', async ({ page, browserName }) => {
- await page.setContent(`
- Show Modal
-
-
- Button
- Active Button
-
-
- `);
-
- const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
- const presentButton = page.locator('ion-button#open-modal');
- const activeButton = page.locator('ion-button#active');
- const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
-
- await presentButton.click();
- await ionModalDidPresent.next();
-
- await page.keyboard.press(tabKey);
- await expect(activeButton).toBeFocused();
-
- await page.keyboard.press(tabKey);
- await expect(activeButton).toBeFocused();
- });
-
- test('should select a focusable element with disabled="false"', async ({ page, browserName }) => {
- await page.setContent(`
- Show Modal
-
-
- Button
- Active Button
-
-
- `);
-
- const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
- const presentButton = page.locator('ion-button#open-modal');
- const disabledFalseButton = page.locator('ion-button#disabled-false');
- const activeButton = page.locator('ion-button#active');
- const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
-
- await presentButton.click();
- await ionModalDidPresent.next();
-
- await page.keyboard.press(tabKey);
- await expect(disabledFalseButton).toBeFocused();
-
- await page.keyboard.press(tabKey);
- await expect(activeButton).toBeFocused();
-
- // Loop back to beginning of overlay
- await page.keyboard.press(tabKey);
- await expect(disabledFalseButton).toBeFocused();
- });
-
- test('toast should not cause focus trapping', async ({ page }) => {
- await page.goto('/src/utils/test/overlays');
- const ionToastDidPresent = await page.spyOnEvent('ionToastDidPresent');
-
- await page.click('#create-and-present-toast');
- await ionToastDidPresent.next();
-
- const input = page.locator('#root-input input');
- await input.click();
-
- await expect(input).toBeFocused();
- });
-
- test('toast should not cause focus trapping even when opened from a focus trapping overlay', async ({ page }) => {
- await page.goto('/src/utils/test/overlays');
-
- const ionToastDidPresent = await page.spyOnEvent('ionToastDidPresent');
- const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
-
- await page.click('#create-and-present');
- await ionModalDidPresent.next();
-
- await page.click('#modal-toast');
- await ionToastDidPresent.next();
-
- const modalInput = page.locator('.modal-input input');
- await modalInput.click();
-
- await expect(modalInput).toBeFocused();
- });
-
- test('focus trapping should only run on the top-most overlay', async ({ page }) => {
- await page.goto('/src/utils/test/overlays');
-
- const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
-
- await page.click('#create-and-present');
- await ionModalDidPresent.next();
-
- const modalInputZero = page.locator('.modal-0 .modal-input input');
- await modalInputZero.click();
-
- await expect(modalInputZero).toBeFocused();
-
- await page.click('#modal-create-and-present');
- await ionModalDidPresent.next();
-
- const modalInputOne = page.locator('.modal-1 .modal-input input');
- await modalInputOne.click();
-
- await expect(modalInputOne).toBeFocused();
- });
-});
diff --git a/core/src/utils/test/overlays/overlays.e2e.ts b/core/src/utils/test/overlays/overlays.e2e.ts
new file mode 100644
index 0000000000..5de399fb41
--- /dev/null
+++ b/core/src/utils/test/overlays/overlays.e2e.ts
@@ -0,0 +1,262 @@
+import { expect } from '@playwright/test';
+import { configs, test } from '@utils/test/playwright';
+
+/**
+ * This behavior does not vary across modes/directions.
+ */
+configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
+ test.describe(title('overlays: dismiss'), () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/src/utils/test/overlays', config);
+ });
+ test('hardware back button: should dismiss a presented overlay', async ({ page }) => {
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+ const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
+
+ await page.click('#create-and-present');
+
+ await ionModalDidPresent.next();
+
+ await page.click('#modal-simulate');
+
+ await ionModalDidDismiss.next();
+ });
+
+ test('hardware back button: should dismiss the presented overlay, even though another hidden modal was added last', async ({
+ page,
+ }) => {
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+ const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
+
+ await page.click('#create-and-present');
+
+ await ionModalDidPresent.next();
+
+ await page.click('#modal-create');
+
+ const modals = page.locator('ion-modal');
+ await expect(await modals.count()).toEqual(2);
+
+ await expect(await modals.nth(0)).not.toHaveClass(/overlay-hidden/);
+ await expect(await modals.nth(1)).toHaveClass(/overlay-hidden/);
+
+ await page.click('#modal-simulate');
+
+ await ionModalDidDismiss.next();
+
+ await expect(await modals.count()).toEqual(1);
+ await expect(await modals.nth(0)).toHaveClass(/overlay-hidden/);
+ });
+
+ test('Esc: should dismiss a presented overlay', async ({ page }) => {
+ const createAndPresentButton = page.locator('#create-and-present');
+
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+ const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
+
+ await createAndPresentButton.click();
+
+ await ionModalDidPresent.next();
+
+ await page.keyboard.press('Escape');
+
+ await ionModalDidDismiss.next();
+ });
+
+ test('Esc: should dismiss the presented overlay, even though another hidden modal was added last', async ({
+ page,
+ }) => {
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+ const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
+
+ await page.click('#create-and-present');
+
+ await ionModalDidPresent.next();
+
+ await page.click('#modal-create');
+
+ const modals = page.locator('ion-modal');
+ await expect(await modals.count()).toEqual(2);
+
+ await expect(await modals.nth(0)).not.toHaveClass(/overlay-hidden/);
+ await expect(await modals.nth(1)).toHaveClass(/overlay-hidden/);
+
+ await page.keyboard.press('Escape');
+
+ await ionModalDidDismiss.next();
+
+ await expect(await modals.count()).toEqual(1);
+ await expect(await modals.nth(0)).toHaveClass(/overlay-hidden/);
+ });
+
+ test('overlays: Nested: should dismiss the top overlay', async ({ page }) => {
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+ const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
+
+ await page.click('#create-nested');
+
+ await ionModalDidPresent.next();
+
+ await page.click('#dismiss-modal-nested-overlay');
+
+ await ionModalDidDismiss.next();
+
+ const modals = page.locator('ion-modal');
+ expect(await modals.count()).toEqual(0);
+ });
+ });
+
+ // TODO FW-3536
+ test.describe.skip(title('overlays: focus'), () => {
+ test('should not select a hidden focusable element', async ({ page, browserName }) => {
+ await page.setContent(
+ `
+
+
+ Show Modal
+
+
+ Hidden Button
+ Visible Button
+
+
+ `,
+ config
+ );
+
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+ const presentButton = page.locator('ion-button#open-modal');
+ const visibleButton = page.locator('ion-button#visible');
+ const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
+
+ await presentButton.click();
+ await ionModalDidPresent.next();
+
+ await page.keyboard.press(tabKey);
+ await expect(visibleButton).toBeFocused();
+
+ await page.keyboard.press(tabKey);
+ await expect(visibleButton).toBeFocused();
+ });
+
+ test('should not select a disabled focusable element', async ({ page, browserName }) => {
+ await page.setContent(
+ `
+ Show Modal
+
+
+ Button
+ Active Button
+
+
+ `,
+ config
+ );
+
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+ const presentButton = page.locator('ion-button#open-modal');
+ const activeButton = page.locator('ion-button#active');
+ const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
+
+ await presentButton.click();
+ await ionModalDidPresent.next();
+
+ await page.keyboard.press(tabKey);
+ await expect(activeButton).toBeFocused();
+
+ await page.keyboard.press(tabKey);
+ await expect(activeButton).toBeFocused();
+ });
+
+ test('should select a focusable element with disabled="false"', async ({ page, browserName }) => {
+ await page.setContent(
+ `
+ Show Modal
+
+
+ Button
+ Active Button
+
+
+ `,
+ config
+ );
+
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+ const presentButton = page.locator('ion-button#open-modal');
+ const disabledFalseButton = page.locator('ion-button#disabled-false');
+ const activeButton = page.locator('ion-button#active');
+ const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
+
+ await presentButton.click();
+ await ionModalDidPresent.next();
+
+ await page.keyboard.press(tabKey);
+ await expect(disabledFalseButton).toBeFocused();
+
+ await page.keyboard.press(tabKey);
+ await expect(activeButton).toBeFocused();
+
+ // Loop back to beginning of overlay
+ await page.keyboard.press(tabKey);
+ await expect(disabledFalseButton).toBeFocused();
+ });
+
+ test('toast should not cause focus trapping', async ({ page }) => {
+ await page.goto('/src/utils/test/overlays', config);
+ const ionToastDidPresent = await page.spyOnEvent('ionToastDidPresent');
+
+ await page.click('#create-and-present-toast');
+ await ionToastDidPresent.next();
+
+ const input = page.locator('#root-input input');
+ await input.click();
+
+ await expect(input).toBeFocused();
+ });
+
+ test('toast should not cause focus trapping even when opened from a focus trapping overlay', async ({ page }) => {
+ await page.goto('/src/utils/test/overlays', config);
+
+ const ionToastDidPresent = await page.spyOnEvent('ionToastDidPresent');
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+
+ await page.click('#create-and-present');
+ await ionModalDidPresent.next();
+
+ await page.click('#modal-toast');
+ await ionToastDidPresent.next();
+
+ const modalInput = page.locator('.modal-input input');
+ await modalInput.click();
+
+ await expect(modalInput).toBeFocused();
+ });
+
+ test('focus trapping should only run on the top-most overlay', async ({ page }) => {
+ await page.goto('/src/utils/test/overlays', config);
+
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+
+ await page.click('#create-and-present');
+ await ionModalDidPresent.next();
+
+ const modalInputZero = page.locator('.modal-0 .modal-input input');
+ await modalInputZero.click();
+
+ await expect(modalInputZero).toBeFocused();
+
+ await page.click('#modal-create-and-present');
+ await ionModalDidPresent.next();
+
+ const modalInputOne = page.locator('.modal-1 .modal-input input');
+ await modalInputOne.click();
+
+ await expect(modalInputOne).toBeFocused();
+ });
+ });
+});