Compare commits

...

4 commits

Author SHA1 Message Date
Playwright Service e7f0635c17
cherry-pick(#29692): docs: better addLocatorHandler example in release notes (#29697)
This PR cherry-picks the following commits:

- 321e9d72c3
2024-02-27 11:05:24 -08:00
Playwright Service 8709a3a24b
cherry-pick(#29687): chore: fix docs roll for functions without args follow-up (#29688)
This PR cherry-picks the following commits:

- 03902d8e85
- 1a43adc506

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-02-27 16:52:29 +01:00
Yury Semikhatsky aa9f6fb718
cherry-pick(#29669): chore: strengthen linting (#29674) 2024-02-26 17:43:55 -08:00
Yury Semikhatsky f5899c1556
chore: set version to 1.42.0 (#29671) 2024-02-26 17:40:03 -08:00
41 changed files with 183 additions and 133 deletions

View file

@ -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"
},
};

View file

@ -4,7 +4,6 @@ module.exports = {
parserOptions: { parserOptions: {
ecmaVersion: 9, ecmaVersion: 9,
sourceType: "module", sourceType: "module",
project: "./tsconfig.json",
}, },
extends: [ extends: [
"plugin:react-hooks/recommended" "plugin:react-hooks/recommended"
@ -49,6 +48,7 @@ module.exports = {
"arrow-parens": [2, "as-needed"], "arrow-parens": [2, "as-needed"],
"prefer-const": 2, "prefer-const": 2,
"quote-props": [2, "consistent"], "quote-props": [2, "consistent"],
"nonblock-statement-body-position": [2, "below"],
// anti-patterns // anti-patterns
"no-var": 2, "no-var": 2,

View file

@ -13,13 +13,15 @@ import LiteYouTube from '@site/src/components/LiteYouTube';
- 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: - 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 ```js
// Setup the handler. // Setup the handler.
await page.addLocatorHandler(page.getByRole('button', { name: 'Accept all cookies' }), async () => { await page.addLocatorHandler(
await page.getByRole('button', { name: 'Reject all cookies' }).click(); page.getByRole('heading', { name: 'Hej! You are in control of your cookies.' }),
async () => {
await page.getByRole('button', { name: 'Accept all' }).click();
}); });
// Write the test as usual. // Write the test as usual.
await page.goto('https://example.com'); await page.goto('https://www.ikea.com/');
await page.getByRole('button', { name: 'Start here' }).click(); await page.getByRole('link', { name: 'Collection of blue and white' }).click();
await expect(page.getByRole('heading', { name: 'Light and easy' })).toBeVisible();
``` ```
- `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) - `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)

View file

@ -137,10 +137,12 @@ self.addEventListener('fetch', event => {
(async () => { (async () => {
// 1. Try to first serve directly from caches // 1. Try to first serve directly from caches
const response = await caches.match(event.request); const response = await caches.match(event.request);
if (response) return response; if (response)
return response;
// 2. Re-write request for /foo to /bar // 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 // 3. Prevent tracker.js from being retrieved, and returns a placeholder response
if (event.request.url.endsWith('tracker.js')) { if (event.request.url.endsWith('tracker.js')) {

68
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "playwright-internal", "name": "playwright-internal",
"version": "1.42.0-next", "version": "1.42.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "playwright-internal", "name": "playwright-internal",
"version": "1.42.0-next", "version": "1.42.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
@ -8136,10 +8136,10 @@
} }
}, },
"packages/playwright": { "packages/playwright": {
"version": "1.42.0-next", "version": "1.42.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next" "playwright-core": "1.42.0"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -8153,11 +8153,11 @@
}, },
"packages/playwright-browser-chromium": { "packages/playwright-browser-chromium": {
"name": "@playwright/browser-chromium", "name": "@playwright/browser-chromium",
"version": "1.42.0-next", "version": "1.42.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next" "playwright-core": "1.42.0"
}, },
"engines": { "engines": {
"node": ">=16" "node": ">=16"
@ -8165,11 +8165,11 @@
}, },
"packages/playwright-browser-firefox": { "packages/playwright-browser-firefox": {
"name": "@playwright/browser-firefox", "name": "@playwright/browser-firefox",
"version": "1.42.0-next", "version": "1.42.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next" "playwright-core": "1.42.0"
}, },
"engines": { "engines": {
"node": ">=16" "node": ">=16"
@ -8177,22 +8177,22 @@
}, },
"packages/playwright-browser-webkit": { "packages/playwright-browser-webkit": {
"name": "@playwright/browser-webkit", "name": "@playwright/browser-webkit",
"version": "1.42.0-next", "version": "1.42.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next" "playwright-core": "1.42.0"
}, },
"engines": { "engines": {
"node": ">=16" "node": ">=16"
} }
}, },
"packages/playwright-chromium": { "packages/playwright-chromium": {
"version": "1.42.0-next", "version": "1.42.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next" "playwright-core": "1.42.0"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -8202,7 +8202,7 @@
} }
}, },
"packages/playwright-core": { "packages/playwright-core": {
"version": "1.42.0-next", "version": "1.42.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
"playwright-core": "cli.js" "playwright-core": "cli.js"
@ -8213,11 +8213,11 @@
}, },
"packages/playwright-ct-core": { "packages/playwright-ct-core": {
"name": "@playwright/experimental-ct-core", "name": "@playwright/experimental-ct-core",
"version": "1.42.0-next", "version": "1.42.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright": "1.42.0-next", "playwright": "1.42.0",
"playwright-core": "1.42.0-next", "playwright-core": "1.42.0",
"vite": "^5.0.12" "vite": "^5.0.12"
}, },
"bin": { "bin": {
@ -8229,10 +8229,10 @@
}, },
"packages/playwright-ct-react": { "packages/playwright-ct-react": {
"name": "@playwright/experimental-ct-react", "name": "@playwright/experimental-ct-react",
"version": "1.42.0-next", "version": "1.42.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.42.0-next", "@playwright/experimental-ct-core": "1.42.0",
"@vitejs/plugin-react": "^4.2.1" "@vitejs/plugin-react": "^4.2.1"
}, },
"bin": { "bin": {
@ -8245,10 +8245,10 @@
}, },
"packages/playwright-ct-react17": { "packages/playwright-ct-react17": {
"name": "@playwright/experimental-ct-react17", "name": "@playwright/experimental-ct-react17",
"version": "1.42.0-next", "version": "1.42.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.42.0-next", "@playwright/experimental-ct-core": "1.42.0",
"@vitejs/plugin-react": "^4.2.1" "@vitejs/plugin-react": "^4.2.1"
}, },
"bin": { "bin": {
@ -8261,10 +8261,10 @@
}, },
"packages/playwright-ct-solid": { "packages/playwright-ct-solid": {
"name": "@playwright/experimental-ct-solid", "name": "@playwright/experimental-ct-solid",
"version": "1.42.0-next", "version": "1.42.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.42.0-next", "@playwright/experimental-ct-core": "1.42.0",
"vite-plugin-solid": "^2.7.0" "vite-plugin-solid": "^2.7.0"
}, },
"bin": { "bin": {
@ -8280,10 +8280,10 @@
}, },
"packages/playwright-ct-svelte": { "packages/playwright-ct-svelte": {
"name": "@playwright/experimental-ct-svelte", "name": "@playwright/experimental-ct-svelte",
"version": "1.42.0-next", "version": "1.42.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.42.0-next", "@playwright/experimental-ct-core": "1.42.0",
"@sveltejs/vite-plugin-svelte": "^3.0.1" "@sveltejs/vite-plugin-svelte": "^3.0.1"
}, },
"bin": { "bin": {
@ -8299,10 +8299,10 @@
}, },
"packages/playwright-ct-vue": { "packages/playwright-ct-vue": {
"name": "@playwright/experimental-ct-vue", "name": "@playwright/experimental-ct-vue",
"version": "1.42.0-next", "version": "1.42.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.42.0-next", "@playwright/experimental-ct-core": "1.42.0",
"@vitejs/plugin-vue": "^4.2.1" "@vitejs/plugin-vue": "^4.2.1"
}, },
"bin": { "bin": {
@ -8315,10 +8315,10 @@
}, },
"packages/playwright-ct-vue2": { "packages/playwright-ct-vue2": {
"name": "@playwright/experimental-ct-vue2", "name": "@playwright/experimental-ct-vue2",
"version": "1.42.0-next", "version": "1.42.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.42.0-next", "@playwright/experimental-ct-core": "1.42.0",
"@vitejs/plugin-vue2": "^2.2.0" "@vitejs/plugin-vue2": "^2.2.0"
}, },
"bin": { "bin": {
@ -8368,11 +8368,11 @@
} }
}, },
"packages/playwright-firefox": { "packages/playwright-firefox": {
"version": "1.42.0-next", "version": "1.42.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next" "playwright-core": "1.42.0"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -8383,10 +8383,10 @@
}, },
"packages/playwright-test": { "packages/playwright-test": {
"name": "@playwright/test", "name": "@playwright/test",
"version": "1.42.0-next", "version": "1.42.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright": "1.42.0-next" "playwright": "1.42.0"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -8396,11 +8396,11 @@
} }
}, },
"packages/playwright-webkit": { "packages/playwright-webkit": {
"version": "1.42.0-next", "version": "1.42.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next" "playwright-core": "1.42.0"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"

View file

@ -1,7 +1,7 @@
{ {
"name": "playwright-internal", "name": "playwright-internal",
"private": true, "private": true,
"version": "1.42.0-next", "version": "1.42.0",
"description": "A high-level API to automate web browsers", "description": "A high-level API to automate web browsers",
"repository": { "repository": {
"type": "git", "type": "git",

View file

@ -1,9 +0,0 @@
module.exports = {
extends: ".eslintrc.js",
rules: {
"@typescript-eslint/no-base-to-string": "error",
},
parserOptions: {
project: "./tsconfig.json"
},
};

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/browser-chromium", "name": "@playwright/browser-chromium",
"version": "1.42.0-next", "version": "1.42.0",
"description": "Playwright package that automatically installs Chromium", "description": "Playwright package that automatically installs Chromium",
"repository": { "repository": {
"type": "git", "type": "git",
@ -27,6 +27,6 @@
"install": "node install.js" "install": "node install.js"
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next" "playwright-core": "1.42.0"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/browser-firefox", "name": "@playwright/browser-firefox",
"version": "1.42.0-next", "version": "1.42.0",
"description": "Playwright package that automatically installs Firefox", "description": "Playwright package that automatically installs Firefox",
"repository": { "repository": {
"type": "git", "type": "git",
@ -27,6 +27,6 @@
"install": "node install.js" "install": "node install.js"
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next" "playwright-core": "1.42.0"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/browser-webkit", "name": "@playwright/browser-webkit",
"version": "1.42.0-next", "version": "1.42.0",
"description": "Playwright package that automatically installs WebKit", "description": "Playwright package that automatically installs WebKit",
"repository": { "repository": {
"type": "git", "type": "git",
@ -27,6 +27,6 @@
"install": "node install.js" "install": "node install.js"
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next" "playwright-core": "1.42.0"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "playwright-chromium", "name": "playwright-chromium",
"version": "1.42.0-next", "version": "1.42.0",
"description": "A high-level API to automate Chromium", "description": "A high-level API to automate Chromium",
"repository": { "repository": {
"type": "git", "type": "git",
@ -30,6 +30,6 @@
"install": "node install.js" "install": "node install.js"
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next" "playwright-core": "1.42.0"
} }
} }

View file

@ -1,3 +1,3 @@
module.exports = { module.exports = {
extends: "../.eslintrc-with-ts-config.js", extends: "../../.eslintrc-with-ts-config.js",
}; };

View file

@ -1,6 +1,6 @@
{ {
"name": "playwright-core", "name": "playwright-core",
"version": "1.42.0-next", "version": "1.42.0",
"description": "A high-level API to automate web browsers", "description": "A high-level API to automate web browsers",
"repository": { "repository": {
"type": "git", "type": "git",

View file

@ -526,7 +526,7 @@ export async function prepareBrowserContextParams(options: BrowserContextOptions
function toAcceptDownloadsProtocol(acceptDownloads?: boolean) { function toAcceptDownloadsProtocol(acceptDownloads?: boolean) {
if (acceptDownloads === undefined) if (acceptDownloads === undefined)
return undefined; return undefined;
if (acceptDownloads === true) if (acceptDownloads)
return 'accept'; return 'accept';
return 'deny'; return 'deny';
} }

View file

@ -594,7 +594,8 @@ export class CRBrowserContext extends BrowserContext {
targetId = (page._delegate as CRPage)._targetId; targetId = (page._delegate as CRPage)._targetId;
} else if (page instanceof Frame) { } else if (page instanceof Frame) {
const session = (page._page._delegate as CRPage)._sessions.get(page._id); 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; targetId = session._targetId;
} else { } else {
throw new Error('page: expected Page or Frame'); throw new Error('page: expected Page or Frame');

View file

@ -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 // @see https://github.com/vuejs/devtools/blob/e7132f3392b975e39e1d9a23cf30456c270099c2/packages/app-backend-vue3/src/components/util.ts#L29
function getInstanceName(instance: VueVNode): string { function getInstanceName(instance: VueVNode): string {
const name = getComponentTypeName(instance.type || {}); const name = getComponentTypeName(instance.type || {});
if (name) return name; if (name)
if (instance.root === instance) return 'Root'; return name;
for (const key in instance.parent?.type?.components) if (instance.root === instance)
if (instance.parent?.type.components[key] === instance.type) return saveComponentName(instance, key); return 'Root';
for (const key in instance.appContext?.components) for (const key in instance.parent?.type?.components) {
if (instance.appContext.components[key] === instance.type) return saveComponentName(instance, key); 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'; 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 // @see https://github.com/vuejs/devtools/blob/e7132f3392b975e39e1d9a23cf30456c270099c2/packages/app-backend-vue3/src/components/el.ts#L15
function getFragmentRootElements(vnode: any): Element[] { function getFragmentRootElements(vnode: any): Element[] {
if (!vnode.children) return []; if (!vnode.children)
return [];
const list = []; const list = [];

View file

@ -48,8 +48,10 @@ function preprocess(str: string): number[] {
if (code === 0xd && str.charCodeAt(i + 1) === 0xa) { if (code === 0xd && str.charCodeAt(i + 1) === 0xa) {
code = 0xa; i++; code = 0xa; i++;
} }
if (code === 0xd || code === 0xc) code = 0xa; if (code === 0xd || code === 0xc)
if (code === 0x0) code = 0xfffd; code = 0xa;
if (code === 0x0)
code = 0xfffd;
if (between(code, 0xd800, 0xdbff) && between(str.charCodeAt(i + 1), 0xdc00, 0xdfff)) { if (between(code, 0xd800, 0xdbff) && between(str.charCodeAt(i + 1), 0xdc00, 0xdfff)) {
// Decode a surrogate pair into an astral codepoint. // Decode a surrogate pair into an astral codepoint.
const lead = code - 0xd800; const lead = code - 0xd800;
@ -63,7 +65,8 @@ function preprocess(str: string): number[] {
} }
function stringFromCode(code: 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. // Otherwise, encode astral char as surrogate pair.
code -= Math.pow(2, 16); code -= Math.pow(2, 16);
const lead = Math.floor(code / Math.pow(2, 10)) + 0xd800; const lead = Math.floor(code / Math.pow(2, 10)) + 0xd800;
@ -107,8 +110,10 @@ export function tokenize(str1: string): CSSTokenInterface[] {
num = 1; num = 1;
i += num; i += num;
code = codepoint(i); code = codepoint(i);
if (newline(code)) incrLineno(); if (newline(code))
else column += num; incrLineno();
else
column += num;
// console.log('Consume '+i+' '+String.fromCharCode(code) + ' 0x' + code.toString(16)); // console.log('Consume '+i+' '+String.fromCharCode(code) + ' 0x' + code.toString(16));
return true; return true;
}; };
@ -125,7 +130,8 @@ export function tokenize(str1: string): CSSTokenInterface[] {
return true; return true;
}; };
const eof = function(codepoint?: number): boolean { const eof = function(codepoint?: number): boolean {
if (codepoint === undefined) codepoint = code; if (codepoint === undefined)
codepoint = code;
return codepoint === -1; return codepoint === -1;
}; };
const donothing = function() { }; const donothing = function() { };
@ -138,12 +144,14 @@ export function tokenize(str1: string): CSSTokenInterface[] {
consumeComments(); consumeComments();
consume(); consume();
if (whitespace(code)) { if (whitespace(code)) {
while (whitespace(next())) consume(); while (whitespace(next()))
consume();
return new WhitespaceToken(); return new WhitespaceToken();
} else if (code === 0x22) {return consumeAStringToken();} else if (code === 0x23) { } else if (code === 0x22) {return consumeAStringToken();} else if (code === 0x23) {
if (namechar(next()) || areAValidEscape(next(1), next(2))) { if (namechar(next()) || areAValidEscape(next(1), next(2))) {
const token = new HashToken(''); 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(); token.value = consumeAName();
return token; return token;
} else { } else {
@ -288,7 +296,8 @@ export function tokenize(str1: string): CSSTokenInterface[] {
const str = consumeAName(); const str = consumeAName();
if (str.toLowerCase() === 'url' && next() === 0x28) { if (str.toLowerCase() === 'url' && next() === 0x28) {
consume(); consume();
while (whitespace(next(1)) && whitespace(next(2))) consume(); while (whitespace(next(1)) && whitespace(next(2)))
consume();
if (next() === 0x22 || next() === 0x27) if (next() === 0x22 || next() === 0x27)
return new FunctionToken(str); return new FunctionToken(str);
else if (whitespace(next()) && (next(2) === 0x22 || next(2) === 0x27)) 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 { const consumeAStringToken = function(endingCodePoint?: number): CSSParserToken {
if (endingCodePoint === undefined) endingCodePoint = code; if (endingCodePoint === undefined)
endingCodePoint = code;
let string = ''; let string = '';
while (consume()) { while (consume()) {
if (code === endingCodePoint || eof()) { if (code === endingCodePoint || eof()) {
@ -331,13 +341,16 @@ export function tokenize(str1: string): CSSTokenInterface[] {
const consumeAURLToken = function(): CSSTokenInterface { const consumeAURLToken = function(): CSSTokenInterface {
const token = new URLToken(''); const token = new URLToken('');
while (whitespace(next())) consume(); while (whitespace(next()))
if (eof(next())) return token; consume();
if (eof(next()))
return token;
while (consume()) { while (consume()) {
if (code === 0x29 || eof()) { if (code === 0x29 || eof()) {
return token; return token;
} else if (whitespace(code)) { } else if (whitespace(code)) {
while (whitespace(next())) consume(); while (whitespace(next()))
consume();
if (next() === 0x29 || eof(next())) { if (next() === 0x29 || eof(next())) {
consume(); consume();
return token; return token;
@ -379,9 +392,11 @@ export function tokenize(str1: string): CSSTokenInterface[] {
break; break;
} }
} }
if (whitespace(next())) consume(); if (whitespace(next()))
consume();
let value = parseInt(digits.map(function(x) { return String.fromCharCode(x); }).join(''), 16); 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; return value;
} else if (eof()) { } else if (eof()) {
return 0xfffd; return 0xfffd;
@ -391,8 +406,10 @@ export function tokenize(str1: string): CSSTokenInterface[] {
}; };
const areAValidEscape = function(c1: number, c2: number) { const areAValidEscape = function(c1: number, c2: number) {
if (c1 !== 0x5c) return false; if (c1 !== 0x5c)
if (newline(c2)) return false; return false;
if (newline(c2))
return false;
return true; return true;
}; };
const startsWithAValidEscape = function() { const startsWithAValidEscape = function() {
@ -416,11 +433,14 @@ export function tokenize(str1: string): CSSTokenInterface[] {
const wouldStartANumber = function(c1: number, c2: number, c3: number) { const wouldStartANumber = function(c1: number, c2: number, c3: number) {
if (c1 === 0x2b || c1 === 0x2d) { if (c1 === 0x2b || c1 === 0x2d) {
if (digit(c2)) return true; if (digit(c2))
if (c2 === 0x2e && digit(c3)) return true; return true;
if (c2 === 0x2e && digit(c3))
return true;
return false; return false;
} else if (c1 === 0x2e) { } else if (c1 === 0x2e) {
if (digit(c2)) return true; if (digit(c2))
return true;
return false; return false;
} else if (digit(c1)) { } else if (digit(c1)) {
return true; return true;
@ -519,7 +539,8 @@ export function tokenize(str1: string): CSSTokenInterface[] {
while (!eof(next())) { while (!eof(next())) {
tokens.push(consumeAToken()); tokens.push(consumeAToken());
iterationCount++; 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; return tokens;
} }

View file

@ -1,3 +1,3 @@
module.exports = { module.exports = {
extends: "../.eslintrc-with-ts-config.js", extends: "../../.eslintrc-with-ts-config.js",
}; };

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/experimental-ct-core", "name": "@playwright/experimental-ct-core",
"version": "1.42.0-next", "version": "1.42.0",
"description": "Playwright Component Testing Helpers", "description": "Playwright Component Testing Helpers",
"repository": { "repository": {
"type": "git", "type": "git",
@ -26,9 +26,9 @@
} }
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next", "playwright-core": "1.42.0",
"vite": "^5.0.12", "vite": "^5.0.12",
"playwright": "1.42.0-next" "playwright": "1.42.0"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/experimental-ct-react", "name": "@playwright/experimental-ct-react",
"version": "1.42.0-next", "version": "1.42.0",
"description": "Playwright Component Testing for React", "description": "Playwright Component Testing for React",
"repository": { "repository": {
"type": "git", "type": "git",
@ -29,7 +29,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.42.0-next", "@playwright/experimental-ct-core": "1.42.0",
"@vitejs/plugin-react": "^4.2.1" "@vitejs/plugin-react": "^4.2.1"
}, },
"bin": { "bin": {

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/experimental-ct-react17", "name": "@playwright/experimental-ct-react17",
"version": "1.42.0-next", "version": "1.42.0",
"description": "Playwright Component Testing for React", "description": "Playwright Component Testing for React",
"repository": { "repository": {
"type": "git", "type": "git",
@ -29,7 +29,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.42.0-next", "@playwright/experimental-ct-core": "1.42.0",
"@vitejs/plugin-react": "^4.2.1" "@vitejs/plugin-react": "^4.2.1"
}, },
"bin": { "bin": {

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/experimental-ct-solid", "name": "@playwright/experimental-ct-solid",
"version": "1.42.0-next", "version": "1.42.0",
"description": "Playwright Component Testing for Solid", "description": "Playwright Component Testing for Solid",
"repository": { "repository": {
"type": "git", "type": "git",
@ -29,7 +29,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.42.0-next", "@playwright/experimental-ct-core": "1.42.0",
"vite-plugin-solid": "^2.7.0" "vite-plugin-solid": "^2.7.0"
}, },
"devDependencies": { "devDependencies": {

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/experimental-ct-svelte", "name": "@playwright/experimental-ct-svelte",
"version": "1.42.0-next", "version": "1.42.0",
"description": "Playwright Component Testing for Svelte", "description": "Playwright Component Testing for Svelte",
"repository": { "repository": {
"type": "git", "type": "git",
@ -29,7 +29,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.42.0-next", "@playwright/experimental-ct-core": "1.42.0",
"@sveltejs/vite-plugin-svelte": "^3.0.1" "@sveltejs/vite-plugin-svelte": "^3.0.1"
}, },
"devDependencies": { "devDependencies": {

View file

@ -55,7 +55,8 @@ function __pwCreateSlots(slots) {
__pwInsert(target, element, anchor); __pwInsert(target, element, anchor);
}, },
d: function destroy(detaching) { d: function destroy(detaching) {
if (detaching) __pwDetach(element); if (detaching)
__pwDetach(element);
}, },
l: __pwNoop, l: __pwNoop,
}; };

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/experimental-ct-vue", "name": "@playwright/experimental-ct-vue",
"version": "1.42.0-next", "version": "1.42.0",
"description": "Playwright Component Testing for Vue", "description": "Playwright Component Testing for Vue",
"repository": { "repository": {
"type": "git", "type": "git",
@ -29,7 +29,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.42.0-next", "@playwright/experimental-ct-core": "1.42.0",
"@vitejs/plugin-vue": "^4.2.1" "@vitejs/plugin-vue": "^4.2.1"
}, },
"bin": { "bin": {

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/experimental-ct-vue2", "name": "@playwright/experimental-ct-vue2",
"version": "1.42.0-next", "version": "1.42.0",
"description": "Playwright Component Testing for Vue2", "description": "Playwright Component Testing for Vue2",
"repository": { "repository": {
"type": "git", "type": "git",
@ -29,7 +29,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.42.0-next", "@playwright/experimental-ct-core": "1.42.0",
"@vitejs/plugin-vue2": "^2.2.0" "@vitejs/plugin-vue2": "^2.2.0"
}, },
"devDependencies": { "devDependencies": {

View file

@ -1,6 +1,6 @@
{ {
"name": "playwright-firefox", "name": "playwright-firefox",
"version": "1.42.0-next", "version": "1.42.0",
"description": "A high-level API to automate Firefox", "description": "A high-level API to automate Firefox",
"repository": { "repository": {
"type": "git", "type": "git",
@ -30,6 +30,6 @@
"install": "node install.js" "install": "node install.js"
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next" "playwright-core": "1.42.0"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/test", "name": "@playwright/test",
"version": "1.42.0-next", "version": "1.42.0",
"description": "A high-level API to automate web browsers", "description": "A high-level API to automate web browsers",
"repository": { "repository": {
"type": "git", "type": "git",
@ -30,6 +30,6 @@
}, },
"scripts": {}, "scripts": {},
"dependencies": { "dependencies": {
"playwright": "1.42.0-next" "playwright": "1.42.0"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "playwright-webkit", "name": "playwright-webkit",
"version": "1.42.0-next", "version": "1.42.0",
"description": "A high-level API to automate WebKit", "description": "A high-level API to automate WebKit",
"repository": { "repository": {
"type": "git", "type": "git",
@ -30,6 +30,6 @@
"install": "node install.js" "install": "node install.js"
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next" "playwright-core": "1.42.0"
} }
} }

View file

@ -1,5 +1,5 @@
module.exports = { module.exports = {
extends: '../.eslintrc.js', extends: '../../.eslintrc-with-ts-config.js',
rules: { rules: {
'@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/no-floating-promises': 'error',
}, },

View file

@ -1,6 +1,6 @@
{ {
"name": "playwright", "name": "playwright",
"version": "1.42.0-next", "version": "1.42.0",
"description": "A high-level API to automate web browsers", "description": "A high-level API to automate web browsers",
"repository": { "repository": {
"type": "git", "type": "git",
@ -58,7 +58,7 @@
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.42.0-next" "playwright-core": "1.42.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"fsevents": "2.3.2" "fsevents": "2.3.2"

View file

@ -25,7 +25,7 @@ export function matcherHint(state: ExpectMatcherContext, locator: Locator | unde
if (timeout) if (timeout)
header = colors.red(`Timed out ${timeout}ms waiting for `) + header; header = colors.red(`Timed out ${timeout}ms waiting for `) + header;
if (locator) if (locator)
header += `Locator: ${locator}\n`; header += `Locator: ${String(locator)}\n`;
return header; return header;
} }

View file

@ -40,7 +40,7 @@ export function toBeAttached(
locator: LocatorEx, locator: LocatorEx,
options?: { attached?: boolean, timeout?: number }, 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 expected = attached ? 'attached' : 'detached';
const unexpected = attached ? 'detached' : 'attached'; const unexpected = attached ? 'detached' : 'attached';
const arg = attached ? '' : '{ attached: false }'; const arg = attached ? '' : '{ attached: false }';
@ -54,7 +54,7 @@ export function toBeChecked(
locator: LocatorEx, locator: LocatorEx,
options?: { checked?: boolean, timeout?: number }, 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 expected = checked ? 'checked' : 'unchecked';
const unexpected = checked ? 'unchecked' : 'checked'; const unexpected = checked ? 'unchecked' : 'checked';
const arg = checked ? '' : '{ checked: false }'; const arg = checked ? '' : '{ checked: false }';
@ -78,7 +78,7 @@ export function toBeEditable(
locator: LocatorEx, locator: LocatorEx,
options?: { editable?: boolean, timeout?: number }, 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 expected = editable ? 'editable' : 'readOnly';
const unexpected = editable ? 'readOnly' : 'editable'; const unexpected = editable ? 'readOnly' : 'editable';
const arg = editable ? '' : '{ editable: false }'; const arg = editable ? '' : '{ editable: false }';
@ -102,7 +102,7 @@ export function toBeEnabled(
locator: LocatorEx, locator: LocatorEx,
options?: { enabled?: boolean, timeout?: number }, 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 expected = enabled ? 'enabled' : 'disabled';
const unexpected = enabled ? 'disabled' : 'enabled'; const unexpected = enabled ? 'disabled' : 'enabled';
const arg = enabled ? '' : '{ enabled: false }'; const arg = enabled ? '' : '{ enabled: false }';
@ -136,7 +136,7 @@ export function toBeVisible(
locator: LocatorEx, locator: LocatorEx,
options?: { visible?: boolean, timeout?: number }, 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 expected = visible ? 'visible' : 'hidden';
const unexpected = visible ? 'hidden' : 'visible'; const unexpected = visible ? 'hidden' : 'visible';
const arg = visible ? '' : '{ visible: false }'; const arg = visible ? '' : '{ visible: false }';

View file

@ -43,7 +43,7 @@ export async function runTestServer() {
onConnection(request: http.IncomingMessage, url: URL, ws: WebSocket, id: string) { onConnection(request: http.IncomingMessage, url: URL, ws: WebSocket, id: string) {
const dispatcher = new Dispatcher(ws); const dispatcher = new Dispatcher(ws);
ws.on('message', async message => { ws.on('message', async message => {
const { id, method, params } = JSON.parse(message.toString()); const { id, method, params } = JSON.parse(String(message));
try { try {
const result = await (dispatcher as any)[method](params); const result = await (dispatcher as any)[method](params);
ws.send(JSON.stringify({ id, result })); ws.send(JSON.stringify({ id, result }));

View file

@ -208,7 +208,8 @@ export function addSuffixToFilePath(filePath: string, suffix: string, customExte
*/ */
export function getContainedPath(parentPath: string, subPath: string = ''): string | null { export function getContainedPath(parentPath: string, subPath: string = ''): string | null {
const resolvedPath = path.resolve(parentPath, subPath); 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; return null;
} }

View file

@ -11,5 +11,6 @@ module.exports = {
}, },
rules: { rules: {
'@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/no-floating-promises': 'error',
"@typescript-eslint/no-unnecessary-boolean-literal-compare": 2,
}, },
}; };

View file

@ -38,7 +38,10 @@ it('should work @smoke', async ({ page, browserName }) => {
it('should emit same log twice', async ({ page }) => { it('should emit same log twice', async ({ page }) => {
const messages = []; const messages = [];
page.on('console', m => messages.push(m.text())); 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']); expect(messages).toEqual(['hello', 'hello']);
}); });

View file

@ -723,7 +723,7 @@ it.describe('page screenshot animations', () => {
el.addEventListener('transitionend', () => { el.addEventListener('transitionend', () => {
const time = Date.now(); const time = Date.now();
// Block main thread for 200ms, emulating heavy layout. // Block main thread for 200ms, emulating heavy layout.
while (Date.now() - time < 200) ; while (Date.now() - time < 200) {}
const h1 = document.createElement('h1'); const h1 = document.createElement('h1');
h1.textContent = 'woof-woof'; h1.textContent = 'woof-woof';
document.body.append(h1); document.body.append(h1);

View file

@ -438,7 +438,8 @@ test(`should support self signed certificate`, async ({ runInlineTest, httpsServ
test('should send Accept header', async ({ runInlineTest, server }) => { test('should send Accept header', async ({ runInlineTest, server }) => {
let acceptHeader: string | undefined | null = null; let acceptHeader: string | undefined | null = null;
server.setRoute('/hello', (req, res) => { server.setRoute('/hello', (req, res) => {
if (acceptHeader === null) acceptHeader = req.headers.accept; if (acceptHeader === null)
acceptHeader = req.headers.accept;
res.end('<html><body>hello</body></html>'); res.end('<html><body>hello</body></html>');
}); });
const result = await runInlineTest({ const result = await runInlineTest({

View file

@ -566,7 +566,7 @@ class Type {
return type; return type;
} }
if (parsedType.args) { if (parsedType.args || parsedType.retType) {
const type = new Type('function'); const type = new Type('function');
type.args = []; type.args = [];
// @ts-ignore // @ts-ignore
@ -737,7 +737,8 @@ function parseTypeExpression(type) {
if (type[i] === '(') { if (type[i] === '(') {
name = type.substring(0, i); name = type.substring(0, i);
const matching = matchingBracket(type.substring(i), '(', ')'); const matching = matchingBracket(type.substring(i), '(', ')');
args = parseTypeExpression(type.substring(i + 1, i + matching - 1)); const argsString = type.substring(i + 1, i + matching - 1);
args = argsString ? parseTypeExpression(argsString) : null;
i = i + matching; i = i + matching;
if (type[i] === ':') { if (type[i] === ':') {
retType = parseTypeExpression(type.substring(i + 1)); retType = parseTypeExpression(type.substring(i + 1));

View file

@ -180,7 +180,10 @@ class JSLintingService extends LintingService {
* @returns {Promise<LintResult[]>} * @returns {Promise<LintResult[]>}
*/ */
async lint(snippets) { 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;
} }
} }