Merge branch 'main' into sharding-algorithm
This commit is contained in:
commit
ef2d35f34a
|
|
@ -60,7 +60,7 @@ jobs:
|
|||
git checkout -b "$BRANCH_NAME"
|
||||
git push origin $BRANCH_NAME
|
||||
- name: Create Pull Request
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.REPOSITORY_DISPATCH_PERSONAL_ACCESS_TOKEN }}
|
||||
script: |
|
||||
|
|
|
|||
2
.github/workflows/create_test_report.yml
vendored
2
.github/workflows/create_test_report.yml
vendored
|
|
@ -58,7 +58,7 @@ jobs:
|
|||
path: '.'
|
||||
|
||||
- name: Comment on PR
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
|
|
|
|||
|
|
@ -15,11 +15,13 @@ jobs:
|
|||
runs-on: ubuntu-20.04
|
||||
if: github.repository == 'microsoft/playwright'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Create GitHub issue
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.REPOSITORY_DISPATCH_PERSONAL_ACCESS_TOKEN }}
|
||||
script: |
|
||||
const currentPlaywrightVersion = require('./package.json').version.match(/\d+\.\d+/)[0];
|
||||
const { data } = await github.rest.git.getCommit({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
|
|
@ -27,10 +29,10 @@ jobs:
|
|||
});
|
||||
const commitHeader = data.message.split('\n')[0];
|
||||
|
||||
const title = '[Ports]: Backport client side changes';
|
||||
const title = '[Ports]: Backport client side changes for ' + currentPlaywrightVersion;
|
||||
for (const repo of ['playwright-python', 'playwright-java', 'playwright-dotnet']) {
|
||||
const { data: issuesData } = await github.rest.search.issuesAndPullRequests({
|
||||
q: `is:issue is:open repo:microsoft/${repo} in:title "${title}"`
|
||||
q: `is:issue is:open repo:microsoft/${repo} in:title "${title}" author:playwrightmachine"`
|
||||
})
|
||||
let issueNumber = null;
|
||||
let issueBody = '';
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
git commit -m "feat(${{ github.event.client_payload.browser }}): roll to r${{ github.event.client_payload.revision }}"
|
||||
git push origin $BRANCH_NAME
|
||||
- name: Create Pull Request
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.REPOSITORY_DISPATCH_PERSONAL_ACCESS_TOKEN }}
|
||||
script: |
|
||||
|
|
|
|||
2
.github/workflows/roll_driver_nodejs.yml
vendored
2
.github/workflows/roll_driver_nodejs.yml
vendored
|
|
@ -35,7 +35,7 @@ jobs:
|
|||
git push origin $BRANCH_NAME
|
||||
- name: Create Pull Request
|
||||
if: ${{ steps.prepare-branch.outputs.HAS_CHANGES == '1' }}
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.REPOSITORY_DISPATCH_PERSONAL_ACCESS_TOKEN }}
|
||||
script: |
|
||||
|
|
|
|||
|
|
@ -202,6 +202,12 @@ Prevents automatic playwright driver installation on attach. Assumes that the dr
|
|||
Optional device serial number to launch the browser on. If not specified, it will
|
||||
throw if multiple devices are connected.
|
||||
|
||||
### option: Android.launchServer.host
|
||||
* since: v1.45
|
||||
- `host` <[string]>
|
||||
|
||||
Host to use for the web socket. It is optional and if it is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise. Consider hardening it with picking a specific interface.
|
||||
|
||||
### option: Android.launchServer.port
|
||||
* since: v1.28
|
||||
- `port` <[int]>
|
||||
|
|
|
|||
|
|
@ -31,3 +31,5 @@ Browser websocket url.
|
|||
|
||||
Browser websocket endpoint which can be used as an argument to [`method: BrowserType.connect`] to establish connection
|
||||
to the browser.
|
||||
|
||||
Note that if the listen `host` option in `launchServer` options is not specified, localhost will be output anyway, even if the actual listening address is an unspecified address.
|
||||
|
|
|
|||
|
|
@ -380,6 +380,12 @@ const { chromium } = require('playwright'); // Or 'webkit' or 'firefox'.
|
|||
### option: BrowserType.launchServer.logger = %%-browser-option-logger-%%
|
||||
* since: v1.8
|
||||
|
||||
### option: BrowserType.launchServer.host
|
||||
* since: v1.45
|
||||
- `host` <[string]>
|
||||
|
||||
Host to use for the web socket. It is optional and if it is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise. Consider hardening it with picking a specific interface.
|
||||
|
||||
### option: BrowserType.launchServer.port
|
||||
* since: v1.8
|
||||
- `port` <[int]>
|
||||
|
|
|
|||
|
|
@ -127,8 +127,6 @@ When you have finished interacting with the page, press the **record** button to
|
|||
|
||||
Use the **clear** button to clear the code to start recording again. Once finished close the Playwright inspector window or stop the terminal command.
|
||||
|
||||
To learn more about generating tests check out or detailed guide on [Codegen](./codegen.md).
|
||||
|
||||
### Generating locators
|
||||
You can generate [locators](/locators.md) with the test generator.
|
||||
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ export default defineConfig({
|
|||
* since: v1.10
|
||||
- type: ?<[RegExp]|[Array]<[RegExp]>>
|
||||
|
||||
Filter to only run tests with a title matching one of the patterns. For example, passing `grep: /cart/` should only run tests with "cart" in the title. Also available in the [command line](../test-cli.md) with the `-g` option. The regular expression will be tested against the string that consists of the test file name, `test.describe` name (if any) and the test name divided by spaces, e.g. `my-test.spec.ts my-suite my-test`.
|
||||
Filter to only run tests with a title matching one of the patterns. For example, passing `grep: /cart/` should only run tests with "cart" in the title. Also available in the [command line](../test-cli.md) with the `-g` option. The regular expression will be tested against the string that consists of the project name, the test file name, the `test.describe` name (if any), the test name and the test tags divided by spaces, e.g. `chromium my-test.spec.ts my-suite my-test`.
|
||||
|
||||
`grep` option is also useful for [tagging tests](../test-annotations.md#tag-tests).
|
||||
|
||||
|
|
|
|||
|
|
@ -559,7 +559,7 @@ Whether to record trace for each test. Defaults to `'off'`.
|
|||
* `'on-first-retry'`: Record trace only when retrying a test for the first time.
|
||||
* `'on-all-retries'`: Record trace only when retrying a test.
|
||||
* `'retain-on-failure'`: Record trace for each test. When test run passes, remove the recorded trace.
|
||||
* `'retain-on-first-failure'`: Record trace for the first run of each test, but not for retires. When test run passes, remove the recorded trace.
|
||||
* `'retain-on-first-failure'`: Record trace for the first run of each test, but not for retries. When test run passes, remove the recorded trace.
|
||||
|
||||
For more control, pass an object that specifies `mode` and trace features to enable.
|
||||
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ You can configure entire test project to concurrently run all tests in all files
|
|||
* since: v1.10
|
||||
- type: ?<[RegExp]|[Array]<[RegExp]>>
|
||||
|
||||
Filter to only run tests with a title matching one of the patterns. For example, passing `grep: /cart/` should only run tests with "cart" in the title. Also available globally and in the [command line](../test-cli.md) with the `-g` option. The regular expression will be tested against the string that consists of the test file name, `test.describe` name (if any) and the test name divided by spaces, e.g. `my-test.spec.ts my-suite my-test`.
|
||||
Filter to only run tests with a title matching one of the patterns. For example, passing `grep: /cart/` should only run tests with "cart" in the title. Also available globally and in the [command line](../test-cli.md) with the `-g` option. The regular expression will be tested against the string that consists of the project name, the test file name, the `test.describe` name (if any), the test name and the test tags divided by spaces, e.g. `chromium my-test.spec.ts my-suite my-test`.
|
||||
|
||||
`grep` option is also useful for [tagging tests](../test-annotations.md#tag-tests).
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,6 @@ Complete set of Playwright Test options is available in the [configuration file]
|
|||
| `--reporter <reporter>` | Choose a reporter: minimalist `dot`, concise `line` or detailed `list`. See [reporters](./test-reporters.md) for more information. You can also pass a path to a [custom reporter](./test-reporters.md#custom-reporters) file. |
|
||||
| `--retries <number>` | The maximum number of [retries](./test-retries.md#retries) for flaky tests, defaults to zero (no retries). |
|
||||
| `--shard <shard>` | [Shard](./test-parallel.md#shard-tests-between-multiple-machines) tests and execute only selected shard, specified in the form `current/all`, 1-based, for example `3/5`.|
|
||||
| `--tag <tag>` | Only run tests with a tag matching this tag expression. Learn more about [tagging](./test-annotations.md#tag-tests). |
|
||||
| `--timeout <number>` | Maximum timeout in milliseconds for each test, defaults to 30 seconds. Learn more about [various timeouts](./test-timeouts.md).|
|
||||
| `--trace <mode>` | Force tracing mode, can be `on`, `off`, `on-first-retry`, `on-all-retries`, `retain-on-failure` |
|
||||
| `--update-snapshots` or `-u` | Whether to update [snapshots](./test-snapshots.md) with actual results instead of comparing them. Use this when snapshot expectations have changed.|
|
||||
|
|
|
|||
|
|
@ -926,9 +926,9 @@ export default defineConfig<MyOptions>({
|
|||
Each fixture has a setup and teardown phase separated by the `await use()` call in the fixture. Setup is executed before the fixture is used by the test/hook, and teardown is executed when the fixture will not be used by the test/hook anymore.
|
||||
|
||||
Fixtures follow these rules to determine the execution order:
|
||||
* When fixture A depends on fixture B: B is always set up before A and teared down after A.
|
||||
* When fixture A depends on fixture B: B is always set up before A and torn down after A.
|
||||
* Non-automatic fixtures are executed lazily, only when the test/hook needs them.
|
||||
* Test-scoped fixtures are teared down after each test, while worker-scoped fixtures are only teared down when the worker process executing tests is shutdown.
|
||||
* Test-scoped fixtures are torn down after each test, while worker-scoped fixtures are only torn down when the worker process executing tests is shutdown.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
|
|
@ -1036,8 +1036,8 @@ Normally, if all tests pass and no errors are thrown, the order of execution is
|
|||
* `beforeEach` runs.
|
||||
* `first test` runs.
|
||||
* `afterEach` runs.
|
||||
* `page` teardown because it is a test-scoped fixture and should be teared down after the test finishes.
|
||||
* `autoTestFixture` teardown because it is a test-scoped fixture and should be teared down after the test finishes.
|
||||
* `page` teardown because it is a test-scoped fixture and should be torn down after the test finishes.
|
||||
* `autoTestFixture` teardown because it is a test-scoped fixture and should be torn down after the test finishes.
|
||||
* `second test` section:
|
||||
* `autoTestFixture` setup because automatic test fixtures are always set up before test and `beforeEach` hooks.
|
||||
* `page` setup because it is required in `beforeEach` hook.
|
||||
|
|
@ -1046,20 +1046,20 @@ Normally, if all tests pass and no errors are thrown, the order of execution is
|
|||
* `testFixture` setup because it is required by the `second test`.
|
||||
* `second test` runs.
|
||||
* `afterEach` runs.
|
||||
* `testFixture` teardown because it is a test-scoped fixture and should be teared down after the test finishes.
|
||||
* `page` teardown because it is a test-scoped fixture and should be teared down after the test finishes.
|
||||
* `autoTestFixture` teardown because it is a test-scoped fixture and should be teared down after the test finishes.
|
||||
* `testFixture` teardown because it is a test-scoped fixture and should be torn down after the test finishes.
|
||||
* `page` teardown because it is a test-scoped fixture and should be torn down after the test finishes.
|
||||
* `autoTestFixture` teardown because it is a test-scoped fixture and should be torn down after the test finishes.
|
||||
* `afterAll` and worker teardown section:
|
||||
* `afterAll` runs.
|
||||
* `workerFixture` teardown because it is a workers-scoped fixture and should be teared down once at the end.
|
||||
* `autoWorkerFixture` teardown because it is a workers-scoped fixture and should be teared down once at the end.
|
||||
* `browser` teardown because it is a workers-scoped fixture and should be teared down once at the end.
|
||||
* `workerFixture` teardown because it is a workers-scoped fixture and should be torn down once at the end.
|
||||
* `autoWorkerFixture` teardown because it is a workers-scoped fixture and should be torn down once at the end.
|
||||
* `browser` teardown because it is a workers-scoped fixture and should be torn down once at the end.
|
||||
|
||||
A few observations:
|
||||
* `page` and `autoTestFixture` are set up and teared down for each test, as test-scoped fixtures.
|
||||
* `page` and `autoTestFixture` are set up and torn down for each test, as test-scoped fixtures.
|
||||
* `unusedFixture` is never set up because it is not used by any tests/hooks.
|
||||
* `testFixture` depends on `workerFixture` and triggers its setup.
|
||||
* `workerFixture` is lazily set up before the second test, but teared down once during worker shutdown, as a worker-scoped fixture.
|
||||
* `workerFixture` is lazily set up before the second test, but torn down once during worker shutdown, as a worker-scoped fixture.
|
||||
* `autoWorkerFixture` is set up for `beforeAll` hook, but `autoTestFixture` is not.
|
||||
|
||||
## Combine custom fixtures from multiple modules
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
},
|
||||
{
|
||||
"name": "chromium-tip-of-tree",
|
||||
"revision": "1223",
|
||||
"revision": "1226",
|
||||
"installByDefault": false,
|
||||
"browserVersion": "127.0.6492.0"
|
||||
"browserVersion": "127.0.6505.0"
|
||||
},
|
||||
{
|
||||
"name": "firefox",
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
},
|
||||
{
|
||||
"name": "webkit",
|
||||
"revision": "2012",
|
||||
"revision": "2013",
|
||||
"installByDefault": true,
|
||||
"revisionOverrides": {
|
||||
"mac10.14": "1446",
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export class AndroidServerLauncherImpl {
|
|||
|
||||
// 2. Start the server
|
||||
const server = new PlaywrightServer({ mode: 'launchServer', path, maxConnections: 1, preLaunchedAndroidDevice: device });
|
||||
const wsEndpoint = await server.listen(options.port);
|
||||
const wsEndpoint = await server.listen(options.port, options.host);
|
||||
|
||||
// 3. Return the BrowserServer interface
|
||||
const browserServer = new ws.EventEmitter() as (BrowserServer & WebSocketEventEmitter);
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher {
|
|||
|
||||
// 2. Start the server
|
||||
const server = new PlaywrightServer({ mode: 'launchServer', path, maxConnections: Infinity, preLaunchedBrowser: browser, preLaunchedSocksProxy: socksProxy });
|
||||
const wsEndpoint = await server.listen(options.port);
|
||||
const wsEndpoint = await server.listen(options.port, options.host);
|
||||
|
||||
// 3. Return the BrowserServer interface
|
||||
const browserServer = new ws.EventEmitter() as (BrowserServer & WebSocketEventEmitter);
|
||||
|
|
|
|||
|
|
@ -235,11 +235,11 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
|||
context.setDefaultTimeout(this._defaultContextTimeout);
|
||||
if (this._defaultContextNavigationTimeout !== undefined)
|
||||
context.setDefaultNavigationTimeout(this._defaultContextNavigationTimeout);
|
||||
await this._instrumentation.onDidCreateBrowserContext(context);
|
||||
await this._instrumentation.runAfterCreateBrowserContext(context);
|
||||
}
|
||||
|
||||
async _willCloseContext(context: BrowserContext) {
|
||||
this._contexts.delete(context);
|
||||
await this._instrumentation.onWillCloseBrowserContext(context);
|
||||
await this._instrumentation.runBeforeCloseBrowserContext(context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,21 +24,23 @@ export interface ClientInstrumentation {
|
|||
removeAllListeners(): void;
|
||||
onApiCallBegin(apiCall: string, params: Record<string, any>, frames: StackFrame[], userData: any, out: { stepId?: string }): void;
|
||||
onApiCallEnd(userData: any, error?: Error): void;
|
||||
onDidCreateBrowserContext(context: BrowserContext): Promise<void>;
|
||||
onDidCreateRequestContext(context: APIRequestContext): Promise<void>;
|
||||
onWillPause(): void;
|
||||
onWillCloseBrowserContext(context: BrowserContext): Promise<void>;
|
||||
onWillCloseRequestContext(context: APIRequestContext): Promise<void>;
|
||||
|
||||
runAfterCreateBrowserContext(context: BrowserContext): Promise<void>;
|
||||
runAfterCreateRequestContext(context: APIRequestContext): Promise<void>;
|
||||
runBeforeCloseBrowserContext(context: BrowserContext): Promise<void>;
|
||||
runBeforeCloseRequestContext(context: APIRequestContext): Promise<void>;
|
||||
}
|
||||
|
||||
export interface ClientInstrumentationListener {
|
||||
onApiCallBegin?(apiName: string, params: Record<string, any>, frames: StackFrame[], userData: any, out: { stepId?: string }): void;
|
||||
onApiCallEnd?(userData: any, error?: Error): void;
|
||||
onDidCreateBrowserContext?(context: BrowserContext): Promise<void>;
|
||||
onDidCreateRequestContext?(context: APIRequestContext): Promise<void>;
|
||||
onWillPause?(): void;
|
||||
onWillCloseBrowserContext?(context: BrowserContext): Promise<void>;
|
||||
onWillCloseRequestContext?(context: APIRequestContext): Promise<void>;
|
||||
|
||||
runAfterCreateBrowserContext?(context: BrowserContext): Promise<void>;
|
||||
runAfterCreateRequestContext?(context: APIRequestContext): Promise<void>;
|
||||
runBeforeCloseBrowserContext?(context: BrowserContext): Promise<void>;
|
||||
runBeforeCloseRequestContext?(context: APIRequestContext): Promise<void>;
|
||||
}
|
||||
|
||||
export function createInstrumentation(): ClientInstrumentation {
|
||||
|
|
@ -53,12 +55,19 @@ export function createInstrumentation(): ClientInstrumentation {
|
|||
return (listener: ClientInstrumentationListener) => listeners.splice(listeners.indexOf(listener), 1);
|
||||
if (prop === 'removeAllListeners')
|
||||
return () => listeners.splice(0, listeners.length);
|
||||
if (!prop.startsWith('on'))
|
||||
return obj[prop];
|
||||
return async (...params: any[]) => {
|
||||
for (const listener of listeners)
|
||||
await (listener as any)[prop]?.(...params);
|
||||
};
|
||||
if (prop.startsWith('run')) {
|
||||
return async (...params: any[]) => {
|
||||
for (const listener of listeners)
|
||||
await (listener as any)[prop]?.(...params);
|
||||
};
|
||||
}
|
||||
if (prop.startsWith('on')) {
|
||||
return (...params: any[]) => {
|
||||
for (const listener of listeners)
|
||||
(listener as any)[prop]?.(...params);
|
||||
};
|
||||
}
|
||||
return obj[prop];
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ export class APIRequest implements api.APIRequest {
|
|||
this._contexts.add(context);
|
||||
context._request = this;
|
||||
context._tracing._tracesDir = tracesDir;
|
||||
await context._instrumentation.onDidCreateRequestContext(context);
|
||||
await context._instrumentation.runAfterCreateRequestContext(context);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
|
@ -102,7 +102,7 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
|
|||
|
||||
async dispose(options: { reason?: string } = {}): Promise<void> {
|
||||
this._closeReason = options.reason;
|
||||
await this._instrumentation.onWillCloseRequestContext(this);
|
||||
await this._instrumentation.runBeforeCloseRequestContext(this);
|
||||
try {
|
||||
await this._channel.dispose(options);
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ export type LaunchServerOptions = {
|
|||
},
|
||||
downloadsPath?: string,
|
||||
chromiumSandbox?: boolean,
|
||||
host?: string,
|
||||
port?: number,
|
||||
wsPath?: string,
|
||||
logger?: Logger,
|
||||
|
|
@ -122,6 +123,7 @@ export type LaunchAndroidServerOptions = {
|
|||
adbHost?: string,
|
||||
adbPort?: number,
|
||||
omitDriverInstall?: boolean,
|
||||
host?: string,
|
||||
port?: number,
|
||||
wsPath?: string,
|
||||
};
|
||||
|
|
|
|||
17
packages/playwright-core/types/types.d.ts
vendored
17
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -13745,6 +13745,13 @@ export interface BrowserType<Unused = {}> {
|
|||
*/
|
||||
headless?: boolean;
|
||||
|
||||
/**
|
||||
* Host to use for the web socket. It is optional and if it is omitted, the server will accept connections on the
|
||||
* unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise. Consider
|
||||
* hardening it with picking a specific interface.
|
||||
*/
|
||||
host?: string;
|
||||
|
||||
/**
|
||||
* If `true`, Playwright does not pass its own configurations args and only uses the ones from `args`. If an array is
|
||||
* given, then filters out the given default arguments. Dangerous option; use with care. Defaults to `false`.
|
||||
|
|
@ -14621,6 +14628,13 @@ export interface Android {
|
|||
*/
|
||||
deviceSerialNumber?: string;
|
||||
|
||||
/**
|
||||
* Host to use for the web socket. It is optional and if it is omitted, the server will accept connections on the
|
||||
* unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise. Consider
|
||||
* hardening it with picking a specific interface.
|
||||
*/
|
||||
host?: string;
|
||||
|
||||
/**
|
||||
* Prevents automatic playwright driver installation on attach. Assumes that the drivers have been installed already.
|
||||
*/
|
||||
|
|
@ -17201,6 +17215,9 @@ export interface BrowserServer {
|
|||
* Browser websocket endpoint which can be used as an argument to
|
||||
* [browserType.connect(wsEndpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect)
|
||||
* to establish connection to the browser.
|
||||
*
|
||||
* Note that if the listen `host` option in `launchServer` options is not specified, localhost will be output anyway,
|
||||
* even if the actual listening address is an unspecified address.
|
||||
*/
|
||||
wsEndpoint(): string;
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ async function innerMount(page: Page, componentRef: JsxComponent | ImportRef, op
|
|||
|
||||
const selector = await page.evaluate(async ({ component, hooksConfig }) => {
|
||||
component = await window.__pwUnwrapObject(component);
|
||||
hooksConfig = await window.__pwUnwrapObject(hooksConfig);
|
||||
let rootElement = document.getElementById('root');
|
||||
if (!rootElement) {
|
||||
rootElement = document.createElement('div');
|
||||
|
|
|
|||
|
|
@ -14,11 +14,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
type JsonPrimitive = string | number | boolean | null;
|
||||
type JsonValue = JsonPrimitive | JsonObject | JsonArray;
|
||||
type JsonArray = JsonValue[];
|
||||
export type JsonObject = { [Key in string]?: JsonValue };
|
||||
|
||||
export type JsxComponent = {
|
||||
__pw_type: 'jsx',
|
||||
type: any,
|
||||
|
|
@ -47,10 +42,10 @@ declare global {
|
|||
playwrightMount(component: Component, rootElement: Element, hooksConfig?: any): Promise<void>;
|
||||
playwrightUnmount(rootElement: Element): Promise<void>;
|
||||
playwrightUpdate(rootElement: Element, component: Component): Promise<void>;
|
||||
__pw_hooks_before_mount?: (<HooksConfig extends JsonObject = JsonObject>(
|
||||
__pw_hooks_before_mount?: (<HooksConfig>(
|
||||
params: { hooksConfig?: HooksConfig; [key: string]: any }
|
||||
) => Promise<any>)[];
|
||||
__pw_hooks_after_mount?: (<HooksConfig extends JsonObject = JsonObject>(
|
||||
__pw_hooks_after_mount?: (<HooksConfig>(
|
||||
params: { hooksConfig?: HooksConfig; [key: string]: any }
|
||||
) => Promise<void>)[];
|
||||
// Can't start with __pw due to core reuse bindings logic for __pw*.
|
||||
|
|
|
|||
6
packages/playwright-ct-react/hooks.d.ts
vendored
6
packages/playwright-ct-react/hooks.d.ts
vendored
|
|
@ -14,11 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
|
||||
|
||||
export declare function beforeMount<HooksConfig extends JsonObject>(
|
||||
export declare function beforeMount<HooksConfig>(
|
||||
callback: (params: { hooksConfig?: HooksConfig; App: () => JSX.Element }) => Promise<void | JSX.Element>
|
||||
): void;
|
||||
export declare function afterMount<HooksConfig extends JsonObject>(
|
||||
export declare function afterMount<HooksConfig>(
|
||||
callback: (params: { hooksConfig?: HooksConfig }) => Promise<void>
|
||||
): void;
|
||||
|
|
|
|||
5
packages/playwright-ct-react/index.d.ts
vendored
5
packages/playwright-ct-react/index.d.ts
vendored
|
|
@ -15,10 +15,9 @@
|
|||
*/
|
||||
|
||||
import type { Locator } from 'playwright/test';
|
||||
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
|
||||
import type { TestType } from '@playwright/experimental-ct-core';
|
||||
|
||||
export interface MountOptions<HooksConfig extends JsonObject> {
|
||||
export interface MountOptions<HooksConfig> {
|
||||
hooksConfig?: HooksConfig;
|
||||
}
|
||||
|
||||
|
|
@ -28,7 +27,7 @@ export interface MountResult extends Locator {
|
|||
}
|
||||
|
||||
export const test: TestType<{
|
||||
mount<HooksConfig extends JsonObject>(
|
||||
mount<HooksConfig>(
|
||||
component: JSX.Element,
|
||||
options?: MountOptions<HooksConfig>
|
||||
): Promise<MountResult>;
|
||||
|
|
|
|||
6
packages/playwright-ct-react17/hooks.d.ts
vendored
6
packages/playwright-ct-react17/hooks.d.ts
vendored
|
|
@ -14,11 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
|
||||
|
||||
export declare function beforeMount<HooksConfig extends JsonObject>(
|
||||
export declare function beforeMount<HooksConfig>(
|
||||
callback: (params: { hooksConfig?: HooksConfig; App: () => JSX.Element }) => Promise<void | JSX.Element>
|
||||
): void;
|
||||
export declare function afterMount<HooksConfig extends JsonObject>(
|
||||
export declare function afterMount<HooksConfig>(
|
||||
callback: (params: { hooksConfig?: HooksConfig }) => Promise<void>
|
||||
): void;
|
||||
|
|
|
|||
5
packages/playwright-ct-react17/index.d.ts
vendored
5
packages/playwright-ct-react17/index.d.ts
vendored
|
|
@ -15,10 +15,9 @@
|
|||
*/
|
||||
|
||||
import type { Locator } from 'playwright/test';
|
||||
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
|
||||
import type { TestType } from '@playwright/experimental-ct-core';
|
||||
|
||||
export interface MountOptions<HooksConfig extends JsonObject> {
|
||||
export interface MountOptions<HooksConfig> {
|
||||
hooksConfig?: HooksConfig;
|
||||
}
|
||||
|
||||
|
|
@ -28,7 +27,7 @@ export interface MountResult extends Locator {
|
|||
}
|
||||
|
||||
export const test: TestType<{
|
||||
mount<HooksConfig extends JsonObject>(
|
||||
mount<HooksConfig>(
|
||||
component: JSX.Element,
|
||||
options?: MountOptions<HooksConfig>
|
||||
): Promise<MountResult>;
|
||||
|
|
|
|||
7
packages/playwright-ct-solid/hooks.d.ts
vendored
7
packages/playwright-ct-solid/hooks.d.ts
vendored
|
|
@ -14,12 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { JSXElement } from "solid-js";
|
||||
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
|
||||
import { JSXElement } from 'solid-js';
|
||||
|
||||
export declare function beforeMount<HooksConfig extends JsonObject>(
|
||||
export declare function beforeMount<HooksConfig>(
|
||||
callback: (params: { hooksConfig?: HooksConfig, App: () => JSXElement }) => Promise<void | JSXElement>
|
||||
): void;
|
||||
export declare function afterMount<HooksConfig extends JsonObject>(
|
||||
export declare function afterMount<HooksConfig>(
|
||||
callback: (params: { hooksConfig?: HooksConfig }) => Promise<void>
|
||||
): void;
|
||||
|
|
|
|||
5
packages/playwright-ct-solid/index.d.ts
vendored
5
packages/playwright-ct-solid/index.d.ts
vendored
|
|
@ -15,10 +15,9 @@
|
|||
*/
|
||||
|
||||
import type { Locator } from 'playwright/test';
|
||||
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
|
||||
import type { TestType } from '@playwright/experimental-ct-core';
|
||||
|
||||
export interface MountOptions<HooksConfig extends JsonObject> {
|
||||
export interface MountOptions<HooksConfig> {
|
||||
hooksConfig?: HooksConfig;
|
||||
}
|
||||
|
||||
|
|
@ -28,7 +27,7 @@ export interface MountResult extends Locator {
|
|||
}
|
||||
|
||||
export const test: TestType<{
|
||||
mount<HooksConfig extends JsonObject>(
|
||||
mount<HooksConfig>(
|
||||
component: JSX.Element,
|
||||
options?: MountOptions<HooksConfig>
|
||||
): Promise<MountResult>;
|
||||
|
|
|
|||
5
packages/playwright-ct-svelte/hooks.d.ts
vendored
5
packages/playwright-ct-svelte/hooks.d.ts
vendored
|
|
@ -15,15 +15,14 @@
|
|||
*/
|
||||
|
||||
import type { ComponentConstructorOptions, SvelteComponent } from 'svelte';
|
||||
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
|
||||
|
||||
export declare function beforeMount<HooksConfig extends JsonObject>(
|
||||
export declare function beforeMount<HooksConfig>(
|
||||
callback: (params: {
|
||||
hooksConfig?: HooksConfig,
|
||||
App: new (options: Partial<ComponentConstructorOptions>) => SvelteComponent
|
||||
}) => Promise<SvelteComponent | void>
|
||||
): void;
|
||||
export declare function afterMount<HooksConfig extends JsonObject>(
|
||||
export declare function afterMount<HooksConfig>(
|
||||
callback: (params: {
|
||||
hooksConfig?: HooksConfig;
|
||||
svelteComponent: SvelteComponent;
|
||||
|
|
|
|||
5
packages/playwright-ct-svelte/index.d.ts
vendored
5
packages/playwright-ct-svelte/index.d.ts
vendored
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
import type { Locator } from 'playwright/test';
|
||||
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
|
||||
import type { SvelteComponent, ComponentProps } from 'svelte/types/runtime';
|
||||
import type { TestType } from '@playwright/experimental-ct-core';
|
||||
|
||||
|
|
@ -23,7 +22,7 @@ type ComponentSlot = string | string[];
|
|||
type ComponentSlots = Record<string, ComponentSlot> & { default?: ComponentSlot };
|
||||
type ComponentEvents = Record<string, Function>;
|
||||
|
||||
export interface MountOptions<HooksConfig extends JsonObject, Component extends SvelteComponent> {
|
||||
export interface MountOptions<HooksConfig, Component extends SvelteComponent> {
|
||||
props?: ComponentProps<Component>;
|
||||
slots?: ComponentSlots;
|
||||
on?: ComponentEvents;
|
||||
|
|
@ -39,7 +38,7 @@ export interface MountResult<Component extends SvelteComponent> extends Locator
|
|||
}
|
||||
|
||||
export const test: TestType<{
|
||||
mount<HooksConfig extends JsonObject, Component extends SvelteComponent = SvelteComponent>(
|
||||
mount<HooksConfig, Component extends SvelteComponent = SvelteComponent>(
|
||||
component: new (...args: any[]) => Component,
|
||||
options?: MountOptions<HooksConfig, Component>
|
||||
): Promise<MountResult<Component>>;
|
||||
|
|
|
|||
5
packages/playwright-ct-vue/hooks.d.ts
vendored
5
packages/playwright-ct-vue/hooks.d.ts
vendored
|
|
@ -15,12 +15,11 @@
|
|||
*/
|
||||
|
||||
import type { App, ComponentPublicInstance } from 'vue';
|
||||
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
|
||||
|
||||
export declare function beforeMount<HooksConfig extends JsonObject>(
|
||||
export declare function beforeMount<HooksConfig>(
|
||||
callback: (params: { app: App; hooksConfig?: HooksConfig }) => Promise<void>
|
||||
): void;
|
||||
export declare function afterMount<HooksConfig extends JsonObject>(
|
||||
export declare function afterMount<HooksConfig>(
|
||||
callback: (params: {
|
||||
app: App;
|
||||
hooksConfig?: HooksConfig;
|
||||
|
|
|
|||
9
packages/playwright-ct-vue/index.d.ts
vendored
9
packages/playwright-ct-vue/index.d.ts
vendored
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
import type { Locator } from 'playwright/test';
|
||||
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
|
||||
import type { TestType } from '@playwright/experimental-ct-core';
|
||||
|
||||
type ComponentSlot = string | string[];
|
||||
|
|
@ -29,14 +28,14 @@ type ComponentProps<T> =
|
|||
T extends (props: infer P, ...args: any) => any ? P :
|
||||
{};
|
||||
|
||||
export interface MountOptions<HooksConfig extends JsonObject, Component> {
|
||||
export interface MountOptions<HooksConfig, Component> {
|
||||
props?: ComponentProps<Component>;
|
||||
slots?: ComponentSlots;
|
||||
on?: ComponentEvents;
|
||||
hooksConfig?: HooksConfig;
|
||||
}
|
||||
|
||||
export interface MountOptionsJsx<HooksConfig extends JsonObject> {
|
||||
export interface MountOptionsJsx<HooksConfig> {
|
||||
hooksConfig?: HooksConfig;
|
||||
}
|
||||
|
||||
|
|
@ -55,11 +54,11 @@ export interface MountResultJsx extends Locator {
|
|||
}
|
||||
|
||||
export const test: TestType<{
|
||||
mount<HooksConfig extends JsonObject>(
|
||||
mount<HooksConfig>(
|
||||
component: JSX.Element,
|
||||
options: MountOptionsJsx<HooksConfig>
|
||||
): Promise<MountResultJsx>;
|
||||
mount<HooksConfig extends JsonObject, Component = unknown>(
|
||||
mount<HooksConfig, Component = unknown>(
|
||||
component: Component,
|
||||
options?: MountOptions<HooksConfig, Component>
|
||||
): Promise<MountResult<Component>>;
|
||||
|
|
|
|||
15
packages/playwright-ct-vue2/hooks.d.ts
vendored
15
packages/playwright-ct-vue2/hooks.d.ts
vendored
|
|
@ -14,17 +14,16 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentOptions } from 'vue';
|
||||
import { CombinedVueInstance, Vue, VueConstructor } from 'vue/types/vue';
|
||||
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
|
||||
import type { ComponentOptions } from 'vue';
|
||||
import type { CombinedVueInstance, Vue, VueConstructor } from 'vue/types/vue';
|
||||
|
||||
export declare function beforeMount<HooksConfig extends JsonObject>(
|
||||
callback: (params: {
|
||||
hooksConfig?: HooksConfig,
|
||||
Vue: VueConstructor<Vue>,
|
||||
export declare function beforeMount<HooksConfig>(
|
||||
callback: (params: {
|
||||
hooksConfig?: HooksConfig,
|
||||
Vue: VueConstructor<Vue>,
|
||||
}) => Promise<void | ComponentOptions<Vue> & Record<string, unknown>>
|
||||
): void;
|
||||
export declare function afterMount<HooksConfig extends JsonObject>(
|
||||
export declare function afterMount<HooksConfig>(
|
||||
callback: (params: {
|
||||
hooksConfig?: HooksConfig;
|
||||
instance: CombinedVueInstance<
|
||||
|
|
|
|||
9
packages/playwright-ct-vue2/index.d.ts
vendored
9
packages/playwright-ct-vue2/index.d.ts
vendored
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
import type { Locator } from 'playwright/test';
|
||||
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
|
||||
import type { TestType } from '@playwright/experimental-ct-core';
|
||||
|
||||
type Slot = string | string[];
|
||||
|
|
@ -29,14 +28,14 @@ type ComponentProps<T> =
|
|||
T extends (props: infer P, ...args: any) => any ? P :
|
||||
{};
|
||||
|
||||
export interface MountOptions<HooksConfig extends JsonObject, Component> {
|
||||
export interface MountOptions<HooksConfig, Component> {
|
||||
props?: ComponentProps<Component>;
|
||||
slots?: ComponentSlots;
|
||||
on?: ComponentEvents;
|
||||
hooksConfig?: HooksConfig;
|
||||
}
|
||||
|
||||
export interface MountOptionsJsx<HooksConfig extends JsonObject> {
|
||||
export interface MountOptionsJsx<HooksConfig> {
|
||||
hooksConfig?: HooksConfig;
|
||||
}
|
||||
|
||||
|
|
@ -55,11 +54,11 @@ export interface MountResultJsx extends Locator {
|
|||
}
|
||||
|
||||
export const test: TestType<{
|
||||
mount<HooksConfig extends JsonObject>(
|
||||
mount<HooksConfig>(
|
||||
component: JSX.Element,
|
||||
options?: MountOptionsJsx<HooksConfig>
|
||||
): Promise<MountResultJsx>;
|
||||
mount<HooksConfig extends JsonObject, Component = unknown>(
|
||||
mount<HooksConfig, Component = unknown>(
|
||||
component: Component,
|
||||
options?: MountOptions<HooksConfig, Component>
|
||||
): Promise<MountResult<Component>>;
|
||||
|
|
|
|||
|
|
@ -268,19 +268,19 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
|||
onWillPause: () => {
|
||||
currentTestInfo()?.setTimeout(0);
|
||||
},
|
||||
onDidCreateBrowserContext: async (context: BrowserContext) => {
|
||||
runAfterCreateBrowserContext: async (context: BrowserContext) => {
|
||||
await artifactsRecorder?.didCreateBrowserContext(context);
|
||||
const testInfo = currentTestInfo();
|
||||
if (testInfo)
|
||||
attachConnectedHeaderIfNeeded(testInfo, context.browser());
|
||||
},
|
||||
onDidCreateRequestContext: async (context: APIRequestContext) => {
|
||||
runAfterCreateRequestContext: async (context: APIRequestContext) => {
|
||||
await artifactsRecorder?.didCreateRequestContext(context);
|
||||
},
|
||||
onWillCloseBrowserContext: async (context: BrowserContext) => {
|
||||
runBeforeCloseBrowserContext: async (context: BrowserContext) => {
|
||||
await artifactsRecorder?.willCloseBrowserContext(context);
|
||||
},
|
||||
onWillCloseRequestContext: async (context: APIRequestContext) => {
|
||||
runBeforeCloseRequestContext: async (context: APIRequestContext) => {
|
||||
await artifactsRecorder?.willCloseRequestContext(context);
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ export class TeleReporterEmitter implements ReporterV2 {
|
|||
private _messageSink: (message: teleReceiver.JsonEvent) => void;
|
||||
private _rootDir!: string;
|
||||
private _emitterOptions: TeleReporterEmitterOptions;
|
||||
// In case there is blob reporter and UI mode, make sure one does override
|
||||
// the id assigned by the other.
|
||||
private readonly _idSymbol = Symbol('id');
|
||||
|
||||
constructor(messageSink: (message: teleReceiver.JsonEvent) => void, options: TeleReporterEmitterOptions = {}) {
|
||||
this._messageSink = messageSink;
|
||||
|
|
@ -55,7 +58,7 @@ export class TeleReporterEmitter implements ReporterV2 {
|
|||
}
|
||||
|
||||
onTestBegin(test: reporterTypes.TestCase, result: reporterTypes.TestResult): void {
|
||||
(result as any)[idSymbol] = createGuid();
|
||||
(result as any)[this._idSymbol] = createGuid();
|
||||
this._messageSink({
|
||||
method: 'onTestBegin',
|
||||
params: {
|
||||
|
|
@ -82,12 +85,12 @@ export class TeleReporterEmitter implements ReporterV2 {
|
|||
}
|
||||
|
||||
onStepBegin(test: reporterTypes.TestCase, result: reporterTypes.TestResult, step: reporterTypes.TestStep): void {
|
||||
(step as any)[idSymbol] = createGuid();
|
||||
(step as any)[this._idSymbol] = createGuid();
|
||||
this._messageSink({
|
||||
method: 'onStepBegin',
|
||||
params: {
|
||||
testId: test.id,
|
||||
resultId: (result as any)[idSymbol],
|
||||
resultId: (result as any)[this._idSymbol],
|
||||
step: this._serializeStepStart(step)
|
||||
}
|
||||
});
|
||||
|
|
@ -98,7 +101,7 @@ export class TeleReporterEmitter implements ReporterV2 {
|
|||
method: 'onStepEnd',
|
||||
params: {
|
||||
testId: test.id,
|
||||
resultId: (result as any)[idSymbol],
|
||||
resultId: (result as any)[this._idSymbol],
|
||||
step: this._serializeStepEnd(step)
|
||||
}
|
||||
});
|
||||
|
|
@ -126,7 +129,7 @@ export class TeleReporterEmitter implements ReporterV2 {
|
|||
const data = isBase64 ? chunk.toString('base64') : chunk;
|
||||
this._messageSink({
|
||||
method: 'onStdIO',
|
||||
params: { testId: test?.id, resultId: result ? (result as any)[idSymbol] : undefined, type, data, isBase64 }
|
||||
params: { testId: test?.id, resultId: result ? (result as any)[this._idSymbol] : undefined, type, data, isBase64 }
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -214,7 +217,7 @@ export class TeleReporterEmitter implements ReporterV2 {
|
|||
|
||||
private _serializeResultStart(result: reporterTypes.TestResult): teleReceiver.JsonTestResultStart {
|
||||
return {
|
||||
id: (result as any)[idSymbol],
|
||||
id: (result as any)[this._idSymbol],
|
||||
retry: result.retry,
|
||||
workerIndex: result.workerIndex,
|
||||
parallelIndex: result.parallelIndex,
|
||||
|
|
@ -224,7 +227,7 @@ export class TeleReporterEmitter implements ReporterV2 {
|
|||
|
||||
private _serializeResultEnd(result: reporterTypes.TestResult): teleReceiver.JsonTestResultEnd {
|
||||
return {
|
||||
id: (result as any)[idSymbol],
|
||||
id: (result as any)[this._idSymbol],
|
||||
duration: result.duration,
|
||||
status: result.status,
|
||||
errors: result.errors,
|
||||
|
|
@ -244,8 +247,8 @@ export class TeleReporterEmitter implements ReporterV2 {
|
|||
|
||||
private _serializeStepStart(step: reporterTypes.TestStep): teleReceiver.JsonTestStepStart {
|
||||
return {
|
||||
id: (step as any)[idSymbol],
|
||||
parentStepId: (step.parent as any)?.[idSymbol],
|
||||
id: (step as any)[this._idSymbol],
|
||||
parentStepId: (step.parent as any)?.[this._idSymbol],
|
||||
title: step.title,
|
||||
category: step.category,
|
||||
startTime: +step.startTime,
|
||||
|
|
@ -255,7 +258,7 @@ export class TeleReporterEmitter implements ReporterV2 {
|
|||
|
||||
private _serializeStepEnd(step: reporterTypes.TestStep): teleReceiver.JsonTestStepEnd {
|
||||
return {
|
||||
id: (step as any)[idSymbol],
|
||||
id: (step as any)[this._idSymbol],
|
||||
duration: step.duration,
|
||||
error: step.error,
|
||||
};
|
||||
|
|
@ -280,5 +283,3 @@ export class TeleReporterEmitter implements ReporterV2 {
|
|||
return path.relative(this._rootDir, absolutePath);
|
||||
}
|
||||
}
|
||||
|
||||
const idSymbol = Symbol('id');
|
||||
|
|
|
|||
12
packages/playwright/types/test.d.ts
vendored
12
packages/playwright/types/test.d.ts
vendored
|
|
@ -266,8 +266,9 @@ interface TestProject<TestArgs = {}, WorkerArgs = {}> {
|
|||
/**
|
||||
* Filter to only run tests with a title matching one of the patterns. For example, passing `grep: /cart/` should only
|
||||
* run tests with "cart" in the title. Also available globally and in the [command line](https://playwright.dev/docs/test-cli) with the `-g`
|
||||
* option. The regular expression will be tested against the string that consists of the test file name,
|
||||
* `test.describe` name (if any) and the test name divided by spaces, e.g. `my-test.spec.ts my-suite my-test`.
|
||||
* option. The regular expression will be tested against the string that consists of the project name, the test file
|
||||
* name, the `test.describe` name (if any), the test name and the test tags divided by spaces, e.g. `chromium
|
||||
* my-test.spec.ts my-suite my-test`.
|
||||
*
|
||||
* `grep` option is also useful for [tagging tests](https://playwright.dev/docs/test-annotations#tag-tests).
|
||||
*/
|
||||
|
|
@ -1130,8 +1131,9 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
|
|||
/**
|
||||
* Filter to only run tests with a title matching one of the patterns. For example, passing `grep: /cart/` should only
|
||||
* run tests with "cart" in the title. Also available in the [command line](https://playwright.dev/docs/test-cli) with the `-g` option. The
|
||||
* regular expression will be tested against the string that consists of the test file name, `test.describe` name (if
|
||||
* any) and the test name divided by spaces, e.g. `my-test.spec.ts my-suite my-test`.
|
||||
* regular expression will be tested against the string that consists of the project name, the test file name, the
|
||||
* `test.describe` name (if any), the test name and the test tags divided by spaces, e.g. `chromium my-test.spec.ts
|
||||
* my-suite my-test`.
|
||||
*
|
||||
* `grep` option is also useful for [tagging tests](https://playwright.dev/docs/test-annotations#tag-tests).
|
||||
*
|
||||
|
|
@ -5088,7 +5090,7 @@ export interface PlaywrightWorkerOptions {
|
|||
* - `'on-first-retry'`: Record trace only when retrying a test for the first time.
|
||||
* - `'on-all-retries'`: Record trace only when retrying a test.
|
||||
* - `'retain-on-failure'`: Record trace for each test. When test run passes, remove the recorded trace.
|
||||
* - `'retain-on-first-failure'`: Record trace for the first run of each test, but not for retires. When test run
|
||||
* - `'retain-on-first-failure'`: Record trace for the first run of each test, but not for retries. When test run
|
||||
* passes, remove the recorded trace.
|
||||
*
|
||||
* For more control, pass an object that specifies `mode` and trace features to enable.
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
const traceUrl = new URL(location.href).searchParams.get('trace');
|
||||
const params = new URLSearchParams();
|
||||
params.set('trace', traceUrl);
|
||||
await fetch('context?' + params.toString()).then(r => r.json());
|
||||
await fetch('contexts?' + params.toString()).then(r => r.json());
|
||||
await location.reload();
|
||||
})();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,17 @@ test('android.launchServer should connect to a device', async ({ playwright }) =
|
|||
await browserServer.close();
|
||||
});
|
||||
|
||||
test('android.launchServer should work with host', async ({ playwright }) => {
|
||||
const host = '0.0.0.0';
|
||||
const browserServer = await playwright._android.launchServer({ host });
|
||||
expect(browserServer.wsEndpoint()).toContain(String(host));
|
||||
const device = await playwright._android.connect(browserServer.wsEndpoint());
|
||||
const output = await device.shell('echo 123');
|
||||
expect(output.toString()).toBe('123\n');
|
||||
await device.close();
|
||||
await browserServer.close();
|
||||
});
|
||||
|
||||
test('android.launchServer should handle close event correctly', async ({ playwright }) => {
|
||||
const receivedEvents: string[] = [];
|
||||
const browserServer = await playwright._android.launchServer();
|
||||
|
|
|
|||
|
|
@ -4,14 +4,17 @@ import Button from '../src/components/Button.vue';
|
|||
import '../src/assets/index.css';
|
||||
|
||||
export type HooksConfig = {
|
||||
route?: string;
|
||||
routing?: boolean;
|
||||
components?: Record<string, any>;
|
||||
}
|
||||
|
||||
beforeMount<HooksConfig>(async ({ app, hooksConfig }) => {
|
||||
if (hooksConfig?.routing)
|
||||
if (hooksConfig?.routing)
|
||||
app.use(router as any); // TODO: remove any and fix the various installed conflicting Vue versions
|
||||
app.component('Button', Button);
|
||||
|
||||
for (const [name, component] of Object.entries(hooksConfig?.components || {}))
|
||||
app.component(name, component);
|
||||
|
||||
console.log(`Before mount: ${JSON.stringify(hooksConfig)}, app: ${!!app}`);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { test, expect } from '@playwright/experimental-ct-vue';
|
||||
import DefaultSlot from '@/components/DefaultSlot.vue';
|
||||
import NamedSlots from '@/components/NamedSlots.vue';
|
||||
import Button from '@/components/Button.vue';
|
||||
|
||||
test('render a default slot', async ({ mount }) => {
|
||||
const component = await mount(DefaultSlot, {
|
||||
|
|
@ -16,6 +17,9 @@ test('render a component as slot', async ({ mount }) => {
|
|||
slots: {
|
||||
default: '<Button title="Submit" />', // component is registered globally in /playwright/index.ts
|
||||
},
|
||||
hooksConfig: {
|
||||
components: { Button }
|
||||
}
|
||||
});
|
||||
await expect(component).toContainText('Submit');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { test, expect } from '@playwright/experimental-ct-vue';
|
||||
import DefaultSlot from '@/components/DefaultSlot.vue';
|
||||
import NamedSlots from '@/components/NamedSlots.vue';
|
||||
import Button from '@/components/Button.vue';
|
||||
|
||||
test('render a default slot', async ({ mount }) => {
|
||||
const component = await mount(DefaultSlot, {
|
||||
|
|
@ -16,6 +17,9 @@ test('render a component as slot', async ({ mount }) => {
|
|||
slots: {
|
||||
default: '<Button title="Submit" />', // component is registered globally in /playwright/index.ts
|
||||
},
|
||||
hooksConfig: {
|
||||
components: { Button }
|
||||
}
|
||||
});
|
||||
await expect(component).toContainText('Submit');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -26,6 +26,13 @@ it.describe('launch server', () => {
|
|||
await browserServer.close();
|
||||
});
|
||||
|
||||
it('should work with host', async ({ browserType }) => {
|
||||
const host = '0.0.0.0';
|
||||
const browserServer = await browserType.launchServer({ host });
|
||||
expect(browserServer.wsEndpoint()).toContain(String(host));
|
||||
await browserServer.close();
|
||||
});
|
||||
|
||||
it('should work with port', async ({ browserType }, testInfo) => {
|
||||
const port = 8800 + testInfo.workerIndex;
|
||||
const browserServer = await browserType.launchServer({ port });
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ test.beforeAll(async function recordTrace({ browser, browserName, browserType, s
|
|||
|
||||
// Go through instrumentation to exercise reentrant stack traces.
|
||||
const csi = {
|
||||
onWillCloseBrowserContext: async () => {
|
||||
runBeforeCloseBrowserContext: async () => {
|
||||
await page.hover('body');
|
||||
await page.close();
|
||||
traceFile = path.join(workerInfo.project.outputDir, String(workerInfo.workerIndex), browserName, 'trace.zip');
|
||||
|
|
@ -1207,3 +1207,20 @@ test('should remove noscript when javaScriptEnabled is set to true', async ({ br
|
|||
await expect(frame.getByText('Always visible')).toBeVisible();
|
||||
await expect(frame.getByText('Enable JavaScript to run this app.')).toBeHidden();
|
||||
});
|
||||
|
||||
test('should open snapshot in new browser context', async ({ browser, page, runAndTrace, server }) => {
|
||||
const traceViewer = await runAndTrace(async () => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('hello');
|
||||
});
|
||||
await traceViewer.snapshotFrame('page.setContent');
|
||||
const popupPromise = traceViewer.page.context().waitForEvent('page');
|
||||
await traceViewer.page.getByTitle('Open snapshot in a new tab').click();
|
||||
const popup = await popupPromise;
|
||||
|
||||
// doesn't share sw.bundle.js
|
||||
const newPage = await browser.newPage();
|
||||
await newPage.goto(popup.url());
|
||||
await expect(newPage.getByText('hello')).toBeVisible();
|
||||
await newPage.close();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -292,6 +292,38 @@ test('should merge blob into blob', async ({ runInlineTest, mergeReports, showRe
|
|||
}
|
||||
});
|
||||
|
||||
test('should produce consistent step ids', {
|
||||
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/31023' },
|
||||
}, async ({ runInlineTest, mergeReports, showReport, page }) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
retries: 1,
|
||||
reporter: [
|
||||
['blob', { outputFile: 'blob-report/report-1.zip' }],
|
||||
['blob', { outputFile: 'blob-report/report-2.zip' }]
|
||||
]
|
||||
};
|
||||
`,
|
||||
'a.test.js': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('math 1', async ({}) => {
|
||||
expect(1 + 1).toBe(2);
|
||||
});
|
||||
`
|
||||
};
|
||||
await runInlineTest(files);
|
||||
const reportDir = test.info().outputPath('blob-report');
|
||||
const reportFiles = await fs.promises.readdir(reportDir);
|
||||
reportFiles.sort();
|
||||
expect(reportFiles).toEqual(['report-1.zip', 'report-2.zip']);
|
||||
const { exitCode } = await mergeReports(reportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html,json'] });
|
||||
expect(exitCode).toBe(0);
|
||||
await showReport();
|
||||
await expect(page.locator('.subnav-item:has-text("All") .counter')).toHaveText('2');
|
||||
await expect(page.locator('.subnav-item:has-text("Passed") .counter')).toHaveText('2');
|
||||
});
|
||||
|
||||
test('be able to merge incomplete shards', async ({ runInlineTest, mergeReports, showReport, page }) => {
|
||||
const reportDir = test.info().outputPath('blob-report');
|
||||
const files = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue