From d568f13651e95ffe214c2b69626eb94496286b25 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Wed, 6 May 2026 07:02:39 +0200 Subject: [PATCH 1/2] chore: upgrade playwright --- package-lock.json | 24 ++++++++++++------------ package.json | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index c1bb3ad8..ff705b29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@babel/core": "^7.18.10", "@babel/plugin-syntax-typescript": "^7.18.6", "@babel/plugin-transform-react-jsx": "^7.18.10", - "@playwright/test": "^1.51.1", + "@playwright/test": "^1.59.1", "@preact/preset-vite": "^2.8.2", "@preact/signals": "^1.2.3", "@prefresh/vite": "^2.4.5", @@ -1446,13 +1446,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.51.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.51.1.tgz", - "integrity": "sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.51.1" + "playwright": "1.59.1" }, "bin": { "playwright": "cli.js" @@ -9664,13 +9664,13 @@ } }, "node_modules/playwright": { - "version": "1.51.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.1.tgz", - "integrity": "sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.51.1" + "playwright-core": "1.59.1" }, "bin": { "playwright": "cli.js" @@ -9683,9 +9683,9 @@ } }, "node_modules/playwright-core": { - "version": "1.51.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.1.tgz", - "integrity": "sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index 8056ba33..b2e60c49 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@babel/core": "^7.18.10", "@babel/plugin-syntax-typescript": "^7.18.6", "@babel/plugin-transform-react-jsx": "^7.18.10", - "@playwright/test": "^1.51.1", + "@playwright/test": "^1.59.1", "@preact/preset-vite": "^2.8.2", "@preact/signals": "^1.2.3", "@prefresh/vite": "^2.4.5", From 6dea7a63e5f166b12112fbed1edcaa97077441af Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Wed, 6 May 2026 08:07:25 +0200 Subject: [PATCH 2/2] chore: harden e2e tests Lots of flakyness was caused by trying to one-shot assertions by grabbing an element and asserting on it instead of polling. Other issues were basically race conditions between elements of which there were plenty. All of this is now much more reliable thanks to new assertions in playwright. --- playwright.config.ts | 11 +- test-e2e/fixtures/devtools.ts | 2 + test-e2e/fixtures/index.ts | 27 ++--- test-e2e/fixtures/load-preact-version.ts | 46 ++++---- test-e2e/fixtures/rewrite-preact-version.ts | 20 +--- test-e2e/pw-utils.ts | 90 +++++++-------- test-e2e/tests/collapse.test.ts | 42 ++++--- test-e2e/tests/context-displayname.test.ts | 13 +-- test-e2e/tests/element-keyboard.test.ts | 22 ++-- test-e2e/tests/element-scroll.test.ts | 5 +- .../tests/element-search-keyboard.test.ts | 14 +-- test-e2e/tests/highlight-iframe.test.ts | 24 ++-- test-e2e/tests/highlight-margin.test.ts | 62 ++++++----- test-e2e/tests/highlight-suspense.test.ts | 25 +++-- test-e2e/tests/highlighter.test.ts | 23 ++-- test-e2e/tests/hoc-filter-search.test.ts | 19 ++-- test-e2e/tests/hoc-filter-update.test.ts | 26 +++-- test-e2e/tests/hoc-filter.test.ts | 39 +++---- test-e2e/tests/hoc-update.test.ts | 11 +- test-e2e/tests/hooks/hook-name.test.ts | 62 +++++------ .../tests/hooks/hooks-depth-limit.test.ts | 24 ++-- .../tests/hooks/hooks-expand-state.test.ts | 3 +- test-e2e/tests/hooks/hooks-multiple.test.ts | 17 +-- test-e2e/tests/hooks/hooks-number.test.ts | 20 ++-- test-e2e/tests/hooks/useCallback.test.ts | 5 +- .../tests/hooks/useContext-10.5.14.test.ts | 11 +- test-e2e/tests/hooks/useContext.test.ts | 17 +-- .../tests/hooks/useDebugValue-complex.test.ts | 5 +- test-e2e/tests/hooks/useDebugValue.test.ts | 10 +- test-e2e/tests/hooks/useDeepHook.test.ts | 16 +-- test-e2e/tests/hooks/useEffect.test.ts | 3 +- test-e2e/tests/hooks/useErrorBoundary.test.ts | 10 +- .../tests/hooks/useImperativeHandle.test.ts | 5 +- test-e2e/tests/hooks/useLayoutEffect.test.ts | 5 +- test-e2e/tests/hooks/useMemo.test.ts | 3 +- test-e2e/tests/hooks/useRef-element.test.ts | 5 +- test-e2e/tests/hooks/useRef.test.ts | 3 +- test-e2e/tests/hooks/useState.test.ts | 6 +- test-e2e/tests/inspect-click.test.ts | 5 +- test-e2e/tests/inspect-key.test.ts | 24 ++-- test-e2e/tests/inspect-map-set.test.ts | 104 +++++++++--------- test-e2e/tests/inspect-non-vnode.test.ts | 25 +++-- test-e2e/tests/inspect-owner-memo.test.ts | 21 ++-- test-e2e/tests/inspect-owner-update.test.ts | 9 +- test-e2e/tests/inspect-owner.test.ts | 43 +++----- test-e2e/tests/inspect-props-sort.test.ts | 20 ++-- test-e2e/tests/inspect-select.test.ts | 28 +++-- test-e2e/tests/inspect-signal.test.ts | 29 ++--- test-e2e/tests/inspect-truncate.test.ts | 18 +-- test-e2e/tests/inspect-virtual.test.ts | 16 +-- test-e2e/tests/inspect.test.ts | 29 +++-- test-e2e/tests/new-prop.test.ts | 27 +++-- .../flamegraph/highlight-flamegraph.test.ts | 13 ++- .../profiler/flamegraph/memo-sibling.test.ts | 33 +++--- .../profiler/flamegraph/profiler-hoc.test.ts | 20 ++-- .../profiler-static-subtree.test.ts | 34 +++--- .../flamegraph/profiler-unmount.test.ts | 5 +- .../profiler/highlight-updates-text.test.ts | 11 +- .../tests/profiler/highlight-updates.test.ts | 11 +- .../profiler/ranked/highlight-ranked.test.ts | 15 ++- .../profiler/ranked/profiler-ranked.test.ts | 11 +- .../profiler/render-reason-disabled.test.ts | 43 +++----- .../profiler/render-reason-support.test.ts | 25 ++--- .../profiler/render-reasons-memo.test.ts | 26 ++--- .../tests/profiler/render-reasons.test.ts | 82 +++++--------- test-e2e/tests/prop-input.test.ts | 45 ++++---- test-e2e/tests/root-islands.test.ts | 16 +-- test-e2e/tests/root-multiple.test.ts | 15 +-- test-e2e/tests/state.test.ts | 12 +- test-e2e/tests/stats/stats-memo.test.ts | 27 ++--- test-e2e/tests/stats/stats-simple.test.ts | 43 +++----- .../tests/stats/stats-single-child.test.ts | 35 +++--- test-e2e/tests/suspense-toggle.test.ts | 31 ++---- test-e2e/tests/suspense.test.ts | 18 ++- test-e2e/tests/update-copy.test.ts | 41 ++++--- 75 files changed, 815 insertions(+), 946 deletions(-) diff --git a/playwright.config.ts b/playwright.config.ts index 95a79944..4a657290 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -7,16 +7,23 @@ const config: PlaywrightTestConfig = { ignoreHTTPSErrors: true, video: "on-first-retry", trace: "retain-on-failure", + actionTimeout: 10 * 1000, + navigationTimeout: 20 * 1000, + }, + expect: { + timeout: 5 * 1000, }, testDir: path.join(__dirname, "test-e2e", "tests"), testMatch: "**/*.test.ts", + forbidOnly: !!process.env.CI, // retries: 3, - timeout: 10 * 1000, + timeout: 20 * 1000, + workers: process.env.CI ? 2 : undefined, webServer: { command: "npm run dev", url: "http://localhost:8100/", timeout: 120 * 1000, - reuseExistingServer: true, + reuseExistingServer: !process.env.CI, }, }; export default config; diff --git a/test-e2e/fixtures/devtools.ts b/test-e2e/fixtures/devtools.ts index 7f7f4905..e7f53abc 100644 --- a/test-e2e/fixtures/devtools.ts +++ b/test-e2e/fixtures/devtools.ts @@ -6,6 +6,8 @@ const store = setupInlineDevtools(container, window); // @ts-ignore window.parent.store = store; +// @ts-ignore +window.__PREACT_DEVTOOLS_READY__ = true; store.subscribe((name, msg) => { window.parent.postMessage( { type: name, data: msg, source: "preact-devtools-to-client" }, diff --git a/test-e2e/fixtures/index.ts b/test-e2e/fixtures/index.ts index 3a29358e..0e123eb7 100644 --- a/test-e2e/fixtures/index.ts +++ b/test-e2e/fixtures/index.ts @@ -62,20 +62,15 @@ async function loadFixture() { params.set("preact", encodeURIComponent(preact.replace(/\./g, "_"))); } - try { - await import(/* @vite-ignore */ source.toString()); - } catch (err) { - // eslint-disable-next-line no-console - console.log(err); - } + await import(/* @vite-ignore */ source.toString()); } } async function waitForDevtoolsInit() { - const iframe = document.querySelector("iframe"); - return new Promise(r => { - iframe.addEventListener("load", r); - }); + const iframe = document.querySelector("#devtools") as HTMLIFrameElement; + while ((iframe.contentWindow as any)?.__PREACT_DEVTOOLS_READY__ !== true) { + await new Promise(r => setTimeout(r, 16)); + } } (async () => { @@ -110,9 +105,9 @@ async function waitForDevtoolsInit() { const log = (window.log = []); window.addEventListener("message", e => { if (e.data.source === "preact-page-hook") { - (document.querySelector( - "#devtools", - ) as HTMLIFrameElement).contentWindow.postMessage(e.data, "*"); + ( + document.querySelector("#devtools") as HTMLIFrameElement + ).contentWindow.postMessage(e.data, "*"); log.push(e.data); } else if ( e.data.source === "preact-devtools-to-client" && @@ -132,9 +127,7 @@ async function waitForDevtoolsInit() { }); await waitForDevtoolsInit(); - - document.querySelector("iframe").contentWindow.postMessage("foobar", "*"); - await loadFixture(); - document.querySelector("iframe").contentWindow.postMessage("foobar", "*"); + // @ts-ignore + window.__PREACT_E2E_READY__ = true; })(); diff --git a/test-e2e/fixtures/load-preact-version.ts b/test-e2e/fixtures/load-preact-version.ts index 202242b0..89b74ada 100644 --- a/test-e2e/fixtures/load-preact-version.ts +++ b/test-e2e/fixtures/load-preact-version.ts @@ -8,8 +8,9 @@ import * as tar from "tar"; * on demand. */ export function loadPreactVersion(): Plugin { - const pending = new Map void>>(); + const pending = new Map>(); const cache = new Map(); + const extracted = new Set(); const versionReg = /preact@([^/]+)/; const tarDir = path.join(__dirname, "vendor", "preact"); @@ -54,28 +55,33 @@ export function loadPreactVersion(): Plugin { }; } else { const versionDir = path.join(cacheDir, version); - // Check if someone is already resolving - const inProgress = pending.get(version); - if (inProgress) { - return new Promise(r => { - inProgress.push(() => r(cache.get(version)!)); - }); - } - // Check for tarball or folder const tarball = path.join(tarDir, `preact-${version}.tgz`); const folder = path.join(tarDir, `preact-${version}`); if (fs.existsSync(tarball)) { - if (!fs.existsSync(versionDir)) { - fs.mkdirSync(versionDir, { recursive: true }); + if (!extracted.has(version)) { + let extraction = pending.get(version); + if (!extraction) { + if (!fs.existsSync(versionDir)) { + fs.mkdirSync(versionDir, { recursive: true }); + } + + extraction = tar + .extract({ + file: tarball, + cwd: versionDir, + strip: 1, + }) + .then(() => { + extracted.add(version); + }) + .finally(() => pending.delete(version)); + pending.set(version, extraction); + } + + await extraction; } - await tar.extract({ - file: tarball, - cwd: versionDir, - strip: 1, - }); - let importee = id.replace(/@[^/]+/, ""); const mappings = { @@ -113,9 +119,6 @@ export function loadPreactVersion(): Plugin { }; cache.set(id, out); - - const fns = pending.get(version) || []; - await Promise.all(fns.map(fn => fn())); return out; } else if (fs.existsSync(folder)) { let importee = id.replace(/@[^/]+/, ""); @@ -142,9 +145,6 @@ export function loadPreactVersion(): Plugin { }; cache.set(id, out); - - const fns = pending.get(version) || []; - await Promise.all(fns.map(fn => fn())); return out; } } diff --git a/test-e2e/fixtures/rewrite-preact-version.ts b/test-e2e/fixtures/rewrite-preact-version.ts index 6694fee0..fc132e2c 100644 --- a/test-e2e/fixtures/rewrite-preact-version.ts +++ b/test-e2e/fixtures/rewrite-preact-version.ts @@ -13,22 +13,9 @@ export function rewritePreactVersion(): Plugin { const PROTOCOL = "relative://"; const PREFIX = "@fixture:"; - let preactVersion = ""; - return { name: "preact:version", enforce: "pre", - configureServer(server) { - server.middlewares.use((req, res, next) => { - const url = new URL(req.originalUrl, "https://localhost"); - const version = url.searchParams.get("preact"); - if (version) { - preactVersion = version.replace(/\./g, "_"); - } - - next(); - }); - }, resolveId(id) { const url = new URL(id, PROTOCOL); const version = url.searchParams.get("preact"); @@ -36,8 +23,8 @@ export function rewritePreactVersion(): Plugin { return `@fixture:${id}`; } - if (id === `@preact/signals@${preactVersion}`) { - return `@fixture-signals:${preactVersion}`; + if (id.startsWith("@preact/signals@")) { + return `@fixture-signals:${id.slice("@preact/signals@".length)}`; } }, load(id) { @@ -61,8 +48,9 @@ export function rewritePreactVersion(): Plugin { }, transform(code, id) { if (id.startsWith("@fixture-signals")) { + const version = id.slice("@fixture-signals:".length); const res = transformSync(code, { - plugins: [[rewriteImportPlugin, { version: preactVersion }]], + plugins: [[rewriteImportPlugin, { version }]], })!; return { code: res.code, diff --git a/test-e2e/pw-utils.ts b/test-e2e/pw-utils.ts index ba566821..f0118d11 100644 --- a/test-e2e/pw-utils.ts +++ b/test-e2e/pw-utils.ts @@ -33,6 +33,34 @@ export async function gotoTest( `http://localhost:8100/?fixtures=${name}&preact=${preactVersion}`, ); + let fixtureError: Error | null = null; + const captureFixtureError = (err: Error) => { + fixtureError ??= err; + }; + page.on("pageerror", captureFixtureError); + try { + const readyTimeout = 20_000; + const deadline = Date.now() + readyTimeout; + // eslint-disable-next-line no-constant-condition + while (true) { + if (fixtureError) { + throw new Error(`Fixture page error: ${(fixtureError as any).message}`); + } + const ready = await page.evaluate( + () => (window as any).__PREACT_E2E_READY__ === true, + ); + if (ready) break; + if (Date.now() > deadline) { + throw new Error( + `Timed out after ${readyTimeout}ms waiting for __PREACT_E2E_READY__ on ${name}`, + ); + } + await new Promise(r => setTimeout(r, 50)); + } + } finally { + page.off("pageerror", captureFixtureError); + } + const devtools = page .mainFrame() .childFrames() @@ -40,56 +68,22 @@ export async function gotoTest( assert(devtools); - // TODO: Find something better - await wait(200); + await devtools.waitForFunction( + () => (window as any).__PREACT_DEVTOOLS_READY__ === true, + ); + await devtools.waitForFunction(() => { + return ( + document.querySelector('[data-testid="tree-item"]') !== null || + document.querySelector('[data-testid="msg-no-results"]') !== null || + document.querySelector('[data-testid="msg-only-connected"]') !== null + ); + }); return { devtools }; } export const wait = (ms: number) => new Promise(r => setTimeout(r, ms)); -export async function waitForPass( - fn, - options: { - timeout?: number; - checkEvery?: number; - crashOnError?: boolean; - } = {}, -) { - const { timeout = 2000, checkEvery = 200, crashOnError = false } = options; - - let caughtError: Error | null = null; - - for (let remaining = timeout; remaining > 0; remaining -= checkEvery) { - if (crashOnError) { - const res = await fn(); - if (res) return res; - } else { - caughtError = null; - let res; - try { - res = await fn(); - } catch (e) { - caughtError = e; - } - if (caughtError === null) return res; - } - - await wait(checkEvery); - } - - if (caughtError !== null) { - caughtError.message += ` (waited ${timeout})`; - throw caughtError; - } - - throw new Error(`waited ${timeout})`); -} - -export async function waitFor(fn) { - return waitForPass(fn, { crashOnError: true }); -} - export async function getLog(page: Page) { return (await page.evaluate(() => (window as any).log)) as any[]; } @@ -134,9 +128,9 @@ export async function getHooks(page: Frame): Promise> { // Check if we're dealing with an input if (!value) { - const rawValue = (item.querySelector( - '[data-testid="prop-value"] input', - ) as any)?.value; + const rawValue = ( + item.querySelector('[data-testid="prop-value"] input') as any + )?.value; if (rawValue === undefined) { value = ""; @@ -192,7 +186,7 @@ export async function clickRecordButton(page: Frame) { await page.locator(selector).click(); const state = start ? "Stop Recording" : "Start Recording"; - await page.locator(selector + `[title="${state}"]`); + await page.locator(selector + `[title="${state}"]`).waitFor(); } export async function getTreeItems(page: Page | Frame) { diff --git a/test-e2e/tests/collapse.test.ts b/test-e2e/tests/collapse.test.ts index 3a528963..c5156617 100644 --- a/test-e2e/tests/collapse.test.ts +++ b/test-e2e/tests/collapse.test.ts @@ -1,55 +1,51 @@ import { expect, test } from "@playwright/test"; -import { locateTab, gotoTest, waitFor, locateTreeItem } from "../pw-utils"; +import { locateTab, gotoTest, locateTreeItem } from "../pw-utils"; test("Display no stats initially", async ({ page }) => { const { devtools } = await gotoTest(page, "update-all"); - await devtools.waitForSelector('[data-testid="tree-item"]'); + await devtools.locator('[data-testid="tree-item"]').first().waitFor(); // All elements in the tree view should be uncollapsed initially - let collapsed = await devtools - .locator('[data-testid="tree-item"] [data-collapsed="true"]') - .count(); - expect(collapsed).toEqual(0); + await expect( + devtools.locator('[data-testid="tree-item"] [data-collapsed="true"]'), + ).toHaveCount(0); // Should be able to collapse tree const rows = await devtools.locator('[data-testid="tree-item"]').count(); - await devtools.click('[data-testid="tree-item"] button'); + await devtools.locator('[data-testid="tree-item"] button').first().click(); - const rowsAfter = await devtools.locator('[data-testid="tree-item"]').count(); - expect(rows).not.toEqual(rowsAfter); + await expect(devtools.locator('[data-testid="tree-item"]')).not.toHaveCount( + rows, + ); // Restore view - await devtools.click('[data-testid="tree-item"] button'); + await devtools.locator('[data-testid="tree-item"] button').first().click(); // Props should be collapsed by default await devtools.locator(locateTreeItem("Provider")).click(); const row = '[data-testid="props-row"]'; - await devtools.waitForSelector(row); + await devtools.locator(row).first().waitFor(); const selector = `${row} [data-collapsed="true"]`; - collapsed = await devtools.locator(selector).count(); - expect(collapsed).toEqual(1); + await expect(devtools.locator(selector)).toHaveCount(1); - await devtools.click(`${row} button`); - expect(await devtools.locator(selector).count()).toEqual(0); + await devtools.locator(`${row} button`).first().click(); + await expect(devtools.locator(selector)).toHaveCount(0); - await devtools.click(`${row} input`); + await devtools.locator(`${row} input`).first().click(); await page.keyboard.press("ArrowUp"); await page.keyboard.press("Enter"); // Our input should still be visible - expect(await devtools.locator(selector).count()).toEqual(0); + await expect(devtools.locator(selector)).toHaveCount(0); // Switching to Profiler and back should not change collapse state await devtools.locator(locateTab("PROFILER")).click(); - await devtools.waitForSelector('[data-testid="record-btn"]'); + await devtools.locator('[data-testid="record-btn"]').first().waitFor(); await devtools.locator(locateTab("ELEMENTS")).click(); - await waitFor(async () => (await devtools.$$(row)).length > 0); - - // Our input should still be visible - expect((await devtools.$$(row)).length > 0).toEqual(true); - expect((await devtools.$$(selector)).length).toEqual(0); + await expect(devtools.locator(row).first()).toBeAttached(); + await expect(devtools.locator(selector)).toHaveCount(0); }); diff --git a/test-e2e/tests/context-displayname.test.ts b/test-e2e/tests/context-displayname.test.ts index 48e97ccc..b2c9ce38 100644 --- a/test-e2e/tests/context-displayname.test.ts +++ b/test-e2e/tests/context-displayname.test.ts @@ -1,13 +1,12 @@ import { test, expect } from "@playwright/test"; -import { gotoTest, waitForPass } from "../pw-utils"; +import { gotoTest } from "../pw-utils"; test("Inspect should select node in elements panel", async ({ page }) => { const { devtools } = await gotoTest(page, "context-displayName"); - await waitForPass(async () => { - const items = await devtools - .locator('[data-testid="tree-item"]') - .allInnerTexts(); - expect(items).toEqual(["App", "Foobar.Provider", "Foobar.Consumer"]); - }); + await expect(devtools.locator('[data-testid="tree-item"]')).toHaveText([ + "App", + "Foobar.Provider", + "Foobar.Consumer", + ]); }); diff --git a/test-e2e/tests/element-keyboard.test.ts b/test-e2e/tests/element-keyboard.test.ts index 5b6d12fa..8eb40560 100644 --- a/test-e2e/tests/element-keyboard.test.ts +++ b/test-e2e/tests/element-keyboard.test.ts @@ -5,19 +5,21 @@ test("Test keyboard navigation in elements tree", async ({ page }) => { const { devtools } = await gotoTest(page, "counter"); await devtools.locator(locateTreeItem("Counter")).click(); - expect(Object.keys(await getProps(devtools))).toEqual([]); - - await page.keyboard.press("ArrowDown"); - let selected = await devtools.locator('[data-selected="true"]').textContent(); + await expect + .poll(async () => Object.keys(await getProps(devtools))) + .toEqual([]); const prop = '[data-testid="Props"] [data-testid="props-row"]'; - expect(selected).toEqual("Display"); - expect((await devtools.$$(prop)).length).toEqual(1); + await page.keyboard.press("ArrowDown"); + await expect(devtools.locator('[data-selected="true"]')).toHaveText( + "Display", + ); + await expect(devtools.locator(prop)).toHaveCount(1); await page.keyboard.press("ArrowUp"); - selected = await devtools.locator('[data-selected="true"]').textContent(); - - expect(selected).toEqual("Counter"); - expect((await devtools.$$(prop)).length).toEqual(0); + await expect(devtools.locator('[data-selected="true"]')).toHaveText( + "Counter", + ); + await expect(devtools.locator(prop)).toHaveCount(0); }); diff --git a/test-e2e/tests/element-scroll.test.ts b/test-e2e/tests/element-scroll.test.ts index 31e30b60..fa9f9bbc 100644 --- a/test-e2e/tests/element-scroll.test.ts +++ b/test-e2e/tests/element-scroll.test.ts @@ -21,6 +21,7 @@ test("Clicking at the right of element names #144", async ({ page }) => { await page.mouse.click(x - 20, y + offset + 200); - const text = await devtools.locator('[data-selected="true"]').textContent(); - expect(text).toEqual("ChildItemName"); + await expect(devtools.locator('[data-selected="true"]')).toHaveText( + "ChildItemName", + ); }); diff --git a/test-e2e/tests/element-search-keyboard.test.ts b/test-e2e/tests/element-search-keyboard.test.ts index 5ca39a16..fd567d21 100644 --- a/test-e2e/tests/element-search-keyboard.test.ts +++ b/test-e2e/tests/element-search-keyboard.test.ts @@ -1,22 +1,18 @@ -import { test } from "@playwright/test"; -import { gotoTest, waitFor } from "../pw-utils"; +import { test, expect } from "@playwright/test"; +import { gotoTest } from "../pw-utils"; test("Pressing Enter should scroll marked results into view during search #162", async ({ page, }) => { const { devtools } = await gotoTest(page, "deep-tree"); - await devtools.waitForSelector('[data-name="App"]'); - await devtools.type('[data-testid="element-search"]', "Child"); + await devtools.locator('[data-name="App"]').waitFor(); + await devtools.locator('[data-testid="element-search"]').fill("Child"); // Press Enter a bunch of times for (let i = 0; i < 24; i++) { await page.keyboard.press("Enter"); } - await waitFor(async () => { - const marked = await devtools.$("[data-marked]"); - if (!marked) return false; - return await marked!.isVisible(); - }); + await expect(devtools.locator('[data-marked="true"]')).toBeVisible(); }); diff --git a/test-e2e/tests/highlight-iframe.test.ts b/test-e2e/tests/highlight-iframe.test.ts index dff10f36..9bcd0db4 100644 --- a/test-e2e/tests/highlight-iframe.test.ts +++ b/test-e2e/tests/highlight-iframe.test.ts @@ -1,24 +1,20 @@ import { expect, test } from "@playwright/test"; -import { gotoTest, wait } from "../pw-utils"; +import { gotoTest } from "../pw-utils"; test("Highlight iframe nodes", async ({ page }) => { const { devtools } = await gotoTest(page, "iframe"); - await devtools - .locator("iframe") - .evaluateAll(iframes => - (iframes as HTMLIFrameElement[]).every( - x => x.contentDocument?.readyState == "complete", - ), - ); + await page.waitForFunction(() => + Array.from(document.querySelectorAll("iframe")).every( + x => (x as HTMLIFrameElement).contentDocument?.readyState === "complete", + ), + ); - await devtools - .locator('[data-testid="elements-tree"] [data-name]') - .first() - .waitFor(); + // All six components should be mounted before we read the tree + await expect( + devtools.locator('[data-testid="elements-tree"] [data-name]'), + ).toHaveCount(6); - // TODO: Find a better solution - await wait(1000); const elements = await devtools .locator('[data-testid="elements-tree"] [data-name]') .allTextContents(); diff --git a/test-e2e/tests/highlight-margin.test.ts b/test-e2e/tests/highlight-margin.test.ts index e4f35698..a9e92783 100644 --- a/test-e2e/tests/highlight-margin.test.ts +++ b/test-e2e/tests/highlight-margin.test.ts @@ -1,44 +1,50 @@ import { expect, Frame, Page, test } from "@playwright/test"; -import { gotoTest, locateTreeItem, wait } from "../pw-utils"; +import { gotoTest, locateTreeItem } from "../pw-utils"; test("Highlight overlay should detect memo for margin", async ({ page }) => { const { devtools } = await gotoTest(page, "highlight-margin"); await devtools.locator(locateTreeItem("Headline")).waitFor(); - await devtools.hover(locateTreeItem("Headline")); - // Wait for possible flickering to occur - await wait(1000); + await devtools.locator(locateTreeItem("Headline")).hover(); + await page.locator("#preact-devtools-highlighter > div").waitFor(); - let element = await page.$eval('[data-testid="headline"]', el => - el.getBoundingClientRect(), - ); - let highlight = await getHighlightSize(page); - expect(element.top > highlight.top).toEqual(true); - expect(element.left === highlight.left).toEqual(true); + await assertHighlight(page, '[data-testid="headline"]', (el, hl) => { + return el.top > hl.top && el.left === hl.left; + }); - await devtools.hover(locateTreeItem("MarginBox")); - await wait(1000); + await devtools.locator(locateTreeItem("MarginBox")).hover(); + await assertHighlight(page, '[data-testid="margin-box"]', (el, hl) => { + return el.top > hl.top && el.left > hl.left; + }); - element = await page.$eval('[data-testid="margin-box"]', el => - el.getBoundingClientRect(), - ); - highlight = await getHighlightSize(page); - expect(element.top > highlight.top).toEqual(true); - expect(element.left > highlight.left).toEqual(true); - - await devtools.hover(locateTreeItem("BorderBox")); - await wait(1000); - element = await page.$eval('[data-testid="border-box"]', el => - el.getBoundingClientRect(), - ); - highlight = await getHighlightSize(page); - expect(element.top > highlight.top).toEqual(true); - expect(element.left > highlight.left).toEqual(true); + await devtools.locator(locateTreeItem("BorderBox")).hover(); + await assertHighlight(page, '[data-testid="border-box"]', (el, hl) => { + return el.top > hl.top && el.left > hl.left; + }); }); +async function assertHighlight( + page: Page, + targetSelector: string, + check: ( + element: { top: number; left: number }, + highlight: { top: number; left: number }, + ) => boolean, +) { + await expect + .poll(async () => { + const element = await page.$eval(targetSelector, el => + el.getBoundingClientRect().toJSON(), + ); + const highlight = await getHighlightSize(page); + return check(element, highlight); + }) + .toBe(true); +} + async function getHighlightSize(page: Frame | Page) { return await page.$eval("#preact-devtools-highlighter > div", el => - el.getBoundingClientRect(), + el.getBoundingClientRect().toJSON(), ); } diff --git a/test-e2e/tests/highlight-suspense.test.ts b/test-e2e/tests/highlight-suspense.test.ts index 7cde5343..5f7138fa 100644 --- a/test-e2e/tests/highlight-suspense.test.ts +++ b/test-e2e/tests/highlight-suspense.test.ts @@ -1,20 +1,23 @@ import { expect, test } from "@playwright/test"; -import { gotoTest, locateTreeItem, wait } from "../pw-utils"; +import { gotoTest, locateTreeItem } from "../pw-utils"; test("Highlight Suspense nodes without crashing", async ({ page }) => { const { devtools } = await gotoTest(page, "suspense"); - await devtools.waitForSelector(locateTreeItem("Suspense")); - await devtools.hover(locateTreeItem("Suspense")); - // Wait for possible flickering to occur - await wait(1000); + await devtools.locator(locateTreeItem("Suspense")).waitFor(); + // The Delayed component resolves after a timeout + await page.locator('[data-testid="delayed"]').waitFor(); + await devtools.locator(locateTreeItem("Suspense")).hover(); + await page.locator("#preact-devtools-highlighter > div").waitFor(); const sizeOnPage = await page.$eval('[data-testid="delayed"]', el => - el.getBoundingClientRect(), + el.getBoundingClientRect().toJSON(), ); - const sizeOfHighlight = await page.$eval( - "#preact-devtools-highlighter > div", - el => el.getBoundingClientRect(), - ); - expect(sizeOfHighlight).toEqual(sizeOnPage); + await expect + .poll(() => + page.$eval("#preact-devtools-highlighter > div", el => + el.getBoundingClientRect().toJSON(), + ), + ) + .toEqual(sizeOnPage); }); diff --git a/test-e2e/tests/highlighter.test.ts b/test-e2e/tests/highlighter.test.ts index 839626cf..8734edb0 100644 --- a/test-e2e/tests/highlighter.test.ts +++ b/test-e2e/tests/highlighter.test.ts @@ -1,20 +1,21 @@ import { expect, test } from "@playwright/test"; -import { gotoTest, locateTreeItem, wait } from "../pw-utils"; +import { gotoTest, locateTreeItem } from "../pw-utils"; test("Highlight item", async ({ page }) => { const { devtools } = await gotoTest(page, "counter"); - await devtools.waitForSelector(locateTreeItem("Counter")); - await devtools.hover(locateTreeItem("Counter")); - // Wait for possible flickering to occur - await wait(1000); + await devtools.locator(locateTreeItem("Counter")).waitFor(); + await devtools.locator(locateTreeItem("Counter")).hover(); + await page.locator("#preact-devtools-highlighter > div").waitFor(); const sizeOnPage = await page.$eval("#app > div", el => - el.getBoundingClientRect(), + el.getBoundingClientRect().toJSON(), ); - const sizeOfHighlight = await page.$eval( - "#preact-devtools-highlighter > div", - el => el.getBoundingClientRect(), - ); - expect(sizeOfHighlight).toEqual(sizeOnPage); + await expect + .poll(() => + page.$eval("#preact-devtools-highlighter > div", el => + el.getBoundingClientRect().toJSON(), + ), + ) + .toEqual(sizeOnPage); }); diff --git a/test-e2e/tests/hoc-filter-search.test.ts b/test-e2e/tests/hoc-filter-search.test.ts index 81d6dc4f..623085f5 100644 --- a/test-e2e/tests/hoc-filter-search.test.ts +++ b/test-e2e/tests/hoc-filter-search.test.ts @@ -4,14 +4,12 @@ import { gotoTest, locateTreeItem } from "../pw-utils"; test("HOC-Component labels should be searchable", async ({ page }) => { const { devtools } = await gotoTest(page, "hoc"); - await devtools.waitForSelector(locateTreeItem("Foo")); - await devtools.type('[data-testid="element-search"]', "forw"); + await devtools.locator(locateTreeItem("Foo")).first().waitFor(); + await devtools.locator('[data-testid="element-search"]').fill("forw"); - let marked = await devtools.$$("mark"); - expect(marked.length).toEqual(2); - expect( - await marked[0].evaluate(el => el.hasAttribute("data-marked")), - ).toEqual(true); + const marks = devtools.locator("mark"); + await expect(marks).toHaveCount(2); + await expect(marks.nth(0)).toHaveAttribute("data-marked", /.*/); await devtools .locator('[data-testid="search-counter"]:has-text("1 | 2")') @@ -19,9 +17,6 @@ test("HOC-Component labels should be searchable", async ({ page }) => { await page.keyboard.press("Enter"); - marked = await devtools.$$("mark"); - expect(marked.length).toEqual(2); - expect( - await marked[1].evaluate(el => el.hasAttribute("data-marked")), - ).toEqual(true); + await expect(marks).toHaveCount(2); + await expect(marks.nth(1)).toHaveAttribute("data-marked", /.*/); }); diff --git a/test-e2e/tests/hoc-filter-update.test.ts b/test-e2e/tests/hoc-filter-update.test.ts index 54c48c1e..64857c74 100644 --- a/test-e2e/tests/hoc-filter-update.test.ts +++ b/test-e2e/tests/hoc-filter-update.test.ts @@ -4,20 +4,22 @@ import { getTreeItems, gotoTest, locateTreeItem } from "../pw-utils"; test("HOC-Component should work with updates", async ({ page }) => { const { devtools } = await gotoTest(page, "hoc-update"); - await devtools.waitForSelector(locateTreeItem("Wrapped")); + await devtools.locator(locateTreeItem("Wrapped")).waitFor(); - let items = await getTreeItems(devtools); - expect(items).toEqual([ - { name: "Wrapped", hocs: ["withBoof"] }, - { name: "Bar", hocs: ["ForwardRef"] }, - ]); + await expect + .poll(() => getTreeItems(devtools)) + .toEqual([ + { name: "Wrapped", hocs: ["withBoof"] }, + { name: "Bar", hocs: ["ForwardRef"] }, + ]); // Trigger update - await page.click("button"); + await page.locator("button").click(); - items = await getTreeItems(devtools); - expect(items).toEqual([ - { name: "Wrapped", hocs: ["withBoof"] }, - { name: "Foo", hocs: ["Memo"] }, - ]); + await expect + .poll(() => getTreeItems(devtools)) + .toEqual([ + { name: "Wrapped", hocs: ["withBoof"] }, + { name: "Foo", hocs: ["Memo"] }, + ]); }); diff --git a/test-e2e/tests/hoc-filter.test.ts b/test-e2e/tests/hoc-filter.test.ts index 68bc2d51..7a8add11 100644 --- a/test-e2e/tests/hoc-filter.test.ts +++ b/test-e2e/tests/hoc-filter.test.ts @@ -1,30 +1,31 @@ import { test, expect } from "@playwright/test"; -import { gotoTest, waitForPass } from "../pw-utils"; +import { gotoTest } from "../pw-utils"; test("HOC-Component filter should flatten tree", async ({ page }) => { const { devtools } = await gotoTest(page, "hoc"); - await devtools.waitForSelector('[data-testid="tree-item"][data-name="Foo"]'); + await devtools + .locator('[data-testid="tree-item"][data-name="Foo"]') + .first() + .waitFor(); - const items = await devtools - .locator('[data-testid="tree-item"]') - .evaluateAll(els => els.map(el => el.getAttribute("data-name"))); + await expect + .poll(() => + devtools + .locator('[data-testid="tree-item"]') + .evaluateAll(els => els.map(el => el.getAttribute("data-name"))), + ) + .toEqual(["Foo", "Bar", "Anonymous", "Foo", "Last"]); - expect(items).toEqual(["Foo", "Bar", "Anonymous", "Foo", "Last"]); + await devtools.locator('[data-name="Anonymous"]').click(); - await devtools.click('[data-name="Anonymous"]'); + await expect( + devtools.locator('[data-testid="hoc-panel"] .hoc-item'), + ).toHaveText(["ForwardRef"]); - const hocs = await devtools - .locator('[data-testid="hoc-panel"] .hoc-item') - .allInnerTexts(); - expect(hocs).toEqual(["ForwardRef"]); + await devtools.locator('[data-name="Last"]').click(); - await devtools.click('[data-name="Last"]'); - - await waitForPass(async () => { - const hocs = await devtools - .locator('[data-testid="hoc-panel"] .hoc-item') - .allInnerTexts(); - expect(hocs).toEqual(["withBoof", "Memo"]); - }); + await expect( + devtools.locator('[data-testid="hoc-panel"] .hoc-item'), + ).toHaveText(["withBoof", "Memo"]); }); diff --git a/test-e2e/tests/hoc-update.test.ts b/test-e2e/tests/hoc-update.test.ts index 82de7d17..c6f4846f 100644 --- a/test-e2e/tests/hoc-update.test.ts +++ b/test-e2e/tests/hoc-update.test.ts @@ -11,11 +11,10 @@ test("Test HOCs on update", async ({ page }) => { await page.locator("button").click(); - const txt = await page.locator("data-testid=result").textContent(); - expect(txt).toEqual("Counter: 1"); + await expect(page.locator("data-testid=result")).toHaveText("Counter: 1"); - const items = await devtools - .locator("data-testid=hoc-labels") - .allInnerTexts(); - expect(items).toEqual(["Memo", "Memo"]); + await expect(devtools.locator("data-testid=hoc-labels")).toHaveText([ + "Memo", + "Memo", + ]); }); diff --git a/test-e2e/tests/hooks/hook-name.test.ts b/test-e2e/tests/hooks/hook-name.test.ts index 3fb2ba33..89b757aa 100644 --- a/test-e2e/tests/hooks/hook-name.test.ts +++ b/test-e2e/tests/hooks/hook-name.test.ts @@ -1,69 +1,65 @@ import { test, expect } from "@playwright/test"; -import { clickTreeItem, getHooks, gotoTest, waitForPass } from "../../pw-utils"; +import { clickTreeItem, getHooks, gotoTest } from "../../pw-utils"; test("Show custom hook name", async ({ page }) => { const { devtools } = await gotoTest(page, "hooks-name"); // Counter await clickTreeItem(devtools, "Counter"); - await waitForPass(async () => { - expect(await getHooks(devtools)).toEqual([["useState customState", "0"]]); - }); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useState customState", "0"]]); // Callback (Mixed) await clickTreeItem(devtools, "CounterCallback"); - await waitForPass(async () => { - expect(await getHooks(devtools)).toEqual([ + await expect + .poll(() => getHooks(devtools)) + .toEqual([ ["useState counterState", "0"], ["useCallback", "ƒ ()"], ]); - }); // Reducer await clickTreeItem(devtools, "ReducerComponent"); - await waitForPass(async () => { - expect(await getHooks(devtools)).toEqual([ - ["useReducer customReducer", '"foo"'], - ]); - }); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useReducer customReducer", '"foo"']]); // Ref await clickTreeItem(devtools, "RefComponent"); - await waitForPass(async () => { - expect(await getHooks(devtools)).toEqual([["useRef customRef", "0"]]); - }); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useRef customRef", "0"]]); // useMemo await clickTreeItem(devtools, "MemoComponent"); - await waitForPass(async () => { - expect(await getHooks(devtools)).toEqual([["useMemo customMemo", "0"]]); - }); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useMemo customMemo", "0"]]); // Multiple (test ordering) await clickTreeItem(devtools, "Multiple"); - await waitForPass(async () => { - expect(await getHooks(devtools)).toEqual([ + await expect + .poll(() => getHooks(devtools)) + .toEqual([ ["useState foo", "0"], ["useState bar", "0"], ["useState baz", "0"], ]); - }); // Do nothing for invalid callsites await clickTreeItem(devtools, "CallbackOnly"); - await waitForPass(async () => { - expect(await getHooks(devtools)).toEqual([["useCallback", "ƒ ()"]]); - }); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useCallback", "ƒ ()"]]); await clickTreeItem(devtools, "LayoutEffect"); - await waitForPass(async () => { - expect(await getHooks(devtools)).toEqual([["useLayoutEffect", "ƒ ()"]]); - }); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useLayoutEffect", "ƒ ()"]]); await clickTreeItem(devtools, "Effect"); - await waitForPass(async () => { - expect(await getHooks(devtools)).toEqual([["useEffect", "ƒ ()"]]); - }); + await expect.poll(() => getHooks(devtools)).toEqual([["useEffect", "ƒ ()"]]); }); test("Skip custom hook name for user hook", async ({ page }) => { @@ -73,11 +69,11 @@ test("Skip custom hook name for user hook", async ({ page }) => { await devtools .locator('[data-testid="prop-name"]:has-text("useFoo")') .click(); - await waitForPass(async () => { - expect(await getHooks(devtools)).toEqual([ + await expect + .poll(() => getHooks(devtools)) + .toEqual([ ["useFoo", ""], ["useMemo a", '"a"'], ["useMemo b", '"b"'], ]); - }); }); diff --git a/test-e2e/tests/hooks/hooks-depth-limit.test.ts b/test-e2e/tests/hooks/hooks-depth-limit.test.ts index c24fba90..a9956da6 100644 --- a/test-e2e/tests/hooks/hooks-depth-limit.test.ts +++ b/test-e2e/tests/hooks/hooks-depth-limit.test.ts @@ -1,20 +1,17 @@ import { test, expect } from "@playwright/test"; -import { clickHookItem, gotoTest, waitForPass } from "../../pw-utils"; +import { clickHookItem, gotoTest } from "../../pw-utils"; test("Show a deeply nested hook tree and limit value parsing depth", async ({ page, }) => { const { devtools } = await gotoTest(page, "hooks-depth-limit"); - await devtools.waitForSelector('[data-testid="tree-item"]'); + await devtools.locator('[data-testid="tree-item"]').first().waitFor(); - // State update - await waitForPass(async () => { - await devtools.click('[data-name="Hook"]'); - await devtools.waitForSelector('[data-testid="props-row"]'); - const count = await devtools.locator('[data-testid="props-row"]').count(); - expect(count).toBeGreaterThan(0); - }); + await devtools.locator('[data-name="Hook"]').click(); + await expect + .poll(() => devtools.locator('[data-testid="props-row"]').count()) + .toBeGreaterThan(0); await clickHookItem(devtools, "useBrobba"); await clickHookItem(devtools, "useBlaBla"); @@ -37,10 +34,9 @@ test("Show a deeply nested hook tree and limit value parsing depth", async ({ await clickHookItem(devtools, "key6"); await clickHookItem(devtools, "key7"); - const text = await devtools - .locator( + await expect( + devtools.locator( 'form [data-testid="props-row"]:last-child [data-testid="prop-value"]', - ) - .textContent(); - expect(text).toEqual('"…"'); + ), + ).toHaveText('"…"'); }); diff --git a/test-e2e/tests/hooks/hooks-expand-state.test.ts b/test-e2e/tests/hooks/hooks-expand-state.test.ts index e05f4ce9..6f9ff975 100644 --- a/test-e2e/tests/hooks/hooks-expand-state.test.ts +++ b/test-e2e/tests/hooks/hooks-expand-state.test.ts @@ -14,6 +14,5 @@ test("Inspect useRef hook", async ({ page }) => { await devtools.locator(locateHook("useMemo")).waitFor(); await clickHookItem(devtools, "useMemo"); - const hooks = await getHooks(devtools); - expect(hooks.length).toEqual(2); + await expect.poll(async () => (await getHooks(devtools)).length).toEqual(2); }); diff --git a/test-e2e/tests/hooks/hooks-multiple.test.ts b/test-e2e/tests/hooks/hooks-multiple.test.ts index e47845aa..698352bf 100644 --- a/test-e2e/tests/hooks/hooks-multiple.test.ts +++ b/test-e2e/tests/hooks/hooks-multiple.test.ts @@ -13,12 +13,13 @@ test("Show multiple hook names at the same time", async ({ page }) => { await devtools.locator('[data-testid="Hooks"]').waitFor(); await clickHookItem(devtools, "useMemo"); - const hooks = await getHooks(devtools); - expect(hooks).toEqual([ - ["useState", "1"], - ["useState", "2"], - ["useState", "3"], - ["useCallback", "ƒ ()"], - ["useMemo", "6"], - ]); + await expect + .poll(() => getHooks(devtools)) + .toEqual([ + ["useState", "1"], + ["useState", "2"], + ["useState", "3"], + ["useCallback", "ƒ ()"], + ["useMemo", "6"], + ]); }); diff --git a/test-e2e/tests/hooks/hooks-number.test.ts b/test-e2e/tests/hooks/hooks-number.test.ts index 61f607aa..f0959489 100644 --- a/test-e2e/tests/hooks/hooks-number.test.ts +++ b/test-e2e/tests/hooks/hooks-number.test.ts @@ -6,11 +6,9 @@ test("Show hook number", async ({ page }) => { await clickTreeItem(devtools, "App"); - const nums = await devtools - .locator('[data-testid="Hooks"] .hook-number') - .allTextContents(); - - expect(nums).toEqual(["1", "2", "3", "4", "5"]); + await expect( + devtools.locator('[data-testid="Hooks"] .hook-number'), + ).toHaveText(["1", "2", "3", "4", "5"]); }); test("Show hook number only for top level items", async ({ page }) => { @@ -18,12 +16,10 @@ test("Show hook number only for top level items", async ({ page }) => { await clickTreeItem(devtools, "Memo"); - await devtools.click('[data-testid="props-row"] button'); - await devtools.waitForSelector('[data-testid="props-row"][data-depth="2"]'); - - const nums = await devtools - .locator('[data-testid="Hooks"] .hook-number') - .allTextContents(); + await devtools.locator('[data-testid="props-row"] button').first().click(); + await devtools.locator('[data-testid="props-row"][data-depth="2"]').waitFor(); - expect(nums).toEqual(["1"]); + await expect( + devtools.locator('[data-testid="Hooks"] .hook-number'), + ).toHaveText(["1"]); }); diff --git a/test-e2e/tests/hooks/useCallback.test.ts b/test-e2e/tests/hooks/useCallback.test.ts index 84160df8..96b0c103 100644 --- a/test-e2e/tests/hooks/useCallback.test.ts +++ b/test-e2e/tests/hooks/useCallback.test.ts @@ -7,8 +7,9 @@ test("Inspect useCallback hook", async ({ page }) => { await devtools.locator(locateTreeItem("CallbackOnly")).click(); await devtools.locator('[data-testid="Hooks"]').waitFor(); - const hooks = await getHooks(devtools); - expect(hooks).toEqual([["useCallback", "ƒ ()"]]); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useCallback", "ƒ ()"]]); // Should not be collapsable await expect( diff --git a/test-e2e/tests/hooks/useContext-10.5.14.test.ts b/test-e2e/tests/hooks/useContext-10.5.14.test.ts index 27f9822b..7e38fd9e 100644 --- a/test-e2e/tests/hooks/useContext-10.5.14.test.ts +++ b/test-e2e/tests/hooks/useContext-10.5.14.test.ts @@ -12,9 +12,10 @@ test("Inspect useContext hook Preact 10.5.14 (goober)", async ({ page }) => { .click(); await devtools.locator('[data-testid="Hooks"] [data-depth="2"]').click(); - const hooks = await getHooks(devtools); - expect(hooks).toEqual([ - ["useTheme", ""], - ["useContext", "undefined"], - ]); + await expect + .poll(() => getHooks(devtools)) + .toEqual([ + ["useTheme", ""], + ["useContext", "undefined"], + ]); }); diff --git a/test-e2e/tests/hooks/useContext.test.ts b/test-e2e/tests/hooks/useContext.test.ts index c278180f..15db22e9 100644 --- a/test-e2e/tests/hooks/useContext.test.ts +++ b/test-e2e/tests/hooks/useContext.test.ts @@ -1,10 +1,5 @@ import { test, expect } from "@playwright/test"; -import { - getHooks, - gotoTest, - locateTreeItem, - waitForPass, -} from "../../pw-utils"; +import { getHooks, gotoTest, locateTreeItem } from "../../pw-utils"; test("Inspect useContext hook", async ({ page }) => { const { devtools } = await gotoTest(page, "hooks"); @@ -12,8 +7,9 @@ test("Inspect useContext hook", async ({ page }) => { await devtools.locator(locateTreeItem("ContextComponent")).click(); await devtools.locator('[data-testid="Hooks"]').waitFor(); - const hooks = await getHooks(devtools); - expect(hooks).toEqual([["useContext", '"foobar"']]); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useContext", '"foobar"']]); // Should not be collapsable await expect( @@ -29,8 +25,5 @@ test("Inspect useContext hook", async ({ page }) => { await devtools.locator(locateTreeItem("ContextNoProvider")).click(); await devtools.locator('[data-testid="Hooks"]').waitFor(); - await waitForPass(async () => { - const hooks = await getHooks(devtools); - expect(hooks).toEqual([["useContext", "0"]]); - }); + await expect.poll(() => getHooks(devtools)).toEqual([["useContext", "0"]]); }); diff --git a/test-e2e/tests/hooks/useDebugValue-complex.test.ts b/test-e2e/tests/hooks/useDebugValue-complex.test.ts index b6d1855a..920f57ee 100644 --- a/test-e2e/tests/hooks/useDebugValue-complex.test.ts +++ b/test-e2e/tests/hooks/useDebugValue-complex.test.ts @@ -7,6 +7,7 @@ test("Show custom debug value complex", async ({ page }) => { await devtools.locator(locateTreeItem("App")).click(); await devtools.locator('[data-testid="Hooks"]').waitFor(); - const hooks = await getHooks(devtools); - expect(hooks).toEqual([["useFoo", '{foo: "bar"}']]); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useFoo", '{foo: "bar"}']]); }); diff --git a/test-e2e/tests/hooks/useDebugValue.test.ts b/test-e2e/tests/hooks/useDebugValue.test.ts index a74dc458..365e0213 100644 --- a/test-e2e/tests/hooks/useDebugValue.test.ts +++ b/test-e2e/tests/hooks/useDebugValue.test.ts @@ -7,11 +7,13 @@ test("Show custom debug value", async ({ page }) => { await devtools.locator(locateTreeItem("DebugValue")).click(); await devtools.locator('[data-testid="Hooks"]').waitFor(); - let hooks = await getHooks(devtools); - expect(hooks).toEqual([["useMyHook", '"Offline"']]); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useMyHook", '"Offline"']]); await page.locator('[data-testid="debug-hook-toggle"]').click(); - hooks = await getHooks(devtools); - expect(hooks).toEqual([["useMyHook", '"Online"']]); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useMyHook", '"Online"']]); }); diff --git a/test-e2e/tests/hooks/useDeepHook.test.ts b/test-e2e/tests/hooks/useDeepHook.test.ts index ad0fbd74..68860cab 100644 --- a/test-e2e/tests/hooks/useDeepHook.test.ts +++ b/test-e2e/tests/hooks/useDeepHook.test.ts @@ -14,22 +14,18 @@ test("Inspect deep hook tree", async ({ page }) => { .first() .waitFor(); - let hooks = await getHooks(devtools); - expect(hooks.length).toEqual(2); + const hookCount = async () => (await getHooks(devtools)).length; + await expect.poll(hookCount).toEqual(2); await devtools.locator(locateHook("useBoof")).click(); - hooks = await getHooks(devtools); - expect(hooks.length).toEqual(3); + await expect.poll(hookCount).toEqual(3); await devtools.locator(locateHook("useBob")).click(); - hooks = await getHooks(devtools); - expect(hooks.length).toEqual(4); + await expect.poll(hookCount).toEqual(4); await devtools.locator(locateHook("useFoo")).click(); - hooks = await getHooks(devtools); - expect(hooks.length).toEqual(5); + await expect.poll(hookCount).toEqual(5); await devtools.locator(locateHook("useBar")).click(); - hooks = await getHooks(devtools); - expect(hooks.length).toEqual(7); + await expect.poll(hookCount).toEqual(7); }); diff --git a/test-e2e/tests/hooks/useEffect.test.ts b/test-e2e/tests/hooks/useEffect.test.ts index 7f598d00..9366e516 100644 --- a/test-e2e/tests/hooks/useEffect.test.ts +++ b/test-e2e/tests/hooks/useEffect.test.ts @@ -7,8 +7,7 @@ test("Inspect useEffect hook", async ({ page }) => { await devtools.locator(locateTreeItem("Effect")).click(); await devtools.locator('[data-testid="Hooks"]').waitFor(); - const hooks = await getHooks(devtools); - expect(hooks).toEqual([["useEffect", "ƒ ()"]]); + await expect.poll(() => getHooks(devtools)).toEqual([["useEffect", "ƒ ()"]]); // Should not be collapsable await expect( diff --git a/test-e2e/tests/hooks/useErrorBoundary.test.ts b/test-e2e/tests/hooks/useErrorBoundary.test.ts index 319e887a..55f8d3a0 100644 --- a/test-e2e/tests/hooks/useErrorBoundary.test.ts +++ b/test-e2e/tests/hooks/useErrorBoundary.test.ts @@ -7,8 +7,9 @@ test("Inspect useErrorBoundary hook", async ({ page }) => { await devtools.locator(locateTreeItem("ErrorBoundary1")).click(); await devtools.locator('[data-testid="Hooks"]').waitFor(); - let hooks = await getHooks(devtools); - expect(hooks).toEqual([["useErrorBoundary", ""]]); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useErrorBoundary", ""]]); // Should not be collapsable await expect( @@ -22,6 +23,7 @@ test("Inspect useErrorBoundary hook", async ({ page }) => { // Error boundary with callback await devtools.locator(locateTreeItem("ErrorBoundary2")).click(); - hooks = await getHooks(devtools); - expect(hooks).toEqual([["useErrorBoundary", "ƒ ()"]]); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useErrorBoundary", "ƒ ()"]]); }); diff --git a/test-e2e/tests/hooks/useImperativeHandle.test.ts b/test-e2e/tests/hooks/useImperativeHandle.test.ts index d9b41af3..e80e5dfb 100644 --- a/test-e2e/tests/hooks/useImperativeHandle.test.ts +++ b/test-e2e/tests/hooks/useImperativeHandle.test.ts @@ -7,8 +7,9 @@ test("Inspect useImperativeHandle hook", async ({ page }) => { await devtools.locator(locateTreeItem("ImperativeHandle")).click(); await devtools.locator('[data-testid="Hooks"]').waitFor(); - const hooks = await getHooks(devtools); - expect(hooks).toEqual([["useImperativeHandle", "ƒ ()"]]); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useImperativeHandle", "ƒ ()"]]); // Should not be collapsable await expect( diff --git a/test-e2e/tests/hooks/useLayoutEffect.test.ts b/test-e2e/tests/hooks/useLayoutEffect.test.ts index 7b6a5d03..66dbd620 100644 --- a/test-e2e/tests/hooks/useLayoutEffect.test.ts +++ b/test-e2e/tests/hooks/useLayoutEffect.test.ts @@ -7,8 +7,9 @@ test("Inspect useLayoutEffect hook", async ({ page }) => { await devtools.locator(locateTreeItem("LayoutEffect")).click(); await devtools.locator('[data-testid="Hooks"]').waitFor(); - const hooks = await getHooks(devtools); - expect(hooks).toEqual([["useLayoutEffect", "ƒ ()"]]); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useLayoutEffect", "ƒ ()"]]); // Should not be collapsable await expect( diff --git a/test-e2e/tests/hooks/useMemo.test.ts b/test-e2e/tests/hooks/useMemo.test.ts index ea55cb6f..8180a743 100644 --- a/test-e2e/tests/hooks/useMemo.test.ts +++ b/test-e2e/tests/hooks/useMemo.test.ts @@ -7,8 +7,7 @@ test("Inspect useMemo hook", async ({ page }) => { await devtools.locator(locateTreeItem("Memo")).click(); await devtools.locator('[data-testid="Hooks"]').waitFor(); - const hooks = await getHooks(devtools); - expect(hooks).toEqual([["useMemo", "0"]]); + await expect.poll(() => getHooks(devtools)).toEqual([["useMemo", "0"]]); // Should not be collapsable await expect( diff --git a/test-e2e/tests/hooks/useRef-element.test.ts b/test-e2e/tests/hooks/useRef-element.test.ts index 2f4296d0..7ec22c96 100644 --- a/test-e2e/tests/hooks/useRef-element.test.ts +++ b/test-e2e/tests/hooks/useRef-element.test.ts @@ -7,8 +7,9 @@ test("Inspect useRef-element hook", async ({ page }) => { await devtools.locator(locateTreeItem("App")).click(); await devtools.locator('[data-testid="Hooks"]').waitFor(); - const hooks = await getHooks(devtools); - expect(hooks).toEqual([["useRef", ""]]); + await expect + .poll(() => getHooks(devtools)) + .toEqual([["useRef", ""]]); // Should not be collapsable await expect( diff --git a/test-e2e/tests/hooks/useRef.test.ts b/test-e2e/tests/hooks/useRef.test.ts index f070b199..2ac3d966 100644 --- a/test-e2e/tests/hooks/useRef.test.ts +++ b/test-e2e/tests/hooks/useRef.test.ts @@ -7,8 +7,7 @@ test("Inspect useRef hook", async ({ page }) => { await devtools.locator(locateTreeItem("RefComponent")).click(); await devtools.locator('[data-testid="Hooks"]').waitFor(); - const hooks = await getHooks(devtools); - expect(hooks).toEqual([["useRef", "0"]]); + await expect.poll(() => getHooks(devtools)).toEqual([["useRef", "0"]]); // Should not be collapsable await expect( diff --git a/test-e2e/tests/hooks/useState.test.ts b/test-e2e/tests/hooks/useState.test.ts index a53b2d04..fc8a2d02 100644 --- a/test-e2e/tests/hooks/useState.test.ts +++ b/test-e2e/tests/hooks/useState.test.ts @@ -7,8 +7,7 @@ test("Inspect useState hook", async ({ page }) => { await devtools.locator(locateTreeItem("Counter")).click(); await devtools.locator('[data-testid="Hooks"]').waitFor(); - const hooks = await getHooks(devtools); - expect(hooks).toEqual([["useState", "0"]]); + await expect.poll(() => getHooks(devtools)).toEqual([["useState", "0"]]); // Should not be collapsable await expect( @@ -22,6 +21,5 @@ test("Inspect useState hook", async ({ page }) => { await page.keyboard.press("ArrowUp"); await page.keyboard.press("Enter"); - const text = await page.locator('[data-testid="result"]').textContent(); - expect(text).toEqual("Counter: 1"); + await expect(page.locator('[data-testid="result"]')).toHaveText("Counter: 1"); }); diff --git a/test-e2e/tests/inspect-click.test.ts b/test-e2e/tests/inspect-click.test.ts index e2dc065c..0f7e340f 100644 --- a/test-e2e/tests/inspect-click.test.ts +++ b/test-e2e/tests/inspect-click.test.ts @@ -4,11 +4,10 @@ import { gotoTest } from "../pw-utils"; test("Don't trigger events on click during inspection", async ({ page }) => { const { devtools } = await gotoTest(page, "counter"); - let txt = await page.locator('[data-testid="result"]').textContent(); + await expect(page.locator('[data-testid="result"]')).toHaveText("Counter: 0"); await devtools.locator('[data-testid="inspect-btn"]').click(); await page.locator("button").click(); - txt = await page.locator('[data-testid="result"]').textContent(); - expect(txt).toEqual("Counter: 0"); + await expect(page.locator('[data-testid="result"]')).toHaveText("Counter: 0"); }); diff --git a/test-e2e/tests/inspect-key.test.ts b/test-e2e/tests/inspect-key.test.ts index 1b5302cd..ceecdc79 100644 --- a/test-e2e/tests/inspect-key.test.ts +++ b/test-e2e/tests/inspect-key.test.ts @@ -1,5 +1,5 @@ import { test, expect } from "@playwright/test"; -import { gotoTest, waitForPass } from "../pw-utils"; +import { gotoTest } from "../pw-utils"; test("Show vnode key in the sidebar", async ({ page }) => { const { devtools } = await gotoTest(page, "keys"); @@ -8,24 +8,18 @@ test("Show vnode key in the sidebar", async ({ page }) => { await devtools .locator(`[data-testid="tree-item"]:has-text('key="ABC"')`) .click(); - await devtools.waitForSelector('[data-testid="key-panel"]'); + await devtools.locator('[data-testid="key-panel"]').waitFor(); - const text = await devtools - .locator('[data-testid="vnode-key"]') - .textContent(); - expect(text).toEqual("ABC"); + await expect(devtools.locator('[data-testid="vnode-key"]')).toHaveText("ABC"); const copy = '[data-testid="key-panel"] button[title="Copy Key"]'; - await devtools.click(copy); + await devtools.locator(copy).click(); - const clipboard = await page.evaluate(() => navigator.clipboard.readText()); - expect(JSON.parse(clipboard)).toEqual("ABC"); + await expect + .poll(() => page.evaluate(() => navigator.clipboard.readText())) + .toEqual(JSON.stringify("ABC")); // Check that the keypanel is not present for keyless components - await devtools.click('[data-name="NoKey"]'); - await waitForPass(async () => { - expect(await devtools.locator('[data-testid="key-panel"]').count()).toEqual( - 0, - ); - }); + await devtools.locator('[data-name="NoKey"]').click(); + await expect(devtools.locator('[data-testid="key-panel"]')).toHaveCount(0); }); diff --git a/test-e2e/tests/inspect-map-set.test.ts b/test-e2e/tests/inspect-map-set.test.ts index 054badba..84a934bf 100644 --- a/test-e2e/tests/inspect-map-set.test.ts +++ b/test-e2e/tests/inspect-map-set.test.ts @@ -4,79 +4,83 @@ import { getHooks, getProps, gotoTest, locateTreeItem } from "../pw-utils"; test("Inspect Map and Set objects", async ({ page }) => { const { devtools } = await gotoTest(page, "inspect-map-set"); - await devtools.click(locateTreeItem("App")); - await devtools.waitForSelector('[data-testid="Props"]'); + await devtools.locator(locateTreeItem("App")).click(); + await devtools.locator('[data-testid="Props"]').waitFor(); - const count = await devtools.locator('[data-testid="props-row"]').count(); - expect(count).toEqual(2); + await expect(devtools.locator('[data-testid="props-row"]')).toHaveCount(2); - const props = await getProps(devtools); - expect(props).toEqual({ - set: "Set(1) [{foo: 123}]", - map: "Map(1) [[{foo: 123}, 123]]", - }); + await expect + .poll(() => getProps(devtools)) + .toEqual({ + set: "Set(1) [{foo: 123}]", + map: "Map(1) [[{foo: 123}, 123]]", + }); // Edit set - await devtools.click('[data-testid="prop-name"][data-type="set"]'); - await devtools.click('[data-testid="props-row"][data-depth="2"]'); - await devtools.fill( - '[data-testid="props-row"][data-depth="3"] input', - "12345", - ); + await devtools.locator('[data-testid="prop-name"][data-type="set"]').click(); + await devtools.locator('[data-testid="props-row"][data-depth="2"]').click(); + await devtools + .locator('[data-testid="props-row"][data-depth="3"] input') + .fill("12345"); await page.keyboard.press("Enter"); - let text = await page.locator("#json-set").textContent(); - expect(text).toEqual(JSON.stringify([{ foo: 12345 }], null, 2)); + await expect + .poll(() => page.locator("#json-set").textContent()) + .toEqual(JSON.stringify([{ foo: 12345 }], null, 2)); // Close set - await devtools.click('[data-testid="prop-name"][data-type="set"]'); + await devtools.locator('[data-testid="prop-name"][data-type="set"]').click(); // Edit map value - await devtools.click('[data-type="map"]'); - await devtools.click('[data-testid="props-row"][data-depth="2"]'); - await devtools.fill( - '[data-testid="props-row"][data-depth="3"] input', - "12345", - ); + await devtools.locator('[data-type="map"]').click(); + await devtools.locator('[data-testid="props-row"][data-depth="2"]').click(); + await devtools + .locator('[data-testid="props-row"][data-depth="3"] input') + .fill("12345"); await page.keyboard.press("Enter"); - text = await page.locator("#json-map").textContent(); - expect(text).toEqual(JSON.stringify([[{ foo: 123 }, 12345]], null, 2)); + await expect + .poll(() => page.locator("#json-map").textContent()) + .toEqual(JSON.stringify([[{ foo: 123 }, 12345]], null, 2)); // Edit map key - await devtools.click('[data-depth="3"] [data-testid="prop-name"]'); - await devtools.fill('[name="root.map.0.0.foo"]', "111"); + await devtools + .locator('[data-depth="3"] [data-testid="prop-name"]') + .first() + .click(); + await devtools.locator('[name="root.map.0.0.foo"]').fill("111"); await page.keyboard.press("Enter"); - text = await page.locator("#json-map").textContent(); - expect(text).toEqual(JSON.stringify([[{ foo: 111 }, 12345]], null, 2)); + await expect + .poll(() => page.locator("#json-map").textContent()) + .toEqual(JSON.stringify([[{ foo: 111 }, 12345]], null, 2)); }); test("Inspect Map and Set objects in hooks", async ({ page }) => { const { devtools } = await gotoTest(page, "inspect-map-set-hooks"); - await devtools.click(locateTreeItem("MapView")); - await devtools.waitForSelector('[data-testid="Hooks"]'); + await devtools.locator(locateTreeItem("MapView")).click(); + await devtools.locator('[data-testid="Hooks"]').waitFor(); - const count = await devtools.locator('[data-testid="props-row"]').count(); - expect(count).toEqual(3); + await expect(devtools.locator('[data-testid="props-row"]')).toHaveCount(3); - const props = await getHooks(devtools); - expect(props).toEqual([ - ["useMemo", "Map(0) []"], - ["useMemo", "Map(1) [[1, 2]]"], - ["useState", "Map(1) [[1, 2]]"], - ]); + await expect + .poll(() => getHooks(devtools)) + .toEqual([ + ["useMemo", "Map(0) []"], + ["useMemo", "Map(1) [[1, 2]]"], + ["useState", "Map(1) [[1, 2]]"], + ]); // TODO: Fix editing for both Map and Set - await devtools.click(locateTreeItem("SetView")); - await devtools.waitForSelector('[data-testid="Hooks"]'); + await devtools.locator(locateTreeItem("SetView")).click(); + await devtools.locator('[data-testid="Hooks"]').waitFor(); - const count2 = await devtools.locator('[data-testid="props-row"]').count(); - expect(count2).toEqual(3); + await expect(devtools.locator('[data-testid="props-row"]')).toHaveCount(3); - const props2 = await getHooks(devtools); - expect(props2).toEqual([ - ["useMemo", "Set(0) []"], - ["useMemo", "Set(2) [1, 2]"], - ["useState", "Set(2) [1, 2]"], - ]); + await expect + .poll(() => getHooks(devtools)) + .toEqual([ + ["useMemo", "Set(0) []"], + ["useMemo", "Set(2) [1, 2]"], + ["useState", "Set(2) [1, 2]"], + ]); }); diff --git a/test-e2e/tests/inspect-non-vnode.test.ts b/test-e2e/tests/inspect-non-vnode.test.ts index f1bb580a..5191ef96 100644 --- a/test-e2e/tests/inspect-non-vnode.test.ts +++ b/test-e2e/tests/inspect-non-vnode.test.ts @@ -4,17 +4,20 @@ import { gotoTest } from "../pw-utils"; test("Inspect should only parse vnodes as vnodes #114", async ({ page }) => { const { devtools } = await gotoTest(page, "non-vnode"); - await devtools.click('[data-testid="tree-item"]'); - await devtools.waitForSelector('[name="new-prop-name"]'); + await devtools.locator('[data-testid="tree-item"]').click(); + await devtools.locator('[name="new-prop-name"]').waitFor(); - const values = await devtools - .locator('[data-testid="Props"] [data-testid="props-row"] [data-type]') - .evaluateAll(els => els.map(el => el.getAttribute("data-type"))); + await expect + .poll(() => + devtools + .locator('[data-testid="Props"] [data-testid="props-row"] [data-type]') + .evaluateAll(els => els.map(el => el.getAttribute("data-type"))), + ) + .toEqual(["blob", "object", "vnode", "vnode"]); - expect(values).toEqual(["blob", "object", "vnode", "vnode"]); - - const blob = await devtools - .locator('[data-testid="props-row"]:first-child [data-testid="prop-value"]') - .textContent(); - expect(blob).toEqual("Blob {}"); + await expect( + devtools.locator( + '[data-testid="props-row"]:first-child [data-testid="prop-value"]', + ), + ).toHaveText("Blob {}"); }); diff --git a/test-e2e/tests/inspect-owner-memo.test.ts b/test-e2e/tests/inspect-owner-memo.test.ts index a729c7c1..d3c0a092 100644 --- a/test-e2e/tests/inspect-owner-memo.test.ts +++ b/test-e2e/tests/inspect-owner-memo.test.ts @@ -4,20 +4,15 @@ import { getOwners, gotoTest } from "../pw-utils"; test("Inspect owner information with filtered nodes", async ({ page }) => { const { devtools } = await gotoTest(page, "static-subtree"); - await devtools.click('[data-name="App"]'); + await devtools.locator('[data-name="App"]').click(); + await expect.poll(() => getOwners(devtools)).toEqual([]); - let owners = await getOwners(devtools); - expect(owners).toEqual([]); + await devtools.locator('[data-name="Static"]').first().click(); + await expect.poll(() => getOwners(devtools)).toEqual(["App"]); - await devtools.click('[data-name="Static"]'); - owners = await getOwners(devtools); - expect(owners).toEqual(["App"]); + await devtools.locator('[data-name="Foo"]').first().click(); + await expect.poll(() => getOwners(devtools)).toEqual(["Static", "App"]); - await devtools.click('[data-name="Foo"]'); - owners = await getOwners(devtools); - expect(owners).toEqual(["Static", "App"]); - - await devtools.click('[data-name="Display"]'); - owners = await getOwners(devtools); - expect(owners).toEqual(["App"]); + await devtools.locator('[data-name="Display"]').first().click(); + await expect.poll(() => getOwners(devtools)).toEqual(["App"]); }); diff --git a/test-e2e/tests/inspect-owner-update.test.ts b/test-e2e/tests/inspect-owner-update.test.ts index 55dc2ffe..d9f6e726 100644 --- a/test-e2e/tests/inspect-owner-update.test.ts +++ b/test-e2e/tests/inspect-owner-update.test.ts @@ -4,11 +4,10 @@ import { getOwners, gotoTest } from "../pw-utils"; test("Inspect owner information with updated nodes", async ({ page }) => { const { devtools } = await gotoTest(page, "update-middle"); - await page.click("button"); + await page.locator("button").click(); - await devtools.waitForSelector('[data-testid="elements-tree"]'); + await devtools.locator('[data-testid="elements-tree"]').waitFor(); - await devtools.click('[data-name="ListItem"]'); - const owners = await getOwners(devtools); - expect(owners).toEqual(["Counter", "App"]); + await devtools.locator('[data-name="ListItem"]').click(); + await expect.poll(() => getOwners(devtools)).toEqual(["Counter", "App"]); }); diff --git a/test-e2e/tests/inspect-owner.test.ts b/test-e2e/tests/inspect-owner.test.ts index beb351cd..bb6bf3e9 100644 --- a/test-e2e/tests/inspect-owner.test.ts +++ b/test-e2e/tests/inspect-owner.test.ts @@ -4,36 +4,29 @@ import { getOwners, gotoTest, locateTreeItem } from "../pw-utils"; test("Inspect owner information", async ({ page }) => { const { devtools } = await gotoTest(page, "update-all"); - await devtools.click(locateTreeItem("App")); + await devtools.locator(locateTreeItem("App")).click(); + await expect.poll(() => getOwners(devtools)).toEqual([]); - let owners = await getOwners(devtools); - expect(owners).toEqual([]); + await devtools.locator(locateTreeItem("Props")).click(); + await expect.poll(() => getOwners(devtools)).toEqual(["App"]); - await devtools.click(locateTreeItem("Props")); - owners = await getOwners(devtools); - expect(owners).toEqual(["App"]); + await devtools.locator(locateTreeItem("State")).click(); + await expect.poll(() => getOwners(devtools)).toEqual(["App"]); - await devtools.click(locateTreeItem("State")); - owners = await getOwners(devtools); - expect(owners).toEqual(["App"]); + await devtools.locator(locateTreeItem("Context")).click(); + await expect.poll(() => getOwners(devtools)).toEqual(["App"]); - await devtools.click(locateTreeItem("Context")); - owners = await getOwners(devtools); - expect(owners).toEqual(["App"]); + await devtools.locator(locateTreeItem("Provider")).click(); + await expect.poll(() => getOwners(devtools)).toEqual(["Context", "App"]); - await devtools.click(locateTreeItem("Provider")); - owners = await getOwners(devtools); - expect(owners).toEqual(["Context", "App"]); + await devtools.locator(locateTreeItem("Consumer")).click(); + await expect.poll(() => getOwners(devtools)).toEqual(["Context", "App"]); - await devtools.click(locateTreeItem("Consumer")); - owners = await getOwners(devtools); - expect(owners).toEqual(["Context", "App"]); + await devtools.locator(locateTreeItem("LegacyContext")).click(); + await expect.poll(() => getOwners(devtools)).toEqual(["App"]); - await devtools.click(locateTreeItem("LegacyContext")); - owners = await getOwners(devtools); - expect(owners).toEqual(["App"]); - - await devtools.click(locateTreeItem("LegacyConsumer")); - owners = await getOwners(devtools); - expect(owners).toEqual(["LegacyContext", "App"]); + await devtools.locator(locateTreeItem("LegacyConsumer")).click(); + await expect + .poll(() => getOwners(devtools)) + .toEqual(["LegacyContext", "App"]); }); diff --git a/test-e2e/tests/inspect-props-sort.test.ts b/test-e2e/tests/inspect-props-sort.test.ts index 5d0e0fb7..01590d34 100644 --- a/test-e2e/tests/inspect-props-sort.test.ts +++ b/test-e2e/tests/inspect-props-sort.test.ts @@ -4,15 +4,15 @@ import { gotoTest, locateTreeItem, getProps } from "../pw-utils"; test("Inspect should sort object keys", async ({ page }) => { const { devtools } = await gotoTest(page, "props"); - await devtools.click(locateTreeItem("NestedObjProps")); - await devtools.waitForSelector('[data-testid="Props"]'); + await devtools.locator(locateTreeItem("NestedObjProps")).click(); + await devtools.locator('[data-testid="Props"]').waitFor(); + await devtools.locator('[data-testid="props-row"]').first().waitFor(); - await devtools.waitForSelector('[data-testid="props-row"]'); - - const props = await getProps(devtools); - expect(props).toEqual({ - a: "1", - b: "{a: 1, b: 2, c: 3}", - c: "3", - }); + await expect + .poll(() => getProps(devtools)) + .toEqual({ + a: "1", + b: "{a: 1, b: 2, c: 3}", + c: "3", + }); }); diff --git a/test-e2e/tests/inspect-select.test.ts b/test-e2e/tests/inspect-select.test.ts index dde22292..b12121f3 100644 --- a/test-e2e/tests/inspect-select.test.ts +++ b/test-e2e/tests/inspect-select.test.ts @@ -6,15 +6,17 @@ test("Should inspect during picking", async ({ page }) => { const elem1 = '[data-testid="tree-item"][data-name="Counter"]'; const prop = '[data-testid="Props"] [data-testid="props-row"]'; - await devtools.click(elem1); + await devtools.locator(elem1).click(); await expect(devtools.locator(prop)).toHaveCount(0); const target = '[data-testid="result"]'; const inspect = '[data-testid="inspect-btn"]'; - await devtools.click(inspect); - let active = await devtools.locator(inspect).getAttribute("data-active"); - expect(active).toEqual("true"); + await devtools.locator(inspect).click(); + await expect(devtools.locator(inspect)).toHaveAttribute( + "data-active", + "true", + ); await page.hover(target); @@ -26,15 +28,19 @@ test("Should inspect during picking", async ({ page }) => { await expect(devtools.locator(prop)).toHaveCount(1); // Should only fire inspect event once per id - const inspects = (await getLog(page)).filter( - x => x.type === "inspect-result", - ); - expect(inspects.length).toEqual(2); + await expect + .poll( + async () => + (await getLog(page)).filter(x => x.type === "inspect-result").length, + ) + .toEqual(2); // Should select new node in element tree - await page.click(target); - active = await devtools.locator(inspect).getAttribute("data-active"); - expect(active).toEqual("false"); + await page.locator(target).click(); + await expect(devtools.locator(inspect)).toHaveAttribute( + "data-active", + "false", + ); // ...and display the newly inspected data await expect(devtools.locator(prop)).toHaveCount(1); diff --git a/test-e2e/tests/inspect-signal.test.ts b/test-e2e/tests/inspect-signal.test.ts index 22287637..876516c1 100644 --- a/test-e2e/tests/inspect-signal.test.ts +++ b/test-e2e/tests/inspect-signal.test.ts @@ -10,13 +10,12 @@ test("Show signal in props and update value", async ({ page }) => { await devtools.locator(locateTreeItem("Display")).first().click(); - await devtools.waitForSelector('[data-testid="props-row"]'); - - let preview = await devtools - .locator('[data-testid="prop-value"]:has-text("Signal")') - .textContent(); + await devtools.locator('[data-testid="props-row"]').first().waitFor(); - expect(preview).toEqual("ƒ Signal (0)"); + const preview = devtools.locator( + '[data-testid="prop-value"]:has-text("Signal")', + ); + await expect(preview).toHaveText("ƒ Signal (0)"); await devtools.locator('[data-type="signal"]').click(); await devtools.locator('input[name="root.value.value"]').focus(); @@ -25,25 +24,19 @@ test("Show signal in props and update value", async ({ page }) => { await page.locator("#result:has-text('value: 1,double: 2')").waitFor(); - preview = await devtools - .locator('[data-testid="prop-value"]:has-text("Signal")') - .textContent(); - - expect(preview).toEqual("ƒ Signal (1)"); + await expect(preview).toHaveText("ƒ Signal (1)"); }); test("Show computed signal as readonly", async ({ page }) => { const { devtools } = await gotoTest(page, "signals"); - await devtools.click(locateTreeItem("Display") + ":nth-child(2n)"); + await devtools.locator(locateTreeItem("Display") + ":nth-child(2n)").click(); - await devtools.waitForSelector('[data-testid="props-row"]'); - - const preview = await devtools - .locator('[data-testid="prop-value"]:has-text("Signal")') - .textContent(); + await devtools.locator('[data-testid="props-row"]').first().waitFor(); - expect(preview).toEqual("ƒ computed Signal (0)"); + await expect( + devtools.locator('[data-testid="prop-value"]:has-text("Signal")'), + ).toHaveText("ƒ computed Signal (0)"); await devtools.locator('[data-type="signal"]').click(); diff --git a/test-e2e/tests/inspect-truncate.test.ts b/test-e2e/tests/inspect-truncate.test.ts index d27324ca..f0f43805 100644 --- a/test-e2e/tests/inspect-truncate.test.ts +++ b/test-e2e/tests/inspect-truncate.test.ts @@ -1,18 +1,19 @@ import { test, expect } from "@playwright/test"; -import { gotoTest, waitForPass } from "../pw-utils"; +import { gotoTest } from "../pw-utils"; test("Format inspected data", async ({ page }) => { const { devtools } = await gotoTest(page, "truncate"); await devtools.locator('[data-name="App"]').click(); - await waitForPass(async () => { - let texts = await devtools - .locator('[data-testid="props-row"]') - .allInnerTexts(); - - texts = texts.map(x => x.replace(/\n/g, "")); - expect(texts).toEqual([ + await expect + .poll(async () => { + const texts = await devtools + .locator('[data-testid="props-row"]') + .allInnerTexts(); + return texts.map(x => x.replace(/\n/g, "")); + }) + .toEqual([ "arr[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39]", "blobBlob {}", 'obj{props: null, type: "foo"}', @@ -20,5 +21,4 @@ test("Format inspected data", async ({ page }) => { "vnode
", "vnode2", ]); - }); }); diff --git a/test-e2e/tests/inspect-virtual.test.ts b/test-e2e/tests/inspect-virtual.test.ts index 9b74a5f9..1a414c6d 100644 --- a/test-e2e/tests/inspect-virtual.test.ts +++ b/test-e2e/tests/inspect-virtual.test.ts @@ -1,27 +1,23 @@ import { test, expect } from "@playwright/test"; -import { gotoTest, wait } from "../pw-utils"; +import { gotoTest } from "../pw-utils"; test("Scroll a virtualized element into view #333", async ({ page }) => { const { devtools } = await gotoTest(page, "deep-tree-2"); const selector = '[data-name="App"]'; - await devtools.waitForSelector(selector); + await devtools.locator(selector).waitFor(); - await devtools.click('[data-testid="inspect-btn"]'); + await devtools.locator('[data-testid="inspect-btn"]').click(); await page.evaluate(() => { const target = document.querySelector("#select-me") as HTMLHeadingElement; target.scrollIntoView(); }); - await wait(1000); await page.hover("#select-me"); - await page.waitForSelector('[data-testid="highlight"]'); - await wait(2000); - await page.waitForSelector('[data-testid="select-me"]'); - await wait(1000); + await page.locator('[data-testid="highlight"]').waitFor(); + await page.locator('[data-testid="select-me"]').waitFor(); - const text = await devtools.locator('[data-selected="true"]').textContent(); - expect(text).toEqual("Foo"); + await expect(devtools.locator('[data-selected="true"]')).toHaveText("Foo"); }); diff --git a/test-e2e/tests/inspect.test.ts b/test-e2e/tests/inspect.test.ts index 7b7e4ed1..03bbaf2e 100644 --- a/test-e2e/tests/inspect.test.ts +++ b/test-e2e/tests/inspect.test.ts @@ -1,24 +1,29 @@ import { test, expect } from "@playwright/test"; -import { getLog, gotoTest, wait } from "../pw-utils"; +import { getLog, gotoTest } from "../pw-utils"; test("Inspect should select node in elements panel", async ({ page }) => { const { devtools } = await gotoTest(page, "counter"); await devtools.locator("data-testid=inspect-btn").click(); await page.hover("[data-testid=result]"); - // Wait for possible flickering to occur - await wait(500); - await page.click("[data-testid=result]"); + await expect + .poll( + async () => + (await getLog(page)).filter(x => x.type === "start-picker").length, + ) + .toEqual(1); - // Wait for possible flickering to occur - await wait(500); + await page.locator("[data-testid=result]").click(); - const log = await getLog(page); - expect(log.filter(x => x.type === "start-picker").length).toEqual(1); - expect(log.filter(x => x.type === "stop-picker").length).toEqual(1); - // expect(log.filter(x => x.type === "start-picker").length).to.equal(1); - // expect(log.filter(x => x.type === "stop-picker").length).to.equal(1); + await expect + .poll( + async () => + (await getLog(page)).filter(x => x.type === "stop-picker").length, + ) + .toEqual(1); - await devtools.locator('[data-selected="true"]:has-text("Display)'); + await devtools + .locator('[data-selected="true"]:has-text("Display")') + .waitFor(); }); diff --git a/test-e2e/tests/new-prop.test.ts b/test-e2e/tests/new-prop.test.ts index f304c833..ca73319b 100644 --- a/test-e2e/tests/new-prop.test.ts +++ b/test-e2e/tests/new-prop.test.ts @@ -1,28 +1,27 @@ import { expect, test } from "@playwright/test"; -import { getProps, gotoTest, locateTreeItem, wait } from "../pw-utils"; +import { getProps, gotoTest, locateTreeItem } from "../pw-utils"; test("Add new props", async ({ page }) => { const { devtools } = await gotoTest(page, "counter"); - await devtools.click(locateTreeItem("Display")); + await devtools.locator(locateTreeItem("Display")).click(); - await devtools.waitForSelector('[data-testid="props-row"]'); + await devtools.locator('[data-testid="props-row"]').first().waitFor(); const propName = 'input[name="new-prop-name"]'; const propValue = 'input[name="new-prop-value"]'; - await devtools.fill(propName, "foo"); - await devtools.fill(propValue, "42"); + await devtools.locator(propName).fill("foo"); + await devtools.locator(propValue).fill("42"); await page.keyboard.press("Enter"); - await wait(500); - - const props = await getProps(devtools); - expect(props).toEqual({ - value: "0", - foo: "42", - }); + await expect + .poll(() => getProps(devtools)) + .toEqual({ + value: "0", + foo: "42", + }); // New prop input should be cleared - expect(await devtools.locator(propName).getAttribute("value")).toEqual(null); - expect(await devtools.locator(propValue).getAttribute("value")).toEqual(null); + await expect(devtools.locator(propName)).toHaveValue(""); + await expect(devtools.locator(propValue)).toHaveValue(""); }); diff --git a/test-e2e/tests/profiler/flamegraph/highlight-flamegraph.test.ts b/test-e2e/tests/profiler/flamegraph/highlight-flamegraph.test.ts index 1303e8aa..90d38d39 100644 --- a/test-e2e/tests/profiler/flamegraph/highlight-flamegraph.test.ts +++ b/test-e2e/tests/profiler/flamegraph/highlight-flamegraph.test.ts @@ -4,7 +4,6 @@ import { locateTab, gotoTest, locateFlame, - wait, } from "../../../pw-utils"; test("Should highlight flamegraph node if present in DOM", async ({ page }) => { @@ -12,13 +11,15 @@ test("Should highlight flamegraph node if present in DOM", async ({ page }) => { await devtools.locator(locateTab("PROFILER")).click(); await clickRecordButton(devtools); - await page.click("button"); + await page.locator("button").click(); await clickRecordButton(devtools); await devtools.locator(locateFlame("Counter")).first().hover(); - // Wait for possible flickering to occur - await wait(1000); - const log = (await page.evaluate(() => (window as any).log)) as any[]; - expect(log.filter(x => x.type === "highlight").length).toEqual(1); + await expect + .poll(async () => { + const log = (await page.evaluate(() => (window as any).log)) as any[]; + return log.filter(x => x.type === "highlight").length; + }) + .toEqual(1); }); diff --git a/test-e2e/tests/profiler/flamegraph/memo-sibling.test.ts b/test-e2e/tests/profiler/flamegraph/memo-sibling.test.ts index 14b3192f..48ab30d1 100644 --- a/test-e2e/tests/profiler/flamegraph/memo-sibling.test.ts +++ b/test-e2e/tests/profiler/flamegraph/memo-sibling.test.ts @@ -1,34 +1,27 @@ import { test, expect } from "@playwright/test"; -import { - clickRecordButton, - locateTab, - gotoTest, - waitForPass, -} from "../../../pw-utils"; +import { clickRecordButton, locateTab, gotoTest } from "../../../pw-utils"; import assert from "assert"; import { getFlameNodes } from "./utils"; test("Correctly position memoized sibling sub-trees", async ({ page }) => { const { devtools } = await gotoTest(page, "memo2"); - await devtools.click('[data-testid="filter-menu-button"]'); - await devtools.waitForSelector('[data-testid="filter-popup"]'); - await devtools.click('[data-testid="add-filter"]'); + await devtools.locator('[data-testid="filter-menu-button"]').click(); + await devtools.locator('[data-testid="filter-popup"]').waitFor(); + await devtools.locator('[data-testid="add-filter"]').click(); await devtools .locator('[data-testid="filter-popup"] input[type="text"]') - .type("Display"); + .fill("Display"); - await devtools.click( - '[data-testid="filter-popup"] input[type="checkbox"]:not(:checked)', - ); - await devtools.click('[data-testid="filter-update"]'); - await devtools.click('[data-testid="filter-menu-button"]'); + await devtools + .locator( + '[data-testid="filter-popup"] input[type="checkbox"]:not(:checked)', + ) + .click(); + await devtools.locator('[data-testid="filter-update"]').click(); + await devtools.locator('[data-testid="filter-menu-button"]').click(); - await waitForPass(async () => { - await expect(devtools.locator('[data-testid="filter-popup"]')).toHaveCount( - 0, - ); - }); + await expect(devtools.locator('[data-testid="filter-popup"]')).toHaveCount(0); await devtools.locator(locateTab("PROFILER")).click(); diff --git a/test-e2e/tests/profiler/flamegraph/profiler-hoc.test.ts b/test-e2e/tests/profiler/flamegraph/profiler-hoc.test.ts index 39b1b8e7..20589604 100644 --- a/test-e2e/tests/profiler/flamegraph/profiler-hoc.test.ts +++ b/test-e2e/tests/profiler/flamegraph/profiler-hoc.test.ts @@ -4,7 +4,6 @@ import { locateTab, gotoTest, locateFlame, - wait, } from "../../../pw-utils"; import { getFlameNodes } from "./utils"; @@ -13,27 +12,30 @@ test("Should work with filtered HOC roots", async ({ page }) => { await devtools.locator(locateTab("PROFILER")).click(); await clickRecordButton(devtools); - await page.click("button"); + await page.locator("button").click(); await clickRecordButton(devtools); await devtools.locator(locateFlame("Wrapped")).waitFor(); - const nodes = await getFlameNodes(devtools); - expect(nodes.find(x => x.name === "Wrapped")?.hocs).toEqual(["withBoof"]); + await expect + .poll(async () => { + const nodes = await getFlameNodes(devtools); + return nodes.find(x => x.name === "Wrapped")?.hocs; + }) + .toEqual(["withBoof"]); // Disabling HOC-filter should remove hoc labels await devtools.locator(locateTab("ELEMENTS")).click(); - await devtools.click('[data-testid="filter-menu-button"]'); - await devtools.waitForSelector('[data-testid="filter-popup"]'); + await devtools.locator('[data-testid="filter-menu-button"]').click(); + await devtools.locator('[data-testid="filter-popup"]').waitFor(); await devtools .locator('[data-testid="filter-popup"] label:has-text("HOC-Components")') .click(); - await devtools.click('[data-testid="filter-update"]'); - await wait(1000); + await devtools.locator('[data-testid="filter-update"]').click(); await devtools.locator(locateTab("PROFILER")).click(); await clickRecordButton(devtools); - await page.click("button"); + await page.locator("button").click(); await clickRecordButton(devtools); await devtools.locator(locateFlame("withBoof(Wrapped)")).waitFor(); diff --git a/test-e2e/tests/profiler/flamegraph/profiler-static-subtree.test.ts b/test-e2e/tests/profiler/flamegraph/profiler-static-subtree.test.ts index 8e4206cb..95a0f119 100644 --- a/test-e2e/tests/profiler/flamegraph/profiler-static-subtree.test.ts +++ b/test-e2e/tests/profiler/flamegraph/profiler-static-subtree.test.ts @@ -4,7 +4,6 @@ import { locateTab, gotoTest, locateFlame, - wait, } from "../../../pw-utils"; test("Static subtree should be smaller in size", async ({ page }) => { @@ -12,8 +11,8 @@ test("Static subtree should be smaller in size", async ({ page }) => { await devtools.locator(locateTab("PROFILER")).click(); await clickRecordButton(devtools); - await page.click("button"); - await page.click("button"); + await page.locator("button").click(); + await page.locator("button").click(); await clickRecordButton(devtools); await devtools.locator(locateFlame("App")).waitFor(); @@ -22,19 +21,18 @@ test("Static subtree should be smaller in size", async ({ page }) => { .locator('[data-testid="commit-page-info"]:has-text("2 / 2")') .waitFor(); - // Wait for layouting - await wait(500); - - const res = await devtools.evaluate(() => { - const display = document.querySelector('[data-name="Display"]')! - .clientWidth; - const statics = Array.from( - document.querySelectorAll('[data-name="Static"]')!, - ).map(el => el.clientWidth); - - return statics.every(w => w < display); - }); - - // Static nodes were bigger than Display - expect(res).toEqual(true); + // Static nodes should be smaller than Display + await expect + .poll(() => + devtools.evaluate(() => { + const display = document.querySelector( + '[data-name="Display"]', + )!.clientWidth; + const statics = Array.from( + document.querySelectorAll('[data-name="Static"]')!, + ).map(el => el.clientWidth); + return statics.every(w => w > 0 && w < display); + }), + ) + .toBe(true); }); diff --git a/test-e2e/tests/profiler/flamegraph/profiler-unmount.test.ts b/test-e2e/tests/profiler/flamegraph/profiler-unmount.test.ts index d82710ee..50e3c836 100644 --- a/test-e2e/tests/profiler/flamegraph/profiler-unmount.test.ts +++ b/test-e2e/tests/profiler/flamegraph/profiler-unmount.test.ts @@ -4,7 +4,6 @@ import { locateTab, gotoTest, locateFlame, - wait, locateProfilerTab, } from "../../../pw-utils"; @@ -16,9 +15,9 @@ test("Should highlight flamegraph node if present in DOM", async ({ page }) => { await page.locator("button").first().click(); await clickRecordButton(devtools); - await page.click("button"); + await page.locator("button").first().click(); await devtools.locator(locateProfilerTab("RANKED")).click(); - await wait(1000); + await devtools.locator('[data-type="ranked"]').waitFor(); await devtools.locator(locateProfilerTab("FLAMEGRAPH")).click(); await devtools.locator(locateFlame("Counter")).first().waitFor(); diff --git a/test-e2e/tests/profiler/highlight-updates-text.test.ts b/test-e2e/tests/profiler/highlight-updates-text.test.ts index 871ce788..05048f35 100644 --- a/test-e2e/tests/profiler/highlight-updates-text.test.ts +++ b/test-e2e/tests/profiler/highlight-updates-text.test.ts @@ -1,18 +1,15 @@ import { expect, test } from "@playwright/test"; -import { locateTab, gotoTest, wait } from "../../pw-utils"; +import { locateTab, gotoTest } from "../../pw-utils"; test("Don't crash on measuring text nodes", async ({ page }) => { const { devtools } = await gotoTest(page, "highlight-text"); await devtools.locator(locateTab("SETTINGS")).click(); - await devtools.click('[data-testId="toggle-highlight-updates"]'); + await devtools.locator('[data-testId="toggle-highlight-updates"]').click(); - await page.click("button", { noWaitAfter: true }); + await page.locator("button").click({ noWaitAfter: true }); - // Run twice to check if canvas is re-created const id = "#preact-devtools-highlight-updates"; - await page.waitForSelector(id, { state: "attached" }); - - await wait(1000); + await page.locator(id).waitFor({ state: "attached" }); await expect(page.locator(id)).toHaveCount(0); }); diff --git a/test-e2e/tests/profiler/highlight-updates.test.ts b/test-e2e/tests/profiler/highlight-updates.test.ts index 04f8a58b..0b3d0414 100644 --- a/test-e2e/tests/profiler/highlight-updates.test.ts +++ b/test-e2e/tests/profiler/highlight-updates.test.ts @@ -1,21 +1,20 @@ import { expect, test } from "@playwright/test"; -import { locateTab, gotoTest, wait } from "../../pw-utils"; +import { locateTab, gotoTest } from "../../pw-utils"; test("Check if highlight updates is rendered", async ({ page }) => { const { devtools } = await gotoTest(page, "todo"); await devtools.locator(locateTab("SETTINGS")).click(); - await devtools.click('[data-testId="toggle-highlight-updates"]'); + await devtools.locator('[data-testId="toggle-highlight-updates"]').click(); + + const id = "#preact-devtools-highlight-updates"; // Run twice to check if canvas is re-created for (let i = 0; i < 2; i++) { await page.locator("input").type("foo"); await page.keyboard.press("Enter"); - const id = "#preact-devtools-highlight-updates"; - await page.waitForSelector(id, { state: "attached" }); - - await wait(1000); + await page.locator(id).waitFor({ state: "attached" }); await expect(page.locator(id)).toHaveCount(0); } }); diff --git a/test-e2e/tests/profiler/ranked/highlight-ranked.test.ts b/test-e2e/tests/profiler/ranked/highlight-ranked.test.ts index 0a7e724b..273f4faf 100644 --- a/test-e2e/tests/profiler/ranked/highlight-ranked.test.ts +++ b/test-e2e/tests/profiler/ranked/highlight-ranked.test.ts @@ -3,7 +3,6 @@ import { clickRecordButton, locateTab, gotoTest, - wait, locateProfilerTab, } from "../../../pw-utils"; @@ -16,9 +15,15 @@ test("Should highlight ranked node if present in DOM", async ({ page }) => { await clickRecordButton(devtools); await devtools.locator(locateProfilerTab("RANKED")).click(); - await devtools.hover('[data-type="ranked"] [data-name="Counter"]'); - await wait(1000); + await devtools + .locator('[data-type="ranked"] [data-name="Counter"]') + .first() + .hover(); - const log = (await page.evaluate(() => (window as any).log)) as any[]; - expect(log.filter(x => x.type === "highlight").length).toEqual(1); + await expect + .poll(async () => { + const log = (await page.evaluate(() => (window as any).log)) as any[]; + return log.filter(x => x.type === "highlight").length; + }) + .toEqual(1); }); diff --git a/test-e2e/tests/profiler/ranked/profiler-ranked.test.ts b/test-e2e/tests/profiler/ranked/profiler-ranked.test.ts index df5d26e7..67811dd0 100644 --- a/test-e2e/tests/profiler/ranked/profiler-ranked.test.ts +++ b/test-e2e/tests/profiler/ranked/profiler-ranked.test.ts @@ -15,12 +15,11 @@ test("Ranked profile view should only show nodes of the current commit", async ( await devtools.locator(locateProfilerTab("RANKED")).click(); await clickRecordButton(devtools); - await page.click('[data-testid="counter-1"]'); - await page.click('[data-testid="counter-2"]'); + await page.locator('[data-testid="counter-1"]').click(); + await page.locator('[data-testid="counter-2"]').click(); await clickRecordButton(devtools); - const nodes = await devtools.$$( - '[data-type="ranked"] [data-id]:not([data-weight])', - ); - expect(nodes.length).toEqual(0); + await expect( + devtools.locator('[data-type="ranked"] [data-id]:not([data-weight])'), + ).toHaveCount(0); }); diff --git a/test-e2e/tests/profiler/render-reason-disabled.test.ts b/test-e2e/tests/profiler/render-reason-disabled.test.ts index c86cc4da..1ba941ab 100644 --- a/test-e2e/tests/profiler/render-reason-disabled.test.ts +++ b/test-e2e/tests/profiler/render-reason-disabled.test.ts @@ -5,46 +5,39 @@ test("Disables render reason capturing", async ({ page }) => { const { devtools } = await gotoTest(page, "render-reasons"); await devtools.locator(locateTab("SETTINGS")).click(); - let checked = await devtools - .locator('[data-testid="toggle-render-reason"]') - .isChecked(); - expect(checked).toEqual(false); + await expect( + devtools.locator('[data-testid="toggle-render-reason"]'), + ).not.toBeChecked(); await devtools.locator(locateTab("PROFILER")).click(); await clickRecordButton(devtools); - await page.click('[data-testid="counter-1"]'); - await page.click('[data-testid="counter-2"]'); + await page.locator('[data-testid="counter-1"]').click(); + await page.locator('[data-testid="counter-2"]').click(); await wait(1000); await clickRecordButton(devtools); - await devtools.click('[data-name="ComponentState"]'); - let reasons = await devtools - .locator('[data-testid="render-reasons"]') - .textContent(); - expect(reasons).toEqual("-"); + const reasons = devtools.locator('[data-testid="render-reasons"]'); + + await devtools.locator('[data-name="ComponentState"]').click(); + await expect(reasons).toHaveText("-"); // Reset flamegraph - await devtools.click('[data-name="Fragment"]'); - reasons = await devtools - .locator('[data-testid="render-reasons"]') - .textContent(); - expect(reasons).toEqual("Did not render"); + await devtools.locator('[data-name="Fragment"]').click(); + await expect(reasons).toHaveText("Did not render"); // Enable capturing - await devtools.click('[data-testid="toggle-render-reason"]'); + await devtools.locator('[data-testid="toggle-render-reason"]').click(); // Should start profiling immediately - const text = await devtools - .locator('[data-testid="profiler-info"]') - .textContent(); - expect(text).toMatch(/Profiling in progress/); + await expect(devtools.locator('[data-testid="profiler-info"]')).toContainText( + "Profiling in progress", + ); await clickRecordButton(devtools); await devtools.locator(locateTab("SETTINGS")).click(); - checked = await devtools - .locator('[data-testid="toggle-render-reason"]') - .isChecked(); - expect(checked).toEqual(true); + await expect( + devtools.locator('[data-testid="toggle-render-reason"]'), + ).toBeChecked(); }); diff --git a/test-e2e/tests/profiler/render-reason-support.test.ts b/test-e2e/tests/profiler/render-reason-support.test.ts index 1a40d76a..66878c90 100644 --- a/test-e2e/tests/profiler/render-reason-support.test.ts +++ b/test-e2e/tests/profiler/render-reason-support.test.ts @@ -11,32 +11,27 @@ test("Disables render reason capturing", async ({ page }) => { const { devtools } = await gotoTest(page, "render-reasons"); await devtools.locator(locateTab("SETTINGS")).click(); - await devtools.click('[data-testid="toggle-render-reason"]'); - const checked = await devtools - .locator('[data-testid="toggle-render-reason"]') - .isChecked(); - expect(checked).toEqual(true); + await devtools.locator('[data-testid="toggle-render-reason"]').click(); + await expect( + devtools.locator('[data-testid="toggle-render-reason"]'), + ).toBeChecked(); // Start profiling await devtools.locator(locateTab("PROFILER")).click(); await clickRecordButton(devtools); - await page.click('[data-testid="counter-1"]'); - await page.click('[data-testid="counter-2"]'); + await page.locator('[data-testid="counter-1"]').click(); + await page.locator('[data-testid="counter-2"]').click(); await wait(1000); await clickRecordButton(devtools); + const reasons = devtools.locator('[data-testid="render-reasons"]'); + await devtools.locator(locateFlame("ComponentState")).click(); - let reasons = await devtools - .locator('[data-testid="render-reasons"]') - .textContent(); - expect(reasons).toEqual("State changed:value"); + await expect(reasons).toHaveText("State changed:value"); // Reset flamegraph await devtools.locator(locateFlame("Fragment")).click(); - reasons = await devtools - .locator('[data-testid="render-reasons"]') - .textContent(); - expect(reasons).toEqual("Did not render"); + await expect(reasons).toHaveText("Did not render"); }); diff --git a/test-e2e/tests/profiler/render-reasons-memo.test.ts b/test-e2e/tests/profiler/render-reasons-memo.test.ts index 5b368a8f..7008314d 100644 --- a/test-e2e/tests/profiler/render-reasons-memo.test.ts +++ b/test-e2e/tests/profiler/render-reasons-memo.test.ts @@ -16,26 +16,24 @@ test("Captures render reasons for memo", async ({ page }) => { // Start profiling await devtools.locator(locateTab("PROFILER")).click(); await clickRecordButton(devtools); - await page.click("button"); + await page.locator("button").click(); await wait(1000); await clickRecordButton(devtools); // Get render reason await devtools.locator(locateFlame("Foo")).click(); - const reasons = await devtools - .locator('[data-testid="render-reasons"]') - .textContent(); - expect(reasons).toEqual("Did not render"); + await expect(devtools.locator('[data-testid="render-reasons"]')).toHaveText( + "Did not render", + ); // Elements should be marked as not rendered - const Foo = await devtools - .locator('[data-name="Foo"]') - .getAttribute("data-weight"); - const Inner = await devtools - .locator('[data-name="FooInner"]') - .getAttribute("data-weight"); - - expect(Foo).toEqual("-1"); - expect(Inner).toEqual("-1"); + await expect(devtools.locator('[data-name="Foo"]')).toHaveAttribute( + "data-weight", + "-1", + ); + await expect(devtools.locator('[data-name="FooInner"]')).toHaveAttribute( + "data-weight", + "-1", + ); }); diff --git a/test-e2e/tests/profiler/render-reasons.test.ts b/test-e2e/tests/profiler/render-reasons.test.ts index 9211b6ce..267892e5 100644 --- a/test-e2e/tests/profiler/render-reasons.test.ts +++ b/test-e2e/tests/profiler/render-reasons.test.ts @@ -16,57 +16,44 @@ test("Captures render reasons", async ({ page }) => { // Start profiling await devtools.locator(locateTab("PROFILER")).click(); await clickRecordButton(devtools); - await page.click('[data-testid="counter-1"]'); - await page.click('[data-testid="class-state-multi"]'); - await page.click('[data-testid="counter-2"]'); - await page.click('[data-testid="force-update"]'); + await page.locator('[data-testid="counter-1"]').click(); + await page.locator('[data-testid="class-state-multi"]').click(); + await page.locator('[data-testid="counter-2"]').click(); + await page.locator('[data-testid="force-update"]').click(); await wait(1000); await clickRecordButton(devtools); + const reasons = devtools.locator('[data-testid="render-reasons"]'); + // Class state await devtools.locator(locateFlame("ComponentState")).click(); - let reasons = await devtools - .locator('[data-testid="render-reasons"]') - .textContent(); - expect(reasons).toEqual("State changed:value"); + await expect(reasons).toHaveText("State changed:value"); await devtools.locator(locateFlame("Fragment")).click(); await devtools.locator(locateFlame("Display")).first().click(); - reasons = await devtools - .locator('[data-testid="render-reasons"]') - .textContent(); - expect(reasons).toEqual("Props changed:value"); + await expect(reasons).toHaveText("Props changed:value"); // Class state multiple - await devtools.click('[data-testid="next-commit"]'); + await devtools.locator('[data-testid="next-commit"]').click(); await devtools.locator(locateFlame("Fragment")).click(); await devtools.locator(locateFlame("ComponentMultiState")).click(); - reasons = await devtools - .locator('[data-testid="render-reasons"]') - .textContent(); - expect(reasons).toEqual("State changed:counter, other"); + await expect(reasons).toHaveText("State changed:counter, other"); // Hooks - await devtools.click('[data-testid="next-commit"]'); + await devtools.locator('[data-testid="next-commit"]').click(); await devtools.locator(locateFlame("Fragment")).click(); await devtools.locator(locateFlame("HookState")).click(); - reasons = await devtools - .locator('[data-testid="render-reasons"]') - .textContent(); - expect(reasons).toEqual("Hooks changed:1"); + await expect(reasons).toHaveText("Hooks changed:1"); // Force update - await devtools.click('[data-testid="next-commit"]'); + await devtools.locator('[data-testid="next-commit"]').click(); await devtools.locator(locateFlame("Fragment")).click(); await devtools.locator(locateFlame("ForceUpdate")).click(); - reasons = await devtools - .locator('[data-testid="render-reasons"]') - .textContent(); - expect(reasons).toEqual("Force update"); + await expect(reasons).toHaveText("Force update"); }); test("Captures hook render reasons", async ({ page }) => { @@ -78,36 +65,25 @@ test("Captures hook render reasons", async ({ page }) => { // Start profiling await devtools.locator(locateTab("PROFILER")).click(); await clickRecordButton(devtools); - await page.click('button:has-text("S1++")'); - await page.click('button:has-text("S2++")'); - await page.click('button:has-text("S3++")'); - await page.click('button:has-text("S1++")'); + await page.locator('button:has-text("S1++")').click(); + await page.locator('button:has-text("S2++")').click(); + await page.locator('button:has-text("S3++")').click(); + await page.locator('button:has-text("S1++")').click(); await wait(1000); await clickRecordButton(devtools); await devtools.locator(locateFlame("App")).click(); - let reasons = await devtools - .locator('[data-testid="render-reasons"]') - .textContent(); - expect(reasons).toEqual("Hooks changed:1"); - - await devtools.click('[data-testid="next-commit"]'); - reasons = await devtools - .locator('[data-testid="render-reasons"]') - .textContent(); - expect(reasons).toEqual("Hooks changed:2"); - - await devtools.click('[data-testid="next-commit"]'); - reasons = await devtools - .locator('[data-testid="render-reasons"]') - .textContent(); - expect(reasons).toEqual("Hooks changed:3"); - - await devtools.click('[data-testid="next-commit"]'); - reasons = await devtools - .locator('[data-testid="render-reasons"]') - .textContent(); - expect(reasons).toEqual("Hooks changed:1"); + const reasons = devtools.locator('[data-testid="render-reasons"]'); + await expect(reasons).toHaveText("Hooks changed:1"); + + await devtools.locator('[data-testid="next-commit"]').click(); + await expect(reasons).toHaveText("Hooks changed:2"); + + await devtools.locator('[data-testid="next-commit"]').click(); + await expect(reasons).toHaveText("Hooks changed:3"); + + await devtools.locator('[data-testid="next-commit"]').click(); + await expect(reasons).toHaveText("Hooks changed:1"); }); diff --git a/test-e2e/tests/prop-input.test.ts b/test-e2e/tests/prop-input.test.ts index 684a41dd..4c6e1420 100644 --- a/test-e2e/tests/prop-input.test.ts +++ b/test-e2e/tests/prop-input.test.ts @@ -1,5 +1,5 @@ import { expect, Frame, Page, test } from "@playwright/test"; -import { getLog, gotoTest, locateTreeItem, wait } from "../pw-utils"; +import { getLog, gotoTest, locateTreeItem } from "../pw-utils"; test("Input various data types into DataInput", async ({ page }) => { const { devtools } = await gotoTest(page, "data-input"); @@ -113,15 +113,21 @@ test("Input various data types into DataInput", async ({ page }) => { }); }); -async function getParsed(page: Page): Promise { - const log = await getLog(page); - for (let i = log.length - 1; i >= 0; i--) { - if (log[i].type === "update-prop") { - return log[i].data.value; - } - } - - throw new Error("No update-prop event found"); +async function getParsedAfter(page: Page, sinceIndex: number): Promise { + let value: any; + await expect + .poll(async () => { + const log = await getLog(page); + for (let i = log.length - 1; i >= sinceIndex; i--) { + if (log[i].type === "update-prop") { + value = log[i].data.value; + return true; + } + } + return false; + }) + .toBe(true); + return value; } async function enterText( @@ -130,26 +136,25 @@ async function enterText( selector: string, text: string, ) { - await page.click("button"); - await devtools.click(locateTreeItem("Display")); + await page.locator("button").click(); + await devtools.locator(locateTreeItem("Display")).click(); await expect(devtools.locator('[data-testid="undo-btn"]')).toHaveCount(0); - await devtools.waitForSelector(selector); - await devtools.fill(selector, text); + await devtools.locator(selector).waitFor(); + const logLengthBefore = (await getLog(page)).length; + await devtools.locator(selector).fill(text); await page.keyboard.press("Enter"); - await page.click('[data-testid="result"]'); - - await wait(200); + await page.locator('[data-testid="result"]').click(); - const rendered = await getParsed(page); + const rendered = await getParsedAfter(page, logLengthBefore); - const present = await devtools.$(selector); + const present = (await devtools.locator(selector).count()) > 0; const type = present ? await devtools.locator(selector).getAttribute("data-type") : "non-editable"; const value = present - ? await devtools.$eval(selector, el => (el as any).value) + ? await devtools.locator(selector).inputValue() : await devtools.locator('[data-testid="prop-value"]').textContent(); return { rendered, type, value }; } diff --git a/test-e2e/tests/root-islands.test.ts b/test-e2e/tests/root-islands.test.ts index 444721c2..e808ea81 100644 --- a/test-e2e/tests/root-islands.test.ts +++ b/test-e2e/tests/root-islands.test.ts @@ -4,8 +4,9 @@ import { getTreeItems, gotoTest } from "../pw-utils"; test("Islands roots should be sorted by DOM order", async ({ page }) => { const { devtools } = await gotoTest(page, "islands-order"); - const items = await getTreeItems(devtools); - expect(items.map(x => x.name)).toEqual(["App1", "App2", "App3"]); + await expect + .poll(async () => (await getTreeItems(devtools)).map(x => x.name)) + .toEqual(["App1", "App2", "App3"]); }); test("Virtual island roots should be sorted by DOM order", async ({ page }) => { @@ -15,12 +16,7 @@ test("Virtual island roots should be sorted by DOM order", async ({ page }) => { ); const { devtools } = await gotoTest(page, "islands-order-virtual"); - const items = await getTreeItems(devtools); - expect(items.map(x => x.name)).toEqual([ - "App1", - "App2", - "Virtual1", - "Virtual2", - "App3", - ]); + await expect + .poll(async () => (await getTreeItems(devtools)).map(x => x.name)) + .toEqual(["App1", "App2", "Virtual1", "Virtual2", "App3"]); }); diff --git a/test-e2e/tests/root-multiple.test.ts b/test-e2e/tests/root-multiple.test.ts index 766080f7..1d47b59a 100644 --- a/test-e2e/tests/root-multiple.test.ts +++ b/test-e2e/tests/root-multiple.test.ts @@ -1,14 +1,15 @@ import { test, expect } from "@playwright/test"; -import { gotoTest, waitForPass } from "../pw-utils"; +import { gotoTest } from "../pw-utils"; test("Inspect should select node in elements panel", async ({ page }) => { const { devtools } = await gotoTest(page, "root-multi"); - await waitForPass(async () => { - const btns = await page.locator("button").count(); - expect(btns).toEqual(2); - }); + await expect(page.locator("button")).toHaveCount(2); - const txts = await devtools.locator("data-testid=tree-item").allInnerTexts(); - expect(txts).toEqual(["Counter", "Display", "Counter", "Display"]); + await expect(devtools.locator("data-testid=tree-item")).toHaveText([ + "Counter", + "Display", + "Counter", + "Display", + ]); }); diff --git a/test-e2e/tests/state.test.ts b/test-e2e/tests/state.test.ts index 2653dece..b1472550 100644 --- a/test-e2e/tests/state.test.ts +++ b/test-e2e/tests/state.test.ts @@ -9,15 +9,11 @@ test("Mirror component state to the devtools", async ({ page }) => { const input = '[data-testid="props-row"] input'; const result = '[data-testid="result"]'; - let value = await devtools.locator(input).inputValue(); - let text = await page.locator(result).textContent(); - expect(value).toEqual("0"); - expect(text).toEqual("Counter: 0"); + await expect(devtools.locator(input)).toHaveValue("0"); + await expect(page.locator(result)).toHaveText("Counter: 0"); await page.click("button"); - value = await devtools.locator(input).inputValue(); - text = await page.locator(result).textContent(); - expect(value).toEqual("1"); - expect(text).toEqual("Counter: 1"); + await expect(devtools.locator(input)).toHaveValue("1"); + await expect(page.locator(result)).toHaveText("Counter: 1"); }); diff --git a/test-e2e/tests/stats/stats-memo.test.ts b/test-e2e/tests/stats/stats-memo.test.ts index 468d3412..8eecc367 100644 --- a/test-e2e/tests/stats/stats-memo.test.ts +++ b/test-e2e/tests/stats/stats-memo.test.ts @@ -5,26 +5,19 @@ test("Skip memoized components for stats", async ({ page }) => { const { devtools } = await gotoTest(page, "memo-stats"); await devtools.locator(locateTab("STATISTICS")).click(); - await devtools.waitForSelector('[data-testId="stats-info"]'); + await devtools.locator('[data-testId="stats-info"]').waitFor(); await clickRecordButton(devtools); - await page.click("button"); - await page.waitForSelector('[data-value="1"]'); + await page.locator("button").click(); + await page.locator('[data-value="1"]').waitFor(); await clickRecordButton(devtools); - const mountTotal = await devtools - .locator('[data-testid="mount-total"]') - .textContent(); - expect(mountTotal).toEqual("0"); - - const updateTotal = await devtools - .locator('[data-testid="update-total"]') - .textContent(); - expect(updateTotal).toEqual("8"); - - const unmountTotal = await devtools - .locator('[data-testid="unmount-total"]') - .textContent(); - expect(unmountTotal).toEqual("0"); + await expect(devtools.locator('[data-testid="mount-total"]')).toHaveText("0"); + await expect(devtools.locator('[data-testid="update-total"]')).toHaveText( + "8", + ); + await expect(devtools.locator('[data-testid="unmount-total"]')).toHaveText( + "0", + ); }); diff --git a/test-e2e/tests/stats/stats-simple.test.ts b/test-e2e/tests/stats/stats-simple.test.ts index 6abd3236..65a35c51 100644 --- a/test-e2e/tests/stats/stats-simple.test.ts +++ b/test-e2e/tests/stats/stats-simple.test.ts @@ -5,36 +5,25 @@ test("Display simple stats", async ({ page }) => { const { devtools } = await gotoTest(page, "simple-stats"); await devtools.locator(locateTab("STATISTICS")).click(); - await devtools.waitForSelector('[data-testId="stats-info"]'); + await devtools.locator('[data-testId="stats-info"]').waitFor(); await clickRecordButton(devtools); - await devtools.waitForSelector('[data-testid="stats-info-recording"]'); + await devtools.locator('[data-testid="stats-info-recording"]').waitFor(); - await page.click('[data-testid="update"]'); + await page.locator('[data-testid="update"]').click(); await clickRecordButton(devtools); - const classComponents = await devtools - .locator('[data-testid="class-component-total"]') - .textContent(); - expect(classComponents).toEqual("1"); - - const fnComponents = await devtools - .locator('[data-testid="function-component-total"]') - .textContent(); - expect(fnComponents).toEqual("1"); - - const fragmentsCount = await devtools - .locator('[data-testid="fragment-total"]') - .textContent(); - expect(fragmentsCount).toEqual("0"); - - const elementsCount = await devtools - .locator('[data-testid="element-total"]') - .textContent(); - expect(elementsCount).toEqual("5"); - - const textCount = await devtools - .locator('[data-testid="text-total"]') - .textContent(); - expect(textCount).toEqual("6"); + await expect( + devtools.locator('[data-testid="class-component-total"]'), + ).toHaveText("1"); + await expect( + devtools.locator('[data-testid="function-component-total"]'), + ).toHaveText("1"); + await expect(devtools.locator('[data-testid="fragment-total"]')).toHaveText( + "0", + ); + await expect(devtools.locator('[data-testid="element-total"]')).toHaveText( + "5", + ); + await expect(devtools.locator('[data-testid="text-total"]')).toHaveText("6"); }); diff --git a/test-e2e/tests/stats/stats-single-child.test.ts b/test-e2e/tests/stats/stats-single-child.test.ts index 7835ab6b..b9cddf50 100644 --- a/test-e2e/tests/stats/stats-single-child.test.ts +++ b/test-e2e/tests/stats/stats-single-child.test.ts @@ -5,31 +5,22 @@ test("Display single child stats", async ({ page }) => { const { devtools } = await gotoTest(page, "simple-stats"); await devtools.locator(locateTab("STATISTICS")).click(); - await devtools.waitForSelector('[data-testId="stats-info"]'); + await devtools.locator('[data-testId="stats-info"]').waitFor(); await clickRecordButton(devtools); - await devtools.waitForSelector('[data-testid="stats-info-recording"]'); + await devtools.locator('[data-testid="stats-info-recording"]').waitFor(); - await page.click('[data-testid="update"]'); + await page.locator('[data-testid="update"]').click(); await clickRecordButton(devtools); - const classComponents = await devtools - .locator('[data-testid="single-class-component"]') - .textContent(); - expect(classComponents).toEqual("0"); - - const fnComponents = await devtools - .locator('[data-testid="single-function-component"]') - .textContent(); - expect(fnComponents).toEqual("0"); - - const elements = await devtools - .locator('[data-testid="single-element"]') - .textContent(); - expect(elements).toEqual("2"); - - const texts = await devtools - .locator('[data-testid="single-text"]') - .textContent(); - expect(texts).toEqual("3"); + await expect( + devtools.locator('[data-testid="single-class-component"]'), + ).toHaveText("0"); + await expect( + devtools.locator('[data-testid="single-function-component"]'), + ).toHaveText("0"); + await expect(devtools.locator('[data-testid="single-element"]')).toHaveText( + "2", + ); + await expect(devtools.locator('[data-testid="single-text"]')).toHaveText("3"); }); diff --git a/test-e2e/tests/suspense-toggle.test.ts b/test-e2e/tests/suspense-toggle.test.ts index 312ab346..bb45b0f9 100644 --- a/test-e2e/tests/suspense-toggle.test.ts +++ b/test-e2e/tests/suspense-toggle.test.ts @@ -1,10 +1,5 @@ import { test, expect, Page } from "@playwright/test"; -import { - getTreeViewItemNames, - gotoTest, - locateTreeItem, - waitFor, -} from "../pw-utils"; +import { getTreeViewItemNames, gotoTest, locateTreeItem } from "../pw-utils"; function testCase(preactVersion: string) { return async ({ page }: { page: Page }) => { @@ -12,12 +7,12 @@ function testCase(preactVersion: string) { preact: preactVersion, }); - await devtools.click(locateTreeItem("Delayed")); - await devtools.click('[data-testid="suspend-action"]'); + await devtools.locator(locateTreeItem("Delayed")).click(); + await devtools.locator('[data-testid="suspend-action"]').click(); - await waitFor(async () => { - const items = await getTreeViewItemNames(devtools); - expect(items).toEqual( + await expect + .poll(() => getTreeViewItemNames(devtools)) + .toEqual( [ "Shortly", "Block", @@ -27,20 +22,16 @@ function testCase(preactVersion: string) { "Block", ].filter(Boolean), ); - return true; - }); - - const selected = await devtools - .locator('[data-testid="tree-item"][data-selected="true"]') - .getAttribute("data-name"); - expect(selected).toEqual("Suspense"); + await expect( + devtools.locator('[data-testid="tree-item"][data-selected="true"]'), + ).toHaveAttribute("data-name", "Suspense"); - await devtools.click(locateTreeItem("Shortly")); + await devtools.locator(locateTreeItem("Shortly")).click(); await devtools .locator('[data-testid="inspect-component-name"]:has-text("")') - .textContent(); + .waitFor(); await expect( devtools.locator('[data-testid="suspend-action"]'), diff --git a/test-e2e/tests/suspense.test.ts b/test-e2e/tests/suspense.test.ts index b8f8cd5d..3c80c735 100644 --- a/test-e2e/tests/suspense.test.ts +++ b/test-e2e/tests/suspense.test.ts @@ -1,5 +1,5 @@ import { test, expect, Page } from "@playwright/test"; -import { getTreeViewItemNames, gotoTest, waitForPass } from "../pw-utils"; +import { getTreeViewItemNames, gotoTest } from "../pw-utils"; function testCase(version: string) { return async ({ page }: { page: Page }) => { @@ -7,11 +7,11 @@ function testCase(version: string) { preact: version, }); - await devtools.waitForSelector('[data-testid="tree-item"]'); + await devtools.locator('[data-testid="tree-item"]').first().waitFor(); - await waitForPass(async () => { - const items = await getTreeViewItemNames(devtools); - expect(items).toEqual( + await expect + .poll(() => getTreeViewItemNames(devtools)) + .toEqual( [ "Shortly", "Block", @@ -20,11 +20,10 @@ function testCase(version: string) { "Block", ].filter(Boolean), ); - }); - await waitForPass(async () => { - const items = await getTreeViewItemNames(devtools); - expect(items).toEqual( + await expect + .poll(() => getTreeViewItemNames(devtools)) + .toEqual( [ "Shortly", "Block", @@ -34,7 +33,6 @@ function testCase(version: string) { "Block", ].filter(Boolean), ); - }); }; } diff --git a/test-e2e/tests/update-copy.test.ts b/test-e2e/tests/update-copy.test.ts index c348105d..7ae71cf1 100644 --- a/test-e2e/tests/update-copy.test.ts +++ b/test-e2e/tests/update-copy.test.ts @@ -1,4 +1,4 @@ -import { test } from "@playwright/test"; +import { test, expect } from "@playwright/test"; import { gotoTest, locateTreeItem } from "../pw-utils"; test("Create a copy when doing props/state/context updates", async ({ @@ -6,29 +6,34 @@ test("Create a copy when doing props/state/context updates", async ({ }) => { const { devtools } = await gotoTest(page, "update-all"); + const valueInput = devtools + .locator('[data-testid="prop-value"] input') + .first(); + // Props - await devtools.click(locateTreeItem("Props")); - await devtools.fill('[data-testid="prop-value"] input', "1"); + await devtools.locator(locateTreeItem("Props")).click(); + await expect(valueInput).toHaveValue("0"); + await valueInput.fill("1"); await page.keyboard.press("Enter"); - await page - .locator('[data-testid="props-result"]:has-text("props: 1, true")') - .waitFor(); + await expect(page.locator('[data-testid="props-result"]')).toHaveText( + "props: 1, true", + ); // State - await devtools.click(locateTreeItem("State")); - await devtools.fill('[data-testid="prop-value"] input', "1"); + await devtools.locator(locateTreeItem("State")).click(); + await expect(valueInput).toHaveValue("0"); + await valueInput.fill("1"); await page.keyboard.press("Enter"); - await page - .locator('[data-testid="state-result"]:has-text("state: 1, true")') - .waitFor(); + await expect(page.locator('[data-testid="state-result"]')).toHaveText( + "state: 1, true", + ); // Legacy Context - await devtools.click(locateTreeItem("LegacyConsumer")); - await devtools.fill('[data-testid="prop-value"] input', "1"); + await devtools.locator(locateTreeItem("LegacyConsumer")).click(); + await expect(valueInput).toHaveValue("0"); + await valueInput.fill("1"); await page.keyboard.press("Enter"); - await page - .locator( - '[data-testid="legacy-context-result"]:has-text("legacy context: 1")', - ) - .waitFor(); + await expect( + page.locator('[data-testid="legacy-context-result"]'), + ).toHaveText("legacy context: 1"); });