diff --git a/.eslintignore b/.eslintignore index f7365e0082..152cbbe228 100644 --- a/.eslintignore +++ b/.eslintignore @@ -19,4 +19,5 @@ tests/components/ tests/installation/fixture-scripts/ examples/ DEPS -.cache/ \ No newline at end of file +.cache/ +utils/ diff --git a/.eslintrc-with-ts-config.js b/.eslintrc-with-ts-config.js new file mode 100644 index 0000000000..b06ec00195 --- /dev/null +++ b/.eslintrc-with-ts-config.js @@ -0,0 +1,15 @@ +module.exports = { + extends: "./.eslintrc.js", + parserOptions: { + ecmaVersion: 9, + sourceType: "module", + project: "./tsconfig.json", + }, + rules: { + "@typescript-eslint/no-base-to-string": "error", + "@typescript-eslint/no-unnecessary-boolean-literal-compare": 2, + }, + parserOptions: { + project: "./tsconfig.json" + }, +}; diff --git a/.eslintrc.js b/.eslintrc.js index 7bc5a0868f..bff9ffeeb4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -48,6 +48,7 @@ module.exports = { "arrow-parens": [2, "as-needed"], "prefer-const": 2, "quote-props": [2, "consistent"], + "nonblock-statement-body-position": [2, "below"], // anti-patterns "no-var": 2, diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 5baad382eb..062d7c7e74 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -23,7 +23,7 @@ body: value: | ## Make a minimal reproduction To file the report, you will need a GitHub repository with a minimal (but complete) example and simple/clear steps on how to reproduce the bug. - The simpler you can make it, the more likely we are to successfully verify and fix the bug. + The simpler you can make it, the more likely we are to successfully verify and fix the bug. You can create a new project with `npm init playwright@latest new-project` and then add the test code there. - type: markdown attributes: value: | diff --git a/README.md b/README.md index 515062ddd0..14737d10d4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 🎭 Playwright -[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) [![Chromium version](https://img.shields.io/badge/chromium-123.0.6312.4-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-122.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-17.4-blue.svg?logo=safari)](https://webkit.org/) +[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) [![Chromium version](https://img.shields.io/badge/chromium-123.0.6312.4-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-123.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-17.4-blue.svg?logo=safari)](https://webkit.org/) ## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright) @@ -10,7 +10,7 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr | :--- | :---: | :---: | :---: | | Chromium 123.0.6312.4 | :white_check_mark: | :white_check_mark: | :white_check_mark: | | WebKit 17.4 | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| Firefox 122.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Firefox 123.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details. diff --git a/docs/src/ci-intro.md b/docs/src/ci-intro.md index da4f31522f..f5393cfc9e 100644 --- a/docs/src/ci-intro.md +++ b/docs/src/ci-intro.md @@ -8,7 +8,7 @@ title: "CI GitHub Actions" Playwright tests can be run on any CI provider. In this section we will cover running tests on GitHub using GitHub actions. If you would like to see how to configure other CI providers check out our detailed [doc on Continuous Integration](./ci.md). -When [installing Playwright](./intro.md) using the [VS Code extension](./getting-started-vscode.md) or with `npm init playwright@latest` you are given the option to add a [GitHub Actions](https://docs.github.com/en/actions). This creates a `playwright.yml` file inside a `.github/workflows` folder containing everything you need so that your tests run on each push and pull request into the main/master branch. +When [installing Playwright](./intro.md) using the [VS Code extension](./getting-started-vscode.md) or with `npm init playwright@latest` you are given the option to add a [GitHub Actions](https://docs.github.com/en/actions) workflow. This creates a `playwright.yml` file inside a `.github/workflows` folder containing everything you need so that your tests run on each push and pull request into the main/master branch. #### You will learn * langs: js diff --git a/docs/src/release-notes-js.md b/docs/src/release-notes-js.md index c496c20ffd..16ad193eba 100644 --- a/docs/src/release-notes-js.md +++ b/docs/src/release-notes-js.md @@ -6,6 +6,82 @@ toc_max_heading_level: 2 import LiteYouTube from '@site/src/components/LiteYouTube'; +## Version 1.42 + +### New APIs + +- New method [`method: Page.addLocatorHandler`] registers a callback that will be invoked when specified element becomes visible and may block Playwright actions. The callback can get rid of the overlay. Here is an example that closes a cookie dialog when it appears: +```js +// Setup the handler. +await page.addLocatorHandler(page.getByRole('button', { name: 'Accept all cookies' }), async () => { + await page.getByRole('button', { name: 'Reject all cookies' }).click(); +}); + +// Write the test as usual. +await page.goto('https://example.com'); +await page.getByRole('button', { name: 'Start here' }).click(); +``` + +- `expect(callback).toPass()` timeout can now be configured by `expect.toPass.timeout` option [globally](./api/class-testconfig#test-config-expect) or in [project config](./api/class-testproject#test-project-expect) + +- [`event: ElectronApplication.console`] event is emitted when Electron main process calls console API methods. +```js +electronApp.on('console', async msg => { + const values = []; + for (const arg of msg.args()) + values.push(await arg.jsonValue()); + console.log(...values); +}); +await electronApp.evaluate(() => console.log('hello', 5, { foo: 'bar' })); +``` + +- [New syntax](./test-annotations#tag-tests) for adding tags to the tests (@-tokens in the test title are still supported): +```js +test('test customer login', { + tag: ['@fast', '@login'], +}, async ({ page }) => { + // ... +}); +``` + Use `--grep` command line option to run only tests with certain tags. +```sh +npx playwright test --grep @fast +``` + +- `--project` command line [flag](./test-cli#reference) now supports '*' wildcard: +```sh +npx playwright test --project='*mobile*' +``` + +- [New syntax](./test-annotations#annotate-tests) for test annotations: +```js +test('test full report', { + annotation: [ + { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/23180' }, + { type: 'docs', description: 'https://playwright.dev/docs/test-annotations#tag-tests' }, + ], +}, async ({ page }) => { + // ... +}); +``` + +- [`method: Page.pdf`] accepts two new options [`tagged`](./api/class-page#page-pdf-option-tagged) and [`outline`](./api/class-page#page-pdf-option-outline). + +### Announcements + +* ⚠️ Ubuntu 18 is not supported anymore. + +### Browser Versions + +* Chromium 123.0.6312.4 +* Mozilla Firefox 123.0 +* WebKit 17.4 + +This version was also tested against the following stable channels: + +* Google Chrome 122 +* Microsoft Edge 123 + ## Version 1.41 ### New APIs diff --git a/docs/src/service-workers-experimental-network-events-js.md b/docs/src/service-workers-experimental-network-events-js.md index 96b8ead1e5..0928460b54 100644 --- a/docs/src/service-workers-experimental-network-events-js.md +++ b/docs/src/service-workers-experimental-network-events-js.md @@ -137,10 +137,12 @@ self.addEventListener('fetch', event => { (async () => { // 1. Try to first serve directly from caches const response = await caches.match(event.request); - if (response) return response; + if (response) + return response; // 2. Re-write request for /foo to /bar - if (event.request.url.endsWith('foo')) return fetch('./bar'); + if (event.request.url.endsWith('foo')) + return fetch('./bar'); // 3. Prevent tracker.js from being retrieved, and returns a placeholder response if (event.request.url.endsWith('tracker.js')) { diff --git a/docs/src/test-typescript-js.md b/docs/src/test-typescript-js.md index 465f2c2d93..12a1173ea4 100644 --- a/docs/src/test-typescript-js.md +++ b/docs/src/test-typescript-js.md @@ -5,7 +5,26 @@ title: "TypeScript" ## Introduction -Playwright supports TypeScript out of the box. You just write tests in TypeScript, and Playwright will read them, transform to JavaScript and run. +Playwright supports TypeScript out of the box. You just write tests in TypeScript, and Playwright will read them, transform to JavaScript and run. Note that Playwright does not check the types and will run tests even if there are non-critical TypeScript compilation errors. + +We recommend you run TypeScript compiler alongside Playwright. For example on GitHub actions: + +```yaml +jobs: + test: + runs-on: ubuntu-latest + steps: + ... + - name: Run type checks + run: npx tsc -p tsconfig.json --noEmit + - name: Run Playwright tests + run: npx playwright test +``` + +For local development, you can run `tsc` in [watch](https://www.typescriptlang.org/docs/handbook/configuring-watch.html) mode like this: +```sh +npx tsc -p tsconfig.json --noEmit -w +``` ## tsconfig.json diff --git a/package-lock.json b/package-lock.json index 9446313ca6..7159768fe7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "playwright-internal", - "version": "1.42.0-next", + "version": "1.43.0-next", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "playwright-internal", - "version": "1.42.0-next", + "version": "1.43.0-next", "license": "Apache-2.0", "workspaces": [ "packages/*" @@ -8136,10 +8136,10 @@ } }, "packages/playwright": { - "version": "1.42.0-next", + "version": "1.43.0-next", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.42.0-next" + "playwright-core": "1.43.0-next" }, "bin": { "playwright": "cli.js" @@ -8153,11 +8153,11 @@ }, "packages/playwright-browser-chromium": { "name": "@playwright/browser-chromium", - "version": "1.42.0-next", + "version": "1.43.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.42.0-next" + "playwright-core": "1.43.0-next" }, "engines": { "node": ">=16" @@ -8165,11 +8165,11 @@ }, "packages/playwright-browser-firefox": { "name": "@playwright/browser-firefox", - "version": "1.42.0-next", + "version": "1.43.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.42.0-next" + "playwright-core": "1.43.0-next" }, "engines": { "node": ">=16" @@ -8177,22 +8177,22 @@ }, "packages/playwright-browser-webkit": { "name": "@playwright/browser-webkit", - "version": "1.42.0-next", + "version": "1.43.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.42.0-next" + "playwright-core": "1.43.0-next" }, "engines": { "node": ">=16" } }, "packages/playwright-chromium": { - "version": "1.42.0-next", + "version": "1.43.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.42.0-next" + "playwright-core": "1.43.0-next" }, "bin": { "playwright": "cli.js" @@ -8202,7 +8202,7 @@ } }, "packages/playwright-core": { - "version": "1.42.0-next", + "version": "1.43.0-next", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -8213,11 +8213,11 @@ }, "packages/playwright-ct-core": { "name": "@playwright/experimental-ct-core", - "version": "1.42.0-next", + "version": "1.43.0-next", "license": "Apache-2.0", "dependencies": { - "playwright": "1.42.0-next", - "playwright-core": "1.42.0-next", + "playwright": "1.43.0-next", + "playwright-core": "1.43.0-next", "vite": "^5.0.12" }, "bin": { @@ -8229,10 +8229,10 @@ }, "packages/playwright-ct-react": { "name": "@playwright/experimental-ct-react", - "version": "1.42.0-next", + "version": "1.43.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.42.0-next", + "@playwright/experimental-ct-core": "1.43.0-next", "@vitejs/plugin-react": "^4.2.1" }, "bin": { @@ -8245,10 +8245,10 @@ }, "packages/playwright-ct-react17": { "name": "@playwright/experimental-ct-react17", - "version": "1.42.0-next", + "version": "1.43.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.42.0-next", + "@playwright/experimental-ct-core": "1.43.0-next", "@vitejs/plugin-react": "^4.2.1" }, "bin": { @@ -8261,10 +8261,10 @@ }, "packages/playwright-ct-solid": { "name": "@playwright/experimental-ct-solid", - "version": "1.42.0-next", + "version": "1.43.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.42.0-next", + "@playwright/experimental-ct-core": "1.43.0-next", "vite-plugin-solid": "^2.7.0" }, "bin": { @@ -8280,10 +8280,10 @@ }, "packages/playwright-ct-svelte": { "name": "@playwright/experimental-ct-svelte", - "version": "1.42.0-next", + "version": "1.43.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.42.0-next", + "@playwright/experimental-ct-core": "1.43.0-next", "@sveltejs/vite-plugin-svelte": "^3.0.1" }, "bin": { @@ -8299,10 +8299,10 @@ }, "packages/playwright-ct-vue": { "name": "@playwright/experimental-ct-vue", - "version": "1.42.0-next", + "version": "1.43.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.42.0-next", + "@playwright/experimental-ct-core": "1.43.0-next", "@vitejs/plugin-vue": "^4.2.1" }, "bin": { @@ -8315,10 +8315,10 @@ }, "packages/playwright-ct-vue2": { "name": "@playwright/experimental-ct-vue2", - "version": "1.42.0-next", + "version": "1.43.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.42.0-next", + "@playwright/experimental-ct-core": "1.43.0-next", "@vitejs/plugin-vue2": "^2.2.0" }, "bin": { @@ -8368,11 +8368,11 @@ } }, "packages/playwright-firefox": { - "version": "1.42.0-next", + "version": "1.43.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.42.0-next" + "playwright-core": "1.43.0-next" }, "bin": { "playwright": "cli.js" @@ -8383,10 +8383,10 @@ }, "packages/playwright-test": { "name": "@playwright/test", - "version": "1.42.0-next", + "version": "1.43.0-next", "license": "Apache-2.0", "dependencies": { - "playwright": "1.42.0-next" + "playwright": "1.43.0-next" }, "bin": { "playwright": "cli.js" @@ -8396,11 +8396,11 @@ } }, "packages/playwright-webkit": { - "version": "1.42.0-next", + "version": "1.43.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.42.0-next" + "playwright-core": "1.43.0-next" }, "bin": { "playwright": "cli.js" diff --git a/package.json b/package.json index 89550ec4e4..fdbafad27c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "playwright-internal", "private": true, - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -29,7 +29,7 @@ "ttest": "node ./tests/playwright-test/stable-test-runner/node_modules/@playwright/test/cli test --config=tests/playwright-test/playwright.config.ts", "ct": "playwright test tests/components/test-all.spec.js --reporter=list", "test": "playwright test --config=tests/library/playwright.config.ts", - "eslint": "eslint --cache --report-unused-disable-directives --ext ts,tsx .", + "eslint": "eslint --cache --report-unused-disable-directives --ext ts,tsx,js,jsx,mjs .", "tsc": "tsc -p .", "build-installer": "babel -s --extensions \".ts\" --out-dir packages/playwright-core/lib/utils/ packages/playwright-core/src/utils", "doc": "node utils/doclint/cli.js", diff --git a/packages/.eslintrc-with-ts-config.js b/packages/.eslintrc-with-ts-config.js deleted file mode 100644 index dea9d4ef41..0000000000 --- a/packages/.eslintrc-with-ts-config.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - extends: ".eslintrc.js", - rules: { - "@typescript-eslint/no-base-to-string": "error", - }, - parserOptions: { - project: "./tsconfig.json" - }, -}; diff --git a/packages/playwright-browser-chromium/package.json b/packages/playwright-browser-chromium/package.json index 4c16717df4..e4ce3d992f 100644 --- a/packages/playwright-browser-chromium/package.json +++ b/packages/playwright-browser-chromium/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-chromium", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "Playwright package that automatically installs Chromium", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.42.0-next" + "playwright-core": "1.43.0-next" } } diff --git a/packages/playwright-browser-firefox/package.json b/packages/playwright-browser-firefox/package.json index 1c100b2fce..866216d956 100644 --- a/packages/playwright-browser-firefox/package.json +++ b/packages/playwright-browser-firefox/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-firefox", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "Playwright package that automatically installs Firefox", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.42.0-next" + "playwright-core": "1.43.0-next" } } diff --git a/packages/playwright-browser-webkit/package.json b/packages/playwright-browser-webkit/package.json index 270c601bd9..4e64a3aa48 100644 --- a/packages/playwright-browser-webkit/package.json +++ b/packages/playwright-browser-webkit/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-webkit", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "Playwright package that automatically installs WebKit", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.42.0-next" + "playwright-core": "1.43.0-next" } } diff --git a/packages/playwright-chromium/package.json b/packages/playwright-chromium/package.json index 5f7431d84a..c39bc5031b 100644 --- a/packages/playwright-chromium/package.json +++ b/packages/playwright-chromium/package.json @@ -1,6 +1,6 @@ { "name": "playwright-chromium", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "A high-level API to automate Chromium", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.42.0-next" + "playwright-core": "1.43.0-next" } } diff --git a/packages/playwright-core/.eslintrc.js b/packages/playwright-core/.eslintrc.js index ae8768db65..84888f1ae3 100644 --- a/packages/playwright-core/.eslintrc.js +++ b/packages/playwright-core/.eslintrc.js @@ -1,3 +1,3 @@ module.exports = { - extends: "../.eslintrc-with-ts-config.js", + extends: "../../.eslintrc-with-ts-config.js", }; diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index 8a0af13f94..bf862ae74c 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -21,15 +21,15 @@ }, { "name": "firefox", - "revision": "1439", + "revision": "1440", "installByDefault": true, - "browserVersion": "122.0" + "browserVersion": "123.0" }, { "name": "firefox-asan", - "revision": "1439", + "revision": "1440", "installByDefault": false, - "browserVersion": "122.0" + "browserVersion": "123.0" }, { "name": "firefox-beta", diff --git a/packages/playwright-core/package.json b/packages/playwright-core/package.json index c846a750e7..769dd27737 100644 --- a/packages/playwright-core/package.json +++ b/packages/playwright-core/package.json @@ -1,6 +1,6 @@ { "name": "playwright-core", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "A high-level API to automate web browsers", "repository": { "type": "git", diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index 6f32385ee8..39140f904c 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -526,7 +526,7 @@ export async function prepareBrowserContextParams(options: BrowserContextOptions function toAcceptDownloadsProtocol(acceptDownloads?: boolean) { if (acceptDownloads === undefined) return undefined; - if (acceptDownloads === true) + if (acceptDownloads) return 'accept'; return 'deny'; } diff --git a/packages/playwright-core/src/server/chromium/crBrowser.ts b/packages/playwright-core/src/server/chromium/crBrowser.ts index 0217005427..7715d8b23f 100644 --- a/packages/playwright-core/src/server/chromium/crBrowser.ts +++ b/packages/playwright-core/src/server/chromium/crBrowser.ts @@ -594,7 +594,8 @@ export class CRBrowserContext extends BrowserContext { targetId = (page._delegate as CRPage)._targetId; } else if (page instanceof Frame) { const session = (page._page._delegate as CRPage)._sessions.get(page._id); - if (!session) throw new Error(`This frame does not have a separate CDP session, it is a part of the parent frame's session`); + if (!session) + throw new Error(`This frame does not have a separate CDP session, it is a part of the parent frame's session`); targetId = session._targetId; } else { throw new Error('page: expected Page or Frame'); diff --git a/packages/playwright-core/src/server/deviceDescriptorsSource.json b/packages/playwright-core/src/server/deviceDescriptorsSource.json index f44a165987..6191ef5d2e 100644 --- a/packages/playwright-core/src/server/deviceDescriptorsSource.json +++ b/packages/playwright-core/src/server/deviceDescriptorsSource.json @@ -1472,7 +1472,7 @@ "defaultBrowserType": "chromium" }, "Desktop Firefox HiDPI": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0", "screen": { "width": 1792, "height": 1120 @@ -1532,7 +1532,7 @@ "defaultBrowserType": "chromium" }, "Desktop Firefox": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0", "screen": { "width": 1920, "height": 1080 diff --git a/packages/playwright-core/src/server/injected/vueSelectorEngine.ts b/packages/playwright-core/src/server/injected/vueSelectorEngine.ts index 0ba552d558..154f36b983 100644 --- a/packages/playwright-core/src/server/injected/vueSelectorEngine.ts +++ b/packages/playwright-core/src/server/injected/vueSelectorEngine.ts @@ -86,12 +86,18 @@ function buildComponentsTreeVue3(instance: VueVNode): ComponentNode { // @see https://github.com/vuejs/devtools/blob/e7132f3392b975e39e1d9a23cf30456c270099c2/packages/app-backend-vue3/src/components/util.ts#L29 function getInstanceName(instance: VueVNode): string { const name = getComponentTypeName(instance.type || {}); - if (name) return name; - if (instance.root === instance) return 'Root'; - for (const key in instance.parent?.type?.components) - if (instance.parent?.type.components[key] === instance.type) return saveComponentName(instance, key); - for (const key in instance.appContext?.components) - if (instance.appContext.components[key] === instance.type) return saveComponentName(instance, key); + if (name) + return name; + if (instance.root === instance) + return 'Root'; + for (const key in instance.parent?.type?.components) { + if (instance.parent?.type.components[key] === instance.type) + return saveComponentName(instance, key); + } + for (const key in instance.appContext?.components) { + if (instance.appContext.components[key] === instance.type) + return saveComponentName(instance, key); + } return 'Anonymous Component'; } @@ -132,7 +138,8 @@ function buildComponentsTreeVue3(instance: VueVNode): ComponentNode { // @see https://github.com/vuejs/devtools/blob/e7132f3392b975e39e1d9a23cf30456c270099c2/packages/app-backend-vue3/src/components/el.ts#L15 function getFragmentRootElements(vnode: any): Element[] { - if (!vnode.children) return []; + if (!vnode.children) + return []; const list = []; diff --git a/packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts b/packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts index 12fa08e80d..f72ef27eb4 100644 --- a/packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts +++ b/packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts @@ -48,8 +48,10 @@ function preprocess(str: string): number[] { if (code === 0xd && str.charCodeAt(i + 1) === 0xa) { code = 0xa; i++; } - if (code === 0xd || code === 0xc) code = 0xa; - if (code === 0x0) code = 0xfffd; + if (code === 0xd || code === 0xc) + code = 0xa; + if (code === 0x0) + code = 0xfffd; if (between(code, 0xd800, 0xdbff) && between(str.charCodeAt(i + 1), 0xdc00, 0xdfff)) { // Decode a surrogate pair into an astral codepoint. const lead = code - 0xd800; @@ -63,7 +65,8 @@ function preprocess(str: string): number[] { } function stringFromCode(code: number) { - if (code <= 0xffff) return String.fromCharCode(code); + if (code <= 0xffff) + return String.fromCharCode(code); // Otherwise, encode astral char as surrogate pair. code -= Math.pow(2, 16); const lead = Math.floor(code / Math.pow(2, 10)) + 0xd800; @@ -107,8 +110,10 @@ export function tokenize(str1: string): CSSTokenInterface[] { num = 1; i += num; code = codepoint(i); - if (newline(code)) incrLineno(); - else column += num; + if (newline(code)) + incrLineno(); + else + column += num; // console.log('Consume '+i+' '+String.fromCharCode(code) + ' 0x' + code.toString(16)); return true; }; @@ -125,7 +130,8 @@ export function tokenize(str1: string): CSSTokenInterface[] { return true; }; const eof = function(codepoint?: number): boolean { - if (codepoint === undefined) codepoint = code; + if (codepoint === undefined) + codepoint = code; return codepoint === -1; }; const donothing = function() { }; @@ -138,12 +144,14 @@ export function tokenize(str1: string): CSSTokenInterface[] { consumeComments(); consume(); if (whitespace(code)) { - while (whitespace(next())) consume(); + while (whitespace(next())) + consume(); return new WhitespaceToken(); } else if (code === 0x22) {return consumeAStringToken();} else if (code === 0x23) { if (namechar(next()) || areAValidEscape(next(1), next(2))) { const token = new HashToken(''); - if (wouldStartAnIdentifier(next(1), next(2), next(3))) token.type = 'id'; + if (wouldStartAnIdentifier(next(1), next(2), next(3))) + token.type = 'id'; token.value = consumeAName(); return token; } else { @@ -288,7 +296,8 @@ export function tokenize(str1: string): CSSTokenInterface[] { const str = consumeAName(); if (str.toLowerCase() === 'url' && next() === 0x28) { consume(); - while (whitespace(next(1)) && whitespace(next(2))) consume(); + while (whitespace(next(1)) && whitespace(next(2))) + consume(); if (next() === 0x22 || next() === 0x27) return new FunctionToken(str); else if (whitespace(next()) && (next(2) === 0x22 || next(2) === 0x27)) @@ -305,7 +314,8 @@ export function tokenize(str1: string): CSSTokenInterface[] { }; const consumeAStringToken = function(endingCodePoint?: number): CSSParserToken { - if (endingCodePoint === undefined) endingCodePoint = code; + if (endingCodePoint === undefined) + endingCodePoint = code; let string = ''; while (consume()) { if (code === endingCodePoint || eof()) { @@ -331,13 +341,16 @@ export function tokenize(str1: string): CSSTokenInterface[] { const consumeAURLToken = function(): CSSTokenInterface { const token = new URLToken(''); - while (whitespace(next())) consume(); - if (eof(next())) return token; + while (whitespace(next())) + consume(); + if (eof(next())) + return token; while (consume()) { if (code === 0x29 || eof()) { return token; } else if (whitespace(code)) { - while (whitespace(next())) consume(); + while (whitespace(next())) + consume(); if (next() === 0x29 || eof(next())) { consume(); return token; @@ -379,9 +392,11 @@ export function tokenize(str1: string): CSSTokenInterface[] { break; } } - if (whitespace(next())) consume(); + if (whitespace(next())) + consume(); let value = parseInt(digits.map(function(x) { return String.fromCharCode(x); }).join(''), 16); - if (value > maximumallowedcodepoint) value = 0xfffd; + if (value > maximumallowedcodepoint) + value = 0xfffd; return value; } else if (eof()) { return 0xfffd; @@ -391,8 +406,10 @@ export function tokenize(str1: string): CSSTokenInterface[] { }; const areAValidEscape = function(c1: number, c2: number) { - if (c1 !== 0x5c) return false; - if (newline(c2)) return false; + if (c1 !== 0x5c) + return false; + if (newline(c2)) + return false; return true; }; const startsWithAValidEscape = function() { @@ -416,11 +433,14 @@ export function tokenize(str1: string): CSSTokenInterface[] { const wouldStartANumber = function(c1: number, c2: number, c3: number) { if (c1 === 0x2b || c1 === 0x2d) { - if (digit(c2)) return true; - if (c2 === 0x2e && digit(c3)) return true; + if (digit(c2)) + return true; + if (c2 === 0x2e && digit(c3)) + return true; return false; } else if (c1 === 0x2e) { - if (digit(c2)) return true; + if (digit(c2)) + return true; return false; } else if (digit(c1)) { return true; @@ -519,7 +539,8 @@ export function tokenize(str1: string): CSSTokenInterface[] { while (!eof(next())) { tokens.push(consumeAToken()); iterationCount++; - if (iterationCount > str.length * 2) throw new Error("I'm infinite-looping!"); + if (iterationCount > str.length * 2) + throw new Error("I'm infinite-looping!"); } return tokens; } diff --git a/packages/playwright-ct-core/.eslintrc.js b/packages/playwright-ct-core/.eslintrc.js index ae8768db65..84888f1ae3 100644 --- a/packages/playwright-ct-core/.eslintrc.js +++ b/packages/playwright-ct-core/.eslintrc.js @@ -1,3 +1,3 @@ module.exports = { - extends: "../.eslintrc-with-ts-config.js", + extends: "../../.eslintrc-with-ts-config.js", }; diff --git a/packages/playwright-ct-core/package.json b/packages/playwright-ct-core/package.json index 99dd286fca..fd63ced898 100644 --- a/packages/playwright-ct-core/package.json +++ b/packages/playwright-ct-core/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-core", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "Playwright Component Testing Helpers", "repository": { "type": "git", @@ -26,9 +26,9 @@ } }, "dependencies": { - "playwright-core": "1.42.0-next", + "playwright-core": "1.43.0-next", "vite": "^5.0.12", - "playwright": "1.42.0-next" + "playwright": "1.43.0-next" }, "bin": { "playwright": "cli.js" diff --git a/packages/playwright-ct-react/package.json b/packages/playwright-ct-react/package.json index 5702342623..cc42049882 100644 --- a/packages/playwright-ct-react/package.json +++ b/packages/playwright-ct-react/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-react", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "Playwright Component Testing for React", "repository": { "type": "git", @@ -29,7 +29,7 @@ } }, "dependencies": { - "@playwright/experimental-ct-core": "1.42.0-next", + "@playwright/experimental-ct-core": "1.43.0-next", "@vitejs/plugin-react": "^4.2.1" }, "bin": { diff --git a/packages/playwright-ct-react/registerSource.mjs b/packages/playwright-ct-react/registerSource.mjs index 10dad4c0db..9ad2612bc4 100644 --- a/packages/playwright-ct-react/registerSource.mjs +++ b/packages/playwright-ct-react/registerSource.mjs @@ -40,13 +40,11 @@ function __pwRender(value) { if (isJsxComponent(v)) { const component = v; const props = component.props ? __pwRender(component.props) : {}; - const {children, ...propsWithoutChildren} = props; - /** @type {[any, any, any?]} */ - const createElementArguments = [component.type, propsWithoutChildren]; - if(children){ + const { children, ...propsWithoutChildren } = props; + const createElementArguments = [propsWithoutChildren]; + if (children) createElementArguments.push(children); - } - return { result: __pwReact.createElement(...createElementArguments) }; + return { result: __pwReact.createElement(component.type, ...createElementArguments) }; } }); } diff --git a/packages/playwright-ct-react17/package.json b/packages/playwright-ct-react17/package.json index 4e926cca74..78f43509e8 100644 --- a/packages/playwright-ct-react17/package.json +++ b/packages/playwright-ct-react17/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-react17", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "Playwright Component Testing for React", "repository": { "type": "git", @@ -29,7 +29,7 @@ } }, "dependencies": { - "@playwright/experimental-ct-core": "1.42.0-next", + "@playwright/experimental-ct-core": "1.43.0-next", "@vitejs/plugin-react": "^4.2.1" }, "bin": { diff --git a/packages/playwright-ct-react17/registerSource.mjs b/packages/playwright-ct-react17/registerSource.mjs index 8dfc1d24e9..158984f3ac 100644 --- a/packages/playwright-ct-react17/registerSource.mjs +++ b/packages/playwright-ct-react17/registerSource.mjs @@ -40,14 +40,11 @@ function __pwRender(value) { if (isJsxComponent(v)) { const component = v; const props = component.props ? __pwRender(component.props) : {}; - - const {children, ...propsWithoutChildren} = props; - /** @type {[any, any, any?]} */ - const createElementArguments = [component.type, propsWithoutChildren]; - if(children){ + const { children, ...propsWithoutChildren } = props; + const createElementArguments = [propsWithoutChildren]; + if (children) createElementArguments.push(children); - } - return { result: __pwReact.createElement(...createElementArguments) }; + return { result: __pwReact.createElement(component.type, ...createElementArguments) }; } }); } diff --git a/packages/playwright-ct-solid/package.json b/packages/playwright-ct-solid/package.json index ac65ccb548..84e0355fb2 100644 --- a/packages/playwright-ct-solid/package.json +++ b/packages/playwright-ct-solid/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-solid", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "Playwright Component Testing for Solid", "repository": { "type": "git", @@ -29,7 +29,7 @@ } }, "dependencies": { - "@playwright/experimental-ct-core": "1.42.0-next", + "@playwright/experimental-ct-core": "1.43.0-next", "vite-plugin-solid": "^2.7.0" }, "devDependencies": { diff --git a/packages/playwright-ct-solid/registerSource.mjs b/packages/playwright-ct-solid/registerSource.mjs index a76792c513..d0077dd494 100644 --- a/packages/playwright-ct-solid/registerSource.mjs +++ b/packages/playwright-ct-solid/registerSource.mjs @@ -19,9 +19,7 @@ import { render as __pwSolidRender, createComponent as __pwSolidCreateComponent } from 'solid-js/web'; import __pwH from 'solid-js/h'; - /** @typedef {import('../playwright-ct-core/types/component').JsxComponent} JsxComponent */ -/** @typedef {() => import('solid-js').JSX.Element} FrameworkComponent */ /** * @param {any} component @@ -32,42 +30,20 @@ function isJsxComponent(component) { } /** - * @param {any} child + * @param {any} value */ -function __pwCreateChild(child) { - if (Array.isArray(child)) - return child.map(grandChild => __pwCreateChild(grandChild)); - if (isJsxComponent(child)) - return __pwCreateComponent(child); - return child; -} - -/** - * @param {JsxComponent} component - * @returns {any[] | undefined} - */ -function __pwJsxChildArray(component) { - if (!component.props.children) - return; - if (Array.isArray(component.props.children)) - return component.props.children; - return [component.props.children]; -} - -/** - * @param {JsxComponent} component - */ -function __pwCreateComponent(component) { - const children = __pwJsxChildArray(component)?.map(child => __pwCreateChild(child)).filter(child => { - if (typeof child === 'string') - return !!child.trim(); - return true; +function __pwCreateComponent(value) { + return window.__pwTransformObject(value, v => { + if (isJsxComponent(v)) { + const component = v; + const props = component.props ? __pwCreateComponent(component.props) : {}; + if (typeof component.type === 'string') { + const { children, ...propsWithoutChildren } = props; + return { result: __pwH(component.type, propsWithoutChildren, children) }; + } + return { result: __pwSolidCreateComponent(component.type, props) }; + } }); - - if (typeof component.type === 'string') - return __pwH(component.type, component.props, children); - - return __pwSolidCreateComponent(component.type, { ...component.props, children }); } const __pwUnmountKey = Symbol('unmountKey'); @@ -96,6 +72,7 @@ window.playwrightUnmount = async rootElement => { throw new Error('Component was not mounted'); unmount(); + delete rootElement[__pwUnmountKey]; }; window.playwrightUpdate = async (rootElement, component) => { diff --git a/packages/playwright-ct-svelte/package.json b/packages/playwright-ct-svelte/package.json index f0809a3d4a..5b9ff27347 100644 --- a/packages/playwright-ct-svelte/package.json +++ b/packages/playwright-ct-svelte/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-svelte", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "Playwright Component Testing for Svelte", "repository": { "type": "git", @@ -29,7 +29,7 @@ } }, "dependencies": { - "@playwright/experimental-ct-core": "1.42.0-next", + "@playwright/experimental-ct-core": "1.43.0-next", "@sveltejs/vite-plugin-svelte": "^3.0.1" }, "devDependencies": { diff --git a/packages/playwright-ct-svelte/registerSource.mjs b/packages/playwright-ct-svelte/registerSource.mjs index 4552a4ba0a..5901458813 100644 --- a/packages/playwright-ct-svelte/registerSource.mjs +++ b/packages/playwright-ct-svelte/registerSource.mjs @@ -42,8 +42,8 @@ function __pwCreateSlots(slots) { for (const slotName in slots) { const template = document - .createRange() - .createContextualFragment(slots[slotName]); + .createRange() + .createContextualFragment(slots[slotName]); svelteSlots[slotName] = [createSlotFn(template)]; } @@ -55,7 +55,8 @@ function __pwCreateSlots(slots) { __pwInsert(target, element, anchor); }, d: function destroy(detaching) { - if (detaching) __pwDetach(element); + if (detaching) + __pwDetach(element); }, l: __pwNoop, }; @@ -108,6 +109,7 @@ window.playwrightUnmount = async rootElement => { if (!svelteComponent) throw new Error('Component was not mounted'); svelteComponent.$destroy(); + delete rootElement[__pwSvelteComponentKey]; }; window.playwrightUpdate = async (rootElement, component) => { diff --git a/packages/playwright-ct-vue/package.json b/packages/playwright-ct-vue/package.json index 7ab9b652cb..c628a490d8 100644 --- a/packages/playwright-ct-vue/package.json +++ b/packages/playwright-ct-vue/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-vue", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "Playwright Component Testing for Vue", "repository": { "type": "git", @@ -29,7 +29,7 @@ } }, "dependencies": { - "@playwright/experimental-ct-core": "1.42.0-next", + "@playwright/experimental-ct-core": "1.43.0-next", "@vitejs/plugin-vue": "^4.2.1" }, "bin": { diff --git a/packages/playwright-ct-vue/registerSource.mjs b/packages/playwright-ct-vue/registerSource.mjs index 5256a241a4..07ce5298f4 100644 --- a/packages/playwright-ct-vue/registerSource.mjs +++ b/packages/playwright-ct-vue/registerSource.mjs @@ -88,6 +88,9 @@ function __pwCreateSlot(html) { }; } +/** + * @param {string | string[]} slot + */ function __pwSlotToFunction(slot) { if (typeof slot === 'string') return __pwCreateSlot(slot)(); @@ -175,7 +178,11 @@ function __pwCreateComponent(component) { return { Component: component.type, props, slots: lastArg, listeners }; } +/** + * @param {any} slots + */ function __pwWrapFunctions(slots) { + /** @type {import('vue').ComponentInternalInstance['slots']} */ const slotsWithRenderFunctions = {}; if (!Array.isArray(slots)) { for (const [key, value] of Object.entries(slots || {})) @@ -198,25 +205,27 @@ function __pwCreateWrapper(component) { return wrapper; } -/** - * @returns {any} - */ -function __pwCreateDevTools() { - return { +function __pwSetDevTools() { + __pwSetDevtoolsHook({ emit(eventType, ...payload) { - if (eventType === 'component:emit') { - const [, componentVM, event, eventArgs] = payload; - for (const [wrapper, listeners] of __pwAllListeners) { - if (wrapper.component !== componentVM) - continue; - const listener = listeners[event]; - if (!listener) - return; - listener(...eventArgs); - } + if (eventType !== 'component:emit') + return; + + const [, componentVM, event, eventArgs] = payload; + for (const [wrapper, listeners] of __pwAllListeners) { + if (wrapper.component !== componentVM) + continue; + const listener = listeners[event]; + if (!listener) + return; + listener(...eventArgs); } - } - }; + }, + on() {}, + off() {}, + once() {}, + appRecords: [] + }, {}); } const __pwAppKey = Symbol('appKey'); @@ -230,7 +239,7 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => { return wrapper; } }); - __pwSetDevtoolsHook(__pwCreateDevTools(), {}); + __pwSetDevTools(); for (const hook of window.__pw_hooks_before_mount || []) await hook({ app, hooksConfig }); @@ -242,13 +251,16 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => { }; window.playwrightUnmount = async rootElement => { - const app = /** @type {import('vue').App} */ (rootElement[__pwAppKey]); + /** @type {import('vue').App | undefined} */ + const app = rootElement[__pwAppKey]; if (!app) throw new Error('Component was not mounted'); app.unmount(); + delete rootElement[__pwAppKey]; }; window.playwrightUpdate = async (rootElement, component) => { + /** @type {import('vue').VNode | undefined} */ const wrapper = rootElement[__pwWrapperKey]; if (!wrapper) throw new Error('Component was not mounted'); diff --git a/packages/playwright-ct-vue2/package.json b/packages/playwright-ct-vue2/package.json index 0ba67d6bd6..c19820ae91 100644 --- a/packages/playwright-ct-vue2/package.json +++ b/packages/playwright-ct-vue2/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-vue2", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "Playwright Component Testing for Vue2", "repository": { "type": "git", @@ -29,7 +29,7 @@ } }, "dependencies": { - "@playwright/experimental-ct-core": "1.42.0-next", + "@playwright/experimental-ct-core": "1.43.0-next", "@vitejs/plugin-vue2": "^2.2.0" }, "devDependencies": { diff --git a/packages/playwright-ct-vue2/registerSource.mjs b/packages/playwright-ct-vue2/registerSource.mjs index b0a7ae71dc..19b4d41c08 100644 --- a/packages/playwright-ct-vue2/registerSource.mjs +++ b/packages/playwright-ct-vue2/registerSource.mjs @@ -182,6 +182,7 @@ window.playwrightUnmount = async rootElement => { throw new Error('Component was not mounted'); component.$destroy(); component.$el.remove(); + delete rootElement[instanceKey]; }; window.playwrightUpdate = async (element, options) => { diff --git a/packages/playwright-firefox/package.json b/packages/playwright-firefox/package.json index 83c9f54637..e2aed567e6 100644 --- a/packages/playwright-firefox/package.json +++ b/packages/playwright-firefox/package.json @@ -1,6 +1,6 @@ { "name": "playwright-firefox", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "A high-level API to automate Firefox", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.42.0-next" + "playwright-core": "1.43.0-next" } } diff --git a/packages/playwright-test/package.json b/packages/playwright-test/package.json index ee88606959..c3174bde51 100644 --- a/packages/playwright-test/package.json +++ b/packages/playwright-test/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/test", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -30,6 +30,6 @@ }, "scripts": {}, "dependencies": { - "playwright": "1.42.0-next" + "playwright": "1.43.0-next" } } diff --git a/packages/playwright-webkit/package.json b/packages/playwright-webkit/package.json index a72ba97917..a269a30da4 100644 --- a/packages/playwright-webkit/package.json +++ b/packages/playwright-webkit/package.json @@ -1,6 +1,6 @@ { "name": "playwright-webkit", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "A high-level API to automate WebKit", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.42.0-next" + "playwright-core": "1.43.0-next" } } diff --git a/packages/playwright/.eslintrc.js b/packages/playwright/.eslintrc.js index 67c9b6313d..b7e707b35f 100644 --- a/packages/playwright/.eslintrc.js +++ b/packages/playwright/.eslintrc.js @@ -1,14 +1,5 @@ -const path = require('path'); - module.exports = { - extends: '../.eslintrc.js', - parser: "@typescript-eslint/parser", - plugins: ["@typescript-eslint", "notice"], - parserOptions: { - ecmaVersion: 9, - sourceType: "module", - project: path.join(__dirname, '..', '..', 'tsconfig.json'), - }, + extends: '../../.eslintrc-with-ts-config.js', rules: { '@typescript-eslint/no-floating-promises': 'error', }, diff --git a/packages/playwright/package.json b/packages/playwright/package.json index 39e4cf23ec..5072f27c14 100644 --- a/packages/playwright/package.json +++ b/packages/playwright/package.json @@ -1,6 +1,6 @@ { "name": "playwright", - "version": "1.42.0-next", + "version": "1.43.0-next", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -58,7 +58,7 @@ }, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.42.0-next" + "playwright-core": "1.43.0-next" }, "optionalDependencies": { "fsevents": "2.3.2" diff --git a/packages/playwright/src/matchers/matcherHint.ts b/packages/playwright/src/matchers/matcherHint.ts index 229ef6ca28..17d893431c 100644 --- a/packages/playwright/src/matchers/matcherHint.ts +++ b/packages/playwright/src/matchers/matcherHint.ts @@ -25,7 +25,7 @@ export function matcherHint(state: ExpectMatcherContext, locator: Locator | unde if (timeout) header = colors.red(`Timed out ${timeout}ms waiting for `) + header; if (locator) - header += `Locator: ${locator}\n`; + header += `Locator: ${String(locator)}\n`; return header; } diff --git a/packages/playwright/src/matchers/matchers.ts b/packages/playwright/src/matchers/matchers.ts index 3dc08ba223..01d1dfad25 100644 --- a/packages/playwright/src/matchers/matchers.ts +++ b/packages/playwright/src/matchers/matchers.ts @@ -40,7 +40,7 @@ export function toBeAttached( locator: LocatorEx, options?: { attached?: boolean, timeout?: number }, ) { - const attached = !options || options.attached === undefined || options.attached === true; + const attached = !options || options.attached === undefined || options.attached; const expected = attached ? 'attached' : 'detached'; const unexpected = attached ? 'detached' : 'attached'; const arg = attached ? '' : '{ attached: false }'; @@ -54,7 +54,7 @@ export function toBeChecked( locator: LocatorEx, options?: { checked?: boolean, timeout?: number }, ) { - const checked = !options || options.checked === undefined || options.checked === true; + const checked = !options || options.checked === undefined || options.checked; const expected = checked ? 'checked' : 'unchecked'; const unexpected = checked ? 'unchecked' : 'checked'; const arg = checked ? '' : '{ checked: false }'; @@ -78,7 +78,7 @@ export function toBeEditable( locator: LocatorEx, options?: { editable?: boolean, timeout?: number }, ) { - const editable = !options || options.editable === undefined || options.editable === true; + const editable = !options || options.editable === undefined || options.editable; const expected = editable ? 'editable' : 'readOnly'; const unexpected = editable ? 'readOnly' : 'editable'; const arg = editable ? '' : '{ editable: false }'; @@ -102,7 +102,7 @@ export function toBeEnabled( locator: LocatorEx, options?: { enabled?: boolean, timeout?: number }, ) { - const enabled = !options || options.enabled === undefined || options.enabled === true; + const enabled = !options || options.enabled === undefined || options.enabled; const expected = enabled ? 'enabled' : 'disabled'; const unexpected = enabled ? 'disabled' : 'enabled'; const arg = enabled ? '' : '{ enabled: false }'; @@ -136,7 +136,7 @@ export function toBeVisible( locator: LocatorEx, options?: { visible?: boolean, timeout?: number }, ) { - const visible = !options || options.visible === undefined || options.visible === true; + const visible = !options || options.visible === undefined || options.visible; const expected = visible ? 'visible' : 'hidden'; const unexpected = visible ? 'hidden' : 'visible'; const arg = visible ? '' : '{ visible: false }'; diff --git a/packages/playwright/src/runner/testServer.ts b/packages/playwright/src/runner/testServer.ts index 0b93adb7c5..c21733b9d5 100644 --- a/packages/playwright/src/runner/testServer.ts +++ b/packages/playwright/src/runner/testServer.ts @@ -43,7 +43,7 @@ export async function runTestServer() { onConnection(request: http.IncomingMessage, url: URL, ws: WebSocket, id: string) { const dispatcher = new Dispatcher(ws); ws.on('message', async message => { - const { id, method, params } = JSON.parse(message.toString()); + const { id, method, params } = JSON.parse(String(message)); try { const result = await (dispatcher as any)[method](params); ws.send(JSON.stringify({ id, result })); diff --git a/packages/playwright/src/util.ts b/packages/playwright/src/util.ts index 34907428c9..505a59c6b6 100644 --- a/packages/playwright/src/util.ts +++ b/packages/playwright/src/util.ts @@ -208,7 +208,8 @@ export function addSuffixToFilePath(filePath: string, suffix: string, customExte */ export function getContainedPath(parentPath: string, subPath: string = ''): string | null { const resolvedPath = path.resolve(parentPath, subPath); - if (resolvedPath === parentPath || resolvedPath.startsWith(parentPath + path.sep)) return resolvedPath; + if (resolvedPath === parentPath || resolvedPath.startsWith(parentPath + path.sep)) + return resolvedPath; return null; } diff --git a/packages/trace-viewer/src/ui/wsPort.ts b/packages/trace-viewer/src/ui/wsPort.ts index fc08d4cdf4..298fa71d17 100644 --- a/packages/trace-viewer/src/ui/wsPort.ts +++ b/packages/trace-viewer/src/ui/wsPort.ts @@ -20,7 +20,9 @@ const callbacks = new Map void, reject: (arg: E export async function connect(options: { onEvent: (method: string, params?: any) => void, onClose: () => void }): Promise<(method: string, params?: any) => Promise> { const guid = new URLSearchParams(window.location.search).get('ws'); - const ws = new WebSocket(`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.hostname}:${window.location.port}/${guid}`); + const wsURL = new URL(`../${guid}`, window.location.toString()); + wsURL.protocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:'); + const ws = new WebSocket(wsURL); await new Promise(f => ws.addEventListener('open', f)); ws.addEventListener('close', options.onClose); ws.addEventListener('message', event => { diff --git a/tests/.eslintrc.js b/tests/.eslintrc.js index 719e15f986..2621e5234e 100644 --- a/tests/.eslintrc.js +++ b/tests/.eslintrc.js @@ -11,5 +11,6 @@ module.exports = { }, rules: { '@typescript-eslint/no-floating-promises': 'error', + "@typescript-eslint/no-unnecessary-boolean-literal-compare": 2, }, }; diff --git a/tests/components/ct-react-vite/src/components/CheckChildrenProp.tsx b/tests/components/ct-react-vite/src/components/CheckChildrenProp.tsx index 42b3a361e8..3e8f405a5b 100644 --- a/tests/components/ct-react-vite/src/components/CheckChildrenProp.tsx +++ b/tests/components/ct-react-vite/src/components/CheckChildrenProp.tsx @@ -1,16 +1,7 @@ -type DefaultChildrenProps = { - children?: any; -} +import type { PropsWithChildren } from 'react'; + +type DefaultChildrenProps = PropsWithChildren<{}>; export default function CheckChildrenProp(props: DefaultChildrenProps) { - const content = 'children' in props ? props.children : 'No Children'; - return
-

Welcome!

-
- {content} -
-
- Thanks for visiting. -
-
+ return <>{'children' in props ? props.children : 'No Children'} } diff --git a/tests/components/ct-react-vite/tests/children.spec.tsx b/tests/components/ct-react-vite/tests/children.spec.tsx index d671b14e64..03da7d2c55 100644 --- a/tests/components/ct-react-vite/tests/children.spec.tsx +++ b/tests/components/ct-react-vite/tests/children.spec.tsx @@ -60,7 +60,7 @@ test('render number as child', async ({ mount }) => { await expect(component).toContainText('1337'); }); -test('render without children', async ({ mount }) => { +test('absence of children when children prop is not provided', async ({ mount }) => { const component = await mount(); await expect(component).toContainText('No Children'); }); diff --git a/tests/components/ct-react-vite/tests/unmount.spec.tsx b/tests/components/ct-react-vite/tests/unmount.spec.tsx index acc11c4fdb..20374d8b8e 100644 --- a/tests/components/ct-react-vite/tests/unmount.spec.tsx +++ b/tests/components/ct-react-vite/tests/unmount.spec.tsx @@ -17,3 +17,9 @@ test('unmount a multi root component', async ({ mount, page }) => { await expect(page.locator('#root')).not.toContainText('root 1'); await expect(page.locator('#root')).not.toContainText('root 2'); }); + +test('unmount twice throws an error', async ({ mount }) => { + const component = await mount(`); + await page.locator('button').evaluate(button => { + window['clicks'] = 0; + button.addEventListener('click', () => ++window['clicks'], false); + }); + return page; + }; + + const clickInPage = async (page, count) => { + for (let i = 0; i < count; ++i) + await page.locator('button').click(); + }; + + const getClicks = async page => page.evaluate(() => window['clicks']); + + const page1 = await createPage(); + const page2 = await createPage(); + + const CLICK_COUNT = 20; + await Promise.all([ + clickInPage(page1, CLICK_COUNT), + clickInPage(page2, CLICK_COUNT), + ]); + expect(await getClicks(page1)).toBe(CLICK_COUNT); + expect(await getClicks(page2)).toBe(CLICK_COUNT); +}); + it('window.open should use parent tab context', async function({ browser, server }) { const context = await browser.newContext(); const page = await context.newPage(); diff --git a/tests/page/page-event-console.spec.ts b/tests/page/page-event-console.spec.ts index df611dd9dd..3b5f652fc9 100644 --- a/tests/page/page-event-console.spec.ts +++ b/tests/page/page-event-console.spec.ts @@ -38,7 +38,10 @@ it('should work @smoke', async ({ page, browserName }) => { it('should emit same log twice', async ({ page }) => { const messages = []; page.on('console', m => messages.push(m.text())); - await page.evaluate(() => { for (let i = 0; i < 2; ++i) console.log('hello'); }); + await page.evaluate(() => { + for (let i = 0; i < 2; ++i) + console.log('hello'); + }); expect(messages).toEqual(['hello', 'hello']); }); diff --git a/tests/page/page-screenshot.spec.ts b/tests/page/page-screenshot.spec.ts index 47188f52a4..278dd38228 100644 --- a/tests/page/page-screenshot.spec.ts +++ b/tests/page/page-screenshot.spec.ts @@ -723,7 +723,7 @@ it.describe('page screenshot animations', () => { el.addEventListener('transitionend', () => { const time = Date.now(); // Block main thread for 200ms, emulating heavy layout. - while (Date.now() - time < 200) ; + while (Date.now() - time < 200) {} const h1 = document.createElement('h1'); h1.textContent = 'woof-woof'; document.body.append(h1); diff --git a/tests/playwright-test/stable-test-runner/package-lock.json b/tests/playwright-test/stable-test-runner/package-lock.json index 2594cfcffd..f1937fd021 100644 --- a/tests/playwright-test/stable-test-runner/package-lock.json +++ b/tests/playwright-test/stable-test-runner/package-lock.json @@ -5,15 +5,15 @@ "packages": { "": { "dependencies": { - "@playwright/test": "1.41.0-beta-1705101589000" + "@playwright/test": "1.42.0-beta-1708994059000" } }, "node_modules/@playwright/test": { - "version": "1.41.0-beta-1705101589000", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.0-beta-1705101589000.tgz", - "integrity": "sha512-tpaEm+ih0PhJdk6yrRtk40I9ahErhGYZT2lR65ZZ6Il7tMnUgzxF5OOw3XLGJ59T29nMyI1+hKGqQnf8nUKkQw==", + "version": "1.42.0-beta-1708994059000", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.0-beta-1708994059000.tgz", + "integrity": "sha512-dfCvXGLsNRTOIRXyU7Cx8EdmMJxpHP3Dg2yD+ACM4GhpcSTJqLD0kwggZY2ziDJNG1sNJ1zN/aDgRqv4G3NREQ==", "dependencies": { - "playwright": "1.41.0-beta-1705101589000" + "playwright": "1.42.0-beta-1708994059000" }, "bin": { "playwright": "cli.js" @@ -36,11 +36,11 @@ } }, "node_modules/playwright": { - "version": "1.41.0-beta-1705101589000", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.0-beta-1705101589000.tgz", - "integrity": "sha512-3mMpZXmkw+fGIb+wBpvDZ4OEm7L1QYptgJgTdP8/OEitYjV5Q05MRZWbgelF/9ameoRpMz7rbkZTWUh/1UtrIw==", + "version": "1.42.0-beta-1708994059000", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.0-beta-1708994059000.tgz", + "integrity": "sha512-+lMRsLfaLx/Okb3qH9r8eUfDBQ7Vq0eTLehxG9plLorSBQnU1b9kQnmchV3jl2hjm9Oh7vNf/No+vgsRBuj7/Q==", "dependencies": { - "playwright-core": "1.41.0-beta-1705101589000" + "playwright-core": "1.42.0-beta-1708994059000" }, "bin": { "playwright": "cli.js" @@ -53,9 +53,9 @@ } }, "node_modules/playwright-core": { - "version": "1.41.0-beta-1705101589000", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.0-beta-1705101589000.tgz", - "integrity": "sha512-w3aDw2Kp/ZwAUSqLZdmd+mq6khl3ufb2csM51b85r9t8S4m2JYoz1WHFNGNHqFWHBXSrjSBXzFLNgKUmwizRuA==", + "version": "1.42.0-beta-1708994059000", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.0-beta-1708994059000.tgz", + "integrity": "sha512-mdXcpctceoX0aHouE+ZglhB7JnLO1NtQgTF8MBgL/Wsa3r6jRI8DESCVTmbcazr8QGs9MT9wTKSx8Y70zYkaxw==", "bin": { "playwright-core": "cli.js" }, @@ -66,11 +66,11 @@ }, "dependencies": { "@playwright/test": { - "version": "1.41.0-beta-1705101589000", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.0-beta-1705101589000.tgz", - "integrity": "sha512-tpaEm+ih0PhJdk6yrRtk40I9ahErhGYZT2lR65ZZ6Il7tMnUgzxF5OOw3XLGJ59T29nMyI1+hKGqQnf8nUKkQw==", + "version": "1.42.0-beta-1708994059000", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.0-beta-1708994059000.tgz", + "integrity": "sha512-dfCvXGLsNRTOIRXyU7Cx8EdmMJxpHP3Dg2yD+ACM4GhpcSTJqLD0kwggZY2ziDJNG1sNJ1zN/aDgRqv4G3NREQ==", "requires": { - "playwright": "1.41.0-beta-1705101589000" + "playwright": "1.42.0-beta-1708994059000" } }, "fsevents": { @@ -80,18 +80,18 @@ "optional": true }, "playwright": { - "version": "1.41.0-beta-1705101589000", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.0-beta-1705101589000.tgz", - "integrity": "sha512-3mMpZXmkw+fGIb+wBpvDZ4OEm7L1QYptgJgTdP8/OEitYjV5Q05MRZWbgelF/9ameoRpMz7rbkZTWUh/1UtrIw==", + "version": "1.42.0-beta-1708994059000", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.0-beta-1708994059000.tgz", + "integrity": "sha512-+lMRsLfaLx/Okb3qH9r8eUfDBQ7Vq0eTLehxG9plLorSBQnU1b9kQnmchV3jl2hjm9Oh7vNf/No+vgsRBuj7/Q==", "requires": { "fsevents": "2.3.2", - "playwright-core": "1.41.0-beta-1705101589000" + "playwright-core": "1.42.0-beta-1708994059000" } }, "playwright-core": { - "version": "1.41.0-beta-1705101589000", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.0-beta-1705101589000.tgz", - "integrity": "sha512-w3aDw2Kp/ZwAUSqLZdmd+mq6khl3ufb2csM51b85r9t8S4m2JYoz1WHFNGNHqFWHBXSrjSBXzFLNgKUmwizRuA==" + "version": "1.42.0-beta-1708994059000", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.0-beta-1708994059000.tgz", + "integrity": "sha512-mdXcpctceoX0aHouE+ZglhB7JnLO1NtQgTF8MBgL/Wsa3r6jRI8DESCVTmbcazr8QGs9MT9wTKSx8Y70zYkaxw==" } } } diff --git a/tests/playwright-test/stable-test-runner/package.json b/tests/playwright-test/stable-test-runner/package.json index 17ce2477d8..363a340c04 100644 --- a/tests/playwright-test/stable-test-runner/package.json +++ b/tests/playwright-test/stable-test-runner/package.json @@ -1,6 +1,6 @@ { "private": true, "dependencies": { - "@playwright/test": "1.41.0-beta-1705101589000" + "@playwright/test": "1.42.0-beta-1708994059000" } } diff --git a/tests/playwright-test/web-server.spec.ts b/tests/playwright-test/web-server.spec.ts index 0cadbddad8..d2eacb2d55 100644 --- a/tests/playwright-test/web-server.spec.ts +++ b/tests/playwright-test/web-server.spec.ts @@ -438,7 +438,8 @@ test(`should support self signed certificate`, async ({ runInlineTest, httpsServ test('should send Accept header', async ({ runInlineTest, server }) => { let acceptHeader: string | undefined | null = null; server.setRoute('/hello', (req, res) => { - if (acceptHeader === null) acceptHeader = req.headers.accept; + if (acceptHeader === null) + acceptHeader = req.headers.accept; res.end('hello'); }); const result = await runInlineTest({ diff --git a/tsconfig.json b/tsconfig.json index 733973760a..56d9996996 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,11 +35,5 @@ "include": ["packages"], "exclude": [ "packages/*/lib", - "packages/playwright-ct-react", - "packages/playwright-ct-react17", - "packages/playwright-ct-solid", - "packages/playwright-ct-svelte", - "packages/playwright-ct-vue", - "packages/playwright-ct-vue2" ], } diff --git a/utils/doclint/linting-code-snippets/cli.js b/utils/doclint/linting-code-snippets/cli.js index 42e78fdcab..8bde076b7c 100644 --- a/utils/doclint/linting-code-snippets/cli.js +++ b/utils/doclint/linting-code-snippets/cli.js @@ -180,7 +180,10 @@ class JSLintingService extends LintingService { * @returns {Promise} */ async lint(snippets) { - return Promise.all(snippets.map(async snippet => this._lintSnippet(snippet))); + const result = []; + for (let i = 0; i < snippets.length; ++i) + result.push(await this._lintSnippet(snippets[i])); + return result; } }