diff --git a/.eslintignore b/.eslintignore index 152cbbe228..60b8fd360f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -7,8 +7,6 @@ test/assets/modernizr.js /packages/playwright-core/types/* /packages/playwright-ct-core/src/generated/* /index.d.ts -utils/generate_types/overrides.d.ts -utils/generate_types/test/test.ts node_modules/ browser_patches/*/checkout/ browser_patches/chromium/output/ diff --git a/.github/actions/download-artifact/action.yml b/.github/actions/download-artifact/action.yml index 3ab38a4c17..d5dd4bea89 100644 --- a/.github/actions/download-artifact/action.yml +++ b/.github/actions/download-artifact/action.yml @@ -34,7 +34,7 @@ runs: artifact_id: artifact.id, archive_format: 'zip' }); - console.log('downloaded artifact', result); + console.log(`Downloaded ${artifact.name}.zip (${result.data.byteLength} bytes)`); fs.writeFileSync(`${{ inputs.path }}/artifacts/${artifact.name}.zip`, Buffer.from(result.data)); } - name: Unzip artifacts diff --git a/.github/actions/download-blob-report-from-azure/action.yml b/.github/actions/download-blob-report-from-azure/action.yml deleted file mode 100644 index 848b98cfbc..0000000000 --- a/.github/actions/download-blob-report-from-azure/action.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: 'Download blob report from Azure' -description: 'Download blob report from Azure blob storage' -inputs: - blob_prefix: - description: 'Name of the Azure blob storage directory containing blob report' - required: true - output_dir: - description: 'Output directory where downloaded blobs will be stored' - required: true - default: 'blob-report' - connection_string: - description: 'Azure connection string' - required: true -runs: - using: "composite" - steps: - - name: Download Blob Reports from Azure Blob Storage - shell: bash - run: | - OUTPUT_DIR='${{ inputs.output_dir }}' - mkdir -p $OUTPUT_DIR - LIST=$(az storage blob list -c '$web' --prefix ${{ inputs.blob_prefix }} --connection-string "${{ inputs.connection_string }}") - for name in $(echo $LIST | jq --raw-output '.[].name | select(test("report-.*\\.zip$"))'); - do - az storage blob download -c '$web' --name $name -f $OUTPUT_DIR/$(basename $name) --connection-string "${{ inputs.connection_string }}" - done diff --git a/.github/dummy-package-files-for-dependents-analytics/playwright-chromium/package.json b/.github/dummy-package-files-for-dependents-analytics/playwright-chromium/package.json deleted file mode 100644 index ec0de7ab12..0000000000 --- a/.github/dummy-package-files-for-dependents-analytics/playwright-chromium/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "playwright-chromium", - "version": "1.0.0", - "description": "A high-level API to automate web browsers", - "repository": "github:Microsoft/playwright", - "license": "Apache-2.0" -} \ No newline at end of file diff --git a/.github/dummy-package-files-for-dependents-analytics/playwright-core/package.json b/.github/dummy-package-files-for-dependents-analytics/playwright-core/package.json deleted file mode 100644 index 7a76fc0b9f..0000000000 --- a/.github/dummy-package-files-for-dependents-analytics/playwright-core/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "playwright-core", - "version": "1.0.0", - "description": "A high-level API to automate web browsers", - "repository": "github:Microsoft/playwright", - "license": "Apache-2.0" -} \ No newline at end of file diff --git a/.github/dummy-package-files-for-dependents-analytics/playwright-firefox/package.json b/.github/dummy-package-files-for-dependents-analytics/playwright-firefox/package.json deleted file mode 100644 index da7b955fc8..0000000000 --- a/.github/dummy-package-files-for-dependents-analytics/playwright-firefox/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "playwright-firefox", - "version": "1.0.0", - "description": "A high-level API to automate web browsers", - "repository": "github:Microsoft/playwright", - "license": "Apache-2.0" -} \ No newline at end of file diff --git a/.github/dummy-package-files-for-dependents-analytics/playwright-test/package.json b/.github/dummy-package-files-for-dependents-analytics/playwright-test/package.json deleted file mode 100644 index e3cef4a193..0000000000 --- a/.github/dummy-package-files-for-dependents-analytics/playwright-test/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "@playwright/test", - "version": "1.0.0", - "description": "A high-level API to automate web browsers", - "repository": "github:Microsoft/playwright", - "license": "Apache-2.0" -} \ No newline at end of file diff --git a/.github/dummy-package-files-for-dependents-analytics/playwright-webkit/package.json b/.github/dummy-package-files-for-dependents-analytics/playwright-webkit/package.json deleted file mode 100644 index 6d0f7491c8..0000000000 --- a/.github/dummy-package-files-for-dependents-analytics/playwright-webkit/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "playwright-webkit", - "version": "1.0.0", - "description": "A high-level API to automate web browsers", - "repository": "github:Microsoft/playwright", - "license": "Apache-2.0" -} \ No newline at end of file diff --git a/.github/dummy-package-files-for-dependents-analytics/playwright/package.json b/.github/dummy-package-files-for-dependents-analytics/playwright/package.json deleted file mode 100644 index 4515e5a1e9..0000000000 --- a/.github/dummy-package-files-for-dependents-analytics/playwright/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "playwright", - "version": "1.0.0", - "description": "A high-level API to automate web browsers", - "repository": "github:Microsoft/playwright", - "license": "Apache-2.0" -} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8902876690..69d85e4975 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ node_modules/ *.swp *.pyc .vscode +.mono .idea yarn.lock /packages/playwright-core/src/generated diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 072f024e17..39610ae84d 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -290,9 +290,7 @@ Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` o **Usage** ```js -page.on('dialog', dialog => { - dialog.accept(); -}); +page.on('dialog', dialog => dialog.accept()); ``` ```java @@ -3157,6 +3155,7 @@ This method lets you set up a special function, called a handler, that activates Things to keep in mind: * When an overlay is shown predictably, we recommend explicitly waiting for it in your test and dismissing it as a part of your normal test flow, instead of using [`method: Page.addLocatorHandler`]. * Playwright checks for the overlay every time before executing or retrying an action that requires an [actionability check](../actionability.md), or before performing an auto-waiting assertion check. When overlay is visible, Playwright calls the handler first, and then proceeds with the action/assertion. Note that the handler is only called when you perform an action/assertion - if the overlay becomes visible but you don't perform any actions, the handler will not be triggered. +* After executing the handler, Playwright will ensure that overlay that triggered the handler is not visible anymore. You can opt-out of this behavior with [`option: allowStayingVisible`]. * The execution time of the handler counts towards the timeout of the action/assertion that executed the handler. If your handler takes too long, it might cause timeouts. * You can register multiple handlers. However, only a single handler will be running at a time. Make sure the actions within a handler don't depend on another handler. @@ -3286,13 +3285,13 @@ await page.GotoAsync("https://example.com"); await page.GetByRole("button", new() { Name = "Start here" }).ClickAsync(); ``` -An example with a custom callback on every actionability check. It uses a `` locator that is always visible, so the handler is called before every actionability check: +An example with a custom callback on every actionability check. It uses a `` locator that is always visible, so the handler is called before every actionability check. It is important to specify [`option: allowStayingVisible`], because the handler does not hide the `` element. ```js // Setup the handler. await page.addLocatorHandler(page.locator('body'), async () => { await page.evaluate(() => window.removeObstructionsForTestIfNeeded()); -}); +}, { allowStayingVisible: true }); // Write the test as usual. await page.goto('https://example.com'); @@ -3303,7 +3302,7 @@ await page.getByRole('button', { name: 'Start here' }).click(); // Setup the handler. page.addLocatorHandler(page.locator("body")), () => { page.evaluate("window.removeObstructionsForTestIfNeeded()"); -}); +}, new Page.AddLocatorHandlerOptions.setAllowStayingVisible(true)); // Write the test as usual. page.goto("https://example.com"); @@ -3314,7 +3313,7 @@ page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click(); # Setup the handler. def handler(): page.evaluate("window.removeObstructionsForTestIfNeeded()") -page.add_locator_handler(page.locator("body"), handler) +page.add_locator_handler(page.locator("body"), handler, allow_staying_visible=True) # Write the test as usual. page.goto("https://example.com") @@ -3325,7 +3324,7 @@ page.get_by_role("button", name="Start here").click() # Setup the handler. def handler(): await page.evaluate("window.removeObstructionsForTestIfNeeded()") -await page.add_locator_handler(page.locator("body"), handler) +await page.add_locator_handler(page.locator("body"), handler, allow_staying_visible=True) # Write the test as usual. await page.goto("https://example.com") @@ -3336,13 +3335,45 @@ await page.get_by_role("button", name="Start here").click() // Setup the handler. await page.AddLocatorHandlerAsync(page.Locator("body"), async () => { await page.EvaluateAsync("window.removeObstructionsForTestIfNeeded()"); -}); +}, new() { AllowStayingVisible = true }); // Write the test as usual. await page.GotoAsync("https://example.com"); await page.GetByRole("button", new() { Name = "Start here" }).ClickAsync(); ``` +Handler takes the original locator as an argument. You can also automatically remove the handler after a number of invocations by setting [`option: times`]: + +```js +await page.addLocatorHandler(page.getByLabel('Close'), async locator => { + await locator.click(); +}, { times: 1 }); +``` + +```java +page.addLocatorHandler(page.getByLabel("Close"), locator => { + locator.click(); +}, new Page.AddLocatorHandlerOptions().setTimes(1)); +``` + +```python sync +def handler(locator): + locator.click() +page.add_locator_handler(page.get_by_label("Close"), handler, times=1) +``` + +```python async +def handler(locator): + await locator.click() +await page.add_locator_handler(page.get_by_label("Close"), handler, times=1) +``` + +```csharp +await page.AddLocatorHandlerAsync(page.GetByText("Sign up to the newsletter"), async locator => { + await locator.ClickAsync(); +}, new() { Times = 1 }); +``` + ### param: Page.addLocatorHandler.locator * since: v1.42 - `locator` <[Locator]> @@ -3352,24 +3383,67 @@ Locator that triggers the handler. ### param: Page.addLocatorHandler.handler * langs: js, python * since: v1.42 -- `handler` <[function]> +- `handler` <[function]\([Locator]\): [Promise]> Function that should be run once [`param: locator`] appears. This function should get rid of the element that blocks actions like click. ### param: Page.addLocatorHandler.handler * langs: csharp * since: v1.42 -- `handler` <[function](): [Promise]> +- `handler` <[function]\([Locator]\)> Function that should be run once [`param: locator`] appears. This function should get rid of the element that blocks actions like click. ### param: Page.addLocatorHandler.handler * langs: java * since: v1.42 -- `handler` <[Runnable]> +- `handler` <[function]\([Locator]\)> Function that should be run once [`param: locator`] appears. This function should get rid of the element that blocks actions like click. +### option: Page.addLocatorHandler.times +* since: v1.44 +- `times` <[int]> + +Specifies the maximum number of times this handler should be called. Unlimited by default. + +### option: Page.addLocatorHandler.allowStayingVisible +* since: v1.44 +- `allowStayingVisible` <[boolean]> + +By default, after calling the handler Playwright will wait until the overlay becomes hidden, and only then Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of this behavior, so that overlay can stay visible after the handler has run. + + +## async method: Page.removeLocatorHandler +* since: v1.44 + +:::warning[Experimental] +This method is experimental and its behavior may change in the upcoming releases. +::: + +Removes locator handler added by [`method: Page.addLocatorHandler`]. + +### param: Page.removeLocatorHandler.locator +* since: v1.44 +- `locator` <[Locator]> + +Locator passed to [`method: Page.addLocatorHandler`]. + +### param: Page.removeLocatorHandler.handler +* langs: js, python +* since: v1.44 +- `handler` <[function]\([Locator]\): [Promise]> + +Handler passed to [`method: Page.addLocatorHandler`]. + +### param: Page.addLocatorHandler.handler +* langs: csharp, java +* since: v1.44 +- `handler` <[function]\([Locator]\)> + +Handler passed to [`method: Page.addLocatorHandler`]. + + ## async method: Page.reload * since: v1.8 - returns: <[null]|[Response]> diff --git a/docs/src/api/params.md b/docs/src/api/params.md index e105f2fd14..9bc70c1232 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -1731,13 +1731,17 @@ test.describe('suite', () => { The list of supported tokens: -* `{testDir}` - Project's [`property: TestConfig.testDir`]. - * Value: `/home/playwright/tests` (absolute path is since `testDir` is resolved relative to directory with config) -* `{snapshotDir}` - Project's [`property: TestConfig.snapshotDir`]. - * Value: `/home/playwright/tests` (since `snapshotDir` is not provided in config, it defaults to `testDir`) +* `{arg}` - Relative snapshot path **without extension**. These come from the arguments passed to the `toHaveScreenshot()` and `toMatchSnapshot()` calls; if called without arguments, this will be an auto-generated snapshot name. + * Value: `foo/bar/baz` +* `{ext}` - snapshot extension (with dots) + * Value: `.png` * `{platform}` - The value of `process.platform`. * `{projectName}` - Project's file-system-sanitized name, if any. * Value: `''` (empty string). +* `{snapshotDir}` - Project's [`property: TestConfig.snapshotDir`]. + * Value: `/home/playwright/tests` (since `snapshotDir` is not provided in config, it defaults to `testDir`) +* `{testDir}` - Project's [`property: TestConfig.testDir`]. + * Value: `/home/playwright/tests` (absolute path is since `testDir` is resolved relative to directory with config) * `{testFileDir}` - Directories in relative path from `testDir` to **test file**. * Value: `page` * `{testFileName}` - Test file name with extension. @@ -1746,10 +1750,6 @@ The list of supported tokens: * Value: `page/page-click.spec.ts` * `{testName}` - File-system-sanitized test title, including parent describes but excluding file name. * Value: `suite-test-should-work` -* `{arg}` - Relative snapshot path **without extension**. These come from the arguments passed to the `toHaveScreenshot()` and `toMatchSnapshot()` calls; if called without arguments, this will be an auto-generated snapshot name. - * Value: `foo/bar/baz` -* `{ext}` - snapshot extension (with dots) - * Value: `.png` Each token can be preceded with a single character that will be used **only if** this token has non-empty value. diff --git a/docs/src/test-api/class-configinworker.md b/docs/src/test-api/class-configinworker.md deleted file mode 100644 index 5832dca885..0000000000 --- a/docs/src/test-api/class-configinworker.md +++ /dev/null @@ -1,135 +0,0 @@ -# class: ConfigInWorker -* since: v1.10 -* langs: js - -Resolved configuration available via [`property: TestInfo.config`] and [`property: WorkerInfo.config`]. - -## property: ConfigInWorker.configFile -* since: v1.20 -- type: ?<[string]> - -Path to the configuration file (if any) used to run the tests. - -## property: ConfigInWorker.forbidOnly -* since: v1.10 -- type: <[boolean]> - -See [`property: TestConfig.forbidOnly`]. - -## property: ConfigInWorker.fullyParallel -* since: v1.20 -- type: <[boolean]> - -See [`property: TestConfig.fullyParallel`]. - -## property: ConfigInWorker.globalSetup -* since: v1.10 -- type: <[null]|[string]> - -See [`property: TestConfig.globalSetup`]. - -## property: ConfigInWorker.globalTeardown -* since: v1.10 -- type: <[null]|[string]> - -See [`property: TestConfig.globalTeardown`]. - -## property: ConfigInWorker.globalTimeout -* since: v1.10 -- type: <[int]> - -See [`property: TestConfig.globalTimeout`]. - -## property: ConfigInWorker.grep -* since: v1.10 -- type: <[RegExp]|[Array]<[RegExp]>> - -See [`property: TestConfig.grep`]. - -## property: ConfigInWorker.grepInvert -* since: v1.10 -- type: <[null]|[RegExp]|[Array]<[RegExp]>> - -See [`property: TestConfig.grepInvert`]. - -## property: ConfigInWorker.maxFailures -* since: v1.10 -- type: <[int]> - -See [`property: TestConfig.maxFailures`]. - -## property: ConfigInWorker.metadata -* since: v1.10 -- type: <[Metadata]> - -See [`property: TestConfig.metadata`]. - -## property: ConfigInWorker.preserveOutput -* since: v1.10 -- type: <[PreserveOutput]<"always"|"never"|"failures-only">> - -See [`property: TestConfig.preserveOutput`]. - -## property: ConfigInWorker.projects -* since: v1.10 -- type: <[Array]<[ProjectInWorker]>> - -List of resolved projects. - -## property: ConfigInWorker.quiet -* since: v1.10 -- type: <[boolean]> - -See [`property: TestConfig.quiet`]. - -## property: ConfigInWorker.reporter -* since: v1.10 -- type: <[string]|[Array]<[Object]>|[BuiltInReporter]<"list"|"dot"|"line"|"github"|"json"|"junit"|"null"|"html">> - - `0` <[string]> Reporter name or module or file path - - `1` <[Object]> An object with reporter options if any - -See [`property: TestConfig.reporter`]. - -## property: ConfigInWorker.reportSlowTests -* since: v1.10 -- type: <[null]|[Object]> - - `max` <[int]> The maximum number of slow test files to report. Defaults to `5`. - - `threshold` <[float]> Test duration in milliseconds that is considered slow. Defaults to 15 seconds. - -See [`property: TestConfig.reportSlowTests`]. - -## property: ConfigInWorker.rootDir -* since: v1.20 -- type: <[string]> - -## property: ConfigInWorker.shard -* since: v1.10 -- type: <[null]|[Object]> - - `total` <[int]> The total number of shards. - - `current` <[int]> The index of the shard to execute, one-based. - -See [`property: TestConfig.shard`]. - -## property: ConfigInWorker.updateSnapshots -* since: v1.10 -- type: <[UpdateSnapshots]<"all"|"none"|"missing">> - -See [`property: TestConfig.updateSnapshots`]. - -## property: ConfigInWorker.version -* since: v1.20 -- type: <[string]> - -Playwright version. - -## property: ConfigInWorker.webServer -* since: v1.10 -- type: <[null]|[Object]> - -See [`property: TestConfig.webServer`]. - -## property: ConfigInWorker.workers -* since: v1.10 -- type: <[int]> - -See [`property: TestConfig.workers`]. diff --git a/docs/src/test-reporter-api/class-fullconfig.md b/docs/src/test-api/class-fullconfig.md similarity index 88% rename from docs/src/test-reporter-api/class-fullconfig.md rename to docs/src/test-api/class-fullconfig.md index ce4318560f..e6437ab314 100644 --- a/docs/src/test-reporter-api/class-fullconfig.md +++ b/docs/src/test-api/class-fullconfig.md @@ -2,13 +2,13 @@ * since: v1.10 * langs: js -Resolved configuration passed to [`method: Reporter.onBegin`]. +Resolved configuration which is accessible via [`property: TestInfo.config`] and is passed to the test reporters. To see the format of Playwright configuration file, please see [TestConfig] instead. ## property: FullConfig.configFile * since: v1.20 - type: ?<[string]> -Path to the configuration file (if any) used to run the tests. +Path to the configuration file used to run the tests. The value is an empty string if no config file was used. ## property: FullConfig.forbidOnly * since: v1.10 @@ -102,6 +102,8 @@ See [`property: TestConfig.reportSlowTests`]. * since: v1.20 - type: <[string]> +Base directory for all relative paths used in the reporters. + ## property: FullConfig.shard * since: v1.10 - type: <[null]|[Object]> diff --git a/docs/src/test-reporter-api/class-fullproject.md b/docs/src/test-api/class-fullproject.md similarity index 85% rename from docs/src/test-reporter-api/class-fullproject.md rename to docs/src/test-api/class-fullproject.md index 9a744115df..682f9bb672 100644 --- a/docs/src/test-reporter-api/class-fullproject.md +++ b/docs/src/test-api/class-fullproject.md @@ -2,10 +2,7 @@ * since: v1.10 * langs: js -Runtime representation of the test project configuration that is passed -to [Reporter]. It exposes some of the resolved fields declared in -[TestProject]. You can get [FullProject] instance from [`property: FullConfig.projects`] -or [`method: Suite.project`]. +Runtime representation of the test project configuration. It is accessible in the tests via [`property: TestInfo.project`] and [`property: WorkerInfo.project`] and is passed to the test reporters. To see the format of the project in the Playwright configuration file please see [TestProject] instead. ## property: FullProject.dependencies * since: v1.31 diff --git a/docs/src/test-api/class-projectinworker.md b/docs/src/test-api/class-projectinworker.md deleted file mode 100644 index c8ede102ba..0000000000 --- a/docs/src/test-api/class-projectinworker.md +++ /dev/null @@ -1,96 +0,0 @@ -# class: ProjectInWorker -* since: v1.10 -* langs: js - -Runtime representation of the test project configuration that can be accessed -in the tests via [`property: TestInfo.project`] and [`property: WorkerInfo.project`]. - -## property: ProjectInWorker.dependencies -* since: v1.31 -- type: <[Array]<[string]>> - -See [`property: TestProject.dependencies`]. - -## property: ProjectInWorker.grep -* since: v1.10 -- type: <[RegExp]|[Array]<[RegExp]>> - -See [`property: TestProject.grep`]. - -## property: ProjectInWorker.grepInvert -* since: v1.10 -- type: <[null]|[RegExp]|[Array]<[RegExp]>> - -See [`property: TestProject.grepInvert`]. - -## property: ProjectInWorker.metadata -* since: v1.10 -- type: <[Metadata]> - -See [`property: TestProject.metadata`]. - -## property: ProjectInWorker.name -* since: v1.10 -- type: <[string]> - -See [`property: TestProject.name`]. - -## property: ProjectInWorker.snapshotDir -* since: v1.10 -- type: <[string]> - -See [`property: TestProject.snapshotDir`]. - -## property: ProjectInWorker.outputDir -* since: v1.10 -- type: <[string]> - -See [`property: TestProject.outputDir`]. - -## property: ProjectInWorker.repeatEach -* since: v1.10 -- type: <[int]> - -See [`property: TestProject.repeatEach`]. - -## property: ProjectInWorker.retries -* since: v1.10 -- type: <[int]> - -See [`property: TestProject.retries`]. - -## property: ProjectInWorker.teardown -* since: v1.34 -- type: ?<[string]> - -See [`property: TestProject.teardown`]. - -## property: ProjectInWorker.testDir -* since: v1.10 -- type: <[string]> - -See [`property: TestProject.testDir`]. - -## property: ProjectInWorker.testIgnore -* since: v1.10 -- type: <[string]|[RegExp]|[Array]<[string]|[RegExp]>> - -See [`property: TestProject.testIgnore`]. - -## property: ProjectInWorker.testMatch -* since: v1.10 -- type: <[string]|[RegExp]|[Array]<[string]|[RegExp]>> - -See [`property: TestProject.testMatch`]. - -## property: ProjectInWorker.timeout -* since: v1.10 -- type: <[int]> - -See [`property: TestProject.timeout`]. - -## property: ProjectInWorker.use -* since: v1.10 -- type: <[Fixtures]> - -See [`property: TestProject.use`]. diff --git a/docs/src/test-api/class-testconfig.md b/docs/src/test-api/class-testconfig.md index a89236cbf0..a36b7437d2 100644 --- a/docs/src/test-api/class-testconfig.md +++ b/docs/src/test-api/class-testconfig.md @@ -2,7 +2,7 @@ * since: v1.10 * langs: js -Playwright Test provides many options to configure how your tests are collected and executed, for example `timeout` or `testDir`. These options are described in the [TestConfig] object in the [configuration file](../test-configuration.md). +Playwright Test provides many options to configure how your tests are collected and executed, for example `timeout` or `testDir`. These options are described in the [TestConfig] object in the [configuration file](../test-configuration.md). This type describes format of the configuration file, to access resolved configuration parameters at run time use [FullConfig]. Playwright Test supports running multiple test projects at the same time. Project-specific options should be put to [`property: TestConfig.projects`], but top-level [TestConfig] can also define base options shared between all projects. @@ -41,20 +41,20 @@ export default defineConfig({ - type: ?<[Object]> - `timeout` ?<[int]> Default timeout for async expect matchers in milliseconds, defaults to 5000ms. - `toHaveScreenshot` ?<[Object]> Configuration for the [`method: PageAssertions.toHaveScreenshot#1`] method. - - `threshold` ?<[float]> an acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. - - `maxDiffPixels` ?<[int]> an acceptable amount of pixels that could be different, unset by default. - - `maxDiffPixelRatio` ?<[float]> an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default. - `animations` ?<[ScreenshotAnimations]<"allow"|"disabled">> See [`option: animations`] in [`method: Page.screenshot`]. Defaults to `"disabled"`. - `caret` ?<[ScreenshotCaret]<"hide"|"initial">> See [`option: caret`] in [`method: Page.screenshot`]. Defaults to `"hide"`. + - `maxDiffPixels` ?<[int]> An acceptable amount of pixels that could be different, unset by default. + - `maxDiffPixelRatio` ?<[float]> An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default. - `scale` ?<[ScreenshotScale]<"css"|"device">> See [`option: scale`] in [`method: Page.screenshot`]. Defaults to `"css"`. - `stylePath` ?<[string]|[Array]<[string]>> See [`option: style`] in [`method: Page.screenshot`]. + - `threshold` ?<[float]> An acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. - `toMatchSnapshot` ?<[Object]> Configuration for the [`method: SnapshotAssertions.toMatchSnapshot#1`] method. - - `threshold` ?<[float]> an acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. - - `maxDiffPixels` ?<[int]> an acceptable amount of pixels that could be different, unset by default. - - `maxDiffPixelRatio` ?<[float]> an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default. + - `maxDiffPixels` ?<[int]> An acceptable amount of pixels that could be different, unset by default. + - `maxDiffPixelRatio` ?<[float]> An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default. + - `threshold` ?<[float]> An acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. - `toPass` ?<[Object]> Configuration for the [expect(value).toPass()](../test-assertions.md#expecttopass) method. - - `timeout` ?<[int]> timeout for toPass method in milliseconds. - - `intervals` ?<[Array]<[int]>> probe intervals for toPass method in milliseconds. + - `intervals` ?<[Array]<[int]>> Probe intervals for toPass method in milliseconds. + - `timeout` ?<[int]> Timeout for toPass method in milliseconds. Configuration for the `expect` assertion library. Learn more about [various timeouts](../test-timeouts.md). @@ -112,7 +112,7 @@ export default defineConfig({ * since: v1.10 - type: ?<[string]> -Path to the global setup file. This file will be required and run before all the tests. It must export a single function that takes a [`TestConfig`] argument. +Path to the global setup file. This file will be required and run before all the tests. It must export a single function that takes a [FullConfig] argument. Learn more about [global setup and teardown](../test-global-setup-teardown.md). @@ -458,8 +458,8 @@ export default defineConfig({ ## property: TestConfig.shard * since: v1.10 - type: ?<[null]|[Object]> - - `total` <[int]> The total number of shards. - `current` <[int]> The index of the shard to execute, one-based. + - `total` <[int]> The total number of shards. Shard tests and execute only the selected shard. Specify in the one-based form like `{ total: 5, current: 2 }`. @@ -589,15 +589,15 @@ export default defineConfig({ * since: v1.10 - type: ?<[Object]|[Array]<[Object]>> - `command` <[string]> Shell command to start. For example `npm run start`.. - - `port` ?<[int]> The port that your http server is expected to appear on. It does wait until it accepts connections. Either `port` or `url` should be specified. - - `url` ?<[string]> The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the server is ready to accept connections. Redirects (3xx status codes) are being followed and the new location is checked. Either `port` or `url` should be specified. + - `cwd` ?<[string]> Current working directory of the spawned process, defaults to the directory of the configuration file. + - `env` ?<[Object]<[string], [string]>> Environment variables to set for the command, `process.env` by default. - `ignoreHTTPSErrors` ?<[boolean]> Whether to ignore HTTPS errors when fetching the `url`. Defaults to `false`. - - `timeout` ?<[int]> How long to wait for the process to start up and be available in milliseconds. Defaults to 60000. + - `port` ?<[int]> The port that your http server is expected to appear on. It does wait until it accepts connections. Either `port` or `url` should be specified. - `reuseExistingServer` ?<[boolean]> If true, it will re-use an existing server on the `port` or `url` when available. If no server is running on that `port` or `url`, it will run the command to start a new server. If `false`, it will throw if an existing process is listening on the `port` or `url`. This should be commonly set to `!process.env.CI` to allow the local dev server when running tests locally. - `stdout` ?<["pipe"|"ignore"]> If `"pipe"`, it will pipe the stdout of the command to the process stdout. If `"ignore"`, it will ignore the stdout of the command. Default to `"ignore"`. - `stderr` ?<["pipe"|"ignore"]> Whether to pipe the stderr of the command to the process stderr or ignore it. Defaults to `"pipe"`. - - `cwd` ?<[string]> Current working directory of the spawned process, defaults to the directory of the configuration file. - - `env` ?<[Object]<[string], [string]>> Environment variables to set for the command, `process.env` by default. + - `timeout` ?<[int]> How long to wait for the process to start up and be available in milliseconds. Defaults to 60000. + - `url` ?<[string]> The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the server is ready to accept connections. Redirects (3xx status codes) are being followed and the new location is checked. Either `port` or `url` should be specified. Launch a development web server (or multiple) during the tests. diff --git a/docs/src/test-api/class-testinfo.md b/docs/src/test-api/class-testinfo.md index 096fbfcc10..cbc125b3bf 100644 --- a/docs/src/test-api/class-testinfo.md +++ b/docs/src/test-api/class-testinfo.md @@ -106,7 +106,7 @@ Column number where the currently running test is declared. ## property: TestInfo.config * since: v1.10 -- type: <[ConfigInWorker]> +- type: <[FullConfig]> Processed configuration from the [configuration file](../test-configuration.md). @@ -279,7 +279,7 @@ Also available as `process.env.TEST_PARALLEL_INDEX`. Learn more about [paralleli ## property: TestInfo.project * since: v1.10 -- type: <[ProjectInWorker]> +- type: <[FullProject]> Processed project configuration from the [configuration file](../test-configuration.md). diff --git a/docs/src/test-api/class-testproject.md b/docs/src/test-api/class-testproject.md index 2ed68de275..7477a3a88d 100644 --- a/docs/src/test-api/class-testproject.md +++ b/docs/src/test-api/class-testproject.md @@ -2,7 +2,7 @@ * since: v1.10 * langs: js -Playwright Test supports running multiple test projects at the same time. This is useful for running tests in multiple configurations. For example, consider running tests against multiple browsers. +Playwright Test supports running multiple test projects at the same time. This is useful for running tests in multiple configurations. For example, consider running tests against multiple browsers. This type describes format of a project in the configuration file, to access resolved configuration parameters at run time use [FullProject]. `TestProject` encapsulates configuration specific to a single project. Projects are configured in [`property: TestConfig.projects`] specified in the [configuration file](../test-configuration.md). Note that all properties of [TestProject] are available in the top-level [TestConfig], in which case they are shared between all projects. diff --git a/docs/src/test-api/class-workerinfo.md b/docs/src/test-api/class-workerinfo.md index e7ae892190..3ae9a3bd21 100644 --- a/docs/src/test-api/class-workerinfo.md +++ b/docs/src/test-api/class-workerinfo.md @@ -6,7 +6,7 @@ ## property: WorkerInfo.config * since: v1.10 -- type: <[ConfigInWorker]> +- type: <[FullConfig]> Processed configuration from the [configuration file](../test-configuration.md). @@ -22,7 +22,7 @@ Also available as `process.env.TEST_PARALLEL_INDEX`. Learn more about [paralleli ## property: WorkerInfo.project * since: v1.10 -- type: <[ProjectInWorker]> +- type: <[FullProject]> Processed project configuration from the [configuration file](../test-configuration.md). diff --git a/docs/src/test-reporter-api/class-suite.md b/docs/src/test-reporter-api/class-suite.md index f9661dc3fd..1d458c842b 100644 --- a/docs/src/test-reporter-api/class-suite.md +++ b/docs/src/test-reporter-api/class-suite.md @@ -30,7 +30,7 @@ Returns the list of all test cases in this suite and its descendants, as opposit * since: v1.44 - type: <[Array]<[TestCase]|[Suite]>> -Test cases and suites defined directly in this suite. The elements are returned in their declaration order. You can discriminate between different entry types using [`property: TestCase.type`] and [`property: Suite.type`]. +Test cases and suites defined directly in this suite. The elements are returned in their declaration order. You can differentiate between various entry types by using [`property: TestCase.type`] and [`property: Suite.type`]. ## property: Suite.location * since: v1.10 diff --git a/docs/src/test-reporter-api/class-testcase.md b/docs/src/test-reporter-api/class-testcase.md index a601b284cd..82ab6ca521 100644 --- a/docs/src/test-reporter-api/class-testcase.md +++ b/docs/src/test-reporter-api/class-testcase.md @@ -112,4 +112,4 @@ Returns a list of titles from the root down to this test. * since: v1.44 - returns: <[TestCaseType]<"test">> -Returns type of the test. +Returns "test". Useful for detecting test cases in [`method: Suite.entries`]. diff --git a/docs/src/test-runners-python.md b/docs/src/test-runners-python.md index 0e3ffde0dd..0ad4043066 100644 --- a/docs/src/test-runners-python.md +++ b/docs/src/test-runners-python.md @@ -55,6 +55,7 @@ def test_my_app_is_working(fixture_name): - `context`: New [browser context](./browser-contexts) for a test. - `page`: New [browser page](./pages) for a test. +- `new_context`: Allows creating different [browser contexts](./browser-contexts) for a test. Useful for multi-user scenarios. Accepts the same parameters as [`method: Browser.newContext`]. **Session scope**: These fixtures are created when requested in a test function and destroyed when all tests end. @@ -211,30 +212,6 @@ def browser_context_args(browser_context_args, playwright): Or via the CLI `--device="iPhone 11 Pro"` -### Persistent context - -```py title="conftest.py" -import pytest -from playwright.sync_api import BrowserType -from typing import Dict - -@pytest.fixture(scope="session") -def context( - browser_type: BrowserType, - browser_type_launch_args: Dict, - browser_context_args: Dict -): - context = browser_type.launch_persistent_context("./foobar", **{ - **browser_type_launch_args, - **browser_context_args, - "locale": "de-DE", - }) - yield context - context.close() -``` - -When using that all pages inside your test are created from the persistent context. - ### Using with `unittest.TestCase` See the following example for using it with `unittest.TestCase`. This has a limitation, diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index d63b4289d6..ec1ac84c6a 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -27,13 +27,13 @@ }, { "name": "firefox-beta", - "revision": "1447", + "revision": "1449", "installByDefault": false, - "browserVersion": "125.0b3" + "browserVersion": "126.0b1" }, { "name": "webkit", - "revision": "2001", + "revision": "2002", "installByDefault": true, "revisionOverrides": { "mac10.14": "1446", diff --git a/packages/playwright-core/src/client/locator.ts b/packages/playwright-core/src/client/locator.ts index 8fd2d8e088..b6058e0abb 100644 --- a/packages/playwright-core/src/client/locator.ts +++ b/packages/playwright-core/src/client/locator.ts @@ -80,6 +80,10 @@ export class Locator implements api.Locator { }); } + _equals(locator: Locator) { + return this._frame === locator._frame && this._selector === locator._selector; + } + page() { return this._frame.page(); } diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 8fd1394a6f..254d4fbf6e 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -96,7 +96,7 @@ export class Page extends ChannelOwner implements api.Page _closeWasCalled: boolean = false; private _harRouters: HarRouter[] = []; - private _locatorHandlers = new Map(); + private _locatorHandlers = new Map any, times: number | undefined }>(); static from(page: channels.PageChannel): Page { return (page as any)._object; @@ -362,19 +362,36 @@ export class Page extends ChannelOwner implements api.Page return Response.fromNullable((await this._channel.reload({ ...options, waitUntil })).response); } - async addLocatorHandler(locator: Locator, handler: Function): Promise { + async addLocatorHandler(locator: Locator, handler: (locator: Locator) => any, options: { times?: number, allowStayingVisible?: boolean } = {}): Promise { if (locator._frame !== this._mainFrame) throw new Error(`Locator must belong to the main frame of this page`); - const { uid } = await this._channel.registerLocatorHandler({ selector: locator._selector }); - this._locatorHandlers.set(uid, handler); + if (options.times === 0) + return; + const { uid } = await this._channel.registerLocatorHandler({ selector: locator._selector, allowStayingVisible: options.allowStayingVisible }); + this._locatorHandlers.set(uid, { locator, handler, times: options.times }); } private async _onLocatorHandlerTriggered(uid: number) { + let remove = false; try { const handler = this._locatorHandlers.get(uid); - await handler?.(); + if (handler && handler.times !== 0) { + if (handler.times !== undefined) + handler.times--; + await handler.handler(handler.locator); + } + remove = handler?.times === 0; } finally { - this._wrapApiCall(() => this._channel.resolveLocatorHandlerNoReply({ uid }), true).catch(() => {}); + this._wrapApiCall(() => this._channel.resolveLocatorHandlerNoReply({ uid, remove }), true).catch(() => {}); + } + } + + async removeLocatorHandler(locator: Locator, handler: (locator: Locator) => any): Promise { + for (const [uid, data] of this._locatorHandlers) { + if (data.locator._equals(locator) && data.handler === handler) { + this._locatorHandlers.delete(uid); + await this._channel.unregisterLocatorHandlerNoReply({ uid }).catch(() => {}); + } } } diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 2ec820711a..d060718c50 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -1046,14 +1046,20 @@ scheme.PageGoForwardResult = tObject({ }); scheme.PageRegisterLocatorHandlerParams = tObject({ selector: tString, + allowStayingVisible: tOptional(tBoolean), }); scheme.PageRegisterLocatorHandlerResult = tObject({ uid: tNumber, }); scheme.PageResolveLocatorHandlerNoReplyParams = tObject({ uid: tNumber, + remove: tOptional(tBoolean), }); scheme.PageResolveLocatorHandlerNoReplyResult = tOptional(tObject({})); +scheme.PageUnregisterLocatorHandlerNoReplyParams = tObject({ + uid: tNumber, +}); +scheme.PageUnregisterLocatorHandlerNoReplyResult = tOptional(tObject({})); scheme.PageReloadParams = tObject({ timeout: tOptional(tNumber), waitUntil: tOptional(tType('LifecycleEvent')), diff --git a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts index fb2e1f0468..d873d8d06a 100644 --- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts @@ -138,12 +138,16 @@ export class PageDispatcher extends Dispatcher { - const uid = this._page.registerLocatorHandler(params.selector); + const uid = this._page.registerLocatorHandler(params.selector, params.allowStayingVisible); return { uid }; } async resolveLocatorHandlerNoReply(params: channels.PageResolveLocatorHandlerNoReplyParams, metadata: CallMetadata): Promise { - this._page.resolveLocatorHandler(params.uid); + this._page.resolveLocatorHandler(params.uid, params.remove); + } + + async unregisterLocatorHandlerNoReply(params: channels.PageUnregisterLocatorHandlerNoReplyParams, metadata: CallMetadata): Promise { + this._page.unregisterLocatorHandler(params.uid); } async emulateMedia(params: channels.PageEmulateMediaParams, metadata: CallMetadata): Promise { diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index 4a49231b3b..73aa1f4dae 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -773,56 +773,61 @@ export class Frame extends SdkObject { throw new Error(`state: expected one of (attached|detached|visible|hidden)`); return controller.run(async progress => { progress.log(`waiting for ${this._asLocator(selector)}${state === 'attached' ? '' : ' to be ' + state}`); - const promise = this.retryWithProgressAndTimeouts(progress, [0, 20, 50, 100, 100, 500], async continuePolling => { - const resolved = await this.selectors.resolveInjectedForSelector(selector, options, scope); - progress.throwIfAborted(); - if (!resolved) { - if (state === 'hidden' || state === 'detached') - return null; - return continuePolling; - } - const result = await resolved.injected.evaluateHandle((injected, { info, root }) => { - const elements = injected.querySelectorAll(info.parsed, root || document); - const element: Element | undefined = elements[0]; - const visible = element ? injected.isVisible(element) : false; - let log = ''; - if (elements.length > 1) { - if (info.strict) - throw injected.strictModeViolationError(info.parsed, elements); - log = ` locator resolved to ${elements.length} elements. Proceeding with the first one: ${injected.previewNode(elements[0])}`; - } else if (element) { - log = ` locator resolved to ${visible ? 'visible' : 'hidden'} ${injected.previewNode(element)}`; - } - return { log, element, visible, attached: !!element }; - }, { info: resolved.info, root: resolved.frame === this ? scope : undefined }); - const { log, visible, attached } = await result.evaluate(r => ({ log: r.log, visible: r.visible, attached: r.attached })); - if (log) - progress.log(log); - const success = { attached, detached: !attached, visible, hidden: !visible }[state]; - if (!success) { - result.dispose(); - return continuePolling; - } - if (options.omitReturnValue) { - result.dispose(); - return null; - } - const element = state === 'attached' || state === 'visible' ? await result.evaluateHandle(r => r.element) : null; - result.dispose(); - if (!element) - return null; - if ((options as any).__testHookBeforeAdoptNode) - await (options as any).__testHookBeforeAdoptNode(); - try { - return await element._adoptTo(await resolved.frame._mainContext()); - } catch (e) { - return continuePolling; - } - }); - return scope ? scope._context._raceAgainstContextDestroyed(promise) : promise; + return await this.waitForSelectorInternal(progress, selector, options, scope); }, this._page._timeoutSettings.timeout(options)); } + async waitForSelectorInternal(progress: Progress, selector: string, options: types.WaitForElementOptions, scope?: dom.ElementHandle): Promise | null> { + const { state = 'visible' } = options; + const promise = this.retryWithProgressAndTimeouts(progress, [0, 20, 50, 100, 100, 500], async continuePolling => { + const resolved = await this.selectors.resolveInjectedForSelector(selector, options, scope); + progress.throwIfAborted(); + if (!resolved) { + if (state === 'hidden' || state === 'detached') + return null; + return continuePolling; + } + const result = await resolved.injected.evaluateHandle((injected, { info, root }) => { + const elements = injected.querySelectorAll(info.parsed, root || document); + const element: Element | undefined = elements[0]; + const visible = element ? injected.isVisible(element) : false; + let log = ''; + if (elements.length > 1) { + if (info.strict) + throw injected.strictModeViolationError(info.parsed, elements); + log = ` locator resolved to ${elements.length} elements. Proceeding with the first one: ${injected.previewNode(elements[0])}`; + } else if (element) { + log = ` locator resolved to ${visible ? 'visible' : 'hidden'} ${injected.previewNode(element)}`; + } + return { log, element, visible, attached: !!element }; + }, { info: resolved.info, root: resolved.frame === this ? scope : undefined }); + const { log, visible, attached } = await result.evaluate(r => ({ log: r.log, visible: r.visible, attached: r.attached })); + if (log) + progress.log(log); + const success = { attached, detached: !attached, visible, hidden: !visible }[state]; + if (!success) { + result.dispose(); + return continuePolling; + } + if (options.omitReturnValue) { + result.dispose(); + return null; + } + const element = state === 'attached' || state === 'visible' ? await result.evaluateHandle(r => r.element) : null; + result.dispose(); + if (!element) + return null; + if ((options as any).__testHookBeforeAdoptNode) + await (options as any).__testHookBeforeAdoptNode(); + try { + return await element._adoptTo(await resolved.frame._mainContext()); + } catch (e) { + return continuePolling; + } + }); + return scope ? scope._context._raceAgainstContextDestroyed(promise) : promise; + } + async dispatchEvent(metadata: CallMetadata, selector: string, type: string, eventInit: Object = {}, options: types.QueryOnSelectorOptions = {}, scope?: dom.ElementHandle): Promise { await this._callOnElementOnceMatches(metadata, selector, (injectedScript, element, data) => { injectedScript.dispatchEvent(element, data.type, data.eventInit); diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 2c3fd8f6dd..4bc8730b26 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -168,7 +168,7 @@ export class Page extends SdkObject { _video: Artifact | null = null; _opener: Page | undefined; private _isServerSideOnly = false; - private _locatorHandlers = new Map }>(); + private _locatorHandlers = new Map }>(); private _lastLocatorHandlerUid = 0; private _locatorHandlerRunningCounter = 0; @@ -432,20 +432,26 @@ export class Page extends SdkObject { }), this._timeoutSettings.navigationTimeout(options)); } - registerLocatorHandler(selector: string) { + registerLocatorHandler(selector: string, allowStayingVisible: boolean | undefined) { const uid = ++this._lastLocatorHandlerUid; - this._locatorHandlers.set(uid, { selector }); + this._locatorHandlers.set(uid, { selector, allowStayingVisible }); return uid; } - resolveLocatorHandler(uid: number) { + resolveLocatorHandler(uid: number, remove: boolean | undefined) { const handler = this._locatorHandlers.get(uid); + if (remove) + this._locatorHandlers.delete(uid); if (handler) { handler.resolved?.resolve(); handler.resolved = undefined; } } + unregisterLocatorHandler(uid: number) { + this._locatorHandlers.delete(uid); + } + async performLocatorHandlersCheckpoint(progress: Progress) { // Do not run locator handlers from inside locator handler callbacks to avoid deadlocks. if (this._locatorHandlerRunningCounter) @@ -460,7 +466,12 @@ export class Page extends SdkObject { if (handler.resolved) { ++this._locatorHandlerRunningCounter; progress.log(` found ${asLocator(this.attribution.playwright.options.sdkLanguage, handler.selector)}, intercepting action to run the handler`); - await this.openScope.race(handler.resolved).finally(() => --this._locatorHandlerRunningCounter); + const promise = handler.resolved.then(async () => { + progress.throwIfAborted(); + if (!handler.allowStayingVisible) + await this.mainFrame().waitForSelectorInternal(progress, handler.selector, { state: 'hidden' }); + }); + await this.openScope.race(promise).finally(() => --this._locatorHandlerRunningCounter); // Avoid side-effects after long-running operation. progress.throwIfAborted(); progress.log(` interception handler has finished, continuing`); diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index fe42f0ae39..fbc4105513 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -898,7 +898,7 @@ export interface Page { /** * Emitted when the page closes. */ - on(event: 'close', listener: (page: Page) => void): this; + on(event: 'close', listener: (page: Page) => any): this; /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. @@ -918,7 +918,7 @@ export interface Page { * ``` * */ - on(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + on(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory. When the page @@ -938,7 +938,7 @@ export interface Page { * ``` * */ - on(event: 'crash', listener: (page: Page) => void): this; + on(event: 'crash', listener: (page: Page) => any): this; /** * Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** @@ -950,28 +950,26 @@ export interface Page { * **Usage** * * ```js - * page.on('dialog', dialog => { - * dialog.accept(); - * }); + * page.on('dialog', dialog => dialog.accept()); * ``` * * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. */ - on(event: 'dialog', listener: (dialog: Dialog) => void): this; + on(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Emitted when the JavaScript * [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) event is dispatched. */ - on(event: 'domcontentloaded', listener: (page: Page) => void): this; + on(event: 'domcontentloaded', listener: (page: Page) => any): this; /** * Emitted when attachment download started. User can access basic file operations on downloaded content via the * passed {@link Download} instance. */ - on(event: 'download', listener: (download: Download) => void): this; + on(event: 'download', listener: (download: Download) => any): this; /** * Emitted when a file chooser is supposed to appear, such as after clicking the ``. Playwright can @@ -986,27 +984,27 @@ export interface Page { * ``` * */ - on(event: 'filechooser', listener: (fileChooser: FileChooser) => void): this; + on(event: 'filechooser', listener: (fileChooser: FileChooser) => any): this; /** * Emitted when a frame is attached. */ - on(event: 'frameattached', listener: (frame: Frame) => void): this; + on(event: 'frameattached', listener: (frame: Frame) => any): this; /** * Emitted when a frame is detached. */ - on(event: 'framedetached', listener: (frame: Frame) => void): this; + on(event: 'framedetached', listener: (frame: Frame) => any): this; /** * Emitted when a frame is navigated to a new url. */ - on(event: 'framenavigated', listener: (frame: Frame) => void): this; + on(event: 'framenavigated', listener: (frame: Frame) => any): this; /** * Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched. */ - on(event: 'load', listener: (page: Page) => void): this; + on(event: 'load', listener: (page: Page) => any): this; /** * Emitted when an uncaught exception happens within the page. @@ -1022,7 +1020,7 @@ export interface Page { * ``` * */ - on(event: 'pageerror', listener: (error: Error) => void): this; + on(event: 'pageerror', listener: (error: Error) => any): this; /** * Emitted when the page opens a new tab or window. This event is emitted in addition to the @@ -1050,14 +1048,14 @@ export interface Page { * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). */ - on(event: 'popup', listener: (page: Page) => void): this; + on(event: 'popup', listener: (page: Page) => any): this; /** * Emitted when a page issues a request. The [request] object is read-only. In order to intercept and mutate requests, * see [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route) or * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route). */ - on(event: 'request', listener: (request: Request) => void): this; + on(event: 'request', listener: (request: Request) => any): this; /** * Emitted when a request fails, for example by timing out. @@ -1075,130 +1073,130 @@ export interface Page { * will only be considered failed when the client cannot get an HTTP response from the server, e.g. due to network * error net::ERR_FAILED. */ - on(event: 'requestfailed', listener: (request: Request) => void): this; + on(event: 'requestfailed', listener: (request: Request) => any): this; /** * Emitted when a request finishes successfully after downloading the response body. For a successful response, the * sequence of events is `request`, `response` and `requestfinished`. */ - on(event: 'requestfinished', listener: (request: Request) => void): this; + on(event: 'requestfinished', listener: (request: Request) => any): this; /** * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of * events is `request`, `response` and `requestfinished`. */ - on(event: 'response', listener: (response: Response) => void): this; + on(event: 'response', listener: (response: Response) => any): this; /** * Emitted when {@link WebSocket} request is sent. */ - on(event: 'websocket', listener: (webSocket: WebSocket) => void): this; + on(event: 'websocket', listener: (webSocket: WebSocket) => any): this; /** * Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned * by the page. */ - on(event: 'worker', listener: (worker: Worker) => void): this; + on(event: 'worker', listener: (worker: Worker) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'close', listener: (page: Page) => void): this; + once(event: 'close', listener: (page: Page) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + once(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'crash', listener: (page: Page) => void): this; + once(event: 'crash', listener: (page: Page) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'dialog', listener: (dialog: Dialog) => void): this; + once(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'domcontentloaded', listener: (page: Page) => void): this; + once(event: 'domcontentloaded', listener: (page: Page) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'download', listener: (download: Download) => void): this; + once(event: 'download', listener: (download: Download) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'filechooser', listener: (fileChooser: FileChooser) => void): this; + once(event: 'filechooser', listener: (fileChooser: FileChooser) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'frameattached', listener: (frame: Frame) => void): this; + once(event: 'frameattached', listener: (frame: Frame) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'framedetached', listener: (frame: Frame) => void): this; + once(event: 'framedetached', listener: (frame: Frame) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'framenavigated', listener: (frame: Frame) => void): this; + once(event: 'framenavigated', listener: (frame: Frame) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'load', listener: (page: Page) => void): this; + once(event: 'load', listener: (page: Page) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'pageerror', listener: (error: Error) => void): this; + once(event: 'pageerror', listener: (error: Error) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'popup', listener: (page: Page) => void): this; + once(event: 'popup', listener: (page: Page) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'request', listener: (request: Request) => void): this; + once(event: 'request', listener: (request: Request) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'requestfailed', listener: (request: Request) => void): this; + once(event: 'requestfailed', listener: (request: Request) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'requestfinished', listener: (request: Request) => void): this; + once(event: 'requestfinished', listener: (request: Request) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'response', listener: (response: Response) => void): this; + once(event: 'response', listener: (response: Response) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'websocket', listener: (webSocket: WebSocket) => void): this; + once(event: 'websocket', listener: (webSocket: WebSocket) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'worker', listener: (worker: Worker) => void): this; + once(event: 'worker', listener: (worker: Worker) => any): this; /** * Emitted when the page closes. */ - addListener(event: 'close', listener: (page: Page) => void): this; + addListener(event: 'close', listener: (page: Page) => any): this; /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. @@ -1218,7 +1216,7 @@ export interface Page { * ``` * */ - addListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + addListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory. When the page @@ -1238,7 +1236,7 @@ export interface Page { * ``` * */ - addListener(event: 'crash', listener: (page: Page) => void): this; + addListener(event: 'crash', listener: (page: Page) => any): this; /** * Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** @@ -1250,28 +1248,26 @@ export interface Page { * **Usage** * * ```js - * page.on('dialog', dialog => { - * dialog.accept(); - * }); + * page.on('dialog', dialog => dialog.accept()); * ``` * * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. */ - addListener(event: 'dialog', listener: (dialog: Dialog) => void): this; + addListener(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Emitted when the JavaScript * [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) event is dispatched. */ - addListener(event: 'domcontentloaded', listener: (page: Page) => void): this; + addListener(event: 'domcontentloaded', listener: (page: Page) => any): this; /** * Emitted when attachment download started. User can access basic file operations on downloaded content via the * passed {@link Download} instance. */ - addListener(event: 'download', listener: (download: Download) => void): this; + addListener(event: 'download', listener: (download: Download) => any): this; /** * Emitted when a file chooser is supposed to appear, such as after clicking the ``. Playwright can @@ -1286,27 +1282,27 @@ export interface Page { * ``` * */ - addListener(event: 'filechooser', listener: (fileChooser: FileChooser) => void): this; + addListener(event: 'filechooser', listener: (fileChooser: FileChooser) => any): this; /** * Emitted when a frame is attached. */ - addListener(event: 'frameattached', listener: (frame: Frame) => void): this; + addListener(event: 'frameattached', listener: (frame: Frame) => any): this; /** * Emitted when a frame is detached. */ - addListener(event: 'framedetached', listener: (frame: Frame) => void): this; + addListener(event: 'framedetached', listener: (frame: Frame) => any): this; /** * Emitted when a frame is navigated to a new url. */ - addListener(event: 'framenavigated', listener: (frame: Frame) => void): this; + addListener(event: 'framenavigated', listener: (frame: Frame) => any): this; /** * Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched. */ - addListener(event: 'load', listener: (page: Page) => void): this; + addListener(event: 'load', listener: (page: Page) => any): this; /** * Emitted when an uncaught exception happens within the page. @@ -1322,7 +1318,7 @@ export interface Page { * ``` * */ - addListener(event: 'pageerror', listener: (error: Error) => void): this; + addListener(event: 'pageerror', listener: (error: Error) => any): this; /** * Emitted when the page opens a new tab or window. This event is emitted in addition to the @@ -1350,14 +1346,14 @@ export interface Page { * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). */ - addListener(event: 'popup', listener: (page: Page) => void): this; + addListener(event: 'popup', listener: (page: Page) => any): this; /** * Emitted when a page issues a request. The [request] object is read-only. In order to intercept and mutate requests, * see [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route) or * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route). */ - addListener(event: 'request', listener: (request: Request) => void): this; + addListener(event: 'request', listener: (request: Request) => any): this; /** * Emitted when a request fails, for example by timing out. @@ -1375,225 +1371,225 @@ export interface Page { * will only be considered failed when the client cannot get an HTTP response from the server, e.g. due to network * error net::ERR_FAILED. */ - addListener(event: 'requestfailed', listener: (request: Request) => void): this; + addListener(event: 'requestfailed', listener: (request: Request) => any): this; /** * Emitted when a request finishes successfully after downloading the response body. For a successful response, the * sequence of events is `request`, `response` and `requestfinished`. */ - addListener(event: 'requestfinished', listener: (request: Request) => void): this; + addListener(event: 'requestfinished', listener: (request: Request) => any): this; /** * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of * events is `request`, `response` and `requestfinished`. */ - addListener(event: 'response', listener: (response: Response) => void): this; + addListener(event: 'response', listener: (response: Response) => any): this; /** * Emitted when {@link WebSocket} request is sent. */ - addListener(event: 'websocket', listener: (webSocket: WebSocket) => void): this; + addListener(event: 'websocket', listener: (webSocket: WebSocket) => any): this; /** * Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned * by the page. */ - addListener(event: 'worker', listener: (worker: Worker) => void): this; + addListener(event: 'worker', listener: (worker: Worker) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'close', listener: (page: Page) => void): this; + removeListener(event: 'close', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + removeListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'crash', listener: (page: Page) => void): this; + removeListener(event: 'crash', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'dialog', listener: (dialog: Dialog) => void): this; + removeListener(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'domcontentloaded', listener: (page: Page) => void): this; + removeListener(event: 'domcontentloaded', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'download', listener: (download: Download) => void): this; + removeListener(event: 'download', listener: (download: Download) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'filechooser', listener: (fileChooser: FileChooser) => void): this; + removeListener(event: 'filechooser', listener: (fileChooser: FileChooser) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'frameattached', listener: (frame: Frame) => void): this; + removeListener(event: 'frameattached', listener: (frame: Frame) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'framedetached', listener: (frame: Frame) => void): this; + removeListener(event: 'framedetached', listener: (frame: Frame) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'framenavigated', listener: (frame: Frame) => void): this; + removeListener(event: 'framenavigated', listener: (frame: Frame) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'load', listener: (page: Page) => void): this; + removeListener(event: 'load', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'pageerror', listener: (error: Error) => void): this; + removeListener(event: 'pageerror', listener: (error: Error) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'popup', listener: (page: Page) => void): this; + removeListener(event: 'popup', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'request', listener: (request: Request) => void): this; + removeListener(event: 'request', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'requestfailed', listener: (request: Request) => void): this; + removeListener(event: 'requestfailed', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'requestfinished', listener: (request: Request) => void): this; + removeListener(event: 'requestfinished', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'response', listener: (response: Response) => void): this; + removeListener(event: 'response', listener: (response: Response) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'websocket', listener: (webSocket: WebSocket) => void): this; + removeListener(event: 'websocket', listener: (webSocket: WebSocket) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'worker', listener: (worker: Worker) => void): this; + removeListener(event: 'worker', listener: (worker: Worker) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'close', listener: (page: Page) => void): this; + off(event: 'close', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + off(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'crash', listener: (page: Page) => void): this; + off(event: 'crash', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'dialog', listener: (dialog: Dialog) => void): this; + off(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'domcontentloaded', listener: (page: Page) => void): this; + off(event: 'domcontentloaded', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'download', listener: (download: Download) => void): this; + off(event: 'download', listener: (download: Download) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'filechooser', listener: (fileChooser: FileChooser) => void): this; + off(event: 'filechooser', listener: (fileChooser: FileChooser) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'frameattached', listener: (frame: Frame) => void): this; + off(event: 'frameattached', listener: (frame: Frame) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'framedetached', listener: (frame: Frame) => void): this; + off(event: 'framedetached', listener: (frame: Frame) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'framenavigated', listener: (frame: Frame) => void): this; + off(event: 'framenavigated', listener: (frame: Frame) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'load', listener: (page: Page) => void): this; + off(event: 'load', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'pageerror', listener: (error: Error) => void): this; + off(event: 'pageerror', listener: (error: Error) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'popup', listener: (page: Page) => void): this; + off(event: 'popup', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'request', listener: (request: Request) => void): this; + off(event: 'request', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'requestfailed', listener: (request: Request) => void): this; + off(event: 'requestfailed', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'requestfinished', listener: (request: Request) => void): this; + off(event: 'requestfinished', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'response', listener: (response: Response) => void): this; + off(event: 'response', listener: (response: Response) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'websocket', listener: (webSocket: WebSocket) => void): this; + off(event: 'websocket', listener: (webSocket: WebSocket) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'worker', listener: (worker: Worker) => void): this; + off(event: 'worker', listener: (worker: Worker) => any): this; /** * Emitted when the page closes. */ - prependListener(event: 'close', listener: (page: Page) => void): this; + prependListener(event: 'close', listener: (page: Page) => any): this; /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. @@ -1613,7 +1609,7 @@ export interface Page { * ``` * */ - prependListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + prependListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory. When the page @@ -1633,7 +1629,7 @@ export interface Page { * ``` * */ - prependListener(event: 'crash', listener: (page: Page) => void): this; + prependListener(event: 'crash', listener: (page: Page) => any): this; /** * Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** @@ -1645,28 +1641,26 @@ export interface Page { * **Usage** * * ```js - * page.on('dialog', dialog => { - * dialog.accept(); - * }); + * page.on('dialog', dialog => dialog.accept()); * ``` * * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. */ - prependListener(event: 'dialog', listener: (dialog: Dialog) => void): this; + prependListener(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Emitted when the JavaScript * [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) event is dispatched. */ - prependListener(event: 'domcontentloaded', listener: (page: Page) => void): this; + prependListener(event: 'domcontentloaded', listener: (page: Page) => any): this; /** * Emitted when attachment download started. User can access basic file operations on downloaded content via the * passed {@link Download} instance. */ - prependListener(event: 'download', listener: (download: Download) => void): this; + prependListener(event: 'download', listener: (download: Download) => any): this; /** * Emitted when a file chooser is supposed to appear, such as after clicking the ``. Playwright can @@ -1681,27 +1675,27 @@ export interface Page { * ``` * */ - prependListener(event: 'filechooser', listener: (fileChooser: FileChooser) => void): this; + prependListener(event: 'filechooser', listener: (fileChooser: FileChooser) => any): this; /** * Emitted when a frame is attached. */ - prependListener(event: 'frameattached', listener: (frame: Frame) => void): this; + prependListener(event: 'frameattached', listener: (frame: Frame) => any): this; /** * Emitted when a frame is detached. */ - prependListener(event: 'framedetached', listener: (frame: Frame) => void): this; + prependListener(event: 'framedetached', listener: (frame: Frame) => any): this; /** * Emitted when a frame is navigated to a new url. */ - prependListener(event: 'framenavigated', listener: (frame: Frame) => void): this; + prependListener(event: 'framenavigated', listener: (frame: Frame) => any): this; /** * Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched. */ - prependListener(event: 'load', listener: (page: Page) => void): this; + prependListener(event: 'load', listener: (page: Page) => any): this; /** * Emitted when an uncaught exception happens within the page. @@ -1717,7 +1711,7 @@ export interface Page { * ``` * */ - prependListener(event: 'pageerror', listener: (error: Error) => void): this; + prependListener(event: 'pageerror', listener: (error: Error) => any): this; /** * Emitted when the page opens a new tab or window. This event is emitted in addition to the @@ -1745,14 +1739,14 @@ export interface Page { * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). */ - prependListener(event: 'popup', listener: (page: Page) => void): this; + prependListener(event: 'popup', listener: (page: Page) => any): this; /** * Emitted when a page issues a request. The [request] object is read-only. In order to intercept and mutate requests, * see [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route) or * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route). */ - prependListener(event: 'request', listener: (request: Request) => void): this; + prependListener(event: 'request', listener: (request: Request) => any): this; /** * Emitted when a request fails, for example by timing out. @@ -1770,30 +1764,30 @@ export interface Page { * will only be considered failed when the client cannot get an HTTP response from the server, e.g. due to network * error net::ERR_FAILED. */ - prependListener(event: 'requestfailed', listener: (request: Request) => void): this; + prependListener(event: 'requestfailed', listener: (request: Request) => any): this; /** * Emitted when a request finishes successfully after downloading the response body. For a successful response, the * sequence of events is `request`, `response` and `requestfinished`. */ - prependListener(event: 'requestfinished', listener: (request: Request) => void): this; + prependListener(event: 'requestfinished', listener: (request: Request) => any): this; /** * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of * events is `request`, `response` and `requestfinished`. */ - prependListener(event: 'response', listener: (response: Response) => void): this; + prependListener(event: 'response', listener: (response: Response) => any): this; /** * Emitted when {@link WebSocket} request is sent. */ - prependListener(event: 'websocket', listener: (webSocket: WebSocket) => void): this; + prependListener(event: 'websocket', listener: (webSocket: WebSocket) => any): this; /** * Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned * by the page. */ - prependListener(event: 'worker', listener: (worker: Worker) => void): this; + prependListener(event: 'worker', listener: (worker: Worker) => any): this; /** * **NOTE** This method is experimental and its behavior may change in the upcoming releases. @@ -1808,12 +1802,14 @@ export interface Page { * Things to keep in mind: * - When an overlay is shown predictably, we recommend explicitly waiting for it in your test and dismissing it as * a part of your normal test flow, instead of using - * [page.addLocatorHandler(locator, handler)](https://playwright.dev/docs/api/class-page#page-add-locator-handler). + * [page.addLocatorHandler(locator, handler[, options])](https://playwright.dev/docs/api/class-page#page-add-locator-handler). * - Playwright checks for the overlay every time before executing or retrying an action that requires an * [actionability check](https://playwright.dev/docs/actionability), or before performing an auto-waiting assertion check. When overlay * is visible, Playwright calls the handler first, and then proceeds with the action/assertion. Note that the * handler is only called when you perform an action/assertion - if the overlay becomes visible but you don't * perform any actions, the handler will not be triggered. + * - After executing the handler, Playwright will ensure that overlay that triggered the handler is not visible + * anymore. You can opt-out of this behavior with `allowStayingVisible`. * - The execution time of the handler counts towards the timeout of the action/assertion that executed the handler. * If your handler takes too long, it might cause timeouts. * - You can register multiple handlers. However, only a single handler will be running at a time. Make sure the @@ -1863,24 +1859,47 @@ export interface Page { * ``` * * An example with a custom callback on every actionability check. It uses a `` locator that is always visible, - * so the handler is called before every actionability check: + * so the handler is called before every actionability check. It is important to specify `allowStayingVisible`, + * because the handler does not hide the `` element. * * ```js * // Setup the handler. * await page.addLocatorHandler(page.locator('body'), async () => { * await page.evaluate(() => window.removeObstructionsForTestIfNeeded()); - * }); + * }, { allowStayingVisible: true }); * * // Write the test as usual. * await page.goto('https://example.com'); * await page.getByRole('button', { name: 'Start here' }).click(); * ``` * + * Handler takes the original locator as an argument. You can also automatically remove the handler after a number of + * invocations by setting `times`: + * + * ```js + * await page.addLocatorHandler(page.getByLabel('Close'), async locator => { + * await locator.click(); + * }, { times: 1 }); + * ``` + * * @param locator Locator that triggers the handler. * @param handler Function that should be run once `locator` appears. This function should get rid of the element that blocks actions * like click. + * @param options */ - addLocatorHandler(locator: Locator, handler: Function): Promise; + addLocatorHandler(locator: Locator, handler: ((locator: Locator) => Promise), options?: { + /** + * By default, after calling the handler Playwright will wait until the overlay becomes hidden, and only then + * Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of + * this behavior, so that overlay can stay visible after the handler has run. + */ + allowStayingVisible?: boolean; + + /** + * Specifies the maximum number of times this handler should be called. Unlimited by default. + */ + times?: number; + }): Promise; /** * Adds a ``, + 'playwright/index.js': ``, + + 'node_modules/@mui/material/index.js': ` + const TextField = () => 'input'; + module.exports = { TextField }; + `, + 'node_modules/@mui/material/package.json': JSON.stringify({ + name: '@mui/material', + main: './index.js', + }), + + 'src/component.spec.tsx': ` + import { test } from '@playwright/experimental-ct-react'; + import { TextField } from '@mui/material'; + + test("passes", async ({ mount }) => { + await mount(); + }); + `, + }, { workers: 1 }); + + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); +}); diff --git a/tests/playwright-test/reporter.spec.ts b/tests/playwright-test/reporter.spec.ts index f07576b90c..f036e3e494 100644 --- a/tests/playwright-test/reporter.spec.ts +++ b/tests/playwright-test/reporter.spec.ts @@ -141,12 +141,8 @@ for (const useIntermediateMergeReport of [false, true] as const) { 'a.test.ts': ` import { test, expect } from '@playwright/test'; test('not run', async ({}) => { - console.log('log'); - console.error('error'); }); test.only('is run', async ({}) => { - console.log('log'); - console.error('error'); }); ` }, { reporter: '', workers: 1 }); @@ -156,16 +152,10 @@ for (const useIntermediateMergeReport of [false, true] as const) { onBegin: 3 tests total options.onBegin=begin-data onTestBegin: foo > a.test.ts > is run; retry #0 -log -error onTestEnd: foo > a.test.ts > is run; retry #0 onTestBegin: foo > a.test.ts > is run; retry #0 -log -error onTestEnd: foo > a.test.ts > is run; retry #0 onTestBegin: bar > a.test.ts > is run; retry #0 -log -error onTestEnd: bar > a.test.ts > is run; retry #0 onEnd options.onEnd=end-data diff --git a/tests/playwright-test/ui-mode-test-setup.spec.ts b/tests/playwright-test/ui-mode-test-setup.spec.ts index 7a5ce3f084..08857d5508 100644 --- a/tests/playwright-test/ui-mode-test-setup.spec.ts +++ b/tests/playwright-test/ui-mode-test-setup.spec.ts @@ -84,6 +84,17 @@ test('should teardown on sigint', async ({ runUITest, nodeVersion }) => { ]); }); +test('should show errors in config', async ({ runUITest }) => { + const { page } = await runUITest({ + 'playwright.config.ts': ` + import { defineConfig, devices } from '@playwright/test'; + throw new Error("URL is empty") + `, + }); + await page.getByText('playwright.config.ts').click(); + await expect(page.getByText('Error: URL is empty')).toBeInViewport(); +}); + const testsWithSetup = { 'playwright.config.ts': ` import { defineConfig } from '@playwright/test'; diff --git a/utils/flakiness-dashboard/README.md b/utils/flakiness-dashboard/README.md index 0c3b84f527..7ea2c92a7f 100644 --- a/utils/flakiness-dashboard/README.md +++ b/utils/flakiness-dashboard/README.md @@ -14,11 +14,11 @@ Azure Functions Core Tools is not available on macOS M1 yet, so we use GitHub Co curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs)-prod $(lsb_release -cs) main" > /etc/apt/sources.list.d/dotnetdev.list' - apt-get update && apt-get install azure-functions-core-tools-4 + apt-get update && apt-get install azure-functions-core-tools-4 sudo ``` - Install Azure CLI: ```bash - curl -sL https://aka.ms/InstallAzureCLIDeb | bash + curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash ``` - Login to Azure: ```bash diff --git a/utils/flakiness-dashboard/host.json b/utils/flakiness-dashboard/host.json index 5c997fdf49..6432c26df3 100644 --- a/utils/flakiness-dashboard/host.json +++ b/utils/flakiness-dashboard/host.json @@ -16,6 +16,6 @@ }, "extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[2.*, 3.0.0)" + "version": "[4.*, 5.0.0)" } } diff --git a/utils/flakiness-dashboard/package-lock.json b/utils/flakiness-dashboard/package-lock.json index 483798e826..43efb92951 100644 --- a/utils/flakiness-dashboard/package-lock.json +++ b/utils/flakiness-dashboard/package-lock.json @@ -6,7 +6,8 @@ "": { "name": "flakiness-dashboard", "dependencies": { - "@azure/storage-blob": "^12.16.0" + "@azure/identity": "^4.1.0", + "@azure/storage-blob": "^12.17.0" } }, "node_modules/@azure/abort-controller": { @@ -26,15 +27,66 @@ "integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg==" }, "node_modules/@azure/core-auth": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", - "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.7.2.tgz", + "integrity": "sha512-Igm/S3fDYmnMq1uKS38Ae1/m37B3zigdlZw+kocwEhh5GjyKjPrXKO2J6rzpC1wAxrNil/jX9BJRqBshyjnF3g==", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", + "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client/node_modules/@azure/core-tracing": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", + "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@azure/core-http": { @@ -86,6 +138,46 @@ "node": ">=8.0.0" } }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.15.2.tgz", + "integrity": "sha512-BmWfpjc/QXc2ipHOh6LbUzp3ONCaa6xzIssTU0DwH9bbYNXJlGUL6tujx5TrbVd/QQknmS+vlQJGrCq2oL1gZA==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline/node_modules/@azure/core-tracing": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", + "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@azure/core-tracing": { "version": "1.0.0-preview.13", "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", @@ -99,15 +191,61 @@ } }, "node_modules/@azure/core-util": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.0.tgz", - "integrity": "sha512-ANP0Er7R2KHHHjwmKzPF9wbd0gXvOX7yRRHeYL1eNd/OaNrMLyfZH/FQasHRVAf6rMXX+EAUpvYwLMFDHDI5Gw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.9.0.tgz", + "integrity": "sha512-AfalUQ1ZppaKuxPPMsFEUdX6GZPB3d9paR9d/TTL7Ow2De8cJaC7ibi7kWVlFAVPCYo31OcnGymc0R89DX8Oaw==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-util/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.1.0.tgz", + "integrity": "sha512-BhYkF8Xr2gXjyDxocm0pc9RI5J5a1jw8iW0dw6Bx95OGdYbuMyFZrrwNw4eYSqQ2BB6FZOqpJP3vjsAqRcvDhw==", "dependencies": { "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.5.0", + "@azure/core-client": "^1.4.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^3.11.1", + "@azure/msal-node": "^2.6.6", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", "tslib": "^2.2.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/@azure/identity/node_modules/@azure/core-tracing": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", + "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@azure/logger": { @@ -121,10 +259,42 @@ "node": ">=14.0.0" } }, + "node_modules/@azure/msal-browser": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.13.0.tgz", + "integrity": "sha512-fD906nmJei3yE7la6DZTdUtXKvpwzJURkfsiz9747Icv4pit77cegSm6prJTKLQ1fw4iiZzrrWwxnhMLrTf5gQ==", + "dependencies": { + "@azure/msal-common": "14.9.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "14.9.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.9.0.tgz", + "integrity": "sha512-yzBPRlWPnTBeixxLNI3BBIgF5/bHpbhoRVuuDBnYjCyWRavaPUsKAHUDYLqpGkBLDciA6TCc6GOxN4/S3WiSxg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.7.0.tgz", + "integrity": "sha512-wXD8LkUvHICeSWZydqg6o8Yvv+grlBEcmLGu+QEI4FcwFendbTEZrlSygnAXXSOCVaGAirWLchca35qrgpO6Jw==", + "dependencies": { + "@azure/msal-common": "14.9.0", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@azure/storage-blob": { - "version": "12.16.0", - "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.16.0.tgz", - "integrity": "sha512-jz33rUSUGUB65FgYrTRgRDjG6hdPHwfvHe+g/UrwVG8MsyLqSxg9TaW7Yuhjxu1v1OZ5xam2NU6+IpCN0xJO8Q==", + "version": "12.17.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.17.0.tgz", + "integrity": "sha512-sM4vpsCpcCApagRW5UIjQNlNylo02my2opgp0Emi8x888hZUvJ3dN69Oq20cEGXkMUWnoCrBaB0zyS3yeB87sQ==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-http": "^3.0.0", @@ -182,11 +352,27 @@ "@types/node": "*" } }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -198,6 +384,30 @@ "node": ">= 0.8" } }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "engines": { + "node": ">=8" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -206,6 +416,14 @@ "node": ">=0.4.0" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -227,6 +445,160 @@ "node": ">= 6" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -246,6 +618,11 @@ "node": ">= 0.6" } }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/node-fetch": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", @@ -265,6 +642,22 @@ } } }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -273,20 +666,62 @@ "node": ">= 0.6.0" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", + "engines": { + "node": ">=4", + "npm": ">=6" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/tunnel": { "version": "0.0.6", @@ -337,6 +772,11 @@ "engines": { "node": ">=4.0" } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } }, "dependencies": { @@ -354,12 +794,55 @@ "integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg==" }, "@azure/core-auth": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", - "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.7.2.tgz", + "integrity": "sha512-Igm/S3fDYmnMq1uKS38Ae1/m37B3zigdlZw+kocwEhh5GjyKjPrXKO2J6rzpC1wAxrNil/jX9BJRqBshyjnF3g==", "requires": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.1.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "requires": { + "tslib": "^2.6.2" + } + } + } + }, + "@azure/core-client": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", + "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", + "requires": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@azure/core-tracing": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", + "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", + "requires": { + "tslib": "^2.6.2" + } + } } }, "@azure/core-http": { @@ -402,6 +885,39 @@ "@azure/core-asynciterator-polyfill": "^1.0.0" } }, + "@azure/core-rest-pipeline": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.15.2.tgz", + "integrity": "sha512-BmWfpjc/QXc2ipHOh6LbUzp3ONCaa6xzIssTU0DwH9bbYNXJlGUL6tujx5TrbVd/QQknmS+vlQJGrCq2oL1gZA==", + "requires": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@azure/core-tracing": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", + "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", + "requires": { + "tslib": "^2.6.2" + } + } + } + }, "@azure/core-tracing": { "version": "1.0.0-preview.13", "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", @@ -412,12 +928,53 @@ } }, "@azure/core-util": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.0.tgz", - "integrity": "sha512-ANP0Er7R2KHHHjwmKzPF9wbd0gXvOX7yRRHeYL1eNd/OaNrMLyfZH/FQasHRVAf6rMXX+EAUpvYwLMFDHDI5Gw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.9.0.tgz", + "integrity": "sha512-AfalUQ1ZppaKuxPPMsFEUdX6GZPB3d9paR9d/TTL7Ow2De8cJaC7ibi7kWVlFAVPCYo31OcnGymc0R89DX8Oaw==", + "requires": { + "@azure/abort-controller": "^2.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "requires": { + "tslib": "^2.6.2" + } + } + } + }, + "@azure/identity": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.1.0.tgz", + "integrity": "sha512-BhYkF8Xr2gXjyDxocm0pc9RI5J5a1jw8iW0dw6Bx95OGdYbuMyFZrrwNw4eYSqQ2BB6FZOqpJP3vjsAqRcvDhw==", "requires": { "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.5.0", + "@azure/core-client": "^1.4.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^3.11.1", + "@azure/msal-node": "^2.6.6", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", "tslib": "^2.2.0" + }, + "dependencies": { + "@azure/core-tracing": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", + "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", + "requires": { + "tslib": "^2.6.2" + } + } } }, "@azure/logger": { @@ -428,10 +985,33 @@ "tslib": "^2.2.0" } }, + "@azure/msal-browser": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.13.0.tgz", + "integrity": "sha512-fD906nmJei3yE7la6DZTdUtXKvpwzJURkfsiz9747Icv4pit77cegSm6prJTKLQ1fw4iiZzrrWwxnhMLrTf5gQ==", + "requires": { + "@azure/msal-common": "14.9.0" + } + }, + "@azure/msal-common": { + "version": "14.9.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.9.0.tgz", + "integrity": "sha512-yzBPRlWPnTBeixxLNI3BBIgF5/bHpbhoRVuuDBnYjCyWRavaPUsKAHUDYLqpGkBLDciA6TCc6GOxN4/S3WiSxg==" + }, + "@azure/msal-node": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.7.0.tgz", + "integrity": "sha512-wXD8LkUvHICeSWZydqg6o8Yvv+grlBEcmLGu+QEI4FcwFendbTEZrlSygnAXXSOCVaGAirWLchca35qrgpO6Jw==", + "requires": { + "@azure/msal-common": "14.9.0", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + } + }, "@azure/storage-blob": { - "version": "12.16.0", - "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.16.0.tgz", - "integrity": "sha512-jz33rUSUGUB65FgYrTRgRDjG6hdPHwfvHe+g/UrwVG8MsyLqSxg9TaW7Yuhjxu1v1OZ5xam2NU6+IpCN0xJO8Q==", + "version": "12.17.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.17.0.tgz", + "integrity": "sha512-sM4vpsCpcCApagRW5UIjQNlNylo02my2opgp0Emi8x888hZUvJ3dN69Oq20cEGXkMUWnoCrBaB0zyS3yeB87sQ==", "requires": { "@azure/abort-controller": "^1.0.0", "@azure/core-http": "^3.0.0", @@ -482,11 +1062,24 @@ "@types/node": "*" } }, + "agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "requires": { + "debug": "^4.3.4" + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -495,11 +1088,32 @@ "delayed-stream": "~1.0.0" } }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -515,6 +1129,137 @@ "mime-types": "^2.1.12" } }, + "http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, + "jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "dependencies": { + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + } + } + }, + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -528,6 +1273,11 @@ "mime-db": "1.52.0" } }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node-fetch": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", @@ -536,25 +1286,53 @@ "whatwg-url": "^5.0.0" } }, + "open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==" + }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "tunnel": { "version": "0.0.6", @@ -593,6 +1371,11 @@ "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/utils/flakiness-dashboard/package.json b/utils/flakiness-dashboard/package.json index 3e13565339..d8844674b9 100644 --- a/utils/flakiness-dashboard/package.json +++ b/utils/flakiness-dashboard/package.json @@ -2,11 +2,10 @@ "name": "flakiness-dashboard", "version": "", "description": "", - "scripts": { - "test": "echo \"No tests yet...\"" - }, + "main": "processing/index.js", "author": "", "dependencies": { - "@azure/storage-blob": "^12.16.0" + "@azure/identity": "^4.1.0", + "@azure/storage-blob": "^12.17.0" } } diff --git a/utils/flakiness-dashboard/processing/host.json b/utils/flakiness-dashboard/processing/host.json deleted file mode 100644 index 7a24f01b78..0000000000 --- a/utils/flakiness-dashboard/processing/host.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": "2.0", - "extensions": { - "queues": { - "batchSize": 1, - "newBatchThreshold": 0 - } - }, - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[1.*, 2.0.0)" - } -} diff --git a/utils/flakiness-dashboard/processing/utils.js b/utils/flakiness-dashboard/processing/utils.js index 5bc6888a8f..a0f166f7e5 100644 --- a/utils/flakiness-dashboard/processing/utils.js +++ b/utils/flakiness-dashboard/processing/utils.js @@ -13,14 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -const { BlobServiceClient } = require("@azure/storage-blob"); +// @ts-check +const { DefaultAzureCredential } = require('@azure/identity'); +const { BlobServiceClient } = require('@azure/storage-blob'); +const defaultAzureCredential = new DefaultAzureCredential(); const zlib = require('zlib'); const util = require('util'); const gzipAsync = util.promisify(zlib.gzip); const gunzipAsync = util.promisify(zlib.gunzip); -const blobServiceClient = BlobServiceClient.fromConnectionString(process.env.AzureWebJobsStorage); +const AZURE_STORAGE_ACCOUNT = 'folioflakinessdashboard'; + +const blobServiceClient = new BlobServiceClient( + `https://${AZURE_STORAGE_ACCOUNT}.blob.core.windows.net`, + defaultAzureCredential +); function flattenSpecs(suite, result = [], titlePaths = []) { if (suite.suites) { diff --git a/utils/generate_types/index.js b/utils/generate_types/index.js index 24966b1704..e6b549c173 100644 --- a/utils/generate_types/index.js +++ b/utils/generate_types/index.js @@ -283,7 +283,7 @@ class TypesGenerator { parts.push(this.writeComment(comment, indent)); else parts.push(this.writeComment(commentForMethod[method], indent)); - parts.push(` ${method}(event: '${eventName}', listener: (${params}) => void): this;\n`); + parts.push(` ${method}(event: '${eventName}', listener: (${params}) => any): this;\n`); } } diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index 9d58db13fe..4907995f3e 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -39,7 +39,7 @@ interface TestProject { export interface Project extends TestProject { } -export interface ProjectInWorker { +export interface FullProject { use: UseOptions; } @@ -57,8 +57,8 @@ export interface Config extends TestConfig { - projects: ProjectInWorker[]; +export interface FullConfig { + projects: FullProject[]; reporter: ReporterDescription[]; webServer: TestConfigWebServer | null; } diff --git a/utils/generate_types/overrides-testReporter.d.ts b/utils/generate_types/overrides-testReporter.d.ts index e5bf37cdc8..51eab7e370 100644 --- a/utils/generate_types/overrides-testReporter.d.ts +++ b/utils/generate_types/overrides-testReporter.d.ts @@ -14,20 +14,8 @@ * limitations under the License. */ -import type { TestStatus, Metadata, PlaywrightTestOptions, PlaywrightWorkerOptions, ReporterDescription, ConfigInWorker } from './test'; -export type { TestStatus } from './test'; - -type UseOptions = Partial & Partial; - -export interface FullConfig { - projects: FullProject[]; - reporter: ReporterDescription[]; - webServer: ConfigInWorker['webServer']; -} - -export interface FullProject { - use: UseOptions; -} +import type { TestStatus, Metadata, PlaywrightTestOptions, PlaywrightWorkerOptions, ReporterDescription, FullConfig, FullProject } from './test'; +export type { FullConfig, FullProject, TestStatus } from './test'; /** * Result of the full test run. diff --git a/utils/generate_types/test/test.ts b/utils/generate_types/test/test.ts index e13c2ac43c..c1fd13506b 100644 --- a/utils/generate_types/test/test.ts +++ b/utils/generate_types/test/test.ts @@ -154,8 +154,6 @@ playwright.chromium.launch().then(async browser => { return 'something random for no reason'; }); - await page.addLocatorHandler(page.locator(''), () => {}); - await page.addLocatorHandler(page.locator(''), () => 42); await page.addLocatorHandler(page.locator(''), async () => { }); await page.addLocatorHandler(page.locator(''), async () => 42); await page.addLocatorHandler(page.locator(''), () => Promise.resolve(42)); @@ -921,6 +919,10 @@ playwright.chromium.launch().then(async browser => { .removeListener('close', listener) .off('close', listener); } + { + const page: playwright.Page = {} as any; + page.on('dialog', dialog => dialog.accept()); + } }); // waitForResponse callback predicate