diff --git a/core/package-lock.json b/core/package-lock.json index 85fdb6476c..a7a31bcd60 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -15,14 +15,14 @@ }, "devDependencies": { "@axe-core/playwright": "^4.7.3", - "@capacitor/core": "^5.2.2", + "@capacitor/core": "^5.2.3", "@capacitor/haptics": "^5.0.6", "@capacitor/keyboard": "^5.0.6", "@capacitor/status-bar": "^5.0.6", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "^2.0.0", "@jest/core": "^27.5.1", - "@playwright/test": "^1.36.2", + "@playwright/test": "^1.37.0", "@rollup/plugin-node-resolve": "^8.4.0", "@rollup/plugin-virtual": "^2.0.3", "@stencil/angular-output-target": "^0.7.1", @@ -607,9 +607,9 @@ "dev": true }, "node_modules/@capacitor/core": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.2.2.tgz", - "integrity": "sha512-3jKECZC5+YD2rljMZm1e/K3AYyoxUmLDZCyofTPbRYPBSI0wJh5ZCkX+XIGzNM0o/Wokl3Voa1JB8xsLC0MPxA==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.2.3.tgz", + "integrity": "sha512-Q1zbgt3Mvldy7six2/GX54kTL0ozgnR37jeDUAXL/fOJBF4Iorr/8A0OjGEAnwEjpR1la7uFZUunESMFyMLhEQ==", "dev": true, "dependencies": { "tslib": "^2.1.0" @@ -1541,13 +1541,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.36.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.36.2.tgz", - "integrity": "sha512-2rVZeyPRjxfPH6J0oGJqE8YxiM1IBRyM8hyrXYK7eSiAqmbNhxwcLa7dZ7fy9Kj26V7FYia5fh9XJRq4Dqme+g==", + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.37.0.tgz", + "integrity": "sha512-181WBLk4SRUyH1Q96VZl7BP6HcK0b7lbdeKisn3N/vnjitk+9HbdlFz/L5fey05vxaAhldIDnzo8KUoy8S3mmQ==", "dev": true, "dependencies": { "@types/node": "*", - "playwright-core": "1.36.2" + "playwright-core": "1.37.0" }, "bin": { "playwright": "cli.js" @@ -8194,9 +8194,9 @@ } }, "node_modules/playwright-core": { - "version": "1.36.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.36.2.tgz", - "integrity": "sha512-sQYZt31dwkqxOrP7xy2ggDfEzUxM1lodjhsQ3NMMv5uGTRDsLxU0e4xf4wwMkF2gplIxf17QMBCodSFgm6bFVQ==", + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.0.tgz", + "integrity": "sha512-1c46jhTH/myQw6sesrcuHVtLoSNfJv8Pfy9t3rs6subY7kARv0HRw5PpyfPYPpPtQvBOmgbE6K+qgYUpj81LAA==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -10788,9 +10788,9 @@ "dev": true }, "@capacitor/core": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.2.2.tgz", - "integrity": "sha512-3jKECZC5+YD2rljMZm1e/K3AYyoxUmLDZCyofTPbRYPBSI0wJh5ZCkX+XIGzNM0o/Wokl3Voa1JB8xsLC0MPxA==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.2.3.tgz", + "integrity": "sha512-Q1zbgt3Mvldy7six2/GX54kTL0ozgnR37jeDUAXL/fOJBF4Iorr/8A0OjGEAnwEjpR1la7uFZUunESMFyMLhEQ==", "dev": true, "requires": { "tslib": "^2.1.0" @@ -11455,14 +11455,14 @@ } }, "@playwright/test": { - "version": "1.36.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.36.2.tgz", - "integrity": "sha512-2rVZeyPRjxfPH6J0oGJqE8YxiM1IBRyM8hyrXYK7eSiAqmbNhxwcLa7dZ7fy9Kj26V7FYia5fh9XJRq4Dqme+g==", + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.37.0.tgz", + "integrity": "sha512-181WBLk4SRUyH1Q96VZl7BP6HcK0b7lbdeKisn3N/vnjitk+9HbdlFz/L5fey05vxaAhldIDnzo8KUoy8S3mmQ==", "dev": true, "requires": { "@types/node": "*", "fsevents": "2.3.2", - "playwright-core": "1.36.2" + "playwright-core": "1.37.0" } }, "@rollup/plugin-node-resolve": { @@ -16332,9 +16332,9 @@ } }, "playwright-core": { - "version": "1.36.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.36.2.tgz", - "integrity": "sha512-sQYZt31dwkqxOrP7xy2ggDfEzUxM1lodjhsQ3NMMv5uGTRDsLxU0e4xf4wwMkF2gplIxf17QMBCodSFgm6bFVQ==", + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.0.tgz", + "integrity": "sha512-1c46jhTH/myQw6sesrcuHVtLoSNfJv8Pfy9t3rs6subY7kARv0HRw5PpyfPYPpPtQvBOmgbE6K+qgYUpj81LAA==", "dev": true }, "postcss": { diff --git a/core/package.json b/core/package.json index fcc33dc909..119ee5a696 100644 --- a/core/package.json +++ b/core/package.json @@ -37,14 +37,14 @@ }, "devDependencies": { "@axe-core/playwright": "^4.7.3", - "@capacitor/core": "^5.2.2", + "@capacitor/core": "^5.2.3", "@capacitor/haptics": "^5.0.6", "@capacitor/keyboard": "^5.0.6", "@capacitor/status-bar": "^5.0.6", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "^2.0.0", "@jest/core": "^27.5.1", - "@playwright/test": "^1.36.2", + "@playwright/test": "^1.37.0", "@rollup/plugin-node-resolve": "^8.4.0", "@rollup/plugin-virtual": "^2.0.3", "@stencil/angular-output-target": "^0.7.1", diff --git a/core/playwright.config.ts b/core/playwright.config.ts index eab4388588..1fa886c512 100644 --- a/core/playwright.config.ts +++ b/core/playwright.config.ts @@ -62,14 +62,16 @@ const config: PlaywrightTestConfig = { }, /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, - /* Fail fast on CI */ - maxFailures: process.env.CI ? 1 : 0, - /* Flaky test should be either addressed or disabled until we can address them */ - retries: 0, + maxFailures: 0, + retries: 2, + reportSlowTests: null, /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', + reporter: [ + ['html'], + ['github'] + ], /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 7a0c2638ba..1cd7c27747 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -1277,9 +1277,6 @@ export namespace Components { * The shape of the input. If "round" it will have an increased border radius. */ "shape"?: 'round'; - /** - * The initial size of the control. This value is in pixels unless the value of the type attribute is `"text"` or `"password"`, in which case it is an integer number of characters. This attribute applies only when the `type` attribute is set to `"text"`, `"search"`, `"tel"`, `"url"`, `"email"`, or `"password"`, otherwise it is ignored. - */ "size"?: number; /** * If `true`, the element will have its spelling and grammar checked. @@ -5339,9 +5336,6 @@ declare namespace LocalJSX { * The shape of the input. If "round" it will have an increased border radius. */ "shape"?: 'round'; - /** - * The initial size of the control. This value is in pixels unless the value of the type attribute is `"text"` or `"password"`, in which case it is an integer number of characters. This attribute applies only when the `type` attribute is set to `"text"`, `"search"`, `"tel"`, `"url"`, `"email"`, or `"password"`, otherwise it is ignored. - */ "size"?: number; /** * If `true`, the element will have its spelling and grammar checked. diff --git a/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-ltr-Mobile-Chrome-linux.png index d3d0ddfa04..e28a86454d 100644 Binary files a/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-ltr-Mobile-Chrome-linux.png and b/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-ltr-Mobile-Firefox-linux.png index b48397c2f9..e6773e2668 100644 Binary files a/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-ltr-Mobile-Firefox-linux.png and b/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-ltr-Mobile-Safari-linux.png b/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-ltr-Mobile-Safari-linux.png index 981ace8b60..63f85646b2 100644 Binary files a/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-ltr-Mobile-Safari-linux.png and b/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-rtl-Mobile-Chrome-linux.png b/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-rtl-Mobile-Chrome-linux.png index 8e063f6237..738540fa16 100644 Binary files a/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-rtl-Mobile-Chrome-linux.png and b/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-rtl-Mobile-Chrome-linux.png differ diff --git a/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-rtl-Mobile-Firefox-linux.png b/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-rtl-Mobile-Firefox-linux.png index 0bf6037316..fc163fede9 100644 Binary files a/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-rtl-Mobile-Firefox-linux.png and b/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-rtl-Mobile-Firefox-linux.png differ diff --git a/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-rtl-Mobile-Safari-linux.png b/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-rtl-Mobile-Safari-linux.png index 4c16a7b717..566fdb230b 100644 Binary files a/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-rtl-Mobile-Safari-linux.png and b/core/src/components/accordion/test/basic/accordion.e2e.ts-snapshots/accordion-basic-ios-rtl-Mobile-Safari-linux.png differ diff --git a/core/src/components/content/test/standalone/content.e2e.ts-snapshots/content-standalone-md-ltr-Mobile-Safari-linux.png b/core/src/components/content/test/standalone/content.e2e.ts-snapshots/content-standalone-md-ltr-Mobile-Safari-linux.png index 282c9940a0..d1f43a9741 100644 Binary files a/core/src/components/content/test/standalone/content.e2e.ts-snapshots/content-standalone-md-ltr-Mobile-Safari-linux.png and b/core/src/components/content/test/standalone/content.e2e.ts-snapshots/content-standalone-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/header/header.ios.scss b/core/src/components/header/header.ios.scss index 57259ed9ac..7c1211b6af 100644 --- a/core/src/components/header/header.ios.scss +++ b/core/src/components/header/header.ios.scss @@ -51,7 +51,7 @@ } .header-collapse-condense ion-toolbar:first-of-type { - padding-top: 7px; + padding-top: 1px; z-index: 1; } diff --git a/core/src/components/header/test/condense/header.e2e.ts-snapshots/header-condense-large-title-initial-diff-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/header/test/condense/header.e2e.ts-snapshots/header-condense-large-title-initial-diff-ios-ltr-Mobile-Chrome-linux.png index 346d13a9b2..e2beb6ff00 100644 Binary files a/core/src/components/header/test/condense/header.e2e.ts-snapshots/header-condense-large-title-initial-diff-ios-ltr-Mobile-Chrome-linux.png and b/core/src/components/header/test/condense/header.e2e.ts-snapshots/header-condense-large-title-initial-diff-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/header/test/condense/header.e2e.ts-snapshots/header-condense-large-title-initial-diff-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/header/test/condense/header.e2e.ts-snapshots/header-condense-large-title-initial-diff-ios-ltr-Mobile-Firefox-linux.png index 45a3125cf5..846927beb3 100644 Binary files a/core/src/components/header/test/condense/header.e2e.ts-snapshots/header-condense-large-title-initial-diff-ios-ltr-Mobile-Firefox-linux.png and b/core/src/components/header/test/condense/header.e2e.ts-snapshots/header-condense-large-title-initial-diff-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/header/test/condense/header.e2e.ts-snapshots/header-condense-large-title-initial-diff-ios-ltr-Mobile-Safari-linux.png b/core/src/components/header/test/condense/header.e2e.ts-snapshots/header-condense-large-title-initial-diff-ios-ltr-Mobile-Safari-linux.png index f01ab9b071..ccf79ca34f 100644 Binary files a/core/src/components/header/test/condense/header.e2e.ts-snapshots/header-condense-large-title-initial-diff-ios-ltr-Mobile-Safari-linux.png and b/core/src/components/header/test/condense/header.e2e.ts-snapshots/header-condense-large-title-initial-diff-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index edbc963c37..44dec367e4 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -268,9 +268,7 @@ export class Input implements ComponentInterface { */ @Prop() step?: string; - /** - * The initial size of the control. This value is in pixels unless the value of the type attribute is `"text"` or `"password"`, in which case it is an integer number of characters. This attribute applies only when the `type` attribute is set to `"text"`, `"search"`, `"tel"`, `"url"`, `"email"`, or `"password"`, otherwise it is ignored. - */ + // FW-4914 Remove this property in Ionic 8 @Prop() size?: number; /** 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 9a6f4fe1b8..f86f138804 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 @@ -3,7 +3,6 @@ import { configs, dragElementBy, test } from '@utils/test/playwright'; import { testSlidingItem } from '../test.utils'; -// TODO FW-3006 /** * item-sliding doesn't have mode-specific styling */ @@ -18,19 +17,12 @@ configs({ modes: ['md'] }).forEach(({ title, screenshot, config }) => { }); }); -// TODO FW-3006 /** * This behavior does not vary across modes/directions */ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { test.describe(title('item-sliding: basic'), () => { - // mouse gesture is flaky on CI, skip for now - test.fixme('should open when swiped', async ({ page, skip }) => { - skip.browser( - (browserName: string) => browserName !== 'chromium', - 'dragElementBy is flaky outside of Chrome browsers.' - ); - + test('should open when swiped', async ({ page }) => { await page.goto(`/src/components/item-sliding/test/basic`, config); const item = page.locator('#item2'); @@ -41,7 +33,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co await expect(item).toHaveScreenshot(screenshot(`item-sliding-gesture`)); }); - test.skip('should not scroll when the item-sliding is swiped', async ({ page, skip }) => { + test('should not scroll when the item-sliding is swiped', async ({ page, skip }) => { skip.browser('webkit', 'mouse.wheel is not available in WebKit'); await page.goto(`/src/components/item-sliding/test/basic`, config); diff --git a/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts-snapshots/item-sliding-gesture-md-ltr-Mobile-Chrome-linux.png b/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts-snapshots/item-sliding-gesture-md-ltr-Mobile-Chrome-linux.png index 4e547fce98..0ed9dfc4d7 100644 Binary files a/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts-snapshots/item-sliding-gesture-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts-snapshots/item-sliding-gesture-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts-snapshots/item-sliding-gesture-md-ltr-Mobile-Firefox-linux.png b/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts-snapshots/item-sliding-gesture-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000..4c9df49ca7 Binary files /dev/null and b/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts-snapshots/item-sliding-gesture-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts-snapshots/item-sliding-gesture-md-ltr-Mobile-Safari-linux.png b/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts-snapshots/item-sliding-gesture-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000..84b4a53f2d Binary files /dev/null and b/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts-snapshots/item-sliding-gesture-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/item/item.scss b/core/src/components/item/item.scss index 402b8eb6ed..46e2cafe05 100644 --- a/core/src/components/item/item.scss +++ b/core/src/components/item/item.scss @@ -2,6 +2,7 @@ // Item // -------------------------------------------------- +// TODO: FW-4955 :host { /** @@ -45,10 +46,10 @@ * * @prop --ripple-color: Color of the item ripple effect * - * @prop --highlight-height: The height of the highlight on the item - * @prop --highlight-color-focused: The color of the highlight on the item when focused - * @prop --highlight-color-valid: The color of the highlight on the item when valid - * @prop --highlight-color-invalid: The color of the highlight on the item when invalid + * @prop --highlight-height: The height of the highlight on the item. Only applies to inputs and textareas using the legacy form syntax. DEPRECATED: Highlights can be styled on `ion-input` or `ion-textarea` when using the modern form syntax. + * @prop --highlight-color-focused: The color of the highlight on the item when focused. Only applies to inputs and textareas using the legacy form syntax. DEPRECATED: Highlights can be styled on `ion-input` or `ion-textarea` when using the modern form syntax. + * @prop --highlight-color-valid: The color of the highlight on the item when valid. Only applies to inputs and textareas using the legacy form syntax. DEPRECATED: Highlights can be styled on `ion-input` or `ion-textarea` when using the modern form syntax. + * @prop --highlight-color-invalid: The color of the highlight on the item when invalid. Only applies to inputs and textareas using the legacy form syntax. DEPRECATED: Highlights can be styled on `ion-input` or `ion-textarea` when using the modern form syntax. */ --border-radius: 0px; --border-width: 0px; diff --git a/core/src/components/modal/test/can-dismiss/modal-sheet.e2e.ts b/core/src/components/modal/test/can-dismiss/modal-sheet.e2e.ts index 09f78b86b3..b282645740 100644 --- a/core/src/components/modal/test/can-dismiss/modal-sheet.e2e.ts +++ b/core/src/components/modal/test/can-dismiss/modal-sheet.e2e.ts @@ -73,7 +73,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => await ionModalDidPresent.next(); const modalHeader = page.locator('#modal-header'); - await dragElementBy(modalHeader, page, 0, -500); + await dragElementBy(modalHeader, page, 0, 30); const modal = page.locator('ion-modal'); expect(modal).not.toBe(null); 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 c4aabaee1f..9985f6babd 100644 --- a/core/src/components/modal/test/card-nav/modal.e2e.ts +++ b/core/src/components/modal/test/card-nav/modal.e2e.ts @@ -9,12 +9,7 @@ import { CardModalPage } from '../fixtures'; configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => { test.describe(title('card modal - nav'), () => { let cardModalPage: CardModalPage; - test.beforeEach(async ({ page, skip }) => { - skip.browser( - (browserName: string) => browserName !== 'chromium', - 'dragElementBy is flaky outside of Chrome browsers.' - ); - + test.beforeEach(async ({ page }) => { cardModalPage = new CardModalPage(page); await cardModalPage.navigate('/src/components/modal/test/card-nav?ionic:_testing=false', config); }); @@ -33,7 +28,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => const content = page.locator('.page-two-content'); - await dragElementBy(content, page, 1000, 0, 10); + await dragElementBy(content, page, 370, 0, 10); await ionNavDidChange.next(); }); @@ -47,7 +42,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => await ionNavDidChange.next(); - await cardModalPage.swipeToCloseModal('ion-modal ion-content.page-two-content'); + await cardModalPage.swipeToCloseModal('ion-modal ion-content.page-two-content', true, 270); }); }); }); 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 15c91dc783..12506c4a90 100644 --- a/core/src/components/modal/test/card-refresher/modal.e2e.ts +++ b/core/src/components/modal/test/card-refresher/modal.e2e.ts @@ -15,7 +15,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => const modal = page.locator('ion-modal'); const content = (await page.$('ion-modal ion-content'))!; - await dragElementBy(content, page, 0, 500); + await dragElementBy(content, page, 0, 300); await content.waitForElementState('stable'); 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 0856e73a8d..f94a5e2c73 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 @@ -30,7 +30,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => await content.evaluate((el: HTMLElement) => (el.scrollTop = 500)); - await dragElementBy(content, page, 0, 500); + await dragElementBy(content, page, 0, 300); await content.waitForElementState('stable'); diff --git a/core/src/components/modal/test/card/modal-tablet.e2e.ts b/core/src/components/modal/test/card/modal-tablet.e2e.ts index 7de91b716e..96291f5027 100644 --- a/core/src/components/modal/test/card/modal-tablet.e2e.ts +++ b/core/src/components/modal/test/card/modal-tablet.e2e.ts @@ -61,12 +61,12 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => test('it should not swipe to close when swiped on the content but the content is scrolled', async ({ page }) => { const modal = await cardModalPage.openModalByTrigger('#card'); - const content = (await page.$('ion-modal ion-content'))!; + const content = page.locator('ion-modal ion-content'); await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0)); await cardModalPage.swipeToCloseModal('ion-modal ion-content', false); - await content.waitForElementState('stable'); + await content.waitFor(); await expect(modal).toBeVisible(); }); test('it should not swipe to close when swiped on the content but the content is scrolled even when content is replaced', async ({ @@ -76,12 +76,12 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => await page.click('ion-button.replace'); - const content = (await page.$('ion-modal ion-content'))!; + const content = page.locator('ion-modal ion-content'); await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0)); await cardModalPage.swipeToCloseModal('ion-modal ion-content', false); - await content.waitForElementState('stable'); + await content.waitFor(); await expect(modal).toBeVisible(); }); test('content should be scrollable after gesture ends', async ({ page }) => { diff --git a/core/src/components/modal/test/fixtures.ts b/core/src/components/modal/test/fixtures.ts index c5632c6f1a..991cc743d0 100644 --- a/core/src/components/modal/test/fixtures.ts +++ b/core/src/components/modal/test/fixtures.ts @@ -16,7 +16,7 @@ export class CardModalPage { this.ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss'); } async openModalByTrigger(selector: string) { - await this.page.click(selector); + await this.page.locator(selector).click(); await this.ionModalDidPresent.next(); return this.page.locator('ion-modal'); diff --git a/core/src/components/modal/test/sheet/modal.e2e.ts b/core/src/components/modal/test/sheet/modal.e2e.ts index 2485842bd3..2caf86fad0 100644 --- a/core/src/components/modal/test/sheet/modal.e2e.ts +++ b/core/src/components/modal/test/sheet/modal.e2e.ts @@ -129,7 +129,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => const ionBreakpointDidChange = await page.spyOnEvent('ionBreakpointDidChange'); const header = page.locator('.modal-sheet ion-header'); - await dragElementBy(header, page, 0, 150); + await dragElementBy(header, page, 0, 125); await ionBreakpointDidChange.next(); diff --git a/core/src/components/range/test/legacy/basic/range.e2e.ts b/core/src/components/range/test/legacy/basic/range.e2e.ts index d990ac59c5..26e00099b8 100644 --- a/core/src/components/range/test/legacy/basic/range.e2e.ts +++ b/core/src/components/range/test/legacy/basic/range.e2e.ts @@ -77,13 +77,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => expect(await scrollEl.evaluate((el: HTMLElement) => el.scrollTop)).toEqual(0); - const box = (await knobEl.boundingBox())!; - const centerX = box.x + box.width / 2; - const centerY = box.y + box.height / 2; - - await page.mouse.move(centerX, centerY); - await page.mouse.down(); - await page.mouse.move(centerX + 30, centerY); + await dragElementBy(knobEl, page, 30, 0, undefined, undefined, false); /** * Do not use scrollToBottom() or other scrolling methods diff --git a/core/src/components/range/test/range-events.e2e.ts b/core/src/components/range/test/range-events.e2e.ts index 08eea08e0c..3eeba5437b 100644 --- a/core/src/components/range/test/range-events.e2e.ts +++ b/core/src/components/range/test/range-events.e2e.ts @@ -10,15 +10,28 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => /** * The mouse events are flaky on CI */ - test.fixme('should emit start/end events', async ({ page }, testInfo) => { - await page.setContent(``, config); + test.fixme('should emit start/end events', async ({ page }) => { + /** + * Requires padding to prevent the knob from being clipped. + * If it's clipped, then the value might be one off. + * For example, if the knob is clipped on the right, then the value + * will be 99 instead of 100. + */ + await page.setContent( + ` +
+ +
+ `, + config + ); const rangeStart = await page.spyOnEvent('ionKnobMoveStart'); const rangeEnd = await page.spyOnEvent('ionKnobMoveEnd'); const rangeEl = page.locator('ion-range'); - await dragElementBy(rangeEl, page, testInfo.project.metadata.rtl ? -300 : 300, 0); + await dragElementBy(rangeEl, page, 300, 0); await page.waitForChanges(); /** @@ -65,13 +78,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => expect(await scrollEl.evaluate((el: HTMLElement) => el.scrollTop)).toEqual(0); - const box = (await knobEl.boundingBox())!; - const centerX = box.x + box.width / 2; - const centerY = box.y + box.height / 2; - - await page.mouse.move(centerX, centerY); - await page.mouse.down(); - await page.mouse.move(centerX + 30, centerY); + await dragElementBy(knobEl, page, 30, 0, undefined, undefined, false); /** * Do not use scrollToBottom() or other scrolling methods @@ -118,13 +125,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => const rangeHandle = page.locator('ion-range .range-knob-handle'); const ionChangeSpy = await page.spyOnEvent('ionChange'); - const boundingBox = await rangeHandle.boundingBox(); - - await rangeHandle.hover(); - await page.mouse.down(); - await page.mouse.move(boundingBox!.x + 100, boundingBox!.y); - - await page.mouse.up(); + await dragElementBy(rangeHandle, page, 100, 0); await ionChangeSpy.next(); @@ -169,11 +170,9 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => const rangeHandle = page.locator('ion-range .range-knob-handle'); const ionInputSpy = await page.spyOnEvent('ionInput'); - const boundingBox = await rangeHandle.boundingBox(); - await rangeHandle.hover(); - await page.mouse.down(); - await page.mouse.move(boundingBox!.x + 100, boundingBox!.y); + + await dragElementBy(rangeHandle, page, 100, 0, undefined, undefined, false); await ionInputSpy.next(); diff --git a/core/src/components/refresher/test/basic/refresher.e2e.ts b/core/src/components/refresher/test/basic/refresher.e2e.ts index fe17dd7fca..70f38bc88e 100644 --- a/core/src/components/refresher/test/basic/refresher.e2e.ts +++ b/core/src/components/refresher/test/basic/refresher.e2e.ts @@ -3,13 +3,11 @@ import { configs, test } from '@utils/test/playwright'; import { pullToRefresh } from '../test.utils'; -// TODO FW-2795: Enable this test when touch events/gestures are better supported in Playwright - /** * This behavior does not vary across directions. */ configs({ directions: ['ltr'] }).forEach(({ title, config }) => { - test.describe.skip(title('refresher: basic'), () => { + test.describe(title('refresher: basic'), () => { test.beforeEach(async ({ page }) => { await page.goto('/src/components/refresher/test/basic', config); }); diff --git a/core/src/components/refresher/test/scroll-target/refresher.e2e.ts b/core/src/components/refresher/test/scroll-target/refresher.e2e.ts index 8cd3547651..c96380ae56 100644 --- a/core/src/components/refresher/test/scroll-target/refresher.e2e.ts +++ b/core/src/components/refresher/test/scroll-target/refresher.e2e.ts @@ -3,12 +3,11 @@ import { configs, test } from '@utils/test/playwright'; import { pullToRefresh } from '../test.utils'; -// TODO FW-2795: Enable this test when touch events/gestures are better supported in Playwright /** * This behavior does not vary across directions. */ configs({ directions: ['ltr'] }).forEach(({ title, config }) => { - test.describe.skip(title('refresher: custom scroll target'), () => { + test.describe(title('refresher: custom scroll target'), () => { test.beforeEach(async ({ page }) => { await page.goto('/src/components/refresher/test/scroll-target', config); }); @@ -19,7 +18,7 @@ configs({ directions: ['ltr'] }).forEach(({ title, config }) => { expect(await items.count()).toBe(30); - await pullToRefresh(page, '#inner-scroll'); + await pullToRefresh(page); expect(await items.count()).toBe(60); }); @@ -39,7 +38,7 @@ configs({ directions: ['ltr'] }).forEach(({ title, config }) => { expect(await items.count()).toBe(30); - await pullToRefresh(page, '#inner-scroll'); + await pullToRefresh(page); expect(await items.count()).toBe(60); }); diff --git a/core/src/components/refresher/test/test.utils.ts b/core/src/components/refresher/test/test.utils.ts index 706e135a9d..7454a88995 100644 --- a/core/src/components/refresher/test/test.utils.ts +++ b/core/src/components/refresher/test/test.utils.ts @@ -18,7 +18,7 @@ const pullToRefresh = async (page: E2EPage, selector = 'body') => { const ev = await page.spyOnEvent('ionRefreshComplete'); - await dragElementByYAxis(target, page, 400); + await dragElementByYAxis(target, page, 320); await ev.next(); }; diff --git a/core/src/components/reorder-group/test/interactive/reorder-group.e2e.ts b/core/src/components/reorder-group/test/interactive/reorder-group.e2e.ts index df76a46478..c443275e43 100644 --- a/core/src/components/reorder-group/test/interactive/reorder-group.e2e.ts +++ b/core/src/components/reorder-group/test/interactive/reorder-group.e2e.ts @@ -1,18 +1,12 @@ import { expect } from '@playwright/test'; import { configs, test, dragElementBy } from '@utils/test/playwright'; -// TODO FW-3079 /** * Reorder group does not have per-mode styles */ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { - test.describe.skip(title('reorder group: interactive'), () => { - test.beforeEach(async ({ page, skip }) => { - skip.browser( - (browserName: string) => browserName !== 'chromium', - 'dragElementBy is flaky outside of Chrome browsers.' - ); - + test.describe(title('reorder group: interactive'), () => { + test.beforeEach(async ({ page }) => { await page.goto(`/src/components/reorder-group/test/interactive`, config); }); test('should drag and drop when ion-reorder wraps ion-item', async ({ page }) => { diff --git a/core/src/components/reorder-group/test/nested/reorder-group.e2e.ts b/core/src/components/reorder-group/test/nested/reorder-group.e2e.ts index 51b426e750..a86de620ce 100644 --- a/core/src/components/reorder-group/test/nested/reorder-group.e2e.ts +++ b/core/src/components/reorder-group/test/nested/reorder-group.e2e.ts @@ -1,17 +1,12 @@ import { expect } from '@playwright/test'; import { configs, test, dragElementBy } from '@utils/test/playwright'; -// TODO FW-3079 /** * Reorder group does not have per-mode styles */ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { - test.describe.skip(title('reorder group: nested'), () => { - test.beforeEach(async ({ page, skip }) => { - skip.browser( - (browserName: string) => browserName !== 'chromium', - 'dragElementBy is flaky outside of Chrome browsers.' - ); + test.describe(title('reorder group: nested'), () => { + test.beforeEach(async ({ page }) => { await page.goto(`/src/components/reorder-group/test/nested`, config); }); test('should drag and drop when ion-reorder wraps ion-item', async ({ page }) => { diff --git a/core/src/components/reorder-group/test/scroll-target/reorder-group.e2e.ts b/core/src/components/reorder-group/test/scroll-target/reorder-group.e2e.ts index a9ef4b809c..12a76299b5 100644 --- a/core/src/components/reorder-group/test/scroll-target/reorder-group.e2e.ts +++ b/core/src/components/reorder-group/test/scroll-target/reorder-group.e2e.ts @@ -1,17 +1,12 @@ import { expect } from '@playwright/test'; import { configs, test, dragElementBy } from '@utils/test/playwright'; -// TODO FW-3079 /** * Reorder group does not have per-mode styles */ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { - test.describe.skip(title('reorder group: scroll-target'), () => { - test.beforeEach(async ({ page, skip }) => { - skip.browser( - (browserName: string) => browserName !== 'chromium', - 'dragElementBy is flaky outside of Chrome browsers.' - ); + test.describe(title('reorder group: scroll-target'), () => { + test.beforeEach(async ({ page }) => { await page.goto(`/src/components/reorder-group/test/scroll-target`, config); }); test('should drag and drop when ion-reorder wraps ion-item', async ({ page }) => { diff --git a/core/src/components/title/test/basic/title.e2e.ts b/core/src/components/title/test/basic/title.e2e.ts index a821bcf9b7..afecacd1cd 100644 --- a/core/src/components/title/test/basic/title.e2e.ts +++ b/core/src/components/title/test/basic/title.e2e.ts @@ -12,3 +12,23 @@ configs().forEach(({ title, screenshot, config }) => { }); }); }); + +configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('title: special characters'), () => { + test('should not cut off characters', async ({ page }) => { + await page.setContent( + ` + + + ÔÔÔ + + + `, + config + ); + + const title = page.locator('ion-title'); + await expect(title).toHaveScreenshot(screenshot('title-characters')); + }); + }); +}); diff --git a/core/src/components/title/test/basic/title.e2e.ts-snapshots/title-characters-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/title/test/basic/title.e2e.ts-snapshots/title-characters-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000..3de472c05a Binary files /dev/null and b/core/src/components/title/test/basic/title.e2e.ts-snapshots/title-characters-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/title/test/basic/title.e2e.ts-snapshots/title-characters-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/title/test/basic/title.e2e.ts-snapshots/title-characters-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000..e2e6f53f2c Binary files /dev/null and b/core/src/components/title/test/basic/title.e2e.ts-snapshots/title-characters-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/title/test/basic/title.e2e.ts-snapshots/title-characters-ios-ltr-Mobile-Safari-linux.png b/core/src/components/title/test/basic/title.e2e.ts-snapshots/title-characters-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000..d619c78d05 Binary files /dev/null and b/core/src/components/title/test/basic/title.e2e.ts-snapshots/title-characters-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/title/title.ios.scss b/core/src/components/title/title.ios.scss index 4031126a20..a993c94787 100644 --- a/core/src/components/title/title.ios.scss +++ b/core/src/components/title/title.ios.scss @@ -49,8 +49,6 @@ min-width: 100%; - padding-bottom: 6px; - font-size: 34px; font-weight: 700; diff --git a/core/src/components/toolbar/toolbar.ios.scss b/core/src/components/toolbar/toolbar.ios.scss index acf6b08818..0c1f0851e8 100644 --- a/core/src/components/toolbar/toolbar.ios.scss +++ b/core/src/components/toolbar/toolbar.ios.scss @@ -84,6 +84,16 @@ // Toolbar: Large Title // -------------------------------------------------- +/** + * Ensure there is enough spacing between the large + * title and any potential content in subsequent + * toolbars. We do not add padding to the title + * itself because it is absolutely positioned. + */ +:host(.toolbar-title-large) { + padding-bottom: 7px; +} + :host(.toolbar-title-large) .toolbar-container { flex-wrap: wrap; align-items: flex-start; @@ -94,4 +104,4 @@ order: map-get($toolbar-order-ios, title-large); min-width: 100%; -} \ No newline at end of file +} diff --git a/core/src/utils/tap-click/index.ts b/core/src/utils/tap-click/index.ts index 775e9f1f04..5b5f81e1c4 100644 --- a/core/src/utils/tap-click/index.ts +++ b/core/src/utils/tap-click/index.ts @@ -1,7 +1,13 @@ +import { doc } from '@utils/browser'; + import type { Config } from '../../interface'; import { now, pointerCoord } from '../helpers'; export const startTapClick = (config: Config) => { + if (doc === undefined) { + return; + } + let lastTouch = -MOUSE_WAIT * 10; let lastActivated = 0; @@ -143,7 +149,6 @@ export const startTapClick = (config: Config) => { } }; - const doc = document; doc.addEventListener('ionGestureCaptured', cancelActive); doc.addEventListener('touchstart', onTouchStart, true); diff --git a/core/src/utils/test/playwright/drag-element.ts b/core/src/utils/test/playwright/drag-element.ts index 96e6880f23..ec0a8d9ce8 100644 --- a/core/src/utils/test/playwright/drag-element.ts +++ b/core/src/utils/test/playwright/drag-element.ts @@ -1,3 +1,11 @@ +/** + * The drag gesture will not operate as expected when the element is dragged outside of the viewport because the Mouse class does not fire events outside of the viewport. + * + * For example, if the mouse is moved outside of the viewport, then the `mouseup` event will not fire. + * + * See https://playwright.dev/docs/api/class-mouse#mouse-move for more information. + */ + import type { ElementHandle, Locator } from '@playwright/test'; import type { E2EPage } from './'; @@ -8,7 +16,8 @@ export const dragElementBy = async ( dragByX = 0, dragByY = 0, startXCoord?: number, - startYCoord?: number + startYCoord?: number, + releaseDrag = true ) => { const boundingBox = await el.boundingBox(); @@ -21,14 +30,17 @@ export const dragElementBy = async ( const startX = startXCoord === undefined ? boundingBox.x + boundingBox.width / 2 : startXCoord; const startY = startYCoord === undefined ? boundingBox.y + boundingBox.height / 2 : startYCoord; - const endX = startX + dragByX; - const endY = startY + dragByY; - + // Navigate to the start position. await page.mouse.move(startX, startY); + await page.mouse.down(); - await page.mouse.move(endX, endY, { steps: 10 }); - await page.mouse.up(); + // Drag the element. + await moveElement(page, startX, startY, dragByX, dragByY); + + if (releaseDrag) { + await page.mouse.up(); + } }; /** @@ -55,12 +67,83 @@ export const dragElementByYAxis = async ( const startX = boundingBox.x + boundingBox.width / 2; const startY = startYCoord === undefined ? boundingBox.y + boundingBox.height / 2 : startYCoord; + // Navigate to the start position. await page.mouse.move(startX, startY); await page.mouse.down(); - for (let i = 0; i < dragByY; i += 20) { - await page.mouse.move(startX, startY + i); - } + // Drag the element. + await moveElement(page, startX, startY, 0, dragByY); await page.mouse.up(); }; + +const validateDragByX = (startX: number, dragByX: number, viewportWidth: number) => { + const endX = startX + dragByX; + // The element is being dragged past the right of the viewport. + if (endX > viewportWidth) { + const recommendedDragByX = viewportWidth - startX - 5; + throw new Error( + `The element is being dragged past the right of the viewport. Update the dragByX value to prevent going out of bounds. A recommended value is ${recommendedDragByX}.` + ); + } + + // The element is being dragged past the left of the viewport. + if (endX < 0) { + const recommendedDragByX = startX - 5; + throw new Error( + `The element is being dragged past the left of the viewport. Update the dragByX value to prevent going out of bounds. A recommended value is ${recommendedDragByX}.` + ); + } +}; + +const validateDragByY = (startY: number, dragByY: number, viewportHeight: number) => { + const endY = startY + dragByY; + // The element is being dragged past the bottom of the viewport. + if (endY > viewportHeight) { + const recommendedDragByY = viewportHeight - startY - 5; + throw new Error( + `The element is being dragged past the bottom of the viewport. Update the dragByY value to prevent going out of bounds. A recommended value is ${recommendedDragByY}.` + ); + } + + // The element is being dragged past the top of the viewport. + if (endY < 0) { + const recommendedDragByY = startY - 5; + throw new Error( + `The element is being dragged past the top of the viewport. Update the dragByY value to prevent going out of bounds. A recommended value is ${recommendedDragByY}.` + ); + } +}; + +const moveElement = async (page: E2EPage, startX: number, startY: number, dragByX = 0, dragByY = 0) => { + const steps = 10; + const browser = page.context().browser()!.browserType().name(); + + const viewport = page.viewportSize(); + if (viewport === null) { + throw new Error( + 'Cannot get viewport size. See https://playwright.dev/docs/api/class-page#page-viewport-size for more information' + ); + } + + validateDragByX(startX, dragByX, viewport.width); + validateDragByY(startY, dragByY, viewport.height); + + const endX = startX + dragByX; + const endY = startY + dragByY; + + // Drag the element. + for (let i = 1; i <= steps; i++) { + const middleX = startX + (endX - startX) * (i / steps); + const middleY = startY + (endY - startY) * (i / steps); + + await page.mouse.move(middleX, middleY); + + // Safari needs to wait for a repaint to occur before moving the mouse again. + if (browser === 'webkit' && i % 2 === 0) { + // Repainting every 2 steps is enough to keep the drag gesture smooth. + // Anything past 4 steps will cause the drag gesture to be flaky. + await page.evaluate(() => new Promise(requestAnimationFrame)); + } + } +};