Compare commits
10 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4aee014caf | ||
|
|
bf25a93617 | ||
|
|
7effaf4f22 | ||
|
|
ef18287266 | ||
|
|
552cba8c05 | ||
|
|
cd94a3f01d | ||
|
|
3543a741fd | ||
|
|
7196f82e52 | ||
|
|
32c247b815 | ||
|
|
c551cce74e |
|
|
@ -1,6 +1,6 @@
|
|||
# 🎭 Playwright
|
||||
|
||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop -->
|
||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop -->
|
||||
|
||||
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
|
|||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->114.0.5735.26<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->114.0.5735.35<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->16.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->113.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
|
||||
|
|
|
|||
|
|
@ -180,10 +180,7 @@ context.on("dialog", lambda dialog: dialog.accept())
|
|||
```
|
||||
|
||||
```csharp
|
||||
context.RequestFailed += (_, request) =>
|
||||
{
|
||||
Console.WriteLine(request.Url + " " + request.Failure);
|
||||
};
|
||||
context.Dialog += (_, dialog) => dialog.AcceptAsync();
|
||||
```
|
||||
|
||||
:::note
|
||||
|
|
@ -1456,6 +1453,37 @@ Condition to wait for.
|
|||
### option: BrowserContext.waitForCondition.timeout = %%-wait-for-function-timeout-%%
|
||||
* since: v1.32
|
||||
|
||||
## async method: BrowserContext.waitForConsoleMessage
|
||||
* since: v1.34
|
||||
* langs: java, python, csharp
|
||||
- alias-python: expect_console_message
|
||||
- alias-csharp: RunAndWaitForConsoleMessage
|
||||
- returns: <[ConsoleMessage]>
|
||||
|
||||
Performs action and waits for a [ConsoleMessage] to be logged by in the pages in the context. If predicate is provided, it passes
|
||||
[ConsoleMessage] value into the `predicate` function and waits for `predicate(message)` to return a truthy value.
|
||||
Will throw an error if the page is closed before the [`event: BrowserContext.console`] event is fired.
|
||||
|
||||
## async method: BrowserContext.waitForConsoleMessage
|
||||
* since: v1.34
|
||||
* langs: python
|
||||
- returns: <[EventContextManager]<[ConsoleMessage]>>
|
||||
|
||||
### param: BrowserContext.waitForConsoleMessage.action = %%-csharp-wait-for-event-action-%%
|
||||
* since: v1.34
|
||||
|
||||
### option: BrowserContext.waitForConsoleMessage.predicate
|
||||
* since: v1.34
|
||||
- `predicate` <[function]\([ConsoleMessage]\):[boolean]>
|
||||
|
||||
Receives the [ConsoleMessage] object and resolves to truthy value when the waiting should resolve.
|
||||
|
||||
### option: BrowserContext.waitForConsoleMessage.timeout = %%-wait-for-event-timeout-%%
|
||||
* since: v1.34
|
||||
|
||||
### param: BrowserContext.waitForConsoleMessage.callback = %%-java-wait-for-event-callback-%%
|
||||
* since: v1.34
|
||||
|
||||
## async method: BrowserContext.waitForEvent
|
||||
* since: v1.8
|
||||
* langs: js, python
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ URL of the resource followed by 0-based line and column numbers in the resource
|
|||
|
||||
## method: ConsoleMessage.page
|
||||
* since: v1.33
|
||||
- returns: <[Page]|[null]>
|
||||
- returns: <[null]|[Page]>
|
||||
|
||||
The page that produced this console message, if any.
|
||||
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ A message displayed in the dialog.
|
|||
|
||||
## method: Dialog.page
|
||||
* since: v1.33
|
||||
- returns: <[Page]|[null]>
|
||||
- returns: <[null]|[Page]>
|
||||
|
||||
The page that initiated this dialog, if available.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ import LiteYouTube from '@site/src/components/LiteYouTube';
|
|||
|
||||
## Version 1.34
|
||||
|
||||
### New APIs
|
||||
### Highlights
|
||||
|
||||
* UI Mode now shows steps, fixtures and attachments:
|
||||

|
||||
* New property [`property: TestProject.teardown`] to specify a project that needs to run after this
|
||||
and all dependent projects have finished. Teardown is useful to cleanup any resources acquired by this project.
|
||||
|
||||
|
|
@ -46,6 +48,18 @@ import LiteYouTube from '@site/src/components/LiteYouTube';
|
|||
],
|
||||
});
|
||||
```
|
||||
* New method [`expect.configure`](./test-assertions.md#expectconfigure) to
|
||||
create pre-configured expect instance with its own defaults such as `timeout`
|
||||
and `soft`.
|
||||
|
||||
```js
|
||||
const slowExpect = expect.configure({ timeout: 10000 });
|
||||
await slowExpect(locator).toHaveText('Submit');
|
||||
|
||||
// Always do soft assertions.
|
||||
const softExpect = expect.configure({ soft: true });
|
||||
```
|
||||
|
||||
* New options `stderr` and `stdout` in [`property: TestConfig.webServer`] to configure output handling:
|
||||
|
||||
```js title="playwright.config.ts"
|
||||
|
|
|
|||
|
|
@ -526,6 +526,7 @@ export default defineConfig({
|
|||
* since: v1.10
|
||||
- type: <[Object]|[TraceMode]<"off"|"on"|"retain-on-failure"|"on-first-retry">>
|
||||
- `mode` <[TraceMode]<"off"|"on"|"retain-on-failure"|"on-first-retry"|"on-all-retries">> Trace recording mode.
|
||||
- `attachments` ?<[boolean]> Whether to include test attachments. Defaults to true. Optional.
|
||||
- `screenshots` ?<[boolean]> Whether to capture screenshots during tracing. Screenshots are used to build a timeline preview. Defaults to true. Optional.
|
||||
- `snapshots` ?<[boolean]> Whether to capture DOM snapshot on every action. Defaults to true. Optional.
|
||||
- `sources` ?<[boolean]> Whether to include source files for trace actions. Defaults to true. Optional.
|
||||
|
|
|
|||
|
|
@ -128,7 +128,10 @@ defaults such as `timeout` and `soft`.
|
|||
|
||||
```js
|
||||
const slowExpect = expect.configure({ timeout: 10000 });
|
||||
await slowExpect(locator).toHaveText('Submit);
|
||||
await slowExpect(locator).toHaveText('Submit');
|
||||
|
||||
// Always do soft assertions.
|
||||
const softExpect = expect.configure({ soft: true });
|
||||
```
|
||||
|
||||
## expect.poll
|
||||
|
|
|
|||
78
package-lock.json
generated
78
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "playwright-internal",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "playwright-internal",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"license": "Apache-2.0",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
|
|
@ -6197,11 +6197,11 @@
|
|||
}
|
||||
},
|
||||
"packages/playwright": {
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
|
@ -6211,11 +6211,11 @@
|
|||
}
|
||||
},
|
||||
"packages/playwright-chromium": {
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
|
@ -6225,7 +6225,7 @@
|
|||
}
|
||||
},
|
||||
"packages/playwright-core": {
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
|
|
@ -6233,10 +6233,10 @@
|
|||
},
|
||||
"packages/playwright-ct-core": {
|
||||
"name": "@playwright/experimental-ct-core",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@playwright/test": "1.34.0-next",
|
||||
"@playwright/test": "1.34.0",
|
||||
"vite": "^4.3.3"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -6248,10 +6248,10 @@
|
|||
},
|
||||
"packages/playwright-ct-react": {
|
||||
"name": "@playwright/experimental-ct-react",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@vitejs/plugin-react": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -6280,10 +6280,10 @@
|
|||
},
|
||||
"packages/playwright-ct-react17": {
|
||||
"name": "@playwright/experimental-ct-react17",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@vitejs/plugin-react": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -6312,10 +6312,10 @@
|
|||
},
|
||||
"packages/playwright-ct-solid": {
|
||||
"name": "@playwright/experimental-ct-solid",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"vite-plugin-solid": "^2.7.0"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -6330,10 +6330,10 @@
|
|||
},
|
||||
"packages/playwright-ct-svelte": {
|
||||
"name": "@playwright/experimental-ct-svelte",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^2.1.1"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -6348,10 +6348,10 @@
|
|||
},
|
||||
"packages/playwright-ct-vue": {
|
||||
"name": "@playwright/experimental-ct-vue",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@vitejs/plugin-vue": "^4.2.1"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -6399,10 +6399,10 @@
|
|||
},
|
||||
"packages/playwright-ct-vue2": {
|
||||
"name": "@playwright/experimental-ct-vue2",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@vitejs/plugin-vue2": "^2.2.0"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -6416,11 +6416,11 @@
|
|||
}
|
||||
},
|
||||
"packages/playwright-firefox": {
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
|
@ -6431,11 +6431,11 @@
|
|||
},
|
||||
"packages/playwright-test": {
|
||||
"name": "@playwright/test",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
|
@ -6448,11 +6448,11 @@
|
|||
}
|
||||
},
|
||||
"packages/playwright-webkit": {
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
|
@ -7262,14 +7262,14 @@
|
|||
"@playwright/experimental-ct-core": {
|
||||
"version": "file:packages/playwright-ct-core",
|
||||
"requires": {
|
||||
"@playwright/test": "1.34.0-next",
|
||||
"@playwright/test": "1.34.0",
|
||||
"vite": "^4.3.3"
|
||||
}
|
||||
},
|
||||
"@playwright/experimental-ct-react": {
|
||||
"version": "file:packages/playwright-ct-react",
|
||||
"requires": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@vitejs/plugin-react": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
@ -7289,7 +7289,7 @@
|
|||
"@playwright/experimental-ct-react17": {
|
||||
"version": "file:packages/playwright-ct-react17",
|
||||
"requires": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@vitejs/plugin-react": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
@ -7309,7 +7309,7 @@
|
|||
"@playwright/experimental-ct-solid": {
|
||||
"version": "file:packages/playwright-ct-solid",
|
||||
"requires": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"solid-js": "^1.7.0",
|
||||
"vite-plugin-solid": "^2.7.0"
|
||||
}
|
||||
|
|
@ -7317,7 +7317,7 @@
|
|||
"@playwright/experimental-ct-svelte": {
|
||||
"version": "file:packages/playwright-ct-svelte",
|
||||
"requires": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^2.1.1",
|
||||
"svelte": "^3.55.1"
|
||||
}
|
||||
|
|
@ -7325,7 +7325,7 @@
|
|||
"@playwright/experimental-ct-vue": {
|
||||
"version": "file:packages/playwright-ct-vue",
|
||||
"requires": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@vitejs/plugin-vue": "^4.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
@ -7359,7 +7359,7 @@
|
|||
"@playwright/experimental-ct-vue2": {
|
||||
"version": "file:packages/playwright-ct-vue2",
|
||||
"requires": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@vitejs/plugin-vue2": "^2.2.0",
|
||||
"vue": "^2.7.14"
|
||||
}
|
||||
|
|
@ -7369,7 +7369,7 @@
|
|||
"requires": {
|
||||
"@types/node": "*",
|
||||
"fsevents": "2.3.2",
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
}
|
||||
},
|
||||
"@sindresorhus/is": {
|
||||
|
|
@ -9663,13 +9663,13 @@
|
|||
"playwright": {
|
||||
"version": "file:packages/playwright",
|
||||
"requires": {
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
}
|
||||
},
|
||||
"playwright-chromium": {
|
||||
"version": "file:packages/playwright-chromium",
|
||||
"requires": {
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
}
|
||||
},
|
||||
"playwright-core": {
|
||||
|
|
@ -9678,13 +9678,13 @@
|
|||
"playwright-firefox": {
|
||||
"version": "file:packages/playwright-firefox",
|
||||
"requires": {
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
}
|
||||
},
|
||||
"playwright-webkit": {
|
||||
"version": "file:packages/playwright-webkit",
|
||||
"requires": {
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "playwright-internal",
|
||||
"private": true,
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"description": "A high-level API to automate web browsers",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
background-color: var(--color-canvas-subtle);
|
||||
padding: 0 8px;
|
||||
border-bottom: none;
|
||||
margin-top: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 38px;
|
||||
white-space: nowrap;
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ export class Filter {
|
|||
token.push(c);
|
||||
continue;
|
||||
}
|
||||
if (c === ' ') {
|
||||
if (c === ' ' || c === ':') {
|
||||
if (token.length) {
|
||||
result.push(token.join('').toLowerCase());
|
||||
token = [];
|
||||
|
|
@ -108,7 +108,7 @@ export class Filter {
|
|||
if (test.outcome === 'skipped')
|
||||
status = 'skipped';
|
||||
const searchValues: SearchValues = {
|
||||
text: (status + ' ' + test.projectName + ' ' + test.path.join(' ') + ' ' + test.title).toLowerCase(),
|
||||
text: (status + ' ' + test.projectName + ' ' + test.location.file + ' ' + test.location.line + ' ' + test.path.join(' ') + ' ' + test.title).toLowerCase(),
|
||||
project: test.projectName.toLowerCase(),
|
||||
status: status as any,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ test('should switch to actual', async ({ mount }) => {
|
|||
for (let i = 0; i < imageCount; ++i) {
|
||||
const image = images.nth(i);
|
||||
const box = await image.boundingBox();
|
||||
expect(box).toEqual({ x: 400, y: 124, width: 200, height: 200 });
|
||||
expect(box).toEqual({ x: 400, y: 108, width: 200, height: 200 });
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ test('should switch to expected', async ({ mount }) => {
|
|||
for (let i = 0; i < imageCount; ++i) {
|
||||
const image = images.nth(i);
|
||||
const box = await image.boundingBox();
|
||||
expect(box).toEqual({ x: 400, y: 124, width: 200, height: 200 });
|
||||
expect(box).toEqual({ x: 400, y: 108, width: 200, height: 200 });
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -79,5 +79,5 @@ test('should show diff by default', async ({ mount }) => {
|
|||
|
||||
const image = component.locator('img');
|
||||
const box = await image.boundingBox();
|
||||
expect(box).toEqual({ x: 400, y: 124, width: 200, height: 200 });
|
||||
expect(box).toEqual({ x: 400, y: 108, width: 200, height: 200 });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
height: 48px;
|
||||
min-width: 70px;
|
||||
box-shadow: inset 0 -1px 0 var(--color-border-muted) !important;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.tabbed-pane-tab-strip:focus {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
.test-case-duration {
|
||||
flex: none;
|
||||
align-items: center;
|
||||
padding: 0 8px 24px;
|
||||
padding: 0 8px 8px;
|
||||
}
|
||||
|
||||
.test-case-path {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "playwright-chromium",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"description": "A high-level API to automate Chromium",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -27,6 +27,6 @@
|
|||
"install": "node install.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@
|
|||
"browsers": [
|
||||
{
|
||||
"name": "chromium",
|
||||
"revision": "1063",
|
||||
"revision": "1064",
|
||||
"installByDefault": true,
|
||||
"browserVersion": "114.0.5735.26"
|
||||
"browserVersion": "114.0.5735.35"
|
||||
},
|
||||
{
|
||||
"name": "chromium-with-symbols",
|
||||
"revision": "1063",
|
||||
"revision": "1064",
|
||||
"installByDefault": false,
|
||||
"browserVersion": "114.0.5735.26"
|
||||
"browserVersion": "114.0.5735.35"
|
||||
},
|
||||
{
|
||||
"name": "chromium-tip-of-tree",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "playwright-core",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"description": "A high-level API to automate web browsers",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
|
|||
|
|
@ -23,11 +23,13 @@ const debugLoggerColorMap = {
|
|||
'install': 34, // green
|
||||
'download': 34, // green
|
||||
'browser': 0, // reset
|
||||
'proxy': 92, // purple
|
||||
'socks': 92, // purple
|
||||
'error': 160, // red,
|
||||
'channel:command': 33, // blue
|
||||
'channel:response': 202, // orange
|
||||
'channel:event': 207, // magenta
|
||||
'server': 45, // cyan
|
||||
'server:channel': 34, // green
|
||||
};
|
||||
export type LogName = keyof typeof debugLoggerColorMap;
|
||||
|
||||
|
|
|
|||
|
|
@ -436,7 +436,6 @@ export class SocksProxy extends EventEmitter implements SocksConnectionClient {
|
|||
this._server.listen(port, () => {
|
||||
const port = (this._server.address() as AddressInfo).port;
|
||||
this._port = port;
|
||||
debugLogger.log('proxy', `Starting socks proxy server on port ${port}`);
|
||||
f(port);
|
||||
});
|
||||
});
|
||||
|
|
@ -525,8 +524,10 @@ export class SocksProxyHandler extends EventEmitter {
|
|||
}
|
||||
|
||||
async socketRequested({ uid, host, port }: SocksSocketRequestedPayload): Promise<void> {
|
||||
debugLogger.log('socks', `[${uid}] => request ${host}:${port}`);
|
||||
if (!this._patternMatcher(host, port)) {
|
||||
const payload: SocksSocketFailedPayload = { uid, errorCode: 'ERULESET' };
|
||||
debugLogger.log('socks', `[${uid}] <= pattern error ${payload.errorCode}`);
|
||||
this.emit(SocksProxyHandler.Events.SocksFailed, payload);
|
||||
return;
|
||||
}
|
||||
|
|
@ -543,11 +544,13 @@ export class SocksProxyHandler extends EventEmitter {
|
|||
});
|
||||
socket.on('error', error => {
|
||||
const payload: SocksSocketErrorPayload = { uid, error: error.message };
|
||||
debugLogger.log('socks', `[${uid}] <= network socket error ${payload.error}`);
|
||||
this.emit(SocksProxyHandler.Events.SocksError, payload);
|
||||
this._sockets.delete(uid);
|
||||
});
|
||||
socket.on('end', () => {
|
||||
const payload: SocksSocketEndPayload = { uid };
|
||||
debugLogger.log('socks', `[${uid}] <= network socket closed`);
|
||||
this.emit(SocksProxyHandler.Events.SocksEnd, payload);
|
||||
this._sockets.delete(uid);
|
||||
});
|
||||
|
|
@ -555,9 +558,11 @@ export class SocksProxyHandler extends EventEmitter {
|
|||
const localPort = socket.localPort;
|
||||
this._sockets.set(uid, socket);
|
||||
const payload: SocksSocketConnectedPayload = { uid, host: localAddress, port: localPort };
|
||||
debugLogger.log('socks', `[${uid}] <= connected to network ${payload.host}:${payload.port}`);
|
||||
this.emit(SocksProxyHandler.Events.SocksConnected, payload);
|
||||
} catch (error) {
|
||||
const payload: SocksSocketFailedPayload = { uid, errorCode: error.code };
|
||||
debugLogger.log('socks', `[${uid}] <= connect error ${payload.errorCode}`);
|
||||
this.emit(SocksProxyHandler.Events.SocksFailed, payload);
|
||||
}
|
||||
}
|
||||
|
|
@ -567,6 +572,7 @@ export class SocksProxyHandler extends EventEmitter {
|
|||
}
|
||||
|
||||
socketClosed({ uid }: SocksSocketClosedPayload): void {
|
||||
debugLogger.log('socks', `[${uid}] <= browser socket closed`);
|
||||
this._sockets.get(uid)?.destroy();
|
||||
this._sockets.delete(uid);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import type { LaunchOptions } from '../server/types';
|
|||
import { AndroidDevice } from '../server/android/android';
|
||||
import { DebugControllerDispatcher } from '../server/dispatchers/debugControllerDispatcher';
|
||||
import { startProfiling, stopProfiling } from '../utils';
|
||||
import { monotonicTime } from '../utils';
|
||||
import { debugLogger } from '../common/debugLogger';
|
||||
|
||||
export type ClientType = 'controller' | 'playwright' | 'launch-browser' | 'reuse-browser' | 'pre-launched-browser-or-android';
|
||||
|
||||
|
|
@ -46,14 +48,14 @@ export class PlaywrightConnection {
|
|||
private _onClose: () => void;
|
||||
private _dispatcherConnection: DispatcherConnection;
|
||||
private _cleanups: (() => Promise<void>)[] = [];
|
||||
private _debugLog: (m: string) => void;
|
||||
private _id: string;
|
||||
private _disconnected = false;
|
||||
private _preLaunched: PreLaunched;
|
||||
private _options: Options;
|
||||
private _root: DispatcherScope;
|
||||
private _profileName: string;
|
||||
|
||||
constructor(lock: Promise<void>, clientType: ClientType, ws: WebSocket, options: Options, preLaunched: PreLaunched, log: (m: string) => void, onClose: () => void) {
|
||||
constructor(lock: Promise<void>, clientType: ClientType, ws: WebSocket, options: Options, preLaunched: PreLaunched, id: string, onClose: () => void) {
|
||||
this._ws = ws;
|
||||
this._preLaunched = preLaunched;
|
||||
this._options = options;
|
||||
|
|
@ -63,18 +65,25 @@ export class PlaywrightConnection {
|
|||
if (clientType === 'pre-launched-browser-or-android')
|
||||
assert(preLaunched.browser || preLaunched.androidDevice);
|
||||
this._onClose = onClose;
|
||||
this._debugLog = log;
|
||||
this._id = id;
|
||||
this._profileName = `${new Date().toISOString()}-${clientType}`;
|
||||
|
||||
this._dispatcherConnection = new DispatcherConnection();
|
||||
this._dispatcherConnection.onmessage = async message => {
|
||||
await lock;
|
||||
if (ws.readyState !== ws.CLOSING)
|
||||
ws.send(JSON.stringify(message));
|
||||
if (ws.readyState !== ws.CLOSING) {
|
||||
const messageString = JSON.stringify(message);
|
||||
if (debugLogger.isEnabled('server:channel'))
|
||||
debugLogger.log('server:channel', `[${this._id}] ${monotonicTime() * 1000} SEND ► ${messageString}`);
|
||||
ws.send(messageString);
|
||||
}
|
||||
};
|
||||
ws.on('message', async (message: string) => {
|
||||
await lock;
|
||||
this._dispatcherConnection.dispatch(JSON.parse(Buffer.from(message).toString()));
|
||||
const messageString = Buffer.from(message).toString();
|
||||
if (debugLogger.isEnabled('server:channel'))
|
||||
debugLogger.log('server:channel', `[${this._id}] ${monotonicTime() * 1000} ◀ RECV ${messageString}`);
|
||||
this._dispatcherConnection.dispatch(JSON.parse(messageString));
|
||||
});
|
||||
|
||||
ws.on('close', () => this._onDisconnect());
|
||||
|
|
@ -100,7 +109,7 @@ export class PlaywrightConnection {
|
|||
}
|
||||
|
||||
private async _initPlaywrightConnectMode(scope: RootDispatcher) {
|
||||
this._debugLog(`engaged playwright.connect mode`);
|
||||
debugLogger.log('server', `[${this._id}] engaged playwright.connect mode`);
|
||||
const playwright = createPlaywright('javascript');
|
||||
// Close all launched browsers on disconnect.
|
||||
this._cleanups.push(async () => {
|
||||
|
|
@ -112,7 +121,7 @@ export class PlaywrightConnection {
|
|||
}
|
||||
|
||||
private async _initLaunchBrowserMode(scope: RootDispatcher) {
|
||||
this._debugLog(`engaged launch mode for "${this._options.browserName}"`);
|
||||
debugLogger.log('server', `[${this._id}] engaged launch mode for "${this._options.browserName}"`);
|
||||
const playwright = createPlaywright('javascript');
|
||||
|
||||
const ownedSocksProxy = await this._createOwnedSocksProxy(playwright);
|
||||
|
|
@ -131,7 +140,7 @@ export class PlaywrightConnection {
|
|||
}
|
||||
|
||||
private async _initPreLaunchedBrowserMode(scope: RootDispatcher) {
|
||||
this._debugLog(`engaged pre-launched (browser) mode`);
|
||||
debugLogger.log('server', `[${this._id}] engaged pre-launched (browser) mode`);
|
||||
const playwright = this._preLaunched.playwright!;
|
||||
|
||||
// Note: connected client owns the socks proxy and configures the pattern.
|
||||
|
|
@ -154,7 +163,7 @@ export class PlaywrightConnection {
|
|||
}
|
||||
|
||||
private async _initPreLaunchedAndroidMode(scope: RootDispatcher) {
|
||||
this._debugLog(`engaged pre-launched (Android) mode`);
|
||||
debugLogger.log('server', `[${this._id}] engaged pre-launched (Android) mode`);
|
||||
const playwright = this._preLaunched.playwright!;
|
||||
const androidDevice = this._preLaunched.androidDevice!;
|
||||
androidDevice.on(AndroidDevice.Events.Close, () => {
|
||||
|
|
@ -167,7 +176,7 @@ export class PlaywrightConnection {
|
|||
}
|
||||
|
||||
private _initDebugControllerMode(): DebugControllerDispatcher {
|
||||
this._debugLog(`engaged reuse controller mode`);
|
||||
debugLogger.log('server', `[${this._id}] engaged reuse controller mode`);
|
||||
const playwright = this._preLaunched.playwright!;
|
||||
// Always create new instance based on the reused Playwright instance.
|
||||
return new DebugControllerDispatcher(this._dispatcherConnection, playwright.debugController);
|
||||
|
|
@ -177,7 +186,7 @@ export class PlaywrightConnection {
|
|||
// Note: reuse browser mode does not support socks proxy, because
|
||||
// clients come and go, while the browser stays the same.
|
||||
|
||||
this._debugLog(`engaged reuse browsers mode for ${this._options.browserName}`);
|
||||
debugLogger.log('server', `[${this._id}] engaged reuse browsers mode for ${this._options.browserName}`);
|
||||
const playwright = this._preLaunched.playwright!;
|
||||
|
||||
const requestedOptions = launchOptionsHash(this._options.launchOptions);
|
||||
|
|
@ -232,27 +241,27 @@ export class PlaywrightConnection {
|
|||
const socksProxy = new SocksProxy();
|
||||
socksProxy.setPattern(this._options.socksProxyPattern);
|
||||
playwright.options.socksProxyPort = await socksProxy.listen(0);
|
||||
this._debugLog(`started socks proxy on port ${playwright.options.socksProxyPort}`);
|
||||
debugLogger.log('server', `[${this._id}] started socks proxy on port ${playwright.options.socksProxyPort}`);
|
||||
this._cleanups.push(() => socksProxy.close());
|
||||
return socksProxy;
|
||||
}
|
||||
|
||||
private async _onDisconnect(error?: Error) {
|
||||
this._disconnected = true;
|
||||
this._debugLog(`disconnected. error: ${error}`);
|
||||
debugLogger.log('server', `[${this._id}] disconnected. error: ${error}`);
|
||||
this._root._dispose();
|
||||
this._debugLog(`starting cleanup`);
|
||||
debugLogger.log('server', `[${this._id}] starting cleanup`);
|
||||
for (const cleanup of this._cleanups)
|
||||
await cleanup().catch(() => {});
|
||||
await stopProfiling(this._profileName);
|
||||
this._onClose();
|
||||
this._debugLog(`finished cleanup`);
|
||||
debugLogger.log('server', `[${this._id}] finished cleanup`);
|
||||
}
|
||||
|
||||
async close(reason?: { code: number, reason: string }) {
|
||||
if (this._disconnected)
|
||||
return;
|
||||
this._debugLog(`force closing connection: ${reason?.reason || ''} (${reason?.code || 0})`);
|
||||
debugLogger.log('server', `[${this._id}] force closing connection: ${reason?.reason || ''} (${reason?.code || 0})`);
|
||||
try {
|
||||
this._ws.close(reason?.code, reason?.reason);
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { debug, wsServer } from '../utilsBundle';
|
||||
import { wsServer } from '../utilsBundle';
|
||||
import type { WebSocketServer } from '../utilsBundle';
|
||||
import http from 'http';
|
||||
import type { Browser } from '../server/browser';
|
||||
|
|
@ -26,17 +26,11 @@ import type { LaunchOptions } from '../server/types';
|
|||
import { ManualPromise } from '../utils/manualPromise';
|
||||
import type { AndroidDevice } from '../server/android/android';
|
||||
import { type SocksProxy } from '../common/socksProxy';
|
||||
|
||||
const debugLog = debug('pw:server');
|
||||
import { debugLogger } from '../common/debugLogger';
|
||||
|
||||
let lastConnectionId = 0;
|
||||
const kConnectionSymbol = Symbol('kConnection');
|
||||
|
||||
function newLogger() {
|
||||
const id = ++lastConnectionId;
|
||||
return (message: string) => debugLog(`[id=${id}] ${message}`);
|
||||
}
|
||||
|
||||
type ServerOptions = {
|
||||
path: string;
|
||||
maxConnections: number;
|
||||
|
|
@ -59,6 +53,8 @@ export class PlaywrightServer {
|
|||
}
|
||||
|
||||
async listen(port: number = 0): Promise<string> {
|
||||
debugLogger.log('server', `Server started at ${new Date()}`);
|
||||
|
||||
const server = http.createServer((request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||
if (request.method === 'GET' && request.url === '/json') {
|
||||
response.setHeader('Content-Type', 'application/json');
|
||||
|
|
@ -69,7 +65,7 @@ export class PlaywrightServer {
|
|||
}
|
||||
response.end('Running');
|
||||
});
|
||||
server.on('error', error => debugLog(error));
|
||||
server.on('error', error => debugLogger.log('server', String(error)));
|
||||
|
||||
const wsEndpoint = await new Promise<string>((resolve, reject) => {
|
||||
server.listen(port, () => {
|
||||
|
|
@ -83,7 +79,7 @@ export class PlaywrightServer {
|
|||
}).on('error', reject);
|
||||
});
|
||||
|
||||
debugLog('Listening at ' + wsEndpoint);
|
||||
debugLogger.log('server', 'Listening at ' + wsEndpoint);
|
||||
this._wsServer = new wsServer({ server, path: this._options.path });
|
||||
const browserSemaphore = new Semaphore(this._options.maxConnections);
|
||||
const controllerSemaphore = new Semaphore(1);
|
||||
|
|
@ -107,8 +103,8 @@ export class PlaywrightServer {
|
|||
} catch (e) {
|
||||
}
|
||||
|
||||
const log = newLogger();
|
||||
log(`serving connection: ${request.url}`);
|
||||
const id = String(++lastConnectionId);
|
||||
debugLogger.log('server', `[${id}] serving connection: ${request.url}`);
|
||||
const isDebugControllerClient = !!request.headers['x-playwright-debug-controller'];
|
||||
const shouldReuseBrowser = !!request.headers['x-playwright-reuse-context'];
|
||||
|
||||
|
|
@ -145,7 +141,7 @@ export class PlaywrightServer {
|
|||
androidDevice: this._options.preLaunchedAndroidDevice,
|
||||
socksProxy: this._options.preLaunchedSocksProxy,
|
||||
},
|
||||
log, () => semaphore.release());
|
||||
id, () => semaphore.release());
|
||||
(ws as any)[kConnectionSymbol] = connection;
|
||||
});
|
||||
|
||||
|
|
@ -156,7 +152,7 @@ export class PlaywrightServer {
|
|||
const server = this._wsServer;
|
||||
if (!server)
|
||||
return;
|
||||
debugLog('closing websocket server');
|
||||
debugLogger.log('server', 'closing websocket server');
|
||||
const waitForClose = new Promise(f => server.close(f));
|
||||
// First disconnect all remaining clients.
|
||||
await Promise.all(Array.from(server.clients).map(async ws => {
|
||||
|
|
@ -169,15 +165,15 @@ export class PlaywrightServer {
|
|||
}
|
||||
}));
|
||||
await waitForClose;
|
||||
debugLog('closing http server');
|
||||
debugLogger.log('server', 'closing http server');
|
||||
await new Promise(f => server.options.server!.close(f));
|
||||
this._wsServer = undefined;
|
||||
debugLog('closed server');
|
||||
debugLogger.log('server', 'closed server');
|
||||
|
||||
debugLog('closing browsers');
|
||||
debugLogger.log('server', 'closing browsers');
|
||||
if (this._preLaunchedPlaywright)
|
||||
await Promise.all(this._preLaunchedPlaywright.allBrowsers().map(browser => browser.close()));
|
||||
debugLog('closed browsers');
|
||||
debugLogger.log('server', 'closed browsers');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@
|
|||
"defaultBrowserType": "webkit"
|
||||
},
|
||||
"Galaxy S5": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -121,7 +121,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S5 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -132,7 +132,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S8": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 740
|
||||
|
|
@ -143,7 +143,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S8 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 740,
|
||||
"height": 360
|
||||
|
|
@ -154,7 +154,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S9+": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 320,
|
||||
"height": 658
|
||||
|
|
@ -165,7 +165,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S9+ landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 658,
|
||||
"height": 320
|
||||
|
|
@ -176,7 +176,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy Tab S4": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 712,
|
||||
"height": 1138
|
||||
|
|
@ -187,7 +187,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy Tab S4 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 1138,
|
||||
"height": 712
|
||||
|
|
@ -836,7 +836,7 @@
|
|||
"defaultBrowserType": "webkit"
|
||||
},
|
||||
"LG Optimus L70": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 384,
|
||||
"height": 640
|
||||
|
|
@ -847,7 +847,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"LG Optimus L70 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 384
|
||||
|
|
@ -858,7 +858,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Microsoft Lumia 550": {
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36 Edge/14.14263",
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36 Edge/14.14263",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -869,7 +869,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Microsoft Lumia 550 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36 Edge/14.14263",
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36 Edge/14.14263",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -880,7 +880,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Microsoft Lumia 950": {
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36 Edge/14.14263",
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36 Edge/14.14263",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -891,7 +891,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Microsoft Lumia 950 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36 Edge/14.14263",
|
||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36 Edge/14.14263",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -902,7 +902,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 10": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 800,
|
||||
"height": 1280
|
||||
|
|
@ -913,7 +913,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 10 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 1280,
|
||||
"height": 800
|
||||
|
|
@ -924,7 +924,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 4": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 384,
|
||||
"height": 640
|
||||
|
|
@ -935,7 +935,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 4 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 384
|
||||
|
|
@ -946,7 +946,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 5": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -957,7 +957,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 5 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -968,7 +968,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 5X": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 412,
|
||||
"height": 732
|
||||
|
|
@ -979,7 +979,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 5X landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 732,
|
||||
"height": 412
|
||||
|
|
@ -990,7 +990,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 6": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 412,
|
||||
"height": 732
|
||||
|
|
@ -1001,7 +1001,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 6 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 732,
|
||||
"height": 412
|
||||
|
|
@ -1012,7 +1012,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 6P": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 412,
|
||||
"height": 732
|
||||
|
|
@ -1023,7 +1023,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 6P landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 732,
|
||||
"height": 412
|
||||
|
|
@ -1034,7 +1034,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 7": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 600,
|
||||
"height": 960
|
||||
|
|
@ -1045,7 +1045,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 7 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 960,
|
||||
"height": 600
|
||||
|
|
@ -1100,7 +1100,7 @@
|
|||
"defaultBrowserType": "webkit"
|
||||
},
|
||||
"Pixel 2": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 411,
|
||||
"height": 731
|
||||
|
|
@ -1111,7 +1111,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 2 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 731,
|
||||
"height": 411
|
||||
|
|
@ -1122,7 +1122,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 2 XL": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 411,
|
||||
"height": 823
|
||||
|
|
@ -1133,7 +1133,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 2 XL landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 823,
|
||||
"height": 411
|
||||
|
|
@ -1144,7 +1144,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 3": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 393,
|
||||
"height": 786
|
||||
|
|
@ -1155,7 +1155,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 3 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 786,
|
||||
"height": 393
|
||||
|
|
@ -1166,7 +1166,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 4": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 353,
|
||||
"height": 745
|
||||
|
|
@ -1177,7 +1177,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 4 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 745,
|
||||
"height": 353
|
||||
|
|
@ -1188,7 +1188,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 4a (5G)": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"screen": {
|
||||
"width": 412,
|
||||
"height": 892
|
||||
|
|
@ -1203,7 +1203,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 4a (5G) landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"screen": {
|
||||
"height": 892,
|
||||
"width": 412
|
||||
|
|
@ -1218,7 +1218,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 5": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"screen": {
|
||||
"width": 393,
|
||||
"height": 851
|
||||
|
|
@ -1233,7 +1233,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 5 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"screen": {
|
||||
"width": 851,
|
||||
"height": 393
|
||||
|
|
@ -1248,7 +1248,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Moto G4": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -1259,7 +1259,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Moto G4 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -1270,7 +1270,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Desktop Chrome HiDPI": {
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
|
||||
"screen": {
|
||||
"width": 1792,
|
||||
"height": 1120
|
||||
|
|
@ -1285,7 +1285,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Desktop Edge HiDPI": {
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36 Edg/114.0.5735.26",
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36 Edg/114.0.5735.35",
|
||||
"screen": {
|
||||
"width": 1792,
|
||||
"height": 1120
|
||||
|
|
@ -1330,7 +1330,7 @@
|
|||
"defaultBrowserType": "webkit"
|
||||
},
|
||||
"Desktop Chrome": {
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
|
||||
"screen": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
|
|
@ -1345,7 +1345,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Desktop Edge": {
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36 Edg/114.0.5735.26",
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36 Edg/114.0.5735.35",
|
||||
"screen": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
|||
for (const entry of entries)
|
||||
zipFile.addFile(entry.value, entry.name);
|
||||
zipFile.end();
|
||||
const zipFileName = state.traceFile + '.zip';
|
||||
const zipFileName = state.traceFile.file + '.zip';
|
||||
zipFile.outputStream.pipe(fs.createWriteStream(zipFileName)).on('close', () => {
|
||||
const artifact = new Artifact(this._context, zipFileName);
|
||||
artifact.reportFinished();
|
||||
|
|
|
|||
4
packages/playwright-core/types/types.d.ts
vendored
4
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -16435,7 +16435,7 @@ export interface ConsoleMessage {
|
|||
/**
|
||||
* The page that produced this console message, if any.
|
||||
*/
|
||||
page(): Page|null;
|
||||
page(): null|Page;
|
||||
|
||||
/**
|
||||
* The text of the console message.
|
||||
|
|
@ -16637,7 +16637,7 @@ export interface Dialog {
|
|||
/**
|
||||
* The page that initiated this dialog, if available.
|
||||
*/
|
||||
page(): Page|null;
|
||||
page(): null|Page;
|
||||
|
||||
/**
|
||||
* Returns dialog's type, can be one of `alert`, `beforeunload`, `confirm` or `prompt`.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/experimental-ct-core",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"description": "Playwright Component Testing Helpers",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"vite": "^4.3.3",
|
||||
"@playwright/test": "1.34.0-next"
|
||||
"@playwright/test": "1.34.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "./cli.js"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/experimental-ct-react",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"description": "Playwright Component Testing for React",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@vitejs/plugin-react": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/experimental-ct-react17",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"description": "Playwright Component Testing for React",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@vitejs/plugin-react": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/experimental-ct-solid",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"description": "Playwright Component Testing for Solid",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"vite-plugin-solid": "^2.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/experimental-ct-svelte",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"description": "Playwright Component Testing for Svelte",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/experimental-ct-vue",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"description": "Playwright Component Testing for Vue",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@vitejs/plugin-vue": "^4.2.1"
|
||||
},
|
||||
"bin": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/experimental-ct-vue2",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"description": "Playwright Component Testing for Vue2",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@playwright/experimental-ct-core": "1.34.0-next",
|
||||
"@playwright/experimental-ct-core": "1.34.0",
|
||||
"@vitejs/plugin-vue2": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "playwright-firefox",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"description": "A high-level API to automate Firefox",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -27,6 +27,6 @@
|
|||
"install": "node install.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/test",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"description": "A high-level API to automate web browsers",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
|
|
|
|||
|
|
@ -558,7 +558,7 @@ class ArtifactsRecorder {
|
|||
private _traceMode: TraceMode;
|
||||
private _captureTrace = false;
|
||||
private _screenshotOptions: { mode: ScreenshotMode } & Pick<playwrightLibrary.PageScreenshotOptions, 'fullPage' | 'omitBackground'> | undefined;
|
||||
private _traceOptions: { screenshots: boolean, snapshots: boolean, sources: boolean, mode?: TraceMode };
|
||||
private _traceOptions: { screenshots: boolean, snapshots: boolean, sources: boolean, attachments: boolean, mode?: TraceMode };
|
||||
private _temporaryTraceFiles: string[] = [];
|
||||
private _temporaryScreenshots: string[] = [];
|
||||
private _reusedContexts = new Set<BrowserContext>();
|
||||
|
|
@ -572,7 +572,7 @@ class ArtifactsRecorder {
|
|||
this._screenshotMode = normalizeScreenshotMode(screenshot);
|
||||
this._screenshotOptions = typeof screenshot === 'string' ? undefined : screenshot;
|
||||
this._traceMode = normalizeTraceMode(trace);
|
||||
const defaultTraceOptions = { screenshots: true, snapshots: true, sources: true };
|
||||
const defaultTraceOptions = { screenshots: true, snapshots: true, sources: true, attachments: true };
|
||||
this._traceOptions = typeof trace === 'string' ? defaultTraceOptions : { ...defaultTraceOptions, ...trace, mode: undefined };
|
||||
this._screenshottedSymbol = Symbol('screenshotted');
|
||||
this._startedCollectingArtifacts = Symbol('startedCollectingArtifacts');
|
||||
|
|
@ -661,6 +661,12 @@ class ArtifactsRecorder {
|
|||
if (this._preserveTrace()) {
|
||||
const events = this._testInfo._traceEvents;
|
||||
if (events.length) {
|
||||
if (!this._traceOptions.attachments) {
|
||||
for (const event of events) {
|
||||
if (event.type === 'after')
|
||||
delete event.attachments;
|
||||
}
|
||||
}
|
||||
const tracePath = path.join(this._artifactsDir, createGuid() + '.zip');
|
||||
this._temporaryTraceFiles.push(tracePath);
|
||||
await saveTraceFile(tracePath, events, this._traceOptions.sources);
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ export class TestInfoImpl implements TestInfo {
|
|||
|
||||
async attach(name: string, options: { path?: string, body?: string | Buffer, contentType?: string } = {}) {
|
||||
const step = this._addStep({
|
||||
title: `attach "${name}"`,
|
||||
title: `attach "${name}"`,
|
||||
category: 'attach',
|
||||
wallTime: Date.now(),
|
||||
});
|
||||
|
|
|
|||
2
packages/playwright-test/types/test.d.ts
vendored
2
packages/playwright-test/types/test.d.ts
vendored
|
|
@ -3554,7 +3554,7 @@ export interface PlaywrightWorkerOptions {
|
|||
*
|
||||
* Learn more about [recording trace](https://playwright.dev/docs/test-configuration#record-test-trace).
|
||||
*/
|
||||
trace: TraceMode | /** deprecated */ 'retry-with-trace' | { mode: TraceMode, snapshots?: boolean, screenshots?: boolean, sources?: boolean };
|
||||
trace: TraceMode | /** deprecated */ 'retry-with-trace' | { mode: TraceMode, snapshots?: boolean, screenshots?: boolean, sources?: boolean, attachments?: boolean };
|
||||
/**
|
||||
* Whether to record video for each test. Defaults to `'off'`.
|
||||
* - `'off'`: Do not record video.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "playwright-webkit",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"description": "A high-level API to automate WebKit",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -27,6 +27,6 @@
|
|||
"install": "node install.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "playwright",
|
||||
"version": "1.34.0-next",
|
||||
"version": "1.34.0",
|
||||
"description": "A high-level API to automate web browsers",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -28,6 +28,6 @@
|
|||
"install": "node install.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"playwright-core": "1.34.0-next"
|
||||
"playwright-core": "1.34.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,13 +76,6 @@ export class TraceModel {
|
|||
unzipProgress(++done, total);
|
||||
|
||||
contextEntry.actions = [...actionMap.values()].sort((a1, a2) => a1.startTime - a2.startTime);
|
||||
if (!backend.isLive()) {
|
||||
for (const action of contextEntry.actions) {
|
||||
if (!action.endTime && !action.error)
|
||||
action.error = { name: 'Error', message: 'Timed out' };
|
||||
}
|
||||
}
|
||||
|
||||
const stacks = await this._backend.readText(ordinal + '.stacks');
|
||||
if (stacks) {
|
||||
const callMetadata = parseClientSideCallMetadata(JSON.parse(stacks));
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ export interface ActionListProps {
|
|||
onSelected: (action: ActionTraceEvent) => void,
|
||||
onHighlighted: (action: ActionTraceEvent | undefined) => void,
|
||||
revealConsole: () => void,
|
||||
isLive?: boolean,
|
||||
}
|
||||
|
||||
type ActionTreeItem = {
|
||||
|
|
@ -49,6 +50,7 @@ export const ActionList: React.FC<ActionListProps> = ({
|
|||
onSelected,
|
||||
onHighlighted,
|
||||
revealConsole,
|
||||
isLive,
|
||||
}) => {
|
||||
const [treeState, setTreeState] = React.useState<TreeState>({ expandedItems: new Map() });
|
||||
const { rootItem, itemMap } = React.useMemo(() => {
|
||||
|
|
@ -86,14 +88,15 @@ export const ActionList: React.FC<ActionListProps> = ({
|
|||
onSelected={item => onSelected(item.action!)}
|
||||
onHighlighted={item => onHighlighted(item?.action)}
|
||||
isError={item => !!item.action?.error?.message}
|
||||
render={item => renderAction(item.action!, sdkLanguage, revealConsole)}
|
||||
render={item => renderAction(item.action!, sdkLanguage, revealConsole, isLive || false)}
|
||||
/>;
|
||||
};
|
||||
|
||||
const renderAction = (
|
||||
action: ActionTraceEvent,
|
||||
sdkLanguage: Language | undefined,
|
||||
revealConsole: () => void
|
||||
revealConsole: () => void,
|
||||
isLive: boolean,
|
||||
) => {
|
||||
const { errors, warnings } = modelUtil.stats(action);
|
||||
const locator = action.params.selector ? asLocator(sdkLanguage || 'javascript', action.params.selector, false /* isFrameLocator */, true /* playSafe */) : undefined;
|
||||
|
|
@ -103,6 +106,8 @@ const renderAction = (
|
|||
time = msToString(action.endTime - action.startTime);
|
||||
else if (action.error)
|
||||
time = 'Timed out';
|
||||
else if (!isLive)
|
||||
time = '-';
|
||||
return <>
|
||||
<div className='action-title'>
|
||||
<span>{action.apiName}</span>
|
||||
|
|
|
|||
|
|
@ -492,7 +492,7 @@ const TraceView: React.FC<{
|
|||
item: { testFile?: SourceLocation, testCase?: TestCase },
|
||||
rootDir?: string,
|
||||
}> = ({ item, rootDir }) => {
|
||||
const [model, setModel] = React.useState<MultiTraceModel | undefined>();
|
||||
const [model, setModel] = React.useState<{ model: MultiTraceModel, isLive: boolean } | undefined>();
|
||||
const [counter, setCounter] = React.useState(0);
|
||||
const pollTimer = React.useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
|
|
@ -505,7 +505,7 @@ const TraceView: React.FC<{
|
|||
// This avoids auto-selection of the last action every time we reload the model.
|
||||
const [selectedActionId, setSelectedActionId] = React.useState<string | undefined>();
|
||||
const onSelectionChanged = React.useCallback((action: ActionTraceEvent) => setSelectedActionId(idForAction(action)), [setSelectedActionId]);
|
||||
const initialSelection = selectedActionId ? model?.actions.find(a => idForAction(a) === selectedActionId) : undefined;
|
||||
const initialSelection = selectedActionId ? model?.model.actions.find(a => idForAction(a) === selectedActionId) : undefined;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (pollTimer.current)
|
||||
|
|
@ -520,7 +520,7 @@ const TraceView: React.FC<{
|
|||
// Test finished.
|
||||
const attachment = result && result.duration >= 0 && result.attachments.find(a => a.name === 'trace');
|
||||
if (attachment && attachment.path) {
|
||||
loadSingleTraceFile(attachment.path).then(model => setModel(model));
|
||||
loadSingleTraceFile(attachment.path).then(model => setModel({ model, isLive: false }));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -534,7 +534,7 @@ const TraceView: React.FC<{
|
|||
pollTimer.current = setTimeout(async () => {
|
||||
try {
|
||||
const model = await loadSingleTraceFile(traceLocation);
|
||||
setModel(model);
|
||||
setModel({ model, isLive: true });
|
||||
} catch {
|
||||
setModel(undefined);
|
||||
} finally {
|
||||
|
|
@ -549,14 +549,15 @@ const TraceView: React.FC<{
|
|||
|
||||
return <Workbench
|
||||
key='workbench'
|
||||
model={model}
|
||||
model={model?.model}
|
||||
hideTimelineBars={true}
|
||||
hideStackFrames={true}
|
||||
showSourcesFirst={true}
|
||||
rootDir={rootDir}
|
||||
initialSelection={initialSelection}
|
||||
onSelectionChanged={onSelectionChanged}
|
||||
fallbackLocation={item.testFile} />;
|
||||
fallbackLocation={item.testFile}
|
||||
isLive={model?.isLive} />;
|
||||
};
|
||||
|
||||
declare global {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ export const Workbench: React.FunctionComponent<{
|
|||
fallbackLocation?: modelUtil.SourceLocation,
|
||||
initialSelection?: ActionTraceEvent,
|
||||
onSelectionChanged?: (action: ActionTraceEvent) => void,
|
||||
}> = ({ model, hideTimelineBars, hideStackFrames, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged }) => {
|
||||
isLive?: boolean,
|
||||
}> = ({ model, hideTimelineBars, hideStackFrames, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged, isLive }) => {
|
||||
const [selectedAction, setSelectedAction] = React.useState<ActionTraceEvent | undefined>(undefined);
|
||||
const [highlightedAction, setHighlightedAction] = React.useState<ActionTraceEvent | undefined>();
|
||||
const [selectedNavigatorTab, setSelectedNavigatorTab] = React.useState<string>('actions');
|
||||
|
|
@ -142,6 +143,7 @@ export const Workbench: React.FunctionComponent<{
|
|||
onSelected={onActionSelected}
|
||||
onHighlighted={setHighlightedAction}
|
||||
revealConsole={() => setSelectedPropertiesTab('console')}
|
||||
isLive={isLive}
|
||||
/>
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -578,7 +578,6 @@ test('should record global request trace', async ({ request, context, server },
|
|||
});
|
||||
|
||||
test('should store global request traces separately', async ({ request, server, playwright, browserName, mode }, testInfo) => {
|
||||
test.fixme(browserName === 'chromium' && mode === 'driver', 'https://github.com/microsoft/playwright/issues/23108');
|
||||
const request2 = await playwright.request.newContext();
|
||||
await Promise.all([
|
||||
(request as any)._tracing.start({ snapshots: true }),
|
||||
|
|
|
|||
|
|
@ -526,3 +526,59 @@ test(`trace:retain-on-failure should create trace if request context is disposed
|
|||
expect(trace.apiNames).toContain('apiRequestContext.get');
|
||||
expect(result.failed).toBe(1);
|
||||
});
|
||||
|
||||
test('should include attachments by default', async ({ runInlineTest, server }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
module.exports = { use: { trace: 'on' } };
|
||||
`,
|
||||
'a.spec.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('pass', async ({}, testInfo) => {
|
||||
testInfo.attach('foo', { body: 'bar' });
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
const trace = await parseTrace(testInfo.outputPath('test-results', 'a-pass', 'trace.zip'));
|
||||
expect(trace.apiNames).toEqual([
|
||||
'Before Hooks',
|
||||
`attach "foo"`,
|
||||
'After Hooks',
|
||||
]);
|
||||
expect(trace.actions[1].attachments).toEqual([{
|
||||
name: 'foo',
|
||||
contentType: 'text/plain',
|
||||
sha1: expect.any(String),
|
||||
}]);
|
||||
expect([...trace.resources.keys()].filter(f => f.startsWith('resources/'))).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('should opt out of attachments', async ({ runInlineTest, server }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
module.exports = { use: { trace: { mode: 'on', attachments: false } } };
|
||||
`,
|
||||
'a.spec.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('pass', async ({}, testInfo) => {
|
||||
testInfo.attach('foo', { body: 'bar' });
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
const trace = await parseTrace(testInfo.outputPath('test-results', 'a-pass', 'trace.zip'));
|
||||
expect(trace.apiNames).toEqual([
|
||||
'Before Hooks',
|
||||
`attach "foo"`,
|
||||
'After Hooks',
|
||||
]);
|
||||
expect(trace.actions[1].attachments).toEqual(undefined);
|
||||
expect([...trace.resources.keys()].filter(f => f.startsWith('resources/'))).toHaveLength(0);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1348,7 +1348,7 @@ test.describe('labels', () => {
|
|||
const result = await runInlineTest({
|
||||
'a.test.js': `
|
||||
const { expect, test } = require('@playwright/test');
|
||||
const tags = ['@smoke:p1', '@issue[123]', '@issue#123', '@$$$', '@tl/dr'];
|
||||
const tags = ['@smoke-p1', '@issue[123]', '@issue#123', '@$$$', '@tl/dr'];
|
||||
|
||||
test.describe('Error Pages', () => {
|
||||
tags.forEach(tag => {
|
||||
|
|
@ -1364,7 +1364,7 @@ test.describe('labels', () => {
|
|||
expect(result.passed).toBe(5);
|
||||
|
||||
await showReport();
|
||||
const tags = ['smoke:p1', 'issue[123]', 'issue#123', '$$$', 'tl/dr'];
|
||||
const tags = ['smoke-p1', 'issue[123]', 'issue#123', '$$$', 'tl/dr'];
|
||||
const searchInput = page.locator('.subnav-search-input');
|
||||
|
||||
for (const tag of tags) {
|
||||
|
|
|
|||
|
|
@ -98,9 +98,12 @@ test('should merge screenshot assertions', async ({ runUITest }, testInfo) => {
|
|||
'action list'
|
||||
).toHaveText([
|
||||
/Before Hooks[\d.]+m?s/,
|
||||
/page\.setContent[\d.]+m?s/,
|
||||
/expect\.toHaveScreenshot[\d.]+m?s/,
|
||||
/After Hooks/,
|
||||
/page.setContent[\d.]+m?s/,
|
||||
/expect.toHaveScreenshot[\d.]+m?s/,
|
||||
/After Hooks-/,
|
||||
/fixture: page[\d.]+m?s/,
|
||||
/fixture: context[\d.]+m?s/,
|
||||
/fixture: browser[\d.]+m?s/,
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
|||
2
utils/generate_types/overrides-test.d.ts
vendored
2
utils/generate_types/overrides-test.d.ts
vendored
|
|
@ -206,7 +206,7 @@ export interface PlaywrightWorkerOptions {
|
|||
launchOptions: LaunchOptions;
|
||||
connectOptions: ConnectOptions | undefined;
|
||||
screenshot: ScreenshotMode | { mode: ScreenshotMode } & Pick<PageScreenshotOptions, 'fullPage' | 'omitBackground'>;
|
||||
trace: TraceMode | /** deprecated */ 'retry-with-trace' | { mode: TraceMode, snapshots?: boolean, screenshots?: boolean, sources?: boolean };
|
||||
trace: TraceMode | /** deprecated */ 'retry-with-trace' | { mode: TraceMode, snapshots?: boolean, screenshots?: boolean, sources?: boolean, attachments?: boolean };
|
||||
video: VideoMode | /** deprecated */ 'retry-with-video' | { mode: VideoMode, size?: ViewportSize };
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue