mirror of
https://github.com/TheBinaryNinja/tvapp2.git
synced 2026-06-04 16:05:42 -04:00
build: push tvapp v2 docker files
This commit is contained in:
6
node_modules/playwright/.eslintrc.js
generated
vendored
Normal file
6
node_modules/playwright/.eslintrc.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
extends: '../../.eslintrc-with-ts-config.js',
|
||||
rules: {
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
},
|
||||
};
|
||||
202
node_modules/playwright/LICENSE
generated
vendored
Normal file
202
node_modules/playwright/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Portions Copyright (c) Microsoft Corporation.
|
||||
Portions Copyright 2017 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
5
node_modules/playwright/NOTICE
generated
vendored
Normal file
5
node_modules/playwright/NOTICE
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
Playwright
|
||||
Copyright (c) Microsoft Corporation
|
||||
|
||||
This software contains code derived from the Puppeteer project (https://github.com/puppeteer/puppeteer),
|
||||
available under the Apache 2.0 license (https://github.com/puppeteer/puppeteer/blob/master/LICENSE).
|
||||
168
node_modules/playwright/README.md
generated
vendored
Normal file
168
node_modules/playwright/README.md
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
# 🎭 Playwright
|
||||
|
||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
||||
|
||||
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
||||
|
||||
Playwright is a framework for Web Testing and Automation. It allows testing [Chromium](https://www.chromium.org/Home), [Firefox](https://www.mozilla.org/en-US/firefox/new/) and [WebKit](https://webkit.org/) with a single API. Playwright is built to enable cross-browser web automation that is **ever-green**, **capable**, **reliable** and **fast**.
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->131.0.6778.33<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->18.2<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->132.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
|
||||
Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.
|
||||
|
||||
Looking for Playwright for [Python](https://playwright.dev/python/docs/intro), [.NET](https://playwright.dev/dotnet/docs/intro), or [Java](https://playwright.dev/java/docs/intro)?
|
||||
|
||||
## Installation
|
||||
|
||||
Playwright has its own test runner for end-to-end tests, we call it Playwright Test.
|
||||
|
||||
### Using init command
|
||||
|
||||
The easiest way to get started with Playwright Test is to run the init command.
|
||||
|
||||
```Shell
|
||||
# Run from your project's root directory
|
||||
npm init playwright@latest
|
||||
# Or create a new project
|
||||
npm init playwright@latest new-project
|
||||
```
|
||||
|
||||
This will create a configuration file, optionally add examples, a GitHub Action workflow and a first test example.spec.ts. You can now jump directly to writing assertions section.
|
||||
|
||||
### Manually
|
||||
|
||||
Add dependency and install browsers.
|
||||
|
||||
```Shell
|
||||
npm i -D @playwright/test
|
||||
# install supported browsers
|
||||
npx playwright install
|
||||
```
|
||||
|
||||
You can optionally install only selected browsers, see [install browsers](https://playwright.dev/docs/cli#install-browsers) for more details. Or you can install no browsers at all and use existing [browser channels](https://playwright.dev/docs/browsers).
|
||||
|
||||
* [Getting started](https://playwright.dev/docs/intro)
|
||||
* [API reference](https://playwright.dev/docs/api/class-playwright)
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Resilient • No flaky tests
|
||||
|
||||
**Auto-wait**. Playwright waits for elements to be actionable prior to performing actions. It also has a rich set of introspection events. The combination of the two eliminates the need for artificial timeouts - a primary cause of flaky tests.
|
||||
|
||||
**Web-first assertions**. Playwright assertions are created specifically for the dynamic web. Checks are automatically retried until the necessary conditions are met.
|
||||
|
||||
**Tracing**. Configure test retry strategy, capture execution trace, videos and screenshots to eliminate flakes.
|
||||
|
||||
### No trade-offs • No limits
|
||||
|
||||
Browsers run web content belonging to different origins in different processes. Playwright is aligned with the architecture of the modern browsers and runs tests out-of-process. This makes Playwright free of the typical in-process test runner limitations.
|
||||
|
||||
**Multiple everything**. Test scenarios that span multiple tabs, multiple origins and multiple users. Create scenarios with different contexts for different users and run them against your server, all in one test.
|
||||
|
||||
**Trusted events**. Hover elements, interact with dynamic controls and produce trusted events. Playwright uses real browser input pipeline indistinguishable from the real user.
|
||||
|
||||
Test frames, pierce Shadow DOM. Playwright selectors pierce shadow DOM and allow entering frames seamlessly.
|
||||
|
||||
### Full isolation • Fast execution
|
||||
|
||||
**Browser contexts**. Playwright creates a browser context for each test. Browser context is equivalent to a brand new browser profile. This delivers full test isolation with zero overhead. Creating a new browser context only takes a handful of milliseconds.
|
||||
|
||||
**Log in once**. Save the authentication state of the context and reuse it in all the tests. This bypasses repetitive log-in operations in each test, yet delivers full isolation of independent tests.
|
||||
|
||||
### Powerful Tooling
|
||||
|
||||
**[Codegen](https://playwright.dev/docs/codegen)**. Generate tests by recording your actions. Save them into any language.
|
||||
|
||||
**[Playwright inspector](https://playwright.dev/docs/inspector)**. Inspect page, generate selectors, step through the test execution, see click points and explore execution logs.
|
||||
|
||||
**[Trace Viewer](https://playwright.dev/docs/trace-viewer)**. Capture all the information to investigate the test failure. Playwright trace contains test execution screencast, live DOM snapshots, action explorer, test source and many more.
|
||||
|
||||
Looking for Playwright for [TypeScript](https://playwright.dev/docs/intro), [JavaScript](https://playwright.dev/docs/intro), [Python](https://playwright.dev/python/docs/intro), [.NET](https://playwright.dev/dotnet/docs/intro), or [Java](https://playwright.dev/java/docs/intro)?
|
||||
|
||||
## Examples
|
||||
|
||||
To learn how to run these Playwright Test examples, check out our [getting started docs](https://playwright.dev/docs/intro).
|
||||
|
||||
#### Page screenshot
|
||||
|
||||
This code snippet navigates to Playwright homepage and saves a screenshot.
|
||||
|
||||
```TypeScript
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
test('Page Screenshot', async ({ page }) => {
|
||||
await page.goto('https://playwright.dev/');
|
||||
await page.screenshot({ path: `example.png` });
|
||||
});
|
||||
```
|
||||
|
||||
#### Mobile and geolocation
|
||||
|
||||
This snippet emulates Mobile Safari on a device at given geolocation, navigates to maps.google.com, performs the action and takes a screenshot.
|
||||
|
||||
```TypeScript
|
||||
import { test, devices } from '@playwright/test';
|
||||
|
||||
test.use({
|
||||
...devices['iPhone 13 Pro'],
|
||||
locale: 'en-US',
|
||||
geolocation: { longitude: 12.492507, latitude: 41.889938 },
|
||||
permissions: ['geolocation'],
|
||||
})
|
||||
|
||||
test('Mobile and geolocation', async ({ page }) => {
|
||||
await page.goto('https://maps.google.com');
|
||||
await page.getByText('Your location').click();
|
||||
await page.waitForRequest(/.*preview\/pwa/);
|
||||
await page.screenshot({ path: 'colosseum-iphone.png' });
|
||||
});
|
||||
```
|
||||
|
||||
#### Evaluate in browser context
|
||||
|
||||
This code snippet navigates to example.com, and executes a script in the page context.
|
||||
|
||||
```TypeScript
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
test('Evaluate in browser context', async ({ page }) => {
|
||||
await page.goto('https://www.example.com/');
|
||||
const dimensions = await page.evaluate(() => {
|
||||
return {
|
||||
width: document.documentElement.clientWidth,
|
||||
height: document.documentElement.clientHeight,
|
||||
deviceScaleFactor: window.devicePixelRatio
|
||||
}
|
||||
});
|
||||
console.log(dimensions);
|
||||
});
|
||||
```
|
||||
|
||||
#### Intercept network requests
|
||||
|
||||
This code snippet sets up request routing for a page to log all network requests.
|
||||
|
||||
```TypeScript
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
test('Intercept network requests', async ({ page }) => {
|
||||
// Log and continue all network requests
|
||||
await page.route('**', route => {
|
||||
console.log(route.request().url());
|
||||
route.continue();
|
||||
});
|
||||
await page.goto('http://todomvc.com');
|
||||
});
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
* [Documentation](https://playwright.dev)
|
||||
* [API reference](https://playwright.dev/docs/api/class-playwright/)
|
||||
* [Contribution guide](CONTRIBUTING.md)
|
||||
* [Changelog](https://github.com/microsoft/playwright/releases)
|
||||
19
node_modules/playwright/cli.js
generated
vendored
Normal file
19
node_modules/playwright/cli.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const { program } = require('./lib/program');
|
||||
program.parse(process.argv);
|
||||
17
node_modules/playwright/index.d.ts
generated
vendored
Normal file
17
node_modules/playwright/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from 'playwright-core';
|
||||
17
node_modules/playwright/index.js
generated
vendored
Normal file
17
node_modules/playwright/index.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
module.exports = require('playwright-core');
|
||||
18
node_modules/playwright/index.mjs
generated
vendored
Normal file
18
node_modules/playwright/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from 'playwright-core';
|
||||
import playwright from 'playwright-core';
|
||||
export default playwright;
|
||||
42
node_modules/playwright/jsx-runtime.js
generated
vendored
Normal file
42
node_modules/playwright/jsx-runtime.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function jsx(type, props, key) {
|
||||
return {
|
||||
__pw_type: 'jsx',
|
||||
type,
|
||||
props,
|
||||
key,
|
||||
};
|
||||
}
|
||||
|
||||
function jsxs(type, props, key) {
|
||||
return {
|
||||
__pw_type: 'jsx',
|
||||
type,
|
||||
props,
|
||||
key,
|
||||
};
|
||||
}
|
||||
|
||||
// this is used in <></> notation
|
||||
const Fragment = { __pw_jsx_fragment: true };
|
||||
|
||||
module.exports = {
|
||||
Fragment,
|
||||
jsx,
|
||||
jsxs,
|
||||
};
|
||||
21
node_modules/playwright/jsx-runtime.mjs
generated
vendored
Normal file
21
node_modules/playwright/jsx-runtime.mjs
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import jsxRuntime from './jsx-runtime.js';
|
||||
|
||||
export const jsx = jsxRuntime.jsx;
|
||||
export const jsxs = jsxRuntime.jsxs;
|
||||
export const Fragment = jsxRuntime.Fragment;
|
||||
264
node_modules/playwright/lib/common/config.js
generated
vendored
Normal file
264
node_modules/playwright/lib/common/config.js
generated
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.defaultTimeout = exports.defaultReporter = exports.defaultGrep = exports.builtInReporters = exports.FullProjectInternal = exports.FullConfigInternal = void 0;
|
||||
exports.getProjectId = getProjectId;
|
||||
exports.takeFirst = takeFirst;
|
||||
exports.toReporters = toReporters;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _os = _interopRequireDefault(require("os"));
|
||||
var _util = require("../util");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const defaultTimeout = exports.defaultTimeout = 30000;
|
||||
class FullConfigInternal {
|
||||
constructor(location, userConfig, configCLIOverrides) {
|
||||
var _this$globalSetups$, _this$globalTeardowns;
|
||||
this.config = void 0;
|
||||
this.configDir = void 0;
|
||||
this.configCLIOverrides = void 0;
|
||||
this.webServers = void 0;
|
||||
this.plugins = void 0;
|
||||
this.projects = [];
|
||||
this.singleTSConfigPath = void 0;
|
||||
this.cliArgs = [];
|
||||
this.cliGrep = void 0;
|
||||
this.cliGrepInvert = void 0;
|
||||
this.cliOnlyChanged = void 0;
|
||||
this.cliProjectFilter = void 0;
|
||||
this.cliListOnly = false;
|
||||
this.cliPassWithNoTests = void 0;
|
||||
this.cliFailOnFlakyTests = void 0;
|
||||
this.cliLastFailed = void 0;
|
||||
this.testIdMatcher = void 0;
|
||||
this.defineConfigWasUsed = false;
|
||||
this.globalSetups = [];
|
||||
this.globalTeardowns = [];
|
||||
if (configCLIOverrides.projects && userConfig.projects) throw new Error(`Cannot use --browser option when configuration file defines projects. Specify browserName in the projects instead.`);
|
||||
const {
|
||||
resolvedConfigFile,
|
||||
configDir
|
||||
} = location;
|
||||
const packageJsonPath = (0, _util.getPackageJsonPath)(configDir);
|
||||
const packageJsonDir = packageJsonPath ? _path.default.dirname(packageJsonPath) : process.cwd();
|
||||
this.configDir = configDir;
|
||||
this.configCLIOverrides = configCLIOverrides;
|
||||
const privateConfiguration = userConfig['@playwright/test'];
|
||||
this.plugins = ((privateConfiguration === null || privateConfiguration === void 0 ? void 0 : privateConfiguration.plugins) || []).map(p => ({
|
||||
factory: p
|
||||
}));
|
||||
this.singleTSConfigPath = pathResolve(configDir, userConfig.tsconfig);
|
||||
this.globalSetups = (Array.isArray(userConfig.globalSetup) ? userConfig.globalSetup : [userConfig.globalSetup]).map(s => resolveScript(s, configDir)).filter(script => script !== undefined);
|
||||
this.globalTeardowns = (Array.isArray(userConfig.globalTeardown) ? userConfig.globalTeardown : [userConfig.globalTeardown]).map(s => resolveScript(s, configDir)).filter(script => script !== undefined);
|
||||
this.config = {
|
||||
configFile: resolvedConfigFile,
|
||||
rootDir: pathResolve(configDir, userConfig.testDir) || configDir,
|
||||
forbidOnly: takeFirst(configCLIOverrides.forbidOnly, userConfig.forbidOnly, false),
|
||||
fullyParallel: takeFirst(configCLIOverrides.fullyParallel, userConfig.fullyParallel, false),
|
||||
globalSetup: (_this$globalSetups$ = this.globalSetups[0]) !== null && _this$globalSetups$ !== void 0 ? _this$globalSetups$ : null,
|
||||
globalTeardown: (_this$globalTeardowns = this.globalTeardowns[0]) !== null && _this$globalTeardowns !== void 0 ? _this$globalTeardowns : null,
|
||||
globalTimeout: takeFirst(configCLIOverrides.globalTimeout, userConfig.globalTimeout, 0),
|
||||
grep: takeFirst(userConfig.grep, defaultGrep),
|
||||
grepInvert: takeFirst(userConfig.grepInvert, null),
|
||||
maxFailures: takeFirst(configCLIOverrides.debug ? 1 : undefined, configCLIOverrides.maxFailures, userConfig.maxFailures, 0),
|
||||
metadata: takeFirst(userConfig.metadata, {}),
|
||||
preserveOutput: takeFirst(userConfig.preserveOutput, 'always'),
|
||||
reporter: takeFirst(configCLIOverrides.reporter, resolveReporters(userConfig.reporter, configDir), [[defaultReporter]]),
|
||||
reportSlowTests: takeFirst(userConfig.reportSlowTests, {
|
||||
max: 5,
|
||||
threshold: 15000
|
||||
}),
|
||||
quiet: takeFirst(configCLIOverrides.quiet, userConfig.quiet, false),
|
||||
projects: [],
|
||||
shard: takeFirst(configCLIOverrides.shard, userConfig.shard, null),
|
||||
updateSnapshots: takeFirst(configCLIOverrides.updateSnapshots, userConfig.updateSnapshots, 'missing'),
|
||||
version: require('../../package.json').version,
|
||||
workers: 0,
|
||||
webServer: null
|
||||
};
|
||||
for (const key in userConfig) {
|
||||
if (key.startsWith('@')) this.config[key] = userConfig[key];
|
||||
}
|
||||
this.config[configInternalSymbol] = this;
|
||||
const workers = takeFirst(configCLIOverrides.debug ? 1 : undefined, configCLIOverrides.workers, userConfig.workers, '50%');
|
||||
if (typeof workers === 'string') {
|
||||
if (workers.endsWith('%')) {
|
||||
const cpus = _os.default.cpus().length;
|
||||
this.config.workers = Math.max(1, Math.floor(cpus * (parseInt(workers, 10) / 100)));
|
||||
} else {
|
||||
this.config.workers = parseWorkers(workers);
|
||||
}
|
||||
} else {
|
||||
this.config.workers = workers;
|
||||
}
|
||||
const webServers = takeFirst(userConfig.webServer, null);
|
||||
if (Array.isArray(webServers)) {
|
||||
// multiple web server mode
|
||||
// Due to previous choices, this value shows up to the user in globalSetup as part of FullConfig. Arrays are not supported by the old type.
|
||||
this.config.webServer = null;
|
||||
this.webServers = webServers;
|
||||
} else if (webServers) {
|
||||
// legacy singleton mode
|
||||
this.config.webServer = webServers;
|
||||
this.webServers = [webServers];
|
||||
} else {
|
||||
this.webServers = [];
|
||||
}
|
||||
const projectConfigs = configCLIOverrides.projects || userConfig.projects || [userConfig];
|
||||
this.projects = projectConfigs.map(p => new FullProjectInternal(configDir, userConfig, this, p, this.configCLIOverrides, packageJsonDir));
|
||||
resolveProjectDependencies(this.projects);
|
||||
this._assignUniqueProjectIds(this.projects);
|
||||
this.config.projects = this.projects.map(p => p.project);
|
||||
}
|
||||
_assignUniqueProjectIds(projects) {
|
||||
const usedNames = new Set();
|
||||
for (const p of projects) {
|
||||
const name = p.project.name || '';
|
||||
for (let i = 0; i < projects.length; ++i) {
|
||||
const candidate = name + (i ? i : '');
|
||||
if (usedNames.has(candidate)) continue;
|
||||
p.id = candidate;
|
||||
p.project.__projectId = p.id;
|
||||
usedNames.add(candidate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.FullConfigInternal = FullConfigInternal;
|
||||
class FullProjectInternal {
|
||||
constructor(configDir, config, fullConfig, projectConfig, configCLIOverrides, packageJsonDir) {
|
||||
var _this$expect$toHaveSc;
|
||||
this.project = void 0;
|
||||
this.fullConfig = void 0;
|
||||
this.fullyParallel = void 0;
|
||||
this.expect = void 0;
|
||||
this.respectGitIgnore = void 0;
|
||||
this.snapshotPathTemplate = void 0;
|
||||
this.ignoreSnapshots = void 0;
|
||||
this.id = '';
|
||||
this.deps = [];
|
||||
this.teardown = void 0;
|
||||
this.fullConfig = fullConfig;
|
||||
const testDir = takeFirst(pathResolve(configDir, projectConfig.testDir), pathResolve(configDir, config.testDir), fullConfig.configDir);
|
||||
const defaultSnapshotPathTemplate = '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}';
|
||||
this.snapshotPathTemplate = takeFirst(projectConfig.snapshotPathTemplate, config.snapshotPathTemplate, defaultSnapshotPathTemplate);
|
||||
this.project = {
|
||||
grep: takeFirst(projectConfig.grep, config.grep, defaultGrep),
|
||||
grepInvert: takeFirst(projectConfig.grepInvert, config.grepInvert, null),
|
||||
outputDir: takeFirst(configCLIOverrides.outputDir, pathResolve(configDir, projectConfig.outputDir), pathResolve(configDir, config.outputDir), _path.default.join(packageJsonDir, 'test-results')),
|
||||
// Note: we either apply the cli override for repeatEach or not, depending on whether the
|
||||
// project is top-level vs dependency. See collectProjectsAndTestFiles in loadUtils.
|
||||
repeatEach: takeFirst(projectConfig.repeatEach, config.repeatEach, 1),
|
||||
retries: takeFirst(configCLIOverrides.retries, projectConfig.retries, config.retries, 0),
|
||||
metadata: takeFirst(projectConfig.metadata, config.metadata, {}),
|
||||
name: takeFirst(projectConfig.name, config.name, ''),
|
||||
testDir,
|
||||
snapshotDir: takeFirst(pathResolve(configDir, projectConfig.snapshotDir), pathResolve(configDir, config.snapshotDir), testDir),
|
||||
testIgnore: takeFirst(projectConfig.testIgnore, config.testIgnore, []),
|
||||
testMatch: takeFirst(projectConfig.testMatch, config.testMatch, '**/*.@(spec|test).?(c|m)[jt]s?(x)'),
|
||||
timeout: takeFirst(configCLIOverrides.debug ? 0 : undefined, configCLIOverrides.timeout, projectConfig.timeout, config.timeout, defaultTimeout),
|
||||
use: (0, _util.mergeObjects)(config.use, projectConfig.use, configCLIOverrides.use),
|
||||
dependencies: projectConfig.dependencies || [],
|
||||
teardown: projectConfig.teardown
|
||||
};
|
||||
this.fullyParallel = takeFirst(configCLIOverrides.fullyParallel, projectConfig.fullyParallel, config.fullyParallel, undefined);
|
||||
this.expect = takeFirst(projectConfig.expect, config.expect, {});
|
||||
if ((_this$expect$toHaveSc = this.expect.toHaveScreenshot) !== null && _this$expect$toHaveSc !== void 0 && _this$expect$toHaveSc.stylePath) {
|
||||
const stylePaths = Array.isArray(this.expect.toHaveScreenshot.stylePath) ? this.expect.toHaveScreenshot.stylePath : [this.expect.toHaveScreenshot.stylePath];
|
||||
this.expect.toHaveScreenshot.stylePath = stylePaths.map(stylePath => _path.default.resolve(configDir, stylePath));
|
||||
}
|
||||
this.respectGitIgnore = takeFirst(projectConfig.respectGitIgnore, config.respectGitIgnore, !projectConfig.testDir && !config.testDir);
|
||||
this.ignoreSnapshots = takeFirst(configCLIOverrides.ignoreSnapshots, projectConfig.ignoreSnapshots, config.ignoreSnapshots, false);
|
||||
}
|
||||
}
|
||||
exports.FullProjectInternal = FullProjectInternal;
|
||||
function takeFirst(...args) {
|
||||
for (const arg of args) {
|
||||
if (arg !== undefined) return arg;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function pathResolve(baseDir, relative) {
|
||||
if (!relative) return undefined;
|
||||
return _path.default.resolve(baseDir, relative);
|
||||
}
|
||||
function resolveReporters(reporters, rootDir) {
|
||||
var _toReporters;
|
||||
return (_toReporters = toReporters(reporters)) === null || _toReporters === void 0 ? void 0 : _toReporters.map(([id, arg]) => {
|
||||
if (builtInReporters.includes(id)) return [id, arg];
|
||||
return [require.resolve(id, {
|
||||
paths: [rootDir]
|
||||
}), arg];
|
||||
});
|
||||
}
|
||||
function parseWorkers(workers) {
|
||||
const parsedWorkers = parseInt(workers, 10);
|
||||
if (isNaN(parsedWorkers)) throw new Error(`Workers ${workers} must be a number or percentage.`);
|
||||
return parsedWorkers;
|
||||
}
|
||||
function resolveProjectDependencies(projects) {
|
||||
const teardownSet = new Set();
|
||||
for (const project of projects) {
|
||||
for (const dependencyName of project.project.dependencies) {
|
||||
const dependencies = projects.filter(p => p.project.name === dependencyName);
|
||||
if (!dependencies.length) throw new Error(`Project '${project.project.name}' depends on unknown project '${dependencyName}'`);
|
||||
if (dependencies.length > 1) throw new Error(`Project dependencies should have unique names, reading ${dependencyName}`);
|
||||
project.deps.push(...dependencies);
|
||||
}
|
||||
if (project.project.teardown) {
|
||||
const teardowns = projects.filter(p => p.project.name === project.project.teardown);
|
||||
if (!teardowns.length) throw new Error(`Project '${project.project.name}' has unknown teardown project '${project.project.teardown}'`);
|
||||
if (teardowns.length > 1) throw new Error(`Project teardowns should have unique names, reading ${project.project.teardown}`);
|
||||
const teardown = teardowns[0];
|
||||
project.teardown = teardown;
|
||||
teardownSet.add(teardown);
|
||||
}
|
||||
}
|
||||
for (const teardown of teardownSet) {
|
||||
if (teardown.deps.length) throw new Error(`Teardown project ${teardown.project.name} must not have dependencies`);
|
||||
}
|
||||
for (const project of projects) {
|
||||
for (const dep of project.deps) {
|
||||
if (teardownSet.has(dep)) throw new Error(`Project ${project.project.name} must not depend on a teardown project ${dep.project.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
function toReporters(reporters) {
|
||||
if (!reporters) return;
|
||||
if (typeof reporters === 'string') return [[reporters]];
|
||||
return reporters;
|
||||
}
|
||||
const builtInReporters = exports.builtInReporters = ['list', 'line', 'dot', 'json', 'junit', 'null', 'github', 'html', 'blob'];
|
||||
function resolveScript(id, rootDir) {
|
||||
if (!id) return undefined;
|
||||
const localPath = _path.default.resolve(rootDir, id);
|
||||
if (_fs.default.existsSync(localPath)) return localPath;
|
||||
return require.resolve(id, {
|
||||
paths: [rootDir]
|
||||
});
|
||||
}
|
||||
const defaultGrep = exports.defaultGrep = /.*/;
|
||||
const defaultReporter = exports.defaultReporter = process.env.CI ? 'dot' : 'list';
|
||||
const configInternalSymbol = Symbol('configInternalSymbol');
|
||||
function getProjectId(project) {
|
||||
return project.__projectId;
|
||||
}
|
||||
330
node_modules/playwright/lib/common/configLoader.js
generated
vendored
Normal file
330
node_modules/playwright/lib/common/configLoader.js
generated
vendored
Normal file
@@ -0,0 +1,330 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.defineConfig = void 0;
|
||||
exports.deserializeConfig = deserializeConfig;
|
||||
exports.loadConfig = loadConfig;
|
||||
exports.loadConfigFromFileRestartIfNeeded = loadConfigFromFileRestartIfNeeded;
|
||||
exports.loadEmptyConfigForMergeReports = loadEmptyConfigForMergeReports;
|
||||
exports.resolveConfigLocation = resolveConfigLocation;
|
||||
exports.restartWithExperimentalTsEsm = restartWithExperimentalTsEsm;
|
||||
var fs = _interopRequireWildcard(require("fs"));
|
||||
var path = _interopRequireWildcard(require("path"));
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _transform = require("../transform/transform");
|
||||
var _util = require("../util");
|
||||
var _config = require("./config");
|
||||
var _compilationCache = require("../transform/compilationCache");
|
||||
var _esmLoaderHost = require("./esmLoaderHost");
|
||||
var _esmUtils = require("../transform/esmUtils");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const kDefineConfigWasUsed = Symbol('defineConfigWasUsed');
|
||||
const defineConfig = (...configs) => {
|
||||
let result = configs[0];
|
||||
for (let i = 1; i < configs.length; ++i) {
|
||||
const config = configs[i];
|
||||
result = {
|
||||
...result,
|
||||
...config,
|
||||
expect: {
|
||||
...result.expect,
|
||||
...config.expect
|
||||
},
|
||||
use: {
|
||||
...result.use,
|
||||
...config.use
|
||||
},
|
||||
build: {
|
||||
...result.build,
|
||||
...config.build
|
||||
},
|
||||
webServer: [...(Array.isArray(result.webServer) ? result.webServer : result.webServer ? [result.webServer] : []), ...(Array.isArray(config.webServer) ? config.webServer : config.webServer ? [config.webServer] : [])]
|
||||
};
|
||||
if (!result.projects && !config.projects) continue;
|
||||
const projectOverrides = new Map();
|
||||
for (const project of config.projects || []) projectOverrides.set(project.name, project);
|
||||
const projects = [];
|
||||
for (const project of result.projects || []) {
|
||||
const projectOverride = projectOverrides.get(project.name);
|
||||
if (projectOverride) {
|
||||
projects.push({
|
||||
...project,
|
||||
...projectOverride,
|
||||
use: {
|
||||
...project.use,
|
||||
...projectOverride.use
|
||||
}
|
||||
});
|
||||
projectOverrides.delete(project.name);
|
||||
} else {
|
||||
projects.push(project);
|
||||
}
|
||||
}
|
||||
projects.push(...projectOverrides.values());
|
||||
result.projects = projects;
|
||||
}
|
||||
result[kDefineConfigWasUsed] = true;
|
||||
return result;
|
||||
};
|
||||
exports.defineConfig = defineConfig;
|
||||
async function deserializeConfig(data) {
|
||||
if (data.compilationCache) (0, _compilationCache.addToCompilationCache)(data.compilationCache);
|
||||
return await loadConfig(data.location, data.configCLIOverrides);
|
||||
}
|
||||
async function loadUserConfig(location) {
|
||||
let object = location.resolvedConfigFile ? await (0, _transform.requireOrImport)(location.resolvedConfigFile) : {};
|
||||
if (object && typeof object === 'object' && 'default' in object) object = object['default'];
|
||||
return object;
|
||||
}
|
||||
async function loadConfig(location, overrides, ignoreProjectDependencies = false) {
|
||||
var _playwrightTest, _userConfig$build;
|
||||
// 1. Setup tsconfig; configure ESM loader with tsconfig and compilation cache.
|
||||
(0, _transform.setSingleTSConfig)(overrides === null || overrides === void 0 ? void 0 : overrides.tsconfig);
|
||||
await (0, _esmLoaderHost.configureESMLoader)();
|
||||
|
||||
// 2. Load and validate playwright config.
|
||||
const userConfig = await loadUserConfig(location);
|
||||
validateConfig(location.resolvedConfigFile || '<default config>', userConfig);
|
||||
const fullConfig = new _config.FullConfigInternal(location, userConfig, overrides || {});
|
||||
fullConfig.defineConfigWasUsed = !!userConfig[kDefineConfigWasUsed];
|
||||
if (ignoreProjectDependencies) {
|
||||
for (const project of fullConfig.projects) {
|
||||
project.deps = [];
|
||||
project.teardown = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Load transform options from the playwright config.
|
||||
const babelPlugins = ((_playwrightTest = userConfig['@playwright/test']) === null || _playwrightTest === void 0 ? void 0 : _playwrightTest.babelPlugins) || [];
|
||||
const external = ((_userConfig$build = userConfig.build) === null || _userConfig$build === void 0 ? void 0 : _userConfig$build.external) || [];
|
||||
(0, _transform.setTransformConfig)({
|
||||
babelPlugins,
|
||||
external
|
||||
});
|
||||
if (!(overrides !== null && overrides !== void 0 && overrides.tsconfig)) (0, _transform.setSingleTSConfig)(fullConfig === null || fullConfig === void 0 ? void 0 : fullConfig.singleTSConfigPath);
|
||||
|
||||
// 4. Send transform options to ESM loader.
|
||||
await (0, _esmLoaderHost.configureESMLoaderTransformConfig)();
|
||||
return fullConfig;
|
||||
}
|
||||
function validateConfig(file, config) {
|
||||
if (typeof config !== 'object' || !config) throw (0, _util.errorWithFile)(file, `Configuration file must export a single object`);
|
||||
validateProject(file, config, 'config');
|
||||
if ('forbidOnly' in config && config.forbidOnly !== undefined) {
|
||||
if (typeof config.forbidOnly !== 'boolean') throw (0, _util.errorWithFile)(file, `config.forbidOnly must be a boolean`);
|
||||
}
|
||||
if ('globalSetup' in config && config.globalSetup !== undefined) {
|
||||
if (Array.isArray(config.globalSetup)) {
|
||||
config.globalSetup.forEach((item, index) => {
|
||||
if (typeof item !== 'string') throw (0, _util.errorWithFile)(file, `config.globalSetup[${index}] must be a string`);
|
||||
});
|
||||
} else if (typeof config.globalSetup !== 'string') {
|
||||
throw (0, _util.errorWithFile)(file, `config.globalSetup must be a string`);
|
||||
}
|
||||
}
|
||||
if ('globalTeardown' in config && config.globalTeardown !== undefined) {
|
||||
if (Array.isArray(config.globalTeardown)) {
|
||||
config.globalTeardown.forEach((item, index) => {
|
||||
if (typeof item !== 'string') throw (0, _util.errorWithFile)(file, `config.globalTeardown[${index}] must be a string`);
|
||||
});
|
||||
} else if (typeof config.globalTeardown !== 'string') {
|
||||
throw (0, _util.errorWithFile)(file, `config.globalTeardown must be a string`);
|
||||
}
|
||||
}
|
||||
if ('globalTimeout' in config && config.globalTimeout !== undefined) {
|
||||
if (typeof config.globalTimeout !== 'number' || config.globalTimeout < 0) throw (0, _util.errorWithFile)(file, `config.globalTimeout must be a non-negative number`);
|
||||
}
|
||||
if ('grep' in config && config.grep !== undefined) {
|
||||
if (Array.isArray(config.grep)) {
|
||||
config.grep.forEach((item, index) => {
|
||||
if (!(0, _utils.isRegExp)(item)) throw (0, _util.errorWithFile)(file, `config.grep[${index}] must be a RegExp`);
|
||||
});
|
||||
} else if (!(0, _utils.isRegExp)(config.grep)) {
|
||||
throw (0, _util.errorWithFile)(file, `config.grep must be a RegExp`);
|
||||
}
|
||||
}
|
||||
if ('grepInvert' in config && config.grepInvert !== undefined) {
|
||||
if (Array.isArray(config.grepInvert)) {
|
||||
config.grepInvert.forEach((item, index) => {
|
||||
if (!(0, _utils.isRegExp)(item)) throw (0, _util.errorWithFile)(file, `config.grepInvert[${index}] must be a RegExp`);
|
||||
});
|
||||
} else if (!(0, _utils.isRegExp)(config.grepInvert)) {
|
||||
throw (0, _util.errorWithFile)(file, `config.grepInvert must be a RegExp`);
|
||||
}
|
||||
}
|
||||
if ('maxFailures' in config && config.maxFailures !== undefined) {
|
||||
if (typeof config.maxFailures !== 'number' || config.maxFailures < 0) throw (0, _util.errorWithFile)(file, `config.maxFailures must be a non-negative number`);
|
||||
}
|
||||
if ('preserveOutput' in config && config.preserveOutput !== undefined) {
|
||||
if (typeof config.preserveOutput !== 'string' || !['always', 'never', 'failures-only'].includes(config.preserveOutput)) throw (0, _util.errorWithFile)(file, `config.preserveOutput must be one of "always", "never" or "failures-only"`);
|
||||
}
|
||||
if ('projects' in config && config.projects !== undefined) {
|
||||
if (!Array.isArray(config.projects)) throw (0, _util.errorWithFile)(file, `config.projects must be an array`);
|
||||
config.projects.forEach((project, index) => {
|
||||
validateProject(file, project, `config.projects[${index}]`);
|
||||
});
|
||||
}
|
||||
if ('quiet' in config && config.quiet !== undefined) {
|
||||
if (typeof config.quiet !== 'boolean') throw (0, _util.errorWithFile)(file, `config.quiet must be a boolean`);
|
||||
}
|
||||
if ('reporter' in config && config.reporter !== undefined) {
|
||||
if (Array.isArray(config.reporter)) {
|
||||
config.reporter.forEach((item, index) => {
|
||||
if (!Array.isArray(item) || item.length <= 0 || item.length > 2 || typeof item[0] !== 'string') throw (0, _util.errorWithFile)(file, `config.reporter[${index}] must be a tuple [name, optionalArgument]`);
|
||||
});
|
||||
} else if (typeof config.reporter !== 'string') {
|
||||
throw (0, _util.errorWithFile)(file, `config.reporter must be a string`);
|
||||
}
|
||||
}
|
||||
if ('reportSlowTests' in config && config.reportSlowTests !== undefined && config.reportSlowTests !== null) {
|
||||
if (!config.reportSlowTests || typeof config.reportSlowTests !== 'object') throw (0, _util.errorWithFile)(file, `config.reportSlowTests must be an object`);
|
||||
if (!('max' in config.reportSlowTests) || typeof config.reportSlowTests.max !== 'number' || config.reportSlowTests.max < 0) throw (0, _util.errorWithFile)(file, `config.reportSlowTests.max must be a non-negative number`);
|
||||
if (!('threshold' in config.reportSlowTests) || typeof config.reportSlowTests.threshold !== 'number' || config.reportSlowTests.threshold < 0) throw (0, _util.errorWithFile)(file, `config.reportSlowTests.threshold must be a non-negative number`);
|
||||
}
|
||||
if ('shard' in config && config.shard !== undefined && config.shard !== null) {
|
||||
if (!config.shard || typeof config.shard !== 'object') throw (0, _util.errorWithFile)(file, `config.shard must be an object`);
|
||||
if (!('total' in config.shard) || typeof config.shard.total !== 'number' || config.shard.total < 1) throw (0, _util.errorWithFile)(file, `config.shard.total must be a positive number`);
|
||||
if (!('current' in config.shard) || typeof config.shard.current !== 'number' || config.shard.current < 1 || config.shard.current > config.shard.total) throw (0, _util.errorWithFile)(file, `config.shard.current must be a positive number, not greater than config.shard.total`);
|
||||
}
|
||||
if ('updateSnapshots' in config && config.updateSnapshots !== undefined) {
|
||||
if (typeof config.updateSnapshots !== 'string' || !['all', 'none', 'missing'].includes(config.updateSnapshots)) throw (0, _util.errorWithFile)(file, `config.updateSnapshots must be one of "all", "none" or "missing"`);
|
||||
}
|
||||
if ('workers' in config && config.workers !== undefined) {
|
||||
if (typeof config.workers === 'number' && config.workers <= 0) throw (0, _util.errorWithFile)(file, `config.workers must be a positive number`);else if (typeof config.workers === 'string' && !config.workers.endsWith('%')) throw (0, _util.errorWithFile)(file, `config.workers must be a number or percentage`);
|
||||
}
|
||||
}
|
||||
function validateProject(file, project, title) {
|
||||
if (typeof project !== 'object' || !project) throw (0, _util.errorWithFile)(file, `${title} must be an object`);
|
||||
if ('name' in project && project.name !== undefined) {
|
||||
if (typeof project.name !== 'string') throw (0, _util.errorWithFile)(file, `${title}.name must be a string`);
|
||||
}
|
||||
if ('outputDir' in project && project.outputDir !== undefined) {
|
||||
if (typeof project.outputDir !== 'string') throw (0, _util.errorWithFile)(file, `${title}.outputDir must be a string`);
|
||||
}
|
||||
if ('repeatEach' in project && project.repeatEach !== undefined) {
|
||||
if (typeof project.repeatEach !== 'number' || project.repeatEach < 0) throw (0, _util.errorWithFile)(file, `${title}.repeatEach must be a non-negative number`);
|
||||
}
|
||||
if ('retries' in project && project.retries !== undefined) {
|
||||
if (typeof project.retries !== 'number' || project.retries < 0) throw (0, _util.errorWithFile)(file, `${title}.retries must be a non-negative number`);
|
||||
}
|
||||
if ('testDir' in project && project.testDir !== undefined) {
|
||||
if (typeof project.testDir !== 'string') throw (0, _util.errorWithFile)(file, `${title}.testDir must be a string`);
|
||||
}
|
||||
for (const prop of ['testIgnore', 'testMatch']) {
|
||||
if (prop in project && project[prop] !== undefined) {
|
||||
const value = project[prop];
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((item, index) => {
|
||||
if (typeof item !== 'string' && !(0, _utils.isRegExp)(item)) throw (0, _util.errorWithFile)(file, `${title}.${prop}[${index}] must be a string or a RegExp`);
|
||||
});
|
||||
} else if (typeof value !== 'string' && !(0, _utils.isRegExp)(value)) {
|
||||
throw (0, _util.errorWithFile)(file, `${title}.${prop} must be a string or a RegExp`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ('timeout' in project && project.timeout !== undefined) {
|
||||
if (typeof project.timeout !== 'number' || project.timeout < 0) throw (0, _util.errorWithFile)(file, `${title}.timeout must be a non-negative number`);
|
||||
}
|
||||
if ('use' in project && project.use !== undefined) {
|
||||
if (!project.use || typeof project.use !== 'object') throw (0, _util.errorWithFile)(file, `${title}.use must be an object`);
|
||||
}
|
||||
if ('ignoreSnapshots' in project && project.ignoreSnapshots !== undefined) {
|
||||
if (typeof project.ignoreSnapshots !== 'boolean') throw (0, _util.errorWithFile)(file, `${title}.ignoreSnapshots must be a boolean`);
|
||||
}
|
||||
}
|
||||
function resolveConfigLocation(configFile) {
|
||||
const configFileOrDirectory = configFile ? path.resolve(process.cwd(), configFile) : process.cwd();
|
||||
const resolvedConfigFile = resolveConfigFile(configFileOrDirectory);
|
||||
return {
|
||||
resolvedConfigFile,
|
||||
configDir: resolvedConfigFile ? path.dirname(resolvedConfigFile) : configFileOrDirectory
|
||||
};
|
||||
}
|
||||
function resolveConfigFile(configFileOrDirectory) {
|
||||
const resolveConfig = configFile => {
|
||||
if (fs.existsSync(configFile)) return configFile;
|
||||
};
|
||||
const resolveConfigFileFromDirectory = directory => {
|
||||
for (const ext of ['.ts', '.js', '.mts', '.mjs', '.cts', '.cjs']) {
|
||||
const configFile = resolveConfig(path.resolve(directory, 'playwright.config' + ext));
|
||||
if (configFile) return configFile;
|
||||
}
|
||||
};
|
||||
if (!fs.existsSync(configFileOrDirectory)) throw new Error(`${configFileOrDirectory} does not exist`);
|
||||
if (fs.statSync(configFileOrDirectory).isDirectory()) {
|
||||
// When passed a directory, look for a config file inside.
|
||||
const configFile = resolveConfigFileFromDirectory(configFileOrDirectory);
|
||||
if (configFile) return configFile;
|
||||
// If there is no config, assume this as a root testing directory.
|
||||
return undefined;
|
||||
}
|
||||
// When passed a file, it must be a config file.
|
||||
return configFileOrDirectory;
|
||||
}
|
||||
async function loadConfigFromFileRestartIfNeeded(configFile, overrides, ignoreDeps) {
|
||||
const location = resolveConfigLocation(configFile);
|
||||
if (restartWithExperimentalTsEsm(location.resolvedConfigFile)) return null;
|
||||
return await loadConfig(location, overrides, ignoreDeps);
|
||||
}
|
||||
async function loadEmptyConfigForMergeReports() {
|
||||
// Merge reports is "different" for no good reason. It should not pick up local config from the cwd.
|
||||
return await loadConfig({
|
||||
configDir: process.cwd()
|
||||
});
|
||||
}
|
||||
function restartWithExperimentalTsEsm(configFile, force = false) {
|
||||
// Opt-out switch.
|
||||
if (process.env.PW_DISABLE_TS_ESM) return false;
|
||||
|
||||
// There are two esm loader APIs:
|
||||
// - Older API that needs a process restart. Available in Node 16, 17, and non-latest 18, 19 and 20.
|
||||
// - Newer API that works in-process. Available in Node 21+ and latest 18, 19 and 20.
|
||||
|
||||
// First check whether we have already restarted with the ESM loader from the older API.
|
||||
if (globalThis.__esmLoaderPortPreV20) {
|
||||
// clear execArgv after restart, so that childProcess.fork in user code does not inherit our loader.
|
||||
process.execArgv = (0, _esmUtils.execArgvWithoutExperimentalLoaderOptions)();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now check for the newer API presence.
|
||||
if (!require('node:module').register) {
|
||||
// With older API requiring a process restart, do so conditionally on the config.
|
||||
const configIsModule = !!configFile && (0, _util.fileIsModule)(configFile);
|
||||
if (!force && !configIsModule) return false;
|
||||
const innerProcess = require('child_process').fork(require.resolve('../../cli'), process.argv.slice(2), {
|
||||
env: {
|
||||
...process.env,
|
||||
PW_TS_ESM_LEGACY_LOADER_ON: '1'
|
||||
},
|
||||
execArgv: (0, _esmUtils.execArgvWithExperimentalLoaderOptions)()
|
||||
});
|
||||
innerProcess.on('close', code => {
|
||||
if (code !== 0 && code !== null) (0, _utils.gracefullyProcessExitDoNotHang)(code);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// With the newer API, always enable the ESM loader, because it does not need a restart.
|
||||
(0, _esmLoaderHost.registerESMLoader)();
|
||||
return false;
|
||||
}
|
||||
96
node_modules/playwright/lib/common/esmLoaderHost.js
generated
vendored
Normal file
96
node_modules/playwright/lib/common/esmLoaderHost.js
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.configureESMLoader = configureESMLoader;
|
||||
exports.configureESMLoaderTransformConfig = configureESMLoaderTransformConfig;
|
||||
exports.esmLoaderRegistered = void 0;
|
||||
exports.incorporateCompilationCache = incorporateCompilationCache;
|
||||
exports.registerESMLoader = registerESMLoader;
|
||||
exports.startCollectingFileDeps = startCollectingFileDeps;
|
||||
exports.stopCollectingFileDeps = stopCollectingFileDeps;
|
||||
var _url = _interopRequireDefault(require("url"));
|
||||
var _compilationCache = require("../transform/compilationCache");
|
||||
var _transform = require("../transform/transform");
|
||||
var _portTransport = require("../transform/portTransport");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
let loaderChannel;
|
||||
// Node.js < 20
|
||||
if (globalThis.__esmLoaderPortPreV20) loaderChannel = createPortTransport(globalThis.__esmLoaderPortPreV20);
|
||||
|
||||
// Node.js >= 20
|
||||
let esmLoaderRegistered = exports.esmLoaderRegistered = false;
|
||||
function registerESMLoader() {
|
||||
const {
|
||||
port1,
|
||||
port2
|
||||
} = new MessageChannel();
|
||||
// register will wait until the loader is initialized.
|
||||
require('node:module').register(_url.default.pathToFileURL(require.resolve('../transform/esmLoader')), {
|
||||
parentURL: _url.default.pathToFileURL(__filename),
|
||||
data: {
|
||||
port: port2
|
||||
},
|
||||
transferList: [port2]
|
||||
});
|
||||
loaderChannel = createPortTransport(port1);
|
||||
exports.esmLoaderRegistered = esmLoaderRegistered = true;
|
||||
}
|
||||
function createPortTransport(port) {
|
||||
return new _portTransport.PortTransport(port, async (method, params) => {
|
||||
if (method === 'pushToCompilationCache') (0, _compilationCache.addToCompilationCache)(params.cache);
|
||||
});
|
||||
}
|
||||
async function startCollectingFileDeps() {
|
||||
if (!loaderChannel) return;
|
||||
await loaderChannel.send('startCollectingFileDeps', {});
|
||||
}
|
||||
async function stopCollectingFileDeps(file) {
|
||||
if (!loaderChannel) return;
|
||||
await loaderChannel.send('stopCollectingFileDeps', {
|
||||
file
|
||||
});
|
||||
}
|
||||
async function incorporateCompilationCache() {
|
||||
if (!loaderChannel) return;
|
||||
// This is needed to gather dependency information from the esm loader
|
||||
// that is populated from the resolve hook. We do not need to push
|
||||
// this information proactively during load, but gather it at the end.
|
||||
const result = await loaderChannel.send('getCompilationCache', {});
|
||||
(0, _compilationCache.addToCompilationCache)(result.cache);
|
||||
}
|
||||
async function configureESMLoader() {
|
||||
if (!loaderChannel) return;
|
||||
await loaderChannel.send('setSingleTSConfig', {
|
||||
tsconfig: (0, _transform.singleTSConfig)()
|
||||
});
|
||||
await loaderChannel.send('addToCompilationCache', {
|
||||
cache: (0, _compilationCache.serializeCompilationCache)()
|
||||
});
|
||||
}
|
||||
async function configureESMLoaderTransformConfig() {
|
||||
if (!loaderChannel) return;
|
||||
await loaderChannel.send('setSingleTSConfig', {
|
||||
tsconfig: (0, _transform.singleTSConfig)()
|
||||
});
|
||||
await loaderChannel.send('setTransformConfig', {
|
||||
config: (0, _transform.transformConfig)()
|
||||
});
|
||||
}
|
||||
30
node_modules/playwright/lib/common/expectBundle.js
generated
vendored
Normal file
30
node_modules/playwright/lib/common/expectBundle.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.printReceived = exports.mock = exports.matcherUtils = exports.expect = exports.asymmetricMatchers = exports.RECEIVED_COLOR = exports.INVERTED_COLOR = exports.EXPECTED_COLOR = void 0;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const expect = exports.expect = require('./expectBundleImpl').expect;
|
||||
const mock = exports.mock = require('./expectBundleImpl').mock;
|
||||
const asymmetricMatchers = exports.asymmetricMatchers = require('./expectBundleImpl').asymmetricMatchers;
|
||||
const matcherUtils = exports.matcherUtils = require('./expectBundleImpl').matcherUtils;
|
||||
const EXPECTED_COLOR = exports.EXPECTED_COLOR = require('./expectBundleImpl').EXPECTED_COLOR;
|
||||
const INVERTED_COLOR = exports.INVERTED_COLOR = require('./expectBundleImpl').INVERTED_COLOR;
|
||||
const RECEIVED_COLOR = exports.RECEIVED_COLOR = require('./expectBundleImpl').RECEIVED_COLOR;
|
||||
const printReceived = exports.printReceived = require('./expectBundleImpl').printReceived;
|
||||
389
node_modules/playwright/lib/common/expectBundleImpl.js
generated
vendored
Normal file
389
node_modules/playwright/lib/common/expectBundleImpl.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
323
node_modules/playwright/lib/common/fixtures.js
generated
vendored
Normal file
323
node_modules/playwright/lib/common/fixtures.js
generated
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.FixturePool = void 0;
|
||||
exports.fixtureParameterNames = fixtureParameterNames;
|
||||
exports.formatPotentiallyInternalLocation = formatPotentiallyInternalLocation;
|
||||
exports.inheritFixtureNames = inheritFixtureNames;
|
||||
var _util = require("../util");
|
||||
var crypto = _interopRequireWildcard(require("crypto"));
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const kScopeOrder = ['test', 'worker'];
|
||||
function isFixtureTuple(value) {
|
||||
return Array.isArray(value) && typeof value[1] === 'object';
|
||||
}
|
||||
function isFixtureOption(value) {
|
||||
return isFixtureTuple(value) && !!value[1].option;
|
||||
}
|
||||
class FixturePool {
|
||||
constructor(fixturesList, onLoadError, parentPool, disallowWorkerFixtures, optionOverrides) {
|
||||
var _optionOverrides$over;
|
||||
this.digest = void 0;
|
||||
this._registrations = void 0;
|
||||
this._onLoadError = void 0;
|
||||
this._registrations = new Map(parentPool ? parentPool._registrations : []);
|
||||
this._onLoadError = onLoadError;
|
||||
const allOverrides = (_optionOverrides$over = optionOverrides === null || optionOverrides === void 0 ? void 0 : optionOverrides.overrides) !== null && _optionOverrides$over !== void 0 ? _optionOverrides$over : {};
|
||||
const overrideKeys = new Set(Object.keys(allOverrides));
|
||||
for (const list of fixturesList) {
|
||||
this._appendFixtureList(list, !!disallowWorkerFixtures, false);
|
||||
|
||||
// Process option overrides immediately after original option definitions,
|
||||
// so that any test.use() override it.
|
||||
const selectedOverrides = {};
|
||||
for (const [key, value] of Object.entries(list.fixtures)) {
|
||||
if (isFixtureOption(value) && overrideKeys.has(key)) selectedOverrides[key] = [allOverrides[key], value[1]];
|
||||
}
|
||||
if (Object.entries(selectedOverrides).length) this._appendFixtureList({
|
||||
fixtures: selectedOverrides,
|
||||
location: optionOverrides.location
|
||||
}, !!disallowWorkerFixtures, true);
|
||||
}
|
||||
this.digest = this.validate();
|
||||
}
|
||||
_appendFixtureList(list, disallowWorkerFixtures, isOptionsOverride) {
|
||||
const {
|
||||
fixtures,
|
||||
location
|
||||
} = list;
|
||||
for (const entry of Object.entries(fixtures)) {
|
||||
const name = entry[0];
|
||||
let value = entry[1];
|
||||
let options;
|
||||
if (isFixtureTuple(value)) {
|
||||
var _value$1$auto;
|
||||
options = {
|
||||
auto: (_value$1$auto = value[1].auto) !== null && _value$1$auto !== void 0 ? _value$1$auto : false,
|
||||
scope: value[1].scope || 'test',
|
||||
option: !!value[1].option,
|
||||
timeout: value[1].timeout,
|
||||
customTitle: value[1].title,
|
||||
box: value[1].box
|
||||
};
|
||||
value = value[0];
|
||||
}
|
||||
let fn = value;
|
||||
const previous = this._registrations.get(name);
|
||||
if (previous && options) {
|
||||
if (previous.scope !== options.scope) {
|
||||
this._addLoadError(`Fixture "${name}" has already been registered as a { scope: '${previous.scope}' } fixture defined in ${(0, _util.formatLocation)(previous.location)}.`, location);
|
||||
continue;
|
||||
}
|
||||
if (previous.auto !== options.auto) {
|
||||
this._addLoadError(`Fixture "${name}" has already been registered as a { auto: '${previous.scope}' } fixture defined in ${(0, _util.formatLocation)(previous.location)}.`, location);
|
||||
continue;
|
||||
}
|
||||
} else if (previous) {
|
||||
options = {
|
||||
auto: previous.auto,
|
||||
scope: previous.scope,
|
||||
option: previous.option,
|
||||
timeout: previous.timeout,
|
||||
customTitle: previous.customTitle,
|
||||
box: previous.box
|
||||
};
|
||||
} else if (!options) {
|
||||
options = {
|
||||
auto: false,
|
||||
scope: 'test',
|
||||
option: false,
|
||||
timeout: undefined
|
||||
};
|
||||
}
|
||||
if (!kScopeOrder.includes(options.scope)) {
|
||||
this._addLoadError(`Fixture "${name}" has unknown { scope: '${options.scope}' }.`, location);
|
||||
continue;
|
||||
}
|
||||
if (options.scope === 'worker' && disallowWorkerFixtures) {
|
||||
this._addLoadError(`Cannot use({ ${name} }) in a describe group, because it forces a new worker.\nMake it top-level in the test file or put in the configuration file.`, location);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Overriding option with "undefined" value means setting it to the default value
|
||||
// from the config or from the original declaration of the option.
|
||||
if (fn === undefined && options.option && previous) {
|
||||
let original = previous;
|
||||
while (!original.optionOverride && original.super) original = original.super;
|
||||
fn = original.fn;
|
||||
}
|
||||
const deps = fixtureParameterNames(fn, location, e => this._onLoadError(e));
|
||||
const registration = {
|
||||
id: '',
|
||||
name,
|
||||
location,
|
||||
scope: options.scope,
|
||||
fn,
|
||||
auto: options.auto,
|
||||
option: options.option,
|
||||
timeout: options.timeout,
|
||||
customTitle: options.customTitle,
|
||||
box: options.box,
|
||||
deps,
|
||||
super: previous,
|
||||
optionOverride: isOptionsOverride
|
||||
};
|
||||
registrationId(registration);
|
||||
this._registrations.set(name, registration);
|
||||
}
|
||||
}
|
||||
validate() {
|
||||
const markers = new Map();
|
||||
const stack = [];
|
||||
let hasDependencyErrors = false;
|
||||
const addDependencyError = (message, location) => {
|
||||
hasDependencyErrors = true;
|
||||
this._addLoadError(message, location);
|
||||
};
|
||||
const visit = (registration, boxedOnly) => {
|
||||
markers.set(registration, 'visiting');
|
||||
stack.push(registration);
|
||||
for (const name of registration.deps) {
|
||||
const dep = this.resolve(name, registration);
|
||||
if (!dep) {
|
||||
if (name === registration.name) addDependencyError(`Fixture "${registration.name}" references itself, but does not have a base implementation.`, registration.location);else addDependencyError(`Fixture "${registration.name}" has unknown parameter "${name}".`, registration.location);
|
||||
continue;
|
||||
}
|
||||
if (kScopeOrder.indexOf(registration.scope) > kScopeOrder.indexOf(dep.scope)) {
|
||||
addDependencyError(`${registration.scope} fixture "${registration.name}" cannot depend on a ${dep.scope} fixture "${name}" defined in ${formatPotentiallyInternalLocation(dep.location)}.`, registration.location);
|
||||
continue;
|
||||
}
|
||||
if (!markers.has(dep)) {
|
||||
visit(dep, boxedOnly);
|
||||
} else if (markers.get(dep) === 'visiting') {
|
||||
const index = stack.indexOf(dep);
|
||||
const allRegs = stack.slice(index, stack.length);
|
||||
const filteredRegs = allRegs.filter(r => !r.box);
|
||||
const regs = boxedOnly ? filteredRegs : allRegs;
|
||||
const names = regs.map(r => `"${r.name}"`);
|
||||
addDependencyError(`Fixtures ${names.join(' -> ')} -> "${dep.name}" form a dependency cycle: ${regs.map(r => formatPotentiallyInternalLocation(r.location)).join(' -> ')} -> ${formatPotentiallyInternalLocation(dep.location)}`, dep.location);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
markers.set(registration, 'visited');
|
||||
stack.pop();
|
||||
};
|
||||
const names = Array.from(this._registrations.keys()).sort();
|
||||
|
||||
// First iterate over non-boxed fixtures to provide clear error messages.
|
||||
for (const name of names) {
|
||||
const registration = this._registrations.get(name);
|
||||
if (!registration.box) visit(registration, true);
|
||||
}
|
||||
|
||||
// If no errors found, iterate over boxed fixtures
|
||||
if (!hasDependencyErrors) {
|
||||
for (const name of names) {
|
||||
const registration = this._registrations.get(name);
|
||||
if (registration.box) visit(registration, false);
|
||||
}
|
||||
}
|
||||
const hash = crypto.createHash('sha1');
|
||||
for (const name of names) {
|
||||
const registration = this._registrations.get(name);
|
||||
if (registration.scope === 'worker') hash.update(registration.id + ';');
|
||||
}
|
||||
return hash.digest('hex');
|
||||
}
|
||||
validateFunction(fn, prefix, location) {
|
||||
for (const name of fixtureParameterNames(fn, location, e => this._onLoadError(e))) {
|
||||
const registration = this._registrations.get(name);
|
||||
if (!registration) this._addLoadError(`${prefix} has unknown parameter "${name}".`, location);
|
||||
}
|
||||
}
|
||||
resolve(name, forFixture) {
|
||||
if (name === (forFixture === null || forFixture === void 0 ? void 0 : forFixture.name)) return forFixture.super;
|
||||
return this._registrations.get(name);
|
||||
}
|
||||
autoFixtures() {
|
||||
return [...this._registrations.values()].filter(r => r.auto !== false);
|
||||
}
|
||||
_addLoadError(message, location) {
|
||||
this._onLoadError({
|
||||
message,
|
||||
location
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.FixturePool = FixturePool;
|
||||
const signatureSymbol = Symbol('signature');
|
||||
function formatPotentiallyInternalLocation(location) {
|
||||
const isUserFixture = location && (0, _util.filterStackFile)(location.file);
|
||||
return isUserFixture ? (0, _util.formatLocation)(location) : '<builtin>';
|
||||
}
|
||||
function fixtureParameterNames(fn, location, onError) {
|
||||
if (typeof fn !== 'function') return [];
|
||||
if (!fn[signatureSymbol]) fn[signatureSymbol] = innerFixtureParameterNames(fn, location, onError);
|
||||
return fn[signatureSymbol];
|
||||
}
|
||||
function inheritFixtureNames(from, to) {
|
||||
to[signatureSymbol] = from[signatureSymbol];
|
||||
}
|
||||
function innerFixtureParameterNames(fn, location, onError) {
|
||||
const text = filterOutComments(fn.toString());
|
||||
const match = text.match(/(?:async)?(?:\s+function)?[^(]*\(([^)]*)/);
|
||||
if (!match) return [];
|
||||
const trimmedParams = match[1].trim();
|
||||
if (!trimmedParams) return [];
|
||||
const [firstParam] = splitByComma(trimmedParams);
|
||||
if (firstParam[0] !== '{' || firstParam[firstParam.length - 1] !== '}') {
|
||||
onError({
|
||||
message: 'First argument must use the object destructuring pattern: ' + firstParam,
|
||||
location
|
||||
});
|
||||
return [];
|
||||
}
|
||||
const props = splitByComma(firstParam.substring(1, firstParam.length - 1)).map(prop => {
|
||||
const colon = prop.indexOf(':');
|
||||
return colon === -1 ? prop.trim() : prop.substring(0, colon).trim();
|
||||
});
|
||||
const restProperty = props.find(prop => prop.startsWith('...'));
|
||||
if (restProperty) {
|
||||
onError({
|
||||
message: `Rest property "${restProperty}" is not supported. List all used fixtures explicitly, separated by comma.`,
|
||||
location
|
||||
});
|
||||
return [];
|
||||
}
|
||||
return props;
|
||||
}
|
||||
function filterOutComments(s) {
|
||||
const result = [];
|
||||
let commentState = 'none';
|
||||
for (let i = 0; i < s.length; ++i) {
|
||||
if (commentState === 'singleline') {
|
||||
if (s[i] === '\n') commentState = 'none';
|
||||
} else if (commentState === 'multiline') {
|
||||
if (s[i - 1] === '*' && s[i] === '/') commentState = 'none';
|
||||
} else if (commentState === 'none') {
|
||||
if (s[i] === '/' && s[i + 1] === '/') {
|
||||
commentState = 'singleline';
|
||||
} else if (s[i] === '/' && s[i + 1] === '*') {
|
||||
commentState = 'multiline';
|
||||
i += 2;
|
||||
} else {
|
||||
result.push(s[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.join('');
|
||||
}
|
||||
function splitByComma(s) {
|
||||
const result = [];
|
||||
const stack = [];
|
||||
let start = 0;
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
if (s[i] === '{' || s[i] === '[') {
|
||||
stack.push(s[i] === '{' ? '}' : ']');
|
||||
} else if (s[i] === stack[stack.length - 1]) {
|
||||
stack.pop();
|
||||
} else if (!stack.length && s[i] === ',') {
|
||||
const token = s.substring(start, i).trim();
|
||||
if (token) result.push(token);
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
const lastToken = s.substring(start).trim();
|
||||
if (lastToken) result.push(lastToken);
|
||||
return result;
|
||||
}
|
||||
|
||||
// name + superId, fn -> id
|
||||
const registrationIdMap = new Map();
|
||||
let lastId = 0;
|
||||
function registrationId(registration) {
|
||||
if (registration.id) return registration.id;
|
||||
const key = registration.name + '@@@' + (registration.super ? registrationId(registration.super) : '');
|
||||
let map = registrationIdMap.get(key);
|
||||
if (!map) {
|
||||
map = new Map();
|
||||
registrationIdMap.set(key, map);
|
||||
}
|
||||
if (!map.has(registration.fn)) map.set(registration.fn, String(lastId++));
|
||||
registration.id = map.get(registration.fn);
|
||||
return registration.id;
|
||||
}
|
||||
48
node_modules/playwright/lib/common/globals.js
generated
vendored
Normal file
48
node_modules/playwright/lib/common/globals.js
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.currentTestInfo = currentTestInfo;
|
||||
exports.currentlyLoadingFileSuite = currentlyLoadingFileSuite;
|
||||
exports.isWorkerProcess = isWorkerProcess;
|
||||
exports.setCurrentTestInfo = setCurrentTestInfo;
|
||||
exports.setCurrentlyLoadingFileSuite = setCurrentlyLoadingFileSuite;
|
||||
exports.setIsWorkerProcess = setIsWorkerProcess;
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
let currentTestInfoValue = null;
|
||||
function setCurrentTestInfo(testInfo) {
|
||||
currentTestInfoValue = testInfo;
|
||||
}
|
||||
function currentTestInfo() {
|
||||
return currentTestInfoValue;
|
||||
}
|
||||
let currentFileSuite;
|
||||
function setCurrentlyLoadingFileSuite(suite) {
|
||||
currentFileSuite = suite;
|
||||
}
|
||||
function currentlyLoadingFileSuite() {
|
||||
return currentFileSuite;
|
||||
}
|
||||
let _isWorkerProcess = false;
|
||||
function setIsWorkerProcess() {
|
||||
_isWorkerProcess = true;
|
||||
}
|
||||
function isWorkerProcess() {
|
||||
return _isWorkerProcess;
|
||||
}
|
||||
48
node_modules/playwright/lib/common/ipc.js
generated
vendored
Normal file
48
node_modules/playwright/lib/common/ipc.js
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.serializeConfig = serializeConfig;
|
||||
exports.stdioChunkToParams = stdioChunkToParams;
|
||||
var _util = _interopRequireDefault(require("util"));
|
||||
var _compilationCache = require("../transform/compilationCache");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function serializeConfig(config, passCompilationCache) {
|
||||
const result = {
|
||||
location: {
|
||||
configDir: config.configDir,
|
||||
resolvedConfigFile: config.config.configFile
|
||||
},
|
||||
configCLIOverrides: config.configCLIOverrides,
|
||||
compilationCache: passCompilationCache ? (0, _compilationCache.serializeCompilationCache)() : undefined
|
||||
};
|
||||
return result;
|
||||
}
|
||||
function stdioChunkToParams(chunk) {
|
||||
if (chunk instanceof Uint8Array) return {
|
||||
buffer: Buffer.from(chunk).toString('base64')
|
||||
};
|
||||
if (typeof chunk !== 'string') return {
|
||||
text: _util.default.inspect(chunk)
|
||||
};
|
||||
return {
|
||||
text: chunk
|
||||
};
|
||||
}
|
||||
79
node_modules/playwright/lib/common/poolBuilder.js
generated
vendored
Normal file
79
node_modules/playwright/lib/common/poolBuilder.js
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.PoolBuilder = void 0;
|
||||
var _fixtures = require("./fixtures");
|
||||
var _util = require("../util");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class PoolBuilder {
|
||||
static createForLoader() {
|
||||
return new PoolBuilder('loader');
|
||||
}
|
||||
static createForWorker(project) {
|
||||
return new PoolBuilder('worker', project);
|
||||
}
|
||||
constructor(type, project) {
|
||||
this._project = void 0;
|
||||
this._testTypePools = new Map();
|
||||
this._type = void 0;
|
||||
this._type = type;
|
||||
this._project = project;
|
||||
}
|
||||
buildPools(suite, testErrors) {
|
||||
suite.forEachTest(test => {
|
||||
const pool = this._buildPoolForTest(test, testErrors);
|
||||
if (this._type === 'loader') test._poolDigest = pool.digest;
|
||||
if (this._type === 'worker') test._pool = pool;
|
||||
});
|
||||
}
|
||||
_buildPoolForTest(test, testErrors) {
|
||||
let pool = this._buildTestTypePool(test._testType, testErrors);
|
||||
const parents = [];
|
||||
for (let parent = test.parent; parent; parent = parent.parent) parents.push(parent);
|
||||
parents.reverse();
|
||||
for (const parent of parents) {
|
||||
if (parent._use.length) pool = new _fixtures.FixturePool(parent._use, e => this._handleLoadError(e, testErrors), pool, parent._type === 'describe');
|
||||
for (const hook of parent._hooks) pool.validateFunction(hook.fn, hook.type + ' hook', hook.location);
|
||||
for (const modifier of parent._modifiers) pool.validateFunction(modifier.fn, modifier.type + ' modifier', modifier.location);
|
||||
}
|
||||
pool.validateFunction(test.fn, 'Test', test.location);
|
||||
return pool;
|
||||
}
|
||||
_buildTestTypePool(testType, testErrors) {
|
||||
if (!this._testTypePools.has(testType)) {
|
||||
var _this$_project$projec, _this$_project, _this$_project2;
|
||||
const optionOverrides = {
|
||||
overrides: (_this$_project$projec = (_this$_project = this._project) === null || _this$_project === void 0 || (_this$_project = _this$_project.project) === null || _this$_project === void 0 ? void 0 : _this$_project.use) !== null && _this$_project$projec !== void 0 ? _this$_project$projec : {},
|
||||
location: {
|
||||
file: `project#${(_this$_project2 = this._project) === null || _this$_project2 === void 0 ? void 0 : _this$_project2.id}`,
|
||||
line: 1,
|
||||
column: 1
|
||||
}
|
||||
};
|
||||
const pool = new _fixtures.FixturePool(testType.fixtures, e => this._handleLoadError(e, testErrors), undefined, undefined, optionOverrides);
|
||||
this._testTypePools.set(testType, pool);
|
||||
}
|
||||
return this._testTypePools.get(testType);
|
||||
}
|
||||
_handleLoadError(e, testErrors) {
|
||||
if (testErrors) testErrors.push(e);else throw new Error(`${(0, _util.formatLocation)(e.location)}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
exports.PoolBuilder = PoolBuilder;
|
||||
140
node_modules/playwright/lib/common/process.js
generated
vendored
Normal file
140
node_modules/playwright/lib/common/process.js
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ProcessRunner = void 0;
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _util = require("../util");
|
||||
var _esmLoaderHost = require("./esmLoaderHost");
|
||||
var _esmUtils = require("../transform/esmUtils");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class ProcessRunner {
|
||||
async gracefullyClose() {}
|
||||
dispatchEvent(method, params) {
|
||||
const response = {
|
||||
method,
|
||||
params
|
||||
};
|
||||
sendMessageToParent({
|
||||
method: '__dispatch__',
|
||||
params: response
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.ProcessRunner = ProcessRunner;
|
||||
let gracefullyCloseCalled = false;
|
||||
let forceExitInitiated = false;
|
||||
sendMessageToParent({
|
||||
method: 'ready'
|
||||
});
|
||||
process.on('disconnect', () => gracefullyCloseAndExit(true));
|
||||
process.on('SIGINT', () => {});
|
||||
process.on('SIGTERM', () => {});
|
||||
|
||||
// Clear execArgv immediately, so that the user-code does not inherit our loader.
|
||||
process.execArgv = (0, _esmUtils.execArgvWithoutExperimentalLoaderOptions)();
|
||||
|
||||
// Node.js >= 20
|
||||
if (process.env.PW_TS_ESM_LOADER_ON) (0, _esmLoaderHost.registerESMLoader)();
|
||||
let processRunner;
|
||||
let processName;
|
||||
const startingEnv = {
|
||||
...process.env
|
||||
};
|
||||
process.on('message', async message => {
|
||||
if (message.method === '__init__') {
|
||||
const {
|
||||
processParams,
|
||||
runnerParams,
|
||||
runnerScript
|
||||
} = message.params;
|
||||
void (0, _utils.startProfiling)();
|
||||
const {
|
||||
create
|
||||
} = require(runnerScript);
|
||||
processRunner = create(runnerParams);
|
||||
processName = processParams.processName;
|
||||
return;
|
||||
}
|
||||
if (message.method === '__stop__') {
|
||||
const keys = new Set([...Object.keys(process.env), ...Object.keys(startingEnv)]);
|
||||
const producedEnv = [...keys].filter(key => startingEnv[key] !== process.env[key]).map(key => {
|
||||
var _process$env$key;
|
||||
return [key, (_process$env$key = process.env[key]) !== null && _process$env$key !== void 0 ? _process$env$key : null];
|
||||
});
|
||||
sendMessageToParent({
|
||||
method: '__env_produced__',
|
||||
params: producedEnv
|
||||
});
|
||||
await gracefullyCloseAndExit(false);
|
||||
return;
|
||||
}
|
||||
if (message.method === '__dispatch__') {
|
||||
const {
|
||||
id,
|
||||
method,
|
||||
params
|
||||
} = message.params;
|
||||
try {
|
||||
const result = await processRunner[method](params);
|
||||
const response = {
|
||||
id,
|
||||
result
|
||||
};
|
||||
sendMessageToParent({
|
||||
method: '__dispatch__',
|
||||
params: response
|
||||
});
|
||||
} catch (e) {
|
||||
const response = {
|
||||
id,
|
||||
error: (0, _util.serializeError)(e)
|
||||
};
|
||||
sendMessageToParent({
|
||||
method: '__dispatch__',
|
||||
params: response
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
const kForceExitTimeout = +(process.env.PWTEST_FORCE_EXIT_TIMEOUT || 30000);
|
||||
async function gracefullyCloseAndExit(forceExit) {
|
||||
if (forceExit && !forceExitInitiated) {
|
||||
forceExitInitiated = true;
|
||||
// Force exit after 30 seconds.
|
||||
// eslint-disable-next-line no-restricted-properties
|
||||
setTimeout(() => process.exit(0), kForceExitTimeout);
|
||||
}
|
||||
if (!gracefullyCloseCalled) {
|
||||
var _processRunner;
|
||||
gracefullyCloseCalled = true;
|
||||
// Meanwhile, try to gracefully shutdown.
|
||||
await ((_processRunner = processRunner) === null || _processRunner === void 0 ? void 0 : _processRunner.gracefullyClose().catch(() => {}));
|
||||
if (processName) await (0, _utils.stopProfiling)(processName).catch(() => {});
|
||||
// eslint-disable-next-line no-restricted-properties
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
function sendMessageToParent(message) {
|
||||
try {
|
||||
process.send(message);
|
||||
} catch (e) {
|
||||
// Can throw when closing.
|
||||
}
|
||||
}
|
||||
133
node_modules/playwright/lib/common/suiteUtils.js
generated
vendored
Normal file
133
node_modules/playwright/lib/common/suiteUtils.js
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.applyRepeatEachIndex = applyRepeatEachIndex;
|
||||
exports.bindFileSuiteToProject = bindFileSuiteToProject;
|
||||
exports.filterByFocusedLine = filterByFocusedLine;
|
||||
exports.filterByTestIds = filterByTestIds;
|
||||
exports.filterOnly = filterOnly;
|
||||
exports.filterSuite = filterSuite;
|
||||
exports.filterSuiteWithOnlySemantics = filterSuiteWithOnlySemantics;
|
||||
exports.filterTestsRemoveEmptySuites = filterTestsRemoveEmptySuites;
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _util = require("../util");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function filterSuite(suite, suiteFilter, testFilter) {
|
||||
for (const child of suite.suites) {
|
||||
if (!suiteFilter(child)) filterSuite(child, suiteFilter, testFilter);
|
||||
}
|
||||
const filteredTests = suite.tests.filter(testFilter);
|
||||
const entries = new Set([...suite.suites, ...filteredTests]);
|
||||
suite._entries = suite._entries.filter(e => entries.has(e)); // Preserve the order.
|
||||
}
|
||||
function filterTestsRemoveEmptySuites(suite, filter) {
|
||||
const filteredSuites = suite.suites.filter(child => filterTestsRemoveEmptySuites(child, filter));
|
||||
const filteredTests = suite.tests.filter(filter);
|
||||
const entries = new Set([...filteredSuites, ...filteredTests]);
|
||||
suite._entries = suite._entries.filter(e => entries.has(e)); // Preserve the order.
|
||||
return !!suite._entries.length;
|
||||
}
|
||||
function bindFileSuiteToProject(project, suite) {
|
||||
const relativeFile = _path.default.relative(project.project.testDir, suite.location.file);
|
||||
const fileId = (0, _utils.calculateSha1)((0, _utils.toPosixPath)(relativeFile)).slice(0, 20);
|
||||
|
||||
// Clone suite.
|
||||
const result = suite._deepClone();
|
||||
result._fileId = fileId;
|
||||
|
||||
// Assign test properties with project-specific values.
|
||||
result.forEachTest((test, suite) => {
|
||||
var _inheritedRetries, _inheritedTimeout;
|
||||
suite._fileId = fileId;
|
||||
// At the point of the query, suite is not yet attached to the project, so we only get file, describe and test titles.
|
||||
const [file, ...titles] = test.titlePath();
|
||||
const testIdExpression = `[project=${project.id}]${(0, _utils.toPosixPath)(file)}\x1e${titles.join('\x1e')}`;
|
||||
const testId = fileId + '-' + (0, _utils.calculateSha1)(testIdExpression).slice(0, 20);
|
||||
test.id = testId;
|
||||
test._projectId = project.id;
|
||||
|
||||
// Inherit properties from parent suites.
|
||||
let inheritedRetries;
|
||||
let inheritedTimeout;
|
||||
test.annotations = [];
|
||||
for (let parentSuite = suite; parentSuite; parentSuite = parentSuite.parent) {
|
||||
if (parentSuite._staticAnnotations.length) test.annotations = [...parentSuite._staticAnnotations, ...test.annotations];
|
||||
if (inheritedRetries === undefined && parentSuite._retries !== undefined) inheritedRetries = parentSuite._retries;
|
||||
if (inheritedTimeout === undefined && parentSuite._timeout !== undefined) inheritedTimeout = parentSuite._timeout;
|
||||
}
|
||||
test.retries = (_inheritedRetries = inheritedRetries) !== null && _inheritedRetries !== void 0 ? _inheritedRetries : project.project.retries;
|
||||
test.timeout = (_inheritedTimeout = inheritedTimeout) !== null && _inheritedTimeout !== void 0 ? _inheritedTimeout : project.project.timeout;
|
||||
test.annotations.push(...test._staticAnnotations);
|
||||
|
||||
// Skip annotations imply skipped expectedStatus.
|
||||
if (test.annotations.some(a => a.type === 'skip' || a.type === 'fixme')) test.expectedStatus = 'skipped';
|
||||
|
||||
// We only compute / set digest in the runner.
|
||||
if (test._poolDigest) test._workerHash = `${project.id}-${test._poolDigest}-0`;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
function applyRepeatEachIndex(project, fileSuite, repeatEachIndex) {
|
||||
// Assign test properties with project-specific values.
|
||||
fileSuite.forEachTest((test, suite) => {
|
||||
if (repeatEachIndex) {
|
||||
const [file, ...titles] = test.titlePath();
|
||||
const testIdExpression = `[project=${project.id}]${(0, _utils.toPosixPath)(file)}\x1e${titles.join('\x1e')} (repeat:${repeatEachIndex})`;
|
||||
const testId = suite._fileId + '-' + (0, _utils.calculateSha1)(testIdExpression).slice(0, 20);
|
||||
test.id = testId;
|
||||
test.repeatEachIndex = repeatEachIndex;
|
||||
if (test._poolDigest) test._workerHash = `${project.id}-${test._poolDigest}-${repeatEachIndex}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
function filterOnly(suite) {
|
||||
if (!suite._getOnlyItems().length) return;
|
||||
const suiteFilter = suite => suite._only;
|
||||
const testFilter = test => test._only;
|
||||
return filterSuiteWithOnlySemantics(suite, suiteFilter, testFilter);
|
||||
}
|
||||
function filterSuiteWithOnlySemantics(suite, suiteFilter, testFilter) {
|
||||
const onlySuites = suite.suites.filter(child => filterSuiteWithOnlySemantics(child, suiteFilter, testFilter) || suiteFilter(child));
|
||||
const onlyTests = suite.tests.filter(testFilter);
|
||||
const onlyEntries = new Set([...onlySuites, ...onlyTests]);
|
||||
if (onlyEntries.size) {
|
||||
suite._entries = suite._entries.filter(e => onlyEntries.has(e)); // Preserve the order.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function filterByFocusedLine(suite, focusedTestFileLines) {
|
||||
if (!focusedTestFileLines.length) return;
|
||||
const matchers = focusedTestFileLines.map(createFileMatcherFromFilter);
|
||||
const testFileLineMatches = (testFileName, testLine, testColumn) => matchers.some(m => m(testFileName, testLine, testColumn));
|
||||
const suiteFilter = suite => !!suite.location && testFileLineMatches(suite.location.file, suite.location.line, suite.location.column);
|
||||
const testFilter = test => testFileLineMatches(test.location.file, test.location.line, test.location.column);
|
||||
return filterSuite(suite, suiteFilter, testFilter);
|
||||
}
|
||||
function filterByTestIds(suite, testIdMatcher) {
|
||||
if (!testIdMatcher) return;
|
||||
filterTestsRemoveEmptySuites(suite, test => testIdMatcher(test.id));
|
||||
}
|
||||
function createFileMatcherFromFilter(filter) {
|
||||
const fileMatcher = (0, _util.createFileMatcher)(filter.re || filter.exact || '');
|
||||
return (testFileName, testLine, testColumn) => fileMatcher(testFileName) && (filter.line === testLine || filter.line === null) && (filter.column === testColumn || filter.column === null);
|
||||
}
|
||||
309
node_modules/playwright/lib/common/test.js
generated
vendored
Normal file
309
node_modules/playwright/lib/common/test.js
generated
vendored
Normal file
@@ -0,0 +1,309 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.TestCase = exports.Suite = void 0;
|
||||
var _testType = require("./testType");
|
||||
var _teleReceiver = require("../isomorphic/teleReceiver");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Base {
|
||||
constructor(title) {
|
||||
this.title = void 0;
|
||||
this._only = false;
|
||||
this._requireFile = '';
|
||||
this.title = title;
|
||||
}
|
||||
}
|
||||
class Suite extends Base {
|
||||
constructor(title, type) {
|
||||
super(title);
|
||||
this.location = void 0;
|
||||
this.parent = void 0;
|
||||
this._use = [];
|
||||
this._entries = [];
|
||||
this._hooks = [];
|
||||
this._timeout = void 0;
|
||||
this._retries = void 0;
|
||||
// Annotations known statically before running the test, e.g. `test.describe.skip()` or `test.describe({ annotation }, body)`.
|
||||
this._staticAnnotations = [];
|
||||
// Explicitly declared tags that are not a part of the title.
|
||||
this._tags = [];
|
||||
this._modifiers = [];
|
||||
this._parallelMode = 'none';
|
||||
this._fullProject = void 0;
|
||||
this._fileId = void 0;
|
||||
this._type = void 0;
|
||||
this._type = type;
|
||||
}
|
||||
get type() {
|
||||
return this._type;
|
||||
}
|
||||
entries() {
|
||||
return this._entries;
|
||||
}
|
||||
get suites() {
|
||||
return this._entries.filter(entry => entry instanceof Suite);
|
||||
}
|
||||
get tests() {
|
||||
return this._entries.filter(entry => entry instanceof TestCase);
|
||||
}
|
||||
_addTest(test) {
|
||||
test.parent = this;
|
||||
this._entries.push(test);
|
||||
}
|
||||
_addSuite(suite) {
|
||||
suite.parent = this;
|
||||
this._entries.push(suite);
|
||||
}
|
||||
_prependSuite(suite) {
|
||||
suite.parent = this;
|
||||
this._entries.unshift(suite);
|
||||
}
|
||||
allTests() {
|
||||
const result = [];
|
||||
const visit = suite => {
|
||||
for (const entry of suite._entries) {
|
||||
if (entry instanceof Suite) visit(entry);else result.push(entry);
|
||||
}
|
||||
};
|
||||
visit(this);
|
||||
return result;
|
||||
}
|
||||
_hasTests() {
|
||||
let result = false;
|
||||
const visit = suite => {
|
||||
for (const entry of suite._entries) {
|
||||
if (result) return;
|
||||
if (entry instanceof Suite) visit(entry);else result = true;
|
||||
}
|
||||
};
|
||||
visit(this);
|
||||
return result;
|
||||
}
|
||||
titlePath() {
|
||||
const titlePath = this.parent ? this.parent.titlePath() : [];
|
||||
// Ignore anonymous describe blocks.
|
||||
if (this.title || this._type !== 'describe') titlePath.push(this.title);
|
||||
return titlePath;
|
||||
}
|
||||
_collectGrepTitlePath(path) {
|
||||
if (this.parent) this.parent._collectGrepTitlePath(path);
|
||||
if (this.title || this._type !== 'describe') path.push(this.title);
|
||||
path.push(...this._tags);
|
||||
}
|
||||
_getOnlyItems() {
|
||||
const items = [];
|
||||
if (this._only) items.push(this);
|
||||
for (const suite of this.suites) items.push(...suite._getOnlyItems());
|
||||
items.push(...this.tests.filter(test => test._only));
|
||||
return items;
|
||||
}
|
||||
_deepClone() {
|
||||
const suite = this._clone();
|
||||
for (const entry of this._entries) {
|
||||
if (entry instanceof Suite) suite._addSuite(entry._deepClone());else suite._addTest(entry._clone());
|
||||
}
|
||||
return suite;
|
||||
}
|
||||
_deepSerialize() {
|
||||
const suite = this._serialize();
|
||||
suite.entries = [];
|
||||
for (const entry of this._entries) {
|
||||
if (entry instanceof Suite) suite.entries.push(entry._deepSerialize());else suite.entries.push(entry._serialize());
|
||||
}
|
||||
return suite;
|
||||
}
|
||||
static _deepParse(data) {
|
||||
const suite = Suite._parse(data);
|
||||
for (const entry of data.entries) {
|
||||
if (entry.kind === 'suite') suite._addSuite(Suite._deepParse(entry));else suite._addTest(TestCase._parse(entry));
|
||||
}
|
||||
return suite;
|
||||
}
|
||||
forEachTest(visitor) {
|
||||
for (const entry of this._entries) {
|
||||
if (entry instanceof Suite) entry.forEachTest(visitor);else visitor(entry, this);
|
||||
}
|
||||
}
|
||||
_serialize() {
|
||||
return {
|
||||
kind: 'suite',
|
||||
title: this.title,
|
||||
type: this._type,
|
||||
location: this.location,
|
||||
only: this._only,
|
||||
requireFile: this._requireFile,
|
||||
timeout: this._timeout,
|
||||
retries: this._retries,
|
||||
staticAnnotations: this._staticAnnotations.slice(),
|
||||
tags: this._tags.slice(),
|
||||
modifiers: this._modifiers.slice(),
|
||||
parallelMode: this._parallelMode,
|
||||
hooks: this._hooks.map(h => ({
|
||||
type: h.type,
|
||||
location: h.location,
|
||||
title: h.title
|
||||
})),
|
||||
fileId: this._fileId
|
||||
};
|
||||
}
|
||||
static _parse(data) {
|
||||
const suite = new Suite(data.title, data.type);
|
||||
suite.location = data.location;
|
||||
suite._only = data.only;
|
||||
suite._requireFile = data.requireFile;
|
||||
suite._timeout = data.timeout;
|
||||
suite._retries = data.retries;
|
||||
suite._staticAnnotations = data.staticAnnotations;
|
||||
suite._tags = data.tags;
|
||||
suite._modifiers = data.modifiers;
|
||||
suite._parallelMode = data.parallelMode;
|
||||
suite._hooks = data.hooks.map(h => ({
|
||||
type: h.type,
|
||||
location: h.location,
|
||||
title: h.title,
|
||||
fn: () => {}
|
||||
}));
|
||||
suite._fileId = data.fileId;
|
||||
return suite;
|
||||
}
|
||||
_clone() {
|
||||
const data = this._serialize();
|
||||
const suite = Suite._parse(data);
|
||||
suite._use = this._use.slice();
|
||||
suite._hooks = this._hooks.slice();
|
||||
suite._fullProject = this._fullProject;
|
||||
return suite;
|
||||
}
|
||||
project() {
|
||||
var _this$_fullProject, _this$parent;
|
||||
return ((_this$_fullProject = this._fullProject) === null || _this$_fullProject === void 0 ? void 0 : _this$_fullProject.project) || ((_this$parent = this.parent) === null || _this$parent === void 0 ? void 0 : _this$parent.project());
|
||||
}
|
||||
}
|
||||
exports.Suite = Suite;
|
||||
class TestCase extends Base {
|
||||
constructor(title, fn, testType, location) {
|
||||
super(title);
|
||||
this.fn = void 0;
|
||||
this.results = [];
|
||||
this.location = void 0;
|
||||
this.parent = void 0;
|
||||
this.type = 'test';
|
||||
this.expectedStatus = 'passed';
|
||||
this.timeout = 0;
|
||||
this.annotations = [];
|
||||
this.retries = 0;
|
||||
this.repeatEachIndex = 0;
|
||||
this._testType = void 0;
|
||||
this.id = '';
|
||||
this._pool = void 0;
|
||||
this._poolDigest = '';
|
||||
this._workerHash = '';
|
||||
this._projectId = '';
|
||||
// Annotations known statically before running the test, e.g. `test.skip()` or `test(title, { annotation }, body)`.
|
||||
this._staticAnnotations = [];
|
||||
// Explicitly declared tags that are not a part of the title.
|
||||
this._tags = [];
|
||||
this.fn = fn;
|
||||
this._testType = testType;
|
||||
this.location = location;
|
||||
}
|
||||
titlePath() {
|
||||
const titlePath = this.parent ? this.parent.titlePath() : [];
|
||||
titlePath.push(this.title);
|
||||
return titlePath;
|
||||
}
|
||||
outcome() {
|
||||
return (0, _teleReceiver.computeTestCaseOutcome)(this);
|
||||
}
|
||||
ok() {
|
||||
const status = this.outcome();
|
||||
return status === 'expected' || status === 'flaky' || status === 'skipped';
|
||||
}
|
||||
get tags() {
|
||||
return this._grepTitle().match(/@[\S]+/g) || [];
|
||||
}
|
||||
_serialize() {
|
||||
return {
|
||||
kind: 'test',
|
||||
id: this.id,
|
||||
title: this.title,
|
||||
retries: this.retries,
|
||||
timeout: this.timeout,
|
||||
expectedStatus: this.expectedStatus,
|
||||
location: this.location,
|
||||
only: this._only,
|
||||
requireFile: this._requireFile,
|
||||
poolDigest: this._poolDigest,
|
||||
workerHash: this._workerHash,
|
||||
staticAnnotations: this._staticAnnotations.slice(),
|
||||
annotations: this.annotations.slice(),
|
||||
tags: this._tags.slice(),
|
||||
projectId: this._projectId
|
||||
};
|
||||
}
|
||||
static _parse(data) {
|
||||
const test = new TestCase(data.title, () => {}, _testType.rootTestType, data.location);
|
||||
test.id = data.id;
|
||||
test.retries = data.retries;
|
||||
test.timeout = data.timeout;
|
||||
test.expectedStatus = data.expectedStatus;
|
||||
test._only = data.only;
|
||||
test._requireFile = data.requireFile;
|
||||
test._poolDigest = data.poolDigest;
|
||||
test._workerHash = data.workerHash;
|
||||
test._staticAnnotations = data.staticAnnotations;
|
||||
test.annotations = data.annotations;
|
||||
test._tags = data.tags;
|
||||
test._projectId = data.projectId;
|
||||
return test;
|
||||
}
|
||||
_clone() {
|
||||
const data = this._serialize();
|
||||
const test = TestCase._parse(data);
|
||||
test._testType = this._testType;
|
||||
test.fn = this.fn;
|
||||
return test;
|
||||
}
|
||||
_appendTestResult() {
|
||||
const result = {
|
||||
retry: this.results.length,
|
||||
parallelIndex: -1,
|
||||
workerIndex: -1,
|
||||
duration: 0,
|
||||
startTime: new Date(),
|
||||
stdout: [],
|
||||
stderr: [],
|
||||
attachments: [],
|
||||
status: 'skipped',
|
||||
steps: [],
|
||||
errors: []
|
||||
};
|
||||
this.results.push(result);
|
||||
return result;
|
||||
}
|
||||
_grepTitle() {
|
||||
const path = [];
|
||||
this.parent._collectGrepTitlePath(path);
|
||||
path.push(this.title);
|
||||
path.push(...this._tags);
|
||||
return path.join(' ');
|
||||
}
|
||||
}
|
||||
exports.TestCase = TestCase;
|
||||
102
node_modules/playwright/lib/common/testLoader.js
generated
vendored
Normal file
102
node_modules/playwright/lib/common/testLoader.js
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.defaultTimeout = void 0;
|
||||
exports.loadTestFile = loadTestFile;
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _util = _interopRequireDefault(require("util"));
|
||||
var _globals = require("./globals");
|
||||
var _test = require("./test");
|
||||
var _transform = require("../transform/transform");
|
||||
var _util2 = require("../util");
|
||||
var _compilationCache = require("../transform/compilationCache");
|
||||
var esmLoaderHost = _interopRequireWildcard(require("./esmLoaderHost"));
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const defaultTimeout = exports.defaultTimeout = 30000;
|
||||
|
||||
// To allow multiple loaders in the same process without clearing require cache,
|
||||
// we make these maps global.
|
||||
const cachedFileSuites = new Map();
|
||||
async function loadTestFile(file, rootDir, testErrors) {
|
||||
if (cachedFileSuites.has(file)) return cachedFileSuites.get(file);
|
||||
const suite = new _test.Suite(_path.default.relative(rootDir, file) || _path.default.basename(file), 'file');
|
||||
suite._requireFile = file;
|
||||
suite.location = {
|
||||
file,
|
||||
line: 0,
|
||||
column: 0
|
||||
};
|
||||
(0, _globals.setCurrentlyLoadingFileSuite)(suite);
|
||||
if (!(0, _globals.isWorkerProcess)()) {
|
||||
(0, _compilationCache.startCollectingFileDeps)();
|
||||
await esmLoaderHost.startCollectingFileDeps();
|
||||
}
|
||||
try {
|
||||
await (0, _transform.requireOrImport)(file);
|
||||
cachedFileSuites.set(file, suite);
|
||||
} catch (e) {
|
||||
if (!testErrors) throw e;
|
||||
testErrors.push(serializeLoadError(file, e));
|
||||
} finally {
|
||||
(0, _globals.setCurrentlyLoadingFileSuite)(undefined);
|
||||
if (!(0, _globals.isWorkerProcess)()) {
|
||||
(0, _compilationCache.stopCollectingFileDeps)(file);
|
||||
await esmLoaderHost.stopCollectingFileDeps(file);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Test locations that we discover potentially have different file name.
|
||||
// This could be due to either
|
||||
// a) use of source maps or due to
|
||||
// b) require of one file from another.
|
||||
// Try fixing (a) w/o regressing (b).
|
||||
|
||||
const files = new Set();
|
||||
suite.allTests().map(t => files.add(t.location.file));
|
||||
if (files.size === 1) {
|
||||
// All tests point to one file.
|
||||
const mappedFile = files.values().next().value;
|
||||
if (suite.location.file !== mappedFile) {
|
||||
// The file is different, check for a likely source map case.
|
||||
if (_path.default.extname(mappedFile) !== _path.default.extname(suite.location.file)) suite.location.file = mappedFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
return suite;
|
||||
}
|
||||
function serializeLoadError(file, error) {
|
||||
if (error instanceof Error) {
|
||||
const result = (0, _util2.filterStackTrace)(error);
|
||||
// Babel parse errors have location.
|
||||
const loc = error.loc;
|
||||
result.location = loc ? {
|
||||
file,
|
||||
line: loc.line || 0,
|
||||
column: loc.column || 0
|
||||
} : undefined;
|
||||
return result;
|
||||
}
|
||||
return {
|
||||
value: _util.default.inspect(error)
|
||||
};
|
||||
}
|
||||
285
node_modules/playwright/lib/common/testType.js
generated
vendored
Normal file
285
node_modules/playwright/lib/common/testType.js
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.TestTypeImpl = void 0;
|
||||
exports.mergeTests = mergeTests;
|
||||
exports.rootTestType = void 0;
|
||||
var _expect = require("../matchers/expect");
|
||||
var _globals = require("./globals");
|
||||
var _test = require("./test");
|
||||
var _transform = require("../transform/transform");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const testTypeSymbol = Symbol('testType');
|
||||
class TestTypeImpl {
|
||||
constructor(fixtures) {
|
||||
this.fixtures = void 0;
|
||||
this.test = void 0;
|
||||
this.fixtures = fixtures;
|
||||
const test = (0, _transform.wrapFunctionWithLocation)(this._createTest.bind(this, 'default'));
|
||||
test[testTypeSymbol] = this;
|
||||
test.expect = _expect.expect;
|
||||
test.only = (0, _transform.wrapFunctionWithLocation)(this._createTest.bind(this, 'only'));
|
||||
test.describe = (0, _transform.wrapFunctionWithLocation)(this._describe.bind(this, 'default'));
|
||||
test.describe.only = (0, _transform.wrapFunctionWithLocation)(this._describe.bind(this, 'only'));
|
||||
test.describe.configure = (0, _transform.wrapFunctionWithLocation)(this._configure.bind(this));
|
||||
test.describe.fixme = (0, _transform.wrapFunctionWithLocation)(this._describe.bind(this, 'fixme'));
|
||||
test.describe.parallel = (0, _transform.wrapFunctionWithLocation)(this._describe.bind(this, 'parallel'));
|
||||
test.describe.parallel.only = (0, _transform.wrapFunctionWithLocation)(this._describe.bind(this, 'parallel.only'));
|
||||
test.describe.serial = (0, _transform.wrapFunctionWithLocation)(this._describe.bind(this, 'serial'));
|
||||
test.describe.serial.only = (0, _transform.wrapFunctionWithLocation)(this._describe.bind(this, 'serial.only'));
|
||||
test.describe.skip = (0, _transform.wrapFunctionWithLocation)(this._describe.bind(this, 'skip'));
|
||||
test.beforeEach = (0, _transform.wrapFunctionWithLocation)(this._hook.bind(this, 'beforeEach'));
|
||||
test.afterEach = (0, _transform.wrapFunctionWithLocation)(this._hook.bind(this, 'afterEach'));
|
||||
test.beforeAll = (0, _transform.wrapFunctionWithLocation)(this._hook.bind(this, 'beforeAll'));
|
||||
test.afterAll = (0, _transform.wrapFunctionWithLocation)(this._hook.bind(this, 'afterAll'));
|
||||
test.skip = (0, _transform.wrapFunctionWithLocation)(this._modifier.bind(this, 'skip'));
|
||||
test.fixme = (0, _transform.wrapFunctionWithLocation)(this._modifier.bind(this, 'fixme'));
|
||||
test.fail = (0, _transform.wrapFunctionWithLocation)(this._modifier.bind(this, 'fail'));
|
||||
test.fail.only = (0, _transform.wrapFunctionWithLocation)(this._createTest.bind(this, 'fail.only'));
|
||||
test.slow = (0, _transform.wrapFunctionWithLocation)(this._modifier.bind(this, 'slow'));
|
||||
test.setTimeout = (0, _transform.wrapFunctionWithLocation)(this._setTimeout.bind(this));
|
||||
test.step = this._step.bind(this);
|
||||
test.use = (0, _transform.wrapFunctionWithLocation)(this._use.bind(this));
|
||||
test.extend = (0, _transform.wrapFunctionWithLocation)(this._extend.bind(this));
|
||||
test.info = () => {
|
||||
const result = (0, _globals.currentTestInfo)();
|
||||
if (!result) throw new Error('test.info() can only be called while test is running');
|
||||
return result;
|
||||
};
|
||||
this.test = test;
|
||||
}
|
||||
_currentSuite(location, title) {
|
||||
const suite = (0, _globals.currentlyLoadingFileSuite)();
|
||||
if (!suite) {
|
||||
throw new Error([`Playwright Test did not expect ${title} to be called here.`, `Most common reasons include:`, `- You are calling ${title} in a configuration file.`, `- You are calling ${title} in a file that is imported by the configuration file.`, `- You have two different versions of @playwright/test. This usually happens`, ` when one of the dependencies in your package.json depends on @playwright/test.`].join('\n'));
|
||||
}
|
||||
return suite;
|
||||
}
|
||||
_createTest(type, location, title, fnOrDetails, fn) {
|
||||
throwIfRunningInsideJest();
|
||||
const suite = this._currentSuite(location, 'test()');
|
||||
if (!suite) return;
|
||||
let details;
|
||||
let body;
|
||||
if (typeof fnOrDetails === 'function') {
|
||||
body = fnOrDetails;
|
||||
details = {};
|
||||
} else {
|
||||
body = fn;
|
||||
details = fnOrDetails;
|
||||
}
|
||||
const validatedDetails = validateTestDetails(details);
|
||||
const test = new _test.TestCase(title, body, this, location);
|
||||
test._requireFile = suite._requireFile;
|
||||
test._staticAnnotations.push(...validatedDetails.annotations);
|
||||
test._tags.push(...validatedDetails.tags);
|
||||
suite._addTest(test);
|
||||
if (type === 'only' || type === 'fail.only') test._only = true;
|
||||
if (type === 'skip' || type === 'fixme' || type === 'fail') test._staticAnnotations.push({
|
||||
type
|
||||
});else if (type === 'fail.only') test._staticAnnotations.push({
|
||||
type: 'fail'
|
||||
});
|
||||
}
|
||||
_describe(type, location, titleOrFn, fnOrDetails, fn) {
|
||||
throwIfRunningInsideJest();
|
||||
const suite = this._currentSuite(location, 'test.describe()');
|
||||
if (!suite) return;
|
||||
let title;
|
||||
let body;
|
||||
let details;
|
||||
if (typeof titleOrFn === 'function') {
|
||||
title = '';
|
||||
details = {};
|
||||
body = titleOrFn;
|
||||
} else if (typeof fnOrDetails === 'function') {
|
||||
title = titleOrFn;
|
||||
details = {};
|
||||
body = fnOrDetails;
|
||||
} else {
|
||||
title = titleOrFn;
|
||||
details = fnOrDetails;
|
||||
body = fn;
|
||||
}
|
||||
const validatedDetails = validateTestDetails(details);
|
||||
const child = new _test.Suite(title, 'describe');
|
||||
child._requireFile = suite._requireFile;
|
||||
child.location = location;
|
||||
child._staticAnnotations.push(...validatedDetails.annotations);
|
||||
child._tags.push(...validatedDetails.tags);
|
||||
suite._addSuite(child);
|
||||
if (type === 'only' || type === 'serial.only' || type === 'parallel.only') child._only = true;
|
||||
if (type === 'serial' || type === 'serial.only') child._parallelMode = 'serial';
|
||||
if (type === 'parallel' || type === 'parallel.only') child._parallelMode = 'parallel';
|
||||
if (type === 'skip' || type === 'fixme') child._staticAnnotations.push({
|
||||
type
|
||||
});
|
||||
for (let parent = suite; parent; parent = parent.parent) {
|
||||
if (parent._parallelMode === 'serial' && child._parallelMode === 'parallel') throw new Error('describe.parallel cannot be nested inside describe.serial');
|
||||
if (parent._parallelMode === 'default' && child._parallelMode === 'parallel') throw new Error('describe.parallel cannot be nested inside describe with default mode');
|
||||
}
|
||||
(0, _globals.setCurrentlyLoadingFileSuite)(child);
|
||||
body();
|
||||
(0, _globals.setCurrentlyLoadingFileSuite)(suite);
|
||||
}
|
||||
_hook(name, location, title, fn) {
|
||||
const suite = this._currentSuite(location, `test.${name}()`);
|
||||
if (!suite) return;
|
||||
if (typeof title === 'function') {
|
||||
fn = title;
|
||||
title = `${name} hook`;
|
||||
}
|
||||
suite._hooks.push({
|
||||
type: name,
|
||||
fn: fn,
|
||||
title,
|
||||
location
|
||||
});
|
||||
}
|
||||
_configure(location, options) {
|
||||
throwIfRunningInsideJest();
|
||||
const suite = this._currentSuite(location, `test.describe.configure()`);
|
||||
if (!suite) return;
|
||||
if (options.timeout !== undefined) suite._timeout = options.timeout;
|
||||
if (options.retries !== undefined) suite._retries = options.retries;
|
||||
if (options.mode !== undefined) {
|
||||
if (suite._parallelMode !== 'none') throw new Error(`"${suite._parallelMode}" mode is already assigned for the enclosing scope.`);
|
||||
suite._parallelMode = options.mode;
|
||||
for (let parent = suite.parent; parent; parent = parent.parent) {
|
||||
if (parent._parallelMode === 'serial' && suite._parallelMode === 'parallel') throw new Error('describe with parallel mode cannot be nested inside describe with serial mode');
|
||||
if (parent._parallelMode === 'default' && suite._parallelMode === 'parallel') throw new Error('describe with parallel mode cannot be nested inside describe with default mode');
|
||||
}
|
||||
}
|
||||
}
|
||||
_modifier(type, location, ...modifierArgs) {
|
||||
const suite = (0, _globals.currentlyLoadingFileSuite)();
|
||||
if (suite) {
|
||||
if (typeof modifierArgs[0] === 'string' && typeof modifierArgs[1] === 'function' && (type === 'skip' || type === 'fixme' || type === 'fail')) {
|
||||
// Support for test.{skip,fixme,fail}(title, body)
|
||||
this._createTest(type, location, modifierArgs[0], modifierArgs[1]);
|
||||
return;
|
||||
}
|
||||
if (typeof modifierArgs[0] === 'string' && typeof modifierArgs[1] === 'object' && typeof modifierArgs[2] === 'function' && (type === 'skip' || type === 'fixme' || type === 'fail')) {
|
||||
// Support for test.{skip,fixme,fail}(title, details, body)
|
||||
this._createTest(type, location, modifierArgs[0], modifierArgs[1], modifierArgs[2]);
|
||||
return;
|
||||
}
|
||||
if (typeof modifierArgs[0] === 'function') {
|
||||
suite._modifiers.push({
|
||||
type,
|
||||
fn: modifierArgs[0],
|
||||
location,
|
||||
description: modifierArgs[1]
|
||||
});
|
||||
} else {
|
||||
if (modifierArgs.length >= 1 && !modifierArgs[0]) return;
|
||||
const description = modifierArgs[1];
|
||||
suite._staticAnnotations.push({
|
||||
type,
|
||||
description
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
if (!testInfo) throw new Error(`test.${type}() can only be called inside test, describe block or fixture`);
|
||||
if (typeof modifierArgs[0] === 'function') throw new Error(`test.${type}() with a function can only be called inside describe block`);
|
||||
testInfo[type](...modifierArgs);
|
||||
}
|
||||
_setTimeout(location, timeout) {
|
||||
const suite = (0, _globals.currentlyLoadingFileSuite)();
|
||||
if (suite) {
|
||||
suite._timeout = timeout;
|
||||
return;
|
||||
}
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
if (!testInfo) throw new Error(`test.setTimeout() can only be called from a test`);
|
||||
testInfo.setTimeout(timeout);
|
||||
}
|
||||
_use(location, fixtures) {
|
||||
const suite = this._currentSuite(location, `test.use()`);
|
||||
if (!suite) return;
|
||||
suite._use.push({
|
||||
fixtures,
|
||||
location
|
||||
});
|
||||
}
|
||||
async _step(title, body, options = {}) {
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
if (!testInfo) throw new Error(`test.step() can only be called from a test`);
|
||||
const step = testInfo._addStep({
|
||||
category: 'test.step',
|
||||
title,
|
||||
location: options.location,
|
||||
box: options.box
|
||||
});
|
||||
return await _utils.zones.run('stepZone', step, async () => {
|
||||
try {
|
||||
const result = await body();
|
||||
step.complete({});
|
||||
return result;
|
||||
} catch (error) {
|
||||
step.complete({
|
||||
error
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
_extend(location, fixtures) {
|
||||
if (fixtures[testTypeSymbol]) throw new Error(`test.extend() accepts fixtures object, not a test object.\nDid you mean to call mergeTests()?`);
|
||||
const fixturesWithLocation = {
|
||||
fixtures,
|
||||
location
|
||||
};
|
||||
return new TestTypeImpl([...this.fixtures, fixturesWithLocation]).test;
|
||||
}
|
||||
}
|
||||
exports.TestTypeImpl = TestTypeImpl;
|
||||
function throwIfRunningInsideJest() {
|
||||
if (process.env.JEST_WORKER_ID) {
|
||||
const packageManagerCommand = (0, _utils.getPackageManagerExecCommand)();
|
||||
throw new Error(`Playwright Test needs to be invoked via '${packageManagerCommand} playwright test' and excluded from Jest test runs.\n` + `Creating one directory for Playwright tests and one for Jest is the recommended way of doing it.\n` + `See https://playwright.dev/docs/intro for more information about Playwright Test.`);
|
||||
}
|
||||
}
|
||||
function validateTestDetails(details) {
|
||||
const annotations = Array.isArray(details.annotation) ? details.annotation : details.annotation ? [details.annotation] : [];
|
||||
const tags = Array.isArray(details.tag) ? details.tag : details.tag ? [details.tag] : [];
|
||||
for (const tag of tags) {
|
||||
if (tag[0] !== '@') throw new Error(`Tag must start with "@" symbol, got "${tag}" instead.`);
|
||||
}
|
||||
return {
|
||||
annotations,
|
||||
tags
|
||||
};
|
||||
}
|
||||
const rootTestType = exports.rootTestType = new TestTypeImpl([]);
|
||||
function mergeTests(...tests) {
|
||||
let result = rootTestType;
|
||||
for (const t of tests) {
|
||||
const testTypeImpl = t[testTypeSymbol];
|
||||
if (!testTypeImpl) throw new Error(`mergeTests() accepts "test" functions as parameters.\nDid you mean to call test.extend() with fixtures instead?`);
|
||||
// Filter out common ancestor fixtures.
|
||||
const newFixtures = testTypeImpl.fixtures.filter(theirs => !result.fixtures.find(ours => ours.fixtures === theirs.fixtures));
|
||||
result = new TestTypeImpl([...result.fixtures, ...newFixtures]);
|
||||
}
|
||||
return result.test;
|
||||
}
|
||||
69
node_modules/playwright/lib/fsWatcher.js
generated
vendored
Normal file
69
node_modules/playwright/lib/fsWatcher.js
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Watcher = void 0;
|
||||
var _utilsBundle = require("./utilsBundle");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Watcher {
|
||||
constructor(onChange) {
|
||||
this._onChange = void 0;
|
||||
this._watchedPaths = [];
|
||||
this._ignoredFolders = [];
|
||||
this._collector = [];
|
||||
this._fsWatcher = void 0;
|
||||
this._throttleTimer = void 0;
|
||||
this._onChange = onChange;
|
||||
}
|
||||
async update(watchedPaths, ignoredFolders, reportPending) {
|
||||
var _this$_fsWatcher;
|
||||
if (JSON.stringify([this._watchedPaths, this._ignoredFolders]) === JSON.stringify(watchedPaths, ignoredFolders)) return;
|
||||
if (reportPending) this._reportEventsIfAny();
|
||||
this._watchedPaths = watchedPaths;
|
||||
this._ignoredFolders = ignoredFolders;
|
||||
void ((_this$_fsWatcher = this._fsWatcher) === null || _this$_fsWatcher === void 0 ? void 0 : _this$_fsWatcher.close());
|
||||
this._fsWatcher = undefined;
|
||||
this._collector.length = 0;
|
||||
clearTimeout(this._throttleTimer);
|
||||
this._throttleTimer = undefined;
|
||||
if (!this._watchedPaths.length) return;
|
||||
const ignored = [...this._ignoredFolders, '**/node_modules/**'];
|
||||
this._fsWatcher = _utilsBundle.chokidar.watch(watchedPaths, {
|
||||
ignoreInitial: true,
|
||||
ignored
|
||||
}).on('all', async (event, file) => {
|
||||
if (this._throttleTimer) clearTimeout(this._throttleTimer);
|
||||
this._collector.push({
|
||||
event,
|
||||
file
|
||||
});
|
||||
this._throttleTimer = setTimeout(() => this._reportEventsIfAny(), 250);
|
||||
});
|
||||
await new Promise((resolve, reject) => this._fsWatcher.once('ready', resolve).once('error', reject));
|
||||
}
|
||||
async close() {
|
||||
var _this$_fsWatcher2;
|
||||
await ((_this$_fsWatcher2 = this._fsWatcher) === null || _this$_fsWatcher2 === void 0 ? void 0 : _this$_fsWatcher2.close());
|
||||
}
|
||||
_reportEventsIfAny() {
|
||||
if (this._collector.length) this._onChange(this._collector.slice());
|
||||
this._collector.length = 0;
|
||||
}
|
||||
}
|
||||
exports.Watcher = Watcher;
|
||||
903
node_modules/playwright/lib/index.js
generated
vendored
Normal file
903
node_modules/playwright/lib/index.js
generated
vendored
Normal file
@@ -0,0 +1,903 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports._baseTest = void 0;
|
||||
Object.defineProperty(exports, "defineConfig", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _configLoader.defineConfig;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "expect", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _expect.expect;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "mergeExpects", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _expect.mergeExpects;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "mergeTests", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _testType.mergeTests;
|
||||
}
|
||||
});
|
||||
exports.test = void 0;
|
||||
var fs = _interopRequireWildcard(require("fs"));
|
||||
var path = _interopRequireWildcard(require("path"));
|
||||
var playwrightLibrary = _interopRequireWildcard(require("playwright-core"));
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _testType = require("./common/testType");
|
||||
var _globals = require("./common/globals");
|
||||
var _expect = require("./matchers/expect");
|
||||
var _configLoader = require("./common/configLoader");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const _baseTest = exports._baseTest = _testType.rootTestType.test;
|
||||
(0, _utils.addInternalStackPrefix)(path.dirname(require.resolve('../package.json')));
|
||||
if (process['__pw_initiator__']) {
|
||||
const originalStackTraceLimit = Error.stackTraceLimit;
|
||||
Error.stackTraceLimit = 200;
|
||||
try {
|
||||
throw new Error('Requiring @playwright/test second time, \nFirst:\n' + process['__pw_initiator__'] + '\n\nSecond: ');
|
||||
} finally {
|
||||
Error.stackTraceLimit = originalStackTraceLimit;
|
||||
}
|
||||
} else {
|
||||
process['__pw_initiator__'] = new Error().stack;
|
||||
}
|
||||
const playwrightFixtures = {
|
||||
defaultBrowserType: ['chromium', {
|
||||
scope: 'worker',
|
||||
option: true
|
||||
}],
|
||||
browserName: [({
|
||||
defaultBrowserType
|
||||
}, use) => use(defaultBrowserType), {
|
||||
scope: 'worker',
|
||||
option: true
|
||||
}],
|
||||
playwright: [async ({}, use) => {
|
||||
await use(require('playwright-core'));
|
||||
}, {
|
||||
scope: 'worker',
|
||||
box: true
|
||||
}],
|
||||
headless: [({
|
||||
launchOptions
|
||||
}, use) => {
|
||||
var _launchOptions$headle;
|
||||
return use((_launchOptions$headle = launchOptions.headless) !== null && _launchOptions$headle !== void 0 ? _launchOptions$headle : true);
|
||||
}, {
|
||||
scope: 'worker',
|
||||
option: true
|
||||
}],
|
||||
channel: [({
|
||||
launchOptions
|
||||
}, use) => use(launchOptions.channel), {
|
||||
scope: 'worker',
|
||||
option: true
|
||||
}],
|
||||
launchOptions: [{}, {
|
||||
scope: 'worker',
|
||||
option: true
|
||||
}],
|
||||
connectOptions: [async ({
|
||||
_optionConnectOptions
|
||||
}, use) => {
|
||||
await use(connectOptionsFromEnv() || _optionConnectOptions);
|
||||
}, {
|
||||
scope: 'worker',
|
||||
option: true
|
||||
}],
|
||||
screenshot: ['off', {
|
||||
scope: 'worker',
|
||||
option: true
|
||||
}],
|
||||
video: ['off', {
|
||||
scope: 'worker',
|
||||
option: true
|
||||
}],
|
||||
trace: ['off', {
|
||||
scope: 'worker',
|
||||
option: true
|
||||
}],
|
||||
_browserOptions: [async ({
|
||||
playwright,
|
||||
headless,
|
||||
channel,
|
||||
launchOptions
|
||||
}, use) => {
|
||||
const options = {
|
||||
handleSIGINT: false,
|
||||
...launchOptions
|
||||
};
|
||||
if (headless !== undefined) options.headless = headless;
|
||||
if (channel !== undefined) options.channel = channel;
|
||||
options.tracesDir = tracing().tracesDir();
|
||||
for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit, playwright._bidiChromium, playwright._bidiFirefox]) browserType._defaultLaunchOptions = options;
|
||||
await use(options);
|
||||
for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit, playwright._bidiChromium, playwright._bidiFirefox]) browserType._defaultLaunchOptions = undefined;
|
||||
}, {
|
||||
scope: 'worker',
|
||||
auto: true,
|
||||
box: true
|
||||
}],
|
||||
browser: [async ({
|
||||
playwright,
|
||||
browserName,
|
||||
_browserOptions,
|
||||
connectOptions,
|
||||
_reuseContext
|
||||
}, use, testInfo) => {
|
||||
if (!['chromium', 'firefox', 'webkit', '_bidiChromium', '_bidiFirefox'].includes(browserName)) throw new Error(`Unexpected browserName "${browserName}", must be one of "chromium", "firefox" or "webkit"`);
|
||||
if (connectOptions) {
|
||||
var _connectOptions$expos;
|
||||
const browser = await playwright[browserName].connect({
|
||||
...connectOptions,
|
||||
exposeNetwork: (_connectOptions$expos = connectOptions.exposeNetwork) !== null && _connectOptions$expos !== void 0 ? _connectOptions$expos : connectOptions._exposeNetwork,
|
||||
headers: {
|
||||
...(_reuseContext ? {
|
||||
'x-playwright-reuse-context': '1'
|
||||
} : {}),
|
||||
// HTTP headers are ASCII only (not UTF-8).
|
||||
'x-playwright-launch-options': (0, _utils.jsonStringifyForceASCII)(_browserOptions),
|
||||
...connectOptions.headers
|
||||
}
|
||||
});
|
||||
await use(browser);
|
||||
await browser._wrapApiCall(async () => {
|
||||
await browser.close({
|
||||
reason: 'Test ended.'
|
||||
});
|
||||
}, true);
|
||||
return;
|
||||
}
|
||||
const browser = await playwright[browserName].launch();
|
||||
await use(browser);
|
||||
await browser._wrapApiCall(async () => {
|
||||
await browser.close({
|
||||
reason: 'Test ended.'
|
||||
});
|
||||
}, true);
|
||||
}, {
|
||||
scope: 'worker',
|
||||
timeout: 0
|
||||
}],
|
||||
acceptDownloads: [({
|
||||
contextOptions
|
||||
}, use) => {
|
||||
var _contextOptions$accep;
|
||||
return use((_contextOptions$accep = contextOptions.acceptDownloads) !== null && _contextOptions$accep !== void 0 ? _contextOptions$accep : true);
|
||||
}, {
|
||||
option: true
|
||||
}],
|
||||
bypassCSP: [({
|
||||
contextOptions
|
||||
}, use) => {
|
||||
var _contextOptions$bypas;
|
||||
return use((_contextOptions$bypas = contextOptions.bypassCSP) !== null && _contextOptions$bypas !== void 0 ? _contextOptions$bypas : false);
|
||||
}, {
|
||||
option: true
|
||||
}],
|
||||
colorScheme: [({
|
||||
contextOptions
|
||||
}, use) => use(contextOptions.colorScheme === undefined ? 'light' : contextOptions.colorScheme), {
|
||||
option: true
|
||||
}],
|
||||
deviceScaleFactor: [({
|
||||
contextOptions
|
||||
}, use) => use(contextOptions.deviceScaleFactor), {
|
||||
option: true
|
||||
}],
|
||||
extraHTTPHeaders: [({
|
||||
contextOptions
|
||||
}, use) => use(contextOptions.extraHTTPHeaders), {
|
||||
option: true
|
||||
}],
|
||||
geolocation: [({
|
||||
contextOptions
|
||||
}, use) => use(contextOptions.geolocation), {
|
||||
option: true
|
||||
}],
|
||||
hasTouch: [({
|
||||
contextOptions
|
||||
}, use) => {
|
||||
var _contextOptions$hasTo;
|
||||
return use((_contextOptions$hasTo = contextOptions.hasTouch) !== null && _contextOptions$hasTo !== void 0 ? _contextOptions$hasTo : false);
|
||||
}, {
|
||||
option: true
|
||||
}],
|
||||
httpCredentials: [({
|
||||
contextOptions
|
||||
}, use) => use(contextOptions.httpCredentials), {
|
||||
option: true
|
||||
}],
|
||||
ignoreHTTPSErrors: [({
|
||||
contextOptions
|
||||
}, use) => {
|
||||
var _contextOptions$ignor;
|
||||
return use((_contextOptions$ignor = contextOptions.ignoreHTTPSErrors) !== null && _contextOptions$ignor !== void 0 ? _contextOptions$ignor : false);
|
||||
}, {
|
||||
option: true
|
||||
}],
|
||||
isMobile: [({
|
||||
contextOptions
|
||||
}, use) => {
|
||||
var _contextOptions$isMob;
|
||||
return use((_contextOptions$isMob = contextOptions.isMobile) !== null && _contextOptions$isMob !== void 0 ? _contextOptions$isMob : false);
|
||||
}, {
|
||||
option: true
|
||||
}],
|
||||
javaScriptEnabled: [({
|
||||
contextOptions
|
||||
}, use) => {
|
||||
var _contextOptions$javaS;
|
||||
return use((_contextOptions$javaS = contextOptions.javaScriptEnabled) !== null && _contextOptions$javaS !== void 0 ? _contextOptions$javaS : true);
|
||||
}, {
|
||||
option: true
|
||||
}],
|
||||
locale: [({
|
||||
contextOptions
|
||||
}, use) => {
|
||||
var _contextOptions$local;
|
||||
return use((_contextOptions$local = contextOptions.locale) !== null && _contextOptions$local !== void 0 ? _contextOptions$local : 'en-US');
|
||||
}, {
|
||||
option: true
|
||||
}],
|
||||
offline: [({
|
||||
contextOptions
|
||||
}, use) => {
|
||||
var _contextOptions$offli;
|
||||
return use((_contextOptions$offli = contextOptions.offline) !== null && _contextOptions$offli !== void 0 ? _contextOptions$offli : false);
|
||||
}, {
|
||||
option: true
|
||||
}],
|
||||
permissions: [({
|
||||
contextOptions
|
||||
}, use) => use(contextOptions.permissions), {
|
||||
option: true
|
||||
}],
|
||||
proxy: [({
|
||||
contextOptions
|
||||
}, use) => use(contextOptions.proxy), {
|
||||
option: true
|
||||
}],
|
||||
storageState: [({
|
||||
contextOptions
|
||||
}, use) => use(contextOptions.storageState), {
|
||||
option: true
|
||||
}],
|
||||
clientCertificates: [({
|
||||
contextOptions
|
||||
}, use) => use(contextOptions.clientCertificates), {
|
||||
option: true
|
||||
}],
|
||||
timezoneId: [({
|
||||
contextOptions
|
||||
}, use) => use(contextOptions.timezoneId), {
|
||||
option: true
|
||||
}],
|
||||
userAgent: [({
|
||||
contextOptions
|
||||
}, use) => use(contextOptions.userAgent), {
|
||||
option: true
|
||||
}],
|
||||
viewport: [({
|
||||
contextOptions
|
||||
}, use) => use(contextOptions.viewport === undefined ? {
|
||||
width: 1280,
|
||||
height: 720
|
||||
} : contextOptions.viewport), {
|
||||
option: true
|
||||
}],
|
||||
actionTimeout: [0, {
|
||||
option: true
|
||||
}],
|
||||
testIdAttribute: ['data-testid', {
|
||||
option: true
|
||||
}],
|
||||
navigationTimeout: [0, {
|
||||
option: true
|
||||
}],
|
||||
baseURL: [async ({}, use) => {
|
||||
await use(process.env.PLAYWRIGHT_TEST_BASE_URL);
|
||||
}, {
|
||||
option: true
|
||||
}],
|
||||
serviceWorkers: [({
|
||||
contextOptions
|
||||
}, use) => {
|
||||
var _contextOptions$servi;
|
||||
return use((_contextOptions$servi = contextOptions.serviceWorkers) !== null && _contextOptions$servi !== void 0 ? _contextOptions$servi : 'allow');
|
||||
}, {
|
||||
option: true
|
||||
}],
|
||||
contextOptions: [{}, {
|
||||
option: true
|
||||
}],
|
||||
_combinedContextOptions: [async ({
|
||||
acceptDownloads,
|
||||
bypassCSP,
|
||||
clientCertificates,
|
||||
colorScheme,
|
||||
deviceScaleFactor,
|
||||
extraHTTPHeaders,
|
||||
hasTouch,
|
||||
geolocation,
|
||||
httpCredentials,
|
||||
ignoreHTTPSErrors,
|
||||
isMobile,
|
||||
javaScriptEnabled,
|
||||
locale,
|
||||
offline,
|
||||
permissions,
|
||||
proxy,
|
||||
storageState,
|
||||
viewport,
|
||||
timezoneId,
|
||||
userAgent,
|
||||
baseURL,
|
||||
contextOptions,
|
||||
serviceWorkers
|
||||
}, use) => {
|
||||
const options = {};
|
||||
if (acceptDownloads !== undefined) options.acceptDownloads = acceptDownloads;
|
||||
if (bypassCSP !== undefined) options.bypassCSP = bypassCSP;
|
||||
if (colorScheme !== undefined) options.colorScheme = colorScheme;
|
||||
if (deviceScaleFactor !== undefined) options.deviceScaleFactor = deviceScaleFactor;
|
||||
if (extraHTTPHeaders !== undefined) options.extraHTTPHeaders = extraHTTPHeaders;
|
||||
if (geolocation !== undefined) options.geolocation = geolocation;
|
||||
if (hasTouch !== undefined) options.hasTouch = hasTouch;
|
||||
if (httpCredentials !== undefined) options.httpCredentials = httpCredentials;
|
||||
if (ignoreHTTPSErrors !== undefined) options.ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
if (isMobile !== undefined) options.isMobile = isMobile;
|
||||
if (javaScriptEnabled !== undefined) options.javaScriptEnabled = javaScriptEnabled;
|
||||
if (locale !== undefined) options.locale = locale;
|
||||
if (offline !== undefined) options.offline = offline;
|
||||
if (permissions !== undefined) options.permissions = permissions;
|
||||
if (proxy !== undefined) options.proxy = proxy;
|
||||
if (storageState !== undefined) options.storageState = storageState;
|
||||
if (clientCertificates !== null && clientCertificates !== void 0 && clientCertificates.length) options.clientCertificates = resolveClientCerticates(clientCertificates);
|
||||
if (timezoneId !== undefined) options.timezoneId = timezoneId;
|
||||
if (userAgent !== undefined) options.userAgent = userAgent;
|
||||
if (viewport !== undefined) options.viewport = viewport;
|
||||
if (baseURL !== undefined) options.baseURL = baseURL;
|
||||
if (serviceWorkers !== undefined) options.serviceWorkers = serviceWorkers;
|
||||
await use({
|
||||
...contextOptions,
|
||||
...options
|
||||
});
|
||||
}, {
|
||||
box: true
|
||||
}],
|
||||
_setupContextOptions: [async ({
|
||||
playwright,
|
||||
_combinedContextOptions,
|
||||
actionTimeout,
|
||||
navigationTimeout,
|
||||
testIdAttribute
|
||||
}, use, testInfo) => {
|
||||
if (testIdAttribute) playwrightLibrary.selectors.setTestIdAttribute(testIdAttribute);
|
||||
testInfo.snapshotSuffix = process.platform;
|
||||
if ((0, _utils.debugMode)()) testInfo._setDebugMode();
|
||||
for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit]) {
|
||||
browserType._defaultContextOptions = _combinedContextOptions;
|
||||
browserType._defaultContextTimeout = actionTimeout || 0;
|
||||
browserType._defaultContextNavigationTimeout = navigationTimeout || 0;
|
||||
}
|
||||
playwright.request._defaultContextOptions = {
|
||||
..._combinedContextOptions
|
||||
};
|
||||
playwright.request._defaultContextOptions.tracesDir = tracing().tracesDir();
|
||||
playwright.request._defaultContextOptions.timeout = actionTimeout || 0;
|
||||
await use();
|
||||
playwright.request._defaultContextOptions = undefined;
|
||||
for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit]) {
|
||||
browserType._defaultContextOptions = undefined;
|
||||
browserType._defaultContextTimeout = undefined;
|
||||
browserType._defaultContextNavigationTimeout = undefined;
|
||||
}
|
||||
}, {
|
||||
auto: 'all-hooks-included',
|
||||
title: 'context configuration',
|
||||
box: true
|
||||
}],
|
||||
_setupArtifacts: [async ({
|
||||
playwright,
|
||||
screenshot
|
||||
}, use, testInfo) => {
|
||||
// This fixture has a separate zero-timeout slot to ensure that artifact collection
|
||||
// happens even after some fixtures or hooks time out.
|
||||
// Now that default test timeout is known, we can replace zero with an actual value.
|
||||
testInfo.setTimeout(testInfo.project.timeout);
|
||||
const artifactsRecorder = new ArtifactsRecorder(playwright, tracing().artifactsDir(), screenshot);
|
||||
await artifactsRecorder.willStartTest(testInfo);
|
||||
const tracingGroupSteps = [];
|
||||
const csiListener = {
|
||||
onApiCallBegin: (apiName, params, frames, userData, out) => {
|
||||
userData.apiName = apiName;
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
if (!testInfo || apiName.includes('setTestIdAttribute') || apiName === 'tracing.groupEnd') return;
|
||||
const step = testInfo._addStep({
|
||||
location: frames[0],
|
||||
category: 'pw:api',
|
||||
title: renderApiCall(apiName, params),
|
||||
apiName,
|
||||
params
|
||||
}, tracingGroupSteps[tracingGroupSteps.length - 1]);
|
||||
userData.step = step;
|
||||
out.stepId = step.stepId;
|
||||
if (apiName === 'tracing.group') tracingGroupSteps.push(step);
|
||||
},
|
||||
onApiCallEnd: (userData, error) => {
|
||||
// "tracing.group" step will end later, when "tracing.groupEnd" finishes.
|
||||
if (userData.apiName === 'tracing.group') return;
|
||||
if (userData.apiName === 'tracing.groupEnd') {
|
||||
const step = tracingGroupSteps.pop();
|
||||
step === null || step === void 0 || step.complete({
|
||||
error
|
||||
});
|
||||
return;
|
||||
}
|
||||
const step = userData.step;
|
||||
step === null || step === void 0 || step.complete({
|
||||
error
|
||||
});
|
||||
},
|
||||
onWillPause: ({
|
||||
keepTestTimeout
|
||||
}) => {
|
||||
var _currentTestInfo;
|
||||
if (!keepTestTimeout) (_currentTestInfo = (0, _globals.currentTestInfo)()) === null || _currentTestInfo === void 0 || _currentTestInfo._setDebugMode();
|
||||
},
|
||||
runAfterCreateBrowserContext: async context => {
|
||||
await (artifactsRecorder === null || artifactsRecorder === void 0 ? void 0 : artifactsRecorder.didCreateBrowserContext(context));
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
if (testInfo) attachConnectedHeaderIfNeeded(testInfo, context.browser());
|
||||
},
|
||||
runAfterCreateRequestContext: async context => {
|
||||
await (artifactsRecorder === null || artifactsRecorder === void 0 ? void 0 : artifactsRecorder.didCreateRequestContext(context));
|
||||
},
|
||||
runBeforeCloseBrowserContext: async context => {
|
||||
await (artifactsRecorder === null || artifactsRecorder === void 0 ? void 0 : artifactsRecorder.willCloseBrowserContext(context));
|
||||
},
|
||||
runBeforeCloseRequestContext: async context => {
|
||||
await (artifactsRecorder === null || artifactsRecorder === void 0 ? void 0 : artifactsRecorder.willCloseRequestContext(context));
|
||||
}
|
||||
};
|
||||
const clientInstrumentation = playwright._instrumentation;
|
||||
clientInstrumentation.addListener(csiListener);
|
||||
await use();
|
||||
clientInstrumentation.removeListener(csiListener);
|
||||
await artifactsRecorder.didFinishTest();
|
||||
}, {
|
||||
auto: 'all-hooks-included',
|
||||
title: 'trace recording',
|
||||
box: true,
|
||||
timeout: 0
|
||||
}],
|
||||
_contextFactory: [async ({
|
||||
browser,
|
||||
video,
|
||||
_reuseContext,
|
||||
_combinedContextOptions /** mitigate dep-via-auto lack of traceability */
|
||||
}, use, testInfo) => {
|
||||
const testInfoImpl = testInfo;
|
||||
const videoMode = normalizeVideoMode(video);
|
||||
const captureVideo = shouldCaptureVideo(videoMode, testInfo) && !_reuseContext;
|
||||
const contexts = new Map();
|
||||
await use(async options => {
|
||||
const hook = testInfoImpl._currentHookType();
|
||||
if (hook === 'beforeAll' || hook === 'afterAll') {
|
||||
throw new Error([`"context" and "page" fixtures are not supported in "${hook}" since they are created on a per-test basis.`, `If you would like to reuse a single page between tests, create context manually with browser.newContext(). See https://aka.ms/playwright/reuse-page for details.`, `If you would like to configure your page before each test, do that in beforeEach hook instead.`].join('\n'));
|
||||
}
|
||||
const videoOptions = captureVideo ? {
|
||||
recordVideo: {
|
||||
dir: tracing().artifactsDir(),
|
||||
size: typeof video === 'string' ? undefined : video.size
|
||||
}
|
||||
} : {};
|
||||
const context = await browser.newContext({
|
||||
...videoOptions,
|
||||
...options
|
||||
});
|
||||
const contextData = {
|
||||
pagesWithVideo: []
|
||||
};
|
||||
contexts.set(context, contextData);
|
||||
if (captureVideo) context.on('page', page => contextData.pagesWithVideo.push(page));
|
||||
if (process.env.PW_CLOCK === 'frozen') {
|
||||
await context._wrapApiCall(async () => {
|
||||
await context.clock.install({
|
||||
time: 0
|
||||
});
|
||||
await context.clock.pauseAt(1000);
|
||||
}, true);
|
||||
} else if (process.env.PW_CLOCK === 'realtime') {
|
||||
await context._wrapApiCall(async () => {
|
||||
await context.clock.install({
|
||||
time: 0
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
return context;
|
||||
});
|
||||
let counter = 0;
|
||||
const closeReason = testInfo.status === 'timedOut' ? 'Test timeout of ' + testInfo.timeout + 'ms exceeded.' : 'Test ended.';
|
||||
await Promise.all([...contexts.keys()].map(async context => {
|
||||
await context._wrapApiCall(async () => {
|
||||
await context.close({
|
||||
reason: closeReason
|
||||
});
|
||||
}, true);
|
||||
const testFailed = testInfo.status !== testInfo.expectedStatus;
|
||||
const preserveVideo = captureVideo && (videoMode === 'on' || testFailed && videoMode === 'retain-on-failure' || videoMode === 'on-first-retry' && testInfo.retry === 1);
|
||||
if (preserveVideo) {
|
||||
const {
|
||||
pagesWithVideo: pagesForVideo
|
||||
} = contexts.get(context);
|
||||
const videos = pagesForVideo.map(p => p.video()).filter(Boolean);
|
||||
await Promise.all(videos.map(async v => {
|
||||
try {
|
||||
const savedPath = testInfo.outputPath(`video${counter ? '-' + counter : ''}.webm`);
|
||||
++counter;
|
||||
await v.saveAs(savedPath);
|
||||
testInfo.attachments.push({
|
||||
name: 'video',
|
||||
path: savedPath,
|
||||
contentType: 'video/webm'
|
||||
});
|
||||
} catch (e) {
|
||||
// Silent catch empty videos.
|
||||
}
|
||||
}));
|
||||
}
|
||||
}));
|
||||
}, {
|
||||
scope: 'test',
|
||||
title: 'context',
|
||||
box: true
|
||||
}],
|
||||
_optionContextReuseMode: ['none', {
|
||||
scope: 'worker',
|
||||
option: true
|
||||
}],
|
||||
_optionConnectOptions: [undefined, {
|
||||
scope: 'worker',
|
||||
option: true
|
||||
}],
|
||||
_reuseContext: [async ({
|
||||
video,
|
||||
_optionContextReuseMode
|
||||
}, use) => {
|
||||
let mode = _optionContextReuseMode;
|
||||
if (process.env.PW_TEST_REUSE_CONTEXT) mode = 'when-possible';
|
||||
const reuse = mode === 'when-possible' && normalizeVideoMode(video) === 'off';
|
||||
await use(reuse);
|
||||
}, {
|
||||
scope: 'worker',
|
||||
title: 'context',
|
||||
box: true
|
||||
}],
|
||||
context: async ({
|
||||
playwright,
|
||||
browser,
|
||||
_reuseContext,
|
||||
_contextFactory
|
||||
}, use, testInfo) => {
|
||||
attachConnectedHeaderIfNeeded(testInfo, browser);
|
||||
if (!_reuseContext) {
|
||||
await use(await _contextFactory());
|
||||
return;
|
||||
}
|
||||
const defaultContextOptions = playwright.chromium._defaultContextOptions;
|
||||
const context = await browser._newContextForReuse(defaultContextOptions);
|
||||
context[kIsReusedContext] = true;
|
||||
await use(context);
|
||||
const closeReason = testInfo.status === 'timedOut' ? 'Test timeout of ' + testInfo.timeout + 'ms exceeded.' : 'Test ended.';
|
||||
await browser._stopPendingOperations(closeReason);
|
||||
},
|
||||
page: async ({
|
||||
context,
|
||||
_reuseContext
|
||||
}, use) => {
|
||||
if (!_reuseContext) {
|
||||
await use(await context.newPage());
|
||||
return;
|
||||
}
|
||||
|
||||
// First time we are reusing the context, we should create the page.
|
||||
let [page] = context.pages();
|
||||
if (!page) page = await context.newPage();
|
||||
await use(page);
|
||||
},
|
||||
request: async ({
|
||||
playwright
|
||||
}, use) => {
|
||||
const request = await playwright.request.newContext();
|
||||
await use(request);
|
||||
const hook = test.info()._currentHookType();
|
||||
if (hook === 'beforeAll') {
|
||||
await request.dispose({
|
||||
reason: [`Fixture { request } from beforeAll cannot be reused in a test.`, ` - Recommended fix: use a separate { request } in the test.`, ` - Alternatively, manually create APIRequestContext in beforeAll and dispose it in afterAll.`, `See https://playwright.dev/docs/api-testing#sending-api-requests-from-ui-tests for more details.`].join('\n')
|
||||
});
|
||||
} else {
|
||||
await request.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
function normalizeVideoMode(video) {
|
||||
if (!video) return 'off';
|
||||
let videoMode = typeof video === 'string' ? video : video.mode;
|
||||
if (videoMode === 'retry-with-video') videoMode = 'on-first-retry';
|
||||
return videoMode;
|
||||
}
|
||||
function shouldCaptureVideo(videoMode, testInfo) {
|
||||
return videoMode === 'on' || videoMode === 'retain-on-failure' || videoMode === 'on-first-retry' && testInfo.retry === 1;
|
||||
}
|
||||
function normalizeScreenshotMode(screenshot) {
|
||||
if (!screenshot) return 'off';
|
||||
return typeof screenshot === 'string' ? screenshot : screenshot.mode;
|
||||
}
|
||||
function attachConnectedHeaderIfNeeded(testInfo, browser) {
|
||||
const connectHeaders = browser === null || browser === void 0 ? void 0 : browser._connectHeaders;
|
||||
if (!connectHeaders) return;
|
||||
for (const header of connectHeaders) {
|
||||
if (header.name !== 'x-playwright-attachment') continue;
|
||||
const [name, value] = header.value.split('=');
|
||||
if (!name || !value) continue;
|
||||
if (testInfo.attachments.some(attachment => attachment.name === name)) continue;
|
||||
testInfo.attachments.push({
|
||||
name,
|
||||
contentType: 'text/plain',
|
||||
body: Buffer.from(value)
|
||||
});
|
||||
}
|
||||
}
|
||||
function resolveFileToConfig(file) {
|
||||
const config = test.info().config.configFile;
|
||||
if (!config || !file) return file;
|
||||
if (path.isAbsolute(file)) return file;
|
||||
return path.resolve(path.dirname(config), file);
|
||||
}
|
||||
function resolveClientCerticates(clientCertificates) {
|
||||
for (const cert of clientCertificates) {
|
||||
cert.certPath = resolveFileToConfig(cert.certPath);
|
||||
cert.keyPath = resolveFileToConfig(cert.keyPath);
|
||||
cert.pfxPath = resolveFileToConfig(cert.pfxPath);
|
||||
}
|
||||
return clientCertificates;
|
||||
}
|
||||
const kTracingStarted = Symbol('kTracingStarted');
|
||||
const kIsReusedContext = Symbol('kReusedContext');
|
||||
function connectOptionsFromEnv() {
|
||||
const wsEndpoint = process.env.PW_TEST_CONNECT_WS_ENDPOINT;
|
||||
if (!wsEndpoint) return undefined;
|
||||
const headers = process.env.PW_TEST_CONNECT_HEADERS ? JSON.parse(process.env.PW_TEST_CONNECT_HEADERS) : undefined;
|
||||
return {
|
||||
wsEndpoint,
|
||||
headers,
|
||||
exposeNetwork: process.env.PW_TEST_CONNECT_EXPOSE_NETWORK
|
||||
};
|
||||
}
|
||||
class ArtifactsRecorder {
|
||||
constructor(playwright, artifactsDir, screenshot) {
|
||||
this._testInfo = void 0;
|
||||
this._playwright = void 0;
|
||||
this._artifactsDir = void 0;
|
||||
this._screenshotMode = void 0;
|
||||
this._screenshotOptions = void 0;
|
||||
this._temporaryScreenshots = [];
|
||||
this._temporaryArtifacts = [];
|
||||
this._reusedContexts = new Set();
|
||||
this._screenshotOrdinal = 0;
|
||||
this._screenshottedSymbol = void 0;
|
||||
this._startedCollectingArtifacts = void 0;
|
||||
this._playwright = playwright;
|
||||
this._artifactsDir = artifactsDir;
|
||||
this._screenshotMode = normalizeScreenshotMode(screenshot);
|
||||
this._screenshotOptions = typeof screenshot === 'string' ? undefined : screenshot;
|
||||
this._screenshottedSymbol = Symbol('screenshotted');
|
||||
this._startedCollectingArtifacts = Symbol('startedCollectingArtifacts');
|
||||
}
|
||||
_createTemporaryArtifact(...name) {
|
||||
const file = path.join(this._artifactsDir, ...name);
|
||||
this._temporaryArtifacts.push(file);
|
||||
return file;
|
||||
}
|
||||
async willStartTest(testInfo) {
|
||||
this._testInfo = testInfo;
|
||||
testInfo._onDidFinishTestFunction = () => this.didFinishTestFunction();
|
||||
|
||||
// Since beforeAll(s), test and afterAll(s) reuse the same TestInfo, make sure we do not
|
||||
// overwrite previous screenshots.
|
||||
this._screenshotOrdinal = testInfo.attachments.filter(a => a.name === 'screenshot').length;
|
||||
|
||||
// Process existing contexts.
|
||||
for (const browserType of [this._playwright.chromium, this._playwright.firefox, this._playwright.webkit]) {
|
||||
const promises = [];
|
||||
const existingContexts = Array.from(browserType._contexts);
|
||||
for (const context of existingContexts) {
|
||||
if (context[kIsReusedContext]) this._reusedContexts.add(context);else promises.push(this.didCreateBrowserContext(context));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}
|
||||
{
|
||||
const existingApiRequests = Array.from(this._playwright.request._contexts);
|
||||
await Promise.all(existingApiRequests.map(c => this.didCreateRequestContext(c)));
|
||||
}
|
||||
}
|
||||
async didCreateBrowserContext(context) {
|
||||
await this._startTraceChunkOnContextCreation(context.tracing);
|
||||
}
|
||||
async willCloseBrowserContext(context) {
|
||||
// When reusing context, we get all previous contexts closed at the start of next test.
|
||||
// Do not record empty traces and useless screenshots for them.
|
||||
if (this._reusedContexts.has(context)) return;
|
||||
await this._stopTracing(context.tracing);
|
||||
if (this._screenshotMode === 'on' || this._screenshotMode === 'only-on-failure' || this._screenshotMode === 'on-first-failure' && this._testInfo.retry === 0) {
|
||||
// Capture screenshot for now. We'll know whether we have to preserve them
|
||||
// after the test finishes.
|
||||
await Promise.all(context.pages().map(page => this._screenshotPage(page, true)));
|
||||
}
|
||||
}
|
||||
async didCreateRequestContext(context) {
|
||||
const tracing = context._tracing;
|
||||
await this._startTraceChunkOnContextCreation(tracing);
|
||||
}
|
||||
async willCloseRequestContext(context) {
|
||||
const tracing = context._tracing;
|
||||
await this._stopTracing(tracing);
|
||||
}
|
||||
_shouldCaptureScreenshotUponFinish() {
|
||||
return this._screenshotMode === 'on' || this._screenshotMode === 'only-on-failure' && this._testInfo._isFailure() || this._screenshotMode === 'on-first-failure' && this._testInfo._isFailure() && this._testInfo.retry === 0;
|
||||
}
|
||||
async didFinishTestFunction() {
|
||||
if (this._shouldCaptureScreenshotUponFinish()) await this._screenshotOnTestFailure();
|
||||
}
|
||||
async didFinishTest() {
|
||||
const captureScreenshots = this._shouldCaptureScreenshotUponFinish();
|
||||
if (captureScreenshots) await this._screenshotOnTestFailure();
|
||||
const leftoverContexts = [];
|
||||
for (const browserType of [this._playwright.chromium, this._playwright.firefox, this._playwright.webkit]) leftoverContexts.push(...browserType._contexts);
|
||||
const leftoverApiRequests = Array.from(this._playwright.request._contexts);
|
||||
|
||||
// Collect traces/screenshots for remaining contexts.
|
||||
await Promise.all(leftoverContexts.map(async context => {
|
||||
await this._stopTracing(context.tracing);
|
||||
}).concat(leftoverApiRequests.map(async context => {
|
||||
const tracing = context._tracing;
|
||||
await this._stopTracing(tracing);
|
||||
})));
|
||||
|
||||
// Attach temporary screenshots for contexts closed before collecting the test trace.
|
||||
if (captureScreenshots) {
|
||||
for (const file of this._temporaryScreenshots) {
|
||||
try {
|
||||
const screenshotPath = this._createScreenshotAttachmentPath();
|
||||
await fs.promises.rename(file, screenshotPath);
|
||||
this._attachScreenshot(screenshotPath);
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_createScreenshotAttachmentPath() {
|
||||
const testFailed = this._testInfo._isFailure();
|
||||
const index = this._screenshotOrdinal + 1;
|
||||
++this._screenshotOrdinal;
|
||||
const screenshotPath = this._testInfo.outputPath(`test-${testFailed ? 'failed' : 'finished'}-${index}.png`);
|
||||
return screenshotPath;
|
||||
}
|
||||
async _screenshotPage(page, temporary) {
|
||||
if (page[this._screenshottedSymbol]) return;
|
||||
page[this._screenshottedSymbol] = true;
|
||||
try {
|
||||
const screenshotPath = temporary ? this._createTemporaryArtifact((0, _utils.createGuid)() + '.png') : this._createScreenshotAttachmentPath();
|
||||
// Pass caret=initial to avoid any evaluations that might slow down the screenshot
|
||||
// and let the page modify itself from the problematic state it had at the moment of failure.
|
||||
await page.screenshot({
|
||||
...this._screenshotOptions,
|
||||
timeout: 5000,
|
||||
path: screenshotPath,
|
||||
caret: 'initial'
|
||||
});
|
||||
if (temporary) this._temporaryScreenshots.push(screenshotPath);else this._attachScreenshot(screenshotPath);
|
||||
} catch {
|
||||
// Screenshot may fail, just ignore.
|
||||
}
|
||||
}
|
||||
_attachScreenshot(screenshotPath) {
|
||||
this._testInfo.attachments.push({
|
||||
name: 'screenshot',
|
||||
path: screenshotPath,
|
||||
contentType: 'image/png'
|
||||
});
|
||||
}
|
||||
async _screenshotOnTestFailure() {
|
||||
const contexts = [];
|
||||
for (const browserType of [this._playwright.chromium, this._playwright.firefox, this._playwright.webkit]) contexts.push(...browserType._contexts);
|
||||
const pages = contexts.map(ctx => ctx.pages()).flat();
|
||||
await Promise.all(pages.map(page => this._screenshotPage(page, false)));
|
||||
}
|
||||
async _startTraceChunkOnContextCreation(tracing) {
|
||||
const options = this._testInfo._tracing.traceOptions();
|
||||
if (options) {
|
||||
const title = this._testInfo._tracing.traceTitle();
|
||||
const name = this._testInfo._tracing.generateNextTraceRecordingName();
|
||||
if (!tracing[kTracingStarted]) {
|
||||
await tracing.start({
|
||||
...options,
|
||||
title,
|
||||
name
|
||||
});
|
||||
tracing[kTracingStarted] = true;
|
||||
} else {
|
||||
await tracing.startChunk({
|
||||
title,
|
||||
name
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (tracing[kTracingStarted]) {
|
||||
tracing[kTracingStarted] = false;
|
||||
await tracing.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
async _stopTracing(tracing) {
|
||||
if (tracing[this._startedCollectingArtifacts]) return;
|
||||
tracing[this._startedCollectingArtifacts] = true;
|
||||
if (this._testInfo._tracing.traceOptions() && tracing[kTracingStarted]) await tracing.stopChunk({
|
||||
path: this._testInfo._tracing.generateNextTraceRecordingPath()
|
||||
});
|
||||
}
|
||||
}
|
||||
const paramsToRender = ['url', 'selector', 'text', 'key'];
|
||||
function renderApiCall(apiName, params) {
|
||||
if (apiName === 'tracing.group') return params.name;
|
||||
const paramsArray = [];
|
||||
if (params) {
|
||||
for (const name of paramsToRender) {
|
||||
if (!(name in params)) continue;
|
||||
let value;
|
||||
if (name === 'selector' && (0, _utils.isString)(params[name]) && params[name].startsWith('internal:')) {
|
||||
const getter = (0, _utils.asLocator)('javascript', params[name]);
|
||||
apiName = apiName.replace(/^locator\./, 'locator.' + getter + '.');
|
||||
apiName = apiName.replace(/^page\./, 'page.' + getter + '.');
|
||||
apiName = apiName.replace(/^frame\./, 'frame.' + getter + '.');
|
||||
} else {
|
||||
value = params[name];
|
||||
paramsArray.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
const paramsText = paramsArray.length ? '(' + paramsArray.join(', ') + ')' : '';
|
||||
return apiName + paramsText;
|
||||
}
|
||||
function tracing() {
|
||||
return test.info()._tracing;
|
||||
}
|
||||
const test = exports.test = _baseTest.extend(playwrightFixtures);
|
||||
28
node_modules/playwright/lib/internalsForTest.js
generated
vendored
Normal file
28
node_modules/playwright/lib/internalsForTest.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.fileDependencies = fileDependencies;
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _compilationCache = require("./transform/compilationCache");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function fileDependencies() {
|
||||
return Object.fromEntries([...(0, _compilationCache.fileDependenciesForTest)().entries()].map(entry => [_path.default.basename(entry[0]), [...entry[1]].map(f => _path.default.basename(f)).sort()]));
|
||||
}
|
||||
72
node_modules/playwright/lib/isomorphic/events.js
generated
vendored
Normal file
72
node_modules/playwright/lib/isomorphic/events.js
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.EventEmitter = exports.Disposable = void 0;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
let Disposable = exports.Disposable = void 0;
|
||||
(function (_Disposable) {
|
||||
function disposeAll(disposables) {
|
||||
for (const disposable of disposables.splice(0)) disposable.dispose();
|
||||
}
|
||||
_Disposable.disposeAll = disposeAll;
|
||||
})(Disposable || (exports.Disposable = Disposable = {}));
|
||||
class EventEmitter {
|
||||
constructor() {
|
||||
this.event = void 0;
|
||||
this._deliveryQueue = void 0;
|
||||
this._listeners = new Set();
|
||||
this.event = (listener, disposables) => {
|
||||
this._listeners.add(listener);
|
||||
let disposed = false;
|
||||
const self = this;
|
||||
const result = {
|
||||
dispose() {
|
||||
if (!disposed) {
|
||||
disposed = true;
|
||||
self._listeners.delete(listener);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (disposables) disposables.push(result);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
fire(event) {
|
||||
const dispatch = !this._deliveryQueue;
|
||||
if (!this._deliveryQueue) this._deliveryQueue = [];
|
||||
for (const listener of this._listeners) this._deliveryQueue.push({
|
||||
listener,
|
||||
event
|
||||
});
|
||||
if (!dispatch) return;
|
||||
for (let index = 0; index < this._deliveryQueue.length; index++) {
|
||||
const {
|
||||
listener,
|
||||
event
|
||||
} = this._deliveryQueue[index];
|
||||
listener.call(null, event);
|
||||
}
|
||||
this._deliveryQueue = undefined;
|
||||
}
|
||||
dispose() {
|
||||
this._listeners.clear();
|
||||
if (this._deliveryQueue) this._deliveryQueue = [];
|
||||
}
|
||||
}
|
||||
exports.EventEmitter = EventEmitter;
|
||||
25
node_modules/playwright/lib/isomorphic/folders.js
generated
vendored
Normal file
25
node_modules/playwright/lib/isomorphic/folders.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.artifactsFolderName = artifactsFolderName;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function artifactsFolderName(workerIndex) {
|
||||
return `.playwright-artifacts-${workerIndex}`;
|
||||
}
|
||||
58
node_modules/playwright/lib/isomorphic/stringInternPool.js
generated
vendored
Normal file
58
node_modules/playwright/lib/isomorphic/stringInternPool.js
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.StringInternPool = exports.JsonStringInternalizer = void 0;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class StringInternPool {
|
||||
constructor() {
|
||||
this._stringCache = new Map();
|
||||
}
|
||||
internString(s) {
|
||||
let result = this._stringCache.get(s);
|
||||
if (!result) {
|
||||
this._stringCache.set(s, s);
|
||||
result = s;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
exports.StringInternPool = StringInternPool;
|
||||
class JsonStringInternalizer {
|
||||
constructor(pool) {
|
||||
this._pool = void 0;
|
||||
this._pool = pool;
|
||||
}
|
||||
traverse(value) {
|
||||
if (typeof value !== 'object') return;
|
||||
if (Array.isArray(value)) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (typeof value[i] === 'string') value[i] = this.intern(value[i]);else this.traverse(value[i]);
|
||||
}
|
||||
} else {
|
||||
for (const name in value) {
|
||||
if (typeof value[name] === 'string') value[name] = this.intern(value[name]);else this.traverse(value[name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
intern(value) {
|
||||
return this._pool.internString(value);
|
||||
}
|
||||
}
|
||||
exports.JsonStringInternalizer = JsonStringInternalizer;
|
||||
504
node_modules/playwright/lib/isomorphic/teleReceiver.js
generated
vendored
Normal file
504
node_modules/playwright/lib/isomorphic/teleReceiver.js
generated
vendored
Normal file
@@ -0,0 +1,504 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.baseFullConfig = exports.TeleTestResult = exports.TeleTestCase = exports.TeleSuite = exports.TeleReporterReceiver = void 0;
|
||||
exports.computeTestCaseOutcome = computeTestCaseOutcome;
|
||||
exports.parseRegexPatterns = parseRegexPatterns;
|
||||
exports.serializeRegexPatterns = serializeRegexPatterns;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class TeleReporterReceiver {
|
||||
constructor(reporter, options = {}) {
|
||||
this.isListing = false;
|
||||
this._rootSuite = void 0;
|
||||
this._options = void 0;
|
||||
this._reporter = void 0;
|
||||
this._tests = new Map();
|
||||
this._rootDir = void 0;
|
||||
this._config = void 0;
|
||||
this._rootSuite = new TeleSuite('', 'root');
|
||||
this._options = options;
|
||||
this._reporter = reporter;
|
||||
}
|
||||
reset() {
|
||||
this._rootSuite._entries = [];
|
||||
this._tests.clear();
|
||||
}
|
||||
dispatch(message) {
|
||||
const {
|
||||
method,
|
||||
params
|
||||
} = message;
|
||||
if (method === 'onConfigure') {
|
||||
this._onConfigure(params.config);
|
||||
return;
|
||||
}
|
||||
if (method === 'onProject') {
|
||||
this._onProject(params.project);
|
||||
return;
|
||||
}
|
||||
if (method === 'onBegin') {
|
||||
this._onBegin();
|
||||
return;
|
||||
}
|
||||
if (method === 'onTestBegin') {
|
||||
this._onTestBegin(params.testId, params.result);
|
||||
return;
|
||||
}
|
||||
if (method === 'onTestEnd') {
|
||||
this._onTestEnd(params.test, params.result);
|
||||
return;
|
||||
}
|
||||
if (method === 'onStepBegin') {
|
||||
this._onStepBegin(params.testId, params.resultId, params.step);
|
||||
return;
|
||||
}
|
||||
if (method === 'onStepEnd') {
|
||||
this._onStepEnd(params.testId, params.resultId, params.step);
|
||||
return;
|
||||
}
|
||||
if (method === 'onError') {
|
||||
this._onError(params.error);
|
||||
return;
|
||||
}
|
||||
if (method === 'onStdIO') {
|
||||
this._onStdIO(params.type, params.testId, params.resultId, params.data, params.isBase64);
|
||||
return;
|
||||
}
|
||||
if (method === 'onEnd') return this._onEnd(params.result);
|
||||
if (method === 'onExit') return this._onExit();
|
||||
}
|
||||
_onConfigure(config) {
|
||||
var _this$_reporter$onCon, _this$_reporter;
|
||||
this._rootDir = config.rootDir;
|
||||
this._config = this._parseConfig(config);
|
||||
(_this$_reporter$onCon = (_this$_reporter = this._reporter).onConfigure) === null || _this$_reporter$onCon === void 0 || _this$_reporter$onCon.call(_this$_reporter, this._config);
|
||||
}
|
||||
_onProject(project) {
|
||||
let projectSuite = this._options.mergeProjects ? this._rootSuite.suites.find(suite => suite.project().name === project.name) : undefined;
|
||||
if (!projectSuite) {
|
||||
projectSuite = new TeleSuite(project.name, 'project');
|
||||
this._rootSuite._addSuite(projectSuite);
|
||||
}
|
||||
// Always update project in watch mode.
|
||||
projectSuite._project = this._parseProject(project);
|
||||
for (const suite of project.suites) this._mergeSuiteInto(suite, projectSuite);
|
||||
}
|
||||
_onBegin() {
|
||||
var _this$_reporter$onBeg, _this$_reporter2;
|
||||
(_this$_reporter$onBeg = (_this$_reporter2 = this._reporter).onBegin) === null || _this$_reporter$onBeg === void 0 || _this$_reporter$onBeg.call(_this$_reporter2, this._rootSuite);
|
||||
}
|
||||
_onTestBegin(testId, payload) {
|
||||
var _this$_reporter$onTes, _this$_reporter3;
|
||||
const test = this._tests.get(testId);
|
||||
if (this._options.clearPreviousResultsWhenTestBegins) test.results = [];
|
||||
const testResult = test._createTestResult(payload.id);
|
||||
testResult.retry = payload.retry;
|
||||
testResult.workerIndex = payload.workerIndex;
|
||||
testResult.parallelIndex = payload.parallelIndex;
|
||||
testResult.setStartTimeNumber(payload.startTime);
|
||||
(_this$_reporter$onTes = (_this$_reporter3 = this._reporter).onTestBegin) === null || _this$_reporter$onTes === void 0 || _this$_reporter$onTes.call(_this$_reporter3, test, testResult);
|
||||
}
|
||||
_onTestEnd(testEndPayload, payload) {
|
||||
var _result$errors, _this$_reporter$onTes2, _this$_reporter4;
|
||||
const test = this._tests.get(testEndPayload.testId);
|
||||
test.timeout = testEndPayload.timeout;
|
||||
test.expectedStatus = testEndPayload.expectedStatus;
|
||||
test.annotations = testEndPayload.annotations;
|
||||
const result = test.results.find(r => r._id === payload.id);
|
||||
result.duration = payload.duration;
|
||||
result.status = payload.status;
|
||||
result.errors = payload.errors;
|
||||
result.error = (_result$errors = result.errors) === null || _result$errors === void 0 ? void 0 : _result$errors[0];
|
||||
result.attachments = this._parseAttachments(payload.attachments);
|
||||
(_this$_reporter$onTes2 = (_this$_reporter4 = this._reporter).onTestEnd) === null || _this$_reporter$onTes2 === void 0 || _this$_reporter$onTes2.call(_this$_reporter4, test, result);
|
||||
// Free up the memory as won't see these step ids.
|
||||
result._stepMap = new Map();
|
||||
}
|
||||
_onStepBegin(testId, resultId, payload) {
|
||||
var _this$_reporter$onSte, _this$_reporter5;
|
||||
const test = this._tests.get(testId);
|
||||
const result = test.results.find(r => r._id === resultId);
|
||||
const parentStep = payload.parentStepId ? result._stepMap.get(payload.parentStepId) : undefined;
|
||||
const location = this._absoluteLocation(payload.location);
|
||||
const step = new TeleTestStep(payload, parentStep, location);
|
||||
if (parentStep) parentStep.steps.push(step);else result.steps.push(step);
|
||||
result._stepMap.set(payload.id, step);
|
||||
(_this$_reporter$onSte = (_this$_reporter5 = this._reporter).onStepBegin) === null || _this$_reporter$onSte === void 0 || _this$_reporter$onSte.call(_this$_reporter5, test, result, step);
|
||||
}
|
||||
_onStepEnd(testId, resultId, payload) {
|
||||
var _this$_reporter$onSte2, _this$_reporter6;
|
||||
const test = this._tests.get(testId);
|
||||
const result = test.results.find(r => r._id === resultId);
|
||||
const step = result._stepMap.get(payload.id);
|
||||
step.duration = payload.duration;
|
||||
step.error = payload.error;
|
||||
(_this$_reporter$onSte2 = (_this$_reporter6 = this._reporter).onStepEnd) === null || _this$_reporter$onSte2 === void 0 || _this$_reporter$onSte2.call(_this$_reporter6, test, result, step);
|
||||
}
|
||||
_onError(error) {
|
||||
var _this$_reporter$onErr, _this$_reporter7;
|
||||
(_this$_reporter$onErr = (_this$_reporter7 = this._reporter).onError) === null || _this$_reporter$onErr === void 0 || _this$_reporter$onErr.call(_this$_reporter7, error);
|
||||
}
|
||||
_onStdIO(type, testId, resultId, data, isBase64) {
|
||||
const chunk = isBase64 ? globalThis.Buffer ? Buffer.from(data, 'base64') : atob(data) : data;
|
||||
const test = testId ? this._tests.get(testId) : undefined;
|
||||
const result = test && resultId ? test.results.find(r => r._id === resultId) : undefined;
|
||||
if (type === 'stdout') {
|
||||
var _this$_reporter$onStd, _this$_reporter8;
|
||||
result === null || result === void 0 || result.stdout.push(chunk);
|
||||
(_this$_reporter$onStd = (_this$_reporter8 = this._reporter).onStdOut) === null || _this$_reporter$onStd === void 0 || _this$_reporter$onStd.call(_this$_reporter8, chunk, test, result);
|
||||
} else {
|
||||
var _this$_reporter$onStd2, _this$_reporter9;
|
||||
result === null || result === void 0 || result.stderr.push(chunk);
|
||||
(_this$_reporter$onStd2 = (_this$_reporter9 = this._reporter).onStdErr) === null || _this$_reporter$onStd2 === void 0 || _this$_reporter$onStd2.call(_this$_reporter9, chunk, test, result);
|
||||
}
|
||||
}
|
||||
async _onEnd(result) {
|
||||
var _this$_reporter$onEnd, _this$_reporter10;
|
||||
await ((_this$_reporter$onEnd = (_this$_reporter10 = this._reporter).onEnd) === null || _this$_reporter$onEnd === void 0 ? void 0 : _this$_reporter$onEnd.call(_this$_reporter10, {
|
||||
status: result.status,
|
||||
startTime: new Date(result.startTime),
|
||||
duration: result.duration
|
||||
}));
|
||||
}
|
||||
_onExit() {
|
||||
var _this$_reporter$onExi, _this$_reporter11;
|
||||
return (_this$_reporter$onExi = (_this$_reporter11 = this._reporter).onExit) === null || _this$_reporter$onExi === void 0 ? void 0 : _this$_reporter$onExi.call(_this$_reporter11);
|
||||
}
|
||||
_parseConfig(config) {
|
||||
const result = {
|
||||
...baseFullConfig,
|
||||
...config
|
||||
};
|
||||
if (this._options.configOverrides) {
|
||||
result.configFile = this._options.configOverrides.configFile;
|
||||
result.reportSlowTests = this._options.configOverrides.reportSlowTests;
|
||||
result.quiet = this._options.configOverrides.quiet;
|
||||
result.reporter = [...this._options.configOverrides.reporter];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
_parseProject(project) {
|
||||
return {
|
||||
metadata: project.metadata,
|
||||
name: project.name,
|
||||
outputDir: this._absolutePath(project.outputDir),
|
||||
repeatEach: project.repeatEach,
|
||||
retries: project.retries,
|
||||
testDir: this._absolutePath(project.testDir),
|
||||
testIgnore: parseRegexPatterns(project.testIgnore),
|
||||
testMatch: parseRegexPatterns(project.testMatch),
|
||||
timeout: project.timeout,
|
||||
grep: parseRegexPatterns(project.grep),
|
||||
grepInvert: parseRegexPatterns(project.grepInvert),
|
||||
dependencies: project.dependencies,
|
||||
teardown: project.teardown,
|
||||
snapshotDir: this._absolutePath(project.snapshotDir),
|
||||
use: {}
|
||||
};
|
||||
}
|
||||
_parseAttachments(attachments) {
|
||||
return attachments.map(a => {
|
||||
return {
|
||||
...a,
|
||||
body: a.base64 && globalThis.Buffer ? Buffer.from(a.base64, 'base64') : undefined
|
||||
};
|
||||
});
|
||||
}
|
||||
_mergeSuiteInto(jsonSuite, parent) {
|
||||
let targetSuite = parent.suites.find(s => s.title === jsonSuite.title);
|
||||
if (!targetSuite) {
|
||||
targetSuite = new TeleSuite(jsonSuite.title, parent.type === 'project' ? 'file' : 'describe');
|
||||
parent._addSuite(targetSuite);
|
||||
}
|
||||
targetSuite.location = this._absoluteLocation(jsonSuite.location);
|
||||
jsonSuite.entries.forEach(e => {
|
||||
if ('testId' in e) this._mergeTestInto(e, targetSuite);else this._mergeSuiteInto(e, targetSuite);
|
||||
});
|
||||
}
|
||||
_mergeTestInto(jsonTest, parent) {
|
||||
let targetTest = this._options.mergeTestCases ? parent.tests.find(s => s.title === jsonTest.title && s.repeatEachIndex === jsonTest.repeatEachIndex) : undefined;
|
||||
if (!targetTest) {
|
||||
targetTest = new TeleTestCase(jsonTest.testId, jsonTest.title, this._absoluteLocation(jsonTest.location), jsonTest.repeatEachIndex);
|
||||
parent._addTest(targetTest);
|
||||
this._tests.set(targetTest.id, targetTest);
|
||||
}
|
||||
this._updateTest(jsonTest, targetTest);
|
||||
}
|
||||
_updateTest(payload, test) {
|
||||
var _payload$tags, _payload$annotations;
|
||||
test.id = payload.testId;
|
||||
test.location = this._absoluteLocation(payload.location);
|
||||
test.retries = payload.retries;
|
||||
test.tags = (_payload$tags = payload.tags) !== null && _payload$tags !== void 0 ? _payload$tags : [];
|
||||
test.annotations = (_payload$annotations = payload.annotations) !== null && _payload$annotations !== void 0 ? _payload$annotations : [];
|
||||
return test;
|
||||
}
|
||||
_absoluteLocation(location) {
|
||||
if (!location) return location;
|
||||
return {
|
||||
...location,
|
||||
file: this._absolutePath(location.file)
|
||||
};
|
||||
}
|
||||
_absolutePath(relativePath) {
|
||||
if (relativePath === undefined) return;
|
||||
return this._options.resolvePath ? this._options.resolvePath(this._rootDir, relativePath) : this._rootDir + '/' + relativePath;
|
||||
}
|
||||
}
|
||||
exports.TeleReporterReceiver = TeleReporterReceiver;
|
||||
class TeleSuite {
|
||||
constructor(title, type) {
|
||||
this.title = void 0;
|
||||
this.location = void 0;
|
||||
this.parent = void 0;
|
||||
this._entries = [];
|
||||
this._requireFile = '';
|
||||
this._timeout = void 0;
|
||||
this._retries = void 0;
|
||||
this._project = void 0;
|
||||
this._parallelMode = 'none';
|
||||
this._type = void 0;
|
||||
this.title = title;
|
||||
this._type = type;
|
||||
}
|
||||
get type() {
|
||||
return this._type;
|
||||
}
|
||||
get suites() {
|
||||
return this._entries.filter(e => e.type !== 'test');
|
||||
}
|
||||
get tests() {
|
||||
return this._entries.filter(e => e.type === 'test');
|
||||
}
|
||||
entries() {
|
||||
return this._entries;
|
||||
}
|
||||
allTests() {
|
||||
const result = [];
|
||||
const visit = suite => {
|
||||
for (const entry of suite.entries()) {
|
||||
if (entry.type === 'test') result.push(entry);else visit(entry);
|
||||
}
|
||||
};
|
||||
visit(this);
|
||||
return result;
|
||||
}
|
||||
titlePath() {
|
||||
const titlePath = this.parent ? this.parent.titlePath() : [];
|
||||
// Ignore anonymous describe blocks.
|
||||
if (this.title || this._type !== 'describe') titlePath.push(this.title);
|
||||
return titlePath;
|
||||
}
|
||||
project() {
|
||||
var _this$_project, _this$parent;
|
||||
return (_this$_project = this._project) !== null && _this$_project !== void 0 ? _this$_project : (_this$parent = this.parent) === null || _this$parent === void 0 ? void 0 : _this$parent.project();
|
||||
}
|
||||
_addTest(test) {
|
||||
test.parent = this;
|
||||
this._entries.push(test);
|
||||
}
|
||||
_addSuite(suite) {
|
||||
suite.parent = this;
|
||||
this._entries.push(suite);
|
||||
}
|
||||
}
|
||||
exports.TeleSuite = TeleSuite;
|
||||
class TeleTestCase {
|
||||
constructor(id, title, location, repeatEachIndex) {
|
||||
this.title = void 0;
|
||||
this.fn = () => {};
|
||||
this.results = [];
|
||||
this.location = void 0;
|
||||
this.parent = void 0;
|
||||
this.type = 'test';
|
||||
this.expectedStatus = 'passed';
|
||||
this.timeout = 0;
|
||||
this.annotations = [];
|
||||
this.retries = 0;
|
||||
this.tags = [];
|
||||
this.repeatEachIndex = 0;
|
||||
this.id = void 0;
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.location = location;
|
||||
this.repeatEachIndex = repeatEachIndex;
|
||||
}
|
||||
titlePath() {
|
||||
const titlePath = this.parent ? this.parent.titlePath() : [];
|
||||
titlePath.push(this.title);
|
||||
return titlePath;
|
||||
}
|
||||
outcome() {
|
||||
return computeTestCaseOutcome(this);
|
||||
}
|
||||
ok() {
|
||||
const status = this.outcome();
|
||||
return status === 'expected' || status === 'flaky' || status === 'skipped';
|
||||
}
|
||||
_createTestResult(id) {
|
||||
const result = new TeleTestResult(this.results.length, id);
|
||||
this.results.push(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
exports.TeleTestCase = TeleTestCase;
|
||||
class TeleTestStep {
|
||||
constructor(payload, parentStep, location) {
|
||||
this.title = void 0;
|
||||
this.category = void 0;
|
||||
this.location = void 0;
|
||||
this.parent = void 0;
|
||||
this.duration = -1;
|
||||
this.steps = [];
|
||||
this._startTime = 0;
|
||||
this.title = payload.title;
|
||||
this.category = payload.category;
|
||||
this.location = location;
|
||||
this.parent = parentStep;
|
||||
this._startTime = payload.startTime;
|
||||
}
|
||||
titlePath() {
|
||||
var _this$parent2;
|
||||
const parentPath = ((_this$parent2 = this.parent) === null || _this$parent2 === void 0 ? void 0 : _this$parent2.titlePath()) || [];
|
||||
return [...parentPath, this.title];
|
||||
}
|
||||
get startTime() {
|
||||
return new Date(this._startTime);
|
||||
}
|
||||
set startTime(value) {
|
||||
this._startTime = +value;
|
||||
}
|
||||
}
|
||||
class TeleTestResult {
|
||||
constructor(retry, id) {
|
||||
this.retry = void 0;
|
||||
this.parallelIndex = -1;
|
||||
this.workerIndex = -1;
|
||||
this.duration = -1;
|
||||
this.stdout = [];
|
||||
this.stderr = [];
|
||||
this.attachments = [];
|
||||
this.status = 'skipped';
|
||||
this.steps = [];
|
||||
this.errors = [];
|
||||
this.error = void 0;
|
||||
this._stepMap = new Map();
|
||||
this._id = void 0;
|
||||
this._startTime = 0;
|
||||
this.retry = retry;
|
||||
this._id = id;
|
||||
}
|
||||
setStartTimeNumber(startTime) {
|
||||
this._startTime = startTime;
|
||||
}
|
||||
get startTime() {
|
||||
return new Date(this._startTime);
|
||||
}
|
||||
set startTime(value) {
|
||||
this._startTime = +value;
|
||||
}
|
||||
}
|
||||
exports.TeleTestResult = TeleTestResult;
|
||||
const baseFullConfig = exports.baseFullConfig = {
|
||||
forbidOnly: false,
|
||||
fullyParallel: false,
|
||||
globalSetup: null,
|
||||
globalTeardown: null,
|
||||
globalTimeout: 0,
|
||||
grep: /.*/,
|
||||
grepInvert: null,
|
||||
maxFailures: 0,
|
||||
metadata: {},
|
||||
preserveOutput: 'always',
|
||||
projects: [],
|
||||
reporter: [[process.env.CI ? 'dot' : 'list']],
|
||||
reportSlowTests: {
|
||||
max: 5,
|
||||
threshold: 15000
|
||||
},
|
||||
configFile: '',
|
||||
rootDir: '',
|
||||
quiet: false,
|
||||
shard: null,
|
||||
updateSnapshots: 'missing',
|
||||
version: '',
|
||||
workers: 0,
|
||||
webServer: null
|
||||
};
|
||||
function serializeRegexPatterns(patterns) {
|
||||
if (!Array.isArray(patterns)) patterns = [patterns];
|
||||
return patterns.map(s => {
|
||||
if (typeof s === 'string') return {
|
||||
s
|
||||
};
|
||||
return {
|
||||
r: {
|
||||
source: s.source,
|
||||
flags: s.flags
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
function parseRegexPatterns(patterns) {
|
||||
return patterns.map(p => {
|
||||
if (p.s !== undefined) return p.s;
|
||||
return new RegExp(p.r.source, p.r.flags);
|
||||
});
|
||||
}
|
||||
function computeTestCaseOutcome(test) {
|
||||
let skipped = 0;
|
||||
let didNotRun = 0;
|
||||
let expected = 0;
|
||||
let interrupted = 0;
|
||||
let unexpected = 0;
|
||||
for (const result of test.results) {
|
||||
if (result.status === 'interrupted') {
|
||||
++interrupted; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
} else if (result.status === 'skipped' && test.expectedStatus === 'skipped') {
|
||||
// Only tests "expected to be skipped" are skipped. These were specifically
|
||||
// marked with test.skip or test.fixme.
|
||||
++skipped;
|
||||
} else if (result.status === 'skipped') {
|
||||
// Tests that were expected to run, but were skipped are "did not run".
|
||||
// This happens when:
|
||||
// - testing finished early;
|
||||
// - test failure prevented other tests in the serial suite to run;
|
||||
// - probably more cases!
|
||||
++didNotRun; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
} else if (result.status === test.expectedStatus) {
|
||||
// Either passed and expected to pass, or failed and expected to fail.
|
||||
++expected;
|
||||
} else {
|
||||
++unexpected;
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that were "skipped as expected" are considered equal to "expected" below,
|
||||
// because that's the expected outcome.
|
||||
//
|
||||
// However, we specifically differentiate the case of "only skipped"
|
||||
// and show it as "skipped" in all reporters.
|
||||
//
|
||||
// More exotic cases like "failed on first run and skipped on retry" are flaky.
|
||||
if (expected === 0 && unexpected === 0) return 'skipped'; // all results were skipped or interrupted
|
||||
if (unexpected === 0) return 'expected'; // no failures, just expected+skipped
|
||||
if (expected === 0 && skipped === 0) return 'unexpected'; // only failures
|
||||
return 'flaky'; // expected+unexpected or skipped+unexpected
|
||||
}
|
||||
144
node_modules/playwright/lib/isomorphic/teleSuiteUpdater.js
generated
vendored
Normal file
144
node_modules/playwright/lib/isomorphic/teleSuiteUpdater.js
generated
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.TeleSuiteUpdater = void 0;
|
||||
var _teleReceiver = require("./teleReceiver");
|
||||
var _testTree = require("./testTree");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class TeleSuiteUpdater {
|
||||
constructor(options) {
|
||||
this.rootSuite = void 0;
|
||||
this.config = void 0;
|
||||
this.loadErrors = [];
|
||||
this.progress = {
|
||||
total: 0,
|
||||
passed: 0,
|
||||
failed: 0,
|
||||
skipped: 0
|
||||
};
|
||||
this._receiver = void 0;
|
||||
this._lastRunReceiver = void 0;
|
||||
this._lastRunTestCount = 0;
|
||||
this._options = void 0;
|
||||
this._testResultsSnapshot = void 0;
|
||||
this._receiver = new _teleReceiver.TeleReporterReceiver(this._createReporter(), {
|
||||
mergeProjects: true,
|
||||
mergeTestCases: true,
|
||||
resolvePath: (rootDir, relativePath) => rootDir + options.pathSeparator + relativePath,
|
||||
clearPreviousResultsWhenTestBegins: true
|
||||
});
|
||||
this._options = options;
|
||||
}
|
||||
_createReporter() {
|
||||
return {
|
||||
version: () => 'v2',
|
||||
onConfigure: c => {
|
||||
this.config = c;
|
||||
// TeleReportReceiver is merging everything into a single suite, so when we
|
||||
// run one test, we still get many tests via rootSuite.allTests().length.
|
||||
// To work around that, have a dedicated per-run receiver that will only have
|
||||
// suite for a single test run, and hence will have correct total.
|
||||
this._lastRunReceiver = new _teleReceiver.TeleReporterReceiver({
|
||||
version: () => 'v2',
|
||||
onBegin: suite => {
|
||||
this._lastRunTestCount = suite.allTests().length;
|
||||
this._lastRunReceiver = undefined;
|
||||
}
|
||||
}, {
|
||||
mergeProjects: true,
|
||||
mergeTestCases: false,
|
||||
resolvePath: (rootDir, relativePath) => rootDir + this._options.pathSeparator + relativePath
|
||||
});
|
||||
},
|
||||
onBegin: suite => {
|
||||
if (!this.rootSuite) this.rootSuite = suite;
|
||||
// As soon as new test tree is built add previous results, before calling onUpdate
|
||||
// to avoid flashing empty results in the UI.
|
||||
if (this._testResultsSnapshot) {
|
||||
for (const test of this.rootSuite.allTests()) {
|
||||
var _this$_testResultsSna;
|
||||
test.results = ((_this$_testResultsSna = this._testResultsSnapshot) === null || _this$_testResultsSna === void 0 ? void 0 : _this$_testResultsSna.get(test.id)) || test.results;
|
||||
}
|
||||
this._testResultsSnapshot = undefined;
|
||||
}
|
||||
this.progress.total = this._lastRunTestCount;
|
||||
this.progress.passed = 0;
|
||||
this.progress.failed = 0;
|
||||
this.progress.skipped = 0;
|
||||
this._options.onUpdate(true);
|
||||
},
|
||||
onEnd: () => {
|
||||
this._options.onUpdate(true);
|
||||
},
|
||||
onTestBegin: (test, testResult) => {
|
||||
testResult[_testTree.statusEx] = 'running';
|
||||
this._options.onUpdate();
|
||||
},
|
||||
onTestEnd: (test, testResult) => {
|
||||
if (test.outcome() === 'skipped') ++this.progress.skipped;else if (test.outcome() === 'unexpected') ++this.progress.failed;else ++this.progress.passed;
|
||||
testResult[_testTree.statusEx] = testResult.status;
|
||||
this._options.onUpdate();
|
||||
},
|
||||
onError: error => this._handleOnError(error),
|
||||
printsToStdio: () => false
|
||||
};
|
||||
}
|
||||
processGlobalReport(report) {
|
||||
const receiver = new _teleReceiver.TeleReporterReceiver({
|
||||
version: () => 'v2',
|
||||
onConfigure: c => {
|
||||
this.config = c;
|
||||
},
|
||||
onError: error => this._handleOnError(error)
|
||||
});
|
||||
for (const message of report) void receiver.dispatch(message);
|
||||
}
|
||||
processListReport(report) {
|
||||
var _this$rootSuite;
|
||||
// Save test results and reset all projects, the results will be restored after
|
||||
// new project structure is built.
|
||||
const tests = ((_this$rootSuite = this.rootSuite) === null || _this$rootSuite === void 0 ? void 0 : _this$rootSuite.allTests()) || [];
|
||||
this._testResultsSnapshot = new Map(tests.map(test => [test.id, test.results]));
|
||||
this._receiver.reset();
|
||||
for (const message of report) void this._receiver.dispatch(message);
|
||||
}
|
||||
processTestReportEvent(message) {
|
||||
var _this$_lastRunReceive, _this$_receiver$dispa;
|
||||
// The order of receiver dispatches matters here, we want to assign `lastRunTestCount`
|
||||
// before we use it.
|
||||
(_this$_lastRunReceive = this._lastRunReceiver) === null || _this$_lastRunReceive === void 0 || (_this$_lastRunReceive = _this$_lastRunReceive.dispatch(message)) === null || _this$_lastRunReceive === void 0 || _this$_lastRunReceive.catch(() => {});
|
||||
(_this$_receiver$dispa = this._receiver.dispatch(message)) === null || _this$_receiver$dispa === void 0 || _this$_receiver$dispa.catch(() => {});
|
||||
}
|
||||
_handleOnError(error) {
|
||||
var _this$_options$onErro, _this$_options;
|
||||
this.loadErrors.push(error);
|
||||
(_this$_options$onErro = (_this$_options = this._options).onError) === null || _this$_options$onErro === void 0 || _this$_options$onErro.call(_this$_options, error);
|
||||
this._options.onUpdate();
|
||||
}
|
||||
asModel() {
|
||||
return {
|
||||
rootSuite: this.rootSuite || new _teleReceiver.TeleSuite('', 'root'),
|
||||
config: this.config,
|
||||
loadErrors: this.loadErrors,
|
||||
progress: this.progress
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.TeleSuiteUpdater = TeleSuiteUpdater;
|
||||
210
node_modules/playwright/lib/isomorphic/testServerConnection.js
generated
vendored
Normal file
210
node_modules/playwright/lib/isomorphic/testServerConnection.js
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.WebSocketTestServerTransport = exports.TestServerConnection = void 0;
|
||||
var events = _interopRequireWildcard(require("./events"));
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// -- Reuse boundary -- Everything below this line is reused in the vscode extension.
|
||||
|
||||
class WebSocketTestServerTransport {
|
||||
constructor(url) {
|
||||
this._ws = void 0;
|
||||
this._ws = new WebSocket(url);
|
||||
}
|
||||
onmessage(listener) {
|
||||
this._ws.addEventListener('message', event => listener(event.data));
|
||||
}
|
||||
onopen(listener) {
|
||||
this._ws.addEventListener('open', listener);
|
||||
}
|
||||
onerror(listener) {
|
||||
this._ws.addEventListener('error', listener);
|
||||
}
|
||||
onclose(listener) {
|
||||
this._ws.addEventListener('close', listener);
|
||||
}
|
||||
send(data) {
|
||||
this._ws.send(data);
|
||||
}
|
||||
close() {
|
||||
this._ws.close();
|
||||
}
|
||||
}
|
||||
exports.WebSocketTestServerTransport = WebSocketTestServerTransport;
|
||||
class TestServerConnection {
|
||||
constructor(transport) {
|
||||
this.onClose = void 0;
|
||||
this.onReport = void 0;
|
||||
this.onStdio = void 0;
|
||||
this.onTestFilesChanged = void 0;
|
||||
this.onLoadTraceRequested = void 0;
|
||||
this._onCloseEmitter = new events.EventEmitter();
|
||||
this._onReportEmitter = new events.EventEmitter();
|
||||
this._onStdioEmitter = new events.EventEmitter();
|
||||
this._onTestFilesChangedEmitter = new events.EventEmitter();
|
||||
this._onLoadTraceRequestedEmitter = new events.EventEmitter();
|
||||
this._lastId = 0;
|
||||
this._transport = void 0;
|
||||
this._callbacks = new Map();
|
||||
this._connectedPromise = void 0;
|
||||
this._isClosed = false;
|
||||
this.onClose = this._onCloseEmitter.event;
|
||||
this.onReport = this._onReportEmitter.event;
|
||||
this.onStdio = this._onStdioEmitter.event;
|
||||
this.onTestFilesChanged = this._onTestFilesChangedEmitter.event;
|
||||
this.onLoadTraceRequested = this._onLoadTraceRequestedEmitter.event;
|
||||
this._transport = transport;
|
||||
this._transport.onmessage(data => {
|
||||
const message = JSON.parse(data);
|
||||
const {
|
||||
id,
|
||||
result,
|
||||
error,
|
||||
method,
|
||||
params
|
||||
} = message;
|
||||
if (id) {
|
||||
const callback = this._callbacks.get(id);
|
||||
if (!callback) return;
|
||||
this._callbacks.delete(id);
|
||||
if (error) callback.reject(new Error(error));else callback.resolve(result);
|
||||
} else {
|
||||
this._dispatchEvent(method, params);
|
||||
}
|
||||
});
|
||||
const pingInterval = setInterval(() => this._sendMessage('ping').catch(() => {}), 30000);
|
||||
this._connectedPromise = new Promise((f, r) => {
|
||||
this._transport.onopen(f);
|
||||
this._transport.onerror(r);
|
||||
});
|
||||
this._transport.onclose(() => {
|
||||
this._isClosed = true;
|
||||
this._onCloseEmitter.fire();
|
||||
clearInterval(pingInterval);
|
||||
});
|
||||
}
|
||||
isClosed() {
|
||||
return this._isClosed;
|
||||
}
|
||||
async _sendMessage(method, params) {
|
||||
const logForTest = globalThis.__logForTest;
|
||||
logForTest === null || logForTest === void 0 || logForTest({
|
||||
method,
|
||||
params
|
||||
});
|
||||
await this._connectedPromise;
|
||||
const id = ++this._lastId;
|
||||
const message = {
|
||||
id,
|
||||
method,
|
||||
params
|
||||
};
|
||||
this._transport.send(JSON.stringify(message));
|
||||
return new Promise((resolve, reject) => {
|
||||
this._callbacks.set(id, {
|
||||
resolve,
|
||||
reject
|
||||
});
|
||||
});
|
||||
}
|
||||
_sendMessageNoReply(method, params) {
|
||||
this._sendMessage(method, params).catch(() => {});
|
||||
}
|
||||
_dispatchEvent(method, params) {
|
||||
if (method === 'report') this._onReportEmitter.fire(params);else if (method === 'stdio') this._onStdioEmitter.fire(params);else if (method === 'testFilesChanged') this._onTestFilesChangedEmitter.fire(params);else if (method === 'loadTraceRequested') this._onLoadTraceRequestedEmitter.fire(params);
|
||||
}
|
||||
async initialize(params) {
|
||||
await this._sendMessage('initialize', params);
|
||||
}
|
||||
async ping(params) {
|
||||
await this._sendMessage('ping', params);
|
||||
}
|
||||
async pingNoReply(params) {
|
||||
this._sendMessageNoReply('ping', params);
|
||||
}
|
||||
async watch(params) {
|
||||
await this._sendMessage('watch', params);
|
||||
}
|
||||
watchNoReply(params) {
|
||||
this._sendMessageNoReply('watch', params);
|
||||
}
|
||||
async open(params) {
|
||||
await this._sendMessage('open', params);
|
||||
}
|
||||
openNoReply(params) {
|
||||
this._sendMessageNoReply('open', params);
|
||||
}
|
||||
async resizeTerminal(params) {
|
||||
await this._sendMessage('resizeTerminal', params);
|
||||
}
|
||||
resizeTerminalNoReply(params) {
|
||||
this._sendMessageNoReply('resizeTerminal', params);
|
||||
}
|
||||
async checkBrowsers(params) {
|
||||
return await this._sendMessage('checkBrowsers', params);
|
||||
}
|
||||
async installBrowsers(params) {
|
||||
await this._sendMessage('installBrowsers', params);
|
||||
}
|
||||
async runGlobalSetup(params) {
|
||||
return await this._sendMessage('runGlobalSetup', params);
|
||||
}
|
||||
async runGlobalTeardown(params) {
|
||||
return await this._sendMessage('runGlobalTeardown', params);
|
||||
}
|
||||
async startDevServer(params) {
|
||||
return await this._sendMessage('startDevServer', params);
|
||||
}
|
||||
async stopDevServer(params) {
|
||||
return await this._sendMessage('stopDevServer', params);
|
||||
}
|
||||
async clearCache(params) {
|
||||
return await this._sendMessage('clearCache', params);
|
||||
}
|
||||
async listFiles(params) {
|
||||
return await this._sendMessage('listFiles', params);
|
||||
}
|
||||
async listTests(params) {
|
||||
return await this._sendMessage('listTests', params);
|
||||
}
|
||||
async runTests(params) {
|
||||
return await this._sendMessage('runTests', params);
|
||||
}
|
||||
async findRelatedTestFiles(params) {
|
||||
return await this._sendMessage('findRelatedTestFiles', params);
|
||||
}
|
||||
async stopTests(params) {
|
||||
await this._sendMessage('stopTests', params);
|
||||
}
|
||||
stopTestsNoReply(params) {
|
||||
this._sendMessageNoReply('stopTests', params);
|
||||
}
|
||||
async closeGracefully(params) {
|
||||
await this._sendMessage('closeGracefully', params);
|
||||
}
|
||||
close() {
|
||||
try {
|
||||
this._transport.close();
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
exports.TestServerConnection = TestServerConnection;
|
||||
5
node_modules/playwright/lib/isomorphic/testServerInterface.js
generated
vendored
Normal file
5
node_modules/playwright/lib/isomorphic/testServerInterface.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
272
node_modules/playwright/lib/isomorphic/testTree.js
generated
vendored
Normal file
272
node_modules/playwright/lib/isomorphic/testTree.js
generated
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.TestTree = void 0;
|
||||
exports.collectTestIds = collectTestIds;
|
||||
exports.sortAndPropagateStatus = sortAndPropagateStatus;
|
||||
exports.statusEx = void 0;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// -- Reuse boundary -- Everything below this line is reused in the vscode extension.
|
||||
|
||||
class TestTree {
|
||||
constructor(rootFolder, rootSuite, loadErrors, projectFilters, pathSeparator) {
|
||||
this.rootItem = void 0;
|
||||
this._treeItemById = new Map();
|
||||
this._treeItemByTestId = new Map();
|
||||
this.pathSeparator = void 0;
|
||||
const filterProjects = projectFilters && [...projectFilters.values()].some(Boolean);
|
||||
this.pathSeparator = pathSeparator;
|
||||
this.rootItem = {
|
||||
kind: 'group',
|
||||
subKind: 'folder',
|
||||
id: rootFolder,
|
||||
title: '',
|
||||
location: {
|
||||
file: '',
|
||||
line: 0,
|
||||
column: 0
|
||||
},
|
||||
duration: 0,
|
||||
parent: undefined,
|
||||
children: [],
|
||||
status: 'none',
|
||||
hasLoadErrors: false
|
||||
};
|
||||
this._treeItemById.set(rootFolder, this.rootItem);
|
||||
const visitSuite = (project, parentSuite, parentGroup) => {
|
||||
for (const suite of parentSuite.suites) {
|
||||
const title = suite.title || '<anonymous>';
|
||||
let group = parentGroup.children.find(item => item.kind === 'group' && item.title === title);
|
||||
if (!group) {
|
||||
group = {
|
||||
kind: 'group',
|
||||
subKind: 'describe',
|
||||
id: 'suite:' + parentSuite.titlePath().join('\x1e') + '\x1e' + title,
|
||||
// account for anonymous suites
|
||||
title,
|
||||
location: suite.location,
|
||||
duration: 0,
|
||||
parent: parentGroup,
|
||||
children: [],
|
||||
status: 'none',
|
||||
hasLoadErrors: false
|
||||
};
|
||||
this._addChild(parentGroup, group);
|
||||
}
|
||||
visitSuite(project, suite, group);
|
||||
}
|
||||
for (const test of parentSuite.tests) {
|
||||
const title = test.title;
|
||||
let testCaseItem = parentGroup.children.find(t => t.kind !== 'group' && t.title === title);
|
||||
if (!testCaseItem) {
|
||||
testCaseItem = {
|
||||
kind: 'case',
|
||||
id: 'test:' + test.titlePath().join('\x1e'),
|
||||
title,
|
||||
parent: parentGroup,
|
||||
children: [],
|
||||
tests: [],
|
||||
location: test.location,
|
||||
duration: 0,
|
||||
status: 'none',
|
||||
project: undefined,
|
||||
test: undefined,
|
||||
tags: test.tags
|
||||
};
|
||||
this._addChild(parentGroup, testCaseItem);
|
||||
}
|
||||
const result = test.results[0];
|
||||
let status = 'none';
|
||||
if ((result === null || result === void 0 ? void 0 : result[statusEx]) === 'scheduled') status = 'scheduled';else if ((result === null || result === void 0 ? void 0 : result[statusEx]) === 'running') status = 'running';else if ((result === null || result === void 0 ? void 0 : result.status) === 'skipped') status = 'skipped';else if ((result === null || result === void 0 ? void 0 : result.status) === 'interrupted') status = 'none';else if (result && test.outcome() !== 'expected') status = 'failed';else if (result && test.outcome() === 'expected') status = 'passed';
|
||||
testCaseItem.tests.push(test);
|
||||
const testItem = {
|
||||
kind: 'test',
|
||||
id: test.id,
|
||||
title: project.name,
|
||||
location: test.location,
|
||||
test,
|
||||
parent: testCaseItem,
|
||||
children: [],
|
||||
status,
|
||||
duration: test.results.length ? Math.max(0, test.results[0].duration) : 0,
|
||||
project
|
||||
};
|
||||
this._addChild(testCaseItem, testItem);
|
||||
this._treeItemByTestId.set(test.id, testItem);
|
||||
testCaseItem.duration = testCaseItem.children.reduce((a, b) => a + b.duration, 0);
|
||||
}
|
||||
};
|
||||
for (const projectSuite of (rootSuite === null || rootSuite === void 0 ? void 0 : rootSuite.suites) || []) {
|
||||
if (filterProjects && !projectFilters.get(projectSuite.title)) continue;
|
||||
for (const fileSuite of projectSuite.suites) {
|
||||
const fileItem = this._fileItem(fileSuite.location.file.split(pathSeparator), true);
|
||||
visitSuite(projectSuite.project(), fileSuite, fileItem);
|
||||
}
|
||||
}
|
||||
for (const loadError of loadErrors) {
|
||||
if (!loadError.location) continue;
|
||||
const fileItem = this._fileItem(loadError.location.file.split(pathSeparator), true);
|
||||
fileItem.hasLoadErrors = true;
|
||||
}
|
||||
}
|
||||
_addChild(parent, child) {
|
||||
parent.children.push(child);
|
||||
child.parent = parent;
|
||||
this._treeItemById.set(child.id, child);
|
||||
}
|
||||
filterTree(filterText, statusFilters, runningTestIds) {
|
||||
const tokens = filterText.trim().toLowerCase().split(' ');
|
||||
const filtersStatuses = [...statusFilters.values()].some(Boolean);
|
||||
const filter = testCase => {
|
||||
const titleWithTags = [...testCase.tests[0].titlePath(), ...testCase.tests[0].tags].join(' ').toLowerCase();
|
||||
if (!tokens.every(token => titleWithTags.includes(token)) && !testCase.tests.some(t => runningTestIds === null || runningTestIds === void 0 ? void 0 : runningTestIds.has(t.id))) return false;
|
||||
testCase.children = testCase.children.filter(test => {
|
||||
return !filtersStatuses || (runningTestIds === null || runningTestIds === void 0 ? void 0 : runningTestIds.has(test.test.id)) || statusFilters.get(test.status);
|
||||
});
|
||||
testCase.tests = testCase.children.map(c => c.test);
|
||||
return !!testCase.children.length;
|
||||
};
|
||||
const visit = treeItem => {
|
||||
const newChildren = [];
|
||||
for (const child of treeItem.children) {
|
||||
if (child.kind === 'case') {
|
||||
if (filter(child)) newChildren.push(child);
|
||||
} else {
|
||||
visit(child);
|
||||
if (child.children.length || child.hasLoadErrors) newChildren.push(child);
|
||||
}
|
||||
}
|
||||
treeItem.children = newChildren;
|
||||
};
|
||||
visit(this.rootItem);
|
||||
}
|
||||
_fileItem(filePath, isFile) {
|
||||
if (filePath.length === 0) return this.rootItem;
|
||||
const fileName = filePath.join(this.pathSeparator);
|
||||
const existingFileItem = this._treeItemById.get(fileName);
|
||||
if (existingFileItem) return existingFileItem;
|
||||
const parentFileItem = this._fileItem(filePath.slice(0, filePath.length - 1), false);
|
||||
const fileItem = {
|
||||
kind: 'group',
|
||||
subKind: isFile ? 'file' : 'folder',
|
||||
id: fileName,
|
||||
title: filePath[filePath.length - 1],
|
||||
location: {
|
||||
file: fileName,
|
||||
line: 0,
|
||||
column: 0
|
||||
},
|
||||
duration: 0,
|
||||
parent: parentFileItem,
|
||||
children: [],
|
||||
status: 'none',
|
||||
hasLoadErrors: false
|
||||
};
|
||||
this._addChild(parentFileItem, fileItem);
|
||||
return fileItem;
|
||||
}
|
||||
sortAndPropagateStatus() {
|
||||
sortAndPropagateStatus(this.rootItem);
|
||||
}
|
||||
flattenForSingleProject() {
|
||||
const visit = treeItem => {
|
||||
if (treeItem.kind === 'case' && treeItem.children.length === 1) {
|
||||
treeItem.project = treeItem.children[0].project;
|
||||
treeItem.test = treeItem.children[0].test;
|
||||
treeItem.children = [];
|
||||
this._treeItemByTestId.set(treeItem.test.id, treeItem);
|
||||
} else {
|
||||
treeItem.children.forEach(visit);
|
||||
}
|
||||
};
|
||||
visit(this.rootItem);
|
||||
}
|
||||
shortenRoot() {
|
||||
let shortRoot = this.rootItem;
|
||||
while (shortRoot.children.length === 1 && shortRoot.children[0].kind === 'group' && shortRoot.children[0].subKind === 'folder') shortRoot = shortRoot.children[0];
|
||||
shortRoot.location = this.rootItem.location;
|
||||
this.rootItem = shortRoot;
|
||||
}
|
||||
testIds() {
|
||||
const result = new Set();
|
||||
const visit = treeItem => {
|
||||
if (treeItem.kind === 'case') treeItem.tests.forEach(t => result.add(t.id));
|
||||
treeItem.children.forEach(visit);
|
||||
};
|
||||
visit(this.rootItem);
|
||||
return result;
|
||||
}
|
||||
fileNames() {
|
||||
const result = new Set();
|
||||
const visit = treeItem => {
|
||||
if (treeItem.kind === 'group' && treeItem.subKind === 'file') result.add(treeItem.id);else treeItem.children.forEach(visit);
|
||||
};
|
||||
visit(this.rootItem);
|
||||
return [...result];
|
||||
}
|
||||
flatTreeItems() {
|
||||
const result = [];
|
||||
const visit = treeItem => {
|
||||
result.push(treeItem);
|
||||
treeItem.children.forEach(visit);
|
||||
};
|
||||
visit(this.rootItem);
|
||||
return result;
|
||||
}
|
||||
treeItemById(id) {
|
||||
return this._treeItemById.get(id);
|
||||
}
|
||||
collectTestIds(treeItem) {
|
||||
return treeItem ? collectTestIds(treeItem) : new Set();
|
||||
}
|
||||
}
|
||||
exports.TestTree = TestTree;
|
||||
function sortAndPropagateStatus(treeItem) {
|
||||
for (const child of treeItem.children) sortAndPropagateStatus(child);
|
||||
if (treeItem.kind === 'group') {
|
||||
treeItem.children.sort((a, b) => {
|
||||
const fc = a.location.file.localeCompare(b.location.file);
|
||||
return fc || a.location.line - b.location.line;
|
||||
});
|
||||
}
|
||||
let allPassed = treeItem.children.length > 0;
|
||||
let allSkipped = treeItem.children.length > 0;
|
||||
let hasFailed = false;
|
||||
let hasRunning = false;
|
||||
let hasScheduled = false;
|
||||
for (const child of treeItem.children) {
|
||||
allSkipped = allSkipped && child.status === 'skipped';
|
||||
allPassed = allPassed && (child.status === 'passed' || child.status === 'skipped');
|
||||
hasFailed = hasFailed || child.status === 'failed';
|
||||
hasRunning = hasRunning || child.status === 'running';
|
||||
hasScheduled = hasScheduled || child.status === 'scheduled';
|
||||
}
|
||||
if (hasRunning) treeItem.status = 'running';else if (hasScheduled) treeItem.status = 'scheduled';else if (hasFailed) treeItem.status = 'failed';else if (allSkipped) treeItem.status = 'skipped';else if (allPassed) treeItem.status = 'passed';
|
||||
}
|
||||
function collectTestIds(treeItem) {
|
||||
const testIds = new Set();
|
||||
const visit = treeItem => {
|
||||
var _treeItem$children;
|
||||
if (treeItem.kind === 'case') treeItem.tests.map(t => t.id).forEach(id => testIds.add(id));else if (treeItem.kind === 'test') testIds.add(treeItem.id);else (_treeItem$children = treeItem.children) === null || _treeItem$children === void 0 || _treeItem$children.forEach(visit);
|
||||
};
|
||||
visit(treeItem);
|
||||
return testIds;
|
||||
}
|
||||
const statusEx = exports.statusEx = Symbol('statusEx');
|
||||
58
node_modules/playwright/lib/loader/loaderMain.js
generated
vendored
Normal file
58
node_modules/playwright/lib/loader/loaderMain.js
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.create = exports.LoaderMain = void 0;
|
||||
var _configLoader = require("../common/configLoader");
|
||||
var _process = require("../common/process");
|
||||
var _testLoader = require("../common/testLoader");
|
||||
var _compilationCache = require("../transform/compilationCache");
|
||||
var _poolBuilder = require("../common/poolBuilder");
|
||||
var _esmLoaderHost = require("../common/esmLoaderHost");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class LoaderMain extends _process.ProcessRunner {
|
||||
constructor(serializedConfig) {
|
||||
super();
|
||||
this._serializedConfig = void 0;
|
||||
this._configPromise = void 0;
|
||||
this._poolBuilder = _poolBuilder.PoolBuilder.createForLoader();
|
||||
this._serializedConfig = serializedConfig;
|
||||
}
|
||||
_config() {
|
||||
if (!this._configPromise) this._configPromise = (0, _configLoader.deserializeConfig)(this._serializedConfig);
|
||||
return this._configPromise;
|
||||
}
|
||||
async loadTestFile(params) {
|
||||
const testErrors = [];
|
||||
const config = await this._config();
|
||||
const fileSuite = await (0, _testLoader.loadTestFile)(params.file, config.config.rootDir, testErrors);
|
||||
this._poolBuilder.buildPools(fileSuite);
|
||||
return {
|
||||
fileSuite: fileSuite._deepSerialize(),
|
||||
testErrors
|
||||
};
|
||||
}
|
||||
async getCompilationCacheFromLoader() {
|
||||
await (0, _esmLoaderHost.incorporateCompilationCache)();
|
||||
return (0, _compilationCache.serializeCompilationCache)();
|
||||
}
|
||||
}
|
||||
exports.LoaderMain = LoaderMain;
|
||||
const create = config => new LoaderMain(config);
|
||||
exports.create = create;
|
||||
350
node_modules/playwright/lib/matchers/expect.js
generated
vendored
Normal file
350
node_modules/playwright/lib/matchers/expect.js
generated
vendored
Normal file
@@ -0,0 +1,350 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.expect = void 0;
|
||||
exports.mergeExpects = mergeExpects;
|
||||
exports.printReceivedStringContainExpectedSubstring = exports.printReceivedStringContainExpectedResult = void 0;
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _matchers = require("./matchers");
|
||||
var _toMatchSnapshot = require("./toMatchSnapshot");
|
||||
var _globals = require("../common/globals");
|
||||
var _util = require("../util");
|
||||
var _expectBundle = require("../common/expectBundle");
|
||||
var _testInfo = require("../worker/testInfo");
|
||||
var _matcherHint = require("./matcherHint");
|
||||
var _toMatchAriaSnapshot = require("./toMatchAriaSnapshot");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// #region
|
||||
// Mirrored from https://github.com/facebook/jest/blob/f13abff8df9a0e1148baf3584bcde6d1b479edc7/packages/expect/src/print.ts
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
*
|
||||
* This source code is licensed under the MIT license found here
|
||||
* https://github.com/facebook/jest/blob/1547740bbc26400d69f4576bf35645163e942829/LICENSE
|
||||
*/
|
||||
|
||||
// Format substring but do not enclose in double quote marks.
|
||||
// The replacement is compatible with pretty-format package.
|
||||
const printSubstring = val => val.replace(/"|\\/g, '\\$&');
|
||||
const printReceivedStringContainExpectedSubstring = (received, start, length // not end
|
||||
) => (0, _expectBundle.RECEIVED_COLOR)('"' + printSubstring(received.slice(0, start)) + (0, _expectBundle.INVERTED_COLOR)(printSubstring(received.slice(start, start + length))) + printSubstring(received.slice(start + length)) + '"');
|
||||
exports.printReceivedStringContainExpectedSubstring = printReceivedStringContainExpectedSubstring;
|
||||
const printReceivedStringContainExpectedResult = (received, result) => result === null ? (0, _expectBundle.printReceived)(received) : printReceivedStringContainExpectedSubstring(received, result.index, result[0].length);
|
||||
|
||||
// #endregion
|
||||
exports.printReceivedStringContainExpectedResult = printReceivedStringContainExpectedResult;
|
||||
function createMatchers(actual, info, prefix) {
|
||||
return new Proxy((0, _expectBundle.expect)(actual), new ExpectMetaInfoProxyHandler(info, prefix));
|
||||
}
|
||||
const getCustomMatchersSymbol = Symbol('get custom matchers');
|
||||
function qualifiedMatcherName(qualifier, matcherName) {
|
||||
return qualifier.join(':') + '$' + matcherName;
|
||||
}
|
||||
function createExpect(info, prefix, customMatchers) {
|
||||
const expectInstance = new Proxy(_expectBundle.expect, {
|
||||
apply: function (target, thisArg, argumentsList) {
|
||||
const [actual, messageOrOptions] = argumentsList;
|
||||
const message = (0, _utils.isString)(messageOrOptions) ? messageOrOptions : (messageOrOptions === null || messageOrOptions === void 0 ? void 0 : messageOrOptions.message) || info.message;
|
||||
const newInfo = {
|
||||
...info,
|
||||
message
|
||||
};
|
||||
if (newInfo.poll) {
|
||||
if (typeof actual !== 'function') throw new Error('`expect.poll()` accepts only function as a first argument');
|
||||
newInfo.poll.generator = actual;
|
||||
}
|
||||
return createMatchers(actual, newInfo, prefix);
|
||||
},
|
||||
get: function (target, property) {
|
||||
if (property === 'configure') return configure;
|
||||
if (property === 'extend') {
|
||||
return matchers => {
|
||||
const qualifier = [...prefix, (0, _utils.createGuid)()];
|
||||
const wrappedMatchers = {};
|
||||
const extendedMatchers = {
|
||||
...customMatchers
|
||||
};
|
||||
for (const [name, matcher] of Object.entries(matchers)) {
|
||||
wrappedMatchers[name] = function (...args) {
|
||||
const {
|
||||
isNot,
|
||||
promise,
|
||||
utils
|
||||
} = this;
|
||||
const newThis = {
|
||||
isNot,
|
||||
promise,
|
||||
utils,
|
||||
timeout: currentExpectTimeout()
|
||||
};
|
||||
newThis.equals = throwUnsupportedExpectMatcherError;
|
||||
return matcher.call(newThis, ...args);
|
||||
};
|
||||
const key = qualifiedMatcherName(qualifier, name);
|
||||
wrappedMatchers[key] = wrappedMatchers[name];
|
||||
Object.defineProperty(wrappedMatchers[key], 'name', {
|
||||
value: name
|
||||
});
|
||||
extendedMatchers[name] = wrappedMatchers[key];
|
||||
}
|
||||
_expectBundle.expect.extend(wrappedMatchers);
|
||||
return createExpect(info, qualifier, extendedMatchers);
|
||||
};
|
||||
}
|
||||
if (property === 'soft') {
|
||||
return (actual, messageOrOptions) => {
|
||||
return configure({
|
||||
soft: true
|
||||
})(actual, messageOrOptions);
|
||||
};
|
||||
}
|
||||
if (property === getCustomMatchersSymbol) return customMatchers;
|
||||
if (property === 'poll') {
|
||||
return (actual, messageOrOptions) => {
|
||||
const poll = (0, _utils.isString)(messageOrOptions) ? {} : messageOrOptions || {};
|
||||
return configure({
|
||||
_poll: poll
|
||||
})(actual, messageOrOptions);
|
||||
};
|
||||
}
|
||||
return _expectBundle.expect[property];
|
||||
}
|
||||
});
|
||||
const configure = configuration => {
|
||||
const newInfo = {
|
||||
...info
|
||||
};
|
||||
if ('message' in configuration) newInfo.message = configuration.message;
|
||||
if ('timeout' in configuration) newInfo.timeout = configuration.timeout;
|
||||
if ('soft' in configuration) newInfo.isSoft = configuration.soft;
|
||||
if ('_poll' in configuration) {
|
||||
newInfo.poll = configuration._poll ? {
|
||||
...info.poll,
|
||||
generator: () => {}
|
||||
} : undefined;
|
||||
if (typeof configuration._poll === 'object') {
|
||||
var _configuration$_poll$, _configuration$_poll$2;
|
||||
newInfo.poll.timeout = (_configuration$_poll$ = configuration._poll.timeout) !== null && _configuration$_poll$ !== void 0 ? _configuration$_poll$ : newInfo.poll.timeout;
|
||||
newInfo.poll.intervals = (_configuration$_poll$2 = configuration._poll.intervals) !== null && _configuration$_poll$2 !== void 0 ? _configuration$_poll$2 : newInfo.poll.intervals;
|
||||
}
|
||||
}
|
||||
return createExpect(newInfo, prefix, customMatchers);
|
||||
};
|
||||
return expectInstance;
|
||||
}
|
||||
function throwUnsupportedExpectMatcherError() {
|
||||
throw new Error('It looks like you are using custom expect matchers that are not compatible with Playwright. See https://aka.ms/playwright/expect-compatibility');
|
||||
}
|
||||
_expectBundle.expect.setState({
|
||||
expand: false
|
||||
});
|
||||
const customAsyncMatchers = {
|
||||
toBeAttached: _matchers.toBeAttached,
|
||||
toBeChecked: _matchers.toBeChecked,
|
||||
toBeDisabled: _matchers.toBeDisabled,
|
||||
toBeEditable: _matchers.toBeEditable,
|
||||
toBeEmpty: _matchers.toBeEmpty,
|
||||
toBeEnabled: _matchers.toBeEnabled,
|
||||
toBeFocused: _matchers.toBeFocused,
|
||||
toBeHidden: _matchers.toBeHidden,
|
||||
toBeInViewport: _matchers.toBeInViewport,
|
||||
toBeOK: _matchers.toBeOK,
|
||||
toBeVisible: _matchers.toBeVisible,
|
||||
toContainText: _matchers.toContainText,
|
||||
toHaveAccessibleDescription: _matchers.toHaveAccessibleDescription,
|
||||
toHaveAccessibleName: _matchers.toHaveAccessibleName,
|
||||
toHaveAttribute: _matchers.toHaveAttribute,
|
||||
toHaveClass: _matchers.toHaveClass,
|
||||
toHaveCount: _matchers.toHaveCount,
|
||||
toHaveCSS: _matchers.toHaveCSS,
|
||||
toHaveId: _matchers.toHaveId,
|
||||
toHaveJSProperty: _matchers.toHaveJSProperty,
|
||||
toHaveRole: _matchers.toHaveRole,
|
||||
toHaveText: _matchers.toHaveText,
|
||||
toHaveTitle: _matchers.toHaveTitle,
|
||||
toHaveURL: _matchers.toHaveURL,
|
||||
toHaveValue: _matchers.toHaveValue,
|
||||
toHaveValues: _matchers.toHaveValues,
|
||||
toHaveScreenshot: _toMatchSnapshot.toHaveScreenshot,
|
||||
toMatchAriaSnapshot: _toMatchAriaSnapshot.toMatchAriaSnapshot,
|
||||
toPass: _matchers.toPass
|
||||
};
|
||||
const customMatchers = {
|
||||
...customAsyncMatchers,
|
||||
toMatchSnapshot: _toMatchSnapshot.toMatchSnapshot
|
||||
};
|
||||
class ExpectMetaInfoProxyHandler {
|
||||
constructor(info, prefix) {
|
||||
this._info = void 0;
|
||||
this._prefix = void 0;
|
||||
this._info = {
|
||||
...info
|
||||
};
|
||||
this._prefix = prefix;
|
||||
}
|
||||
get(target, matcherName, receiver) {
|
||||
let matcher = Reflect.get(target, matcherName, receiver);
|
||||
if (typeof matcherName !== 'string') return matcher;
|
||||
let resolvedMatcherName = matcherName;
|
||||
for (let i = this._prefix.length; i > 0; i--) {
|
||||
const qualifiedName = qualifiedMatcherName(this._prefix.slice(0, i), matcherName);
|
||||
if (Reflect.has(target, qualifiedName)) {
|
||||
matcher = Reflect.get(target, qualifiedName, receiver);
|
||||
resolvedMatcherName = qualifiedName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matcher === undefined) throw new Error(`expect: Property '${matcherName}' not found.`);
|
||||
if (typeof matcher !== 'function') {
|
||||
if (matcherName === 'not') this._info.isNot = !this._info.isNot;
|
||||
return new Proxy(matcher, this);
|
||||
}
|
||||
if (this._info.poll) {
|
||||
if (customAsyncMatchers[matcherName] || matcherName === 'resolves' || matcherName === 'rejects') throw new Error(`\`expect.poll()\` does not support "${matcherName}" matcher.`);
|
||||
matcher = (...args) => pollMatcher(resolvedMatcherName, this._info, this._prefix, ...args);
|
||||
}
|
||||
return (...args) => {
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
// We assume that the matcher will read the current expect timeout the first thing.
|
||||
setCurrentExpectConfigureTimeout(this._info.timeout);
|
||||
if (!testInfo) return matcher.call(target, ...args);
|
||||
const customMessage = this._info.message || '';
|
||||
const argsSuffix = computeArgsSuffix(matcherName, args);
|
||||
const defaultTitle = `expect${this._info.poll ? '.poll' : ''}${this._info.isSoft ? '.soft' : ''}${this._info.isNot ? '.not' : ''}.${matcherName}${argsSuffix}`;
|
||||
const title = customMessage || defaultTitle;
|
||||
|
||||
// This looks like it is unnecessary, but it isn't - we need to filter
|
||||
// out all the frames that belong to the test runner from caught runtime errors.
|
||||
const stackFrames = (0, _util.filteredStackTrace)((0, _utils.captureRawStack)());
|
||||
|
||||
// Enclose toPass in a step to maintain async stacks, toPass matcher is always async.
|
||||
const stepInfo = {
|
||||
category: 'expect',
|
||||
title: (0, _util.trimLongString)(title, 1024),
|
||||
params: args[0] ? {
|
||||
expected: args[0]
|
||||
} : undefined,
|
||||
infectParentStepsWithError: this._info.isSoft
|
||||
};
|
||||
const step = testInfo._addStep(stepInfo);
|
||||
const reportStepError = e => {
|
||||
const jestError = (0, _matcherHint.isJestError)(e) ? e : null;
|
||||
const error = jestError ? new _matcherHint.ExpectError(jestError, customMessage, stackFrames) : e;
|
||||
if (jestError !== null && jestError !== void 0 && jestError.matcherResult.suggestedRebaseline) {
|
||||
step.complete({
|
||||
suggestedRebaseline: jestError === null || jestError === void 0 ? void 0 : jestError.matcherResult.suggestedRebaseline
|
||||
});
|
||||
return;
|
||||
}
|
||||
step.complete({
|
||||
error
|
||||
});
|
||||
if (this._info.isSoft) testInfo._failWithError(error);else throw error;
|
||||
};
|
||||
const finalizer = () => {
|
||||
step.complete({});
|
||||
};
|
||||
try {
|
||||
const callback = () => matcher.call(target, ...args);
|
||||
// toPass and poll matchers can contain other steps, expects and API calls,
|
||||
// so they behave like a retriable step.
|
||||
const result = matcherName === 'toPass' || this._info.poll ? _utils.zones.run('stepZone', step, callback) : _utils.zones.run('expectZone', {
|
||||
title,
|
||||
stepId: step.stepId
|
||||
}, callback);
|
||||
if (result instanceof Promise) return result.then(finalizer).catch(reportStepError);
|
||||
finalizer();
|
||||
return result;
|
||||
} catch (e) {
|
||||
reportStepError(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
async function pollMatcher(qualifiedMatcherName, info, prefix, ...args) {
|
||||
var _poll$timeout, _poll$intervals;
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
const poll = info.poll;
|
||||
const timeout = (_poll$timeout = poll.timeout) !== null && _poll$timeout !== void 0 ? _poll$timeout : currentExpectTimeout();
|
||||
const {
|
||||
deadline,
|
||||
timeoutMessage
|
||||
} = testInfo ? testInfo._deadlineForMatcher(timeout) : _testInfo.TestInfoImpl._defaultDeadlineForMatcher(timeout);
|
||||
const result = await (0, _utils.pollAgainstDeadline)(async () => {
|
||||
if (testInfo && (0, _globals.currentTestInfo)() !== testInfo) return {
|
||||
continuePolling: false,
|
||||
result: undefined
|
||||
};
|
||||
const innerInfo = {
|
||||
...info,
|
||||
isSoft: false,
|
||||
// soft is outside of poll, not inside
|
||||
poll: undefined
|
||||
};
|
||||
const value = await poll.generator();
|
||||
try {
|
||||
let matchers = createMatchers(value, innerInfo, prefix);
|
||||
if (info.isNot) matchers = matchers.not;
|
||||
matchers[qualifiedMatcherName](...args);
|
||||
return {
|
||||
continuePolling: false,
|
||||
result: undefined
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
continuePolling: true,
|
||||
result: error
|
||||
};
|
||||
}
|
||||
}, deadline, (_poll$intervals = poll.intervals) !== null && _poll$intervals !== void 0 ? _poll$intervals : [100, 250, 500, 1000]);
|
||||
if (result.timedOut) {
|
||||
const message = result.result ? [result.result.message, '', `Call Log:`, `- ${timeoutMessage}`].join('\n') : timeoutMessage;
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
let currentExpectConfigureTimeout;
|
||||
function setCurrentExpectConfigureTimeout(timeout) {
|
||||
currentExpectConfigureTimeout = timeout;
|
||||
}
|
||||
function currentExpectTimeout() {
|
||||
var _testInfo$_projectInt;
|
||||
if (currentExpectConfigureTimeout !== undefined) return currentExpectConfigureTimeout;
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
let defaultExpectTimeout = testInfo === null || testInfo === void 0 || (_testInfo$_projectInt = testInfo._projectInternal) === null || _testInfo$_projectInt === void 0 || (_testInfo$_projectInt = _testInfo$_projectInt.expect) === null || _testInfo$_projectInt === void 0 ? void 0 : _testInfo$_projectInt.timeout;
|
||||
if (typeof defaultExpectTimeout === 'undefined') defaultExpectTimeout = 5000;
|
||||
return defaultExpectTimeout;
|
||||
}
|
||||
function computeArgsSuffix(matcherName, args) {
|
||||
let value = '';
|
||||
if (matcherName === 'toHaveScreenshot') value = (0, _toMatchSnapshot.toHaveScreenshotStepTitle)(...args);
|
||||
return value ? `(${value})` : '';
|
||||
}
|
||||
const expect = exports.expect = createExpect({}, [], {}).extend(customMatchers);
|
||||
function mergeExpects(...expects) {
|
||||
let merged = expect;
|
||||
for (const e of expects) {
|
||||
const internals = e[getCustomMatchersSymbol];
|
||||
if (!internals)
|
||||
// non-playwright expects mutate the global expect, so we don't need to do anything special
|
||||
continue;
|
||||
merged = merged.extend(internals);
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
50
node_modules/playwright/lib/matchers/matcherHint.js
generated
vendored
Normal file
50
node_modules/playwright/lib/matchers/matcherHint.js
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ExpectError = void 0;
|
||||
exports.isJestError = isJestError;
|
||||
exports.kNoElementsFoundError = void 0;
|
||||
exports.matcherHint = matcherHint;
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const kNoElementsFoundError = exports.kNoElementsFoundError = '<element(s) not found>';
|
||||
function matcherHint(state, locator, matcherName, expression, actual, matcherOptions, timeout) {
|
||||
let header = state.utils.matcherHint(matcherName, expression, actual, matcherOptions).replace(/ \/\/ deep equality/, '') + '\n\n';
|
||||
if (timeout) header = _utilsBundle.colors.red(`Timed out ${timeout}ms waiting for `) + header;
|
||||
if (locator) header += `Locator: ${String(locator)}\n`;
|
||||
return header;
|
||||
}
|
||||
class ExpectError extends Error {
|
||||
constructor(jestError, customMessage, stackFrames) {
|
||||
super('');
|
||||
// Copy to erase the JestMatcherError constructor name from the console.log(error).
|
||||
this.matcherResult = void 0;
|
||||
this.name = jestError.name;
|
||||
this.message = jestError.message;
|
||||
this.matcherResult = jestError.matcherResult;
|
||||
if (customMessage) this.message = customMessage + '\n\n' + this.message;
|
||||
this.stack = this.name + ': ' + this.message + '\n' + (0, _utils.stringifyStackFrames)(stackFrames).join('\n');
|
||||
}
|
||||
}
|
||||
exports.ExpectError = ExpectError;
|
||||
function isJestError(e) {
|
||||
return e instanceof Error && 'matcherResult' in e;
|
||||
}
|
||||
451
node_modules/playwright/lib/matchers/matchers.js
generated
vendored
Normal file
451
node_modules/playwright/lib/matchers/matchers.js
generated
vendored
Normal file
@@ -0,0 +1,451 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.toBeAttached = toBeAttached;
|
||||
exports.toBeChecked = toBeChecked;
|
||||
exports.toBeDisabled = toBeDisabled;
|
||||
exports.toBeEditable = toBeEditable;
|
||||
exports.toBeEmpty = toBeEmpty;
|
||||
exports.toBeEnabled = toBeEnabled;
|
||||
exports.toBeFocused = toBeFocused;
|
||||
exports.toBeHidden = toBeHidden;
|
||||
exports.toBeInViewport = toBeInViewport;
|
||||
exports.toBeOK = toBeOK;
|
||||
exports.toBeVisible = toBeVisible;
|
||||
exports.toContainText = toContainText;
|
||||
exports.toHaveAccessibleDescription = toHaveAccessibleDescription;
|
||||
exports.toHaveAccessibleName = toHaveAccessibleName;
|
||||
exports.toHaveAttribute = toHaveAttribute;
|
||||
exports.toHaveCSS = toHaveCSS;
|
||||
exports.toHaveClass = toHaveClass;
|
||||
exports.toHaveCount = toHaveCount;
|
||||
exports.toHaveId = toHaveId;
|
||||
exports.toHaveJSProperty = toHaveJSProperty;
|
||||
exports.toHaveRole = toHaveRole;
|
||||
exports.toHaveText = toHaveText;
|
||||
exports.toHaveTitle = toHaveTitle;
|
||||
exports.toHaveURL = toHaveURL;
|
||||
exports.toHaveValue = toHaveValue;
|
||||
exports.toHaveValues = toHaveValues;
|
||||
exports.toPass = toPass;
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _util = require("../util");
|
||||
var _toBeTruthy = require("./toBeTruthy");
|
||||
var _toEqual = require("./toEqual");
|
||||
var _toMatchText = require("./toMatchText");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _globals = require("../common/globals");
|
||||
var _testInfo = require("../worker/testInfo");
|
||||
var _config = require("../common/config");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function toBeAttached(locator, options) {
|
||||
const attached = !options || options.attached === undefined || options.attached;
|
||||
const expected = attached ? 'attached' : 'detached';
|
||||
const unexpected = attached ? 'detached' : 'attached';
|
||||
const arg = attached ? '' : '{ attached: false }';
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeAttached', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
||||
return await locator._expect(attached ? 'to.be.attached' : 'to.be.detached', {
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
function toBeChecked(locator, options) {
|
||||
const checked = !options || options.checked === undefined || options.checked;
|
||||
const expected = checked ? 'checked' : 'unchecked';
|
||||
const unexpected = checked ? 'unchecked' : 'checked';
|
||||
const arg = checked ? '' : '{ checked: false }';
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeChecked', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
||||
return await locator._expect(checked ? 'to.be.checked' : 'to.be.unchecked', {
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
function toBeDisabled(locator, options) {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeDisabled', locator, 'Locator', 'disabled', 'enabled', '', async (isNot, timeout) => {
|
||||
return await locator._expect('to.be.disabled', {
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
function toBeEditable(locator, options) {
|
||||
const editable = !options || options.editable === undefined || options.editable;
|
||||
const expected = editable ? 'editable' : 'readOnly';
|
||||
const unexpected = editable ? 'readOnly' : 'editable';
|
||||
const arg = editable ? '' : '{ editable: false }';
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeEditable', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
||||
return await locator._expect(editable ? 'to.be.editable' : 'to.be.readonly', {
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
function toBeEmpty(locator, options) {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeEmpty', locator, 'Locator', 'empty', 'notEmpty', '', async (isNot, timeout) => {
|
||||
return await locator._expect('to.be.empty', {
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
function toBeEnabled(locator, options) {
|
||||
const enabled = !options || options.enabled === undefined || options.enabled;
|
||||
const expected = enabled ? 'enabled' : 'disabled';
|
||||
const unexpected = enabled ? 'disabled' : 'enabled';
|
||||
const arg = enabled ? '' : '{ enabled: false }';
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeEnabled', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
||||
return await locator._expect(enabled ? 'to.be.enabled' : 'to.be.disabled', {
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
function toBeFocused(locator, options) {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeFocused', locator, 'Locator', 'focused', 'inactive', '', async (isNot, timeout) => {
|
||||
return await locator._expect('to.be.focused', {
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
function toBeHidden(locator, options) {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeHidden', locator, 'Locator', 'hidden', 'visible', '', async (isNot, timeout) => {
|
||||
return await locator._expect('to.be.hidden', {
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
function toBeVisible(locator, options) {
|
||||
const visible = !options || options.visible === undefined || options.visible;
|
||||
const expected = visible ? 'visible' : 'hidden';
|
||||
const unexpected = visible ? 'hidden' : 'visible';
|
||||
const arg = visible ? '' : '{ visible: false }';
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeVisible', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
||||
return await locator._expect(visible ? 'to.be.visible' : 'to.be.hidden', {
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
function toBeInViewport(locator, options) {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeInViewport', locator, 'Locator', 'in viewport', 'outside viewport', '', async (isNot, timeout) => {
|
||||
return await locator._expect('to.be.in.viewport', {
|
||||
isNot,
|
||||
expectedNumber: options === null || options === void 0 ? void 0 : options.ratio,
|
||||
timeout
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
function toContainText(locator, expected, options = {}) {
|
||||
if (Array.isArray(expected)) {
|
||||
return _toEqual.toEqual.call(this, 'toContainText', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)(expected, {
|
||||
matchSubstring: true,
|
||||
normalizeWhiteSpace: true,
|
||||
ignoreCase: options.ignoreCase
|
||||
});
|
||||
return await locator._expect('to.contain.text.array', {
|
||||
expectedText,
|
||||
isNot,
|
||||
useInnerText: options.useInnerText,
|
||||
timeout
|
||||
});
|
||||
}, expected, {
|
||||
...options,
|
||||
contains: true
|
||||
});
|
||||
} else {
|
||||
return _toMatchText.toMatchText.call(this, 'toContainText', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)([expected], {
|
||||
matchSubstring: true,
|
||||
normalizeWhiteSpace: true,
|
||||
ignoreCase: options.ignoreCase
|
||||
});
|
||||
return await locator._expect('to.have.text', {
|
||||
expectedText,
|
||||
isNot,
|
||||
useInnerText: options.useInnerText,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
}
|
||||
function toHaveAccessibleDescription(locator, expected, options) {
|
||||
return _toMatchText.toMatchText.call(this, 'toHaveAccessibleDescription', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)([expected], {
|
||||
ignoreCase: options === null || options === void 0 ? void 0 : options.ignoreCase,
|
||||
normalizeWhiteSpace: true
|
||||
});
|
||||
return await locator._expect('to.have.accessible.description', {
|
||||
expectedText,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
function toHaveAccessibleName(locator, expected, options) {
|
||||
return _toMatchText.toMatchText.call(this, 'toHaveAccessibleName', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)([expected], {
|
||||
ignoreCase: options === null || options === void 0 ? void 0 : options.ignoreCase,
|
||||
normalizeWhiteSpace: true
|
||||
});
|
||||
return await locator._expect('to.have.accessible.name', {
|
||||
expectedText,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
function toHaveAttribute(locator, name, expected, options) {
|
||||
if (!options) {
|
||||
// Update params for the case toHaveAttribute(name, options);
|
||||
if (typeof expected === 'object' && !(0, _utils.isRegExp)(expected)) {
|
||||
options = expected;
|
||||
expected = undefined;
|
||||
}
|
||||
}
|
||||
if (expected === undefined) {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toHaveAttribute', locator, 'Locator', 'have attribute', 'not have attribute', '', async (isNot, timeout) => {
|
||||
return await locator._expect('to.have.attribute', {
|
||||
expressionArg: name,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
return _toMatchText.toMatchText.call(this, 'toHaveAttribute', locator, 'Locator', async (isNot, timeout) => {
|
||||
var _options;
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)([expected], {
|
||||
ignoreCase: (_options = options) === null || _options === void 0 ? void 0 : _options.ignoreCase
|
||||
});
|
||||
return await locator._expect('to.have.attribute.value', {
|
||||
expressionArg: name,
|
||||
expectedText,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
function toHaveClass(locator, expected, options) {
|
||||
if (Array.isArray(expected)) {
|
||||
return _toEqual.toEqual.call(this, 'toHaveClass', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)(expected);
|
||||
return await locator._expect('to.have.class.array', {
|
||||
expectedText,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
} else {
|
||||
return _toMatchText.toMatchText.call(this, 'toHaveClass', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)([expected]);
|
||||
return await locator._expect('to.have.class', {
|
||||
expectedText,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
}
|
||||
function toHaveCount(locator, expected, options) {
|
||||
return _toEqual.toEqual.call(this, 'toHaveCount', locator, 'Locator', async (isNot, timeout) => {
|
||||
return await locator._expect('to.have.count', {
|
||||
expectedNumber: expected,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
function toHaveCSS(locator, name, expected, options) {
|
||||
return _toMatchText.toMatchText.call(this, 'toHaveCSS', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)([expected]);
|
||||
return await locator._expect('to.have.css', {
|
||||
expressionArg: name,
|
||||
expectedText,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
function toHaveId(locator, expected, options) {
|
||||
return _toMatchText.toMatchText.call(this, 'toHaveId', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)([expected]);
|
||||
return await locator._expect('to.have.id', {
|
||||
expectedText,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
function toHaveJSProperty(locator, name, expected, options) {
|
||||
return _toEqual.toEqual.call(this, 'toHaveJSProperty', locator, 'Locator', async (isNot, timeout) => {
|
||||
return await locator._expect('to.have.property', {
|
||||
expressionArg: name,
|
||||
expectedValue: expected,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
function toHaveRole(locator, expected, options) {
|
||||
if (!(0, _utils.isString)(expected)) throw new Error(`"role" argument in toHaveRole must be a string`);
|
||||
return _toMatchText.toMatchText.call(this, 'toHaveRole', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)([expected]);
|
||||
return await locator._expect('to.have.role', {
|
||||
expectedText,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
function toHaveText(locator, expected, options = {}) {
|
||||
if (Array.isArray(expected)) {
|
||||
return _toEqual.toEqual.call(this, 'toHaveText', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)(expected, {
|
||||
normalizeWhiteSpace: true,
|
||||
ignoreCase: options.ignoreCase
|
||||
});
|
||||
return await locator._expect('to.have.text.array', {
|
||||
expectedText,
|
||||
isNot,
|
||||
useInnerText: options === null || options === void 0 ? void 0 : options.useInnerText,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
} else {
|
||||
return _toMatchText.toMatchText.call(this, 'toHaveText', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)([expected], {
|
||||
normalizeWhiteSpace: true,
|
||||
ignoreCase: options.ignoreCase
|
||||
});
|
||||
return await locator._expect('to.have.text', {
|
||||
expectedText,
|
||||
isNot,
|
||||
useInnerText: options === null || options === void 0 ? void 0 : options.useInnerText,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
}
|
||||
function toHaveValue(locator, expected, options) {
|
||||
return _toMatchText.toMatchText.call(this, 'toHaveValue', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)([expected]);
|
||||
return await locator._expect('to.have.value', {
|
||||
expectedText,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
function toHaveValues(locator, expected, options) {
|
||||
return _toEqual.toEqual.call(this, 'toHaveValues', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)(expected);
|
||||
return await locator._expect('to.have.values', {
|
||||
expectedText,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
function toHaveTitle(page, expected, options = {}) {
|
||||
const locator = page.locator(':root');
|
||||
return _toMatchText.toMatchText.call(this, 'toHaveTitle', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)([expected], {
|
||||
normalizeWhiteSpace: true
|
||||
});
|
||||
return await locator._expect('to.have.title', {
|
||||
expectedText,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
function toHaveURL(page, expected, options) {
|
||||
const baseURL = page.context()._options.baseURL;
|
||||
expected = typeof expected === 'string' ? (0, _utils.constructURLBasedOnBaseURL)(baseURL, expected) : expected;
|
||||
const locator = page.locator(':root');
|
||||
return _toMatchText.toMatchText.call(this, 'toHaveURL', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)([expected], {
|
||||
ignoreCase: options === null || options === void 0 ? void 0 : options.ignoreCase
|
||||
});
|
||||
return await locator._expect('to.have.url', {
|
||||
expectedText,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
async function toBeOK(response) {
|
||||
const matcherName = 'toBeOK';
|
||||
(0, _util.expectTypes)(response, ['APIResponse'], matcherName);
|
||||
const contentType = response.headers()['content-type'];
|
||||
const isTextEncoding = contentType && (0, _utils.isTextualMimeType)(contentType);
|
||||
const [log, text] = this.isNot === response.ok() ? await Promise.all([response._fetchLog(), isTextEncoding ? response.text() : null]) : [];
|
||||
const message = () => this.utils.matcherHint(matcherName, undefined, '', {
|
||||
isNot: this.isNot
|
||||
}) + (0, _util.callLogText)(log) + (text === null ? '' : `\nResponse text:\n${_utilsBundle.colors.dim((text === null || text === void 0 ? void 0 : text.substring(0, 1000)) || '')}`);
|
||||
const pass = response.ok();
|
||||
return {
|
||||
message,
|
||||
pass
|
||||
};
|
||||
}
|
||||
async function toPass(callback, options = {}) {
|
||||
var _testInfo$_projectInt, _testInfo$_projectInt2;
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
const timeout = (0, _config.takeFirst)(options.timeout, testInfo === null || testInfo === void 0 || (_testInfo$_projectInt = testInfo._projectInternal.expect) === null || _testInfo$_projectInt === void 0 || (_testInfo$_projectInt = _testInfo$_projectInt.toPass) === null || _testInfo$_projectInt === void 0 ? void 0 : _testInfo$_projectInt.timeout, 0);
|
||||
const intervals = (0, _config.takeFirst)(options.intervals, testInfo === null || testInfo === void 0 || (_testInfo$_projectInt2 = testInfo._projectInternal.expect) === null || _testInfo$_projectInt2 === void 0 || (_testInfo$_projectInt2 = _testInfo$_projectInt2.toPass) === null || _testInfo$_projectInt2 === void 0 ? void 0 : _testInfo$_projectInt2.intervals, [100, 250, 500, 1000]);
|
||||
const {
|
||||
deadline,
|
||||
timeoutMessage
|
||||
} = testInfo ? testInfo._deadlineForMatcher(timeout) : _testInfo.TestInfoImpl._defaultDeadlineForMatcher(timeout);
|
||||
const result = await (0, _utils.pollAgainstDeadline)(async () => {
|
||||
if (testInfo && (0, _globals.currentTestInfo)() !== testInfo) return {
|
||||
continuePolling: false,
|
||||
result: undefined
|
||||
};
|
||||
try {
|
||||
await callback();
|
||||
return {
|
||||
continuePolling: !!this.isNot,
|
||||
result: undefined
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
continuePolling: !this.isNot,
|
||||
result: e
|
||||
};
|
||||
}
|
||||
}, deadline, intervals);
|
||||
if (result.timedOut) {
|
||||
const message = result.result ? [result.result.message, '', `Call Log:`, `- ${timeoutMessage}`].join('\n') : timeoutMessage;
|
||||
return {
|
||||
message: () => message,
|
||||
pass: !!this.isNot
|
||||
};
|
||||
}
|
||||
return {
|
||||
pass: !this.isNot,
|
||||
message: () => ''
|
||||
};
|
||||
}
|
||||
72
node_modules/playwright/lib/matchers/toBeTruthy.js
generated
vendored
Normal file
72
node_modules/playwright/lib/matchers/toBeTruthy.js
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.toBeTruthy = toBeTruthy;
|
||||
var _util = require("../util");
|
||||
var _matcherHint = require("./matcherHint");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
async function toBeTruthy(matcherName, receiver, receiverType, expected, unexpected, arg, query, options = {}) {
|
||||
var _options$timeout;
|
||||
(0, _util.expectTypes)(receiver, [receiverType], matcherName);
|
||||
const matcherOptions = {
|
||||
isNot: this.isNot,
|
||||
promise: this.promise
|
||||
};
|
||||
const timeout = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : this.timeout;
|
||||
const {
|
||||
matches: pass,
|
||||
log,
|
||||
timedOut,
|
||||
received
|
||||
} = await query(!!this.isNot, timeout);
|
||||
if (pass === !this.isNot) {
|
||||
return {
|
||||
name: matcherName,
|
||||
message: () => '',
|
||||
pass,
|
||||
expected
|
||||
};
|
||||
}
|
||||
const notFound = received === _matcherHint.kNoElementsFoundError ? received : undefined;
|
||||
const actual = pass ? expected : unexpected;
|
||||
let printedReceived;
|
||||
let printedExpected;
|
||||
if (pass) {
|
||||
printedExpected = `Expected: not ${expected}`;
|
||||
printedReceived = `Received: ${notFound ? _matcherHint.kNoElementsFoundError : expected}`;
|
||||
} else {
|
||||
printedExpected = `Expected: ${expected}`;
|
||||
printedReceived = `Received: ${notFound ? _matcherHint.kNoElementsFoundError : unexpected}`;
|
||||
}
|
||||
const message = () => {
|
||||
const header = (0, _matcherHint.matcherHint)(this, receiver, matcherName, 'locator', arg, matcherOptions, timedOut ? timeout : undefined);
|
||||
const logText = (0, _util.callLogText)(log);
|
||||
return `${header}${printedExpected}\n${printedReceived}${logText}`;
|
||||
};
|
||||
return {
|
||||
message,
|
||||
pass,
|
||||
actual,
|
||||
name: matcherName,
|
||||
expected,
|
||||
log,
|
||||
timeout: timedOut ? timeout : undefined
|
||||
};
|
||||
}
|
||||
85
node_modules/playwright/lib/matchers/toEqual.js
generated
vendored
Normal file
85
node_modules/playwright/lib/matchers/toEqual.js
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.toEqual = toEqual;
|
||||
var _util = require("../util");
|
||||
var _matcherHint = require("./matcherHint");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Omit colon and one or more spaces, so can call getLabelPrinter.
|
||||
const EXPECTED_LABEL = 'Expected';
|
||||
const RECEIVED_LABEL = 'Received';
|
||||
async function toEqual(matcherName, receiver, receiverType, query, expected, options = {}) {
|
||||
var _options$timeout;
|
||||
(0, _util.expectTypes)(receiver, [receiverType], matcherName);
|
||||
const matcherOptions = {
|
||||
comment: options.contains ? '' : 'deep equality',
|
||||
isNot: this.isNot,
|
||||
promise: this.promise
|
||||
};
|
||||
const timeout = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : this.timeout;
|
||||
const {
|
||||
matches: pass,
|
||||
received,
|
||||
log,
|
||||
timedOut
|
||||
} = await query(!!this.isNot, timeout);
|
||||
if (pass === !this.isNot) {
|
||||
return {
|
||||
name: matcherName,
|
||||
message: () => '',
|
||||
pass,
|
||||
expected
|
||||
};
|
||||
}
|
||||
let printedReceived;
|
||||
let printedExpected;
|
||||
let printedDiff;
|
||||
if (pass) {
|
||||
printedExpected = `Expected: not ${this.utils.printExpected(expected)}`;
|
||||
printedReceived = `Received: ${this.utils.printReceived(received)}`;
|
||||
} else if (Array.isArray(expected) && Array.isArray(received)) {
|
||||
const normalizedExpected = expected.map((exp, index) => {
|
||||
const rec = received[index];
|
||||
if ((0, _utils.isRegExp)(exp)) return exp.test(rec) ? rec : exp;
|
||||
return exp;
|
||||
});
|
||||
printedDiff = this.utils.printDiffOrStringify(normalizedExpected, received, EXPECTED_LABEL, RECEIVED_LABEL, false);
|
||||
} else {
|
||||
printedDiff = this.utils.printDiffOrStringify(expected, received, EXPECTED_LABEL, RECEIVED_LABEL, false);
|
||||
}
|
||||
const message = () => {
|
||||
const header = (0, _matcherHint.matcherHint)(this, receiver, matcherName, 'locator', undefined, matcherOptions, timedOut ? timeout : undefined);
|
||||
const details = printedDiff || `${printedExpected}\n${printedReceived}`;
|
||||
return `${header}${details}${(0, _util.callLogText)(log)}`;
|
||||
};
|
||||
// Passing the actual and expected objects so that a custom reporter
|
||||
// could access them, for example in order to display a custom visual diff,
|
||||
// or create a different error message
|
||||
return {
|
||||
actual: received,
|
||||
expected,
|
||||
message,
|
||||
name: matcherName,
|
||||
pass,
|
||||
log,
|
||||
timeout: timedOut ? timeout : undefined
|
||||
};
|
||||
}
|
||||
132
node_modules/playwright/lib/matchers/toMatchAriaSnapshot.js
generated
vendored
Normal file
132
node_modules/playwright/lib/matchers/toMatchAriaSnapshot.js
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.toMatchAriaSnapshot = toMatchAriaSnapshot;
|
||||
var _matcherHint = require("./matcherHint");
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _expectBundle = require("../common/expectBundle");
|
||||
var _util = require("../util");
|
||||
var _expect = require("./expect");
|
||||
var _globals = require("../common/globals");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
async function toMatchAriaSnapshot(receiver, expected, options = {}) {
|
||||
var _options$timeout;
|
||||
const matcherName = 'toMatchAriaSnapshot';
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
if (!testInfo) throw new Error(`toMatchAriaSnapshot() must be called during the test`);
|
||||
if (testInfo._projectInternal.ignoreSnapshots) return {
|
||||
pass: !this.isNot,
|
||||
message: () => '',
|
||||
name: 'toMatchAriaSnapshot',
|
||||
expected
|
||||
};
|
||||
const updateSnapshots = testInfo.config.updateSnapshots;
|
||||
const matcherOptions = {
|
||||
isNot: this.isNot,
|
||||
promise: this.promise
|
||||
};
|
||||
if (typeof expected !== 'string') {
|
||||
throw new Error([(0, _matcherHint.matcherHint)(this, receiver, matcherName, receiver, expected, matcherOptions), `${_utilsBundle.colors.bold('Matcher error')}: ${(0, _expectBundle.EXPECTED_COLOR)('expected')} value must be a string`, this.utils.printWithType('Expected', expected, this.utils.printExpected)].join('\n\n'));
|
||||
}
|
||||
const generateMissingBaseline = updateSnapshots === 'missing' && !expected;
|
||||
const generateNewBaseline = updateSnapshots === 'all' || generateMissingBaseline;
|
||||
if (generateMissingBaseline) {
|
||||
if (this.isNot) {
|
||||
const message = `Matchers using ".not" can't generate new baselines`;
|
||||
return {
|
||||
pass: this.isNot,
|
||||
message: () => message,
|
||||
name: 'toMatchAriaSnapshot'
|
||||
};
|
||||
} else {
|
||||
// When generating new baseline, run entire pipeline against impossible match.
|
||||
expected = `- none "Generating new baseline"`;
|
||||
}
|
||||
}
|
||||
const timeout = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : this.timeout;
|
||||
expected = unshift(expected);
|
||||
const {
|
||||
matches: pass,
|
||||
received,
|
||||
log,
|
||||
timedOut
|
||||
} = await receiver._expect('to.match.aria', {
|
||||
expectedValue: expected,
|
||||
isNot: this.isNot,
|
||||
timeout
|
||||
});
|
||||
const typedReceived = received;
|
||||
const messagePrefix = (0, _matcherHint.matcherHint)(this, receiver, matcherName, 'locator', undefined, matcherOptions, timedOut ? timeout : undefined);
|
||||
const notFound = typedReceived === _matcherHint.kNoElementsFoundError;
|
||||
if (notFound) {
|
||||
return {
|
||||
pass: this.isNot,
|
||||
message: () => messagePrefix + `Expected: ${this.utils.printExpected(expected)}\nReceived: ${(0, _expectBundle.EXPECTED_COLOR)('<element not found>')}` + (0, _util.callLogText)(log),
|
||||
name: 'toMatchAriaSnapshot',
|
||||
expected
|
||||
};
|
||||
}
|
||||
const receivedText = typedReceived.raw;
|
||||
const message = () => {
|
||||
if (pass) {
|
||||
if (notFound) return messagePrefix + `Expected: not ${this.utils.printExpected(expected)}\nReceived: ${receivedText}` + (0, _util.callLogText)(log);
|
||||
const printedReceived = (0, _expect.printReceivedStringContainExpectedSubstring)(receivedText, receivedText.indexOf(expected), expected.length);
|
||||
return messagePrefix + `Expected: not ${this.utils.printExpected(expected)}\nReceived: ${printedReceived}` + (0, _util.callLogText)(log);
|
||||
} else {
|
||||
const labelExpected = `Expected`;
|
||||
if (notFound) return messagePrefix + `${labelExpected}: ${this.utils.printExpected(expected)}\nReceived: ${receivedText}` + (0, _util.callLogText)(log);
|
||||
return messagePrefix + this.utils.printDiffOrStringify(expected, receivedText, labelExpected, 'Received', false) + (0, _util.callLogText)(log);
|
||||
}
|
||||
};
|
||||
if (!this.isNot && pass === this.isNot && generateNewBaseline) {
|
||||
// Only rebaseline failed snapshots.
|
||||
const suggestedRebaseline = `toMatchAriaSnapshot(\`\n${(0, _utils.escapeTemplateString)(indent(typedReceived.regex, '{indent} '))}\n{indent}\`)`;
|
||||
return {
|
||||
pass: this.isNot,
|
||||
message: () => '',
|
||||
name: 'toMatchAriaSnapshot',
|
||||
suggestedRebaseline
|
||||
};
|
||||
}
|
||||
return {
|
||||
name: matcherName,
|
||||
expected,
|
||||
message,
|
||||
pass,
|
||||
actual: received,
|
||||
log,
|
||||
timeout: timedOut ? timeout : undefined
|
||||
};
|
||||
}
|
||||
function unshift(snapshot) {
|
||||
const lines = snapshot.split('\n');
|
||||
let whitespacePrefixLength = 100;
|
||||
for (const line of lines) {
|
||||
if (!line.trim()) continue;
|
||||
const match = line.match(/^(\s*)/);
|
||||
if (match && match[1].length < whitespacePrefixLength) whitespacePrefixLength = match[1].length;
|
||||
break;
|
||||
}
|
||||
return lines.filter(t => t.trim()).map(line => line.substring(whitespacePrefixLength)).join('\n');
|
||||
}
|
||||
function indent(snapshot, indent) {
|
||||
return snapshot.split('\n').map(line => indent + line).join('\n');
|
||||
}
|
||||
394
node_modules/playwright/lib/matchers/toMatchSnapshot.js
generated
vendored
Normal file
394
node_modules/playwright/lib/matchers/toMatchSnapshot.js
generated
vendored
Normal file
@@ -0,0 +1,394 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.toHaveScreenshot = toHaveScreenshot;
|
||||
exports.toHaveScreenshotStepTitle = toHaveScreenshotStepTitle;
|
||||
exports.toMatchSnapshot = toMatchSnapshot;
|
||||
var _globals = require("../common/globals");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _util = require("../util");
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _matcherHint = require("./matcherHint");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const snapshotNamesSymbol = Symbol('snapshotNames');
|
||||
// Keep in sync with above (begin).
|
||||
const NonConfigProperties = ['clip', 'fullPage', 'mask', 'maskColor', 'omitBackground', 'timeout'];
|
||||
// Keep in sync with above (end).
|
||||
|
||||
class SnapshotHelper {
|
||||
constructor(testInfo, matcherName, locator, anonymousSnapshotExtension, configOptions, nameOrOptions, optOptions) {
|
||||
var _mime$getType;
|
||||
this.testInfo = void 0;
|
||||
this.attachmentBaseName = void 0;
|
||||
this.legacyExpectedPath = void 0;
|
||||
this.previousPath = void 0;
|
||||
this.expectedPath = void 0;
|
||||
this.actualPath = void 0;
|
||||
this.diffPath = void 0;
|
||||
this.mimeType = void 0;
|
||||
this.kind = void 0;
|
||||
this.updateSnapshots = void 0;
|
||||
this.comparator = void 0;
|
||||
this.options = void 0;
|
||||
this.matcherName = void 0;
|
||||
this.locator = void 0;
|
||||
let name;
|
||||
if (Array.isArray(nameOrOptions) || typeof nameOrOptions === 'string') {
|
||||
name = nameOrOptions;
|
||||
this.options = {
|
||||
...optOptions
|
||||
};
|
||||
} else {
|
||||
const {
|
||||
name: nameFromOptions,
|
||||
...options
|
||||
} = nameOrOptions;
|
||||
this.options = options;
|
||||
name = nameFromOptions;
|
||||
}
|
||||
let snapshotNames = testInfo[snapshotNamesSymbol];
|
||||
if (!testInfo[snapshotNamesSymbol]) {
|
||||
snapshotNames = {
|
||||
anonymousSnapshotIndex: 0,
|
||||
namedSnapshotIndex: {}
|
||||
};
|
||||
testInfo[snapshotNamesSymbol] = snapshotNames;
|
||||
}
|
||||
let expectedPathSegments;
|
||||
let outputBasePath;
|
||||
if (!name) {
|
||||
// Consider the use case below. We should save actual to different paths.
|
||||
// Therefore we auto-increment |anonymousSnapshotIndex|.
|
||||
//
|
||||
// expect.toMatchSnapshot('a.png')
|
||||
// // noop
|
||||
// expect.toMatchSnapshot('a.png')
|
||||
const fullTitleWithoutSpec = [...testInfo.titlePath.slice(1), ++snapshotNames.anonymousSnapshotIndex].join(' ');
|
||||
// Note: expected path must not ever change for backwards compatibility.
|
||||
expectedPathSegments = [(0, _utils.sanitizeForFilePath)((0, _util.trimLongString)(fullTitleWithoutSpec)) + '.' + anonymousSnapshotExtension];
|
||||
// Trim the output file paths more aggressively to avoid hitting Windows filesystem limits.
|
||||
const sanitizedName = (0, _utils.sanitizeForFilePath)((0, _util.trimLongString)(fullTitleWithoutSpec, _util.windowsFilesystemFriendlyLength)) + '.' + anonymousSnapshotExtension;
|
||||
outputBasePath = testInfo._getOutputPath(sanitizedName);
|
||||
this.attachmentBaseName = sanitizedName;
|
||||
} else {
|
||||
// We intentionally do not sanitize user-provided array of segments, assuming
|
||||
// it is a file system path. See https://github.com/microsoft/playwright/pull/9156.
|
||||
// Note: expected path must not ever change for backwards compatibility.
|
||||
expectedPathSegments = Array.isArray(name) ? name : [(0, _util.sanitizeFilePathBeforeExtension)(name)];
|
||||
const joinedName = Array.isArray(name) ? name.join(_path.default.sep) : (0, _util.sanitizeFilePathBeforeExtension)((0, _util.trimLongString)(name, _util.windowsFilesystemFriendlyLength));
|
||||
snapshotNames.namedSnapshotIndex[joinedName] = (snapshotNames.namedSnapshotIndex[joinedName] || 0) + 1;
|
||||
const index = snapshotNames.namedSnapshotIndex[joinedName];
|
||||
const sanitizedName = index > 1 ? (0, _util.addSuffixToFilePath)(joinedName, `-${index - 1}`) : joinedName;
|
||||
outputBasePath = testInfo._getOutputPath(sanitizedName);
|
||||
this.attachmentBaseName = sanitizedName;
|
||||
}
|
||||
this.expectedPath = testInfo.snapshotPath(...expectedPathSegments);
|
||||
this.legacyExpectedPath = (0, _util.addSuffixToFilePath)(outputBasePath, '-expected');
|
||||
this.previousPath = (0, _util.addSuffixToFilePath)(outputBasePath, '-previous');
|
||||
this.actualPath = (0, _util.addSuffixToFilePath)(outputBasePath, '-actual');
|
||||
this.diffPath = (0, _util.addSuffixToFilePath)(outputBasePath, '-diff');
|
||||
const filteredConfigOptions = {
|
||||
...configOptions
|
||||
};
|
||||
for (const prop of NonConfigProperties) delete filteredConfigOptions[prop];
|
||||
this.options = {
|
||||
...filteredConfigOptions,
|
||||
...this.options
|
||||
};
|
||||
|
||||
// While comparator is not a part of the public API, it is translated here.
|
||||
if (this.options._comparator) {
|
||||
this.options.comparator = this.options._comparator;
|
||||
delete this.options._comparator;
|
||||
}
|
||||
if (this.options.maxDiffPixels !== undefined && this.options.maxDiffPixels < 0) throw new Error('`maxDiffPixels` option value must be non-negative integer');
|
||||
if (this.options.maxDiffPixelRatio !== undefined && (this.options.maxDiffPixelRatio < 0 || this.options.maxDiffPixelRatio > 1)) throw new Error('`maxDiffPixelRatio` option value must be between 0 and 1');
|
||||
this.matcherName = matcherName;
|
||||
this.locator = locator;
|
||||
this.updateSnapshots = testInfo.config.updateSnapshots;
|
||||
this.mimeType = (_mime$getType = _utilsBundle.mime.getType(_path.default.basename(this.expectedPath))) !== null && _mime$getType !== void 0 ? _mime$getType : 'application/octet-string';
|
||||
this.comparator = (0, _utils.getComparator)(this.mimeType);
|
||||
this.testInfo = testInfo;
|
||||
this.kind = this.mimeType.startsWith('image/') ? 'Screenshot' : 'Snapshot';
|
||||
}
|
||||
createMatcherResult(message, pass, log) {
|
||||
const unfiltered = {
|
||||
name: this.matcherName,
|
||||
expected: this.expectedPath,
|
||||
actual: this.actualPath,
|
||||
diff: this.diffPath,
|
||||
pass,
|
||||
message: () => message,
|
||||
log
|
||||
};
|
||||
return Object.fromEntries(Object.entries(unfiltered).filter(([_, v]) => v !== undefined));
|
||||
}
|
||||
handleMissingNegated() {
|
||||
const isWriteMissingMode = this.updateSnapshots === 'all' || this.updateSnapshots === 'missing';
|
||||
const message = `A snapshot doesn't exist at ${this.expectedPath}${isWriteMissingMode ? ', matchers using ".not" won\'t write them automatically.' : '.'}`;
|
||||
// NOTE: 'isNot' matcher implies inversed value.
|
||||
return this.createMatcherResult(message, true);
|
||||
}
|
||||
handleDifferentNegated() {
|
||||
// NOTE: 'isNot' matcher implies inversed value.
|
||||
return this.createMatcherResult('', false);
|
||||
}
|
||||
handleMatchingNegated() {
|
||||
const message = [_utilsBundle.colors.red(`${this.kind} comparison failed:`), '', indent('Expected result should be different from the actual one.', ' ')].join('\n');
|
||||
// NOTE: 'isNot' matcher implies inversed value.
|
||||
return this.createMatcherResult(message, true);
|
||||
}
|
||||
handleMissing(actual) {
|
||||
const isWriteMissingMode = this.updateSnapshots === 'all' || this.updateSnapshots === 'missing';
|
||||
if (isWriteMissingMode) writeFileSync(this.expectedPath, actual);
|
||||
this.testInfo.attachments.push({
|
||||
name: (0, _util.addSuffixToFilePath)(this.attachmentBaseName, '-expected'),
|
||||
contentType: this.mimeType,
|
||||
path: this.expectedPath
|
||||
});
|
||||
writeFileSync(this.actualPath, actual);
|
||||
this.testInfo.attachments.push({
|
||||
name: (0, _util.addSuffixToFilePath)(this.attachmentBaseName, '-actual'),
|
||||
contentType: this.mimeType,
|
||||
path: this.actualPath
|
||||
});
|
||||
const message = `A snapshot doesn't exist at ${this.expectedPath}${isWriteMissingMode ? ', writing actual.' : '.'}`;
|
||||
if (this.updateSnapshots === 'all') {
|
||||
/* eslint-disable no-console */
|
||||
console.log(message);
|
||||
return this.createMatcherResult(message, true);
|
||||
}
|
||||
if (this.updateSnapshots === 'missing') {
|
||||
this.testInfo._hasNonRetriableError = true;
|
||||
this.testInfo._failWithError(new Error(message));
|
||||
return this.createMatcherResult('', true);
|
||||
}
|
||||
return this.createMatcherResult(message, false);
|
||||
}
|
||||
handleDifferent(actual, expected, previous, diff, header, diffError, log) {
|
||||
const output = [`${header}${indent(diffError, ' ')}`];
|
||||
if (expected !== undefined) {
|
||||
// Copy the expectation inside the `test-results/` folder for backwards compatibility,
|
||||
// so that one can upload `test-results/` directory and have all the data inside.
|
||||
writeFileSync(this.legacyExpectedPath, expected);
|
||||
this.testInfo.attachments.push({
|
||||
name: (0, _util.addSuffixToFilePath)(this.attachmentBaseName, '-expected'),
|
||||
contentType: this.mimeType,
|
||||
path: this.expectedPath
|
||||
});
|
||||
output.push(`\nExpected: ${_utilsBundle.colors.yellow(this.expectedPath)}`);
|
||||
}
|
||||
if (previous !== undefined) {
|
||||
writeFileSync(this.previousPath, previous);
|
||||
this.testInfo.attachments.push({
|
||||
name: (0, _util.addSuffixToFilePath)(this.attachmentBaseName, '-previous'),
|
||||
contentType: this.mimeType,
|
||||
path: this.previousPath
|
||||
});
|
||||
output.push(`Previous: ${_utilsBundle.colors.yellow(this.previousPath)}`);
|
||||
}
|
||||
if (actual !== undefined) {
|
||||
writeFileSync(this.actualPath, actual);
|
||||
this.testInfo.attachments.push({
|
||||
name: (0, _util.addSuffixToFilePath)(this.attachmentBaseName, '-actual'),
|
||||
contentType: this.mimeType,
|
||||
path: this.actualPath
|
||||
});
|
||||
output.push(`Received: ${_utilsBundle.colors.yellow(this.actualPath)}`);
|
||||
}
|
||||
if (diff !== undefined) {
|
||||
writeFileSync(this.diffPath, diff);
|
||||
this.testInfo.attachments.push({
|
||||
name: (0, _util.addSuffixToFilePath)(this.attachmentBaseName, '-diff'),
|
||||
contentType: this.mimeType,
|
||||
path: this.diffPath
|
||||
});
|
||||
output.push(` Diff: ${_utilsBundle.colors.yellow(this.diffPath)}`);
|
||||
}
|
||||
if (log !== null && log !== void 0 && log.length) output.push((0, _util.callLogText)(log));else output.push('');
|
||||
return this.createMatcherResult(output.join('\n'), false, log);
|
||||
}
|
||||
handleMatching() {
|
||||
return this.createMatcherResult('', true);
|
||||
}
|
||||
}
|
||||
function toMatchSnapshot(received, nameOrOptions = {}, optOptions = {}) {
|
||||
var _testInfo$_projectInt;
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
if (!testInfo) throw new Error(`toMatchSnapshot() must be called during the test`);
|
||||
if (received instanceof Promise) throw new Error('An unresolved Promise was passed to toMatchSnapshot(), make sure to resolve it by adding await to it.');
|
||||
if (testInfo._projectInternal.ignoreSnapshots) return {
|
||||
pass: !this.isNot,
|
||||
message: () => '',
|
||||
name: 'toMatchSnapshot',
|
||||
expected: nameOrOptions
|
||||
};
|
||||
const configOptions = ((_testInfo$_projectInt = testInfo._projectInternal.expect) === null || _testInfo$_projectInt === void 0 ? void 0 : _testInfo$_projectInt.toMatchSnapshot) || {};
|
||||
const helper = new SnapshotHelper(testInfo, 'toMatchSnapshot', undefined, determineFileExtension(received), configOptions, nameOrOptions, optOptions);
|
||||
if (this.isNot) {
|
||||
if (!_fs.default.existsSync(helper.expectedPath)) return helper.handleMissingNegated();
|
||||
const isDifferent = !!helper.comparator(received, _fs.default.readFileSync(helper.expectedPath), helper.options);
|
||||
return isDifferent ? helper.handleDifferentNegated() : helper.handleMatchingNegated();
|
||||
}
|
||||
if (!_fs.default.existsSync(helper.expectedPath)) return helper.handleMissing(received);
|
||||
const expected = _fs.default.readFileSync(helper.expectedPath);
|
||||
const result = helper.comparator(received, expected, helper.options);
|
||||
if (!result) return helper.handleMatching();
|
||||
if (helper.updateSnapshots === 'all') {
|
||||
writeFileSync(helper.expectedPath, received);
|
||||
/* eslint-disable no-console */
|
||||
console.log(helper.expectedPath + ' does not match, writing actual.');
|
||||
return helper.createMatcherResult(helper.expectedPath + ' running with --update-snapshots, writing actual.', true);
|
||||
}
|
||||
const receiver = (0, _utils.isString)(received) ? 'string' : 'Buffer';
|
||||
const header = (0, _matcherHint.matcherHint)(this, undefined, 'toMatchSnapshot', receiver, undefined, undefined);
|
||||
return helper.handleDifferent(received, expected, undefined, result.diff, header, result.errorMessage, undefined);
|
||||
}
|
||||
function toHaveScreenshotStepTitle(nameOrOptions = {}, optOptions = {}) {
|
||||
let name;
|
||||
if (typeof nameOrOptions === 'object' && !Array.isArray(nameOrOptions)) name = nameOrOptions.name;else name = nameOrOptions;
|
||||
return Array.isArray(name) ? name.join(_path.default.sep) : name || '';
|
||||
}
|
||||
async function toHaveScreenshot(pageOrLocator, nameOrOptions = {}, optOptions = {}) {
|
||||
var _testInfo$_projectInt2, _helper$options$timeo, _helper$options$anima, _helper$options$caret, _helper$options$scale;
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
if (!testInfo) throw new Error(`toHaveScreenshot() must be called during the test`);
|
||||
if (testInfo._projectInternal.ignoreSnapshots) return {
|
||||
pass: !this.isNot,
|
||||
message: () => '',
|
||||
name: 'toHaveScreenshot',
|
||||
expected: nameOrOptions
|
||||
};
|
||||
(0, _util.expectTypes)(pageOrLocator, ['Page', 'Locator'], 'toHaveScreenshot');
|
||||
const [page, locator] = pageOrLocator.constructor.name === 'Page' ? [pageOrLocator, undefined] : [pageOrLocator.page(), pageOrLocator];
|
||||
const configOptions = ((_testInfo$_projectInt2 = testInfo._projectInternal.expect) === null || _testInfo$_projectInt2 === void 0 ? void 0 : _testInfo$_projectInt2.toHaveScreenshot) || {};
|
||||
const helper = new SnapshotHelper(testInfo, 'toHaveScreenshot', locator, 'png', configOptions, nameOrOptions, optOptions);
|
||||
if (!helper.expectedPath.toLowerCase().endsWith('.png')) throw new Error(`Screenshot name "${_path.default.basename(helper.expectedPath)}" must have '.png' extension`);
|
||||
(0, _util.expectTypes)(pageOrLocator, ['Page', 'Locator'], 'toHaveScreenshot');
|
||||
const style = await loadScreenshotStyles(helper.options.stylePath);
|
||||
const timeout = (_helper$options$timeo = helper.options.timeout) !== null && _helper$options$timeo !== void 0 ? _helper$options$timeo : this.timeout;
|
||||
const expectScreenshotOptions = {
|
||||
locator,
|
||||
animations: (_helper$options$anima = helper.options.animations) !== null && _helper$options$anima !== void 0 ? _helper$options$anima : 'disabled',
|
||||
caret: (_helper$options$caret = helper.options.caret) !== null && _helper$options$caret !== void 0 ? _helper$options$caret : 'hide',
|
||||
clip: helper.options.clip,
|
||||
fullPage: helper.options.fullPage,
|
||||
mask: helper.options.mask,
|
||||
maskColor: helper.options.maskColor,
|
||||
omitBackground: helper.options.omitBackground,
|
||||
scale: (_helper$options$scale = helper.options.scale) !== null && _helper$options$scale !== void 0 ? _helper$options$scale : 'css',
|
||||
style,
|
||||
isNot: !!this.isNot,
|
||||
timeout,
|
||||
comparator: helper.options.comparator,
|
||||
maxDiffPixels: helper.options.maxDiffPixels,
|
||||
maxDiffPixelRatio: helper.options.maxDiffPixelRatio,
|
||||
threshold: helper.options.threshold
|
||||
};
|
||||
const hasSnapshot = _fs.default.existsSync(helper.expectedPath);
|
||||
if (this.isNot) {
|
||||
if (!hasSnapshot) return helper.handleMissingNegated();
|
||||
|
||||
// Having `errorMessage` means we timed out while waiting
|
||||
// for screenshots not to match, so screenshots
|
||||
// are actually the same in the end.
|
||||
expectScreenshotOptions.expected = await _fs.default.promises.readFile(helper.expectedPath);
|
||||
const isDifferent = !(await page._expectScreenshot(expectScreenshotOptions)).errorMessage;
|
||||
return isDifferent ? helper.handleDifferentNegated() : helper.handleMatchingNegated();
|
||||
}
|
||||
|
||||
// Fast path: there's no screenshot and we don't intend to update it.
|
||||
if (helper.updateSnapshots === 'none' && !hasSnapshot) return helper.createMatcherResult(`A snapshot doesn't exist at ${helper.expectedPath}.`, false);
|
||||
const receiver = locator ? 'locator' : 'page';
|
||||
if (!hasSnapshot) {
|
||||
// Regenerate a new screenshot by waiting until two screenshots are the same.
|
||||
const {
|
||||
actual,
|
||||
previous,
|
||||
diff,
|
||||
errorMessage,
|
||||
log,
|
||||
timedOut
|
||||
} = await page._expectScreenshot(expectScreenshotOptions);
|
||||
// We tried re-generating new snapshot but failed.
|
||||
// This can be due to e.g. spinning animation, so we want to show it as a diff.
|
||||
if (errorMessage) {
|
||||
const header = (0, _matcherHint.matcherHint)(this, locator, 'toHaveScreenshot', receiver, undefined, undefined, timedOut ? timeout : undefined);
|
||||
return helper.handleDifferent(actual, undefined, previous, diff, header, errorMessage, log);
|
||||
}
|
||||
|
||||
// We successfully generated new screenshot.
|
||||
return helper.handleMissing(actual);
|
||||
}
|
||||
|
||||
// General case:
|
||||
// - snapshot exists
|
||||
// - regular matcher (i.e. not a `.not`)
|
||||
// - perhaps an 'all' flag to update non-matching screenshots
|
||||
expectScreenshotOptions.expected = await _fs.default.promises.readFile(helper.expectedPath);
|
||||
const {
|
||||
actual,
|
||||
previous,
|
||||
diff,
|
||||
errorMessage,
|
||||
log,
|
||||
timedOut
|
||||
} = await page._expectScreenshot(expectScreenshotOptions);
|
||||
if (!errorMessage) return helper.handleMatching();
|
||||
if (helper.updateSnapshots === 'all') {
|
||||
writeFileSync(helper.expectedPath, actual);
|
||||
writeFileSync(helper.actualPath, actual);
|
||||
/* eslint-disable no-console */
|
||||
console.log(helper.expectedPath + ' is re-generated, writing actual.');
|
||||
return helper.createMatcherResult(helper.expectedPath + ' running with --update-snapshots, writing actual.', true);
|
||||
}
|
||||
const header = (0, _matcherHint.matcherHint)(this, undefined, 'toHaveScreenshot', receiver, undefined, undefined, timedOut ? timeout : undefined);
|
||||
return helper.handleDifferent(actual, expectScreenshotOptions.expected, previous, diff, header, errorMessage, log);
|
||||
}
|
||||
function writeFileSync(aPath, content) {
|
||||
_fs.default.mkdirSync(_path.default.dirname(aPath), {
|
||||
recursive: true
|
||||
});
|
||||
_fs.default.writeFileSync(aPath, content);
|
||||
}
|
||||
function indent(lines, tab) {
|
||||
return lines.replace(/^(?=.+$)/gm, tab);
|
||||
}
|
||||
function determineFileExtension(file) {
|
||||
if (typeof file === 'string') return 'txt';
|
||||
if (compareMagicBytes(file, [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])) return 'png';
|
||||
if (compareMagicBytes(file, [0xff, 0xd8, 0xff])) return 'jpg';
|
||||
return 'dat';
|
||||
}
|
||||
function compareMagicBytes(file, magicBytes) {
|
||||
return Buffer.compare(Buffer.from(magicBytes), file.slice(0, magicBytes.length)) === 0;
|
||||
}
|
||||
async function loadScreenshotStyles(stylePath) {
|
||||
if (!stylePath) return;
|
||||
const stylePaths = Array.isArray(stylePath) ? stylePath : [stylePath];
|
||||
const styles = await Promise.all(stylePaths.map(async stylePath => {
|
||||
const text = await _fs.default.promises.readFile(stylePath, 'utf8');
|
||||
return text.trim();
|
||||
}));
|
||||
return styles.join('\n').trim() || undefined;
|
||||
}
|
||||
103
node_modules/playwright/lib/matchers/toMatchText.js
generated
vendored
Normal file
103
node_modules/playwright/lib/matchers/toMatchText.js
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.toMatchText = toMatchText;
|
||||
var _util = require("../util");
|
||||
var _expect = require("./expect");
|
||||
var _expectBundle = require("../common/expectBundle");
|
||||
var _matcherHint = require("./matcherHint");
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
async function toMatchText(matcherName, receiver, receiverType, query, expected, options = {}) {
|
||||
var _options$timeout;
|
||||
(0, _util.expectTypes)(receiver, [receiverType], matcherName);
|
||||
const matcherOptions = {
|
||||
isNot: this.isNot,
|
||||
promise: this.promise
|
||||
};
|
||||
if (!(typeof expected === 'string') && !(expected && typeof expected.test === 'function')) {
|
||||
// Same format as jest's matcherErrorMessage
|
||||
throw new Error([(0, _matcherHint.matcherHint)(this, receiver, matcherName, receiver, expected, matcherOptions), `${_utilsBundle.colors.bold('Matcher error')}: ${(0, _expectBundle.EXPECTED_COLOR)('expected')} value must be a string or regular expression`, this.utils.printWithType('Expected', expected, this.utils.printExpected)].join('\n\n'));
|
||||
}
|
||||
const timeout = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : this.timeout;
|
||||
const {
|
||||
matches: pass,
|
||||
received,
|
||||
log,
|
||||
timedOut
|
||||
} = await query(!!this.isNot, timeout);
|
||||
if (pass === !this.isNot) {
|
||||
return {
|
||||
name: matcherName,
|
||||
message: () => '',
|
||||
pass,
|
||||
expected
|
||||
};
|
||||
}
|
||||
const stringSubstring = options.matchSubstring ? 'substring' : 'string';
|
||||
const receivedString = received || '';
|
||||
const messagePrefix = (0, _matcherHint.matcherHint)(this, receiver, matcherName, 'locator', undefined, matcherOptions, timedOut ? timeout : undefined);
|
||||
const notFound = received === _matcherHint.kNoElementsFoundError;
|
||||
let printedReceived;
|
||||
let printedExpected;
|
||||
let printedDiff;
|
||||
if (pass) {
|
||||
if (typeof expected === 'string') {
|
||||
if (notFound) {
|
||||
printedExpected = `Expected ${stringSubstring}: not ${this.utils.printExpected(expected)}`;
|
||||
printedReceived = `Received: ${received}`;
|
||||
} else {
|
||||
printedExpected = `Expected ${stringSubstring}: not ${this.utils.printExpected(expected)}`;
|
||||
const formattedReceived = (0, _expect.printReceivedStringContainExpectedSubstring)(receivedString, receivedString.indexOf(expected), expected.length);
|
||||
printedReceived = `Received string: ${formattedReceived}`;
|
||||
}
|
||||
} else {
|
||||
if (notFound) {
|
||||
printedExpected = `Expected pattern: not ${this.utils.printExpected(expected)}`;
|
||||
printedReceived = `Received: ${received}`;
|
||||
} else {
|
||||
printedExpected = `Expected pattern: not ${this.utils.printExpected(expected)}`;
|
||||
const formattedReceived = (0, _expect.printReceivedStringContainExpectedResult)(receivedString, typeof expected.exec === 'function' ? expected.exec(receivedString) : null);
|
||||
printedReceived = `Received string: ${formattedReceived}`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const labelExpected = `Expected ${typeof expected === 'string' ? stringSubstring : 'pattern'}`;
|
||||
if (notFound) {
|
||||
printedExpected = `${labelExpected}: ${this.utils.printExpected(expected)}`;
|
||||
printedReceived = `Received: ${received}`;
|
||||
} else {
|
||||
printedDiff = this.utils.printDiffOrStringify(expected, receivedString, labelExpected, 'Received string', false);
|
||||
}
|
||||
}
|
||||
const message = () => {
|
||||
const resultDetails = printedDiff ? printedDiff : printedExpected + '\n' + printedReceived;
|
||||
return messagePrefix + resultDetails + (0, _util.callLogText)(log);
|
||||
};
|
||||
return {
|
||||
name: matcherName,
|
||||
expected,
|
||||
message,
|
||||
pass,
|
||||
actual: received,
|
||||
log,
|
||||
timeout: timedOut ? timeout : undefined
|
||||
};
|
||||
}
|
||||
78
node_modules/playwright/lib/plugins/gitCommitInfoPlugin.js
generated
vendored
Normal file
78
node_modules/playwright/lib/plugins/gitCommitInfoPlugin.js
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.gitStatusFromCLI = exports.gitCommitInfo = void 0;
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const GIT_OPERATIONS_TIMEOUT_MS = 1500;
|
||||
const gitCommitInfo = options => {
|
||||
return {
|
||||
name: 'playwright:git-commit-info',
|
||||
setup: async (config, configDir) => {
|
||||
const info = {
|
||||
...linksFromEnv(),
|
||||
...(options !== null && options !== void 0 && options.info ? options.info : await gitStatusFromCLI((options === null || options === void 0 ? void 0 : options.directory) || configDir)),
|
||||
timestamp: Date.now()
|
||||
};
|
||||
// Normalize dates
|
||||
const timestamp = info['revision.timestamp'];
|
||||
if (timestamp instanceof Date) info['revision.timestamp'] = timestamp.getTime();
|
||||
config.metadata = config.metadata || {};
|
||||
Object.assign(config.metadata, info);
|
||||
}
|
||||
};
|
||||
};
|
||||
exports.gitCommitInfo = gitCommitInfo;
|
||||
const linksFromEnv = () => {
|
||||
const out = {};
|
||||
// Jenkins: https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables
|
||||
if (process.env.BUILD_URL) out['ci.link'] = process.env.BUILD_URL;
|
||||
// GitLab: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html
|
||||
if (process.env.CI_PROJECT_URL && process.env.CI_COMMIT_SHA) out['revision.link'] = `${process.env.CI_PROJECT_URL}/-/commit/${process.env.CI_COMMIT_SHA}`;
|
||||
if (process.env.CI_JOB_URL) out['ci.link'] = process.env.CI_JOB_URL;
|
||||
// GitHub: https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
|
||||
if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_SHA) out['revision.link'] = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/commit/${process.env.GITHUB_SHA}`;
|
||||
if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) out['ci.link'] = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
|
||||
return out;
|
||||
};
|
||||
const gitStatusFromCLI = async gitDir => {
|
||||
const separator = `:${(0, _utils.createGuid)().slice(0, 4)}:`;
|
||||
const {
|
||||
code,
|
||||
stdout
|
||||
} = await (0, _utils.spawnAsync)('git', ['show', '-s', `--format=%H${separator}%s${separator}%an${separator}%ae${separator}%ct`, 'HEAD'], {
|
||||
stdio: 'pipe',
|
||||
cwd: gitDir,
|
||||
timeout: GIT_OPERATIONS_TIMEOUT_MS
|
||||
});
|
||||
if (code) return;
|
||||
const showOutput = stdout.trim();
|
||||
const [id, subject, author, email, rawTimestamp] = showOutput.split(separator);
|
||||
let timestamp = Number.parseInt(rawTimestamp, 10);
|
||||
timestamp = Number.isInteger(timestamp) ? timestamp * 1000 : 0;
|
||||
return {
|
||||
'revision.id': id,
|
||||
'revision.author': author,
|
||||
'revision.email': email,
|
||||
'revision.subject': subject,
|
||||
'revision.timestamp': timestamp
|
||||
};
|
||||
};
|
||||
exports.gitStatusFromCLI = gitStatusFromCLI;
|
||||
19
node_modules/playwright/lib/plugins/index.js
generated
vendored
Normal file
19
node_modules/playwright/lib/plugins/index.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
Object.defineProperty(exports, "gitCommitInfo", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _gitCommitInfoPlugin.gitCommitInfo;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "webServer", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _webServerPlugin.webServer;
|
||||
}
|
||||
});
|
||||
var _webServerPlugin = require("./webServerPlugin");
|
||||
var _gitCommitInfoPlugin = require("./gitCommitInfoPlugin");
|
||||
187
node_modules/playwright/lib/plugins/webServerPlugin.js
generated
vendored
Normal file
187
node_modules/playwright/lib/plugins/webServerPlugin.js
generated
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.webServerPluginsForConfig = exports.webServer = exports.WebServerPlugin = void 0;
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _net = _interopRequireDefault(require("net"));
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const DEFAULT_ENVIRONMENT_VARIABLES = {
|
||||
'BROWSER': 'none',
|
||||
// Disable that create-react-app will open the page in the browser
|
||||
'FORCE_COLOR': '1',
|
||||
'DEBUG_COLORS': '1'
|
||||
};
|
||||
const debugWebServer = (0, _utilsBundle.debug)('pw:webserver');
|
||||
class WebServerPlugin {
|
||||
constructor(options, checkPortOnly) {
|
||||
this._isAvailableCallback = void 0;
|
||||
this._killProcess = void 0;
|
||||
this._processExitedPromise = void 0;
|
||||
this._options = void 0;
|
||||
this._checkPortOnly = void 0;
|
||||
this._reporter = void 0;
|
||||
this.name = 'playwright:webserver';
|
||||
this._options = options;
|
||||
this._checkPortOnly = checkPortOnly;
|
||||
}
|
||||
async setup(config, configDir, reporter) {
|
||||
var _this$_reporter$onStd;
|
||||
this._reporter = reporter;
|
||||
this._isAvailableCallback = this._options.url ? getIsAvailableFunction(this._options.url, this._checkPortOnly, !!this._options.ignoreHTTPSErrors, (_this$_reporter$onStd = this._reporter.onStdErr) === null || _this$_reporter$onStd === void 0 ? void 0 : _this$_reporter$onStd.bind(this._reporter)) : undefined;
|
||||
this._options.cwd = this._options.cwd ? _path.default.resolve(configDir, this._options.cwd) : configDir;
|
||||
try {
|
||||
await this._startProcess();
|
||||
await this._waitForProcess();
|
||||
} catch (error) {
|
||||
await this.teardown();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async teardown() {
|
||||
var _this$_killProcess;
|
||||
debugWebServer(`Terminating the WebServer`);
|
||||
await ((_this$_killProcess = this._killProcess) === null || _this$_killProcess === void 0 ? void 0 : _this$_killProcess.call(this));
|
||||
debugWebServer(`Terminated the WebServer`);
|
||||
}
|
||||
async _startProcess() {
|
||||
var _this$_isAvailableCal;
|
||||
let processExitedReject = error => {};
|
||||
this._processExitedPromise = new Promise((_, reject) => processExitedReject = reject);
|
||||
const isAlreadyAvailable = await ((_this$_isAvailableCal = this._isAvailableCallback) === null || _this$_isAvailableCal === void 0 ? void 0 : _this$_isAvailableCal.call(this));
|
||||
if (isAlreadyAvailable) {
|
||||
var _this$_options$url;
|
||||
debugWebServer(`WebServer is already available`);
|
||||
if (this._options.reuseExistingServer) return;
|
||||
const port = new URL(this._options.url).port;
|
||||
throw new Error(`${(_this$_options$url = this._options.url) !== null && _this$_options$url !== void 0 ? _this$_options$url : `http://localhost${port ? ':' + port : ''}`} is already used, make sure that nothing is running on the port/url or set reuseExistingServer:true in config.webServer.`);
|
||||
}
|
||||
debugWebServer(`Starting WebServer process ${this._options.command}...`);
|
||||
const {
|
||||
launchedProcess,
|
||||
kill
|
||||
} = await (0, _utils.launchProcess)({
|
||||
command: this._options.command,
|
||||
env: {
|
||||
...DEFAULT_ENVIRONMENT_VARIABLES,
|
||||
...process.env,
|
||||
...this._options.env
|
||||
},
|
||||
cwd: this._options.cwd,
|
||||
stdio: 'stdin',
|
||||
shell: true,
|
||||
// Reject to indicate that we cannot close the web server gracefully
|
||||
// and should fallback to non-graceful shutdown.
|
||||
attemptToGracefullyClose: () => Promise.reject(),
|
||||
log: () => {},
|
||||
onExit: code => processExitedReject(new Error(code ? `Process from config.webServer was not able to start. Exit code: ${code}` : 'Process from config.webServer exited early.')),
|
||||
tempDirectories: []
|
||||
});
|
||||
this._killProcess = kill;
|
||||
debugWebServer(`Process started`);
|
||||
launchedProcess.stderr.on('data', data => {
|
||||
var _onStdErr, _ref;
|
||||
if (debugWebServer.enabled || this._options.stderr === 'pipe' || !this._options.stderr) (_onStdErr = (_ref = this._reporter).onStdErr) === null || _onStdErr === void 0 || _onStdErr.call(_ref, prefixOutputLines(data.toString()));
|
||||
});
|
||||
launchedProcess.stdout.on('data', data => {
|
||||
var _onStdOut, _ref2;
|
||||
if (debugWebServer.enabled || this._options.stdout === 'pipe') (_onStdOut = (_ref2 = this._reporter).onStdOut) === null || _onStdOut === void 0 || _onStdOut.call(_ref2, prefixOutputLines(data.toString()));
|
||||
});
|
||||
}
|
||||
async _waitForProcess() {
|
||||
if (!this._isAvailableCallback) {
|
||||
this._processExitedPromise.catch(() => {});
|
||||
return;
|
||||
}
|
||||
debugWebServer(`Waiting for availability...`);
|
||||
const launchTimeout = this._options.timeout || 60 * 1000;
|
||||
const cancellationToken = {
|
||||
canceled: false
|
||||
};
|
||||
const {
|
||||
timedOut
|
||||
} = await Promise.race([(0, _utils.raceAgainstDeadline)(() => waitFor(this._isAvailableCallback, cancellationToken), (0, _utils.monotonicTime)() + launchTimeout), this._processExitedPromise]);
|
||||
cancellationToken.canceled = true;
|
||||
if (timedOut) throw new Error(`Timed out waiting ${launchTimeout}ms from config.webServer.`);
|
||||
debugWebServer(`WebServer available`);
|
||||
}
|
||||
}
|
||||
exports.WebServerPlugin = WebServerPlugin;
|
||||
async function isPortUsed(port) {
|
||||
const innerIsPortUsed = host => new Promise(resolve => {
|
||||
const conn = _net.default.connect(port, host).on('error', () => {
|
||||
resolve(false);
|
||||
}).on('connect', () => {
|
||||
conn.end();
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
return (await innerIsPortUsed('127.0.0.1')) || (await innerIsPortUsed('::1'));
|
||||
}
|
||||
async function waitFor(waitFn, cancellationToken) {
|
||||
const logScale = [100, 250, 500];
|
||||
while (!cancellationToken.canceled) {
|
||||
const connected = await waitFn();
|
||||
if (connected) return;
|
||||
const delay = logScale.shift() || 1000;
|
||||
debugWebServer(`Waiting ${delay}ms`);
|
||||
await new Promise(x => setTimeout(x, delay));
|
||||
}
|
||||
}
|
||||
function getIsAvailableFunction(url, checkPortOnly, ignoreHTTPSErrors, onStdErr) {
|
||||
const urlObject = new URL(url);
|
||||
if (!checkPortOnly) return () => (0, _utils.isURLAvailable)(urlObject, ignoreHTTPSErrors, debugWebServer, onStdErr);
|
||||
const port = urlObject.port;
|
||||
return () => isPortUsed(+port);
|
||||
}
|
||||
const webServer = options => {
|
||||
return new WebServerPlugin(options, false);
|
||||
};
|
||||
exports.webServer = webServer;
|
||||
const webServerPluginsForConfig = config => {
|
||||
const shouldSetBaseUrl = !!config.config.webServer;
|
||||
const webServerPlugins = [];
|
||||
for (const webServerConfig of config.webServers) {
|
||||
if (webServerConfig.port && webServerConfig.url) throw new Error(`Either 'port' or 'url' should be specified in config.webServer.`);
|
||||
let url;
|
||||
if (webServerConfig.port || webServerConfig.url) {
|
||||
url = webServerConfig.url || `http://localhost:${webServerConfig.port}`;
|
||||
|
||||
// We only set base url when only the port is given. That's a legacy mode we have regrets about.
|
||||
if (shouldSetBaseUrl && !webServerConfig.url) process.env.PLAYWRIGHT_TEST_BASE_URL = url;
|
||||
}
|
||||
webServerPlugins.push(new WebServerPlugin({
|
||||
...webServerConfig,
|
||||
url
|
||||
}, webServerConfig.port !== undefined));
|
||||
}
|
||||
return webServerPlugins;
|
||||
};
|
||||
exports.webServerPluginsForConfig = webServerPluginsForConfig;
|
||||
function prefixOutputLines(output) {
|
||||
const lastIsNewLine = output[output.length - 1] === '\n';
|
||||
let lines = output.split('\n');
|
||||
if (lastIsNewLine) lines.pop();
|
||||
lines = lines.map(line => _utilsBundle.colors.dim('[WebServer] ') + line);
|
||||
if (lastIsNewLine) lines.push('');
|
||||
return lines.join('\n');
|
||||
}
|
||||
339
node_modules/playwright/lib/program.js
generated
vendored
Normal file
339
node_modules/playwright/lib/program.js
generated
vendored
Normal file
@@ -0,0 +1,339 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
Object.defineProperty(exports, "program", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _program.program;
|
||||
}
|
||||
});
|
||||
exports.withRunnerAndMutedWrite = withRunnerAndMutedWrite;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _runner = require("./runner/runner");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _util = require("./util");
|
||||
var _html = require("./reporters/html");
|
||||
var _merge = require("./reporters/merge");
|
||||
var _configLoader = require("./common/configLoader");
|
||||
var _config = require("./common/config");
|
||||
var _program = require("playwright-core/lib/cli/program");
|
||||
var _base = require("./reporters/base");
|
||||
var testServer = _interopRequireWildcard(require("./runner/testServer"));
|
||||
var _watchMode = require("./runner/watchMode");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
function addTestCommand(program) {
|
||||
const command = program.command('test [test-filter...]');
|
||||
command.description('run tests with Playwright Test');
|
||||
const options = testOptions.sort((a, b) => a[0].replace(/-/g, '').localeCompare(b[0].replace(/-/g, '')));
|
||||
options.forEach(([name, description]) => command.option(name, description));
|
||||
command.action(async (args, opts) => {
|
||||
try {
|
||||
await runTests(args, opts);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
(0, _utils.gracefullyProcessExitDoNotHang)(1);
|
||||
}
|
||||
});
|
||||
command.addHelpText('afterAll', `
|
||||
Arguments [test-filter...]:
|
||||
Pass arguments to filter test files. Each argument is treated as a regular expression. Matching is performed against the absolute file paths.
|
||||
|
||||
Examples:
|
||||
$ npx playwright test my.spec.ts
|
||||
$ npx playwright test some.spec.ts:42
|
||||
$ npx playwright test --headed
|
||||
$ npx playwright test --project=webkit`);
|
||||
}
|
||||
function addListFilesCommand(program) {
|
||||
const command = program.command('list-files [file-filter...]', {
|
||||
hidden: true
|
||||
});
|
||||
command.description('List files with Playwright Test tests');
|
||||
command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
|
||||
command.option('--project <project-name...>', `Only run tests from the specified list of projects, supports '*' wildcard (default: list all projects)`);
|
||||
command.action(async (args, opts) => listTestFiles(opts));
|
||||
}
|
||||
function addClearCacheCommand(program) {
|
||||
const command = program.command('clear-cache');
|
||||
command.description('clears build and test caches');
|
||||
command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
|
||||
command.action(async opts => {
|
||||
const config = await (0, _configLoader.loadConfigFromFileRestartIfNeeded)(opts.config);
|
||||
if (!config) return;
|
||||
const runner = new _runner.Runner(config);
|
||||
const {
|
||||
status
|
||||
} = await runner.clearCache();
|
||||
const exitCode = status === 'interrupted' ? 130 : status === 'passed' ? 0 : 1;
|
||||
(0, _utils.gracefullyProcessExitDoNotHang)(exitCode);
|
||||
});
|
||||
}
|
||||
function addFindRelatedTestFilesCommand(program) {
|
||||
const command = program.command('find-related-test-files [source-files...]', {
|
||||
hidden: true
|
||||
});
|
||||
command.description('Returns the list of related tests to the given files');
|
||||
command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
|
||||
command.action(async (files, options) => {
|
||||
const resolvedFiles = files.map(file => _path.default.resolve(process.cwd(), file));
|
||||
await withRunnerAndMutedWrite(options.config, runner => runner.findRelatedTestFiles(resolvedFiles));
|
||||
});
|
||||
}
|
||||
function addDevServerCommand(program) {
|
||||
const command = program.command('dev-server', {
|
||||
hidden: true
|
||||
});
|
||||
command.description('start dev server');
|
||||
command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
|
||||
command.action(async options => {
|
||||
const config = await (0, _configLoader.loadConfigFromFileRestartIfNeeded)(options.config);
|
||||
if (!config) return;
|
||||
const runner = new _runner.Runner(config);
|
||||
const {
|
||||
status
|
||||
} = await runner.runDevServer();
|
||||
const exitCode = status === 'interrupted' ? 130 : status === 'passed' ? 0 : 1;
|
||||
(0, _utils.gracefullyProcessExitDoNotHang)(exitCode);
|
||||
});
|
||||
}
|
||||
function addTestServerCommand(program) {
|
||||
const command = program.command('test-server', {
|
||||
hidden: true
|
||||
});
|
||||
command.description('start test server');
|
||||
command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
|
||||
command.option('--host <host>', 'Host to start the server on', 'localhost');
|
||||
command.option('--port <port>', 'Port to start the server on', '0');
|
||||
command.action(opts => runTestServer(opts));
|
||||
}
|
||||
function addShowReportCommand(program) {
|
||||
const command = program.command('show-report [report]');
|
||||
command.description('show HTML report');
|
||||
command.action((report, options) => (0, _html.showHTMLReport)(report, options.host, +options.port));
|
||||
command.option('--host <host>', 'Host to serve report on', 'localhost');
|
||||
command.option('--port <port>', 'Port to serve report on', '9323');
|
||||
command.addHelpText('afterAll', `
|
||||
Arguments [report]:
|
||||
When specified, opens given report, otherwise opens last generated report.
|
||||
|
||||
Examples:
|
||||
$ npx playwright show-report
|
||||
$ npx playwright show-report playwright-report`);
|
||||
}
|
||||
function addMergeReportsCommand(program) {
|
||||
const command = program.command('merge-reports [dir]');
|
||||
command.description('merge multiple blob reports (for sharded tests) into a single report');
|
||||
command.action(async (dir, options) => {
|
||||
try {
|
||||
await mergeReports(dir, options);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
(0, _utils.gracefullyProcessExitDoNotHang)(1);
|
||||
}
|
||||
});
|
||||
command.option('-c, --config <file>', `Configuration file. Can be used to specify additional configuration for the output report.`);
|
||||
command.option('--reporter <reporter>', `Reporter to use, comma-separated, can be ${_config.builtInReporters.map(name => `"${name}"`).join(', ')} (default: "${_config.defaultReporter}")`);
|
||||
command.addHelpText('afterAll', `
|
||||
Arguments [dir]:
|
||||
Directory containing blob reports.
|
||||
|
||||
Examples:
|
||||
$ npx playwright merge-reports playwright-report`);
|
||||
}
|
||||
async function runTests(args, opts) {
|
||||
await (0, _utils.startProfiling)();
|
||||
const cliOverrides = overridesFromOptions(opts);
|
||||
if (opts.ui || opts.uiHost || opts.uiPort) {
|
||||
if (opts.onlyChanged) throw new Error(`--only-changed is not supported in UI mode. If you'd like that to change, see https://github.com/microsoft/playwright/issues/15075 for more details.`);
|
||||
const status = await testServer.runUIMode(opts.config, cliOverrides, {
|
||||
host: opts.uiHost,
|
||||
port: opts.uiPort ? +opts.uiPort : undefined,
|
||||
args,
|
||||
grep: opts.grep,
|
||||
grepInvert: opts.grepInvert,
|
||||
project: opts.project || undefined,
|
||||
reporter: Array.isArray(opts.reporter) ? opts.reporter : opts.reporter ? [opts.reporter] : undefined
|
||||
});
|
||||
await (0, _utils.stopProfiling)('runner');
|
||||
if (status === 'restarted') return;
|
||||
const exitCode = status === 'interrupted' ? 130 : status === 'passed' ? 0 : 1;
|
||||
(0, _utils.gracefullyProcessExitDoNotHang)(exitCode);
|
||||
return;
|
||||
}
|
||||
if (process.env.PWTEST_WATCH) {
|
||||
if (opts.onlyChanged) throw new Error(`--only-changed is not supported in watch mode. If you'd like that to change, file an issue and let us know about your usecase for it.`);
|
||||
const status = await (0, _watchMode.runWatchModeLoop)((0, _configLoader.resolveConfigLocation)(opts.config), {
|
||||
projects: opts.project,
|
||||
files: args,
|
||||
grep: opts.grep
|
||||
});
|
||||
await (0, _utils.stopProfiling)('runner');
|
||||
if (status === 'restarted') return;
|
||||
const exitCode = status === 'interrupted' ? 130 : status === 'passed' ? 0 : 1;
|
||||
(0, _utils.gracefullyProcessExitDoNotHang)(exitCode);
|
||||
return;
|
||||
}
|
||||
const config = await (0, _configLoader.loadConfigFromFileRestartIfNeeded)(opts.config, cliOverrides, opts.deps === false);
|
||||
if (!config) return;
|
||||
config.cliArgs = args;
|
||||
config.cliGrep = opts.grep;
|
||||
config.cliOnlyChanged = opts.onlyChanged === true ? 'HEAD' : opts.onlyChanged;
|
||||
config.cliGrepInvert = opts.grepInvert;
|
||||
config.cliListOnly = !!opts.list;
|
||||
config.cliProjectFilter = opts.project || undefined;
|
||||
config.cliPassWithNoTests = !!opts.passWithNoTests;
|
||||
config.cliFailOnFlakyTests = !!opts.failOnFlakyTests;
|
||||
config.cliLastFailed = !!opts.lastFailed;
|
||||
const runner = new _runner.Runner(config);
|
||||
const status = await runner.runAllTests();
|
||||
await (0, _utils.stopProfiling)('runner');
|
||||
const exitCode = status === 'interrupted' ? 130 : status === 'passed' ? 0 : 1;
|
||||
(0, _utils.gracefullyProcessExitDoNotHang)(exitCode);
|
||||
}
|
||||
async function runTestServer(opts) {
|
||||
const host = opts.host || 'localhost';
|
||||
const port = opts.port ? +opts.port : 0;
|
||||
const status = await testServer.runTestServer(opts.config, {}, {
|
||||
host,
|
||||
port
|
||||
});
|
||||
if (status === 'restarted') return;
|
||||
const exitCode = status === 'interrupted' ? 130 : status === 'passed' ? 0 : 1;
|
||||
(0, _utils.gracefullyProcessExitDoNotHang)(exitCode);
|
||||
}
|
||||
async function withRunnerAndMutedWrite(configFile, callback) {
|
||||
// Redefine process.stdout.write in case config decides to pollute stdio.
|
||||
const stdoutWrite = process.stdout.write.bind(process.stdout);
|
||||
process.stdout.write = (a, b, c) => process.stderr.write(a, b, c);
|
||||
try {
|
||||
const config = await (0, _configLoader.loadConfigFromFileRestartIfNeeded)(configFile);
|
||||
if (!config) return;
|
||||
const runner = new _runner.Runner(config);
|
||||
const result = await callback(runner);
|
||||
stdoutWrite(JSON.stringify(result, undefined, 2), () => {
|
||||
(0, _utils.gracefullyProcessExitDoNotHang)(0);
|
||||
});
|
||||
} catch (e) {
|
||||
const error = (0, _util.serializeError)(e);
|
||||
error.location = (0, _base.prepareErrorStack)(e.stack).location;
|
||||
stdoutWrite(JSON.stringify({
|
||||
error
|
||||
}, undefined, 2), () => {
|
||||
(0, _utils.gracefullyProcessExitDoNotHang)(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
async function listTestFiles(opts) {
|
||||
await withRunnerAndMutedWrite(opts.config, async runner => {
|
||||
return await runner.listTestFiles();
|
||||
});
|
||||
}
|
||||
async function mergeReports(reportDir, opts) {
|
||||
const configFile = opts.config;
|
||||
const config = configFile ? await (0, _configLoader.loadConfigFromFileRestartIfNeeded)(configFile) : await (0, _configLoader.loadEmptyConfigForMergeReports)();
|
||||
if (!config) return;
|
||||
const dir = _path.default.resolve(process.cwd(), reportDir || '');
|
||||
const dirStat = await _fs.default.promises.stat(dir).catch(e => null);
|
||||
if (!dirStat) throw new Error('Directory does not exist: ' + dir);
|
||||
if (!dirStat.isDirectory()) throw new Error(`"${dir}" is not a directory`);
|
||||
let reporterDescriptions = resolveReporterOption(opts.reporter);
|
||||
if (!reporterDescriptions && configFile) reporterDescriptions = config.config.reporter;
|
||||
if (!reporterDescriptions) reporterDescriptions = [[_config.defaultReporter]];
|
||||
const rootDirOverride = configFile ? config.config.rootDir : undefined;
|
||||
await (0, _merge.createMergedReport)(config, dir, reporterDescriptions, rootDirOverride);
|
||||
(0, _utils.gracefullyProcessExitDoNotHang)(0);
|
||||
}
|
||||
function overridesFromOptions(options) {
|
||||
const shardPair = options.shard ? options.shard.split('/').map(t => parseInt(t, 10)) : undefined;
|
||||
const overrides = {
|
||||
forbidOnly: options.forbidOnly ? true : undefined,
|
||||
fullyParallel: options.fullyParallel ? true : undefined,
|
||||
globalTimeout: options.globalTimeout ? parseInt(options.globalTimeout, 10) : undefined,
|
||||
maxFailures: options.x ? 1 : options.maxFailures ? parseInt(options.maxFailures, 10) : undefined,
|
||||
outputDir: options.output ? _path.default.resolve(process.cwd(), options.output) : undefined,
|
||||
quiet: options.quiet ? options.quiet : undefined,
|
||||
repeatEach: options.repeatEach ? parseInt(options.repeatEach, 10) : undefined,
|
||||
retries: options.retries ? parseInt(options.retries, 10) : undefined,
|
||||
reporter: resolveReporterOption(options.reporter),
|
||||
shard: shardPair ? {
|
||||
current: shardPair[0],
|
||||
total: shardPair[1]
|
||||
} : undefined,
|
||||
timeout: options.timeout ? parseInt(options.timeout, 10) : undefined,
|
||||
tsconfig: options.tsconfig ? _path.default.resolve(process.cwd(), options.tsconfig) : undefined,
|
||||
ignoreSnapshots: options.ignoreSnapshots ? !!options.ignoreSnapshots : undefined,
|
||||
updateSnapshots: options.updateSnapshots ? 'all' : undefined,
|
||||
workers: options.workers
|
||||
};
|
||||
if (options.browser) {
|
||||
const browserOpt = options.browser.toLowerCase();
|
||||
if (!['all', 'chromium', 'firefox', 'webkit'].includes(browserOpt)) throw new Error(`Unsupported browser "${options.browser}", must be one of "all", "chromium", "firefox" or "webkit"`);
|
||||
const browserNames = browserOpt === 'all' ? ['chromium', 'firefox', 'webkit'] : [browserOpt];
|
||||
overrides.projects = browserNames.map(browserName => {
|
||||
return {
|
||||
name: browserName,
|
||||
use: {
|
||||
browserName
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
if (options.headed || options.debug) overrides.use = {
|
||||
headless: false
|
||||
};
|
||||
if (!options.ui && options.debug) {
|
||||
overrides.debug = true;
|
||||
process.env.PWDEBUG = '1';
|
||||
}
|
||||
if (!options.ui && options.trace) {
|
||||
if (!kTraceModes.includes(options.trace)) throw new Error(`Unsupported trace mode "${options.trace}", must be one of ${kTraceModes.map(mode => `"${mode}"`).join(', ')}`);
|
||||
overrides.use = overrides.use || {};
|
||||
overrides.use.trace = options.trace;
|
||||
}
|
||||
return overrides;
|
||||
}
|
||||
function resolveReporterOption(reporter) {
|
||||
if (!reporter || !reporter.length) return undefined;
|
||||
return reporter.split(',').map(r => [resolveReporter(r)]);
|
||||
}
|
||||
function resolveReporter(id) {
|
||||
if (_config.builtInReporters.includes(id)) return id;
|
||||
const localPath = _path.default.resolve(process.cwd(), id);
|
||||
if (_fs.default.existsSync(localPath)) return localPath;
|
||||
return require.resolve(id, {
|
||||
paths: [process.cwd()]
|
||||
});
|
||||
}
|
||||
const kTraceModes = ['on', 'off', 'on-first-retry', 'on-all-retries', 'retain-on-failure', 'retain-on-first-failure'];
|
||||
const testOptions = [['--browser <browser>', `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")`], ['-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`], ['--debug', `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --max-failures=1 --headed --workers=1" options`], ['--fail-on-flaky-tests', `Fail if any test is flagged as flaky (default: false)`], ['--forbid-only', `Fail if test.only is called (default: false)`], ['--fully-parallel', `Run all tests in parallel (default: false)`], ['--global-timeout <timeout>', `Maximum time this test suite can run in milliseconds (default: unlimited)`], ['-g, --grep <grep>', `Only run tests matching this regular expression (default: ".*")`], ['-gv, --grep-invert <grep>', `Only run tests that do not match this regular expression`], ['--headed', `Run tests in headed browsers (default: headless)`], ['--ignore-snapshots', `Ignore screenshot and snapshot expectations`], ['--last-failed', `Only re-run the failures`], ['--list', `Collect all the tests and report them, but do not run`], ['--max-failures <N>', `Stop after the first N failures`], ['--no-deps', 'Do not run project dependencies'], ['--output <dir>', `Folder for output artifacts (default: "test-results")`], ['--only-changed [ref]', `Only run test files that have been changed between 'HEAD' and 'ref'. Defaults to running all uncommitted changes. Only supports Git.`], ['--pass-with-no-tests', `Makes test run succeed even if no tests were found`], ['--project <project-name...>', `Only run tests from the specified list of projects, supports '*' wildcard (default: run all projects)`], ['--quiet', `Suppress stdio`], ['--repeat-each <N>', `Run each test N times (default: 1)`], ['--reporter <reporter>', `Reporter to use, comma-separated, can be ${_config.builtInReporters.map(name => `"${name}"`).join(', ')} (default: "${_config.defaultReporter}")`], ['--retries <retries>', `Maximum retry count for flaky tests, zero for no retries (default: no retries)`], ['--shard <shard>', `Shard tests and execute only the selected shard, specify in the form "current/all", 1-based, for example "3/5"`], ['--timeout <timeout>', `Specify test timeout threshold in milliseconds, zero for unlimited (default: ${_config.defaultTimeout})`], ['--trace <mode>', `Force tracing mode, can be ${kTraceModes.map(mode => `"${mode}"`).join(', ')}`], ['--tsconfig <path>', `Path to a single tsconfig applicable to all imported files (default: look up tsconfig for each imported file separately)`], ['--ui', `Run tests in interactive UI mode`], ['--ui-host <host>', 'Host to serve UI on; specifying this option opens UI in a browser tab'], ['--ui-port <port>', 'Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab'], ['-u, --update-snapshots', `Update snapshots with actual results (default: only create missing snapshots)`], ['-j, --workers <workers>', `Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 50%)`], ['-x', `Stop after the first failure`]];
|
||||
addTestCommand(_program.program);
|
||||
addShowReportCommand(_program.program);
|
||||
addListFilesCommand(_program.program);
|
||||
addMergeReportsCommand(_program.program);
|
||||
addClearCacheCommand(_program.program);
|
||||
addFindRelatedTestFilesCommand(_program.program);
|
||||
addDevServerCommand(_program.program);
|
||||
addTestServerCommand(_program.program);
|
||||
549
node_modules/playwright/lib/reporters/base.js
generated
vendored
Normal file
549
node_modules/playwright/lib/reporters/base.js
generated
vendored
Normal file
@@ -0,0 +1,549 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.colors = exports.BaseReporter = void 0;
|
||||
exports.fitToWidth = fitToWidth;
|
||||
exports.formatError = formatError;
|
||||
exports.formatFailure = formatFailure;
|
||||
exports.formatResultFailure = formatResultFailure;
|
||||
exports.formatRetry = formatRetry;
|
||||
exports.formatTestHeader = formatTestHeader;
|
||||
exports.formatTestTitle = formatTestTitle;
|
||||
exports.kOutputSymbol = exports.isTTY = void 0;
|
||||
exports.prepareErrorStack = prepareErrorStack;
|
||||
exports.relativeFilePath = relativeFilePath;
|
||||
exports.resolveOutputFile = resolveOutputFile;
|
||||
exports.separator = separator;
|
||||
exports.stepSuffix = stepSuffix;
|
||||
exports.stripAnsiEscapes = stripAnsiEscapes;
|
||||
exports.ttyWidth = void 0;
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _utilsBundle2 = require("../utilsBundle");
|
||||
var _util = require("../util");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const kOutputSymbol = exports.kOutputSymbol = Symbol('output');
|
||||
const {
|
||||
isTTY,
|
||||
ttyWidth,
|
||||
colors
|
||||
} = (() => {
|
||||
let isTTY = !!process.stdout.isTTY;
|
||||
let ttyWidth = process.stdout.columns || 0;
|
||||
if (process.env.PLAYWRIGHT_FORCE_TTY === 'false' || process.env.PLAYWRIGHT_FORCE_TTY === '0') {
|
||||
isTTY = false;
|
||||
ttyWidth = 0;
|
||||
} else if (process.env.PLAYWRIGHT_FORCE_TTY === 'true' || process.env.PLAYWRIGHT_FORCE_TTY === '1') {
|
||||
isTTY = true;
|
||||
ttyWidth = process.stdout.columns || 100;
|
||||
} else if (process.env.PLAYWRIGHT_FORCE_TTY) {
|
||||
isTTY = true;
|
||||
ttyWidth = +process.env.PLAYWRIGHT_FORCE_TTY;
|
||||
if (isNaN(ttyWidth)) ttyWidth = 100;
|
||||
}
|
||||
let useColors = isTTY;
|
||||
if (process.env.DEBUG_COLORS === '0' || process.env.DEBUG_COLORS === 'false' || process.env.FORCE_COLOR === '0' || process.env.FORCE_COLOR === 'false') useColors = false;else if (process.env.DEBUG_COLORS || process.env.FORCE_COLOR) useColors = true;
|
||||
const colors = useColors ? _utilsBundle.colors : {
|
||||
bold: t => t,
|
||||
cyan: t => t,
|
||||
dim: t => t,
|
||||
gray: t => t,
|
||||
green: t => t,
|
||||
red: t => t,
|
||||
yellow: t => t,
|
||||
enabled: false
|
||||
};
|
||||
return {
|
||||
isTTY,
|
||||
ttyWidth,
|
||||
colors
|
||||
};
|
||||
})();
|
||||
exports.colors = colors;
|
||||
exports.ttyWidth = ttyWidth;
|
||||
exports.isTTY = isTTY;
|
||||
class BaseReporter {
|
||||
constructor(options = {}) {
|
||||
this.config = void 0;
|
||||
this.suite = void 0;
|
||||
this.totalTestCount = 0;
|
||||
this.result = void 0;
|
||||
this.fileDurations = new Map();
|
||||
this._omitFailures = void 0;
|
||||
this._fatalErrors = [];
|
||||
this._failureCount = 0;
|
||||
this._omitFailures = options.omitFailures || false;
|
||||
}
|
||||
version() {
|
||||
return 'v2';
|
||||
}
|
||||
onConfigure(config) {
|
||||
this.config = config;
|
||||
}
|
||||
onBegin(suite) {
|
||||
this.suite = suite;
|
||||
this.totalTestCount = suite.allTests().length;
|
||||
}
|
||||
onStdOut(chunk, test, result) {
|
||||
this._appendOutput({
|
||||
chunk,
|
||||
type: 'stdout'
|
||||
}, result);
|
||||
}
|
||||
onStdErr(chunk, test, result) {
|
||||
this._appendOutput({
|
||||
chunk,
|
||||
type: 'stderr'
|
||||
}, result);
|
||||
}
|
||||
_appendOutput(output, result) {
|
||||
if (!result) return;
|
||||
result[kOutputSymbol] = result[kOutputSymbol] || [];
|
||||
result[kOutputSymbol].push(output);
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
if (result.status !== 'skipped' && result.status !== test.expectedStatus) ++this._failureCount;
|
||||
const projectName = test.titlePath()[1];
|
||||
const relativePath = relativeTestPath(this.config, test);
|
||||
const fileAndProject = (projectName ? `[${projectName}] › ` : '') + relativePath;
|
||||
const entry = this.fileDurations.get(fileAndProject) || {
|
||||
duration: 0,
|
||||
workers: new Set()
|
||||
};
|
||||
entry.duration += result.duration;
|
||||
entry.workers.add(result.workerIndex);
|
||||
this.fileDurations.set(fileAndProject, entry);
|
||||
}
|
||||
onError(error) {
|
||||
this._fatalErrors.push(error);
|
||||
}
|
||||
async onEnd(result) {
|
||||
this.result = result;
|
||||
}
|
||||
fitToScreen(line, prefix) {
|
||||
if (!ttyWidth) {
|
||||
// Guard against the case where we cannot determine available width.
|
||||
return line;
|
||||
}
|
||||
return fitToWidth(line, ttyWidth, prefix);
|
||||
}
|
||||
generateStartingMessage() {
|
||||
var _this$config$metadata;
|
||||
const jobs = (_this$config$metadata = this.config.metadata.actualWorkers) !== null && _this$config$metadata !== void 0 ? _this$config$metadata : this.config.workers;
|
||||
const shardDetails = this.config.shard ? `, shard ${this.config.shard.current} of ${this.config.shard.total}` : '';
|
||||
if (!this.totalTestCount) return '';
|
||||
return '\n' + colors.dim('Running ') + this.totalTestCount + colors.dim(` test${this.totalTestCount !== 1 ? 's' : ''} using `) + jobs + colors.dim(` worker${jobs !== 1 ? 's' : ''}${shardDetails}`);
|
||||
}
|
||||
getSlowTests() {
|
||||
if (!this.config.reportSlowTests) return [];
|
||||
// Only pick durations that were served by single worker.
|
||||
const fileDurations = [...this.fileDurations.entries()].filter(([key, value]) => value.workers.size === 1).map(([key, value]) => [key, value.duration]);
|
||||
fileDurations.sort((a, b) => b[1] - a[1]);
|
||||
const count = Math.min(fileDurations.length, this.config.reportSlowTests.max || Number.POSITIVE_INFINITY);
|
||||
const threshold = this.config.reportSlowTests.threshold;
|
||||
return fileDurations.filter(([, duration]) => duration > threshold).slice(0, count);
|
||||
}
|
||||
generateSummaryMessage({
|
||||
didNotRun,
|
||||
skipped,
|
||||
expected,
|
||||
interrupted,
|
||||
unexpected,
|
||||
flaky,
|
||||
fatalErrors
|
||||
}) {
|
||||
const tokens = [];
|
||||
if (unexpected.length) {
|
||||
tokens.push(colors.red(` ${unexpected.length} failed`));
|
||||
for (const test of unexpected) tokens.push(colors.red(formatTestHeader(this.config, test, {
|
||||
indent: ' '
|
||||
})));
|
||||
}
|
||||
if (interrupted.length) {
|
||||
tokens.push(colors.yellow(` ${interrupted.length} interrupted`));
|
||||
for (const test of interrupted) tokens.push(colors.yellow(formatTestHeader(this.config, test, {
|
||||
indent: ' '
|
||||
})));
|
||||
}
|
||||
if (flaky.length) {
|
||||
tokens.push(colors.yellow(` ${flaky.length} flaky`));
|
||||
for (const test of flaky) tokens.push(colors.yellow(formatTestHeader(this.config, test, {
|
||||
indent: ' '
|
||||
})));
|
||||
}
|
||||
if (skipped) tokens.push(colors.yellow(` ${skipped} skipped`));
|
||||
if (didNotRun) tokens.push(colors.yellow(` ${didNotRun} did not run`));
|
||||
if (expected) tokens.push(colors.green(` ${expected} passed`) + colors.dim(` (${(0, _utilsBundle.ms)(this.result.duration)})`));
|
||||
if (fatalErrors.length && expected + unexpected.length + interrupted.length + flaky.length > 0) tokens.push(colors.red(` ${fatalErrors.length === 1 ? '1 error was not a part of any test' : fatalErrors.length + ' errors were not a part of any test'}, see above for details`));
|
||||
return tokens.join('\n');
|
||||
}
|
||||
generateSummary() {
|
||||
let didNotRun = 0;
|
||||
let skipped = 0;
|
||||
let expected = 0;
|
||||
const interrupted = [];
|
||||
const interruptedToPrint = [];
|
||||
const unexpected = [];
|
||||
const flaky = [];
|
||||
this.suite.allTests().forEach(test => {
|
||||
switch (test.outcome()) {
|
||||
case 'skipped':
|
||||
{
|
||||
if (test.results.some(result => result.status === 'interrupted')) {
|
||||
if (test.results.some(result => !!result.error)) interruptedToPrint.push(test);
|
||||
interrupted.push(test);
|
||||
} else if (!test.results.length || test.expectedStatus !== 'skipped') {
|
||||
++didNotRun;
|
||||
} else {
|
||||
++skipped;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'expected':
|
||||
++expected;
|
||||
break;
|
||||
case 'unexpected':
|
||||
unexpected.push(test);
|
||||
break;
|
||||
case 'flaky':
|
||||
flaky.push(test);
|
||||
break;
|
||||
}
|
||||
});
|
||||
const failuresToPrint = [...unexpected, ...flaky, ...interruptedToPrint];
|
||||
return {
|
||||
didNotRun,
|
||||
skipped,
|
||||
expected,
|
||||
interrupted,
|
||||
unexpected,
|
||||
flaky,
|
||||
failuresToPrint,
|
||||
fatalErrors: this._fatalErrors
|
||||
};
|
||||
}
|
||||
epilogue(full) {
|
||||
const summary = this.generateSummary();
|
||||
const summaryMessage = this.generateSummaryMessage(summary);
|
||||
if (full && summary.failuresToPrint.length && !this._omitFailures) this._printFailures(summary.failuresToPrint);
|
||||
this._printSlowTests();
|
||||
this._printSummary(summaryMessage);
|
||||
}
|
||||
_printFailures(failures) {
|
||||
console.log('');
|
||||
failures.forEach((test, index) => {
|
||||
console.log(formatFailure(this.config, test, index + 1));
|
||||
});
|
||||
}
|
||||
_printSlowTests() {
|
||||
const slowTests = this.getSlowTests();
|
||||
slowTests.forEach(([file, duration]) => {
|
||||
console.log(colors.yellow(' Slow test file: ') + file + colors.yellow(` (${(0, _utilsBundle.ms)(duration)})`));
|
||||
});
|
||||
if (slowTests.length) console.log(colors.yellow(' Consider splitting slow test files to speed up parallel execution'));
|
||||
}
|
||||
_printSummary(summary) {
|
||||
if (summary.trim()) console.log(summary);
|
||||
}
|
||||
willRetry(test) {
|
||||
return test.outcome() === 'unexpected' && test.results.length <= test.retries;
|
||||
}
|
||||
}
|
||||
exports.BaseReporter = BaseReporter;
|
||||
function formatFailure(config, test, index) {
|
||||
const lines = [];
|
||||
const header = formatTestHeader(config, test, {
|
||||
indent: ' ',
|
||||
index,
|
||||
mode: 'error'
|
||||
});
|
||||
lines.push(colors.red(header));
|
||||
for (const result of test.results) {
|
||||
const resultLines = [];
|
||||
const errors = formatResultFailure(test, result, ' ', colors.enabled);
|
||||
if (!errors.length) continue;
|
||||
const retryLines = [];
|
||||
if (result.retry) {
|
||||
retryLines.push('');
|
||||
retryLines.push(colors.gray(separator(` Retry #${result.retry}`)));
|
||||
}
|
||||
resultLines.push(...retryLines);
|
||||
resultLines.push(...errors.map(error => '\n' + error.message));
|
||||
for (let i = 0; i < result.attachments.length; ++i) {
|
||||
const attachment = result.attachments[i];
|
||||
const hasPrintableContent = attachment.contentType.startsWith('text/');
|
||||
if (!attachment.path && !hasPrintableContent) continue;
|
||||
resultLines.push('');
|
||||
resultLines.push(colors.cyan(separator(` attachment #${i + 1}: ${attachment.name} (${attachment.contentType})`)));
|
||||
if (attachment.path) {
|
||||
const relativePath = _path.default.relative(process.cwd(), attachment.path);
|
||||
resultLines.push(colors.cyan(` ${relativePath}`));
|
||||
// Make this extensible
|
||||
if (attachment.name === 'trace') {
|
||||
const packageManagerCommand = (0, _utils.getPackageManagerExecCommand)();
|
||||
resultLines.push(colors.cyan(` Usage:`));
|
||||
resultLines.push('');
|
||||
resultLines.push(colors.cyan(` ${packageManagerCommand} playwright show-trace ${quotePathIfNeeded(relativePath)}`));
|
||||
resultLines.push('');
|
||||
}
|
||||
} else {
|
||||
if (attachment.contentType.startsWith('text/') && attachment.body) {
|
||||
let text = attachment.body.toString();
|
||||
if (text.length > 300) text = text.slice(0, 300) + '...';
|
||||
for (const line of text.split('\n')) resultLines.push(colors.cyan(` ${line}`));
|
||||
}
|
||||
}
|
||||
resultLines.push(colors.cyan(separator(' ')));
|
||||
}
|
||||
lines.push(...resultLines);
|
||||
}
|
||||
lines.push('');
|
||||
return lines.join('\n');
|
||||
}
|
||||
function formatRetry(result) {
|
||||
const retryLines = [];
|
||||
if (result.retry) {
|
||||
retryLines.push('');
|
||||
retryLines.push(colors.gray(separator(` Retry #${result.retry}`)));
|
||||
}
|
||||
return retryLines;
|
||||
}
|
||||
function quotePathIfNeeded(path) {
|
||||
if (/\s/.test(path)) return `"${path}"`;
|
||||
return path;
|
||||
}
|
||||
function formatResultFailure(test, result, initialIndent, highlightCode) {
|
||||
const errorDetails = [];
|
||||
if (result.status === 'passed' && test.expectedStatus === 'failed') {
|
||||
errorDetails.push({
|
||||
message: indent(colors.red(`Expected to fail, but passed.`), initialIndent)
|
||||
});
|
||||
}
|
||||
if (result.status === 'interrupted') {
|
||||
errorDetails.push({
|
||||
message: indent(colors.red(`Test was interrupted.`), initialIndent)
|
||||
});
|
||||
}
|
||||
for (const error of result.errors) {
|
||||
const formattedError = formatError(error, highlightCode);
|
||||
errorDetails.push({
|
||||
message: indent(formattedError.message, initialIndent),
|
||||
location: formattedError.location
|
||||
});
|
||||
}
|
||||
return errorDetails;
|
||||
}
|
||||
function relativeFilePath(config, file) {
|
||||
return _path.default.relative(config.rootDir, file) || _path.default.basename(file);
|
||||
}
|
||||
function relativeTestPath(config, test) {
|
||||
return relativeFilePath(config, test.location.file);
|
||||
}
|
||||
function stepSuffix(step) {
|
||||
const stepTitles = step ? step.titlePath() : [];
|
||||
return stepTitles.map(t => t.split('\n')[0]).map(t => ' › ' + t).join('');
|
||||
}
|
||||
function formatTestTitle(config, test, step, omitLocation = false) {
|
||||
var _step$location$line, _step$location, _step$location$column, _step$location2;
|
||||
// root, project, file, ...describes, test
|
||||
const [, projectName,, ...titles] = test.titlePath();
|
||||
let location;
|
||||
if (omitLocation) location = `${relativeTestPath(config, test)}`;else location = `${relativeTestPath(config, test)}:${(_step$location$line = step === null || step === void 0 || (_step$location = step.location) === null || _step$location === void 0 ? void 0 : _step$location.line) !== null && _step$location$line !== void 0 ? _step$location$line : test.location.line}:${(_step$location$column = step === null || step === void 0 || (_step$location2 = step.location) === null || _step$location2 === void 0 ? void 0 : _step$location2.column) !== null && _step$location$column !== void 0 ? _step$location$column : test.location.column}`;
|
||||
const projectTitle = projectName ? `[${projectName}] › ` : '';
|
||||
const testTitle = `${projectTitle}${location} › ${titles.join(' › ')}`;
|
||||
const extraTags = test.tags.filter(t => !testTitle.includes(t));
|
||||
return `${testTitle}${stepSuffix(step)}${extraTags.length ? ' ' + extraTags.join(' ') : ''}`;
|
||||
}
|
||||
function formatTestHeader(config, test, options = {}) {
|
||||
const title = formatTestTitle(config, test);
|
||||
const header = `${options.indent || ''}${options.index ? options.index + ') ' : ''}${title}`;
|
||||
let fullHeader = header;
|
||||
|
||||
// Render the path to the deepest failing test.step.
|
||||
if (options.mode === 'error') {
|
||||
const stepPaths = new Set();
|
||||
for (const result of test.results.filter(r => !!r.errors.length)) {
|
||||
const stepPath = [];
|
||||
const visit = steps => {
|
||||
const errors = steps.filter(s => s.error);
|
||||
if (errors.length > 1) return;
|
||||
if (errors.length === 1 && errors[0].category === 'test.step') {
|
||||
stepPath.push(errors[0].title);
|
||||
visit(errors[0].steps);
|
||||
}
|
||||
};
|
||||
visit(result.steps);
|
||||
stepPaths.add(['', ...stepPath].join(' › '));
|
||||
}
|
||||
fullHeader = header + (stepPaths.size === 1 ? stepPaths.values().next().value : '');
|
||||
}
|
||||
return separator(fullHeader);
|
||||
}
|
||||
function formatError(error, highlightCode) {
|
||||
const message = error.message || error.value || '';
|
||||
const stack = error.stack;
|
||||
if (!stack && !error.location) return {
|
||||
message
|
||||
};
|
||||
const tokens = [];
|
||||
|
||||
// Now that we filter out internals from our stack traces, we can safely render
|
||||
// the helper / original exception locations.
|
||||
const parsedStack = stack ? prepareErrorStack(stack) : undefined;
|
||||
tokens.push((parsedStack === null || parsedStack === void 0 ? void 0 : parsedStack.message) || message);
|
||||
if (error.snippet) {
|
||||
let snippet = error.snippet;
|
||||
if (!highlightCode) snippet = stripAnsiEscapes(snippet);
|
||||
tokens.push('');
|
||||
tokens.push(snippet);
|
||||
}
|
||||
if (parsedStack && parsedStack.stackLines.length) tokens.push(colors.dim(parsedStack.stackLines.join('\n')));
|
||||
let location = error.location;
|
||||
if (parsedStack && !location) location = parsedStack.location;
|
||||
if (error.cause) tokens.push(colors.dim('[cause]: ') + formatError(error.cause, highlightCode).message);
|
||||
return {
|
||||
location,
|
||||
message: tokens.join('\n')
|
||||
};
|
||||
}
|
||||
function separator(text = '') {
|
||||
if (text) text += ' ';
|
||||
const columns = Math.min(100, ttyWidth || 100);
|
||||
return text + colors.dim('─'.repeat(Math.max(0, columns - text.length)));
|
||||
}
|
||||
function indent(lines, tab) {
|
||||
return lines.replace(/^(?=.+$)/gm, tab);
|
||||
}
|
||||
function prepareErrorStack(stack) {
|
||||
const lines = stack.split('\n');
|
||||
let firstStackLine = lines.findIndex(line => line.startsWith(' at '));
|
||||
if (firstStackLine === -1) firstStackLine = lines.length;
|
||||
const message = lines.slice(0, firstStackLine).join('\n');
|
||||
const stackLines = lines.slice(firstStackLine);
|
||||
let location;
|
||||
for (const line of stackLines) {
|
||||
const frame = (0, _utilsBundle.parseStackTraceLine)(line);
|
||||
if (!frame || !frame.file) continue;
|
||||
if (belongsToNodeModules(frame.file)) continue;
|
||||
location = {
|
||||
file: frame.file,
|
||||
column: frame.column || 0,
|
||||
line: frame.line || 0
|
||||
};
|
||||
break;
|
||||
}
|
||||
return {
|
||||
message,
|
||||
stackLines,
|
||||
location
|
||||
};
|
||||
}
|
||||
const ansiRegex = new RegExp('([\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~])))', 'g');
|
||||
function stripAnsiEscapes(str) {
|
||||
return str.replace(ansiRegex, '');
|
||||
}
|
||||
function characterWidth(c) {
|
||||
return _utilsBundle2.getEastAsianWidth.eastAsianWidth(c.codePointAt(0));
|
||||
}
|
||||
function stringWidth(v) {
|
||||
let width = 0;
|
||||
for (const {
|
||||
segment
|
||||
} of new Intl.Segmenter(undefined, {
|
||||
granularity: 'grapheme'
|
||||
}).segment(v)) width += characterWidth(segment);
|
||||
return width;
|
||||
}
|
||||
function suffixOfWidth(v, width) {
|
||||
const segments = [...new Intl.Segmenter(undefined, {
|
||||
granularity: 'grapheme'
|
||||
}).segment(v)];
|
||||
let suffixBegin = v.length;
|
||||
for (const {
|
||||
segment,
|
||||
index
|
||||
} of segments.reverse()) {
|
||||
const segmentWidth = stringWidth(segment);
|
||||
if (segmentWidth > width) break;
|
||||
width -= segmentWidth;
|
||||
suffixBegin = index;
|
||||
}
|
||||
return v.substring(suffixBegin);
|
||||
}
|
||||
|
||||
// Leaves enough space for the "prefix" to also fit.
|
||||
function fitToWidth(line, width, prefix) {
|
||||
const prefixLength = prefix ? stripAnsiEscapes(prefix).length : 0;
|
||||
width -= prefixLength;
|
||||
if (stringWidth(line) <= width) return line;
|
||||
|
||||
// Even items are plain text, odd items are control sequences.
|
||||
const parts = line.split(ansiRegex);
|
||||
const taken = [];
|
||||
for (let i = parts.length - 1; i >= 0; i--) {
|
||||
if (i % 2) {
|
||||
// Include all control sequences to preserve formatting.
|
||||
taken.push(parts[i]);
|
||||
} else {
|
||||
let part = suffixOfWidth(parts[i], width);
|
||||
const wasTruncated = part.length < parts[i].length;
|
||||
if (wasTruncated && parts[i].length > 0) {
|
||||
// Add ellipsis if we are truncating.
|
||||
part = '\u2026' + suffixOfWidth(parts[i], width - 1);
|
||||
}
|
||||
taken.push(part);
|
||||
width -= stringWidth(part);
|
||||
}
|
||||
}
|
||||
return taken.reverse().join('');
|
||||
}
|
||||
function belongsToNodeModules(file) {
|
||||
return file.includes(`${_path.default.sep}node_modules${_path.default.sep}`);
|
||||
}
|
||||
function resolveFromEnv(name) {
|
||||
const value = process.env[name];
|
||||
if (value) return _path.default.resolve(process.cwd(), value);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// In addition to `outputFile` the function returns `outputDir` which should
|
||||
// be cleaned up if present by some reporters contract.
|
||||
function resolveOutputFile(reporterName, options) {
|
||||
var _ref, _process$env, _options$default;
|
||||
const name = reporterName.toUpperCase();
|
||||
let outputFile = resolveFromEnv(`PLAYWRIGHT_${name}_OUTPUT_FILE`);
|
||||
if (!outputFile && options.outputFile) outputFile = _path.default.resolve(options.configDir, options.outputFile);
|
||||
if (outputFile) return {
|
||||
outputFile
|
||||
};
|
||||
let outputDir = resolveFromEnv(`PLAYWRIGHT_${name}_OUTPUT_DIR`);
|
||||
if (!outputDir && options.outputDir) outputDir = _path.default.resolve(options.configDir, options.outputDir);
|
||||
if (!outputDir && options.default) outputDir = (0, _util.resolveReporterOutputPath)(options.default.outputDir, options.configDir, undefined);
|
||||
if (!outputDir) outputDir = options.configDir;
|
||||
const reportName = (_ref = (_process$env = process.env[`PLAYWRIGHT_${name}_OUTPUT_NAME`]) !== null && _process$env !== void 0 ? _process$env : options.fileName) !== null && _ref !== void 0 ? _ref : (_options$default = options.default) === null || _options$default === void 0 ? void 0 : _options$default.fileName;
|
||||
if (!reportName) return undefined;
|
||||
outputFile = _path.default.resolve(outputDir, reportName);
|
||||
return {
|
||||
outputFile,
|
||||
outputDir
|
||||
};
|
||||
}
|
||||
133
node_modules/playwright/lib/reporters/blob.js
generated
vendored
Normal file
133
node_modules/playwright/lib/reporters/blob.js
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.currentBlobReportVersion = exports.BlobReporter = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _stream = require("stream");
|
||||
var _teleEmitter = require("./teleEmitter");
|
||||
var _zipBundle = require("playwright-core/lib/zipBundle");
|
||||
var _base = require("./base");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const currentBlobReportVersion = exports.currentBlobReportVersion = 2;
|
||||
class BlobReporter extends _teleEmitter.TeleReporterEmitter {
|
||||
constructor(options) {
|
||||
super(message => this._messages.push(message));
|
||||
this._messages = [];
|
||||
this._attachments = [];
|
||||
this._options = void 0;
|
||||
this._salt = void 0;
|
||||
this._config = void 0;
|
||||
this._options = options;
|
||||
if (this._options.fileName && !this._options.fileName.endsWith('.zip')) throw new Error(`Blob report file name must end with .zip extension: ${this._options.fileName}`);
|
||||
this._salt = (0, _utils.createGuid)();
|
||||
}
|
||||
onConfigure(config) {
|
||||
var _config$shard;
|
||||
const metadata = {
|
||||
version: currentBlobReportVersion,
|
||||
userAgent: (0, _utils.getUserAgent)(),
|
||||
name: process.env.PWTEST_BOT_NAME,
|
||||
shard: (_config$shard = config.shard) !== null && _config$shard !== void 0 ? _config$shard : undefined,
|
||||
pathSeparator: _path.default.sep
|
||||
};
|
||||
this._messages.push({
|
||||
method: 'onBlobReportMetadata',
|
||||
params: metadata
|
||||
});
|
||||
this._config = config;
|
||||
super.onConfigure(config);
|
||||
}
|
||||
async onEnd(result) {
|
||||
await super.onEnd(result);
|
||||
const zipFileName = await this._prepareOutputFile();
|
||||
const zipFile = new _zipBundle.yazl.ZipFile();
|
||||
const zipFinishPromise = new _utils.ManualPromise();
|
||||
const finishPromise = zipFinishPromise.catch(e => {
|
||||
throw new Error(`Failed to write report ${zipFileName}: ` + e.message);
|
||||
});
|
||||
zipFile.on('error', error => zipFinishPromise.reject(error));
|
||||
zipFile.outputStream.pipe(_fs.default.createWriteStream(zipFileName)).on('close', () => {
|
||||
zipFinishPromise.resolve(undefined);
|
||||
}).on('error', error => zipFinishPromise.reject(error));
|
||||
for (const {
|
||||
originalPath,
|
||||
zipEntryPath
|
||||
} of this._attachments) {
|
||||
var _fs$statSync;
|
||||
if (!((_fs$statSync = _fs.default.statSync(originalPath, {
|
||||
throwIfNoEntry: false
|
||||
})) !== null && _fs$statSync !== void 0 && _fs$statSync.isFile())) continue;
|
||||
zipFile.addFile(originalPath, zipEntryPath);
|
||||
}
|
||||
const lines = this._messages.map(m => JSON.stringify(m) + '\n');
|
||||
const content = _stream.Readable.from(lines);
|
||||
zipFile.addReadStream(content, 'report.jsonl');
|
||||
zipFile.end();
|
||||
await finishPromise;
|
||||
}
|
||||
async _prepareOutputFile() {
|
||||
const {
|
||||
outputFile,
|
||||
outputDir
|
||||
} = (0, _base.resolveOutputFile)('BLOB', {
|
||||
...this._options,
|
||||
default: {
|
||||
fileName: this._defaultReportName(this._config),
|
||||
outputDir: 'blob-report'
|
||||
}
|
||||
});
|
||||
if (!process.env.PWTEST_BLOB_DO_NOT_REMOVE) await (0, _utils.removeFolders)([outputDir]);
|
||||
await _fs.default.promises.mkdir(_path.default.dirname(outputFile), {
|
||||
recursive: true
|
||||
});
|
||||
return outputFile;
|
||||
}
|
||||
_defaultReportName(config) {
|
||||
let reportName = 'report';
|
||||
if (this._options._commandHash) reportName += '-' + (0, _utils.sanitizeForFilePath)(this._options._commandHash);
|
||||
if (config.shard) {
|
||||
const paddedNumber = `${config.shard.current}`.padStart(`${config.shard.total}`.length, '0');
|
||||
reportName = `${reportName}-${paddedNumber}`;
|
||||
}
|
||||
return `${reportName}.zip`;
|
||||
}
|
||||
_serializeAttachments(attachments) {
|
||||
return super._serializeAttachments(attachments).map(attachment => {
|
||||
if (!attachment.path) return attachment;
|
||||
// Add run guid to avoid clashes between shards.
|
||||
const sha1 = (0, _utils.calculateSha1)(attachment.path + this._salt);
|
||||
const extension = _utilsBundle.mime.getExtension(attachment.contentType) || 'dat';
|
||||
const newPath = `resources/${sha1}.${extension}`;
|
||||
this._attachments.push({
|
||||
originalPath: attachment.path,
|
||||
zipEntryPath: newPath
|
||||
});
|
||||
return {
|
||||
...attachment,
|
||||
path: newPath
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.BlobReporter = BlobReporter;
|
||||
79
node_modules/playwright/lib/reporters/dot.js
generated
vendored
Normal file
79
node_modules/playwright/lib/reporters/dot.js
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _base = require("./base");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class DotReporter extends _base.BaseReporter {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this._counter = 0;
|
||||
}
|
||||
onBegin(suite) {
|
||||
super.onBegin(suite);
|
||||
console.log(this.generateStartingMessage());
|
||||
}
|
||||
onStdOut(chunk, test, result) {
|
||||
super.onStdOut(chunk, test, result);
|
||||
if (!this.config.quiet) process.stdout.write(chunk);
|
||||
}
|
||||
onStdErr(chunk, test, result) {
|
||||
super.onStdErr(chunk, test, result);
|
||||
if (!this.config.quiet) process.stderr.write(chunk);
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
super.onTestEnd(test, result);
|
||||
if (this._counter === 80) {
|
||||
process.stdout.write('\n');
|
||||
this._counter = 0;
|
||||
}
|
||||
++this._counter;
|
||||
if (result.status === 'skipped') {
|
||||
process.stdout.write(_base.colors.yellow('°'));
|
||||
return;
|
||||
}
|
||||
if (this.willRetry(test)) {
|
||||
process.stdout.write(_base.colors.gray('×'));
|
||||
return;
|
||||
}
|
||||
switch (test.outcome()) {
|
||||
case 'expected':
|
||||
process.stdout.write(_base.colors.green('·'));
|
||||
break;
|
||||
case 'unexpected':
|
||||
process.stdout.write(_base.colors.red(result.status === 'timedOut' ? 'T' : 'F'));
|
||||
break;
|
||||
case 'flaky':
|
||||
process.stdout.write(_base.colors.yellow('±'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
onError(error) {
|
||||
super.onError(error);
|
||||
console.log('\n' + (0, _base.formatError)(error, _base.colors.enabled).message);
|
||||
this._counter = 0;
|
||||
}
|
||||
async onEnd(result) {
|
||||
await super.onEnd(result);
|
||||
process.stdout.write('\n');
|
||||
this.epilogue(true);
|
||||
}
|
||||
}
|
||||
var _default = exports.default = DotReporter;
|
||||
31
node_modules/playwright/lib/reporters/empty.js
generated
vendored
Normal file
31
node_modules/playwright/lib/reporters/empty.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class EmptyReporter {
|
||||
version() {
|
||||
return 'v2';
|
||||
}
|
||||
printsToStdio() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var _default = exports.default = EmptyReporter;
|
||||
115
node_modules/playwright/lib/reporters/github.js
generated
vendored
Normal file
115
node_modules/playwright/lib/reporters/github.js
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = exports.GitHubReporter = void 0;
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _base = require("./base");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class GitHubLogger {
|
||||
_log(message, type = 'notice', options = {}) {
|
||||
message = message.replace(/\n/g, '%0A');
|
||||
const configs = Object.entries(options).map(([key, option]) => `${key}=${option}`).join(',');
|
||||
console.log((0, _base.stripAnsiEscapes)(`::${type} ${configs}::${message}`));
|
||||
}
|
||||
debug(message, options) {
|
||||
this._log(message, 'debug', options);
|
||||
}
|
||||
error(message, options) {
|
||||
this._log(message, 'error', options);
|
||||
}
|
||||
notice(message, options) {
|
||||
this._log(message, 'notice', options);
|
||||
}
|
||||
warning(message, options) {
|
||||
this._log(message, 'warning', options);
|
||||
}
|
||||
}
|
||||
class GitHubReporter extends _base.BaseReporter {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.githubLogger = new GitHubLogger();
|
||||
}
|
||||
printsToStdio() {
|
||||
return false;
|
||||
}
|
||||
async onEnd(result) {
|
||||
await super.onEnd(result);
|
||||
this._printAnnotations();
|
||||
}
|
||||
onError(error) {
|
||||
const errorMessage = (0, _base.formatError)(error, false).message;
|
||||
this.githubLogger.error(errorMessage);
|
||||
}
|
||||
_printAnnotations() {
|
||||
const summary = this.generateSummary();
|
||||
const summaryMessage = this.generateSummaryMessage(summary);
|
||||
if (summary.failuresToPrint.length) this._printFailureAnnotations(summary.failuresToPrint);
|
||||
this._printSlowTestAnnotations();
|
||||
this._printSummaryAnnotation(summaryMessage);
|
||||
}
|
||||
_printSlowTestAnnotations() {
|
||||
this.getSlowTests().forEach(([file, duration]) => {
|
||||
const filePath = workspaceRelativePath(_path.default.join(process.cwd(), file));
|
||||
this.githubLogger.warning(`${filePath} took ${(0, _utilsBundle.ms)(duration)}`, {
|
||||
title: 'Slow Test',
|
||||
file: filePath
|
||||
});
|
||||
});
|
||||
}
|
||||
_printSummaryAnnotation(summary) {
|
||||
this.githubLogger.notice(summary, {
|
||||
title: '🎭 Playwright Run Summary'
|
||||
});
|
||||
}
|
||||
_printFailureAnnotations(failures) {
|
||||
failures.forEach((test, index) => {
|
||||
const title = (0, _base.formatTestTitle)(this.config, test);
|
||||
const header = (0, _base.formatTestHeader)(this.config, test, {
|
||||
indent: ' ',
|
||||
index: index + 1,
|
||||
mode: 'error'
|
||||
});
|
||||
for (const result of test.results) {
|
||||
const errors = (0, _base.formatResultFailure)(test, result, ' ', _base.colors.enabled);
|
||||
for (const error of errors) {
|
||||
var _error$location;
|
||||
const options = {
|
||||
file: workspaceRelativePath(((_error$location = error.location) === null || _error$location === void 0 ? void 0 : _error$location.file) || test.location.file),
|
||||
title
|
||||
};
|
||||
if (error.location) {
|
||||
options.line = error.location.line;
|
||||
options.col = error.location.column;
|
||||
}
|
||||
const message = [header, ...(0, _base.formatRetry)(result), error.message].join('\n');
|
||||
this.githubLogger.error(message, options);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.GitHubReporter = GitHubReporter;
|
||||
function workspaceRelativePath(filePath) {
|
||||
var _process$env$GITHUB_W;
|
||||
return _path.default.relative((_process$env$GITHUB_W = process.env['GITHUB_WORKSPACE']) !== null && _process$env$GITHUB_W !== void 0 ? _process$env$GITHUB_W : '', filePath);
|
||||
}
|
||||
var _default = exports.default = GitHubReporter;
|
||||
645
node_modules/playwright/lib/reporters/html.js
generated
vendored
Normal file
645
node_modules/playwright/lib/reporters/html.js
generated
vendored
Normal file
@@ -0,0 +1,645 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
exports.showHTMLReport = showHTMLReport;
|
||||
exports.startHtmlReportServer = startHtmlReportServer;
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _stream = require("stream");
|
||||
var _babelBundle = require("../transform/babelBundle");
|
||||
var _base = require("./base");
|
||||
var _util = require("../util");
|
||||
var _zipBundle = require("playwright-core/lib/zipBundle");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const htmlReportOptions = ['always', 'never', 'on-failure'];
|
||||
const isHtmlReportOption = type => {
|
||||
return htmlReportOptions.includes(type);
|
||||
};
|
||||
class HtmlReporter {
|
||||
constructor(options) {
|
||||
this.config = void 0;
|
||||
this.suite = void 0;
|
||||
this._options = void 0;
|
||||
this._outputFolder = void 0;
|
||||
this._attachmentsBaseURL = void 0;
|
||||
this._open = void 0;
|
||||
this._port = void 0;
|
||||
this._host = void 0;
|
||||
this._buildResult = void 0;
|
||||
this._topLevelErrors = [];
|
||||
this._options = options;
|
||||
}
|
||||
version() {
|
||||
return 'v2';
|
||||
}
|
||||
printsToStdio() {
|
||||
return false;
|
||||
}
|
||||
onConfigure(config) {
|
||||
this.config = config;
|
||||
}
|
||||
onBegin(suite) {
|
||||
const {
|
||||
outputFolder,
|
||||
open,
|
||||
attachmentsBaseURL,
|
||||
host,
|
||||
port
|
||||
} = this._resolveOptions();
|
||||
this._outputFolder = outputFolder;
|
||||
this._open = open;
|
||||
this._host = host;
|
||||
this._port = port;
|
||||
this._attachmentsBaseURL = attachmentsBaseURL;
|
||||
const reportedWarnings = new Set();
|
||||
for (const project of this.config.projects) {
|
||||
if (this._isSubdirectory(outputFolder, project.outputDir) || this._isSubdirectory(project.outputDir, outputFolder)) {
|
||||
const key = outputFolder + '|' + project.outputDir;
|
||||
if (reportedWarnings.has(key)) continue;
|
||||
reportedWarnings.add(key);
|
||||
console.log(_base.colors.red(`Configuration Error: HTML reporter output folder clashes with the tests output folder:`));
|
||||
console.log(`
|
||||
html reporter folder: ${_base.colors.bold(outputFolder)}
|
||||
test results folder: ${_base.colors.bold(project.outputDir)}`);
|
||||
console.log('');
|
||||
console.log(`HTML reporter will clear its output directory prior to being generated, which will lead to the artifact loss.
|
||||
`);
|
||||
}
|
||||
}
|
||||
this.suite = suite;
|
||||
}
|
||||
_resolveOptions() {
|
||||
var _reportFolderFromEnv;
|
||||
const outputFolder = (_reportFolderFromEnv = reportFolderFromEnv()) !== null && _reportFolderFromEnv !== void 0 ? _reportFolderFromEnv : (0, _util.resolveReporterOutputPath)('playwright-report', this._options.configDir, this._options.outputFolder);
|
||||
return {
|
||||
outputFolder,
|
||||
open: getHtmlReportOptionProcessEnv() || this._options.open || 'on-failure',
|
||||
attachmentsBaseURL: process.env.PLAYWRIGHT_HTML_ATTACHMENTS_BASE_URL || this._options.attachmentsBaseURL || 'data/',
|
||||
host: process.env.PLAYWRIGHT_HTML_HOST || this._options.host,
|
||||
port: process.env.PLAYWRIGHT_HTML_PORT ? +process.env.PLAYWRIGHT_HTML_PORT : this._options.port
|
||||
};
|
||||
}
|
||||
_isSubdirectory(parentDir, dir) {
|
||||
const relativePath = _path.default.relative(parentDir, dir);
|
||||
return !!relativePath && !relativePath.startsWith('..') && !_path.default.isAbsolute(relativePath);
|
||||
}
|
||||
onError(error) {
|
||||
this._topLevelErrors.push(error);
|
||||
}
|
||||
async onEnd(result) {
|
||||
const projectSuites = this.suite.suites;
|
||||
await (0, _utils.removeFolders)([this._outputFolder]);
|
||||
const builder = new HtmlBuilder(this.config, this._outputFolder, this._attachmentsBaseURL);
|
||||
this._buildResult = await builder.build(this.config.metadata, projectSuites, result, this._topLevelErrors);
|
||||
}
|
||||
async onExit() {
|
||||
if (process.env.CI || !this._buildResult) return;
|
||||
const {
|
||||
ok,
|
||||
singleTestId
|
||||
} = this._buildResult;
|
||||
const shouldOpen = !this._options._isTestServer && (this._open === 'always' || !ok && this._open === 'on-failure');
|
||||
if (shouldOpen) {
|
||||
await showHTMLReport(this._outputFolder, this._host, this._port, singleTestId);
|
||||
} else if (this._options._mode === 'test' && !this._options._isTestServer) {
|
||||
const packageManagerCommand = (0, _utils.getPackageManagerExecCommand)();
|
||||
const relativeReportPath = this._outputFolder === standaloneDefaultFolder() ? '' : ' ' + _path.default.relative(process.cwd(), this._outputFolder);
|
||||
const hostArg = this._host ? ` --host ${this._host}` : '';
|
||||
const portArg = this._port ? ` --port ${this._port}` : '';
|
||||
console.log('');
|
||||
console.log('To open last HTML report run:');
|
||||
console.log(_base.colors.cyan(`
|
||||
${packageManagerCommand} playwright show-report${relativeReportPath}${hostArg}${portArg}
|
||||
`));
|
||||
}
|
||||
}
|
||||
}
|
||||
function reportFolderFromEnv() {
|
||||
// Note: PLAYWRIGHT_HTML_REPORT is for backwards compatibility.
|
||||
const envValue = process.env.PLAYWRIGHT_HTML_OUTPUT_DIR || process.env.PLAYWRIGHT_HTML_REPORT;
|
||||
return envValue ? _path.default.resolve(envValue) : undefined;
|
||||
}
|
||||
function getHtmlReportOptionProcessEnv() {
|
||||
// Note: PW_TEST_HTML_REPORT_OPEN is for backwards compatibility.
|
||||
const htmlOpenEnv = process.env.PLAYWRIGHT_HTML_OPEN || process.env.PW_TEST_HTML_REPORT_OPEN;
|
||||
if (!htmlOpenEnv) return undefined;
|
||||
if (!isHtmlReportOption(htmlOpenEnv)) {
|
||||
console.log(_base.colors.red(`Configuration Error: HTML reporter Invalid value for PLAYWRIGHT_HTML_OPEN: ${htmlOpenEnv}. Valid values are: ${htmlReportOptions.join(', ')}`));
|
||||
return undefined;
|
||||
}
|
||||
return htmlOpenEnv;
|
||||
}
|
||||
function standaloneDefaultFolder() {
|
||||
var _reportFolderFromEnv2;
|
||||
return (_reportFolderFromEnv2 = reportFolderFromEnv()) !== null && _reportFolderFromEnv2 !== void 0 ? _reportFolderFromEnv2 : (0, _util.resolveReporterOutputPath)('playwright-report', process.cwd(), undefined);
|
||||
}
|
||||
async function showHTMLReport(reportFolder, host = 'localhost', port, testId) {
|
||||
const folder = reportFolder !== null && reportFolder !== void 0 ? reportFolder : standaloneDefaultFolder();
|
||||
try {
|
||||
(0, _utils.assert)(_fs.default.statSync(folder).isDirectory());
|
||||
} catch (e) {
|
||||
console.log(_base.colors.red(`No report found at "${folder}"`));
|
||||
(0, _utils.gracefullyProcessExitDoNotHang)(1);
|
||||
return;
|
||||
}
|
||||
const server = startHtmlReportServer(folder);
|
||||
await server.start({
|
||||
port,
|
||||
host,
|
||||
preferredPort: port ? undefined : 9323
|
||||
});
|
||||
let url = server.urlPrefix('human-readable');
|
||||
console.log('');
|
||||
console.log(_base.colors.cyan(` Serving HTML report at ${url}. Press Ctrl+C to quit.`));
|
||||
if (testId) url += `#?testId=${testId}`;
|
||||
url = url.replace('0.0.0.0', 'localhost');
|
||||
await (0, _utilsBundle.open)(url, {
|
||||
wait: true
|
||||
}).catch(() => {});
|
||||
await new Promise(() => {});
|
||||
}
|
||||
function startHtmlReportServer(folder) {
|
||||
const server = new _utils.HttpServer();
|
||||
server.routePrefix('/', (request, response) => {
|
||||
let relativePath = new URL('http://localhost' + request.url).pathname;
|
||||
if (relativePath.startsWith('/trace/file')) {
|
||||
const url = new URL('http://localhost' + request.url);
|
||||
try {
|
||||
return server.serveFile(request, response, url.searchParams.get('path'));
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (relativePath.endsWith('/stall.js')) return true;
|
||||
if (relativePath === '/') relativePath = '/index.html';
|
||||
const absolutePath = _path.default.join(folder, ...relativePath.split('/'));
|
||||
return server.serveFile(request, response, absolutePath);
|
||||
});
|
||||
return server;
|
||||
}
|
||||
class HtmlBuilder {
|
||||
constructor(config, outputDir, attachmentsBaseURL) {
|
||||
this._config = void 0;
|
||||
this._reportFolder = void 0;
|
||||
this._stepsInFile = new _utils.MultiMap();
|
||||
this._dataZipFile = void 0;
|
||||
this._hasTraces = false;
|
||||
this._attachmentsBaseURL = void 0;
|
||||
this._config = config;
|
||||
this._reportFolder = outputDir;
|
||||
_fs.default.mkdirSync(this._reportFolder, {
|
||||
recursive: true
|
||||
});
|
||||
this._dataZipFile = new _zipBundle.yazl.ZipFile();
|
||||
this._attachmentsBaseURL = attachmentsBaseURL;
|
||||
}
|
||||
async build(metadata, projectSuites, result, topLevelErrors) {
|
||||
const data = new Map();
|
||||
for (const projectSuite of projectSuites) {
|
||||
const testDir = projectSuite.project().testDir;
|
||||
for (const fileSuite of projectSuite.suites) {
|
||||
const fileName = this._relativeLocation(fileSuite.location).file;
|
||||
// Preserve file ids computed off the testDir.
|
||||
const relativeFile = _path.default.relative(testDir, fileSuite.location.file);
|
||||
const fileId = (0, _utils.calculateSha1)((0, _utils.toPosixPath)(relativeFile)).slice(0, 20);
|
||||
let fileEntry = data.get(fileId);
|
||||
if (!fileEntry) {
|
||||
fileEntry = {
|
||||
testFile: {
|
||||
fileId,
|
||||
fileName,
|
||||
tests: []
|
||||
},
|
||||
testFileSummary: {
|
||||
fileId,
|
||||
fileName,
|
||||
tests: [],
|
||||
stats: emptyStats()
|
||||
}
|
||||
};
|
||||
data.set(fileId, fileEntry);
|
||||
}
|
||||
const {
|
||||
testFile,
|
||||
testFileSummary
|
||||
} = fileEntry;
|
||||
const testEntries = [];
|
||||
this._processSuite(fileSuite, projectSuite.project().name, [], testEntries);
|
||||
for (const test of testEntries) {
|
||||
testFile.tests.push(test.testCase);
|
||||
testFileSummary.tests.push(test.testCaseSummary);
|
||||
}
|
||||
}
|
||||
}
|
||||
createSnippets(this._stepsInFile);
|
||||
let ok = true;
|
||||
for (const [fileId, {
|
||||
testFile,
|
||||
testFileSummary
|
||||
}] of data) {
|
||||
const stats = testFileSummary.stats;
|
||||
for (const test of testFileSummary.tests) {
|
||||
if (test.outcome === 'expected') ++stats.expected;
|
||||
if (test.outcome === 'skipped') ++stats.skipped;
|
||||
if (test.outcome === 'unexpected') ++stats.unexpected;
|
||||
if (test.outcome === 'flaky') ++stats.flaky;
|
||||
++stats.total;
|
||||
}
|
||||
stats.ok = stats.unexpected + stats.flaky === 0;
|
||||
if (!stats.ok) ok = false;
|
||||
const testCaseSummaryComparator = (t1, t2) => {
|
||||
const w1 = (t1.outcome === 'unexpected' ? 1000 : 0) + (t1.outcome === 'flaky' ? 1 : 0);
|
||||
const w2 = (t2.outcome === 'unexpected' ? 1000 : 0) + (t2.outcome === 'flaky' ? 1 : 0);
|
||||
return w2 - w1;
|
||||
};
|
||||
testFileSummary.tests.sort(testCaseSummaryComparator);
|
||||
this._addDataFile(fileId + '.json', testFile);
|
||||
}
|
||||
const htmlReport = {
|
||||
metadata,
|
||||
startTime: result.startTime.getTime(),
|
||||
duration: result.duration,
|
||||
files: [...data.values()].map(e => e.testFileSummary),
|
||||
projectNames: projectSuites.map(r => r.project().name),
|
||||
stats: {
|
||||
...[...data.values()].reduce((a, e) => addStats(a, e.testFileSummary.stats), emptyStats())
|
||||
},
|
||||
errors: topLevelErrors.map(error => (0, _base.formatError)(error, true).message)
|
||||
};
|
||||
htmlReport.files.sort((f1, f2) => {
|
||||
const w1 = f1.stats.unexpected * 1000 + f1.stats.flaky;
|
||||
const w2 = f2.stats.unexpected * 1000 + f2.stats.flaky;
|
||||
return w2 - w1;
|
||||
});
|
||||
this._addDataFile('report.json', htmlReport);
|
||||
let singleTestId;
|
||||
if (htmlReport.stats.total === 1) {
|
||||
const testFile = data.values().next().value.testFile;
|
||||
singleTestId = testFile.tests[0].testId;
|
||||
}
|
||||
if (process.env.PW_HMR === '1') {
|
||||
const redirectFile = _path.default.join(this._reportFolder, 'index.html');
|
||||
await this._writeReportData(redirectFile);
|
||||
async function redirect() {
|
||||
const hmrURL = new URL('http://localhost:44224'); // dev server, port is harcoded in build.js
|
||||
const popup = window.open(hmrURL);
|
||||
window.addEventListener('message', evt => {
|
||||
if (evt.source === popup && evt.data === 'ready') {
|
||||
popup.postMessage(window.playwrightReportBase64, hmrURL.origin);
|
||||
window.close();
|
||||
}
|
||||
}, {
|
||||
once: true
|
||||
});
|
||||
}
|
||||
_fs.default.appendFileSync(redirectFile, `<script>(${redirect.toString()})()</script>`);
|
||||
return {
|
||||
ok,
|
||||
singleTestId
|
||||
};
|
||||
}
|
||||
|
||||
// Copy app.
|
||||
const appFolder = _path.default.join(require.resolve('playwright-core'), '..', 'lib', 'vite', 'htmlReport');
|
||||
await (0, _utils.copyFileAndMakeWritable)(_path.default.join(appFolder, 'index.html'), _path.default.join(this._reportFolder, 'index.html'));
|
||||
|
||||
// Copy trace viewer.
|
||||
if (this._hasTraces) {
|
||||
const traceViewerFolder = _path.default.join(require.resolve('playwright-core'), '..', 'lib', 'vite', 'traceViewer');
|
||||
const traceViewerTargetFolder = _path.default.join(this._reportFolder, 'trace');
|
||||
const traceViewerAssetsTargetFolder = _path.default.join(traceViewerTargetFolder, 'assets');
|
||||
_fs.default.mkdirSync(traceViewerAssetsTargetFolder, {
|
||||
recursive: true
|
||||
});
|
||||
for (const file of _fs.default.readdirSync(traceViewerFolder)) {
|
||||
if (file.endsWith('.map') || file.includes('watch') || file.includes('assets')) continue;
|
||||
await (0, _utils.copyFileAndMakeWritable)(_path.default.join(traceViewerFolder, file), _path.default.join(traceViewerTargetFolder, file));
|
||||
}
|
||||
for (const file of _fs.default.readdirSync(_path.default.join(traceViewerFolder, 'assets'))) {
|
||||
if (file.endsWith('.map') || file.includes('xtermModule')) continue;
|
||||
await (0, _utils.copyFileAndMakeWritable)(_path.default.join(traceViewerFolder, 'assets', file), _path.default.join(traceViewerAssetsTargetFolder, file));
|
||||
}
|
||||
}
|
||||
await this._writeReportData(_path.default.join(this._reportFolder, 'index.html'));
|
||||
return {
|
||||
ok,
|
||||
singleTestId
|
||||
};
|
||||
}
|
||||
async _writeReportData(filePath) {
|
||||
_fs.default.appendFileSync(filePath, '<script>\nwindow.playwrightReportBase64 = "data:application/zip;base64,');
|
||||
await new Promise(f => {
|
||||
this._dataZipFile.end(undefined, () => {
|
||||
this._dataZipFile.outputStream.pipe(new Base64Encoder()).pipe(_fs.default.createWriteStream(filePath, {
|
||||
flags: 'a'
|
||||
})).on('close', f);
|
||||
});
|
||||
});
|
||||
_fs.default.appendFileSync(filePath, '";</script>');
|
||||
}
|
||||
_addDataFile(fileName, data) {
|
||||
this._dataZipFile.addBuffer(Buffer.from(JSON.stringify(data)), fileName);
|
||||
}
|
||||
_processSuite(suite, projectName, path, outTests) {
|
||||
const newPath = [...path, suite.title];
|
||||
suite.entries().forEach(e => {
|
||||
if (e.type === 'test') outTests.push(this._createTestEntry(e, projectName, newPath));else this._processSuite(e, projectName, newPath, outTests);
|
||||
});
|
||||
}
|
||||
_createTestEntry(test, projectName, path) {
|
||||
const duration = test.results.reduce((a, r) => a + r.duration, 0);
|
||||
const location = this._relativeLocation(test.location);
|
||||
path = path.slice(1).filter(path => path.length > 0);
|
||||
const results = test.results.map(r => this._createTestResult(test, r));
|
||||
return {
|
||||
testCase: {
|
||||
testId: test.id,
|
||||
title: test.title,
|
||||
projectName,
|
||||
location,
|
||||
duration,
|
||||
// Annotations can be pushed directly, with a wrong type.
|
||||
annotations: test.annotations.map(a => ({
|
||||
type: a.type,
|
||||
description: a.description ? String(a.description) : a.description
|
||||
})),
|
||||
tags: test.tags,
|
||||
outcome: test.outcome(),
|
||||
path,
|
||||
results,
|
||||
ok: test.outcome() === 'expected' || test.outcome() === 'flaky'
|
||||
},
|
||||
testCaseSummary: {
|
||||
testId: test.id,
|
||||
title: test.title,
|
||||
projectName,
|
||||
location,
|
||||
duration,
|
||||
// Annotations can be pushed directly, with a wrong type.
|
||||
annotations: test.annotations.map(a => ({
|
||||
type: a.type,
|
||||
description: a.description ? String(a.description) : a.description
|
||||
})),
|
||||
tags: test.tags,
|
||||
outcome: test.outcome(),
|
||||
path,
|
||||
ok: test.outcome() === 'expected' || test.outcome() === 'flaky',
|
||||
results: results.map(result => {
|
||||
return {
|
||||
attachments: result.attachments.map(a => ({
|
||||
name: a.name,
|
||||
contentType: a.contentType,
|
||||
path: a.path
|
||||
}))
|
||||
};
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
_serializeAttachments(attachments) {
|
||||
let lastAttachment;
|
||||
return attachments.map(a => {
|
||||
if (a.name === 'trace') this._hasTraces = true;
|
||||
if ((a.name === 'stdout' || a.name === 'stderr') && a.contentType === 'text/plain') {
|
||||
if (lastAttachment && lastAttachment.name === a.name && lastAttachment.contentType === a.contentType) {
|
||||
lastAttachment.body += (0, _base.stripAnsiEscapes)(a.body);
|
||||
return null;
|
||||
}
|
||||
a.body = (0, _base.stripAnsiEscapes)(a.body);
|
||||
lastAttachment = a;
|
||||
return a;
|
||||
}
|
||||
if (a.path) {
|
||||
let fileName = a.path;
|
||||
try {
|
||||
const buffer = _fs.default.readFileSync(a.path);
|
||||
const sha1 = (0, _utils.calculateSha1)(buffer) + _path.default.extname(a.path);
|
||||
fileName = this._attachmentsBaseURL + sha1;
|
||||
_fs.default.mkdirSync(_path.default.join(this._reportFolder, 'data'), {
|
||||
recursive: true
|
||||
});
|
||||
_fs.default.writeFileSync(_path.default.join(this._reportFolder, 'data', sha1), buffer);
|
||||
} catch (e) {}
|
||||
return {
|
||||
name: a.name,
|
||||
contentType: a.contentType,
|
||||
path: fileName,
|
||||
body: a.body
|
||||
};
|
||||
}
|
||||
if (a.body instanceof Buffer) {
|
||||
if (isTextContentType(a.contentType)) {
|
||||
var _a$contentType$match;
|
||||
// Content type is like this: "text/html; charset=UTF-8"
|
||||
const charset = (_a$contentType$match = a.contentType.match(/charset=(.*)/)) === null || _a$contentType$match === void 0 ? void 0 : _a$contentType$match[1];
|
||||
try {
|
||||
const body = a.body.toString(charset || 'utf-8');
|
||||
return {
|
||||
name: a.name,
|
||||
contentType: a.contentType,
|
||||
body
|
||||
};
|
||||
} catch (e) {
|
||||
// Invalid encoding, fall through and save to file.
|
||||
}
|
||||
}
|
||||
_fs.default.mkdirSync(_path.default.join(this._reportFolder, 'data'), {
|
||||
recursive: true
|
||||
});
|
||||
const extension = (0, _utils.sanitizeForFilePath)(_path.default.extname(a.name).replace(/^\./, '')) || _utilsBundle.mime.getExtension(a.contentType) || 'dat';
|
||||
const sha1 = (0, _utils.calculateSha1)(a.body) + '.' + extension;
|
||||
_fs.default.writeFileSync(_path.default.join(this._reportFolder, 'data', sha1), a.body);
|
||||
return {
|
||||
name: a.name,
|
||||
contentType: a.contentType,
|
||||
path: this._attachmentsBaseURL + sha1
|
||||
};
|
||||
}
|
||||
|
||||
// string
|
||||
return {
|
||||
name: a.name,
|
||||
contentType: a.contentType,
|
||||
body: a.body
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}
|
||||
_createTestResult(test, result) {
|
||||
return {
|
||||
duration: result.duration,
|
||||
startTime: result.startTime.toISOString(),
|
||||
retry: result.retry,
|
||||
steps: dedupeSteps(result.steps).map(s => this._createTestStep(s)),
|
||||
errors: (0, _base.formatResultFailure)(test, result, '', true).map(error => error.message),
|
||||
status: result.status,
|
||||
attachments: this._serializeAttachments([...result.attachments, ...result.stdout.map(m => stdioAttachment(m, 'stdout')), ...result.stderr.map(m => stdioAttachment(m, 'stderr'))])
|
||||
};
|
||||
}
|
||||
_createTestStep(dedupedStep) {
|
||||
var _step$error;
|
||||
const {
|
||||
step,
|
||||
duration,
|
||||
count
|
||||
} = dedupedStep;
|
||||
const result = {
|
||||
title: step.title,
|
||||
startTime: step.startTime.toISOString(),
|
||||
duration,
|
||||
steps: dedupeSteps(step.steps).map(s => this._createTestStep(s)),
|
||||
location: this._relativeLocation(step.location),
|
||||
error: (_step$error = step.error) === null || _step$error === void 0 ? void 0 : _step$error.message,
|
||||
count
|
||||
};
|
||||
if (step.location) this._stepsInFile.set(step.location.file, result);
|
||||
return result;
|
||||
}
|
||||
_relativeLocation(location) {
|
||||
if (!location) return undefined;
|
||||
const file = (0, _utils.toPosixPath)(_path.default.relative(this._config.rootDir, location.file));
|
||||
return {
|
||||
file,
|
||||
line: location.line,
|
||||
column: location.column
|
||||
};
|
||||
}
|
||||
}
|
||||
const emptyStats = () => {
|
||||
return {
|
||||
total: 0,
|
||||
expected: 0,
|
||||
unexpected: 0,
|
||||
flaky: 0,
|
||||
skipped: 0,
|
||||
ok: true
|
||||
};
|
||||
};
|
||||
const addStats = (stats, delta) => {
|
||||
stats.total += delta.total;
|
||||
stats.skipped += delta.skipped;
|
||||
stats.expected += delta.expected;
|
||||
stats.unexpected += delta.unexpected;
|
||||
stats.flaky += delta.flaky;
|
||||
stats.ok = stats.ok && delta.ok;
|
||||
return stats;
|
||||
};
|
||||
class Base64Encoder extends _stream.Transform {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this._remainder = void 0;
|
||||
}
|
||||
_transform(chunk, encoding, callback) {
|
||||
if (this._remainder) {
|
||||
chunk = Buffer.concat([this._remainder, chunk]);
|
||||
this._remainder = undefined;
|
||||
}
|
||||
const remaining = chunk.length % 3;
|
||||
if (remaining) {
|
||||
this._remainder = chunk.slice(chunk.length - remaining);
|
||||
chunk = chunk.slice(0, chunk.length - remaining);
|
||||
}
|
||||
chunk = chunk.toString('base64');
|
||||
this.push(Buffer.from(chunk));
|
||||
callback();
|
||||
}
|
||||
_flush(callback) {
|
||||
if (this._remainder) this.push(Buffer.from(this._remainder.toString('base64')));
|
||||
callback();
|
||||
}
|
||||
}
|
||||
function isTextContentType(contentType) {
|
||||
return contentType.startsWith('text/') || contentType.startsWith('application/json');
|
||||
}
|
||||
function stdioAttachment(chunk, type) {
|
||||
if (typeof chunk === 'string') {
|
||||
return {
|
||||
name: type,
|
||||
contentType: 'text/plain',
|
||||
body: chunk
|
||||
};
|
||||
}
|
||||
return {
|
||||
name: type,
|
||||
contentType: 'application/octet-stream',
|
||||
body: chunk
|
||||
};
|
||||
}
|
||||
function dedupeSteps(steps) {
|
||||
const result = [];
|
||||
let lastResult = undefined;
|
||||
for (const step of steps) {
|
||||
var _step$location, _lastResult, _step$location2, _lastStep$location, _step$location3, _lastStep$location2, _step$location4, _lastStep$location3;
|
||||
const canDedupe = !step.error && step.duration >= 0 && ((_step$location = step.location) === null || _step$location === void 0 ? void 0 : _step$location.file) && !step.steps.length;
|
||||
const lastStep = (_lastResult = lastResult) === null || _lastResult === void 0 ? void 0 : _lastResult.step;
|
||||
if (canDedupe && lastResult && lastStep && step.category === lastStep.category && step.title === lastStep.title && ((_step$location2 = step.location) === null || _step$location2 === void 0 ? void 0 : _step$location2.file) === ((_lastStep$location = lastStep.location) === null || _lastStep$location === void 0 ? void 0 : _lastStep$location.file) && ((_step$location3 = step.location) === null || _step$location3 === void 0 ? void 0 : _step$location3.line) === ((_lastStep$location2 = lastStep.location) === null || _lastStep$location2 === void 0 ? void 0 : _lastStep$location2.line) && ((_step$location4 = step.location) === null || _step$location4 === void 0 ? void 0 : _step$location4.column) === ((_lastStep$location3 = lastStep.location) === null || _lastStep$location3 === void 0 ? void 0 : _lastStep$location3.column)) {
|
||||
++lastResult.count;
|
||||
lastResult.duration += step.duration;
|
||||
continue;
|
||||
}
|
||||
lastResult = {
|
||||
step,
|
||||
count: 1,
|
||||
duration: step.duration
|
||||
};
|
||||
result.push(lastResult);
|
||||
if (!canDedupe) lastResult = undefined;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function createSnippets(stepsInFile) {
|
||||
for (const file of stepsInFile.keys()) {
|
||||
let source;
|
||||
try {
|
||||
source = _fs.default.readFileSync(file, 'utf-8') + '\n//';
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
const lines = source.split('\n').length;
|
||||
const highlighted = (0, _babelBundle.codeFrameColumns)(source, {
|
||||
start: {
|
||||
line: lines,
|
||||
column: 1
|
||||
}
|
||||
}, {
|
||||
highlightCode: true,
|
||||
linesAbove: lines,
|
||||
linesBelow: 0
|
||||
});
|
||||
const highlightedLines = highlighted.split('\n');
|
||||
const lineWithArrow = highlightedLines[highlightedLines.length - 1];
|
||||
for (const step of stepsInFile.get(file)) {
|
||||
// Don't bother with snippets that have less than 3 lines.
|
||||
if (step.location.line < 2 || step.location.line >= lines) continue;
|
||||
// Cut out snippet.
|
||||
const snippetLines = highlightedLines.slice(step.location.line - 2, step.location.line + 1);
|
||||
// Relocate arrow.
|
||||
const index = lineWithArrow.indexOf('^');
|
||||
const shiftedArrow = lineWithArrow.slice(0, index) + ' '.repeat(step.location.column - 1) + lineWithArrow.slice(index);
|
||||
// Insert arrow line.
|
||||
snippetLines.splice(2, 0, shiftedArrow);
|
||||
step.snippet = snippetLines.join('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
var _default = exports.default = HtmlReporter;
|
||||
134
node_modules/playwright/lib/reporters/internalReporter.js
generated
vendored
Normal file
134
node_modules/playwright/lib/reporters/internalReporter.js
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.InternalReporter = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _babelBundle = require("../transform/babelBundle");
|
||||
var _test = require("../common/test");
|
||||
var _base = require("./base");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _multiplexer = require("./multiplexer");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class InternalReporter {
|
||||
constructor(reporters) {
|
||||
this._reporter = void 0;
|
||||
this._didBegin = false;
|
||||
this._config = void 0;
|
||||
this._startTime = void 0;
|
||||
this._monotonicStartTime = void 0;
|
||||
this._reporter = new _multiplexer.Multiplexer(reporters);
|
||||
}
|
||||
version() {
|
||||
return 'v2';
|
||||
}
|
||||
onConfigure(config) {
|
||||
var _this$_reporter$onCon, _this$_reporter;
|
||||
this._config = config;
|
||||
this._startTime = new Date();
|
||||
this._monotonicStartTime = (0, _utils.monotonicTime)();
|
||||
(_this$_reporter$onCon = (_this$_reporter = this._reporter).onConfigure) === null || _this$_reporter$onCon === void 0 || _this$_reporter$onCon.call(_this$_reporter, config);
|
||||
}
|
||||
onBegin(suite) {
|
||||
var _this$_reporter$onBeg, _this$_reporter2;
|
||||
this._didBegin = true;
|
||||
(_this$_reporter$onBeg = (_this$_reporter2 = this._reporter).onBegin) === null || _this$_reporter$onBeg === void 0 || _this$_reporter$onBeg.call(_this$_reporter2, suite);
|
||||
}
|
||||
onTestBegin(test, result) {
|
||||
var _this$_reporter$onTes, _this$_reporter3;
|
||||
(_this$_reporter$onTes = (_this$_reporter3 = this._reporter).onTestBegin) === null || _this$_reporter$onTes === void 0 || _this$_reporter$onTes.call(_this$_reporter3, test, result);
|
||||
}
|
||||
onStdOut(chunk, test, result) {
|
||||
var _this$_reporter$onStd, _this$_reporter4;
|
||||
(_this$_reporter$onStd = (_this$_reporter4 = this._reporter).onStdOut) === null || _this$_reporter$onStd === void 0 || _this$_reporter$onStd.call(_this$_reporter4, chunk, test, result);
|
||||
}
|
||||
onStdErr(chunk, test, result) {
|
||||
var _this$_reporter$onStd2, _this$_reporter5;
|
||||
(_this$_reporter$onStd2 = (_this$_reporter5 = this._reporter).onStdErr) === null || _this$_reporter$onStd2 === void 0 || _this$_reporter$onStd2.call(_this$_reporter5, chunk, test, result);
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
var _this$_reporter$onTes2, _this$_reporter6;
|
||||
this._addSnippetToTestErrors(test, result);
|
||||
(_this$_reporter$onTes2 = (_this$_reporter6 = this._reporter).onTestEnd) === null || _this$_reporter$onTes2 === void 0 || _this$_reporter$onTes2.call(_this$_reporter6, test, result);
|
||||
}
|
||||
async onEnd(result) {
|
||||
var _this$_reporter$onEnd, _this$_reporter7;
|
||||
if (!this._didBegin) {
|
||||
// onBegin was not reported, emit it.
|
||||
this.onBegin(new _test.Suite('', 'root'));
|
||||
}
|
||||
return await ((_this$_reporter$onEnd = (_this$_reporter7 = this._reporter).onEnd) === null || _this$_reporter$onEnd === void 0 ? void 0 : _this$_reporter$onEnd.call(_this$_reporter7, {
|
||||
...result,
|
||||
startTime: this._startTime,
|
||||
duration: (0, _utils.monotonicTime)() - this._monotonicStartTime
|
||||
}));
|
||||
}
|
||||
async onExit() {
|
||||
var _this$_reporter$onExi, _this$_reporter8;
|
||||
await ((_this$_reporter$onExi = (_this$_reporter8 = this._reporter).onExit) === null || _this$_reporter$onExi === void 0 ? void 0 : _this$_reporter$onExi.call(_this$_reporter8));
|
||||
}
|
||||
onError(error) {
|
||||
var _this$_reporter$onErr, _this$_reporter9;
|
||||
addLocationAndSnippetToError(this._config, error);
|
||||
(_this$_reporter$onErr = (_this$_reporter9 = this._reporter).onError) === null || _this$_reporter$onErr === void 0 || _this$_reporter$onErr.call(_this$_reporter9, error);
|
||||
}
|
||||
onStepBegin(test, result, step) {
|
||||
var _this$_reporter$onSte, _this$_reporter10;
|
||||
(_this$_reporter$onSte = (_this$_reporter10 = this._reporter).onStepBegin) === null || _this$_reporter$onSte === void 0 || _this$_reporter$onSte.call(_this$_reporter10, test, result, step);
|
||||
}
|
||||
onStepEnd(test, result, step) {
|
||||
var _this$_reporter$onSte2, _this$_reporter11;
|
||||
this._addSnippetToStepError(test, step);
|
||||
(_this$_reporter$onSte2 = (_this$_reporter11 = this._reporter).onStepEnd) === null || _this$_reporter$onSte2 === void 0 || _this$_reporter$onSte2.call(_this$_reporter11, test, result, step);
|
||||
}
|
||||
printsToStdio() {
|
||||
return this._reporter.printsToStdio ? this._reporter.printsToStdio() : true;
|
||||
}
|
||||
_addSnippetToTestErrors(test, result) {
|
||||
for (const error of result.errors) addLocationAndSnippetToError(this._config, error, test.location.file);
|
||||
}
|
||||
_addSnippetToStepError(test, step) {
|
||||
if (step.error) addLocationAndSnippetToError(this._config, step.error, test.location.file);
|
||||
}
|
||||
}
|
||||
exports.InternalReporter = InternalReporter;
|
||||
function addLocationAndSnippetToError(config, error, file) {
|
||||
if (error.stack && !error.location) error.location = (0, _base.prepareErrorStack)(error.stack).location;
|
||||
const location = error.location;
|
||||
if (!location) return;
|
||||
try {
|
||||
const tokens = [];
|
||||
const source = _fs.default.readFileSync(location.file, 'utf8');
|
||||
const codeFrame = (0, _babelBundle.codeFrameColumns)(source, {
|
||||
start: location
|
||||
}, {
|
||||
highlightCode: true
|
||||
});
|
||||
// Convert /var/folders to /private/var/folders on Mac.
|
||||
if (!file || _fs.default.realpathSync(file) !== location.file) {
|
||||
tokens.push(_base.colors.gray(` at `) + `${(0, _base.relativeFilePath)(config, location.file)}:${location.line}`);
|
||||
tokens.push('');
|
||||
}
|
||||
tokens.push(codeFrame);
|
||||
error.snippet = tokens.join('\n');
|
||||
} catch (e) {
|
||||
// Failed to read the source file - that's ok.
|
||||
}
|
||||
}
|
||||
244
node_modules/playwright/lib/reporters/json.js
generated
vendored
Normal file
244
node_modules/playwright/lib/reporters/json.js
generated
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
exports.serializePatterns = serializePatterns;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _base = require("./base");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _config = require("../common/config");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class JSONReporter {
|
||||
constructor(options) {
|
||||
var _resolveOutputFile;
|
||||
this.config = void 0;
|
||||
this.suite = void 0;
|
||||
this._errors = [];
|
||||
this._resolvedOutputFile = void 0;
|
||||
this._resolvedOutputFile = (_resolveOutputFile = (0, _base.resolveOutputFile)('JSON', options)) === null || _resolveOutputFile === void 0 ? void 0 : _resolveOutputFile.outputFile;
|
||||
}
|
||||
version() {
|
||||
return 'v2';
|
||||
}
|
||||
printsToStdio() {
|
||||
return !this._resolvedOutputFile;
|
||||
}
|
||||
onConfigure(config) {
|
||||
this.config = config;
|
||||
}
|
||||
onBegin(suite) {
|
||||
this.suite = suite;
|
||||
}
|
||||
onError(error) {
|
||||
this._errors.push(error);
|
||||
}
|
||||
async onEnd(result) {
|
||||
await outputReport(this._serializeReport(result), this._resolvedOutputFile);
|
||||
}
|
||||
_serializeReport(result) {
|
||||
const report = {
|
||||
config: {
|
||||
...removePrivateFields(this.config),
|
||||
rootDir: (0, _utils.toPosixPath)(this.config.rootDir),
|
||||
projects: this.config.projects.map(project => {
|
||||
return {
|
||||
outputDir: (0, _utils.toPosixPath)(project.outputDir),
|
||||
repeatEach: project.repeatEach,
|
||||
retries: project.retries,
|
||||
metadata: project.metadata,
|
||||
id: (0, _config.getProjectId)(project),
|
||||
name: project.name,
|
||||
testDir: (0, _utils.toPosixPath)(project.testDir),
|
||||
testIgnore: serializePatterns(project.testIgnore),
|
||||
testMatch: serializePatterns(project.testMatch),
|
||||
timeout: project.timeout
|
||||
};
|
||||
})
|
||||
},
|
||||
suites: this._mergeSuites(this.suite.suites),
|
||||
errors: this._errors,
|
||||
stats: {
|
||||
startTime: result.startTime.toISOString(),
|
||||
duration: result.duration,
|
||||
expected: 0,
|
||||
skipped: 0,
|
||||
unexpected: 0,
|
||||
flaky: 0
|
||||
}
|
||||
};
|
||||
for (const test of this.suite.allTests()) ++report.stats[test.outcome()];
|
||||
return report;
|
||||
}
|
||||
_mergeSuites(suites) {
|
||||
const fileSuites = new _utils.MultiMap();
|
||||
for (const projectSuite of suites) {
|
||||
const projectId = (0, _config.getProjectId)(projectSuite.project());
|
||||
const projectName = projectSuite.project().name;
|
||||
for (const fileSuite of projectSuite.suites) {
|
||||
const file = fileSuite.location.file;
|
||||
const serialized = this._serializeSuite(projectId, projectName, fileSuite);
|
||||
if (serialized) fileSuites.set(file, serialized);
|
||||
}
|
||||
}
|
||||
const results = [];
|
||||
for (const [, suites] of fileSuites) {
|
||||
const result = {
|
||||
title: suites[0].title,
|
||||
file: suites[0].file,
|
||||
column: 0,
|
||||
line: 0,
|
||||
specs: []
|
||||
};
|
||||
for (const suite of suites) this._mergeTestsFromSuite(result, suite);
|
||||
results.push(result);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
_relativeLocation(location) {
|
||||
if (!location) return {
|
||||
file: '',
|
||||
line: 0,
|
||||
column: 0
|
||||
};
|
||||
return {
|
||||
file: (0, _utils.toPosixPath)(_path.default.relative(this.config.rootDir, location.file)),
|
||||
line: location.line,
|
||||
column: location.column
|
||||
};
|
||||
}
|
||||
_locationMatches(s1, s2) {
|
||||
return s1.file === s2.file && s1.line === s2.line && s1.column === s2.column;
|
||||
}
|
||||
_mergeTestsFromSuite(to, from) {
|
||||
for (const fromSuite of from.suites || []) {
|
||||
const toSuite = (to.suites || []).find(s => s.title === fromSuite.title && this._locationMatches(s, fromSuite));
|
||||
if (toSuite) {
|
||||
this._mergeTestsFromSuite(toSuite, fromSuite);
|
||||
} else {
|
||||
if (!to.suites) to.suites = [];
|
||||
to.suites.push(fromSuite);
|
||||
}
|
||||
}
|
||||
for (const spec of from.specs || []) {
|
||||
const toSpec = to.specs.find(s => s.title === spec.title && s.file === (0, _utils.toPosixPath)(_path.default.relative(this.config.rootDir, spec.file)) && s.line === spec.line && s.column === spec.column);
|
||||
if (toSpec) toSpec.tests.push(...spec.tests);else to.specs.push(spec);
|
||||
}
|
||||
}
|
||||
_serializeSuite(projectId, projectName, suite) {
|
||||
if (!suite.allTests().length) return null;
|
||||
const suites = suite.suites.map(suite => this._serializeSuite(projectId, projectName, suite)).filter(s => s);
|
||||
return {
|
||||
title: suite.title,
|
||||
...this._relativeLocation(suite.location),
|
||||
specs: suite.tests.map(test => this._serializeTestSpec(projectId, projectName, test)),
|
||||
suites: suites.length ? suites : undefined
|
||||
};
|
||||
}
|
||||
_serializeTestSpec(projectId, projectName, test) {
|
||||
return {
|
||||
title: test.title,
|
||||
ok: test.ok(),
|
||||
tags: test.tags.map(tag => tag.substring(1)),
|
||||
// Strip '@'.
|
||||
tests: [this._serializeTest(projectId, projectName, test)],
|
||||
id: test.id,
|
||||
...this._relativeLocation(test.location)
|
||||
};
|
||||
}
|
||||
_serializeTest(projectId, projectName, test) {
|
||||
return {
|
||||
timeout: test.timeout,
|
||||
annotations: test.annotations,
|
||||
expectedStatus: test.expectedStatus,
|
||||
projectId,
|
||||
projectName,
|
||||
results: test.results.map(r => this._serializeTestResult(r, test)),
|
||||
status: test.outcome()
|
||||
};
|
||||
}
|
||||
_serializeTestResult(result, test) {
|
||||
var _result$error;
|
||||
const steps = result.steps.filter(s => s.category === 'test.step');
|
||||
const jsonResult = {
|
||||
workerIndex: result.workerIndex,
|
||||
status: result.status,
|
||||
duration: result.duration,
|
||||
error: result.error,
|
||||
errors: result.errors.map(e => this._serializeError(e)),
|
||||
stdout: result.stdout.map(s => stdioEntry(s)),
|
||||
stderr: result.stderr.map(s => stdioEntry(s)),
|
||||
retry: result.retry,
|
||||
steps: steps.length ? steps.map(s => this._serializeTestStep(s)) : undefined,
|
||||
startTime: result.startTime.toISOString(),
|
||||
attachments: result.attachments.map(a => {
|
||||
var _a$body;
|
||||
return {
|
||||
name: a.name,
|
||||
contentType: a.contentType,
|
||||
path: a.path,
|
||||
body: (_a$body = a.body) === null || _a$body === void 0 ? void 0 : _a$body.toString('base64')
|
||||
};
|
||||
})
|
||||
};
|
||||
if ((_result$error = result.error) !== null && _result$error !== void 0 && _result$error.stack) jsonResult.errorLocation = (0, _base.prepareErrorStack)(result.error.stack).location;
|
||||
return jsonResult;
|
||||
}
|
||||
_serializeError(error) {
|
||||
return (0, _base.formatError)(error, true);
|
||||
}
|
||||
_serializeTestStep(step) {
|
||||
const steps = step.steps.filter(s => s.category === 'test.step');
|
||||
return {
|
||||
title: step.title,
|
||||
duration: step.duration,
|
||||
error: step.error,
|
||||
steps: steps.length ? steps.map(s => this._serializeTestStep(s)) : undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
async function outputReport(report, resolvedOutputFile) {
|
||||
const reportString = JSON.stringify(report, undefined, 2);
|
||||
if (resolvedOutputFile) {
|
||||
await _fs.default.promises.mkdir(_path.default.dirname(resolvedOutputFile), {
|
||||
recursive: true
|
||||
});
|
||||
await _fs.default.promises.writeFile(resolvedOutputFile, reportString);
|
||||
} else {
|
||||
console.log(reportString);
|
||||
}
|
||||
}
|
||||
function stdioEntry(s) {
|
||||
if (typeof s === 'string') return {
|
||||
text: s
|
||||
};
|
||||
return {
|
||||
buffer: s.toString('base64')
|
||||
};
|
||||
}
|
||||
function removePrivateFields(config) {
|
||||
return Object.fromEntries(Object.entries(config).filter(([name, value]) => !name.startsWith('_')));
|
||||
}
|
||||
function serializePatterns(patterns) {
|
||||
if (!Array.isArray(patterns)) patterns = [patterns];
|
||||
return patterns.map(s => s.toString());
|
||||
}
|
||||
var _default = exports.default = JSONReporter;
|
||||
234
node_modules/playwright/lib/reporters/junit.js
generated
vendored
Normal file
234
node_modules/playwright/lib/reporters/junit.js
generated
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _base = require("./base");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class JUnitReporter {
|
||||
constructor(options) {
|
||||
var _resolveOutputFile;
|
||||
this.config = void 0;
|
||||
this.configDir = void 0;
|
||||
this.suite = void 0;
|
||||
this.timestamp = void 0;
|
||||
this.totalTests = 0;
|
||||
this.totalFailures = 0;
|
||||
this.totalSkipped = 0;
|
||||
this.resolvedOutputFile = void 0;
|
||||
this.stripANSIControlSequences = false;
|
||||
this.includeProjectInTestName = false;
|
||||
this.stripANSIControlSequences = (0, _utils.getAsBooleanFromENV)('PLAYWRIGHT_JUNIT_STRIP_ANSI', !!options.stripANSIControlSequences);
|
||||
this.includeProjectInTestName = (0, _utils.getAsBooleanFromENV)('PLAYWRIGHT_JUNIT_INCLUDE_PROJECT_IN_TEST_NAME', !!options.includeProjectInTestName);
|
||||
this.configDir = options.configDir;
|
||||
this.resolvedOutputFile = (_resolveOutputFile = (0, _base.resolveOutputFile)('JUNIT', options)) === null || _resolveOutputFile === void 0 ? void 0 : _resolveOutputFile.outputFile;
|
||||
}
|
||||
version() {
|
||||
return 'v2';
|
||||
}
|
||||
printsToStdio() {
|
||||
return !this.resolvedOutputFile;
|
||||
}
|
||||
onConfigure(config) {
|
||||
this.config = config;
|
||||
}
|
||||
onBegin(suite) {
|
||||
this.suite = suite;
|
||||
this.timestamp = new Date();
|
||||
}
|
||||
async onEnd(result) {
|
||||
const children = [];
|
||||
for (const projectSuite of this.suite.suites) {
|
||||
for (const fileSuite of projectSuite.suites) children.push(await this._buildTestSuite(projectSuite.title, fileSuite));
|
||||
}
|
||||
const tokens = [];
|
||||
const self = this;
|
||||
const root = {
|
||||
name: 'testsuites',
|
||||
attributes: {
|
||||
id: process.env[`PLAYWRIGHT_JUNIT_SUITE_ID`] || '',
|
||||
name: process.env[`PLAYWRIGHT_JUNIT_SUITE_NAME`] || '',
|
||||
tests: self.totalTests,
|
||||
failures: self.totalFailures,
|
||||
skipped: self.totalSkipped,
|
||||
errors: 0,
|
||||
time: result.duration / 1000
|
||||
},
|
||||
children
|
||||
};
|
||||
serializeXML(root, tokens, this.stripANSIControlSequences);
|
||||
const reportString = tokens.join('\n');
|
||||
if (this.resolvedOutputFile) {
|
||||
await _fs.default.promises.mkdir(_path.default.dirname(this.resolvedOutputFile), {
|
||||
recursive: true
|
||||
});
|
||||
await _fs.default.promises.writeFile(this.resolvedOutputFile, reportString);
|
||||
} else {
|
||||
console.log(reportString);
|
||||
}
|
||||
}
|
||||
async _buildTestSuite(projectName, suite) {
|
||||
let tests = 0;
|
||||
let skipped = 0;
|
||||
let failures = 0;
|
||||
let duration = 0;
|
||||
const children = [];
|
||||
const testCaseNamePrefix = projectName && this.includeProjectInTestName ? `[${projectName}] ` : '';
|
||||
for (const test of suite.allTests()) {
|
||||
++tests;
|
||||
if (test.outcome() === 'skipped') ++skipped;
|
||||
if (!test.ok()) ++failures;
|
||||
for (const result of test.results) duration += result.duration;
|
||||
await this._addTestCase(suite.title, testCaseNamePrefix, test, children);
|
||||
}
|
||||
this.totalTests += tests;
|
||||
this.totalSkipped += skipped;
|
||||
this.totalFailures += failures;
|
||||
const entry = {
|
||||
name: 'testsuite',
|
||||
attributes: {
|
||||
name: suite.title,
|
||||
timestamp: this.timestamp.toISOString(),
|
||||
hostname: projectName,
|
||||
tests,
|
||||
failures,
|
||||
skipped,
|
||||
time: duration / 1000,
|
||||
errors: 0
|
||||
},
|
||||
children
|
||||
};
|
||||
return entry;
|
||||
}
|
||||
async _addTestCase(suiteName, namePrefix, test, entries) {
|
||||
var _properties$children2;
|
||||
const entry = {
|
||||
name: 'testcase',
|
||||
attributes: {
|
||||
// Skip root, project, file
|
||||
name: namePrefix + test.titlePath().slice(3).join(' › '),
|
||||
// filename
|
||||
classname: suiteName,
|
||||
time: test.results.reduce((acc, value) => acc + value.duration, 0) / 1000
|
||||
},
|
||||
children: []
|
||||
};
|
||||
entries.push(entry);
|
||||
|
||||
// Xray Test Management supports testcase level properties, where additional metadata may be provided
|
||||
// some annotations are encoded as value attributes, other as cdata content; this implementation supports
|
||||
// Xray JUnit extensions but it also agnostic, so other tools can also take advantage of this format
|
||||
const properties = {
|
||||
name: 'properties',
|
||||
children: []
|
||||
};
|
||||
for (const annotation of test.annotations) {
|
||||
var _properties$children;
|
||||
const property = {
|
||||
name: 'property',
|
||||
attributes: {
|
||||
name: annotation.type,
|
||||
value: annotation !== null && annotation !== void 0 && annotation.description ? annotation.description : ''
|
||||
}
|
||||
};
|
||||
(_properties$children = properties.children) === null || _properties$children === void 0 || _properties$children.push(property);
|
||||
}
|
||||
if ((_properties$children2 = properties.children) !== null && _properties$children2 !== void 0 && _properties$children2.length) entry.children.push(properties);
|
||||
if (test.outcome() === 'skipped') {
|
||||
entry.children.push({
|
||||
name: 'skipped'
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!test.ok()) {
|
||||
entry.children.push({
|
||||
name: 'failure',
|
||||
attributes: {
|
||||
message: `${_path.default.basename(test.location.file)}:${test.location.line}:${test.location.column} ${test.title}`,
|
||||
type: 'FAILURE'
|
||||
},
|
||||
text: (0, _base.stripAnsiEscapes)((0, _base.formatFailure)(this.config, test))
|
||||
});
|
||||
}
|
||||
const systemOut = [];
|
||||
const systemErr = [];
|
||||
for (const result of test.results) {
|
||||
systemOut.push(...result.stdout.map(item => item.toString()));
|
||||
systemErr.push(...result.stderr.map(item => item.toString()));
|
||||
for (const attachment of result.attachments) {
|
||||
if (!attachment.path) continue;
|
||||
let attachmentPath = _path.default.relative(this.configDir, attachment.path);
|
||||
try {
|
||||
if (this.resolvedOutputFile) attachmentPath = _path.default.relative(_path.default.dirname(this.resolvedOutputFile), attachment.path);
|
||||
} catch {
|
||||
systemOut.push(`\nWarning: Unable to make attachment path ${attachment.path} relative to report output file ${this.resolvedOutputFile}`);
|
||||
}
|
||||
try {
|
||||
await _fs.default.promises.access(attachment.path);
|
||||
systemOut.push(`\n[[ATTACHMENT|${attachmentPath}]]\n`);
|
||||
} catch {
|
||||
systemErr.push(`\nWarning: attachment ${attachmentPath} is missing`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Note: it is important to only produce a single system-out/system-err entry
|
||||
// so that parsers in the wild understand it.
|
||||
if (systemOut.length) entry.children.push({
|
||||
name: 'system-out',
|
||||
text: systemOut.join('')
|
||||
});
|
||||
if (systemErr.length) entry.children.push({
|
||||
name: 'system-err',
|
||||
text: systemErr.join('')
|
||||
});
|
||||
}
|
||||
}
|
||||
function serializeXML(entry, tokens, stripANSIControlSequences) {
|
||||
const attrs = [];
|
||||
for (const [name, value] of Object.entries(entry.attributes || {})) attrs.push(`${name}="${escape(String(value), stripANSIControlSequences, false)}"`);
|
||||
tokens.push(`<${entry.name}${attrs.length ? ' ' : ''}${attrs.join(' ')}>`);
|
||||
for (const child of entry.children || []) serializeXML(child, tokens, stripANSIControlSequences);
|
||||
if (entry.text) tokens.push(escape(entry.text, stripANSIControlSequences, true));
|
||||
tokens.push(`</${entry.name}>`);
|
||||
}
|
||||
|
||||
// See https://en.wikipedia.org/wiki/Valid_characters_in_XML
|
||||
const discouragedXMLCharacters = /[\u0000-\u0008\u000b-\u000c\u000e-\u001f\u007f-\u0084\u0086-\u009f]/g;
|
||||
function escape(text, stripANSIControlSequences, isCharacterData) {
|
||||
if (stripANSIControlSequences) text = (0, _base.stripAnsiEscapes)(text);
|
||||
if (isCharacterData) {
|
||||
text = '<![CDATA[' + text.replace(/]]>/g, ']]>') + ']]>';
|
||||
} else {
|
||||
const escapeRe = /[&"'<>]/g;
|
||||
text = text.replace(escapeRe, c => ({
|
||||
'&': '&',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'<': '<',
|
||||
'>': '>'
|
||||
})[c]);
|
||||
}
|
||||
text = text.replace(discouragedXMLCharacters, '');
|
||||
return text;
|
||||
}
|
||||
var _default = exports.default = JUnitReporter;
|
||||
100
node_modules/playwright/lib/reporters/line.js
generated
vendored
Normal file
100
node_modules/playwright/lib/reporters/line.js
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _base = require("./base");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class LineReporter extends _base.BaseReporter {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this._current = 0;
|
||||
this._failures = 0;
|
||||
this._lastTest = void 0;
|
||||
this._didBegin = false;
|
||||
}
|
||||
onBegin(suite) {
|
||||
super.onBegin(suite);
|
||||
const startingMessage = this.generateStartingMessage();
|
||||
if (startingMessage) {
|
||||
console.log(startingMessage);
|
||||
console.log();
|
||||
}
|
||||
this._didBegin = true;
|
||||
}
|
||||
onStdOut(chunk, test, result) {
|
||||
super.onStdOut(chunk, test, result);
|
||||
this._dumpToStdio(test, chunk, process.stdout);
|
||||
}
|
||||
onStdErr(chunk, test, result) {
|
||||
super.onStdErr(chunk, test, result);
|
||||
this._dumpToStdio(test, chunk, process.stderr);
|
||||
}
|
||||
_dumpToStdio(test, chunk, stream) {
|
||||
if (this.config.quiet) return;
|
||||
if (!process.env.PW_TEST_DEBUG_REPORTERS) stream.write(`\u001B[1A\u001B[2K`);
|
||||
if (test && this._lastTest !== test) {
|
||||
// Write new header for the output.
|
||||
const title = _base.colors.dim((0, _base.formatTestTitle)(this.config, test));
|
||||
stream.write(this.fitToScreen(title) + `\n`);
|
||||
this._lastTest = test;
|
||||
}
|
||||
stream.write(chunk);
|
||||
if (chunk[chunk.length - 1] !== '\n') console.log();
|
||||
console.log();
|
||||
}
|
||||
onTestBegin(test, result) {
|
||||
++this._current;
|
||||
this._updateLine(test, result, undefined);
|
||||
}
|
||||
onStepBegin(test, result, step) {
|
||||
if (step.category === 'test.step') this._updateLine(test, result, step);
|
||||
}
|
||||
onStepEnd(test, result, step) {
|
||||
if (step.category === 'test.step') this._updateLine(test, result, step.parent);
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
super.onTestEnd(test, result);
|
||||
if (!this.willRetry(test) && (test.outcome() === 'flaky' || test.outcome() === 'unexpected' || result.status === 'interrupted')) {
|
||||
if (!process.env.PW_TEST_DEBUG_REPORTERS) process.stdout.write(`\u001B[1A\u001B[2K`);
|
||||
console.log((0, _base.formatFailure)(this.config, test, ++this._failures));
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
_updateLine(test, result, step) {
|
||||
const retriesPrefix = this.totalTestCount < this._current ? ` (retries)` : ``;
|
||||
const prefix = `[${this._current}/${this.totalTestCount}]${retriesPrefix} `;
|
||||
const currentRetrySuffix = result.retry ? _base.colors.yellow(` (retry #${result.retry})`) : '';
|
||||
const title = (0, _base.formatTestTitle)(this.config, test, step) + currentRetrySuffix;
|
||||
if (process.env.PW_TEST_DEBUG_REPORTERS) process.stdout.write(`${prefix + title}\n`);else process.stdout.write(`\u001B[1A\u001B[2K${prefix + this.fitToScreen(title, prefix)}\n`);
|
||||
}
|
||||
onError(error) {
|
||||
super.onError(error);
|
||||
const message = (0, _base.formatError)(error, _base.colors.enabled).message + '\n';
|
||||
if (!process.env.PW_TEST_DEBUG_REPORTERS && this._didBegin) process.stdout.write(`\u001B[1A\u001B[2K`);
|
||||
process.stdout.write(message);
|
||||
console.log();
|
||||
}
|
||||
async onEnd(result) {
|
||||
if (!process.env.PW_TEST_DEBUG_REPORTERS && this._didBegin) process.stdout.write(`\u001B[1A\u001B[2K`);
|
||||
await super.onEnd(result);
|
||||
this.epilogue(false);
|
||||
}
|
||||
}
|
||||
var _default = exports.default = LineReporter;
|
||||
216
node_modules/playwright/lib/reporters/list.js
generated
vendored
Normal file
216
node_modules/playwright/lib/reporters/list.js
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _base = require("./base");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Allow it in the Visual Studio Code Terminal and the new Windows Terminal
|
||||
const DOES_NOT_SUPPORT_UTF8_IN_TERMINAL = process.platform === 'win32' && process.env.TERM_PROGRAM !== 'vscode' && !process.env.WT_SESSION;
|
||||
const POSITIVE_STATUS_MARK = DOES_NOT_SUPPORT_UTF8_IN_TERMINAL ? 'ok' : '✓';
|
||||
const NEGATIVE_STATUS_MARK = DOES_NOT_SUPPORT_UTF8_IN_TERMINAL ? 'x' : '✘';
|
||||
class ListReporter extends _base.BaseReporter {
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
this._lastRow = 0;
|
||||
this._lastColumn = 0;
|
||||
this._testRows = new Map();
|
||||
this._stepRows = new Map();
|
||||
this._resultIndex = new Map();
|
||||
this._stepIndex = new Map();
|
||||
this._needNewLine = false;
|
||||
this._printSteps = void 0;
|
||||
this._printSteps = (0, _utils.getAsBooleanFromENV)('PLAYWRIGHT_LIST_PRINT_STEPS', options.printSteps);
|
||||
}
|
||||
onBegin(suite) {
|
||||
super.onBegin(suite);
|
||||
const startingMessage = this.generateStartingMessage();
|
||||
if (startingMessage) {
|
||||
console.log(startingMessage);
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
onTestBegin(test, result) {
|
||||
const index = String(this._resultIndex.size + 1);
|
||||
this._resultIndex.set(result, index);
|
||||
if (!_base.isTTY) return;
|
||||
this._maybeWriteNewLine();
|
||||
this._testRows.set(test, this._lastRow);
|
||||
const prefix = this._testPrefix(index, '');
|
||||
const line = _base.colors.dim((0, _base.formatTestTitle)(this.config, test)) + this._retrySuffix(result);
|
||||
this._appendLine(line, prefix);
|
||||
}
|
||||
onStdOut(chunk, test, result) {
|
||||
super.onStdOut(chunk, test, result);
|
||||
this._dumpToStdio(test, chunk, process.stdout);
|
||||
}
|
||||
onStdErr(chunk, test, result) {
|
||||
super.onStdErr(chunk, test, result);
|
||||
this._dumpToStdio(test, chunk, process.stderr);
|
||||
}
|
||||
getStepIndex(testIndex, result, step) {
|
||||
if (this._stepIndex.has(step)) return this._stepIndex.get(step);
|
||||
const ordinal = (result[lastStepOrdinalSymbol] || 0) + 1;
|
||||
result[lastStepOrdinalSymbol] = ordinal;
|
||||
const stepIndex = `${testIndex}.${ordinal}`;
|
||||
this._stepIndex.set(step, stepIndex);
|
||||
return stepIndex;
|
||||
}
|
||||
onStepBegin(test, result, step) {
|
||||
if (step.category !== 'test.step') return;
|
||||
const testIndex = this._resultIndex.get(result) || '';
|
||||
if (!_base.isTTY) return;
|
||||
if (this._printSteps) {
|
||||
this._maybeWriteNewLine();
|
||||
this._stepRows.set(step, this._lastRow);
|
||||
const prefix = this._testPrefix(this.getStepIndex(testIndex, result, step), '');
|
||||
const line = test.title + _base.colors.dim((0, _base.stepSuffix)(step));
|
||||
this._appendLine(line, prefix);
|
||||
} else {
|
||||
this._updateLine(this._testRows.get(test), _base.colors.dim((0, _base.formatTestTitle)(this.config, test, step)) + this._retrySuffix(result), this._testPrefix(testIndex, ''));
|
||||
}
|
||||
}
|
||||
onStepEnd(test, result, step) {
|
||||
if (step.category !== 'test.step') return;
|
||||
const testIndex = this._resultIndex.get(result) || '';
|
||||
if (!this._printSteps) {
|
||||
if (_base.isTTY) this._updateLine(this._testRows.get(test), _base.colors.dim((0, _base.formatTestTitle)(this.config, test, step.parent)) + this._retrySuffix(result), this._testPrefix(testIndex, ''));
|
||||
return;
|
||||
}
|
||||
const index = this.getStepIndex(testIndex, result, step);
|
||||
const title = _base.isTTY ? test.title + _base.colors.dim((0, _base.stepSuffix)(step)) : (0, _base.formatTestTitle)(this.config, test, step);
|
||||
const prefix = this._testPrefix(index, '');
|
||||
let text = '';
|
||||
if (step.error) text = _base.colors.red(title);else text = title;
|
||||
text += _base.colors.dim(` (${(0, _utilsBundle.ms)(step.duration)})`);
|
||||
this._updateOrAppendLine(this._stepRows.get(step), text, prefix);
|
||||
}
|
||||
_maybeWriteNewLine() {
|
||||
if (this._needNewLine) {
|
||||
this._needNewLine = false;
|
||||
process.stdout.write('\n');
|
||||
}
|
||||
}
|
||||
_updateLineCountAndNewLineFlagForOutput(text) {
|
||||
this._needNewLine = text[text.length - 1] !== '\n';
|
||||
if (!_base.ttyWidth) return;
|
||||
for (const ch of text) {
|
||||
if (ch === '\n') {
|
||||
this._lastColumn = 0;
|
||||
++this._lastRow;
|
||||
continue;
|
||||
}
|
||||
++this._lastColumn;
|
||||
if (this._lastColumn > _base.ttyWidth) {
|
||||
this._lastColumn = 0;
|
||||
++this._lastRow;
|
||||
}
|
||||
}
|
||||
}
|
||||
_dumpToStdio(test, chunk, stream) {
|
||||
if (this.config.quiet) return;
|
||||
const text = chunk.toString('utf-8');
|
||||
this._updateLineCountAndNewLineFlagForOutput(text);
|
||||
stream.write(chunk);
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
super.onTestEnd(test, result);
|
||||
const title = (0, _base.formatTestTitle)(this.config, test);
|
||||
let prefix = '';
|
||||
let text = '';
|
||||
|
||||
// In TTY mode test index is incremented in onTestStart
|
||||
// and in non-TTY mode it is incremented onTestEnd.
|
||||
let index = this._resultIndex.get(result);
|
||||
if (!index) {
|
||||
index = String(this._resultIndex.size + 1);
|
||||
this._resultIndex.set(result, index);
|
||||
}
|
||||
if (result.status === 'skipped') {
|
||||
prefix = this._testPrefix(index, _base.colors.green('-'));
|
||||
// Do not show duration for skipped.
|
||||
text = _base.colors.cyan(title) + this._retrySuffix(result);
|
||||
} else {
|
||||
const statusMark = result.status === 'passed' ? POSITIVE_STATUS_MARK : NEGATIVE_STATUS_MARK;
|
||||
if (result.status === test.expectedStatus) {
|
||||
prefix = this._testPrefix(index, _base.colors.green(statusMark));
|
||||
text = title;
|
||||
} else {
|
||||
prefix = this._testPrefix(index, _base.colors.red(statusMark));
|
||||
text = _base.colors.red(title);
|
||||
}
|
||||
text += this._retrySuffix(result) + _base.colors.dim(` (${(0, _utilsBundle.ms)(result.duration)})`);
|
||||
}
|
||||
this._updateOrAppendLine(this._testRows.get(test), text, prefix);
|
||||
}
|
||||
_updateOrAppendLine(row, text, prefix) {
|
||||
if (_base.isTTY) {
|
||||
this._updateLine(row, text, prefix);
|
||||
} else {
|
||||
this._maybeWriteNewLine();
|
||||
this._appendLine(text, prefix);
|
||||
}
|
||||
}
|
||||
_appendLine(text, prefix) {
|
||||
const line = prefix + this.fitToScreen(text, prefix);
|
||||
if (process.env.PW_TEST_DEBUG_REPORTERS) {
|
||||
process.stdout.write('#' + this._lastRow + ' : ' + line + '\n');
|
||||
} else {
|
||||
process.stdout.write(line);
|
||||
process.stdout.write('\n');
|
||||
}
|
||||
++this._lastRow;
|
||||
}
|
||||
_updateLine(row, text, prefix) {
|
||||
const line = prefix + this.fitToScreen(text, prefix);
|
||||
if (process.env.PW_TEST_DEBUG_REPORTERS) process.stdout.write('#' + row + ' : ' + line + '\n');else this._updateLineForTTY(row, line);
|
||||
}
|
||||
_updateLineForTTY(row, line) {
|
||||
// Go up if needed
|
||||
if (row !== this._lastRow) process.stdout.write(`\u001B[${this._lastRow - row}A`);
|
||||
// Erase line, go to the start
|
||||
process.stdout.write('\u001B[2K\u001B[0G');
|
||||
process.stdout.write(line);
|
||||
// Go down if needed.
|
||||
if (row !== this._lastRow) process.stdout.write(`\u001B[${this._lastRow - row}E`);
|
||||
}
|
||||
_testPrefix(index, statusMark) {
|
||||
const statusMarkLength = (0, _base.stripAnsiEscapes)(statusMark).length;
|
||||
return ' ' + statusMark + ' '.repeat(3 - statusMarkLength) + _base.colors.dim(index + ' ');
|
||||
}
|
||||
_retrySuffix(result) {
|
||||
return result.retry ? _base.colors.yellow(` (retry #${result.retry})`) : '';
|
||||
}
|
||||
onError(error) {
|
||||
super.onError(error);
|
||||
this._maybeWriteNewLine();
|
||||
const message = (0, _base.formatError)(error, _base.colors.enabled).message + '\n';
|
||||
this._updateLineCountAndNewLineFlagForOutput(message);
|
||||
process.stdout.write(message);
|
||||
}
|
||||
async onEnd(result) {
|
||||
await super.onEnd(result);
|
||||
process.stdout.write('\n');
|
||||
this.epilogue(true);
|
||||
}
|
||||
}
|
||||
const lastStepOrdinalSymbol = Symbol('lastStepOrdinal');
|
||||
var _default = exports.default = ListReporter;
|
||||
76
node_modules/playwright/lib/reporters/markdown.js
generated
vendored
Normal file
76
node_modules/playwright/lib/reporters/markdown.js
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _util = require("../util");
|
||||
var _base = require("./base");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class MarkdownReporter extends _base.BaseReporter {
|
||||
constructor(options) {
|
||||
super();
|
||||
this._options = void 0;
|
||||
this._options = options;
|
||||
}
|
||||
printsToStdio() {
|
||||
return false;
|
||||
}
|
||||
async onEnd(result) {
|
||||
await super.onEnd(result);
|
||||
const summary = this.generateSummary();
|
||||
const lines = [];
|
||||
if (summary.fatalErrors.length) lines.push(`**${summary.fatalErrors.length} fatal errors, not part of any test**`);
|
||||
if (summary.unexpected.length) {
|
||||
lines.push(`**${summary.unexpected.length} failed**`);
|
||||
this._printTestList(':x:', summary.unexpected, lines);
|
||||
}
|
||||
if (summary.flaky.length) {
|
||||
lines.push(`<details>`);
|
||||
lines.push(`<summary><b>${summary.flaky.length} flaky</b></summary>`);
|
||||
this._printTestList(':warning:', summary.flaky, lines, ' <br/>');
|
||||
lines.push(`</details>`);
|
||||
lines.push(``);
|
||||
}
|
||||
if (summary.interrupted.length) {
|
||||
lines.push(`<details>`);
|
||||
lines.push(`<summary><b>${summary.interrupted.length} interrupted</b></summary>`);
|
||||
this._printTestList(':warning:', summary.interrupted, lines, ' <br/>');
|
||||
lines.push(`</details>`);
|
||||
lines.push(``);
|
||||
}
|
||||
const skipped = summary.skipped ? `, ${summary.skipped} skipped` : '';
|
||||
const didNotRun = summary.didNotRun ? `, ${summary.didNotRun} did not run` : '';
|
||||
lines.push(`**${summary.expected} passed${skipped}${didNotRun}**`);
|
||||
lines.push(`:heavy_check_mark::heavy_check_mark::heavy_check_mark:`);
|
||||
lines.push(``);
|
||||
const reportFile = (0, _util.resolveReporterOutputPath)('report.md', this._options.configDir, this._options.outputFile);
|
||||
await _fs.default.promises.mkdir(_path.default.dirname(reportFile), {
|
||||
recursive: true
|
||||
});
|
||||
await _fs.default.promises.writeFile(reportFile, lines.join('\n'));
|
||||
}
|
||||
_printTestList(prefix, tests, lines, suffix) {
|
||||
for (const test of tests) lines.push(`${prefix} ${(0, _base.formatTestTitle)(this.config, test)}${suffix || ''}`);
|
||||
lines.push(``);
|
||||
}
|
||||
}
|
||||
var _default = exports.default = MarkdownReporter;
|
||||
488
node_modules/playwright/lib/reporters/merge.js
generated
vendored
Normal file
488
node_modules/playwright/lib/reporters/merge.js
generated
vendored
Normal file
@@ -0,0 +1,488 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.createMergedReport = createMergedReport;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _teleReceiver = require("../isomorphic/teleReceiver");
|
||||
var _stringInternPool = require("../isomorphic/stringInternPool");
|
||||
var _reporters = require("../runner/reporters");
|
||||
var _multiplexer = require("./multiplexer");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _blob = require("./blob");
|
||||
var _util = require("../util");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
async function createMergedReport(config, dir, reporterDescriptions, rootDirOverride) {
|
||||
var _eventData$pathSepara;
|
||||
const reporters = await (0, _reporters.createReporters)(config, 'merge', false, reporterDescriptions);
|
||||
const multiplexer = new _multiplexer.Multiplexer(reporters);
|
||||
const stringPool = new _stringInternPool.StringInternPool();
|
||||
let printStatus = () => {};
|
||||
if (!multiplexer.printsToStdio()) {
|
||||
printStatus = printStatusToStdout;
|
||||
printStatus(`merging reports from ${dir}`);
|
||||
}
|
||||
const shardFiles = await sortedShardFiles(dir);
|
||||
if (shardFiles.length === 0) throw new Error(`No report files found in ${dir}`);
|
||||
const eventData = await mergeEvents(dir, shardFiles, stringPool, printStatus, rootDirOverride);
|
||||
// If explicit config is provided, use platform path separator, otherwise use the one from the report (if any).
|
||||
const pathSeparator = rootDirOverride ? _path.default.sep : (_eventData$pathSepara = eventData.pathSeparatorFromMetadata) !== null && _eventData$pathSepara !== void 0 ? _eventData$pathSepara : _path.default.sep;
|
||||
const receiver = new _teleReceiver.TeleReporterReceiver(multiplexer, {
|
||||
mergeProjects: false,
|
||||
mergeTestCases: false,
|
||||
resolvePath: (rootDir, relativePath) => stringPool.internString(rootDir + pathSeparator + relativePath),
|
||||
configOverrides: config.config
|
||||
});
|
||||
printStatus(`processing test events`);
|
||||
const dispatchEvents = async events => {
|
||||
for (const event of events) {
|
||||
if (event.method === 'onEnd') printStatus(`building final report`);
|
||||
await receiver.dispatch(event);
|
||||
if (event.method === 'onEnd') printStatus(`finished building report`);
|
||||
}
|
||||
};
|
||||
await dispatchEvents(eventData.prologue);
|
||||
for (const {
|
||||
reportFile,
|
||||
eventPatchers,
|
||||
metadata
|
||||
} of eventData.reports) {
|
||||
const reportJsonl = await _fs.default.promises.readFile(reportFile);
|
||||
const events = parseTestEvents(reportJsonl);
|
||||
new _stringInternPool.JsonStringInternalizer(stringPool).traverse(events);
|
||||
eventPatchers.patchers.push(new AttachmentPathPatcher(dir));
|
||||
if (metadata.name) eventPatchers.patchers.push(new GlobalErrorPatcher(metadata.name));
|
||||
eventPatchers.patchEvents(events);
|
||||
await dispatchEvents(events);
|
||||
}
|
||||
await dispatchEvents(eventData.epilogue);
|
||||
}
|
||||
const commonEventNames = ['onBlobReportMetadata', 'onConfigure', 'onProject', 'onBegin', 'onEnd'];
|
||||
const commonEvents = new Set(commonEventNames);
|
||||
const commonEventRegex = new RegExp(`${commonEventNames.join('|')}`);
|
||||
function parseCommonEvents(reportJsonl) {
|
||||
return splitBufferLines(reportJsonl).map(line => line.toString('utf8')).filter(line => commonEventRegex.test(line)) // quick filter
|
||||
.map(line => JSON.parse(line)).filter(event => commonEvents.has(event.method));
|
||||
}
|
||||
function parseTestEvents(reportJsonl) {
|
||||
return splitBufferLines(reportJsonl).map(line => line.toString('utf8')).filter(line => line.length).map(line => JSON.parse(line)).filter(event => !commonEvents.has(event.method));
|
||||
}
|
||||
function splitBufferLines(buffer) {
|
||||
const lines = [];
|
||||
let start = 0;
|
||||
while (start < buffer.length) {
|
||||
// 0x0A is the byte for '\n'
|
||||
const end = buffer.indexOf(0x0A, start);
|
||||
if (end === -1) {
|
||||
lines.push(buffer.slice(start));
|
||||
break;
|
||||
}
|
||||
lines.push(buffer.slice(start, end));
|
||||
start = end + 1;
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
async function extractAndParseReports(dir, shardFiles, internalizer, printStatus) {
|
||||
const shardEvents = [];
|
||||
await _fs.default.promises.mkdir(_path.default.join(dir, 'resources'), {
|
||||
recursive: true
|
||||
});
|
||||
const reportNames = new UniqueFileNameGenerator();
|
||||
for (const file of shardFiles) {
|
||||
const absolutePath = _path.default.join(dir, file);
|
||||
printStatus(`extracting: ${(0, _util.relativeFilePath)(absolutePath)}`);
|
||||
const zipFile = new _utils.ZipFile(absolutePath);
|
||||
const entryNames = await zipFile.entries();
|
||||
for (const entryName of entryNames.sort()) {
|
||||
let fileName = _path.default.join(dir, entryName);
|
||||
const content = await zipFile.read(entryName);
|
||||
if (entryName.endsWith('.jsonl')) {
|
||||
fileName = reportNames.makeUnique(fileName);
|
||||
let parsedEvents = parseCommonEvents(content);
|
||||
// Passing reviver to JSON.parse doesn't work, as the original strings
|
||||
// keep being used. To work around that we traverse the parsed events
|
||||
// as a post-processing step.
|
||||
internalizer.traverse(parsedEvents);
|
||||
const metadata = findMetadata(parsedEvents, file);
|
||||
parsedEvents = modernizer.modernize(metadata.version, parsedEvents);
|
||||
shardEvents.push({
|
||||
file,
|
||||
localPath: fileName,
|
||||
metadata,
|
||||
parsedEvents
|
||||
});
|
||||
}
|
||||
await _fs.default.promises.writeFile(fileName, content);
|
||||
}
|
||||
zipFile.close();
|
||||
}
|
||||
return shardEvents;
|
||||
}
|
||||
function findMetadata(events, file) {
|
||||
var _events$;
|
||||
if (((_events$ = events[0]) === null || _events$ === void 0 ? void 0 : _events$.method) !== 'onBlobReportMetadata') throw new Error(`No metadata event found in ${file}`);
|
||||
const metadata = events[0].params;
|
||||
if (metadata.version > _blob.currentBlobReportVersion) throw new Error(`Blob report ${file} was created with a newer version of Playwright.`);
|
||||
return metadata;
|
||||
}
|
||||
async function mergeEvents(dir, shardReportFiles, stringPool, printStatus, rootDirOverride) {
|
||||
var _blobs$;
|
||||
const internalizer = new _stringInternPool.JsonStringInternalizer(stringPool);
|
||||
const configureEvents = [];
|
||||
const projectEvents = [];
|
||||
const endEvents = [];
|
||||
const blobs = await extractAndParseReports(dir, shardReportFiles, internalizer, printStatus);
|
||||
// Sort by (report name; shard; file name), so that salt generation below is deterministic when:
|
||||
// - report names are unique;
|
||||
// - report names are missing;
|
||||
// - report names are clashing between shards.
|
||||
blobs.sort((a, b) => {
|
||||
var _a$metadata$name, _b$metadata$name, _a$metadata$shard$cur, _a$metadata$shard, _b$metadata$shard$cur, _b$metadata$shard;
|
||||
const nameA = (_a$metadata$name = a.metadata.name) !== null && _a$metadata$name !== void 0 ? _a$metadata$name : '';
|
||||
const nameB = (_b$metadata$name = b.metadata.name) !== null && _b$metadata$name !== void 0 ? _b$metadata$name : '';
|
||||
if (nameA !== nameB) return nameA.localeCompare(nameB);
|
||||
const shardA = (_a$metadata$shard$cur = (_a$metadata$shard = a.metadata.shard) === null || _a$metadata$shard === void 0 ? void 0 : _a$metadata$shard.current) !== null && _a$metadata$shard$cur !== void 0 ? _a$metadata$shard$cur : 0;
|
||||
const shardB = (_b$metadata$shard$cur = (_b$metadata$shard = b.metadata.shard) === null || _b$metadata$shard === void 0 ? void 0 : _b$metadata$shard.current) !== null && _b$metadata$shard$cur !== void 0 ? _b$metadata$shard$cur : 0;
|
||||
if (shardA !== shardB) return shardA - shardB;
|
||||
return a.file.localeCompare(b.file);
|
||||
});
|
||||
printStatus(`merging events`);
|
||||
const reports = [];
|
||||
const globalTestIdSet = new Set();
|
||||
for (let i = 0; i < blobs.length; ++i) {
|
||||
// Generate unique salt for each blob.
|
||||
const {
|
||||
parsedEvents,
|
||||
metadata,
|
||||
localPath
|
||||
} = blobs[i];
|
||||
const eventPatchers = new JsonEventPatchers();
|
||||
eventPatchers.patchers.push(new IdsPatcher(stringPool, metadata.name, String(i), globalTestIdSet));
|
||||
// Only patch path separators if we are merging reports with explicit config.
|
||||
if (rootDirOverride) eventPatchers.patchers.push(new PathSeparatorPatcher(metadata.pathSeparator));
|
||||
eventPatchers.patchEvents(parsedEvents);
|
||||
for (const event of parsedEvents) {
|
||||
if (event.method === 'onConfigure') configureEvents.push(event);else if (event.method === 'onProject') projectEvents.push(event);else if (event.method === 'onEnd') endEvents.push(event);
|
||||
}
|
||||
|
||||
// Save information about the reports to stream their test events later.
|
||||
reports.push({
|
||||
eventPatchers,
|
||||
reportFile: localPath,
|
||||
metadata
|
||||
});
|
||||
}
|
||||
return {
|
||||
prologue: [mergeConfigureEvents(configureEvents, rootDirOverride), ...projectEvents, {
|
||||
method: 'onBegin',
|
||||
params: undefined
|
||||
}],
|
||||
reports,
|
||||
epilogue: [mergeEndEvents(endEvents), {
|
||||
method: 'onExit',
|
||||
params: undefined
|
||||
}],
|
||||
pathSeparatorFromMetadata: (_blobs$ = blobs[0]) === null || _blobs$ === void 0 ? void 0 : _blobs$.metadata.pathSeparator
|
||||
};
|
||||
}
|
||||
function mergeConfigureEvents(configureEvents, rootDirOverride) {
|
||||
if (!configureEvents.length) throw new Error('No configure events found');
|
||||
let config = {
|
||||
configFile: undefined,
|
||||
globalTimeout: 0,
|
||||
maxFailures: 0,
|
||||
metadata: {},
|
||||
rootDir: '',
|
||||
version: '',
|
||||
workers: 0
|
||||
};
|
||||
for (const event of configureEvents) config = mergeConfigs(config, event.params.config);
|
||||
if (rootDirOverride) {
|
||||
config.rootDir = rootDirOverride;
|
||||
} else {
|
||||
const rootDirs = new Set(configureEvents.map(e => e.params.config.rootDir));
|
||||
if (rootDirs.size > 1) {
|
||||
throw new Error([`Blob reports being merged were recorded with different test directories, and`, `merging cannot proceed. This may happen if you are merging reports from`, `machines with different environments, like different operating systems or`, `if the tests ran with different playwright configs.`, ``, `You can force merge by specifying a merge config file with "-c" option. If`, `you'd like all test paths to be correct, make sure 'testDir' in the merge config`, `file points to the actual tests location.`, ``, `Found directories:`, ...rootDirs].join('\n'));
|
||||
}
|
||||
}
|
||||
return {
|
||||
method: 'onConfigure',
|
||||
params: {
|
||||
config
|
||||
}
|
||||
};
|
||||
}
|
||||
function mergeConfigs(to, from) {
|
||||
return {
|
||||
...to,
|
||||
...from,
|
||||
metadata: {
|
||||
...to.metadata,
|
||||
...from.metadata,
|
||||
actualWorkers: (to.metadata.actualWorkers || 0) + (from.metadata.actualWorkers || 0)
|
||||
},
|
||||
workers: to.workers + from.workers
|
||||
};
|
||||
}
|
||||
function mergeEndEvents(endEvents) {
|
||||
let startTime = endEvents.length ? 10000000000000 : Date.now();
|
||||
let status = 'passed';
|
||||
let duration = 0;
|
||||
for (const event of endEvents) {
|
||||
const shardResult = event.params.result;
|
||||
if (shardResult.status === 'failed') status = 'failed';else if (shardResult.status === 'timedout' && status !== 'failed') status = 'timedout';else if (shardResult.status === 'interrupted' && status !== 'failed' && status !== 'timedout') status = 'interrupted';
|
||||
startTime = Math.min(startTime, shardResult.startTime);
|
||||
duration = Math.max(duration, shardResult.duration);
|
||||
}
|
||||
const result = {
|
||||
status,
|
||||
startTime,
|
||||
duration
|
||||
};
|
||||
return {
|
||||
method: 'onEnd',
|
||||
params: {
|
||||
result
|
||||
}
|
||||
};
|
||||
}
|
||||
async function sortedShardFiles(dir) {
|
||||
const files = await _fs.default.promises.readdir(dir);
|
||||
return files.filter(file => file.endsWith('.zip')).sort();
|
||||
}
|
||||
function printStatusToStdout(message) {
|
||||
process.stdout.write(`${message}\n`);
|
||||
}
|
||||
class UniqueFileNameGenerator {
|
||||
constructor() {
|
||||
this._usedNames = new Set();
|
||||
}
|
||||
makeUnique(name) {
|
||||
if (!this._usedNames.has(name)) {
|
||||
this._usedNames.add(name);
|
||||
return name;
|
||||
}
|
||||
const extension = _path.default.extname(name);
|
||||
name = name.substring(0, name.length - extension.length);
|
||||
let index = 0;
|
||||
while (true) {
|
||||
const candidate = `${name}-${++index}${extension}`;
|
||||
if (!this._usedNames.has(candidate)) {
|
||||
this._usedNames.add(candidate);
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
class IdsPatcher {
|
||||
constructor(stringPool, botName, salt, globalTestIdSet) {
|
||||
this._stringPool = void 0;
|
||||
this._botName = void 0;
|
||||
this._salt = void 0;
|
||||
this._testIdsMap = void 0;
|
||||
this._globalTestIdSet = void 0;
|
||||
this._stringPool = stringPool;
|
||||
this._botName = botName;
|
||||
this._salt = salt;
|
||||
this._testIdsMap = new Map();
|
||||
this._globalTestIdSet = globalTestIdSet;
|
||||
}
|
||||
patchEvent(event) {
|
||||
const {
|
||||
method,
|
||||
params
|
||||
} = event;
|
||||
switch (method) {
|
||||
case 'onProject':
|
||||
this._onProject(params.project);
|
||||
return;
|
||||
case 'onTestBegin':
|
||||
case 'onStepBegin':
|
||||
case 'onStepEnd':
|
||||
case 'onStdIO':
|
||||
params.testId = this._mapTestId(params.testId);
|
||||
return;
|
||||
case 'onTestEnd':
|
||||
params.test.testId = this._mapTestId(params.test.testId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_onProject(project) {
|
||||
var _project$metadata;
|
||||
(_project$metadata = project.metadata) !== null && _project$metadata !== void 0 ? _project$metadata : project.metadata = {};
|
||||
project.suites.forEach(suite => this._updateTestIds(suite));
|
||||
}
|
||||
_updateTestIds(suite) {
|
||||
suite.entries.forEach(entry => {
|
||||
if ('testId' in entry) this._updateTestId(entry);else this._updateTestIds(entry);
|
||||
});
|
||||
}
|
||||
_updateTestId(test) {
|
||||
test.testId = this._mapTestId(test.testId);
|
||||
if (this._botName) {
|
||||
test.tags = test.tags || [];
|
||||
test.tags.unshift('@' + this._botName);
|
||||
}
|
||||
}
|
||||
_mapTestId(testId) {
|
||||
const t1 = this._stringPool.internString(testId);
|
||||
if (this._testIdsMap.has(t1))
|
||||
// already mapped
|
||||
return this._testIdsMap.get(t1);
|
||||
if (this._globalTestIdSet.has(t1)) {
|
||||
// test id is used in another blob, so we need to salt it.
|
||||
const t2 = this._stringPool.internString(testId + this._salt);
|
||||
this._globalTestIdSet.add(t2);
|
||||
this._testIdsMap.set(t1, t2);
|
||||
return t2;
|
||||
}
|
||||
this._globalTestIdSet.add(t1);
|
||||
this._testIdsMap.set(t1, t1);
|
||||
return t1;
|
||||
}
|
||||
}
|
||||
class AttachmentPathPatcher {
|
||||
constructor(_resourceDir) {
|
||||
this._resourceDir = _resourceDir;
|
||||
}
|
||||
patchEvent(event) {
|
||||
if (event.method !== 'onTestEnd') return;
|
||||
for (const attachment of event.params.result.attachments) {
|
||||
if (!attachment.path) continue;
|
||||
attachment.path = _path.default.join(this._resourceDir, attachment.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
class PathSeparatorPatcher {
|
||||
constructor(from) {
|
||||
this._from = void 0;
|
||||
this._to = void 0;
|
||||
this._from = from !== null && from !== void 0 ? from : _path.default.sep === '/' ? '\\' : '/';
|
||||
this._to = _path.default.sep;
|
||||
}
|
||||
patchEvent(jsonEvent) {
|
||||
if (this._from === this._to) return;
|
||||
if (jsonEvent.method === 'onProject') {
|
||||
this._updateProject(jsonEvent.params.project);
|
||||
return;
|
||||
}
|
||||
if (jsonEvent.method === 'onTestEnd') {
|
||||
const testResult = jsonEvent.params.result;
|
||||
testResult.errors.forEach(error => this._updateErrorLocations(error));
|
||||
testResult.attachments.forEach(attachment => {
|
||||
if (attachment.path) attachment.path = this._updatePath(attachment.path);
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (jsonEvent.method === 'onStepBegin') {
|
||||
const step = jsonEvent.params.step;
|
||||
this._updateLocation(step.location);
|
||||
return;
|
||||
}
|
||||
if (jsonEvent.method === 'onStepEnd') {
|
||||
const step = jsonEvent.params.step;
|
||||
this._updateErrorLocations(step.error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_updateProject(project) {
|
||||
project.outputDir = this._updatePath(project.outputDir);
|
||||
project.testDir = this._updatePath(project.testDir);
|
||||
project.snapshotDir = this._updatePath(project.snapshotDir);
|
||||
project.suites.forEach(suite => this._updateSuite(suite, true));
|
||||
}
|
||||
_updateSuite(suite, isFileSuite = false) {
|
||||
this._updateLocation(suite.location);
|
||||
if (isFileSuite) suite.title = this._updatePath(suite.title);
|
||||
for (const entry of suite.entries) {
|
||||
if ('testId' in entry) this._updateLocation(entry.location);else this._updateSuite(entry);
|
||||
}
|
||||
}
|
||||
_updateErrorLocations(error) {
|
||||
while (error) {
|
||||
this._updateLocation(error.location);
|
||||
error = error.cause;
|
||||
}
|
||||
}
|
||||
_updateLocation(location) {
|
||||
if (location) location.file = this._updatePath(location.file);
|
||||
}
|
||||
_updatePath(text) {
|
||||
return text.split(this._from).join(this._to);
|
||||
}
|
||||
}
|
||||
class GlobalErrorPatcher {
|
||||
constructor(botName) {
|
||||
this._prefix = void 0;
|
||||
this._prefix = `(${botName}) `;
|
||||
}
|
||||
patchEvent(event) {
|
||||
if (event.method !== 'onError') return;
|
||||
const error = event.params.error;
|
||||
if (error.message !== undefined) error.message = this._prefix + error.message;
|
||||
if (error.stack !== undefined) error.stack = this._prefix + error.stack;
|
||||
}
|
||||
}
|
||||
class JsonEventPatchers {
|
||||
constructor() {
|
||||
this.patchers = [];
|
||||
}
|
||||
patchEvents(events) {
|
||||
for (const event of events) {
|
||||
for (const patcher of this.patchers) patcher.patchEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
class BlobModernizer {
|
||||
modernize(fromVersion, events) {
|
||||
const result = [];
|
||||
for (const event of events) result.push(...this._modernize(fromVersion, event));
|
||||
return result;
|
||||
}
|
||||
_modernize(fromVersion, event) {
|
||||
let events = [event];
|
||||
for (let version = fromVersion; version < _blob.currentBlobReportVersion; ++version) events = this[`_modernize_${version}_to_${version + 1}`].call(this, events);
|
||||
return events;
|
||||
}
|
||||
_modernize_1_to_2(events) {
|
||||
return events.map(event => {
|
||||
if (event.method === 'onProject') {
|
||||
const modernizeSuite = suite => {
|
||||
const newSuites = suite.suites.map(modernizeSuite);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const {
|
||||
suites,
|
||||
tests,
|
||||
...remainder
|
||||
} = suite;
|
||||
return {
|
||||
entries: [...newSuites, ...tests],
|
||||
...remainder
|
||||
};
|
||||
};
|
||||
const project = event.params.project;
|
||||
project.suites = project.suites.map(modernizeSuite);
|
||||
}
|
||||
return event;
|
||||
});
|
||||
}
|
||||
}
|
||||
const modernizer = new BlobModernizer();
|
||||
123
node_modules/playwright/lib/reporters/multiplexer.js
generated
vendored
Normal file
123
node_modules/playwright/lib/reporters/multiplexer.js
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Multiplexer = void 0;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Multiplexer {
|
||||
constructor(reporters) {
|
||||
this._reporters = void 0;
|
||||
this._reporters = reporters;
|
||||
}
|
||||
version() {
|
||||
return 'v2';
|
||||
}
|
||||
onConfigure(config) {
|
||||
for (const reporter of this._reporters) wrap(() => {
|
||||
var _reporter$onConfigure;
|
||||
return (_reporter$onConfigure = reporter.onConfigure) === null || _reporter$onConfigure === void 0 ? void 0 : _reporter$onConfigure.call(reporter, config);
|
||||
});
|
||||
}
|
||||
onBegin(suite) {
|
||||
for (const reporter of this._reporters) wrap(() => {
|
||||
var _reporter$onBegin;
|
||||
return (_reporter$onBegin = reporter.onBegin) === null || _reporter$onBegin === void 0 ? void 0 : _reporter$onBegin.call(reporter, suite);
|
||||
});
|
||||
}
|
||||
onTestBegin(test, result) {
|
||||
for (const reporter of this._reporters) wrap(() => {
|
||||
var _reporter$onTestBegin;
|
||||
return (_reporter$onTestBegin = reporter.onTestBegin) === null || _reporter$onTestBegin === void 0 ? void 0 : _reporter$onTestBegin.call(reporter, test, result);
|
||||
});
|
||||
}
|
||||
onStdOut(chunk, test, result) {
|
||||
for (const reporter of this._reporters) wrap(() => {
|
||||
var _reporter$onStdOut;
|
||||
return (_reporter$onStdOut = reporter.onStdOut) === null || _reporter$onStdOut === void 0 ? void 0 : _reporter$onStdOut.call(reporter, chunk, test, result);
|
||||
});
|
||||
}
|
||||
onStdErr(chunk, test, result) {
|
||||
for (const reporter of this._reporters) wrap(() => {
|
||||
var _reporter$onStdErr;
|
||||
return (_reporter$onStdErr = reporter.onStdErr) === null || _reporter$onStdErr === void 0 ? void 0 : _reporter$onStdErr.call(reporter, chunk, test, result);
|
||||
});
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
for (const reporter of this._reporters) wrap(() => {
|
||||
var _reporter$onTestEnd;
|
||||
return (_reporter$onTestEnd = reporter.onTestEnd) === null || _reporter$onTestEnd === void 0 ? void 0 : _reporter$onTestEnd.call(reporter, test, result);
|
||||
});
|
||||
}
|
||||
async onEnd(result) {
|
||||
for (const reporter of this._reporters) {
|
||||
const outResult = await wrapAsync(() => {
|
||||
var _reporter$onEnd;
|
||||
return (_reporter$onEnd = reporter.onEnd) === null || _reporter$onEnd === void 0 ? void 0 : _reporter$onEnd.call(reporter, result);
|
||||
});
|
||||
if (outResult !== null && outResult !== void 0 && outResult.status) result.status = outResult.status;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
async onExit() {
|
||||
for (const reporter of this._reporters) await wrapAsync(() => {
|
||||
var _reporter$onExit;
|
||||
return (_reporter$onExit = reporter.onExit) === null || _reporter$onExit === void 0 ? void 0 : _reporter$onExit.call(reporter);
|
||||
});
|
||||
}
|
||||
onError(error) {
|
||||
for (const reporter of this._reporters) wrap(() => {
|
||||
var _reporter$onError;
|
||||
return (_reporter$onError = reporter.onError) === null || _reporter$onError === void 0 ? void 0 : _reporter$onError.call(reporter, error);
|
||||
});
|
||||
}
|
||||
onStepBegin(test, result, step) {
|
||||
for (const reporter of this._reporters) wrap(() => {
|
||||
var _reporter$onStepBegin;
|
||||
return (_reporter$onStepBegin = reporter.onStepBegin) === null || _reporter$onStepBegin === void 0 ? void 0 : _reporter$onStepBegin.call(reporter, test, result, step);
|
||||
});
|
||||
}
|
||||
onStepEnd(test, result, step) {
|
||||
for (const reporter of this._reporters) wrap(() => {
|
||||
var _reporter$onStepEnd;
|
||||
return (_reporter$onStepEnd = reporter.onStepEnd) === null || _reporter$onStepEnd === void 0 ? void 0 : _reporter$onStepEnd.call(reporter, test, result, step);
|
||||
});
|
||||
}
|
||||
printsToStdio() {
|
||||
return this._reporters.some(r => {
|
||||
let prints = false;
|
||||
wrap(() => prints = r.printsToStdio ? r.printsToStdio() : true);
|
||||
return prints;
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.Multiplexer = Multiplexer;
|
||||
async function wrapAsync(callback) {
|
||||
try {
|
||||
return await callback();
|
||||
} catch (e) {
|
||||
console.error('Error in reporter', e);
|
||||
}
|
||||
}
|
||||
function wrap(callback) {
|
||||
try {
|
||||
callback();
|
||||
} catch (e) {
|
||||
console.error('Error in reporter', e);
|
||||
}
|
||||
}
|
||||
118
node_modules/playwright/lib/reporters/reporterV2.js
generated
vendored
Normal file
118
node_modules/playwright/lib/reporters/reporterV2.js
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.wrapReporterAsV2 = wrapReporterAsV2;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function wrapReporterAsV2(reporter) {
|
||||
try {
|
||||
if ('version' in reporter && reporter.version() === 'v2') return reporter;
|
||||
} catch (e) {}
|
||||
return new ReporterV2Wrapper(reporter);
|
||||
}
|
||||
class ReporterV2Wrapper {
|
||||
constructor(reporter) {
|
||||
this._reporter = void 0;
|
||||
this._deferred = [];
|
||||
this._config = void 0;
|
||||
this._reporter = reporter;
|
||||
}
|
||||
version() {
|
||||
return 'v2';
|
||||
}
|
||||
onConfigure(config) {
|
||||
this._config = config;
|
||||
}
|
||||
onBegin(suite) {
|
||||
var _this$_reporter$onBeg, _this$_reporter;
|
||||
(_this$_reporter$onBeg = (_this$_reporter = this._reporter).onBegin) === null || _this$_reporter$onBeg === void 0 || _this$_reporter$onBeg.call(_this$_reporter, this._config, suite);
|
||||
const deferred = this._deferred;
|
||||
this._deferred = null;
|
||||
for (const item of deferred) {
|
||||
if (item.error) this.onError(item.error);
|
||||
if (item.stdout) this.onStdOut(item.stdout.chunk, item.stdout.test, item.stdout.result);
|
||||
if (item.stderr) this.onStdErr(item.stderr.chunk, item.stderr.test, item.stderr.result);
|
||||
}
|
||||
}
|
||||
onTestBegin(test, result) {
|
||||
var _this$_reporter$onTes, _this$_reporter2;
|
||||
(_this$_reporter$onTes = (_this$_reporter2 = this._reporter).onTestBegin) === null || _this$_reporter$onTes === void 0 || _this$_reporter$onTes.call(_this$_reporter2, test, result);
|
||||
}
|
||||
onStdOut(chunk, test, result) {
|
||||
var _this$_reporter$onStd, _this$_reporter3;
|
||||
if (this._deferred) {
|
||||
this._deferred.push({
|
||||
stdout: {
|
||||
chunk,
|
||||
test,
|
||||
result
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
(_this$_reporter$onStd = (_this$_reporter3 = this._reporter).onStdOut) === null || _this$_reporter$onStd === void 0 || _this$_reporter$onStd.call(_this$_reporter3, chunk, test, result);
|
||||
}
|
||||
onStdErr(chunk, test, result) {
|
||||
var _this$_reporter$onStd2, _this$_reporter4;
|
||||
if (this._deferred) {
|
||||
this._deferred.push({
|
||||
stderr: {
|
||||
chunk,
|
||||
test,
|
||||
result
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
(_this$_reporter$onStd2 = (_this$_reporter4 = this._reporter).onStdErr) === null || _this$_reporter$onStd2 === void 0 || _this$_reporter$onStd2.call(_this$_reporter4, chunk, test, result);
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
var _this$_reporter$onTes2, _this$_reporter5;
|
||||
(_this$_reporter$onTes2 = (_this$_reporter5 = this._reporter).onTestEnd) === null || _this$_reporter$onTes2 === void 0 || _this$_reporter$onTes2.call(_this$_reporter5, test, result);
|
||||
}
|
||||
async onEnd(result) {
|
||||
var _this$_reporter$onEnd, _this$_reporter6;
|
||||
return await ((_this$_reporter$onEnd = (_this$_reporter6 = this._reporter).onEnd) === null || _this$_reporter$onEnd === void 0 ? void 0 : _this$_reporter$onEnd.call(_this$_reporter6, result));
|
||||
}
|
||||
async onExit() {
|
||||
var _this$_reporter$onExi, _this$_reporter7;
|
||||
await ((_this$_reporter$onExi = (_this$_reporter7 = this._reporter).onExit) === null || _this$_reporter$onExi === void 0 ? void 0 : _this$_reporter$onExi.call(_this$_reporter7));
|
||||
}
|
||||
onError(error) {
|
||||
var _this$_reporter$onErr, _this$_reporter8;
|
||||
if (this._deferred) {
|
||||
this._deferred.push({
|
||||
error
|
||||
});
|
||||
return;
|
||||
}
|
||||
(_this$_reporter$onErr = (_this$_reporter8 = this._reporter).onError) === null || _this$_reporter$onErr === void 0 || _this$_reporter$onErr.call(_this$_reporter8, error);
|
||||
}
|
||||
onStepBegin(test, result, step) {
|
||||
var _this$_reporter$onSte, _this$_reporter9;
|
||||
(_this$_reporter$onSte = (_this$_reporter9 = this._reporter).onStepBegin) === null || _this$_reporter$onSte === void 0 || _this$_reporter$onSte.call(_this$_reporter9, test, result, step);
|
||||
}
|
||||
onStepEnd(test, result, step) {
|
||||
var _this$_reporter$onSte2, _this$_reporter10;
|
||||
(_this$_reporter$onSte2 = (_this$_reporter10 = this._reporter).onStepEnd) === null || _this$_reporter$onSte2 === void 0 || _this$_reporter$onSte2.call(_this$_reporter10, test, result, step);
|
||||
}
|
||||
printsToStdio() {
|
||||
return this._reporter.printsToStdio ? this._reporter.printsToStdio() : true;
|
||||
}
|
||||
}
|
||||
267
node_modules/playwright/lib/reporters/teleEmitter.js
generated
vendored
Normal file
267
node_modules/playwright/lib/reporters/teleEmitter.js
generated
vendored
Normal file
@@ -0,0 +1,267 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.TeleReporterEmitter = void 0;
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _teleReceiver = require("../isomorphic/teleReceiver");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class TeleReporterEmitter {
|
||||
constructor(messageSink, options = {}) {
|
||||
this._messageSink = void 0;
|
||||
this._rootDir = void 0;
|
||||
this._emitterOptions = void 0;
|
||||
// In case there is blob reporter and UI mode, make sure one does override
|
||||
// the id assigned by the other.
|
||||
this._idSymbol = Symbol('id');
|
||||
this._messageSink = messageSink;
|
||||
this._emitterOptions = options;
|
||||
}
|
||||
version() {
|
||||
return 'v2';
|
||||
}
|
||||
onConfigure(config) {
|
||||
this._rootDir = config.rootDir;
|
||||
this._messageSink({
|
||||
method: 'onConfigure',
|
||||
params: {
|
||||
config: this._serializeConfig(config)
|
||||
}
|
||||
});
|
||||
}
|
||||
onBegin(suite) {
|
||||
const projects = suite.suites.map(projectSuite => this._serializeProject(projectSuite));
|
||||
for (const project of projects) this._messageSink({
|
||||
method: 'onProject',
|
||||
params: {
|
||||
project
|
||||
}
|
||||
});
|
||||
this._messageSink({
|
||||
method: 'onBegin',
|
||||
params: undefined
|
||||
});
|
||||
}
|
||||
onTestBegin(test, result) {
|
||||
result[this._idSymbol] = (0, _utils.createGuid)();
|
||||
this._messageSink({
|
||||
method: 'onTestBegin',
|
||||
params: {
|
||||
testId: test.id,
|
||||
result: this._serializeResultStart(result)
|
||||
}
|
||||
});
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
const testEnd = {
|
||||
testId: test.id,
|
||||
expectedStatus: test.expectedStatus,
|
||||
annotations: test.annotations,
|
||||
timeout: test.timeout
|
||||
};
|
||||
this._messageSink({
|
||||
method: 'onTestEnd',
|
||||
params: {
|
||||
test: testEnd,
|
||||
result: this._serializeResultEnd(result)
|
||||
}
|
||||
});
|
||||
}
|
||||
onStepBegin(test, result, step) {
|
||||
step[this._idSymbol] = (0, _utils.createGuid)();
|
||||
this._messageSink({
|
||||
method: 'onStepBegin',
|
||||
params: {
|
||||
testId: test.id,
|
||||
resultId: result[this._idSymbol],
|
||||
step: this._serializeStepStart(step)
|
||||
}
|
||||
});
|
||||
}
|
||||
onStepEnd(test, result, step) {
|
||||
this._messageSink({
|
||||
method: 'onStepEnd',
|
||||
params: {
|
||||
testId: test.id,
|
||||
resultId: result[this._idSymbol],
|
||||
step: this._serializeStepEnd(step)
|
||||
}
|
||||
});
|
||||
}
|
||||
onError(error) {
|
||||
this._messageSink({
|
||||
method: 'onError',
|
||||
params: {
|
||||
error
|
||||
}
|
||||
});
|
||||
}
|
||||
onStdOut(chunk, test, result) {
|
||||
this._onStdIO('stdout', chunk, test, result);
|
||||
}
|
||||
onStdErr(chunk, test, result) {
|
||||
this._onStdIO('stderr', chunk, test, result);
|
||||
}
|
||||
_onStdIO(type, chunk, test, result) {
|
||||
if (this._emitterOptions.omitOutput) return;
|
||||
const isBase64 = typeof chunk !== 'string';
|
||||
const data = isBase64 ? chunk.toString('base64') : chunk;
|
||||
this._messageSink({
|
||||
method: 'onStdIO',
|
||||
params: {
|
||||
testId: test === null || test === void 0 ? void 0 : test.id,
|
||||
resultId: result ? result[this._idSymbol] : undefined,
|
||||
type,
|
||||
data,
|
||||
isBase64
|
||||
}
|
||||
});
|
||||
}
|
||||
async onEnd(result) {
|
||||
const resultPayload = {
|
||||
status: result.status,
|
||||
startTime: result.startTime.getTime(),
|
||||
duration: result.duration
|
||||
};
|
||||
this._messageSink({
|
||||
method: 'onEnd',
|
||||
params: {
|
||||
result: resultPayload
|
||||
}
|
||||
});
|
||||
}
|
||||
printsToStdio() {
|
||||
return false;
|
||||
}
|
||||
_serializeConfig(config) {
|
||||
return {
|
||||
configFile: this._relativePath(config.configFile),
|
||||
globalTimeout: config.globalTimeout,
|
||||
maxFailures: config.maxFailures,
|
||||
metadata: config.metadata,
|
||||
rootDir: config.rootDir,
|
||||
version: config.version,
|
||||
workers: config.workers
|
||||
};
|
||||
}
|
||||
_serializeProject(suite) {
|
||||
const project = suite.project();
|
||||
const report = {
|
||||
metadata: project.metadata,
|
||||
name: project.name,
|
||||
outputDir: this._relativePath(project.outputDir),
|
||||
repeatEach: project.repeatEach,
|
||||
retries: project.retries,
|
||||
testDir: this._relativePath(project.testDir),
|
||||
testIgnore: (0, _teleReceiver.serializeRegexPatterns)(project.testIgnore),
|
||||
testMatch: (0, _teleReceiver.serializeRegexPatterns)(project.testMatch),
|
||||
timeout: project.timeout,
|
||||
suites: suite.suites.map(fileSuite => {
|
||||
return this._serializeSuite(fileSuite);
|
||||
}),
|
||||
grep: (0, _teleReceiver.serializeRegexPatterns)(project.grep),
|
||||
grepInvert: (0, _teleReceiver.serializeRegexPatterns)(project.grepInvert || []),
|
||||
dependencies: project.dependencies,
|
||||
snapshotDir: this._relativePath(project.snapshotDir),
|
||||
teardown: project.teardown
|
||||
};
|
||||
return report;
|
||||
}
|
||||
_serializeSuite(suite) {
|
||||
const result = {
|
||||
title: suite.title,
|
||||
location: this._relativeLocation(suite.location),
|
||||
entries: suite.entries().map(e => {
|
||||
if (e.type === 'test') return this._serializeTest(e);
|
||||
return this._serializeSuite(e);
|
||||
})
|
||||
};
|
||||
return result;
|
||||
}
|
||||
_serializeTest(test) {
|
||||
return {
|
||||
testId: test.id,
|
||||
title: test.title,
|
||||
location: this._relativeLocation(test.location),
|
||||
retries: test.retries,
|
||||
tags: test.tags,
|
||||
repeatEachIndex: test.repeatEachIndex,
|
||||
annotations: test.annotations
|
||||
};
|
||||
}
|
||||
_serializeResultStart(result) {
|
||||
return {
|
||||
id: result[this._idSymbol],
|
||||
retry: result.retry,
|
||||
workerIndex: result.workerIndex,
|
||||
parallelIndex: result.parallelIndex,
|
||||
startTime: +result.startTime
|
||||
};
|
||||
}
|
||||
_serializeResultEnd(result) {
|
||||
return {
|
||||
id: result[this._idSymbol],
|
||||
duration: result.duration,
|
||||
status: result.status,
|
||||
errors: result.errors,
|
||||
attachments: this._serializeAttachments(result.attachments)
|
||||
};
|
||||
}
|
||||
_serializeAttachments(attachments) {
|
||||
return attachments.map(a => {
|
||||
return {
|
||||
...a,
|
||||
// There is no Buffer in the browser, so there is no point in sending the data there.
|
||||
base64: a.body && !this._emitterOptions.omitBuffers ? a.body.toString('base64') : undefined
|
||||
};
|
||||
});
|
||||
}
|
||||
_serializeStepStart(step) {
|
||||
var _step$parent;
|
||||
return {
|
||||
id: step[this._idSymbol],
|
||||
parentStepId: (_step$parent = step.parent) === null || _step$parent === void 0 ? void 0 : _step$parent[this._idSymbol],
|
||||
title: step.title,
|
||||
category: step.category,
|
||||
startTime: +step.startTime,
|
||||
location: this._relativeLocation(step.location)
|
||||
};
|
||||
}
|
||||
_serializeStepEnd(step) {
|
||||
return {
|
||||
id: step[this._idSymbol],
|
||||
duration: step.duration,
|
||||
error: step.error
|
||||
};
|
||||
}
|
||||
_relativeLocation(location) {
|
||||
if (!location) return location;
|
||||
return {
|
||||
...location,
|
||||
file: this._relativePath(location.file)
|
||||
};
|
||||
}
|
||||
_relativePath(absolutePath) {
|
||||
if (!absolutePath) return absolutePath;
|
||||
return _path.default.relative(this._rootDir, absolutePath);
|
||||
}
|
||||
}
|
||||
exports.TeleReporterEmitter = TeleReporterEmitter;
|
||||
5
node_modules/playwright/lib/reporters/versions/blobV1.js
generated
vendored
Normal file
5
node_modules/playwright/lib/reporters/versions/blobV1.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
550
node_modules/playwright/lib/runner/dispatcher.js
generated
vendored
Normal file
550
node_modules/playwright/lib/runner/dispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,550 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Dispatcher = void 0;
|
||||
var _ipc = require("../common/ipc");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _workerHost = require("./workerHost");
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _rebase = require("./rebase");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Dispatcher {
|
||||
constructor(config, reporter, failureTracker) {
|
||||
this._workerSlots = [];
|
||||
this._queue = [];
|
||||
this._queuedOrRunningHashCount = new Map();
|
||||
this._finished = new _utils.ManualPromise();
|
||||
this._isStopped = true;
|
||||
this._config = void 0;
|
||||
this._reporter = void 0;
|
||||
this._failureTracker = void 0;
|
||||
this._extraEnvByProjectId = new Map();
|
||||
this._producedEnvByProjectId = new Map();
|
||||
this._config = config;
|
||||
this._reporter = reporter;
|
||||
this._failureTracker = failureTracker;
|
||||
}
|
||||
async _scheduleJob() {
|
||||
// 1. Find a job to run.
|
||||
if (this._isStopped || !this._queue.length) return;
|
||||
const job = this._queue[0];
|
||||
|
||||
// 2. Find a worker with the same hash, or just some free worker.
|
||||
let index = this._workerSlots.findIndex(w => !w.busy && w.worker && w.worker.hash() === job.workerHash && !w.worker.didSendStop());
|
||||
if (index === -1) index = this._workerSlots.findIndex(w => !w.busy);
|
||||
// No workers available, bail out.
|
||||
if (index === -1) return;
|
||||
|
||||
// 3. Claim both the job and the worker, run the job and release the worker.
|
||||
this._queue.shift();
|
||||
this._workerSlots[index].busy = true;
|
||||
await this._startJobInWorker(index, job);
|
||||
this._workerSlots[index].busy = false;
|
||||
|
||||
// 4. Check the "finished" condition.
|
||||
this._checkFinished();
|
||||
|
||||
// 5. We got a free worker - perhaps we can immediately start another job?
|
||||
void this._scheduleJob();
|
||||
}
|
||||
async _startJobInWorker(index, job) {
|
||||
const stopCallback = () => this.stop().catch(() => {});
|
||||
const jobDispatcher = new JobDispatcher(job, this._reporter, this._failureTracker, stopCallback);
|
||||
if (jobDispatcher.skipWholeJob()) return;
|
||||
let worker = this._workerSlots[index].worker;
|
||||
|
||||
// 1. Restart the worker if it has the wrong hash or is being stopped already.
|
||||
if (worker && (worker.hash() !== job.workerHash || worker.didSendStop())) {
|
||||
await worker.stop();
|
||||
worker = undefined;
|
||||
if (this._isStopped)
|
||||
// Check stopped signal after async hop.
|
||||
return;
|
||||
}
|
||||
this._workerSlots[index].jobDispatcher = jobDispatcher;
|
||||
|
||||
// 2. Start the worker if it is down.
|
||||
let startError;
|
||||
if (!worker) {
|
||||
worker = this._createWorker(job, index, (0, _ipc.serializeConfig)(this._config, true));
|
||||
this._workerSlots[index].worker = worker;
|
||||
worker.on('exit', () => this._workerSlots[index].worker = undefined);
|
||||
startError = await worker.start();
|
||||
if (this._isStopped)
|
||||
// Check stopped signal after async hop.
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Run the job.
|
||||
if (startError) jobDispatcher.onExit(startError);else jobDispatcher.runInWorker(worker);
|
||||
const result = await jobDispatcher.jobResult;
|
||||
this._workerSlots[index].jobDispatcher = undefined;
|
||||
this._updateCounterForWorkerHash(job.workerHash, -1);
|
||||
|
||||
// 4. When worker encounters error, we stop it and create a new one.
|
||||
// We also do not keep the worker alive if it cannot serve any more jobs.
|
||||
if (result.didFail) void worker.stop(true /* didFail */);else if (this._isWorkerRedundant(worker)) void worker.stop();
|
||||
|
||||
// 5. Possibly schedule a new job with leftover tests and/or retries.
|
||||
if (!this._isStopped && result.newJob) {
|
||||
this._queue.unshift(result.newJob);
|
||||
this._updateCounterForWorkerHash(job.workerHash, +1);
|
||||
}
|
||||
}
|
||||
_checkFinished() {
|
||||
if (this._finished.isDone()) return;
|
||||
|
||||
// Check that we have no more work to do.
|
||||
if (this._queue.length && !this._isStopped) return;
|
||||
|
||||
// Make sure all workers have finished the current job.
|
||||
if (this._workerSlots.some(w => w.busy)) return;
|
||||
this._finished.resolve();
|
||||
}
|
||||
_isWorkerRedundant(worker) {
|
||||
let workersWithSameHash = 0;
|
||||
for (const slot of this._workerSlots) {
|
||||
if (slot.worker && !slot.worker.didSendStop() && slot.worker.hash() === worker.hash()) workersWithSameHash++;
|
||||
}
|
||||
return workersWithSameHash > this._queuedOrRunningHashCount.get(worker.hash());
|
||||
}
|
||||
_updateCounterForWorkerHash(hash, delta) {
|
||||
this._queuedOrRunningHashCount.set(hash, delta + (this._queuedOrRunningHashCount.get(hash) || 0));
|
||||
}
|
||||
async run(testGroups, extraEnvByProjectId) {
|
||||
this._extraEnvByProjectId = extraEnvByProjectId;
|
||||
this._queue = testGroups;
|
||||
for (const group of testGroups) this._updateCounterForWorkerHash(group.workerHash, +1);
|
||||
this._isStopped = false;
|
||||
this._workerSlots = [];
|
||||
// 0. Stop right away if we have reached max failures.
|
||||
if (this._failureTracker.hasReachedMaxFailures()) void this.stop();
|
||||
// 1. Allocate workers.
|
||||
for (let i = 0; i < this._config.config.workers; i++) this._workerSlots.push({
|
||||
busy: false
|
||||
});
|
||||
// 2. Schedule enough jobs.
|
||||
for (let i = 0; i < this._workerSlots.length; i++) void this._scheduleJob();
|
||||
this._checkFinished();
|
||||
// 3. More jobs are scheduled when the worker becomes free.
|
||||
// 4. Wait for all jobs to finish.
|
||||
await this._finished;
|
||||
}
|
||||
_createWorker(testGroup, parallelIndex, loaderData) {
|
||||
const projectConfig = this._config.projects.find(p => p.id === testGroup.projectId);
|
||||
const outputDir = projectConfig.project.outputDir;
|
||||
const worker = new _workerHost.WorkerHost(testGroup, parallelIndex, loaderData, this._extraEnvByProjectId.get(testGroup.projectId) || {}, outputDir);
|
||||
const handleOutput = params => {
|
||||
var _this$_workerSlots$pa;
|
||||
const chunk = chunkFromParams(params);
|
||||
if (worker.didFail()) {
|
||||
// Note: we keep reading stdio from workers that are currently stopping after failure,
|
||||
// to debug teardown issues. However, we avoid spoiling the test result from
|
||||
// the next retry.
|
||||
return {
|
||||
chunk
|
||||
};
|
||||
}
|
||||
const currentlyRunning = (_this$_workerSlots$pa = this._workerSlots[parallelIndex].jobDispatcher) === null || _this$_workerSlots$pa === void 0 ? void 0 : _this$_workerSlots$pa.currentlyRunning();
|
||||
if (!currentlyRunning) return {
|
||||
chunk
|
||||
};
|
||||
return {
|
||||
chunk,
|
||||
test: currentlyRunning.test,
|
||||
result: currentlyRunning.result
|
||||
};
|
||||
};
|
||||
worker.on('stdOut', params => {
|
||||
var _this$_reporter$onStd, _this$_reporter;
|
||||
const {
|
||||
chunk,
|
||||
test,
|
||||
result
|
||||
} = handleOutput(params);
|
||||
result === null || result === void 0 || result.stdout.push(chunk);
|
||||
(_this$_reporter$onStd = (_this$_reporter = this._reporter).onStdOut) === null || _this$_reporter$onStd === void 0 || _this$_reporter$onStd.call(_this$_reporter, chunk, test, result);
|
||||
});
|
||||
worker.on('stdErr', params => {
|
||||
var _this$_reporter$onStd2, _this$_reporter2;
|
||||
const {
|
||||
chunk,
|
||||
test,
|
||||
result
|
||||
} = handleOutput(params);
|
||||
result === null || result === void 0 || result.stderr.push(chunk);
|
||||
(_this$_reporter$onStd2 = (_this$_reporter2 = this._reporter).onStdErr) === null || _this$_reporter$onStd2 === void 0 || _this$_reporter$onStd2.call(_this$_reporter2, chunk, test, result);
|
||||
});
|
||||
worker.on('teardownErrors', params => {
|
||||
this._failureTracker.onWorkerError();
|
||||
for (const error of params.fatalErrors) {
|
||||
var _this$_reporter$onErr, _this$_reporter3;
|
||||
(_this$_reporter$onErr = (_this$_reporter3 = this._reporter).onError) === null || _this$_reporter$onErr === void 0 || _this$_reporter$onErr.call(_this$_reporter3, error);
|
||||
}
|
||||
});
|
||||
worker.on('exit', () => {
|
||||
const producedEnv = this._producedEnvByProjectId.get(testGroup.projectId) || {};
|
||||
this._producedEnvByProjectId.set(testGroup.projectId, {
|
||||
...producedEnv,
|
||||
...worker.producedEnv()
|
||||
});
|
||||
});
|
||||
return worker;
|
||||
}
|
||||
producedEnvByProjectId() {
|
||||
return this._producedEnvByProjectId;
|
||||
}
|
||||
async stop() {
|
||||
if (this._isStopped) return;
|
||||
this._isStopped = true;
|
||||
await Promise.all(this._workerSlots.map(({
|
||||
worker
|
||||
}) => worker === null || worker === void 0 ? void 0 : worker.stop()));
|
||||
this._checkFinished();
|
||||
}
|
||||
}
|
||||
exports.Dispatcher = Dispatcher;
|
||||
class JobDispatcher {
|
||||
constructor(_job, _reporter, _failureTracker, _stopCallback) {
|
||||
this.jobResult = new _utils.ManualPromise();
|
||||
this._listeners = [];
|
||||
this._failedTests = new Set();
|
||||
this._failedWithNonRetriableError = new Set();
|
||||
this._remainingByTestId = new Map();
|
||||
this._dataByTestId = new Map();
|
||||
this._parallelIndex = 0;
|
||||
this._workerIndex = 0;
|
||||
this._currentlyRunning = void 0;
|
||||
this._job = _job;
|
||||
this._reporter = _reporter;
|
||||
this._failureTracker = _failureTracker;
|
||||
this._stopCallback = _stopCallback;
|
||||
this._remainingByTestId = new Map(this._job.tests.map(e => [e.id, e]));
|
||||
}
|
||||
_onTestBegin(params) {
|
||||
var _this$_reporter$onTes, _this$_reporter4;
|
||||
const test = this._remainingByTestId.get(params.testId);
|
||||
if (!test) {
|
||||
// TODO: this should never be the case, report an internal error?
|
||||
return;
|
||||
}
|
||||
const result = test._appendTestResult();
|
||||
this._dataByTestId.set(test.id, {
|
||||
test,
|
||||
result,
|
||||
steps: new Map()
|
||||
});
|
||||
result.parallelIndex = this._parallelIndex;
|
||||
result.workerIndex = this._workerIndex;
|
||||
result.startTime = new Date(params.startWallTime);
|
||||
(_this$_reporter$onTes = (_this$_reporter4 = this._reporter).onTestBegin) === null || _this$_reporter$onTes === void 0 || _this$_reporter$onTes.call(_this$_reporter4, test, result);
|
||||
this._currentlyRunning = {
|
||||
test,
|
||||
result
|
||||
};
|
||||
}
|
||||
_onTestEnd(params) {
|
||||
if (this._failureTracker.hasReachedMaxFailures()) {
|
||||
// Do not show more than one error to avoid confusion, but report
|
||||
// as interrupted to indicate that we did actually start the test.
|
||||
params.status = 'interrupted';
|
||||
params.errors = [];
|
||||
}
|
||||
const data = this._dataByTestId.get(params.testId);
|
||||
if (!data) {
|
||||
// TODO: this should never be the case, report an internal error?
|
||||
return;
|
||||
}
|
||||
this._dataByTestId.delete(params.testId);
|
||||
this._remainingByTestId.delete(params.testId);
|
||||
const {
|
||||
result,
|
||||
test
|
||||
} = data;
|
||||
result.duration = params.duration;
|
||||
result.errors = params.errors;
|
||||
result.error = result.errors[0];
|
||||
result.status = params.status;
|
||||
test.expectedStatus = params.expectedStatus;
|
||||
test.annotations = params.annotations;
|
||||
test.timeout = params.timeout;
|
||||
const isFailure = result.status !== 'skipped' && result.status !== test.expectedStatus;
|
||||
if (isFailure) this._failedTests.add(test);
|
||||
if (params.hasNonRetriableError) this._addNonretriableTestAndSerialModeParents(test);
|
||||
this._reportTestEnd(test, result);
|
||||
this._currentlyRunning = undefined;
|
||||
}
|
||||
_addNonretriableTestAndSerialModeParents(test) {
|
||||
this._failedWithNonRetriableError.add(test);
|
||||
for (let parent = test.parent; parent; parent = parent.parent) {
|
||||
if (parent._parallelMode === 'serial') this._failedWithNonRetriableError.add(parent);
|
||||
}
|
||||
}
|
||||
_onStepBegin(params) {
|
||||
var _this$_reporter$onSte, _this$_reporter5;
|
||||
const data = this._dataByTestId.get(params.testId);
|
||||
if (!data) {
|
||||
// The test has finished, but steps are still coming. Just ignore them.
|
||||
return;
|
||||
}
|
||||
const {
|
||||
result,
|
||||
steps,
|
||||
test
|
||||
} = data;
|
||||
const parentStep = params.parentStepId ? steps.get(params.parentStepId) : undefined;
|
||||
const step = {
|
||||
title: params.title,
|
||||
titlePath: () => {
|
||||
const parentPath = (parentStep === null || parentStep === void 0 ? void 0 : parentStep.titlePath()) || [];
|
||||
return [...parentPath, params.title];
|
||||
},
|
||||
parent: parentStep,
|
||||
category: params.category,
|
||||
startTime: new Date(params.wallTime),
|
||||
duration: -1,
|
||||
steps: [],
|
||||
location: params.location
|
||||
};
|
||||
steps.set(params.stepId, step);
|
||||
(parentStep || result).steps.push(step);
|
||||
(_this$_reporter$onSte = (_this$_reporter5 = this._reporter).onStepBegin) === null || _this$_reporter$onSte === void 0 || _this$_reporter$onSte.call(_this$_reporter5, test, result, step);
|
||||
}
|
||||
_onStepEnd(params) {
|
||||
var _this$_reporter$onSte2, _this$_reporter7;
|
||||
const data = this._dataByTestId.get(params.testId);
|
||||
if (!data) {
|
||||
// The test has finished, but steps are still coming. Just ignore them.
|
||||
return;
|
||||
}
|
||||
const {
|
||||
result,
|
||||
steps,
|
||||
test
|
||||
} = data;
|
||||
const step = steps.get(params.stepId);
|
||||
if (!step) {
|
||||
var _this$_reporter$onStd3, _this$_reporter6;
|
||||
(_this$_reporter$onStd3 = (_this$_reporter6 = this._reporter).onStdErr) === null || _this$_reporter$onStd3 === void 0 || _this$_reporter$onStd3.call(_this$_reporter6, 'Internal error: step end without step begin: ' + params.stepId, test, result);
|
||||
return;
|
||||
}
|
||||
step.duration = params.wallTime - step.startTime.getTime();
|
||||
if (params.error) step.error = params.error;
|
||||
if (params.suggestedRebaseline) (0, _rebase.addSuggestedRebaseline)(step.location, params.suggestedRebaseline);
|
||||
steps.delete(params.stepId);
|
||||
(_this$_reporter$onSte2 = (_this$_reporter7 = this._reporter).onStepEnd) === null || _this$_reporter$onSte2 === void 0 || _this$_reporter$onSte2.call(_this$_reporter7, test, result, step);
|
||||
}
|
||||
_onAttach(params) {
|
||||
const data = this._dataByTestId.get(params.testId);
|
||||
if (!data) {
|
||||
// The test has finished, but attachments are still coming. Just ignore them.
|
||||
return;
|
||||
}
|
||||
const attachment = {
|
||||
name: params.name,
|
||||
path: params.path,
|
||||
contentType: params.contentType,
|
||||
body: params.body !== undefined ? Buffer.from(params.body, 'base64') : undefined
|
||||
};
|
||||
data.result.attachments.push(attachment);
|
||||
}
|
||||
_failTestWithErrors(test, errors) {
|
||||
const runData = this._dataByTestId.get(test.id);
|
||||
// There might be a single test that has started but has not finished yet.
|
||||
let result;
|
||||
if (runData) {
|
||||
result = runData.result;
|
||||
} else {
|
||||
var _this$_reporter$onTes2, _this$_reporter8;
|
||||
result = test._appendTestResult();
|
||||
(_this$_reporter$onTes2 = (_this$_reporter8 = this._reporter).onTestBegin) === null || _this$_reporter$onTes2 === void 0 || _this$_reporter$onTes2.call(_this$_reporter8, test, result);
|
||||
}
|
||||
result.errors = [...errors];
|
||||
result.error = result.errors[0];
|
||||
result.status = errors.length ? 'failed' : 'skipped';
|
||||
this._reportTestEnd(test, result);
|
||||
this._failedTests.add(test);
|
||||
}
|
||||
_massSkipTestsFromRemaining(testIds, errors) {
|
||||
for (const test of this._remainingByTestId.values()) {
|
||||
if (!testIds.has(test.id)) continue;
|
||||
if (!this._failureTracker.hasReachedMaxFailures()) {
|
||||
this._failTestWithErrors(test, errors);
|
||||
errors = []; // Only report errors for the first test.
|
||||
}
|
||||
this._remainingByTestId.delete(test.id);
|
||||
}
|
||||
if (errors.length) {
|
||||
// We had fatal errors after all tests have passed - most likely in some teardown.
|
||||
// Let's just fail the test run.
|
||||
this._failureTracker.onWorkerError();
|
||||
for (const error of errors) {
|
||||
var _this$_reporter$onErr2, _this$_reporter9;
|
||||
(_this$_reporter$onErr2 = (_this$_reporter9 = this._reporter).onError) === null || _this$_reporter$onErr2 === void 0 || _this$_reporter$onErr2.call(_this$_reporter9, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
_onDone(params) {
|
||||
// We won't file remaining if:
|
||||
// - there are no remaining
|
||||
// - we are here not because something failed
|
||||
// - no unrecoverable worker error
|
||||
if (!this._remainingByTestId.size && !this._failedTests.size && !params.fatalErrors.length && !params.skipTestsDueToSetupFailure.length && !params.fatalUnknownTestIds && !params.unexpectedExitError) {
|
||||
this._finished({
|
||||
didFail: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
for (const testId of params.fatalUnknownTestIds || []) {
|
||||
const test = this._remainingByTestId.get(testId);
|
||||
if (test) {
|
||||
this._remainingByTestId.delete(testId);
|
||||
this._failTestWithErrors(test, [{
|
||||
message: `Test not found in the worker process. Make sure test title does not change.`
|
||||
}]);
|
||||
}
|
||||
}
|
||||
if (params.fatalErrors.length) {
|
||||
// In case of fatal errors, report first remaining test as failing with these errors,
|
||||
// and all others as skipped.
|
||||
this._massSkipTestsFromRemaining(new Set(this._remainingByTestId.keys()), params.fatalErrors);
|
||||
}
|
||||
// Handle tests that should be skipped because of the setup failure.
|
||||
this._massSkipTestsFromRemaining(new Set(params.skipTestsDueToSetupFailure), []);
|
||||
if (params.unexpectedExitError) {
|
||||
// When worker exits during a test, we blame the test itself.
|
||||
//
|
||||
// The most common situation when worker exits while not running a test is:
|
||||
// worker failed to require the test file (at the start) because of an exception in one of imports.
|
||||
// In this case, "skip" all remaining tests, to avoid running into the same exception over and over.
|
||||
if (this._currentlyRunning) this._massSkipTestsFromRemaining(new Set([this._currentlyRunning.test.id]), [params.unexpectedExitError]);else this._massSkipTestsFromRemaining(new Set(this._remainingByTestId.keys()), [params.unexpectedExitError]);
|
||||
}
|
||||
const retryCandidates = new Set();
|
||||
const serialSuitesWithFailures = new Set();
|
||||
for (const failedTest of this._failedTests) {
|
||||
if (this._failedWithNonRetriableError.has(failedTest)) continue;
|
||||
retryCandidates.add(failedTest);
|
||||
let outermostSerialSuite;
|
||||
for (let parent = failedTest.parent; parent; parent = parent.parent) {
|
||||
if (parent._parallelMode === 'serial') outermostSerialSuite = parent;
|
||||
}
|
||||
if (outermostSerialSuite && !this._failedWithNonRetriableError.has(outermostSerialSuite)) serialSuitesWithFailures.add(outermostSerialSuite);
|
||||
}
|
||||
|
||||
// If we have failed tests that belong to a serial suite,
|
||||
// we should skip all future tests from the same serial suite.
|
||||
const testsBelongingToSomeSerialSuiteWithFailures = [...this._remainingByTestId.values()].filter(test => {
|
||||
let parent = test.parent;
|
||||
while (parent && !serialSuitesWithFailures.has(parent)) parent = parent.parent;
|
||||
return !!parent;
|
||||
});
|
||||
this._massSkipTestsFromRemaining(new Set(testsBelongingToSomeSerialSuiteWithFailures.map(test => test.id)), []);
|
||||
for (const serialSuite of serialSuitesWithFailures) {
|
||||
// Add all tests from failed serial suites for possible retry.
|
||||
// These will only be retried together, because they have the same
|
||||
// "retries" setting and the same number of previous runs.
|
||||
serialSuite.allTests().forEach(test => retryCandidates.add(test));
|
||||
}
|
||||
const remaining = [...this._remainingByTestId.values()];
|
||||
for (const test of retryCandidates) {
|
||||
if (test.results.length < test.retries + 1) remaining.push(test);
|
||||
}
|
||||
|
||||
// This job is over, we will schedule another one.
|
||||
const newJob = remaining.length ? {
|
||||
...this._job,
|
||||
tests: remaining
|
||||
} : undefined;
|
||||
this._finished({
|
||||
didFail: true,
|
||||
newJob
|
||||
});
|
||||
}
|
||||
onExit(data) {
|
||||
const unexpectedExitError = data.unexpectedly ? {
|
||||
message: `Error: worker process exited unexpectedly (code=${data.code}, signal=${data.signal})`
|
||||
} : undefined;
|
||||
this._onDone({
|
||||
skipTestsDueToSetupFailure: [],
|
||||
fatalErrors: [],
|
||||
unexpectedExitError
|
||||
});
|
||||
}
|
||||
_finished(result) {
|
||||
_utils.eventsHelper.removeEventListeners(this._listeners);
|
||||
this.jobResult.resolve(result);
|
||||
}
|
||||
runInWorker(worker) {
|
||||
this._parallelIndex = worker.parallelIndex;
|
||||
this._workerIndex = worker.workerIndex;
|
||||
const runPayload = {
|
||||
file: this._job.requireFile,
|
||||
entries: this._job.tests.map(test => {
|
||||
return {
|
||||
testId: test.id,
|
||||
retry: test.results.length
|
||||
};
|
||||
})
|
||||
};
|
||||
worker.runTestGroup(runPayload);
|
||||
this._listeners = [_utils.eventsHelper.addEventListener(worker, 'testBegin', this._onTestBegin.bind(this)), _utils.eventsHelper.addEventListener(worker, 'testEnd', this._onTestEnd.bind(this)), _utils.eventsHelper.addEventListener(worker, 'stepBegin', this._onStepBegin.bind(this)), _utils.eventsHelper.addEventListener(worker, 'stepEnd', this._onStepEnd.bind(this)), _utils.eventsHelper.addEventListener(worker, 'attach', this._onAttach.bind(this)), _utils.eventsHelper.addEventListener(worker, 'done', this._onDone.bind(this)), _utils.eventsHelper.addEventListener(worker, 'exit', this.onExit.bind(this))];
|
||||
}
|
||||
skipWholeJob() {
|
||||
// If all the tests in a group are skipped, we report them immediately
|
||||
// without sending anything to a worker. This avoids creating unnecessary worker processes.
|
||||
//
|
||||
// However, if there is at least one non-skipped test in a group, we'll send
|
||||
// the whole group to the worker process and report tests in the natural order,
|
||||
// with skipped tests mixed in-between non-skipped. This makes
|
||||
// for a better reporter experience.
|
||||
const allTestsSkipped = this._job.tests.every(test => test.expectedStatus === 'skipped');
|
||||
if (allTestsSkipped && !this._failureTracker.hasReachedMaxFailures()) {
|
||||
for (const test of this._job.tests) {
|
||||
var _this$_reporter$onTes3, _this$_reporter10;
|
||||
const result = test._appendTestResult();
|
||||
(_this$_reporter$onTes3 = (_this$_reporter10 = this._reporter).onTestBegin) === null || _this$_reporter$onTes3 === void 0 || _this$_reporter$onTes3.call(_this$_reporter10, test, result);
|
||||
result.status = 'skipped';
|
||||
this._reportTestEnd(test, result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
currentlyRunning() {
|
||||
return this._currentlyRunning;
|
||||
}
|
||||
_reportTestEnd(test, result) {
|
||||
var _this$_reporter$onTes4, _this$_reporter11;
|
||||
(_this$_reporter$onTes4 = (_this$_reporter11 = this._reporter).onTestEnd) === null || _this$_reporter$onTes4 === void 0 || _this$_reporter$onTes4.call(_this$_reporter11, test, result);
|
||||
const hadMaxFailures = this._failureTracker.hasReachedMaxFailures();
|
||||
this._failureTracker.onTestEnd(test, result);
|
||||
if (this._failureTracker.hasReachedMaxFailures()) {
|
||||
var _this$_reporter$onErr3, _this$_reporter12;
|
||||
this._stopCallback();
|
||||
if (!hadMaxFailures) (_this$_reporter$onErr3 = (_this$_reporter12 = this._reporter).onError) === null || _this$_reporter$onErr3 === void 0 || _this$_reporter$onErr3.call(_this$_reporter12, {
|
||||
message: _utilsBundle.colors.red(`Testing stopped early after ${this._failureTracker.maxFailures()} maximum allowed failures.`)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
function chunkFromParams(params) {
|
||||
if (typeof params.text === 'string') return params.text;
|
||||
return Buffer.from(params.buffer, 'base64');
|
||||
}
|
||||
61
node_modules/playwright/lib/runner/failureTracker.js
generated
vendored
Normal file
61
node_modules/playwright/lib/runner/failureTracker.js
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.FailureTracker = void 0;
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class FailureTracker {
|
||||
constructor(_config) {
|
||||
this._failureCount = 0;
|
||||
this._hasWorkerErrors = false;
|
||||
this._rootSuite = void 0;
|
||||
this._config = _config;
|
||||
}
|
||||
onRootSuite(rootSuite) {
|
||||
this._rootSuite = rootSuite;
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
// Test is considered failing after the last retry.
|
||||
if (test.outcome() === 'unexpected' && test.results.length > test.retries) ++this._failureCount;
|
||||
}
|
||||
onWorkerError() {
|
||||
this._hasWorkerErrors = true;
|
||||
}
|
||||
hasReachedMaxFailures() {
|
||||
return this.maxFailures() > 0 && this._failureCount >= this.maxFailures();
|
||||
}
|
||||
hasWorkerErrors() {
|
||||
return this._hasWorkerErrors;
|
||||
}
|
||||
result() {
|
||||
return this._hasWorkerErrors || this.hasReachedMaxFailures() || this.hasFailedTests() || this._config.cliFailOnFlakyTests && this.hasFlakyTests() ? 'failed' : 'passed';
|
||||
}
|
||||
hasFailedTests() {
|
||||
var _this$_rootSuite;
|
||||
return (_this$_rootSuite = this._rootSuite) === null || _this$_rootSuite === void 0 ? void 0 : _this$_rootSuite.allTests().some(test => !test.ok());
|
||||
}
|
||||
hasFlakyTests() {
|
||||
var _this$_rootSuite2;
|
||||
return (_this$_rootSuite2 = this._rootSuite) === null || _this$_rootSuite2 === void 0 ? void 0 : _this$_rootSuite2.allTests().some(test => test.outcome() === 'flaky');
|
||||
}
|
||||
maxFailures() {
|
||||
return this._config.config.maxFailures;
|
||||
}
|
||||
}
|
||||
exports.FailureTracker = FailureTracker;
|
||||
66
node_modules/playwright/lib/runner/lastRun.js
generated
vendored
Normal file
66
node_modules/playwright/lib/runner/lastRun.js
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.LastRunReporter = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _projectUtils = require("./projectUtils");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class LastRunReporter {
|
||||
constructor(config) {
|
||||
this._config = void 0;
|
||||
this._lastRunFile = void 0;
|
||||
this._suite = void 0;
|
||||
this._config = config;
|
||||
const [project] = (0, _projectUtils.filterProjects)(config.projects, config.cliProjectFilter);
|
||||
if (project) this._lastRunFile = _path.default.join(project.project.outputDir, '.last-run.json');
|
||||
}
|
||||
async filterLastFailed() {
|
||||
if (!this._lastRunFile) return;
|
||||
try {
|
||||
const lastRunInfo = JSON.parse(await _fs.default.promises.readFile(this._lastRunFile, 'utf8'));
|
||||
this._config.testIdMatcher = id => lastRunInfo.failedTests.includes(id);
|
||||
} catch {}
|
||||
}
|
||||
version() {
|
||||
return 'v2';
|
||||
}
|
||||
printsToStdio() {
|
||||
return false;
|
||||
}
|
||||
onBegin(suite) {
|
||||
this._suite = suite;
|
||||
}
|
||||
async onEnd(result) {
|
||||
var _this$_suite;
|
||||
if (!this._lastRunFile || this._config.cliListOnly) return;
|
||||
await _fs.default.promises.mkdir(_path.default.dirname(this._lastRunFile), {
|
||||
recursive: true
|
||||
});
|
||||
const failedTests = (_this$_suite = this._suite) === null || _this$_suite === void 0 ? void 0 : _this$_suite.allTests().filter(t => !t.ok()).map(t => t.id);
|
||||
const lastRunReport = JSON.stringify({
|
||||
status: result.status,
|
||||
failedTests
|
||||
}, undefined, 2);
|
||||
await _fs.default.promises.writeFile(this._lastRunFile, lastRunReport);
|
||||
}
|
||||
}
|
||||
exports.LastRunReporter = LastRunReporter;
|
||||
312
node_modules/playwright/lib/runner/loadUtils.js
generated
vendored
Normal file
312
node_modules/playwright/lib/runner/loadUtils.js
generated
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.collectProjectsAndTestFiles = collectProjectsAndTestFiles;
|
||||
exports.createRootSuite = createRootSuite;
|
||||
exports.loadFileSuites = loadFileSuites;
|
||||
exports.loadGlobalHook = loadGlobalHook;
|
||||
exports.loadReporter = loadReporter;
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _loaderHost = require("./loaderHost");
|
||||
var _test = require("../common/test");
|
||||
var _util = require("../util");
|
||||
var _projectUtils = require("./projectUtils");
|
||||
var _transform = require("../transform/transform");
|
||||
var _suiteUtils = require("../common/suiteUtils");
|
||||
var _testGroups = require("./testGroups");
|
||||
var _compilationCache = require("../transform/compilationCache");
|
||||
var _utilsBundle = require("../utilsBundle");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
async function collectProjectsAndTestFiles(testRun, doNotRunTestsOutsideProjectFilter) {
|
||||
const config = testRun.config;
|
||||
const fsCache = new Map();
|
||||
const sourceMapCache = new Map();
|
||||
const cliFileMatcher = config.cliArgs.length ? (0, _util.createFileMatcherFromArguments)(config.cliArgs) : null;
|
||||
|
||||
// First collect all files for the projects in the command line, don't apply any file filters.
|
||||
const allFilesForProject = new Map();
|
||||
const filteredProjects = (0, _projectUtils.filterProjects)(config.projects, config.cliProjectFilter);
|
||||
for (const project of filteredProjects) {
|
||||
const files = await (0, _projectUtils.collectFilesForProject)(project, fsCache);
|
||||
allFilesForProject.set(project, files);
|
||||
}
|
||||
|
||||
// Filter files based on the file filters, eliminate the empty projects.
|
||||
const filesToRunByProject = new Map();
|
||||
for (const [project, files] of allFilesForProject) {
|
||||
const matchedFiles = files.filter(file => {
|
||||
const hasMatchingSources = sourceMapSources(file, sourceMapCache).some(source => {
|
||||
if (cliFileMatcher && !cliFileMatcher(source)) return false;
|
||||
return true;
|
||||
});
|
||||
return hasMatchingSources;
|
||||
});
|
||||
const filteredFiles = matchedFiles.filter(Boolean);
|
||||
filesToRunByProject.set(project, filteredFiles);
|
||||
}
|
||||
|
||||
// (Re-)add all files for dependent projects, disregard filters.
|
||||
const projectClosure = (0, _projectUtils.buildProjectsClosure)([...filesToRunByProject.keys()]);
|
||||
for (const [project, type] of projectClosure) {
|
||||
if (type === 'dependency') {
|
||||
const treatProjectAsEmpty = doNotRunTestsOutsideProjectFilter && !filteredProjects.includes(project);
|
||||
const files = treatProjectAsEmpty ? [] : allFilesForProject.get(project) || (await (0, _projectUtils.collectFilesForProject)(project, fsCache));
|
||||
filesToRunByProject.set(project, files);
|
||||
}
|
||||
}
|
||||
testRun.projectFiles = filesToRunByProject;
|
||||
testRun.projectSuites = new Map();
|
||||
}
|
||||
async function loadFileSuites(testRun, mode, errors) {
|
||||
// Determine all files to load.
|
||||
const config = testRun.config;
|
||||
const allTestFiles = new Set();
|
||||
for (const files of testRun.projectFiles.values()) files.forEach(file => allTestFiles.add(file));
|
||||
|
||||
// Load test files.
|
||||
const fileSuiteByFile = new Map();
|
||||
const loaderHost = mode === 'out-of-process' ? new _loaderHost.OutOfProcessLoaderHost(config) : new _loaderHost.InProcessLoaderHost(config);
|
||||
if (await loaderHost.start(errors)) {
|
||||
for (const file of allTestFiles) {
|
||||
const fileSuite = await loaderHost.loadTestFile(file, errors);
|
||||
fileSuiteByFile.set(file, fileSuite);
|
||||
errors.push(...createDuplicateTitlesErrors(config, fileSuite));
|
||||
}
|
||||
await loaderHost.stop();
|
||||
}
|
||||
|
||||
// Check that no test file imports another test file.
|
||||
// Loader must be stopped first, since it populates the dependency tree.
|
||||
for (const file of allTestFiles) {
|
||||
for (const dependency of (0, _compilationCache.dependenciesForTestFile)(file)) {
|
||||
if (allTestFiles.has(dependency)) {
|
||||
const importer = _path.default.relative(config.config.rootDir, file);
|
||||
const importee = _path.default.relative(config.config.rootDir, dependency);
|
||||
errors.push({
|
||||
message: `Error: test file "${importer}" should not import test file "${importee}"`,
|
||||
location: {
|
||||
file,
|
||||
line: 1,
|
||||
column: 1
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect file suites for each project.
|
||||
for (const [project, files] of testRun.projectFiles) {
|
||||
const suites = files.map(file => fileSuiteByFile.get(file)).filter(Boolean);
|
||||
testRun.projectSuites.set(project, suites);
|
||||
}
|
||||
}
|
||||
async function createRootSuite(testRun, errors, shouldFilterOnly, additionalFileMatcher) {
|
||||
const config = testRun.config;
|
||||
// Create root suite, where each child will be a project suite with cloned file suites inside it.
|
||||
const rootSuite = new _test.Suite('', 'root');
|
||||
const projectSuites = new Map();
|
||||
const filteredProjectSuites = new Map();
|
||||
|
||||
// Filter all the projects using grep, testId, file names.
|
||||
{
|
||||
// Interpret cli parameters.
|
||||
const cliFileFilters = (0, _util.createFileFiltersFromArguments)(config.cliArgs);
|
||||
const grepMatcher = config.cliGrep ? (0, _util.createTitleMatcher)((0, _util.forceRegExp)(config.cliGrep)) : () => true;
|
||||
const grepInvertMatcher = config.cliGrepInvert ? (0, _util.createTitleMatcher)((0, _util.forceRegExp)(config.cliGrepInvert)) : () => false;
|
||||
const cliTitleMatcher = title => !grepInvertMatcher(title) && grepMatcher(title);
|
||||
|
||||
// Filter file suites for all projects.
|
||||
for (const [project, fileSuites] of testRun.projectSuites) {
|
||||
const projectSuite = createProjectSuite(project, fileSuites);
|
||||
projectSuites.set(project, projectSuite);
|
||||
const filteredProjectSuite = filterProjectSuite(projectSuite, {
|
||||
cliFileFilters,
|
||||
cliTitleMatcher,
|
||||
testIdMatcher: config.testIdMatcher,
|
||||
additionalFileMatcher
|
||||
});
|
||||
filteredProjectSuites.set(project, filteredProjectSuite);
|
||||
}
|
||||
}
|
||||
if (shouldFilterOnly) {
|
||||
// Create a fake root to execute the exclusive semantics across the projects.
|
||||
const filteredRoot = new _test.Suite('', 'root');
|
||||
for (const filteredProjectSuite of filteredProjectSuites.values()) filteredRoot._addSuite(filteredProjectSuite);
|
||||
(0, _suiteUtils.filterOnly)(filteredRoot);
|
||||
for (const [project, filteredProjectSuite] of filteredProjectSuites) {
|
||||
if (!filteredRoot.suites.includes(filteredProjectSuite)) filteredProjectSuites.delete(project);
|
||||
}
|
||||
}
|
||||
|
||||
// Add post-filtered top-level projects to the root suite for sharding and 'only' processing.
|
||||
const projectClosure = (0, _projectUtils.buildProjectsClosure)([...filteredProjectSuites.keys()], project => filteredProjectSuites.get(project)._hasTests());
|
||||
for (const [project, type] of projectClosure) {
|
||||
if (type === 'top-level') {
|
||||
var _project$fullConfig$c;
|
||||
project.project.repeatEach = (_project$fullConfig$c = project.fullConfig.configCLIOverrides.repeatEach) !== null && _project$fullConfig$c !== void 0 ? _project$fullConfig$c : project.project.repeatEach;
|
||||
rootSuite._addSuite(buildProjectSuite(project, filteredProjectSuites.get(project)));
|
||||
}
|
||||
}
|
||||
|
||||
// Complain about only.
|
||||
if (config.config.forbidOnly) {
|
||||
const onlyTestsAndSuites = rootSuite._getOnlyItems();
|
||||
if (onlyTestsAndSuites.length > 0) {
|
||||
const configFilePath = config.config.configFile ? _path.default.relative(config.config.rootDir, config.config.configFile) : undefined;
|
||||
errors.push(...createForbidOnlyErrors(onlyTestsAndSuites, config.configCLIOverrides.forbidOnly, configFilePath));
|
||||
}
|
||||
}
|
||||
|
||||
// Shard only the top-level projects.
|
||||
if (config.config.shard) {
|
||||
// Create test groups for top-level projects.
|
||||
const testGroups = [];
|
||||
for (const projectSuite of rootSuite.suites) {
|
||||
// Split beforeAll-grouped tests into "config.shard.total" groups when needed.
|
||||
// Later on, we'll re-split them between workers by using "config.workers" instead.
|
||||
testGroups.push(...(0, _testGroups.createTestGroups)(projectSuite, config.config.shard.total));
|
||||
}
|
||||
|
||||
// Shard test groups.
|
||||
const testGroupsInThisShard = (0, _testGroups.filterForShard)(config.config.shard, testGroups);
|
||||
const testsInThisShard = new Set();
|
||||
for (const group of testGroupsInThisShard) {
|
||||
for (const test of group.tests) testsInThisShard.add(test);
|
||||
}
|
||||
|
||||
// Update project suites, removing empty ones.
|
||||
(0, _suiteUtils.filterTestsRemoveEmptySuites)(rootSuite, test => testsInThisShard.has(test));
|
||||
}
|
||||
|
||||
// Now prepend dependency projects without filtration.
|
||||
{
|
||||
// Filtering 'only' and sharding might have reduced the number of top-level projects.
|
||||
// Build the project closure to only include dependencies that are still needed.
|
||||
const projectClosure = new Map((0, _projectUtils.buildProjectsClosure)(rootSuite.suites.map(suite => suite._fullProject)));
|
||||
|
||||
// Clone file suites for dependency projects.
|
||||
for (const [project, level] of projectClosure.entries()) {
|
||||
if (level === 'dependency') rootSuite._prependSuite(buildProjectSuite(project, projectSuites.get(project)));
|
||||
}
|
||||
}
|
||||
return rootSuite;
|
||||
}
|
||||
function createProjectSuite(project, fileSuites) {
|
||||
const projectSuite = new _test.Suite(project.project.name, 'project');
|
||||
for (const fileSuite of fileSuites) projectSuite._addSuite((0, _suiteUtils.bindFileSuiteToProject)(project, fileSuite));
|
||||
const grepMatcher = (0, _util.createTitleMatcher)(project.project.grep);
|
||||
const grepInvertMatcher = project.project.grepInvert ? (0, _util.createTitleMatcher)(project.project.grepInvert) : null;
|
||||
(0, _suiteUtils.filterTestsRemoveEmptySuites)(projectSuite, test => {
|
||||
const grepTitle = test._grepTitle();
|
||||
if (grepInvertMatcher !== null && grepInvertMatcher !== void 0 && grepInvertMatcher(grepTitle)) return false;
|
||||
return grepMatcher(grepTitle);
|
||||
});
|
||||
return projectSuite;
|
||||
}
|
||||
function filterProjectSuite(projectSuite, options) {
|
||||
// Fast path.
|
||||
if (!options.cliFileFilters.length && !options.cliTitleMatcher && !options.testIdMatcher && !options.additionalFileMatcher) return projectSuite;
|
||||
const result = projectSuite._deepClone();
|
||||
if (options.cliFileFilters.length) (0, _suiteUtils.filterByFocusedLine)(result, options.cliFileFilters);
|
||||
if (options.testIdMatcher) (0, _suiteUtils.filterByTestIds)(result, options.testIdMatcher);
|
||||
(0, _suiteUtils.filterTestsRemoveEmptySuites)(result, test => {
|
||||
if (options.cliTitleMatcher && !options.cliTitleMatcher(test._grepTitle())) return false;
|
||||
if (options.additionalFileMatcher && !options.additionalFileMatcher(test.location.file)) return false;
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
function buildProjectSuite(project, projectSuite) {
|
||||
const result = new _test.Suite(project.project.name, 'project');
|
||||
result._fullProject = project;
|
||||
if (project.fullyParallel) result._parallelMode = 'parallel';
|
||||
for (const fileSuite of projectSuite.suites) {
|
||||
// Fast path for the repeatEach = 0.
|
||||
result._addSuite(fileSuite);
|
||||
for (let repeatEachIndex = 1; repeatEachIndex < project.project.repeatEach; repeatEachIndex++) {
|
||||
const clone = fileSuite._deepClone();
|
||||
(0, _suiteUtils.applyRepeatEachIndex)(project, clone, repeatEachIndex);
|
||||
result._addSuite(clone);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function createForbidOnlyErrors(onlyTestsAndSuites, forbidOnlyCLIFlag, configFilePath) {
|
||||
const errors = [];
|
||||
for (const testOrSuite of onlyTestsAndSuites) {
|
||||
// Skip root and file.
|
||||
const title = testOrSuite.titlePath().slice(2).join(' ');
|
||||
const configFilePathName = configFilePath ? `'${configFilePath}'` : 'the Playwright configuration file';
|
||||
const forbidOnlySource = forbidOnlyCLIFlag ? `'--forbid-only' CLI flag` : `'forbidOnly' option in ${configFilePathName}`;
|
||||
const error = {
|
||||
message: `Error: item focused with '.only' is not allowed due to the ${forbidOnlySource}: "${title}"`,
|
||||
location: testOrSuite.location
|
||||
};
|
||||
errors.push(error);
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
function createDuplicateTitlesErrors(config, fileSuite) {
|
||||
const errors = [];
|
||||
const testsByFullTitle = new Map();
|
||||
for (const test of fileSuite.allTests()) {
|
||||
const fullTitle = test.titlePath().slice(1).join(' › ');
|
||||
const existingTest = testsByFullTitle.get(fullTitle);
|
||||
if (existingTest) {
|
||||
const error = {
|
||||
message: `Error: duplicate test title "${fullTitle}", first declared in ${buildItemLocation(config.config.rootDir, existingTest)}`,
|
||||
location: test.location
|
||||
};
|
||||
errors.push(error);
|
||||
}
|
||||
testsByFullTitle.set(fullTitle, test);
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
function buildItemLocation(rootDir, testOrSuite) {
|
||||
if (!testOrSuite.location) return '';
|
||||
return `${_path.default.relative(rootDir, testOrSuite.location.file)}:${testOrSuite.location.line}`;
|
||||
}
|
||||
async function requireOrImportDefaultFunction(file, expectConstructor) {
|
||||
let func = await (0, _transform.requireOrImport)(file);
|
||||
if (func && typeof func === 'object' && 'default' in func) func = func['default'];
|
||||
if (typeof func !== 'function') throw (0, _util.errorWithFile)(file, `file must export a single ${expectConstructor ? 'class' : 'function'}.`);
|
||||
return func;
|
||||
}
|
||||
function loadGlobalHook(config, file) {
|
||||
return requireOrImportDefaultFunction(_path.default.resolve(config.config.rootDir, file), false);
|
||||
}
|
||||
function loadReporter(config, file) {
|
||||
return requireOrImportDefaultFunction(config ? _path.default.resolve(config.config.rootDir, file) : file, true);
|
||||
}
|
||||
function sourceMapSources(file, cache) {
|
||||
let sources = [file];
|
||||
if (!file.endsWith('.js')) return sources;
|
||||
if (cache.has(file)) return cache.get(file);
|
||||
try {
|
||||
const sourceMap = _utilsBundle.sourceMapSupport.retrieveSourceMap(file);
|
||||
const sourceMapData = typeof (sourceMap === null || sourceMap === void 0 ? void 0 : sourceMap.map) === 'string' ? JSON.parse(sourceMap.map) : sourceMap === null || sourceMap === void 0 ? void 0 : sourceMap.map;
|
||||
if (sourceMapData !== null && sourceMapData !== void 0 && sourceMapData.sources) sources = sourceMapData.sources.map(source => _path.default.resolve(_path.default.dirname(file), source));
|
||||
} finally {
|
||||
cache.set(file, sources);
|
||||
return sources;
|
||||
}
|
||||
}
|
||||
85
node_modules/playwright/lib/runner/loaderHost.js
generated
vendored
Normal file
85
node_modules/playwright/lib/runner/loaderHost.js
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.OutOfProcessLoaderHost = exports.InProcessLoaderHost = void 0;
|
||||
var _ipc = require("../common/ipc");
|
||||
var _processHost = require("./processHost");
|
||||
var _test = require("../common/test");
|
||||
var _testLoader = require("../common/testLoader");
|
||||
var _poolBuilder = require("../common/poolBuilder");
|
||||
var _compilationCache = require("../transform/compilationCache");
|
||||
var _esmLoaderHost = require("../common/esmLoaderHost");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class InProcessLoaderHost {
|
||||
constructor(config) {
|
||||
this._config = void 0;
|
||||
this._poolBuilder = void 0;
|
||||
this._config = config;
|
||||
this._poolBuilder = _poolBuilder.PoolBuilder.createForLoader();
|
||||
}
|
||||
async start(errors) {
|
||||
return true;
|
||||
}
|
||||
async loadTestFile(file, testErrors) {
|
||||
const result = await (0, _testLoader.loadTestFile)(file, this._config.config.rootDir, testErrors);
|
||||
this._poolBuilder.buildPools(result, testErrors);
|
||||
return result;
|
||||
}
|
||||
async stop() {
|
||||
await (0, _esmLoaderHost.incorporateCompilationCache)();
|
||||
}
|
||||
}
|
||||
exports.InProcessLoaderHost = InProcessLoaderHost;
|
||||
class OutOfProcessLoaderHost {
|
||||
constructor(config) {
|
||||
this._config = void 0;
|
||||
this._processHost = void 0;
|
||||
this._config = config;
|
||||
this._processHost = new _processHost.ProcessHost(require.resolve('../loader/loaderMain.js'), 'loader', {});
|
||||
}
|
||||
async start(errors) {
|
||||
const startError = await this._processHost.startRunner((0, _ipc.serializeConfig)(this._config, false));
|
||||
if (startError) {
|
||||
errors.push({
|
||||
message: `Test loader process failed to start with code "${startError.code}" and signal "${startError.signal}"`
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
async loadTestFile(file, testErrors) {
|
||||
const result = await this._processHost.sendMessage({
|
||||
method: 'loadTestFile',
|
||||
params: {
|
||||
file
|
||||
}
|
||||
});
|
||||
testErrors.push(...result.testErrors);
|
||||
return _test.Suite._deepParse(result.fileSuite);
|
||||
}
|
||||
async stop() {
|
||||
const result = await this._processHost.sendMessage({
|
||||
method: 'getCompilationCacheFromLoader'
|
||||
});
|
||||
(0, _compilationCache.addToCompilationCache)(result);
|
||||
await this._processHost.stop();
|
||||
}
|
||||
}
|
||||
exports.OutOfProcessLoaderHost = OutOfProcessLoaderHost;
|
||||
175
node_modules/playwright/lib/runner/processHost.js
generated
vendored
Normal file
175
node_modules/playwright/lib/runner/processHost.js
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ProcessHost = void 0;
|
||||
var _child_process = _interopRequireDefault(require("child_process"));
|
||||
var _events = require("events");
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _esmUtils = require("../transform/esmUtils");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _esmLoaderHost = require("../common/esmLoaderHost");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class ProcessHost extends _events.EventEmitter {
|
||||
constructor(runnerScript, processName, env) {
|
||||
super();
|
||||
this.process = void 0;
|
||||
this._didSendStop = false;
|
||||
this._processDidExit = false;
|
||||
this._didExitAndRanOnExit = false;
|
||||
this._runnerScript = void 0;
|
||||
this._lastMessageId = 0;
|
||||
this._callbacks = new Map();
|
||||
this._processName = void 0;
|
||||
this._producedEnv = {};
|
||||
this._extraEnv = void 0;
|
||||
this._runnerScript = runnerScript;
|
||||
this._processName = processName;
|
||||
this._extraEnv = env;
|
||||
}
|
||||
async startRunner(runnerParams, options = {}) {
|
||||
var _this$process$stdout, _this$process$stderr;
|
||||
(0, _utils.assert)(!this.process, 'Internal error: starting the same process twice');
|
||||
this.process = _child_process.default.fork(require.resolve('../common/process'), {
|
||||
detached: false,
|
||||
env: {
|
||||
...process.env,
|
||||
...this._extraEnv,
|
||||
...(_esmLoaderHost.esmLoaderRegistered ? {
|
||||
PW_TS_ESM_LOADER_ON: '1'
|
||||
} : {})
|
||||
},
|
||||
stdio: ['ignore', options.onStdOut ? 'pipe' : 'inherit', options.onStdErr && !process.env.PW_RUNNER_DEBUG ? 'pipe' : 'inherit', 'ipc'],
|
||||
...(process.env.PW_TS_ESM_LEGACY_LOADER_ON ? {
|
||||
execArgv: (0, _esmUtils.execArgvWithExperimentalLoaderOptions)()
|
||||
} : {})
|
||||
});
|
||||
this.process.on('exit', async (code, signal) => {
|
||||
this._processDidExit = true;
|
||||
await this.onExit();
|
||||
this._didExitAndRanOnExit = true;
|
||||
this.emit('exit', {
|
||||
unexpectedly: !this._didSendStop,
|
||||
code,
|
||||
signal
|
||||
});
|
||||
});
|
||||
this.process.on('error', e => {}); // do not yell at a send to dead process.
|
||||
this.process.on('message', message => {
|
||||
if (_utilsBundle.debug.enabled('pw:test:protocol')) (0, _utilsBundle.debug)('pw:test:protocol')('◀ RECV ' + JSON.stringify(message));
|
||||
if (message.method === '__env_produced__') {
|
||||
const producedEnv = message.params;
|
||||
this._producedEnv = Object.fromEntries(producedEnv.map(e => {
|
||||
var _e$;
|
||||
return [e[0], (_e$ = e[1]) !== null && _e$ !== void 0 ? _e$ : undefined];
|
||||
}));
|
||||
} else if (message.method === '__dispatch__') {
|
||||
const {
|
||||
id,
|
||||
error,
|
||||
method,
|
||||
params,
|
||||
result
|
||||
} = message.params;
|
||||
if (id && this._callbacks.has(id)) {
|
||||
const {
|
||||
resolve,
|
||||
reject
|
||||
} = this._callbacks.get(id);
|
||||
this._callbacks.delete(id);
|
||||
if (error) {
|
||||
const errorObject = new Error(error.message);
|
||||
errorObject.stack = error.stack;
|
||||
reject(errorObject);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
} else {
|
||||
this.emit(method, params);
|
||||
}
|
||||
} else {
|
||||
this.emit(message.method, message.params);
|
||||
}
|
||||
});
|
||||
if (options.onStdOut) (_this$process$stdout = this.process.stdout) === null || _this$process$stdout === void 0 || _this$process$stdout.on('data', options.onStdOut);
|
||||
if (options.onStdErr) (_this$process$stderr = this.process.stderr) === null || _this$process$stderr === void 0 || _this$process$stderr.on('data', options.onStdErr);
|
||||
const error = await new Promise(resolve => {
|
||||
this.process.once('exit', (code, signal) => resolve({
|
||||
unexpectedly: true,
|
||||
code,
|
||||
signal
|
||||
}));
|
||||
this.once('ready', () => resolve(undefined));
|
||||
});
|
||||
if (error) return error;
|
||||
const processParams = {
|
||||
processName: this._processName
|
||||
};
|
||||
this.send({
|
||||
method: '__init__',
|
||||
params: {
|
||||
processParams,
|
||||
runnerScript: this._runnerScript,
|
||||
runnerParams
|
||||
}
|
||||
});
|
||||
}
|
||||
sendMessage(message) {
|
||||
const id = ++this._lastMessageId;
|
||||
this.send({
|
||||
method: '__dispatch__',
|
||||
params: {
|
||||
id,
|
||||
...message
|
||||
}
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
this._callbacks.set(id, {
|
||||
resolve,
|
||||
reject
|
||||
});
|
||||
});
|
||||
}
|
||||
sendMessageNoReply(message) {
|
||||
this.sendMessage(message).catch(() => {});
|
||||
}
|
||||
async onExit() {}
|
||||
async stop() {
|
||||
if (!this._processDidExit && !this._didSendStop) {
|
||||
this.send({
|
||||
method: '__stop__'
|
||||
});
|
||||
this._didSendStop = true;
|
||||
}
|
||||
if (!this._didExitAndRanOnExit) await new Promise(f => this.once('exit', f));
|
||||
}
|
||||
didSendStop() {
|
||||
return this._didSendStop;
|
||||
}
|
||||
producedEnv() {
|
||||
return this._producedEnv;
|
||||
}
|
||||
send(message) {
|
||||
var _this$process;
|
||||
if (_utilsBundle.debug.enabled('pw:test:protocol')) (0, _utilsBundle.debug)('pw:test:protocol')('SEND ► ' + JSON.stringify(message));
|
||||
(_this$process = this.process) === null || _this$process === void 0 || _this$process.send(message);
|
||||
}
|
||||
}
|
||||
exports.ProcessHost = ProcessHost;
|
||||
203
node_modules/playwright/lib/runner/projectUtils.js
generated
vendored
Normal file
203
node_modules/playwright/lib/runner/projectUtils.js
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.buildDependentProjects = buildDependentProjects;
|
||||
exports.buildProjectsClosure = buildProjectsClosure;
|
||||
exports.buildTeardownToSetupsMap = buildTeardownToSetupsMap;
|
||||
exports.collectFilesForProject = collectFilesForProject;
|
||||
exports.filterProjects = filterProjects;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _util = require("util");
|
||||
var _util2 = require("../util");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const readFileAsync = (0, _util.promisify)(_fs.default.readFile);
|
||||
const readDirAsync = (0, _util.promisify)(_fs.default.readdir);
|
||||
function wildcardPatternToRegExp(pattern) {
|
||||
return new RegExp('^' + pattern.split('*').map(_utils.escapeRegExp).join('.*') + '$', 'ig');
|
||||
}
|
||||
function filterProjects(projects, projectNames) {
|
||||
if (!projectNames) return [...projects];
|
||||
const projectNamesToFind = new Set();
|
||||
const unmatchedProjectNames = new Map();
|
||||
const patterns = new Set();
|
||||
for (const name of projectNames) {
|
||||
const lowerCaseName = name.toLocaleLowerCase();
|
||||
if (lowerCaseName.includes('*')) {
|
||||
patterns.add(wildcardPatternToRegExp(lowerCaseName));
|
||||
} else {
|
||||
projectNamesToFind.add(lowerCaseName);
|
||||
unmatchedProjectNames.set(lowerCaseName, name);
|
||||
}
|
||||
}
|
||||
const result = projects.filter(project => {
|
||||
const lowerCaseName = project.project.name.toLocaleLowerCase();
|
||||
if (projectNamesToFind.has(lowerCaseName)) {
|
||||
unmatchedProjectNames.delete(lowerCaseName);
|
||||
return true;
|
||||
}
|
||||
for (const regex of patterns) {
|
||||
regex.lastIndex = 0;
|
||||
if (regex.test(lowerCaseName)) return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (unmatchedProjectNames.size) {
|
||||
const unknownProjectNames = Array.from(unmatchedProjectNames.values()).map(n => `"${n}"`).join(', ');
|
||||
throw new Error(`Project(s) ${unknownProjectNames} not found. Available projects: ${projects.map(p => `"${p.project.name}"`).join(', ')}`);
|
||||
}
|
||||
if (!result.length) {
|
||||
const allProjects = projects.map(p => `"${p.project.name}"`).join(', ');
|
||||
throw new Error(`No projects matched. Available projects: ${allProjects}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function buildTeardownToSetupsMap(projects) {
|
||||
const result = new Map();
|
||||
for (const project of projects) {
|
||||
if (project.teardown) {
|
||||
const setups = result.get(project.teardown) || [];
|
||||
setups.push(project);
|
||||
result.set(project.teardown, setups);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function buildProjectsClosure(projects, hasTests) {
|
||||
const result = new Map();
|
||||
const visit = (depth, project) => {
|
||||
if (depth > 100) {
|
||||
const error = new Error('Circular dependency detected between projects.');
|
||||
error.stack = '';
|
||||
throw error;
|
||||
}
|
||||
if (depth === 0 && hasTests && !hasTests(project)) return;
|
||||
if (result.get(project) !== 'dependency') result.set(project, depth ? 'dependency' : 'top-level');
|
||||
for (const dep of project.deps) visit(depth + 1, dep);
|
||||
if (project.teardown) visit(depth + 1, project.teardown);
|
||||
};
|
||||
for (const p of projects) visit(0, p);
|
||||
return result;
|
||||
}
|
||||
function buildDependentProjects(forProjects, projects) {
|
||||
const reverseDeps = new Map(projects.map(p => [p, []]));
|
||||
for (const project of projects) {
|
||||
for (const dep of project.deps) reverseDeps.get(dep).push(project);
|
||||
}
|
||||
const result = new Set();
|
||||
const visit = (depth, project) => {
|
||||
if (depth > 100) {
|
||||
const error = new Error('Circular dependency detected between projects.');
|
||||
error.stack = '';
|
||||
throw error;
|
||||
}
|
||||
result.add(project);
|
||||
for (const reverseDep of reverseDeps.get(project)) visit(depth + 1, reverseDep);
|
||||
if (project.teardown) visit(depth + 1, project.teardown);
|
||||
};
|
||||
for (const forProject of forProjects) visit(0, forProject);
|
||||
return result;
|
||||
}
|
||||
async function collectFilesForProject(project, fsCache = new Map()) {
|
||||
const extensions = new Set(['.js', '.ts', '.mjs', '.mts', '.cjs', '.cts', '.jsx', '.tsx', '.mjsx', '.mtsx', '.cjsx', '.ctsx']);
|
||||
const testFileExtension = file => extensions.has(_path.default.extname(file));
|
||||
const allFiles = await cachedCollectFiles(project.project.testDir, project.respectGitIgnore, fsCache);
|
||||
const testMatch = (0, _util2.createFileMatcher)(project.project.testMatch);
|
||||
const testIgnore = (0, _util2.createFileMatcher)(project.project.testIgnore);
|
||||
const testFiles = allFiles.filter(file => {
|
||||
if (!testFileExtension(file)) return false;
|
||||
const isTest = !testIgnore(file) && testMatch(file);
|
||||
if (!isTest) return false;
|
||||
return true;
|
||||
});
|
||||
return testFiles;
|
||||
}
|
||||
async function cachedCollectFiles(testDir, respectGitIgnore, fsCache) {
|
||||
const key = testDir + ':' + respectGitIgnore;
|
||||
let result = fsCache.get(key);
|
||||
if (!result) {
|
||||
result = await collectFiles(testDir, respectGitIgnore);
|
||||
fsCache.set(key, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
async function collectFiles(testDir, respectGitIgnore) {
|
||||
if (!_fs.default.existsSync(testDir)) return [];
|
||||
if (!_fs.default.statSync(testDir).isDirectory()) return [];
|
||||
const checkIgnores = (entryPath, rules, isDirectory, parentStatus) => {
|
||||
let status = parentStatus;
|
||||
for (const rule of rules) {
|
||||
const ruleIncludes = rule.negate;
|
||||
if (status === 'included' === ruleIncludes) continue;
|
||||
const relative = _path.default.relative(rule.dir, entryPath);
|
||||
if (rule.match('/' + relative) || rule.match(relative)) {
|
||||
// Matches "/dir/file" or "dir/file"
|
||||
status = ruleIncludes ? 'included' : 'ignored';
|
||||
} else if (isDirectory && (rule.match('/' + relative + '/') || rule.match(relative + '/'))) {
|
||||
// Matches "/dir/subdir/" or "dir/subdir/" for directories.
|
||||
status = ruleIncludes ? 'included' : 'ignored';
|
||||
} else if (isDirectory && ruleIncludes && (rule.match('/' + relative, true) || rule.match(relative, true))) {
|
||||
// Matches "/dir/donotskip/" when "/dir" is excluded, but "!/dir/donotskip/file" is included.
|
||||
status = 'ignored-but-recurse';
|
||||
}
|
||||
}
|
||||
return status;
|
||||
};
|
||||
const files = [];
|
||||
const visit = async (dir, rules, status) => {
|
||||
const entries = await readDirAsync(dir, {
|
||||
withFileTypes: true
|
||||
});
|
||||
entries.sort((a, b) => a.name.localeCompare(b.name));
|
||||
if (respectGitIgnore) {
|
||||
const gitignore = entries.find(e => e.isFile() && e.name === '.gitignore');
|
||||
if (gitignore) {
|
||||
const content = await readFileAsync(_path.default.join(dir, gitignore.name), 'utf8');
|
||||
const newRules = content.split(/\r?\n/).map(s => {
|
||||
s = s.trim();
|
||||
if (!s) return;
|
||||
// Use flipNegate, because we handle negation ourselves.
|
||||
const rule = new _utilsBundle.minimatch.Minimatch(s, {
|
||||
matchBase: true,
|
||||
dot: true,
|
||||
flipNegate: true
|
||||
});
|
||||
if (rule.comment) return;
|
||||
rule.dir = dir;
|
||||
return rule;
|
||||
}).filter(rule => !!rule);
|
||||
rules = [...rules, ...newRules];
|
||||
}
|
||||
}
|
||||
for (const entry of entries) {
|
||||
if (entry.name === '.' || entry.name === '..') continue;
|
||||
if (entry.isFile() && entry.name === '.gitignore') continue;
|
||||
if (entry.isDirectory() && entry.name === 'node_modules') continue;
|
||||
const entryPath = _path.default.join(dir, entry.name);
|
||||
const entryStatus = checkIgnores(entryPath, rules, entry.isDirectory(), status);
|
||||
if (entry.isDirectory() && entryStatus !== 'ignored') await visit(entryPath, rules, entryStatus);else if (entry.isFile() && entryStatus === 'included') files.push(entryPath);
|
||||
}
|
||||
};
|
||||
await visit(testDir, [], 'included');
|
||||
return files;
|
||||
}
|
||||
100
node_modules/playwright/lib/runner/rebase.js
generated
vendored
Normal file
100
node_modules/playwright/lib/runner/rebase.js
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.addSuggestedRebaseline = addSuggestedRebaseline;
|
||||
exports.applySuggestedRebaselines = applySuggestedRebaselines;
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _babelBundle = require("../transform/babelBundle");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _projectUtils = require("./projectUtils");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const t = _babelBundle.types;
|
||||
const suggestedRebaselines = new _utils.MultiMap();
|
||||
function addSuggestedRebaseline(location, suggestedRebaseline) {
|
||||
suggestedRebaselines.set(location.file, {
|
||||
location,
|
||||
code: suggestedRebaseline
|
||||
});
|
||||
}
|
||||
async function applySuggestedRebaselines(config, reporter) {
|
||||
if (config.config.updateSnapshots !== 'all' && config.config.updateSnapshots !== 'missing') return;
|
||||
if (!suggestedRebaselines.size) return;
|
||||
const [project] = (0, _projectUtils.filterProjects)(config.projects, config.cliProjectFilter);
|
||||
if (!project) return;
|
||||
const patches = [];
|
||||
const files = [];
|
||||
for (const fileName of [...suggestedRebaselines.keys()].sort()) {
|
||||
const source = await _fs.default.promises.readFile(fileName, 'utf8');
|
||||
const lines = source.split('\n');
|
||||
const replacements = suggestedRebaselines.get(fileName);
|
||||
const fileNode = (0, _babelBundle.babelParse)(source, fileName, true);
|
||||
const ranges = [];
|
||||
(0, _babelBundle.traverse)(fileNode, {
|
||||
CallExpression: path => {
|
||||
const node = path.node;
|
||||
if (node.arguments.length !== 1) return;
|
||||
if (!t.isMemberExpression(node.callee)) return;
|
||||
const argument = node.arguments[0];
|
||||
if (!t.isStringLiteral(argument) && !t.isTemplateLiteral(argument)) return;
|
||||
const matcher = node.callee.property;
|
||||
for (const replacement of replacements) {
|
||||
// In Babel, rows are 1-based, columns are 0-based.
|
||||
if (matcher.loc.start.line !== replacement.location.line) continue;
|
||||
if (matcher.loc.start.column + 1 !== replacement.location.column) continue;
|
||||
const indent = lines[matcher.loc.start.line - 1].match(/^\s*/)[0];
|
||||
const newText = replacement.code.replace(/\{indent\}/g, indent);
|
||||
ranges.push({
|
||||
start: matcher.start,
|
||||
end: node.end,
|
||||
oldText: source.substring(matcher.start, node.end),
|
||||
newText
|
||||
});
|
||||
// We can have multiple, hopefully equal, replacements for the same location,
|
||||
// for example when a single test runs multiple times because of projects or retries.
|
||||
// Do not apply multiple replacements for the same assertion.
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
ranges.sort((a, b) => b.start - a.start);
|
||||
let result = source;
|
||||
for (const range of ranges) result = result.substring(0, range.start) + range.newText + result.substring(range.end);
|
||||
const relativeName = _path.default.relative(process.cwd(), fileName);
|
||||
files.push(relativeName);
|
||||
patches.push(createPatch(relativeName, source, result));
|
||||
}
|
||||
const patchFile = _path.default.join(project.project.outputDir, 'rebaselines.patch');
|
||||
await _fs.default.promises.mkdir(_path.default.dirname(patchFile), {
|
||||
recursive: true
|
||||
});
|
||||
await _fs.default.promises.writeFile(patchFile, patches.join('\n'));
|
||||
const fileList = files.map(file => ' ' + _utilsBundle.colors.dim(file)).join('\n');
|
||||
reporter.onStdErr(`\nNew baselines created for:\n\n${fileList}\n\n ` + _utilsBundle.colors.cyan('git apply ' + _path.default.relative(process.cwd(), patchFile)) + '\n');
|
||||
}
|
||||
function createPatch(fileName, before, after) {
|
||||
const file = fileName.replace(/\\/g, '/');
|
||||
const text = _utilsBundle.diff.createPatch(file, before, after, undefined, undefined, {
|
||||
context: 3
|
||||
});
|
||||
return ['diff --git a/' + file + ' b/' + file, '--- a/' + file, '+++ b/' + file, ...text.split('\n').slice(4)].join('\n');
|
||||
}
|
||||
152
node_modules/playwright/lib/runner/reporters.js
generated
vendored
Normal file
152
node_modules/playwright/lib/runner/reporters.js
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.createErrorCollectingReporter = createErrorCollectingReporter;
|
||||
exports.createReporterForTestServer = createReporterForTestServer;
|
||||
exports.createReporters = createReporters;
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _base = require("../reporters/base");
|
||||
var _dot = _interopRequireDefault(require("../reporters/dot"));
|
||||
var _empty = _interopRequireDefault(require("../reporters/empty"));
|
||||
var _github = _interopRequireDefault(require("../reporters/github"));
|
||||
var _html = _interopRequireDefault(require("../reporters/html"));
|
||||
var _json = _interopRequireDefault(require("../reporters/json"));
|
||||
var _junit = _interopRequireDefault(require("../reporters/junit"));
|
||||
var _line = _interopRequireDefault(require("../reporters/line"));
|
||||
var _list = _interopRequireDefault(require("../reporters/list"));
|
||||
var _loadUtils = require("./loadUtils");
|
||||
var _blob = require("../reporters/blob");
|
||||
var _reporterV = require("../reporters/reporterV2");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
async function createReporters(config, mode, isTestServer, descriptions) {
|
||||
var _descriptions;
|
||||
const defaultReporters = {
|
||||
blob: _blob.BlobReporter,
|
||||
dot: mode === 'list' ? ListModeReporter : _dot.default,
|
||||
line: mode === 'list' ? ListModeReporter : _line.default,
|
||||
list: mode === 'list' ? ListModeReporter : _list.default,
|
||||
github: _github.default,
|
||||
json: _json.default,
|
||||
junit: _junit.default,
|
||||
null: _empty.default,
|
||||
html: _html.default
|
||||
};
|
||||
const reporters = [];
|
||||
(_descriptions = descriptions) !== null && _descriptions !== void 0 ? _descriptions : descriptions = config.config.reporter;
|
||||
if (config.configCLIOverrides.additionalReporters) descriptions = [...descriptions, ...config.configCLIOverrides.additionalReporters];
|
||||
const runOptions = reporterOptions(config, mode, isTestServer);
|
||||
for (const r of descriptions) {
|
||||
const [name, arg] = r;
|
||||
const options = {
|
||||
...runOptions,
|
||||
...arg
|
||||
};
|
||||
if (name in defaultReporters) {
|
||||
reporters.push(new defaultReporters[name](options));
|
||||
} else {
|
||||
const reporterConstructor = await (0, _loadUtils.loadReporter)(config, name);
|
||||
reporters.push((0, _reporterV.wrapReporterAsV2)(new reporterConstructor(options)));
|
||||
}
|
||||
}
|
||||
if (process.env.PW_TEST_REPORTER) {
|
||||
const reporterConstructor = await (0, _loadUtils.loadReporter)(config, process.env.PW_TEST_REPORTER);
|
||||
reporters.push((0, _reporterV.wrapReporterAsV2)(new reporterConstructor(runOptions)));
|
||||
}
|
||||
const someReporterPrintsToStdio = reporters.some(r => r.printsToStdio ? r.printsToStdio() : true);
|
||||
if (reporters.length && !someReporterPrintsToStdio) {
|
||||
// Add a line/dot/list-mode reporter for convenience.
|
||||
// Important to put it first, just in case some other reporter stalls onEnd.
|
||||
if (mode === 'list') reporters.unshift(new ListModeReporter());else if (mode !== 'merge') reporters.unshift(!process.env.CI ? new _line.default({
|
||||
omitFailures: true
|
||||
}) : new _dot.default());
|
||||
}
|
||||
return reporters;
|
||||
}
|
||||
async function createReporterForTestServer(file, messageSink) {
|
||||
const reporterConstructor = await (0, _loadUtils.loadReporter)(null, file);
|
||||
return (0, _reporterV.wrapReporterAsV2)(new reporterConstructor({
|
||||
_send: messageSink
|
||||
}));
|
||||
}
|
||||
function createErrorCollectingReporter(writeToConsole) {
|
||||
const errors = [];
|
||||
return {
|
||||
version: () => 'v2',
|
||||
onError(error) {
|
||||
errors.push(error);
|
||||
if (writeToConsole) process.stdout.write((0, _base.formatError)(error, _base.colors.enabled).message + '\n');
|
||||
},
|
||||
errors: () => errors
|
||||
};
|
||||
}
|
||||
function reporterOptions(config, mode, isTestServer) {
|
||||
return {
|
||||
configDir: config.configDir,
|
||||
_mode: mode,
|
||||
_isTestServer: isTestServer,
|
||||
_commandHash: computeCommandHash(config)
|
||||
};
|
||||
}
|
||||
function computeCommandHash(config) {
|
||||
const parts = [];
|
||||
// Include project names for readability.
|
||||
if (config.cliProjectFilter) parts.push(...config.cliProjectFilter);
|
||||
const command = {};
|
||||
if (config.cliArgs.length) command.cliArgs = config.cliArgs;
|
||||
if (config.cliGrep) command.cliGrep = config.cliGrep;
|
||||
if (config.cliGrepInvert) command.cliGrepInvert = config.cliGrepInvert;
|
||||
if (config.cliOnlyChanged) command.cliOnlyChanged = config.cliOnlyChanged;
|
||||
if (Object.keys(command).length) parts.push((0, _utils.calculateSha1)(JSON.stringify(command)).substring(0, 7));
|
||||
return parts.join('-');
|
||||
}
|
||||
class ListModeReporter {
|
||||
constructor() {
|
||||
this.config = void 0;
|
||||
}
|
||||
version() {
|
||||
return 'v2';
|
||||
}
|
||||
onConfigure(config) {
|
||||
this.config = config;
|
||||
}
|
||||
onBegin(suite) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Listing tests:`);
|
||||
const tests = suite.allTests();
|
||||
const files = new Set();
|
||||
for (const test of tests) {
|
||||
// root, project, file, ...describes, test
|
||||
const [, projectName,, ...titles] = test.titlePath();
|
||||
const location = `${_path.default.relative(this.config.rootDir, test.location.file)}:${test.location.line}:${test.location.column}`;
|
||||
const projectTitle = projectName ? `[${projectName}] › ` : '';
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(` ${projectTitle}${location} › ${titles.join(' › ')}`);
|
||||
files.add(test.location.file);
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Total: ${tests.length} ${tests.length === 1 ? 'test' : 'tests'} in ${files.size} ${files.size === 1 ? 'file' : 'files'}`);
|
||||
}
|
||||
onError(error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('\n' + (0, _base.formatError)(error, false).message);
|
||||
}
|
||||
}
|
||||
118
node_modules/playwright/lib/runner/runner.js
generated
vendored
Normal file
118
node_modules/playwright/lib/runner/runner.js
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Runner = void 0;
|
||||
var _webServerPlugin = require("../plugins/webServerPlugin");
|
||||
var _projectUtils = require("./projectUtils");
|
||||
var _reporters = require("./reporters");
|
||||
var _tasks = require("./tasks");
|
||||
var _compilationCache = require("../transform/compilationCache");
|
||||
var _internalReporter = require("../reporters/internalReporter");
|
||||
var _lastRun = require("./lastRun");
|
||||
/**
|
||||
* Copyright 2019 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Runner {
|
||||
constructor(config) {
|
||||
this._config = void 0;
|
||||
this._config = config;
|
||||
}
|
||||
async listTestFiles(projectNames) {
|
||||
const projects = (0, _projectUtils.filterProjects)(this._config.projects, projectNames);
|
||||
const report = {
|
||||
projects: []
|
||||
};
|
||||
for (const project of projects) {
|
||||
report.projects.push({
|
||||
name: project.project.name,
|
||||
testDir: project.project.testDir,
|
||||
use: {
|
||||
testIdAttribute: project.project.use.testIdAttribute
|
||||
},
|
||||
files: await (0, _projectUtils.collectFilesForProject)(project)
|
||||
});
|
||||
}
|
||||
return report;
|
||||
}
|
||||
async runAllTests() {
|
||||
const config = this._config;
|
||||
const listOnly = config.cliListOnly;
|
||||
|
||||
// Legacy webServer support.
|
||||
(0, _webServerPlugin.webServerPluginsForConfig)(config).forEach(p => config.plugins.push({
|
||||
factory: p
|
||||
}));
|
||||
const reporters = await (0, _reporters.createReporters)(config, listOnly ? 'list' : 'test', false);
|
||||
const lastRun = new _lastRun.LastRunReporter(config);
|
||||
if (config.cliLastFailed) await lastRun.filterLastFailed();
|
||||
const reporter = new _internalReporter.InternalReporter([...reporters, lastRun]);
|
||||
const tasks = listOnly ? [(0, _tasks.createLoadTask)('in-process', {
|
||||
failOnLoadErrors: true,
|
||||
filterOnly: false
|
||||
}), (0, _tasks.createReportBeginTask)()] : [(0, _tasks.createApplyRebaselinesTask)(), ...(0, _tasks.createGlobalSetupTasks)(config), (0, _tasks.createLoadTask)('in-process', {
|
||||
filterOnly: true,
|
||||
failOnLoadErrors: true
|
||||
}), ...(0, _tasks.createRunTestsTasks)(config)];
|
||||
const status = await (0, _tasks.runTasks)(new _tasks.TestRun(config, reporter), tasks, config.config.globalTimeout);
|
||||
|
||||
// Calling process.exit() might truncate large stdout/stderr output.
|
||||
// See https://github.com/nodejs/node/issues/6456.
|
||||
// See https://github.com/nodejs/node/issues/12921
|
||||
await new Promise(resolve => process.stdout.write('', () => resolve()));
|
||||
await new Promise(resolve => process.stderr.write('', () => resolve()));
|
||||
return status;
|
||||
}
|
||||
async findRelatedTestFiles(files) {
|
||||
const errorReporter = (0, _reporters.createErrorCollectingReporter)();
|
||||
const reporter = new _internalReporter.InternalReporter([errorReporter]);
|
||||
const status = await (0, _tasks.runTasks)(new _tasks.TestRun(this._config, reporter), [...(0, _tasks.createPluginSetupTasks)(this._config), (0, _tasks.createLoadTask)('in-process', {
|
||||
failOnLoadErrors: true,
|
||||
filterOnly: false,
|
||||
populateDependencies: true
|
||||
})]);
|
||||
if (status !== 'passed') return {
|
||||
errors: errorReporter.errors(),
|
||||
testFiles: []
|
||||
};
|
||||
return {
|
||||
testFiles: (0, _compilationCache.affectedTestFiles)(files)
|
||||
};
|
||||
}
|
||||
async runDevServer() {
|
||||
const reporter = new _internalReporter.InternalReporter([(0, _reporters.createErrorCollectingReporter)(true)]);
|
||||
const status = await (0, _tasks.runTasks)(new _tasks.TestRun(this._config, reporter), [...(0, _tasks.createPluginSetupTasks)(this._config), (0, _tasks.createLoadTask)('in-process', {
|
||||
failOnLoadErrors: true,
|
||||
filterOnly: false
|
||||
}), (0, _tasks.createStartDevServerTask)(), {
|
||||
title: 'wait until interrupted',
|
||||
setup: async () => new Promise(() => {})
|
||||
}]);
|
||||
return {
|
||||
status
|
||||
};
|
||||
}
|
||||
async clearCache() {
|
||||
const reporter = new _internalReporter.InternalReporter([(0, _reporters.createErrorCollectingReporter)(true)]);
|
||||
const status = await (0, _tasks.runTasks)(new _tasks.TestRun(this._config, reporter), [...(0, _tasks.createPluginSetupTasks)(this._config), (0, _tasks.createClearCacheTask)(this._config)]);
|
||||
return {
|
||||
status
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.Runner = Runner;
|
||||
100
node_modules/playwright/lib/runner/sigIntWatcher.js
generated
vendored
Normal file
100
node_modules/playwright/lib/runner/sigIntWatcher.js
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.SigIntWatcher = void 0;
|
||||
var _class2;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class SigIntWatcher {
|
||||
constructor() {
|
||||
this._hadSignal = false;
|
||||
this._sigintPromise = void 0;
|
||||
this._sigintHandler = void 0;
|
||||
let sigintCallback;
|
||||
this._sigintPromise = new Promise(f => sigintCallback = f);
|
||||
this._sigintHandler = () => {
|
||||
FixedNodeSIGINTHandler.off(this._sigintHandler);
|
||||
this._hadSignal = true;
|
||||
sigintCallback();
|
||||
};
|
||||
FixedNodeSIGINTHandler.on(this._sigintHandler);
|
||||
}
|
||||
promise() {
|
||||
return this._sigintPromise;
|
||||
}
|
||||
hadSignal() {
|
||||
return this._hadSignal;
|
||||
}
|
||||
disarm() {
|
||||
FixedNodeSIGINTHandler.off(this._sigintHandler);
|
||||
}
|
||||
}
|
||||
|
||||
// NPM/NPX will send us duplicate SIGINT signals, so we need to ignore them.
|
||||
exports.SigIntWatcher = SigIntWatcher;
|
||||
class FixedNodeSIGINTHandler {
|
||||
static _install() {
|
||||
if (!this._handlerInstalled) {
|
||||
this._handlerInstalled = true;
|
||||
process.on('SIGINT', this._dispatch);
|
||||
}
|
||||
}
|
||||
static _uninstall() {
|
||||
if (this._handlerInstalled) {
|
||||
this._handlerInstalled = false;
|
||||
process.off('SIGINT', this._dispatch);
|
||||
}
|
||||
}
|
||||
static on(handler) {
|
||||
this._handlers.push(handler);
|
||||
if (this._handlers.length === 1) this._install();
|
||||
}
|
||||
static off(handler) {
|
||||
this._handlers = this._handlers.filter(h => h !== handler);
|
||||
if (!this._ignoreNextSIGINTs && !this._handlers.length) this._uninstall();
|
||||
}
|
||||
}
|
||||
_class2 = FixedNodeSIGINTHandler;
|
||||
FixedNodeSIGINTHandler._handlers = [];
|
||||
FixedNodeSIGINTHandler._ignoreNextSIGINTs = false;
|
||||
FixedNodeSIGINTHandler._handlerInstalled = false;
|
||||
FixedNodeSIGINTHandler._dispatch = () => {
|
||||
if (_class2._ignoreNextSIGINTs) return;
|
||||
_class2._ignoreNextSIGINTs = true;
|
||||
setTimeout(() => {
|
||||
_class2._ignoreNextSIGINTs = false;
|
||||
// We remove the handler so that second Ctrl+C immediately kills the process
|
||||
// via the default sigint handler. This is handy in the case where our shutdown
|
||||
// takes a lot of time or is buggy.
|
||||
//
|
||||
// When running through NPM we might get multiple SIGINT signals
|
||||
// for a single Ctrl+C - this is an NPM bug present since NPM v6+.
|
||||
// https://github.com/npm/cli/issues/1591
|
||||
// https://github.com/npm/cli/issues/2124
|
||||
// https://github.com/npm/cli/issues/5021
|
||||
//
|
||||
// Therefore, removing the handler too soon will just kill the process
|
||||
// with default handler without printing the results.
|
||||
// We work around this by giving NPM 1000ms to send us duplicate signals.
|
||||
// The side effect is that slow shutdown or bug in our process will force
|
||||
// the user to hit Ctrl+C again after at least a second.
|
||||
if (!_class2._handlers.length) _class2._uninstall();
|
||||
}, 1000);
|
||||
for (const handler of _class2._handlers) handler();
|
||||
};
|
||||
136
node_modules/playwright/lib/runner/taskRunner.js
generated
vendored
Normal file
136
node_modules/playwright/lib/runner/taskRunner.js
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.TaskRunner = void 0;
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _sigIntWatcher = require("./sigIntWatcher");
|
||||
var _util = require("../util");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class TaskRunner {
|
||||
constructor(reporter, globalTimeoutForError) {
|
||||
this._tasks = [];
|
||||
this._reporter = void 0;
|
||||
this._hasErrors = false;
|
||||
this._interrupted = false;
|
||||
this._isTearDown = false;
|
||||
this._globalTimeoutForError = void 0;
|
||||
this._reporter = reporter;
|
||||
this._globalTimeoutForError = globalTimeoutForError;
|
||||
}
|
||||
addTask(task) {
|
||||
this._tasks.push(task);
|
||||
}
|
||||
async run(context, deadline, cancelPromise) {
|
||||
const {
|
||||
status,
|
||||
cleanup
|
||||
} = await this.runDeferCleanup(context, deadline, cancelPromise);
|
||||
const teardownStatus = await cleanup();
|
||||
return status === 'passed' ? teardownStatus : status;
|
||||
}
|
||||
async runDeferCleanup(context, deadline, cancelPromise = new _utils.ManualPromise()) {
|
||||
const sigintWatcher = new _sigIntWatcher.SigIntWatcher();
|
||||
const timeoutWatcher = new TimeoutWatcher(deadline);
|
||||
const teardownRunner = new TaskRunner(this._reporter, this._globalTimeoutForError);
|
||||
teardownRunner._isTearDown = true;
|
||||
let currentTaskName;
|
||||
const taskLoop = async () => {
|
||||
for (const task of this._tasks) {
|
||||
currentTaskName = task.title;
|
||||
if (this._interrupted) break;
|
||||
(0, _utilsBundle.debug)('pw:test:task')(`"${task.title}" started`);
|
||||
const errors = [];
|
||||
const softErrors = [];
|
||||
try {
|
||||
var _task$setup;
|
||||
teardownRunner._tasks.unshift({
|
||||
title: `teardown for ${task.title}`,
|
||||
setup: task.teardown
|
||||
});
|
||||
await ((_task$setup = task.setup) === null || _task$setup === void 0 ? void 0 : _task$setup.call(task, context, errors, softErrors));
|
||||
} catch (e) {
|
||||
(0, _utilsBundle.debug)('pw:test:task')(`error in "${task.title}": `, e);
|
||||
errors.push((0, _util.serializeError)(e));
|
||||
} finally {
|
||||
for (const error of [...softErrors, ...errors]) {
|
||||
var _this$_reporter$onErr, _this$_reporter;
|
||||
(_this$_reporter$onErr = (_this$_reporter = this._reporter).onError) === null || _this$_reporter$onErr === void 0 || _this$_reporter$onErr.call(_this$_reporter, error);
|
||||
}
|
||||
if (errors.length) {
|
||||
if (!this._isTearDown) this._interrupted = true;
|
||||
this._hasErrors = true;
|
||||
}
|
||||
}
|
||||
(0, _utilsBundle.debug)('pw:test:task')(`"${task.title}" finished`);
|
||||
}
|
||||
};
|
||||
await Promise.race([taskLoop(), cancelPromise, sigintWatcher.promise(), timeoutWatcher.promise]);
|
||||
sigintWatcher.disarm();
|
||||
timeoutWatcher.disarm();
|
||||
|
||||
// Prevent subsequent tasks from running.
|
||||
this._interrupted = true;
|
||||
let status = 'passed';
|
||||
if (sigintWatcher.hadSignal() || cancelPromise !== null && cancelPromise !== void 0 && cancelPromise.isDone()) {
|
||||
status = 'interrupted';
|
||||
} else if (timeoutWatcher.timedOut()) {
|
||||
var _this$_reporter$onErr2, _this$_reporter2;
|
||||
(_this$_reporter$onErr2 = (_this$_reporter2 = this._reporter).onError) === null || _this$_reporter$onErr2 === void 0 || _this$_reporter$onErr2.call(_this$_reporter2, {
|
||||
message: _utilsBundle.colors.red(`Timed out waiting ${this._globalTimeoutForError / 1000}s for the ${currentTaskName} to run`)
|
||||
});
|
||||
status = 'timedout';
|
||||
} else if (this._hasErrors) {
|
||||
status = 'failed';
|
||||
}
|
||||
cancelPromise === null || cancelPromise === void 0 || cancelPromise.resolve();
|
||||
// Note that upon hitting deadline, we "run cleanup", but it exits immediately
|
||||
// because of the same deadline. Essentially, we're not performing any cleanup.
|
||||
const cleanup = () => teardownRunner.runDeferCleanup(context, deadline).then(r => r.status);
|
||||
return {
|
||||
status,
|
||||
cleanup
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.TaskRunner = TaskRunner;
|
||||
class TimeoutWatcher {
|
||||
constructor(deadline) {
|
||||
this._timedOut = false;
|
||||
this.promise = new _utils.ManualPromise();
|
||||
this._timer = void 0;
|
||||
if (!deadline) return;
|
||||
if (deadline - (0, _utils.monotonicTime)() <= 0) {
|
||||
this._timedOut = true;
|
||||
this.promise.resolve();
|
||||
return;
|
||||
}
|
||||
this._timer = setTimeout(() => {
|
||||
this._timedOut = true;
|
||||
this.promise.resolve();
|
||||
}, deadline - (0, _utils.monotonicTime)());
|
||||
}
|
||||
timedOut() {
|
||||
return this._timedOut;
|
||||
}
|
||||
disarm() {
|
||||
clearTimeout(this._timer);
|
||||
}
|
||||
}
|
||||
430
node_modules/playwright/lib/runner/tasks.js
generated
vendored
Normal file
430
node_modules/playwright/lib/runner/tasks.js
generated
vendored
Normal file
@@ -0,0 +1,430 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.TestRun = void 0;
|
||||
exports.createApplyRebaselinesTask = createApplyRebaselinesTask;
|
||||
exports.createClearCacheTask = createClearCacheTask;
|
||||
exports.createGlobalSetupTasks = createGlobalSetupTasks;
|
||||
exports.createListFilesTask = createListFilesTask;
|
||||
exports.createLoadTask = createLoadTask;
|
||||
exports.createPluginSetupTasks = createPluginSetupTasks;
|
||||
exports.createReportBeginTask = createReportBeginTask;
|
||||
exports.createRunTestsTasks = createRunTestsTasks;
|
||||
exports.createStartDevServerTask = createStartDevServerTask;
|
||||
exports.runTasks = runTasks;
|
||||
exports.runTasksDeferCleanup = runTasksDeferCleanup;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _util = require("util");
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _testGroups = require("../runner/testGroups");
|
||||
var _taskRunner = require("./taskRunner");
|
||||
var _loadUtils = require("./loadUtils");
|
||||
var _util2 = require("../util");
|
||||
var _test = require("../common/test");
|
||||
var _projectUtils = require("./projectUtils");
|
||||
var _failureTracker = require("./failureTracker");
|
||||
var _vcs = require("./vcs");
|
||||
var _compilationCache = require("../transform/compilationCache");
|
||||
var _rebase = require("./rebase");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const readDirAsync = (0, _util.promisify)(_fs.default.readdir);
|
||||
class TestRun {
|
||||
constructor(config, reporter) {
|
||||
this.config = void 0;
|
||||
this.reporter = void 0;
|
||||
this.failureTracker = void 0;
|
||||
this.rootSuite = undefined;
|
||||
this.phases = [];
|
||||
this.projectFiles = new Map();
|
||||
this.projectSuites = new Map();
|
||||
this.config = config;
|
||||
this.reporter = reporter;
|
||||
this.failureTracker = new _failureTracker.FailureTracker(config);
|
||||
}
|
||||
}
|
||||
exports.TestRun = TestRun;
|
||||
async function runTasks(testRun, tasks, globalTimeout, cancelPromise) {
|
||||
const deadline = globalTimeout ? (0, _utils.monotonicTime)() + globalTimeout : 0;
|
||||
const taskRunner = new _taskRunner.TaskRunner(testRun.reporter, globalTimeout || 0);
|
||||
for (const task of tasks) taskRunner.addTask(task);
|
||||
testRun.reporter.onConfigure(testRun.config.config);
|
||||
const status = await taskRunner.run(testRun, deadline, cancelPromise);
|
||||
return await finishTaskRun(testRun, status);
|
||||
}
|
||||
async function runTasksDeferCleanup(testRun, tasks) {
|
||||
const taskRunner = new _taskRunner.TaskRunner(testRun.reporter, 0);
|
||||
for (const task of tasks) taskRunner.addTask(task);
|
||||
testRun.reporter.onConfigure(testRun.config.config);
|
||||
const {
|
||||
status,
|
||||
cleanup
|
||||
} = await taskRunner.runDeferCleanup(testRun, 0);
|
||||
return {
|
||||
status: await finishTaskRun(testRun, status),
|
||||
cleanup
|
||||
};
|
||||
}
|
||||
async function finishTaskRun(testRun, status) {
|
||||
if (status === 'passed') status = testRun.failureTracker.result();
|
||||
const modifiedResult = await testRun.reporter.onEnd({
|
||||
status
|
||||
});
|
||||
if (modifiedResult && modifiedResult.status) status = modifiedResult.status;
|
||||
await testRun.reporter.onExit();
|
||||
return status;
|
||||
}
|
||||
function createGlobalSetupTasks(config) {
|
||||
const tasks = [];
|
||||
if (!config.configCLIOverrides.preserveOutputDir && !process.env.PW_TEST_NO_REMOVE_OUTPUT_DIRS) tasks.push(createRemoveOutputDirsTask());
|
||||
tasks.push(...createPluginSetupTasks(config), ...config.globalTeardowns.map(file => createGlobalTeardownTask(file, config)).reverse(), ...config.globalSetups.map(file => createGlobalSetupTask(file, config)));
|
||||
return tasks;
|
||||
}
|
||||
function createRunTestsTasks(config) {
|
||||
return [createPhasesTask(), createReportBeginTask(), ...config.plugins.map(plugin => createPluginBeginTask(plugin)), createRunTestsTask()];
|
||||
}
|
||||
function createClearCacheTask(config) {
|
||||
return {
|
||||
title: 'clear cache',
|
||||
setup: async () => {
|
||||
await (0, _util2.removeDirAndLogToConsole)(_compilationCache.cacheDir);
|
||||
for (const plugin of config.plugins) {
|
||||
var _plugin$instance, _plugin$instance$clea;
|
||||
await ((_plugin$instance = plugin.instance) === null || _plugin$instance === void 0 || (_plugin$instance$clea = _plugin$instance.clearCache) === null || _plugin$instance$clea === void 0 ? void 0 : _plugin$instance$clea.call(_plugin$instance));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
function createReportBeginTask() {
|
||||
return {
|
||||
title: 'report begin',
|
||||
setup: async testRun => {
|
||||
var _testRun$reporter$onB, _testRun$reporter;
|
||||
(_testRun$reporter$onB = (_testRun$reporter = testRun.reporter).onBegin) === null || _testRun$reporter$onB === void 0 || _testRun$reporter$onB.call(_testRun$reporter, testRun.rootSuite);
|
||||
},
|
||||
teardown: async ({}) => {}
|
||||
};
|
||||
}
|
||||
function createPluginSetupTasks(config) {
|
||||
return config.plugins.map(plugin => ({
|
||||
title: 'plugin setup',
|
||||
setup: async ({
|
||||
reporter
|
||||
}) => {
|
||||
var _plugin$instance2, _plugin$instance2$set;
|
||||
if (typeof plugin.factory === 'function') plugin.instance = await plugin.factory();else plugin.instance = plugin.factory;
|
||||
await ((_plugin$instance2 = plugin.instance) === null || _plugin$instance2 === void 0 || (_plugin$instance2$set = _plugin$instance2.setup) === null || _plugin$instance2$set === void 0 ? void 0 : _plugin$instance2$set.call(_plugin$instance2, config.config, config.configDir, reporter));
|
||||
},
|
||||
teardown: async () => {
|
||||
var _plugin$instance3, _plugin$instance3$tea;
|
||||
await ((_plugin$instance3 = plugin.instance) === null || _plugin$instance3 === void 0 || (_plugin$instance3$tea = _plugin$instance3.teardown) === null || _plugin$instance3$tea === void 0 ? void 0 : _plugin$instance3$tea.call(_plugin$instance3));
|
||||
}
|
||||
}));
|
||||
}
|
||||
function createPluginBeginTask(plugin) {
|
||||
return {
|
||||
title: 'plugin begin',
|
||||
setup: async testRun => {
|
||||
var _plugin$instance4, _plugin$instance4$beg;
|
||||
await ((_plugin$instance4 = plugin.instance) === null || _plugin$instance4 === void 0 || (_plugin$instance4$beg = _plugin$instance4.begin) === null || _plugin$instance4$beg === void 0 ? void 0 : _plugin$instance4$beg.call(_plugin$instance4, testRun.rootSuite));
|
||||
},
|
||||
teardown: async () => {
|
||||
var _plugin$instance5, _plugin$instance5$end;
|
||||
await ((_plugin$instance5 = plugin.instance) === null || _plugin$instance5 === void 0 || (_plugin$instance5$end = _plugin$instance5.end) === null || _plugin$instance5$end === void 0 ? void 0 : _plugin$instance5$end.call(_plugin$instance5));
|
||||
}
|
||||
};
|
||||
}
|
||||
function createGlobalSetupTask(file, config) {
|
||||
let title = 'global setup';
|
||||
if (config.globalSetups.length > 1) title += ` (${file})`;
|
||||
let globalSetupResult;
|
||||
return {
|
||||
title,
|
||||
setup: async ({
|
||||
config
|
||||
}) => {
|
||||
const setupHook = await (0, _loadUtils.loadGlobalHook)(config, file);
|
||||
globalSetupResult = await setupHook(config.config);
|
||||
},
|
||||
teardown: async () => {
|
||||
if (typeof globalSetupResult === 'function') await globalSetupResult();
|
||||
}
|
||||
};
|
||||
}
|
||||
function createGlobalTeardownTask(file, config) {
|
||||
let title = 'global teardown';
|
||||
if (config.globalTeardowns.length > 1) title += ` (${file})`;
|
||||
return {
|
||||
title,
|
||||
teardown: async ({
|
||||
config
|
||||
}) => {
|
||||
const teardownHook = await (0, _loadUtils.loadGlobalHook)(config, file);
|
||||
await teardownHook(config.config);
|
||||
}
|
||||
};
|
||||
}
|
||||
function createRemoveOutputDirsTask() {
|
||||
return {
|
||||
title: 'clear output',
|
||||
setup: async ({
|
||||
config
|
||||
}) => {
|
||||
const outputDirs = new Set();
|
||||
const projects = (0, _projectUtils.filterProjects)(config.projects, config.cliProjectFilter);
|
||||
projects.forEach(p => outputDirs.add(p.project.outputDir));
|
||||
await Promise.all(Array.from(outputDirs).map(outputDir => (0, _utils.removeFolders)([outputDir]).then(async ([error]) => {
|
||||
if (!error) return;
|
||||
if (error.code === 'EBUSY') {
|
||||
// We failed to remove folder, might be due to the whole folder being mounted inside a container:
|
||||
// https://github.com/microsoft/playwright/issues/12106
|
||||
// Do a best-effort to remove all files inside of it instead.
|
||||
const entries = await readDirAsync(outputDir).catch(e => []);
|
||||
await Promise.all(entries.map(entry => (0, _utils.removeFolders)([_path.default.join(outputDir, entry)])));
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
})));
|
||||
}
|
||||
};
|
||||
}
|
||||
function createListFilesTask() {
|
||||
return {
|
||||
title: 'load tests',
|
||||
setup: async (testRun, errors) => {
|
||||
testRun.rootSuite = await (0, _loadUtils.createRootSuite)(testRun, errors, false);
|
||||
testRun.failureTracker.onRootSuite(testRun.rootSuite);
|
||||
await (0, _loadUtils.collectProjectsAndTestFiles)(testRun, false);
|
||||
for (const [project, files] of testRun.projectFiles) {
|
||||
const projectSuite = new _test.Suite(project.project.name, 'project');
|
||||
projectSuite._fullProject = project;
|
||||
testRun.rootSuite._addSuite(projectSuite);
|
||||
const suites = files.map(file => {
|
||||
const title = _path.default.relative(testRun.config.config.rootDir, file);
|
||||
const suite = new _test.Suite(title, 'file');
|
||||
suite.location = {
|
||||
file,
|
||||
line: 0,
|
||||
column: 0
|
||||
};
|
||||
projectSuite._addSuite(suite);
|
||||
return suite;
|
||||
});
|
||||
testRun.projectSuites.set(project, suites);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
function createLoadTask(mode, options) {
|
||||
return {
|
||||
title: 'load tests',
|
||||
setup: async (testRun, errors, softErrors) => {
|
||||
await (0, _loadUtils.collectProjectsAndTestFiles)(testRun, !!options.doNotRunDepsOutsideProjectFilter);
|
||||
await (0, _loadUtils.loadFileSuites)(testRun, mode, options.failOnLoadErrors ? errors : softErrors);
|
||||
if (testRun.config.cliOnlyChanged || options.populateDependencies) {
|
||||
for (const plugin of testRun.config.plugins) {
|
||||
var _plugin$instance6, _plugin$instance6$pop;
|
||||
await ((_plugin$instance6 = plugin.instance) === null || _plugin$instance6 === void 0 || (_plugin$instance6$pop = _plugin$instance6.populateDependencies) === null || _plugin$instance6$pop === void 0 ? void 0 : _plugin$instance6$pop.call(_plugin$instance6));
|
||||
}
|
||||
}
|
||||
let cliOnlyChangedMatcher = undefined;
|
||||
if (testRun.config.cliOnlyChanged) {
|
||||
const changedFiles = await (0, _vcs.detectChangedTestFiles)(testRun.config.cliOnlyChanged, testRun.config.configDir);
|
||||
cliOnlyChangedMatcher = file => changedFiles.has(file);
|
||||
}
|
||||
testRun.rootSuite = await (0, _loadUtils.createRootSuite)(testRun, options.failOnLoadErrors ? errors : softErrors, !!options.filterOnly, cliOnlyChangedMatcher);
|
||||
testRun.failureTracker.onRootSuite(testRun.rootSuite);
|
||||
// Fail when no tests.
|
||||
if (options.failOnLoadErrors && !testRun.rootSuite.allTests().length && !testRun.config.cliPassWithNoTests && !testRun.config.config.shard && !testRun.config.cliOnlyChanged) {
|
||||
if (testRun.config.cliArgs.length) {
|
||||
throw new Error([`No tests found.`, `Make sure that arguments are regular expressions matching test files.`, `You may need to escape symbols like "$" or "*" and quote the arguments.`].join('\n'));
|
||||
}
|
||||
throw new Error(`No tests found`);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
function createApplyRebaselinesTask() {
|
||||
return {
|
||||
title: 'apply rebaselines',
|
||||
teardown: async ({
|
||||
config,
|
||||
reporter
|
||||
}) => {
|
||||
await (0, _rebase.applySuggestedRebaselines)(config, reporter);
|
||||
}
|
||||
};
|
||||
}
|
||||
function createPhasesTask() {
|
||||
return {
|
||||
title: 'create phases',
|
||||
setup: async testRun => {
|
||||
let maxConcurrentTestGroups = 0;
|
||||
const processed = new Set();
|
||||
const projectToSuite = new Map(testRun.rootSuite.suites.map(suite => [suite._fullProject, suite]));
|
||||
const allProjects = [...projectToSuite.keys()];
|
||||
const teardownToSetups = (0, _projectUtils.buildTeardownToSetupsMap)(allProjects);
|
||||
const teardownToSetupsDependents = new Map();
|
||||
for (const [teardown, setups] of teardownToSetups) {
|
||||
const closure = (0, _projectUtils.buildDependentProjects)(setups, allProjects);
|
||||
closure.delete(teardown);
|
||||
teardownToSetupsDependents.set(teardown, [...closure]);
|
||||
}
|
||||
for (let i = 0; i < projectToSuite.size; i++) {
|
||||
// Find all projects that have all their dependencies processed by previous phases.
|
||||
const phaseProjects = [];
|
||||
for (const project of projectToSuite.keys()) {
|
||||
if (processed.has(project)) continue;
|
||||
const projectsThatShouldFinishFirst = [...project.deps, ...(teardownToSetupsDependents.get(project) || [])];
|
||||
if (projectsThatShouldFinishFirst.find(p => !processed.has(p))) continue;
|
||||
phaseProjects.push(project);
|
||||
}
|
||||
|
||||
// Create a new phase.
|
||||
for (const project of phaseProjects) processed.add(project);
|
||||
if (phaseProjects.length) {
|
||||
let testGroupsInPhase = 0;
|
||||
const phase = {
|
||||
dispatcher: new _dispatcher.Dispatcher(testRun.config, testRun.reporter, testRun.failureTracker),
|
||||
projects: []
|
||||
};
|
||||
testRun.phases.push(phase);
|
||||
for (const project of phaseProjects) {
|
||||
const projectSuite = projectToSuite.get(project);
|
||||
const testGroups = (0, _testGroups.createTestGroups)(projectSuite, testRun.config.config.workers);
|
||||
phase.projects.push({
|
||||
project,
|
||||
projectSuite,
|
||||
testGroups
|
||||
});
|
||||
testGroupsInPhase += testGroups.length;
|
||||
}
|
||||
(0, _utilsBundle.debug)('pw:test:task')(`created phase #${testRun.phases.length} with ${phase.projects.map(p => p.project.project.name).sort()} projects, ${testGroupsInPhase} testGroups`);
|
||||
maxConcurrentTestGroups = Math.max(maxConcurrentTestGroups, testGroupsInPhase);
|
||||
}
|
||||
}
|
||||
testRun.config.config.metadata.actualWorkers = Math.min(testRun.config.config.workers, maxConcurrentTestGroups);
|
||||
}
|
||||
};
|
||||
}
|
||||
function createRunTestsTask() {
|
||||
return {
|
||||
title: 'test suite',
|
||||
setup: async ({
|
||||
phases,
|
||||
failureTracker
|
||||
}) => {
|
||||
const successfulProjects = new Set();
|
||||
const extraEnvByProjectId = new Map();
|
||||
const teardownToSetups = (0, _projectUtils.buildTeardownToSetupsMap)(phases.map(phase => phase.projects.map(p => p.project)).flat());
|
||||
for (const {
|
||||
dispatcher,
|
||||
projects
|
||||
} of phases) {
|
||||
// Each phase contains dispatcher and a set of test groups.
|
||||
// We don't want to run the test groups belonging to the projects
|
||||
// that depend on the projects that failed previously.
|
||||
const phaseTestGroups = [];
|
||||
for (const {
|
||||
project,
|
||||
testGroups
|
||||
} of projects) {
|
||||
// Inherit extra environment variables from dependencies.
|
||||
let extraEnv = {};
|
||||
for (const dep of project.deps) extraEnv = {
|
||||
...extraEnv,
|
||||
...extraEnvByProjectId.get(dep.id)
|
||||
};
|
||||
for (const setup of teardownToSetups.get(project) || []) extraEnv = {
|
||||
...extraEnv,
|
||||
...extraEnvByProjectId.get(setup.id)
|
||||
};
|
||||
extraEnvByProjectId.set(project.id, extraEnv);
|
||||
const hasFailedDeps = project.deps.some(p => !successfulProjects.has(p));
|
||||
if (!hasFailedDeps) phaseTestGroups.push(...testGroups);
|
||||
}
|
||||
if (phaseTestGroups.length) {
|
||||
await dispatcher.run(phaseTestGroups, extraEnvByProjectId);
|
||||
await dispatcher.stop();
|
||||
for (const [projectId, envProduced] of dispatcher.producedEnvByProjectId()) {
|
||||
const extraEnv = extraEnvByProjectId.get(projectId) || {};
|
||||
extraEnvByProjectId.set(projectId, {
|
||||
...extraEnv,
|
||||
...envProduced
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// If the worker broke, fail everything, we have no way of knowing which
|
||||
// projects failed.
|
||||
if (!failureTracker.hasWorkerErrors()) {
|
||||
for (const {
|
||||
project,
|
||||
projectSuite
|
||||
} of projects) {
|
||||
const hasFailedDeps = project.deps.some(p => !successfulProjects.has(p));
|
||||
if (!hasFailedDeps && !projectSuite.allTests().some(test => !test.ok())) successfulProjects.add(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
teardown: async ({
|
||||
phases
|
||||
}) => {
|
||||
for (const {
|
||||
dispatcher
|
||||
} of phases.reverse()) await dispatcher.stop();
|
||||
}
|
||||
};
|
||||
}
|
||||
function createStartDevServerTask() {
|
||||
return {
|
||||
title: 'start dev server',
|
||||
setup: async ({
|
||||
config
|
||||
}, errors, softErrors) => {
|
||||
if (config.plugins.some(plugin => !!plugin.devServerCleanup)) {
|
||||
errors.push({
|
||||
message: `DevServer is already running`
|
||||
});
|
||||
return;
|
||||
}
|
||||
for (const plugin of config.plugins) {
|
||||
var _plugin$instance7, _plugin$instance7$sta;
|
||||
plugin.devServerCleanup = await ((_plugin$instance7 = plugin.instance) === null || _plugin$instance7 === void 0 || (_plugin$instance7$sta = _plugin$instance7.startDevServer) === null || _plugin$instance7$sta === void 0 ? void 0 : _plugin$instance7$sta.call(_plugin$instance7));
|
||||
}
|
||||
if (!config.plugins.some(plugin => !!plugin.devServerCleanup)) errors.push({
|
||||
message: `DevServer is not available in the package you are using. Did you mean to use component testing?`
|
||||
});
|
||||
},
|
||||
teardown: async ({
|
||||
config
|
||||
}) => {
|
||||
for (const plugin of config.plugins) {
|
||||
var _plugin$devServerClea;
|
||||
await ((_plugin$devServerClea = plugin.devServerCleanup) === null || _plugin$devServerClea === void 0 ? void 0 : _plugin$devServerClea.call(plugin));
|
||||
plugin.devServerCleanup = undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
136
node_modules/playwright/lib/runner/testGroups.js
generated
vendored
Normal file
136
node_modules/playwright/lib/runner/testGroups.js
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.createTestGroups = createTestGroups;
|
||||
exports.filterForShard = filterForShard;
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function createTestGroups(projectSuite, expectedParallelism) {
|
||||
// This function groups tests that can be run together.
|
||||
// Tests cannot be run together when:
|
||||
// - They belong to different projects - requires different workers.
|
||||
// - They have a different repeatEachIndex - requires different workers.
|
||||
// - They have a different set of worker fixtures in the pool - requires different workers.
|
||||
// - They have a different requireFile - reuses the worker, but runs each requireFile separately.
|
||||
// - They belong to a parallel suite.
|
||||
|
||||
// Using the map "workerHash -> requireFile -> group" makes us preserve the natural order
|
||||
// of worker hashes and require files for the simple cases.
|
||||
const groups = new Map();
|
||||
const createGroup = test => {
|
||||
return {
|
||||
workerHash: test._workerHash,
|
||||
requireFile: test._requireFile,
|
||||
repeatEachIndex: test.repeatEachIndex,
|
||||
projectId: test._projectId,
|
||||
tests: []
|
||||
};
|
||||
};
|
||||
for (const test of projectSuite.allTests()) {
|
||||
let withWorkerHash = groups.get(test._workerHash);
|
||||
if (!withWorkerHash) {
|
||||
withWorkerHash = new Map();
|
||||
groups.set(test._workerHash, withWorkerHash);
|
||||
}
|
||||
let withRequireFile = withWorkerHash.get(test._requireFile);
|
||||
if (!withRequireFile) {
|
||||
withRequireFile = {
|
||||
general: createGroup(test),
|
||||
parallel: new Map(),
|
||||
parallelWithHooks: createGroup(test)
|
||||
};
|
||||
withWorkerHash.set(test._requireFile, withRequireFile);
|
||||
}
|
||||
|
||||
// Note that a parallel suite cannot be inside a serial suite. This is enforced in TestType.
|
||||
let insideParallel = false;
|
||||
let outerMostSequentialSuite;
|
||||
let hasAllHooks = false;
|
||||
for (let parent = test.parent; parent; parent = parent.parent) {
|
||||
if (parent._parallelMode === 'serial' || parent._parallelMode === 'default') outerMostSequentialSuite = parent;
|
||||
insideParallel = insideParallel || parent._parallelMode === 'parallel';
|
||||
hasAllHooks = hasAllHooks || parent._hooks.some(hook => hook.type === 'beforeAll' || hook.type === 'afterAll');
|
||||
}
|
||||
if (insideParallel) {
|
||||
if (hasAllHooks && !outerMostSequentialSuite) {
|
||||
withRequireFile.parallelWithHooks.tests.push(test);
|
||||
} else {
|
||||
const key = outerMostSequentialSuite || test;
|
||||
let group = withRequireFile.parallel.get(key);
|
||||
if (!group) {
|
||||
group = createGroup(test);
|
||||
withRequireFile.parallel.set(key, group);
|
||||
}
|
||||
group.tests.push(test);
|
||||
}
|
||||
} else {
|
||||
withRequireFile.general.tests.push(test);
|
||||
}
|
||||
}
|
||||
const result = [];
|
||||
for (const withWorkerHash of groups.values()) {
|
||||
for (const withRequireFile of withWorkerHash.values()) {
|
||||
// Tests without parallel mode should run serially as a single group.
|
||||
if (withRequireFile.general.tests.length) result.push(withRequireFile.general);
|
||||
|
||||
// Parallel test groups without beforeAll/afterAll can be run independently.
|
||||
result.push(...withRequireFile.parallel.values());
|
||||
|
||||
// Tests with beforeAll/afterAll should try to share workers as much as possible.
|
||||
const parallelWithHooksGroupSize = Math.ceil(withRequireFile.parallelWithHooks.tests.length / expectedParallelism);
|
||||
let lastGroup;
|
||||
for (const test of withRequireFile.parallelWithHooks.tests) {
|
||||
if (!lastGroup || lastGroup.tests.length >= parallelWithHooksGroupSize) {
|
||||
lastGroup = createGroup(test);
|
||||
result.push(lastGroup);
|
||||
}
|
||||
lastGroup.tests.push(test);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function filterForShard(shard, testGroups) {
|
||||
// Note that sharding works based on test groups.
|
||||
// This means parallel files will be sharded by single tests,
|
||||
// while non-parallel files will be sharded by the whole file.
|
||||
//
|
||||
// Shards are still balanced by the number of tests, not files,
|
||||
// even in the case of non-paralleled files.
|
||||
|
||||
let shardableTotal = 0;
|
||||
for (const group of testGroups) shardableTotal += group.tests.length;
|
||||
|
||||
// Each shard gets some tests.
|
||||
const shardSize = Math.floor(shardableTotal / shard.total);
|
||||
// First few shards get one more test each.
|
||||
const extraOne = shardableTotal - shardSize * shard.total;
|
||||
const currentShard = shard.current - 1; // Make it zero-based for calculations.
|
||||
const from = shardSize * currentShard + Math.min(extraOne, currentShard);
|
||||
const to = from + shardSize + (currentShard < extraOne ? 1 : 0);
|
||||
let current = 0;
|
||||
const result = new Set();
|
||||
for (const group of testGroups) {
|
||||
// Any test group goes to the shard that contains the first test of this group.
|
||||
// So, this shard gets any group that starts at [from; to)
|
||||
if (current >= from && current < to) result.add(group);
|
||||
current += group.tests.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
569
node_modules/playwright/lib/runner/testServer.js
generated
vendored
Normal file
569
node_modules/playwright/lib/runner/testServer.js
generated
vendored
Normal file
@@ -0,0 +1,569 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.TestServerDispatcher = void 0;
|
||||
exports.resolveCtDirs = resolveCtDirs;
|
||||
exports.runTestServer = runTestServer;
|
||||
exports.runUIMode = runUIMode;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _server = require("playwright-core/lib/server");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _compilationCache = require("../transform/compilationCache");
|
||||
var _reporters = require("./reporters");
|
||||
var _tasks = require("./tasks");
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _list = _interopRequireDefault(require("../reporters/list"));
|
||||
var _sigIntWatcher = require("./sigIntWatcher");
|
||||
var _fsWatcher = require("../fsWatcher");
|
||||
var _configLoader = require("../common/configLoader");
|
||||
var _webServerPlugin = require("../plugins/webServerPlugin");
|
||||
var _util = require("../util");
|
||||
var _teleReceiver = require("../isomorphic/teleReceiver");
|
||||
var _internalReporter = require("../reporters/internalReporter");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const originalStdoutWrite = process.stdout.write;
|
||||
const originalStderrWrite = process.stderr.write;
|
||||
class TestServer {
|
||||
constructor(configLocation, configCLIOverrides) {
|
||||
this._configLocation = void 0;
|
||||
this._configCLIOverrides = void 0;
|
||||
this._dispatcher = void 0;
|
||||
this._configLocation = configLocation;
|
||||
this._configCLIOverrides = configCLIOverrides;
|
||||
}
|
||||
async start(options) {
|
||||
this._dispatcher = new TestServerDispatcher(this._configLocation, this._configCLIOverrides);
|
||||
return await (0, _server.startTraceViewerServer)({
|
||||
...options,
|
||||
transport: this._dispatcher.transport
|
||||
});
|
||||
}
|
||||
async stop() {
|
||||
var _this$_dispatcher, _this$_dispatcher2;
|
||||
await ((_this$_dispatcher = this._dispatcher) === null || _this$_dispatcher === void 0 ? void 0 : _this$_dispatcher._setInterceptStdio(false));
|
||||
await ((_this$_dispatcher2 = this._dispatcher) === null || _this$_dispatcher2 === void 0 ? void 0 : _this$_dispatcher2.runGlobalTeardown());
|
||||
}
|
||||
}
|
||||
class TestServerDispatcher {
|
||||
constructor(configLocation, configCLIOverrides) {
|
||||
this._configLocation = void 0;
|
||||
this._configCLIOverrides = void 0;
|
||||
this._watcher = void 0;
|
||||
this._watchedProjectDirs = new Set();
|
||||
this._ignoredProjectOutputs = new Set();
|
||||
this._watchedTestDependencies = new Set();
|
||||
this._testRun = void 0;
|
||||
this.transport = void 0;
|
||||
this._queue = Promise.resolve();
|
||||
this._globalSetup = void 0;
|
||||
this._devServer = void 0;
|
||||
this._dispatchEvent = void 0;
|
||||
this._plugins = void 0;
|
||||
this._serializer = require.resolve('./uiModeReporter');
|
||||
this._watchTestDirs = false;
|
||||
this._closeOnDisconnect = false;
|
||||
this._populateDependenciesOnList = false;
|
||||
this._configLocation = configLocation;
|
||||
this._configCLIOverrides = configCLIOverrides;
|
||||
this.transport = {
|
||||
onconnect: () => {},
|
||||
dispatch: (method, params) => this[method](params),
|
||||
onclose: () => {
|
||||
if (this._closeOnDisconnect) (0, _utils.gracefullyProcessExitDoNotHang)(0);
|
||||
}
|
||||
};
|
||||
this._watcher = new _fsWatcher.Watcher(events => {
|
||||
const collector = new Set();
|
||||
events.forEach(f => (0, _compilationCache.collectAffectedTestFiles)(f.file, collector));
|
||||
this._dispatchEvent('testFilesChanged', {
|
||||
testFiles: [...collector]
|
||||
});
|
||||
});
|
||||
this._dispatchEvent = (method, params) => {
|
||||
var _this$transport$sendE, _this$transport;
|
||||
return (_this$transport$sendE = (_this$transport = this.transport).sendEvent) === null || _this$transport$sendE === void 0 ? void 0 : _this$transport$sendE.call(_this$transport, method, params);
|
||||
};
|
||||
}
|
||||
async _wireReporter(messageSink) {
|
||||
return await (0, _reporters.createReporterForTestServer)(this._serializer, messageSink);
|
||||
}
|
||||
async _collectingInternalReporter(...extraReporters) {
|
||||
const report = [];
|
||||
const collectingReporter = await (0, _reporters.createReporterForTestServer)(this._serializer, e => report.push(e));
|
||||
return {
|
||||
reporter: new _internalReporter.InternalReporter([collectingReporter, ...extraReporters]),
|
||||
report
|
||||
};
|
||||
}
|
||||
async initialize(params) {
|
||||
// Note: this method can be called multiple times, for example from a new connection after UI mode reload.
|
||||
this._serializer = params.serializer || require.resolve('./uiModeReporter');
|
||||
this._closeOnDisconnect = !!params.closeOnDisconnect;
|
||||
await this._setInterceptStdio(!!params.interceptStdio);
|
||||
this._watchTestDirs = !!params.watchTestDirs;
|
||||
this._populateDependenciesOnList = !!params.populateDependenciesOnList;
|
||||
}
|
||||
async ping() {}
|
||||
async open(params) {
|
||||
if ((0, _utils.isUnderTest)()) return;
|
||||
// eslint-disable-next-line no-console
|
||||
(0, _utilsBundle.open)('vscode://file/' + params.location.file + ':' + params.location.line).catch(e => console.error(e));
|
||||
}
|
||||
async resizeTerminal(params) {
|
||||
process.stdout.columns = params.cols;
|
||||
process.stdout.rows = params.rows;
|
||||
process.stderr.columns = params.cols;
|
||||
process.stderr.columns = params.rows;
|
||||
}
|
||||
async checkBrowsers() {
|
||||
return {
|
||||
hasBrowsers: hasSomeBrowsers()
|
||||
};
|
||||
}
|
||||
async installBrowsers() {
|
||||
await installBrowsers();
|
||||
}
|
||||
async runGlobalSetup(params) {
|
||||
await this.runGlobalTeardown();
|
||||
const {
|
||||
reporter,
|
||||
report
|
||||
} = await this._collectingInternalReporter(new _list.default());
|
||||
const config = await this._loadConfigOrReportError(reporter, this._configCLIOverrides);
|
||||
if (!config) return {
|
||||
status: 'failed',
|
||||
report
|
||||
};
|
||||
const {
|
||||
status,
|
||||
cleanup
|
||||
} = await (0, _tasks.runTasksDeferCleanup)(new _tasks.TestRun(config, reporter), [...(0, _tasks.createGlobalSetupTasks)(config)]);
|
||||
if (status !== 'passed') await cleanup();else this._globalSetup = {
|
||||
cleanup,
|
||||
report
|
||||
};
|
||||
return {
|
||||
report,
|
||||
status
|
||||
};
|
||||
}
|
||||
async runGlobalTeardown() {
|
||||
const globalSetup = this._globalSetup;
|
||||
const status = await (globalSetup === null || globalSetup === void 0 ? void 0 : globalSetup.cleanup());
|
||||
this._globalSetup = undefined;
|
||||
return {
|
||||
status,
|
||||
report: (globalSetup === null || globalSetup === void 0 ? void 0 : globalSetup.report) || []
|
||||
};
|
||||
}
|
||||
async startDevServer(params) {
|
||||
await this.stopDevServer({});
|
||||
const {
|
||||
reporter,
|
||||
report
|
||||
} = await this._collectingInternalReporter();
|
||||
const config = await this._loadConfigOrReportError(reporter);
|
||||
if (!config) return {
|
||||
report,
|
||||
status: 'failed'
|
||||
};
|
||||
const {
|
||||
status,
|
||||
cleanup
|
||||
} = await (0, _tasks.runTasksDeferCleanup)(new _tasks.TestRun(config, reporter), [(0, _tasks.createLoadTask)('out-of-process', {
|
||||
failOnLoadErrors: true,
|
||||
filterOnly: false
|
||||
}), (0, _tasks.createStartDevServerTask)()]);
|
||||
if (status !== 'passed') await cleanup();else this._devServer = {
|
||||
cleanup,
|
||||
report
|
||||
};
|
||||
return {
|
||||
report,
|
||||
status
|
||||
};
|
||||
}
|
||||
async stopDevServer(params) {
|
||||
const devServer = this._devServer;
|
||||
const status = await (devServer === null || devServer === void 0 ? void 0 : devServer.cleanup());
|
||||
this._devServer = undefined;
|
||||
return {
|
||||
status,
|
||||
report: (devServer === null || devServer === void 0 ? void 0 : devServer.report) || []
|
||||
};
|
||||
}
|
||||
async clearCache(params) {
|
||||
const reporter = new _internalReporter.InternalReporter([]);
|
||||
const config = await this._loadConfigOrReportError(reporter);
|
||||
if (!config) return;
|
||||
await (0, _tasks.runTasks)(new _tasks.TestRun(config, reporter), [(0, _tasks.createClearCacheTask)(config)]);
|
||||
}
|
||||
async listFiles(params) {
|
||||
var _params$projects;
|
||||
const {
|
||||
reporter,
|
||||
report
|
||||
} = await this._collectingInternalReporter();
|
||||
const config = await this._loadConfigOrReportError(reporter);
|
||||
if (!config) return {
|
||||
status: 'failed',
|
||||
report
|
||||
};
|
||||
config.cliProjectFilter = (_params$projects = params.projects) !== null && _params$projects !== void 0 && _params$projects.length ? params.projects : undefined;
|
||||
const status = await (0, _tasks.runTasks)(new _tasks.TestRun(config, reporter), [(0, _tasks.createListFilesTask)(), (0, _tasks.createReportBeginTask)()]);
|
||||
return {
|
||||
report,
|
||||
status
|
||||
};
|
||||
}
|
||||
async listTests(params) {
|
||||
let result;
|
||||
this._queue = this._queue.then(async () => {
|
||||
const {
|
||||
config,
|
||||
report,
|
||||
status
|
||||
} = await this._innerListTests(params);
|
||||
if (config) await this._updateWatchedDirs(config);
|
||||
result = {
|
||||
report,
|
||||
status
|
||||
};
|
||||
}).catch(printInternalError);
|
||||
await this._queue;
|
||||
return result;
|
||||
}
|
||||
async _innerListTests(params) {
|
||||
var _params$projects2;
|
||||
const overrides = {
|
||||
...this._configCLIOverrides,
|
||||
repeatEach: 1,
|
||||
retries: 0
|
||||
};
|
||||
const {
|
||||
reporter,
|
||||
report
|
||||
} = await this._collectingInternalReporter();
|
||||
const config = await this._loadConfigOrReportError(reporter, overrides);
|
||||
if (!config) return {
|
||||
report,
|
||||
reporter,
|
||||
status: 'failed'
|
||||
};
|
||||
config.cliArgs = params.locations || [];
|
||||
config.cliGrep = params.grep;
|
||||
config.cliGrepInvert = params.grepInvert;
|
||||
config.cliProjectFilter = (_params$projects2 = params.projects) !== null && _params$projects2 !== void 0 && _params$projects2.length ? params.projects : undefined;
|
||||
config.cliListOnly = true;
|
||||
const status = await (0, _tasks.runTasks)(new _tasks.TestRun(config, reporter), [(0, _tasks.createLoadTask)('out-of-process', {
|
||||
failOnLoadErrors: false,
|
||||
filterOnly: false,
|
||||
populateDependencies: this._populateDependenciesOnList
|
||||
}), (0, _tasks.createReportBeginTask)()]);
|
||||
return {
|
||||
config,
|
||||
report,
|
||||
reporter,
|
||||
status
|
||||
};
|
||||
}
|
||||
async _updateWatchedDirs(config) {
|
||||
this._watchedProjectDirs = new Set();
|
||||
this._ignoredProjectOutputs = new Set();
|
||||
for (const p of config.projects) {
|
||||
this._watchedProjectDirs.add(p.project.testDir);
|
||||
this._ignoredProjectOutputs.add(p.project.outputDir);
|
||||
}
|
||||
const result = await resolveCtDirs(config);
|
||||
if (result) {
|
||||
this._watchedProjectDirs.add(result.templateDir);
|
||||
this._ignoredProjectOutputs.add(result.outDir);
|
||||
}
|
||||
if (this._watchTestDirs) await this._updateWatcher(false);
|
||||
}
|
||||
async _updateWatcher(reportPending) {
|
||||
await this._watcher.update([...this._watchedProjectDirs, ...this._watchedTestDependencies], [...this._ignoredProjectOutputs], reportPending);
|
||||
}
|
||||
async runTests(params) {
|
||||
let result = {
|
||||
status: 'passed'
|
||||
};
|
||||
this._queue = this._queue.then(async () => {
|
||||
result = await this._innerRunTests(params).catch(e => {
|
||||
printInternalError(e);
|
||||
return {
|
||||
status: 'failed'
|
||||
};
|
||||
});
|
||||
});
|
||||
await this._queue;
|
||||
return result;
|
||||
}
|
||||
async _innerRunTests(params) {
|
||||
var _params$projects3;
|
||||
await this.stopTests();
|
||||
const overrides = {
|
||||
...this._configCLIOverrides,
|
||||
repeatEach: 1,
|
||||
retries: 0,
|
||||
preserveOutputDir: true,
|
||||
reporter: params.reporters ? params.reporters.map(r => [r]) : undefined,
|
||||
use: {
|
||||
...this._configCLIOverrides.use,
|
||||
...(params.trace === 'on' ? {
|
||||
trace: {
|
||||
mode: 'on',
|
||||
sources: false,
|
||||
_live: true
|
||||
}
|
||||
} : {}),
|
||||
...(params.trace === 'off' ? {
|
||||
trace: 'off'
|
||||
} : {}),
|
||||
...(params.video === 'on' || params.video === 'off' ? {
|
||||
video: params.video
|
||||
} : {}),
|
||||
...(params.headed !== undefined ? {
|
||||
headless: !params.headed
|
||||
} : {}),
|
||||
_optionContextReuseMode: params.reuseContext ? 'when-possible' : undefined,
|
||||
_optionConnectOptions: params.connectWsEndpoint ? {
|
||||
wsEndpoint: params.connectWsEndpoint
|
||||
} : undefined
|
||||
},
|
||||
...(params.updateSnapshots ? {
|
||||
updateSnapshots: params.updateSnapshots
|
||||
} : {}),
|
||||
...(params.workers ? {
|
||||
workers: params.workers
|
||||
} : {})
|
||||
};
|
||||
if (params.trace === 'on') process.env.PW_LIVE_TRACE_STACKS = '1';else process.env.PW_LIVE_TRACE_STACKS = undefined;
|
||||
const wireReporter = await this._wireReporter(e => this._dispatchEvent('report', e));
|
||||
const config = await this._loadConfigOrReportError(new _internalReporter.InternalReporter([wireReporter]), overrides);
|
||||
if (!config) return {
|
||||
status: 'failed'
|
||||
};
|
||||
const testIdSet = params.testIds ? new Set(params.testIds) : null;
|
||||
config.cliListOnly = false;
|
||||
config.cliPassWithNoTests = true;
|
||||
config.cliArgs = params.locations || [];
|
||||
config.cliGrep = params.grep;
|
||||
config.cliGrepInvert = params.grepInvert;
|
||||
config.cliProjectFilter = (_params$projects3 = params.projects) !== null && _params$projects3 !== void 0 && _params$projects3.length ? params.projects : undefined;
|
||||
config.testIdMatcher = testIdSet ? id => testIdSet.has(id) : undefined;
|
||||
const configReporters = await (0, _reporters.createReporters)(config, 'test', true);
|
||||
const reporter = new _internalReporter.InternalReporter([...configReporters, wireReporter]);
|
||||
const stop = new _utils.ManualPromise();
|
||||
const tasks = [(0, _tasks.createApplyRebaselinesTask)(), (0, _tasks.createLoadTask)('out-of-process', {
|
||||
filterOnly: true,
|
||||
failOnLoadErrors: false,
|
||||
doNotRunDepsOutsideProjectFilter: true
|
||||
}), ...(0, _tasks.createRunTestsTasks)(config)];
|
||||
const run = (0, _tasks.runTasks)(new _tasks.TestRun(config, reporter), tasks, 0, stop).then(async status => {
|
||||
this._testRun = undefined;
|
||||
return status;
|
||||
});
|
||||
this._testRun = {
|
||||
run,
|
||||
stop
|
||||
};
|
||||
return {
|
||||
status: await run
|
||||
};
|
||||
}
|
||||
async watch(params) {
|
||||
this._watchedTestDependencies = new Set();
|
||||
for (const fileName of params.fileNames) {
|
||||
this._watchedTestDependencies.add(fileName);
|
||||
(0, _compilationCache.dependenciesForTestFile)(fileName).forEach(file => this._watchedTestDependencies.add(file));
|
||||
}
|
||||
await this._updateWatcher(true);
|
||||
}
|
||||
async findRelatedTestFiles(params) {
|
||||
const errorReporter = (0, _reporters.createErrorCollectingReporter)();
|
||||
const reporter = new _internalReporter.InternalReporter([errorReporter]);
|
||||
const config = await this._loadConfigOrReportError(reporter);
|
||||
if (!config) return {
|
||||
errors: errorReporter.errors(),
|
||||
testFiles: []
|
||||
};
|
||||
const status = await (0, _tasks.runTasks)(new _tasks.TestRun(config, reporter), [(0, _tasks.createLoadTask)('out-of-process', {
|
||||
failOnLoadErrors: true,
|
||||
filterOnly: false,
|
||||
populateDependencies: true
|
||||
})]);
|
||||
if (status !== 'passed') return {
|
||||
errors: errorReporter.errors(),
|
||||
testFiles: []
|
||||
};
|
||||
return {
|
||||
testFiles: (0, _compilationCache.affectedTestFiles)(params.files)
|
||||
};
|
||||
}
|
||||
async stopTests() {
|
||||
var _this$_testRun, _this$_testRun2;
|
||||
(_this$_testRun = this._testRun) === null || _this$_testRun === void 0 || (_this$_testRun = _this$_testRun.stop) === null || _this$_testRun === void 0 || _this$_testRun.resolve();
|
||||
await ((_this$_testRun2 = this._testRun) === null || _this$_testRun2 === void 0 ? void 0 : _this$_testRun2.run);
|
||||
}
|
||||
async _setInterceptStdio(intercept) {
|
||||
if (process.env.PWTEST_DEBUG) return;
|
||||
if (intercept) {
|
||||
process.stdout.write = chunk => {
|
||||
this._dispatchEvent('stdio', chunkToPayload('stdout', chunk));
|
||||
return true;
|
||||
};
|
||||
process.stderr.write = chunk => {
|
||||
this._dispatchEvent('stdio', chunkToPayload('stderr', chunk));
|
||||
return true;
|
||||
};
|
||||
} else {
|
||||
process.stdout.write = originalStdoutWrite;
|
||||
process.stderr.write = originalStderrWrite;
|
||||
}
|
||||
}
|
||||
async closeGracefully() {
|
||||
(0, _utils.gracefullyProcessExitDoNotHang)(0);
|
||||
}
|
||||
async _loadConfig(overrides) {
|
||||
try {
|
||||
const config = await (0, _configLoader.loadConfig)(this._configLocation, overrides);
|
||||
// Preserve plugin instances between setup and build.
|
||||
if (!this._plugins) {
|
||||
(0, _webServerPlugin.webServerPluginsForConfig)(config).forEach(p => config.plugins.push({
|
||||
factory: p
|
||||
}));
|
||||
this._plugins = config.plugins || [];
|
||||
} else {
|
||||
config.plugins.splice(0, config.plugins.length, ...this._plugins);
|
||||
}
|
||||
return {
|
||||
config
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
config: null,
|
||||
error: (0, _util.serializeError)(e)
|
||||
};
|
||||
}
|
||||
}
|
||||
async _loadConfigOrReportError(reporter, overrides) {
|
||||
const {
|
||||
config,
|
||||
error
|
||||
} = await this._loadConfig(overrides);
|
||||
if (config) return config;
|
||||
// Produce dummy config when it has an error.
|
||||
reporter.onConfigure(_teleReceiver.baseFullConfig);
|
||||
reporter.onError(error);
|
||||
await reporter.onEnd({
|
||||
status: 'failed'
|
||||
});
|
||||
await reporter.onExit();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
exports.TestServerDispatcher = TestServerDispatcher;
|
||||
async function runUIMode(configFile, configCLIOverrides, options) {
|
||||
const configLocation = (0, _configLoader.resolveConfigLocation)(configFile);
|
||||
return await innerRunTestServer(configLocation, configCLIOverrides, options, async (server, cancelPromise) => {
|
||||
await (0, _server.installRootRedirect)(server, [], {
|
||||
...options,
|
||||
webApp: 'uiMode.html'
|
||||
});
|
||||
if (options.host !== undefined || options.port !== undefined) {
|
||||
await (0, _server.openTraceInBrowser)(server.urlPrefix('human-readable'));
|
||||
} else {
|
||||
const page = await (0, _server.openTraceViewerApp)(server.urlPrefix('precise'), 'chromium', {
|
||||
headless: (0, _utils.isUnderTest)() && process.env.PWTEST_HEADED_FOR_TEST !== '1',
|
||||
persistentContextOptions: {
|
||||
handleSIGINT: false
|
||||
}
|
||||
});
|
||||
page.on('close', () => cancelPromise.resolve());
|
||||
}
|
||||
});
|
||||
}
|
||||
async function runTestServer(configFile, configCLIOverrides, options) {
|
||||
const configLocation = (0, _configLoader.resolveConfigLocation)(configFile);
|
||||
return await innerRunTestServer(configLocation, configCLIOverrides, options, async server => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Listening on ' + server.urlPrefix('precise').replace('http:', 'ws:') + '/' + server.wsGuid());
|
||||
});
|
||||
}
|
||||
async function innerRunTestServer(configLocation, configCLIOverrides, options, openUI) {
|
||||
if ((0, _configLoader.restartWithExperimentalTsEsm)(undefined, true)) return 'restarted';
|
||||
const testServer = new TestServer(configLocation, configCLIOverrides);
|
||||
const cancelPromise = new _utils.ManualPromise();
|
||||
const sigintWatcher = new _sigIntWatcher.SigIntWatcher();
|
||||
process.stdin.on('close', () => (0, _utils.gracefullyProcessExitDoNotHang)(0));
|
||||
void sigintWatcher.promise().then(() => cancelPromise.resolve());
|
||||
try {
|
||||
const server = await testServer.start(options);
|
||||
await openUI(server, cancelPromise, configLocation);
|
||||
await cancelPromise;
|
||||
} finally {
|
||||
await testServer.stop();
|
||||
sigintWatcher.disarm();
|
||||
}
|
||||
return sigintWatcher.hadSignal() ? 'interrupted' : 'passed';
|
||||
}
|
||||
function chunkToPayload(type, chunk) {
|
||||
if (chunk instanceof Buffer) return {
|
||||
type,
|
||||
buffer: chunk.toString('base64')
|
||||
};
|
||||
return {
|
||||
type,
|
||||
text: chunk
|
||||
};
|
||||
}
|
||||
function hasSomeBrowsers() {
|
||||
for (const browserName of ['chromium', 'webkit', 'firefox']) {
|
||||
try {
|
||||
_server.registry.findExecutable(browserName).executablePathOrDie('javascript');
|
||||
return true;
|
||||
} catch {}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
async function installBrowsers() {
|
||||
const executables = _server.registry.defaultExecutables();
|
||||
await _server.registry.install(executables, false);
|
||||
}
|
||||
function printInternalError(e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Internal error:', e);
|
||||
}
|
||||
|
||||
// TODO: remove CT dependency.
|
||||
async function resolveCtDirs(config) {
|
||||
const use = config.config.projects[0].use;
|
||||
const relativeTemplateDir = use.ctTemplateDir || 'playwright';
|
||||
const templateDir = await _fs.default.promises.realpath(_path.default.normalize(_path.default.join(config.configDir, relativeTemplateDir))).catch(() => undefined);
|
||||
if (!templateDir) return null;
|
||||
const outDir = use.ctCacheDir ? _path.default.resolve(config.configDir, use.ctCacheDir) : _path.default.resolve(templateDir, '.cache');
|
||||
return {
|
||||
outDir,
|
||||
templateDir
|
||||
};
|
||||
}
|
||||
31
node_modules/playwright/lib/runner/uiModeReporter.js
generated
vendored
Normal file
31
node_modules/playwright/lib/runner/uiModeReporter.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _teleEmitter = require("../reporters/teleEmitter");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class UIModeReporter extends _teleEmitter.TeleReporterEmitter {
|
||||
constructor(options) {
|
||||
super(options._send, {
|
||||
omitBuffers: true
|
||||
});
|
||||
}
|
||||
}
|
||||
var _default = exports.default = UIModeReporter;
|
||||
55
node_modules/playwright/lib/runner/vcs.js
generated
vendored
Normal file
55
node_modules/playwright/lib/runner/vcs.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.detectChangedTestFiles = detectChangedTestFiles;
|
||||
var _child_process = _interopRequireDefault(require("child_process"));
|
||||
var _compilationCache = require("../transform/compilationCache");
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
async function detectChangedTestFiles(baseCommit, configDir) {
|
||||
function gitFileList(command) {
|
||||
try {
|
||||
return _child_process.default.execSync(`git ${command}`, {
|
||||
encoding: 'utf-8',
|
||||
stdio: 'pipe',
|
||||
cwd: configDir
|
||||
}).split('\n').filter(Boolean);
|
||||
} catch (_error) {
|
||||
const error = _error;
|
||||
const unknownRevision = error.output.some(line => line === null || line === void 0 ? void 0 : line.includes('unknown revision'));
|
||||
if (unknownRevision) {
|
||||
const isShallowClone = _child_process.default.execSync('git rev-parse --is-shallow-repository', {
|
||||
encoding: 'utf-8',
|
||||
stdio: 'pipe',
|
||||
cwd: configDir
|
||||
}).trim() === 'true';
|
||||
if (isShallowClone) {
|
||||
throw new Error([`The repository is a shallow clone and does not have '${baseCommit}' available locally.`, `Note that GitHub Actions checkout is shallow by default: https://github.com/actions/checkout`].join('\n'));
|
||||
}
|
||||
}
|
||||
throw new Error([`Cannot detect changed files for --only-changed mode:`, `git ${command}`, '', ...error.output].join('\n'));
|
||||
}
|
||||
}
|
||||
const untrackedFiles = gitFileList(`ls-files --others --exclude-standard`).map(file => _path.default.join(configDir, file));
|
||||
const [gitRoot] = gitFileList('rev-parse --show-toplevel');
|
||||
const trackedFilesWithChanges = gitFileList(`diff ${baseCommit} --name-only`).map(file => _path.default.join(gitRoot, file));
|
||||
return new Set((0, _compilationCache.affectedTestFiles)([...untrackedFiles, ...trackedFilesWithChanges]));
|
||||
}
|
||||
423
node_modules/playwright/lib/runner/watchMode.js
generated
vendored
Normal file
423
node_modules/playwright/lib/runner/watchMode.js
generated
vendored
Normal file
@@ -0,0 +1,423 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.runWatchModeLoop = runWatchModeLoop;
|
||||
var _readline = _interopRequireDefault(require("readline"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _utilsBundle2 = require("../utilsBundle");
|
||||
var _base = require("../reporters/base");
|
||||
var _playwrightServer = require("playwright-core/lib/remote/playwrightServer");
|
||||
var _testServer = require("./testServer");
|
||||
var _stream = require("stream");
|
||||
var _testServerConnection = require("../isomorphic/testServerConnection");
|
||||
var _teleSuiteUpdater = require("../isomorphic/teleSuiteUpdater");
|
||||
var _configLoader = require("../common/configLoader");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class InMemoryTransport extends _stream.EventEmitter {
|
||||
constructor(send) {
|
||||
super();
|
||||
this._send = void 0;
|
||||
this._send = send;
|
||||
}
|
||||
close() {
|
||||
this.emit('close');
|
||||
}
|
||||
onclose(listener) {
|
||||
this.on('close', listener);
|
||||
}
|
||||
onerror(listener) {
|
||||
// no-op to fulfil the interface, the user of InMemoryTransport doesn't emit any errors.
|
||||
}
|
||||
onmessage(listener) {
|
||||
this.on('message', listener);
|
||||
}
|
||||
onopen(listener) {
|
||||
this.on('open', listener);
|
||||
}
|
||||
send(data) {
|
||||
this._send(data);
|
||||
}
|
||||
}
|
||||
async function runWatchModeLoop(configLocation, initialOptions) {
|
||||
if ((0, _configLoader.restartWithExperimentalTsEsm)(undefined, true)) return 'restarted';
|
||||
const options = {
|
||||
...initialOptions
|
||||
};
|
||||
let bufferMode = false;
|
||||
const testServerDispatcher = new _testServer.TestServerDispatcher(configLocation, {});
|
||||
const transport = new InMemoryTransport(async data => {
|
||||
const {
|
||||
id,
|
||||
method,
|
||||
params
|
||||
} = JSON.parse(data);
|
||||
try {
|
||||
const result = await testServerDispatcher.transport.dispatch(method, params);
|
||||
transport.emit('message', JSON.stringify({
|
||||
id,
|
||||
result
|
||||
}));
|
||||
} catch (e) {
|
||||
transport.emit('message', JSON.stringify({
|
||||
id,
|
||||
error: String(e)
|
||||
}));
|
||||
}
|
||||
});
|
||||
testServerDispatcher.transport.sendEvent = (method, params) => {
|
||||
transport.emit('message', JSON.stringify({
|
||||
method,
|
||||
params
|
||||
}));
|
||||
};
|
||||
const testServerConnection = new _testServerConnection.TestServerConnection(transport);
|
||||
transport.emit('open');
|
||||
const teleSuiteUpdater = new _teleSuiteUpdater.TeleSuiteUpdater({
|
||||
pathSeparator: _path.default.sep,
|
||||
onUpdate() {}
|
||||
});
|
||||
const dirtyTestFiles = new Set();
|
||||
const dirtyTestIds = new Set();
|
||||
let onDirtyTests = new _utils.ManualPromise();
|
||||
let queue = Promise.resolve();
|
||||
const changedFiles = new Set();
|
||||
testServerConnection.onTestFilesChanged(({
|
||||
testFiles
|
||||
}) => {
|
||||
testFiles.forEach(file => changedFiles.add(file));
|
||||
queue = queue.then(async () => {
|
||||
if (changedFiles.size === 0) return;
|
||||
const {
|
||||
report
|
||||
} = await testServerConnection.listTests({
|
||||
locations: options.files,
|
||||
projects: options.projects,
|
||||
grep: options.grep
|
||||
});
|
||||
teleSuiteUpdater.processListReport(report);
|
||||
for (const test of teleSuiteUpdater.rootSuite.allTests()) {
|
||||
if (changedFiles.has(test.location.file)) {
|
||||
dirtyTestFiles.add(test.location.file);
|
||||
dirtyTestIds.add(test.id);
|
||||
}
|
||||
}
|
||||
changedFiles.clear();
|
||||
if (dirtyTestIds.size > 0) {
|
||||
onDirtyTests.resolve('changed');
|
||||
onDirtyTests = new _utils.ManualPromise();
|
||||
}
|
||||
});
|
||||
});
|
||||
testServerConnection.onReport(report => teleSuiteUpdater.processTestReportEvent(report));
|
||||
await testServerConnection.initialize({
|
||||
interceptStdio: false,
|
||||
watchTestDirs: true,
|
||||
populateDependenciesOnList: true
|
||||
});
|
||||
await testServerConnection.runGlobalSetup({});
|
||||
const {
|
||||
report
|
||||
} = await testServerConnection.listTests({});
|
||||
teleSuiteUpdater.processListReport(report);
|
||||
const projectNames = teleSuiteUpdater.rootSuite.suites.map(s => s.title);
|
||||
let lastRun = {
|
||||
type: 'regular'
|
||||
};
|
||||
let result = 'passed';
|
||||
while (true) {
|
||||
if (bufferMode) printBufferPrompt(dirtyTestFiles, teleSuiteUpdater.config.rootDir);else printPrompt();
|
||||
const waitForCommand = readCommand();
|
||||
const command = await Promise.race([onDirtyTests, waitForCommand.result]);
|
||||
if (command === 'changed') waitForCommand.cancel();
|
||||
if (bufferMode && command === 'changed') continue;
|
||||
const shouldRunChangedFiles = bufferMode ? command === 'run' : command === 'changed';
|
||||
if (shouldRunChangedFiles) {
|
||||
if (dirtyTestIds.size === 0) continue;
|
||||
const testIds = [...dirtyTestIds];
|
||||
dirtyTestIds.clear();
|
||||
dirtyTestFiles.clear();
|
||||
await runTests(options, testServerConnection, {
|
||||
testIds,
|
||||
title: 'files changed'
|
||||
});
|
||||
lastRun = {
|
||||
type: 'changed',
|
||||
dirtyTestIds: testIds
|
||||
};
|
||||
continue;
|
||||
}
|
||||
if (command === 'run') {
|
||||
// All means reset filters.
|
||||
await runTests(options, testServerConnection);
|
||||
lastRun = {
|
||||
type: 'regular'
|
||||
};
|
||||
continue;
|
||||
}
|
||||
if (command === 'project') {
|
||||
const {
|
||||
selectedProjects
|
||||
} = await _utilsBundle2.enquirer.prompt({
|
||||
type: 'multiselect',
|
||||
name: 'selectedProjects',
|
||||
message: 'Select projects',
|
||||
choices: projectNames
|
||||
}).catch(() => ({
|
||||
selectedProjects: null
|
||||
}));
|
||||
if (!selectedProjects) continue;
|
||||
options.projects = selectedProjects.length ? selectedProjects : undefined;
|
||||
await runTests(options, testServerConnection);
|
||||
lastRun = {
|
||||
type: 'regular'
|
||||
};
|
||||
continue;
|
||||
}
|
||||
if (command === 'file') {
|
||||
const {
|
||||
filePattern
|
||||
} = await _utilsBundle2.enquirer.prompt({
|
||||
type: 'text',
|
||||
name: 'filePattern',
|
||||
message: 'Input filename pattern (regex)'
|
||||
}).catch(() => ({
|
||||
filePattern: null
|
||||
}));
|
||||
if (filePattern === null) continue;
|
||||
if (filePattern.trim()) options.files = filePattern.split(' ');else options.files = undefined;
|
||||
await runTests(options, testServerConnection);
|
||||
lastRun = {
|
||||
type: 'regular'
|
||||
};
|
||||
continue;
|
||||
}
|
||||
if (command === 'grep') {
|
||||
const {
|
||||
testPattern
|
||||
} = await _utilsBundle2.enquirer.prompt({
|
||||
type: 'text',
|
||||
name: 'testPattern',
|
||||
message: 'Input test name pattern (regex)'
|
||||
}).catch(() => ({
|
||||
testPattern: null
|
||||
}));
|
||||
if (testPattern === null) continue;
|
||||
if (testPattern.trim()) options.grep = testPattern;else options.grep = undefined;
|
||||
await runTests(options, testServerConnection);
|
||||
lastRun = {
|
||||
type: 'regular'
|
||||
};
|
||||
continue;
|
||||
}
|
||||
if (command === 'failed') {
|
||||
const failedTestIds = teleSuiteUpdater.rootSuite.allTests().filter(t => !t.ok()).map(t => t.id);
|
||||
await runTests({}, testServerConnection, {
|
||||
title: 'running failed tests',
|
||||
testIds: failedTestIds
|
||||
});
|
||||
lastRun = {
|
||||
type: 'failed',
|
||||
failedTestIds
|
||||
};
|
||||
continue;
|
||||
}
|
||||
if (command === 'repeat') {
|
||||
if (lastRun.type === 'regular') {
|
||||
await runTests(options, testServerConnection, {
|
||||
title: 're-running tests'
|
||||
});
|
||||
continue;
|
||||
} else if (lastRun.type === 'changed') {
|
||||
await runTests(options, testServerConnection, {
|
||||
title: 're-running tests',
|
||||
testIds: lastRun.dirtyTestIds
|
||||
});
|
||||
} else if (lastRun.type === 'failed') {
|
||||
await runTests({}, testServerConnection, {
|
||||
title: 're-running tests',
|
||||
testIds: lastRun.failedTestIds
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (command === 'toggle-show-browser') {
|
||||
await toggleShowBrowser();
|
||||
continue;
|
||||
}
|
||||
if (command === 'toggle-buffer-mode') {
|
||||
bufferMode = !bufferMode;
|
||||
continue;
|
||||
}
|
||||
if (command === 'exit') break;
|
||||
if (command === 'interrupted') {
|
||||
result = 'interrupted';
|
||||
break;
|
||||
}
|
||||
}
|
||||
const teardown = await testServerConnection.runGlobalTeardown({});
|
||||
return result === 'passed' ? teardown.status : result;
|
||||
}
|
||||
function readKeyPress(handler) {
|
||||
const promise = new _utils.ManualPromise();
|
||||
const rl = _readline.default.createInterface({
|
||||
input: process.stdin,
|
||||
escapeCodeTimeout: 50
|
||||
});
|
||||
_readline.default.emitKeypressEvents(process.stdin, rl);
|
||||
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
||||
const listener = _utils.eventsHelper.addEventListener(process.stdin, 'keypress', (text, key) => {
|
||||
const result = handler(text, key);
|
||||
if (result) promise.resolve(result);
|
||||
});
|
||||
const cancel = () => {
|
||||
_utils.eventsHelper.removeEventListeners([listener]);
|
||||
rl.close();
|
||||
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
||||
};
|
||||
void promise.finally(cancel);
|
||||
return {
|
||||
result: promise,
|
||||
cancel
|
||||
};
|
||||
}
|
||||
const isInterrupt = (text, key) => text === '\x03' || text === '\x1B' || key && key.name === 'escape' || key && key.ctrl && key.name === 'c';
|
||||
async function runTests(watchOptions, testServerConnection, options) {
|
||||
printConfiguration(watchOptions, options === null || options === void 0 ? void 0 : options.title);
|
||||
const waitForDone = readKeyPress((text, key) => {
|
||||
if (isInterrupt(text, key)) {
|
||||
testServerConnection.stopTestsNoReply({});
|
||||
return 'done';
|
||||
}
|
||||
});
|
||||
await testServerConnection.runTests({
|
||||
grep: watchOptions.grep,
|
||||
testIds: options === null || options === void 0 ? void 0 : options.testIds,
|
||||
locations: watchOptions === null || watchOptions === void 0 ? void 0 : watchOptions.files,
|
||||
projects: watchOptions.projects,
|
||||
connectWsEndpoint,
|
||||
reuseContext: connectWsEndpoint ? true : undefined,
|
||||
workers: connectWsEndpoint ? 1 : undefined,
|
||||
headed: connectWsEndpoint ? true : undefined
|
||||
}).finally(() => waitForDone.cancel());
|
||||
}
|
||||
function readCommand() {
|
||||
return readKeyPress((text, key) => {
|
||||
if (isInterrupt(text, key)) return 'interrupted';
|
||||
if (process.platform !== 'win32' && key && key.ctrl && key.name === 'z') {
|
||||
process.kill(process.ppid, 'SIGTSTP');
|
||||
process.kill(process.pid, 'SIGTSTP');
|
||||
}
|
||||
const name = key === null || key === void 0 ? void 0 : key.name;
|
||||
if (name === 'q') return 'exit';
|
||||
if (name === 'h') {
|
||||
process.stdout.write(`${(0, _base.separator)()}
|
||||
Run tests
|
||||
${_utilsBundle.colors.bold('enter')} ${_utilsBundle.colors.dim('run tests')}
|
||||
${_utilsBundle.colors.bold('f')} ${_utilsBundle.colors.dim('run failed tests')}
|
||||
${_utilsBundle.colors.bold('r')} ${_utilsBundle.colors.dim('repeat last run')}
|
||||
${_utilsBundle.colors.bold('q')} ${_utilsBundle.colors.dim('quit')}
|
||||
|
||||
Change settings
|
||||
${_utilsBundle.colors.bold('c')} ${_utilsBundle.colors.dim('set project')}
|
||||
${_utilsBundle.colors.bold('p')} ${_utilsBundle.colors.dim('set file filter')}
|
||||
${_utilsBundle.colors.bold('t')} ${_utilsBundle.colors.dim('set title filter')}
|
||||
${_utilsBundle.colors.bold('s')} ${_utilsBundle.colors.dim('toggle show & reuse the browser')}
|
||||
${_utilsBundle.colors.bold('b')} ${_utilsBundle.colors.dim('toggle buffer mode')}
|
||||
`);
|
||||
return;
|
||||
}
|
||||
switch (name) {
|
||||
case 'return':
|
||||
return 'run';
|
||||
case 'r':
|
||||
return 'repeat';
|
||||
case 'c':
|
||||
return 'project';
|
||||
case 'p':
|
||||
return 'file';
|
||||
case 't':
|
||||
return 'grep';
|
||||
case 'f':
|
||||
return 'failed';
|
||||
case 's':
|
||||
return 'toggle-show-browser';
|
||||
case 'b':
|
||||
return 'toggle-buffer-mode';
|
||||
}
|
||||
});
|
||||
}
|
||||
let showBrowserServer;
|
||||
let connectWsEndpoint = undefined;
|
||||
let seq = 1;
|
||||
function printConfiguration(options, title) {
|
||||
const packageManagerCommand = (0, _utils.getPackageManagerExecCommand)();
|
||||
const tokens = [];
|
||||
tokens.push(`${packageManagerCommand} playwright test`);
|
||||
if (options.projects) tokens.push(...options.projects.map(p => _utilsBundle.colors.blue(`--project ${p}`)));
|
||||
if (options.grep) tokens.push(_utilsBundle.colors.red(`--grep ${options.grep}`));
|
||||
if (options.files) tokens.push(...options.files.map(a => _utilsBundle.colors.bold(a)));
|
||||
if (title) tokens.push(_utilsBundle.colors.dim(`(${title})`));
|
||||
tokens.push(_utilsBundle.colors.dim(`#${seq++}`));
|
||||
const lines = [];
|
||||
const sep = (0, _base.separator)();
|
||||
lines.push('\x1Bc' + sep);
|
||||
lines.push(`${tokens.join(' ')}`);
|
||||
lines.push(`${_utilsBundle.colors.dim('Show & reuse browser:')} ${_utilsBundle.colors.bold(showBrowserServer ? 'on' : 'off')}`);
|
||||
process.stdout.write(lines.join('\n'));
|
||||
}
|
||||
function printBufferPrompt(dirtyTestFiles, rootDir) {
|
||||
const sep = (0, _base.separator)();
|
||||
process.stdout.write('\x1Bc');
|
||||
process.stdout.write(`${sep}\n`);
|
||||
if (dirtyTestFiles.size === 0) {
|
||||
process.stdout.write(`${_utilsBundle.colors.dim('Waiting for file changes. Press')} ${_utilsBundle.colors.bold('q')} ${_utilsBundle.colors.dim('to quit or')} ${_utilsBundle.colors.bold('h')} ${_utilsBundle.colors.dim('for more options.')}\n\n`);
|
||||
return;
|
||||
}
|
||||
process.stdout.write(`${_utilsBundle.colors.dim(`${dirtyTestFiles.size} test ${dirtyTestFiles.size === 1 ? 'file' : 'files'} changed:`)}\n\n`);
|
||||
for (const file of dirtyTestFiles) process.stdout.write(` · ${_path.default.relative(rootDir, file)}\n`);
|
||||
process.stdout.write(`\n${_utilsBundle.colors.dim(`Press`)} ${_utilsBundle.colors.bold('enter')} ${_utilsBundle.colors.dim('to run')}, ${_utilsBundle.colors.bold('q')} ${_utilsBundle.colors.dim('to quit or')} ${_utilsBundle.colors.bold('h')} ${_utilsBundle.colors.dim('for more options.')}\n\n`);
|
||||
}
|
||||
function printPrompt() {
|
||||
const sep = (0, _base.separator)();
|
||||
process.stdout.write(`
|
||||
${sep}
|
||||
${_utilsBundle.colors.dim('Waiting for file changes. Press')} ${_utilsBundle.colors.bold('enter')} ${_utilsBundle.colors.dim('to run tests')}, ${_utilsBundle.colors.bold('q')} ${_utilsBundle.colors.dim('to quit or')} ${_utilsBundle.colors.bold('h')} ${_utilsBundle.colors.dim('for more options.')}
|
||||
`);
|
||||
}
|
||||
async function toggleShowBrowser() {
|
||||
if (!showBrowserServer) {
|
||||
showBrowserServer = new _playwrightServer.PlaywrightServer({
|
||||
mode: 'extension',
|
||||
path: '/' + (0, _utils.createGuid)(),
|
||||
maxConnections: 1
|
||||
});
|
||||
connectWsEndpoint = await showBrowserServer.listen();
|
||||
process.stdout.write(`${_utilsBundle.colors.dim('Show & reuse browser:')} ${_utilsBundle.colors.bold('on')}\n`);
|
||||
} else {
|
||||
var _showBrowserServer;
|
||||
await ((_showBrowserServer = showBrowserServer) === null || _showBrowserServer === void 0 ? void 0 : _showBrowserServer.close());
|
||||
showBrowserServer = undefined;
|
||||
connectWsEndpoint = undefined;
|
||||
process.stdout.write(`${_utilsBundle.colors.dim('Show & reuse browser:')} ${_utilsBundle.colors.bold('off')}\n`);
|
||||
}
|
||||
}
|
||||
85
node_modules/playwright/lib/runner/workerHost.js
generated
vendored
Normal file
85
node_modules/playwright/lib/runner/workerHost.js
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.WorkerHost = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _ipc = require("../common/ipc");
|
||||
var _processHost = require("./processHost");
|
||||
var _folders = require("../isomorphic/folders");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
let lastWorkerIndex = 0;
|
||||
class WorkerHost extends _processHost.ProcessHost {
|
||||
constructor(testGroup, parallelIndex, config, extraEnv, outputDir) {
|
||||
const workerIndex = lastWorkerIndex++;
|
||||
super(require.resolve('../worker/workerMain.js'), `worker-${workerIndex}`, {
|
||||
...extraEnv,
|
||||
FORCE_COLOR: '1',
|
||||
DEBUG_COLORS: process.env.DEBUG_COLORS === undefined ? '1' : process.env.DEBUG_COLORS
|
||||
});
|
||||
this.parallelIndex = void 0;
|
||||
this.workerIndex = void 0;
|
||||
this._hash = void 0;
|
||||
this._params = void 0;
|
||||
this._didFail = false;
|
||||
this.workerIndex = workerIndex;
|
||||
this.parallelIndex = parallelIndex;
|
||||
this._hash = testGroup.workerHash;
|
||||
this._params = {
|
||||
workerIndex: this.workerIndex,
|
||||
parallelIndex,
|
||||
repeatEachIndex: testGroup.repeatEachIndex,
|
||||
projectId: testGroup.projectId,
|
||||
config,
|
||||
artifactsDir: _path.default.join(outputDir, (0, _folders.artifactsFolderName)(workerIndex))
|
||||
};
|
||||
}
|
||||
async start() {
|
||||
await _fs.default.promises.mkdir(this._params.artifactsDir, {
|
||||
recursive: true
|
||||
});
|
||||
return await this.startRunner(this._params, {
|
||||
onStdOut: chunk => this.emit('stdOut', (0, _ipc.stdioChunkToParams)(chunk)),
|
||||
onStdErr: chunk => this.emit('stdErr', (0, _ipc.stdioChunkToParams)(chunk))
|
||||
});
|
||||
}
|
||||
async onExit() {
|
||||
await (0, _utils.removeFolders)([this._params.artifactsDir]);
|
||||
}
|
||||
async stop(didFail) {
|
||||
if (didFail) this._didFail = true;
|
||||
await super.stop();
|
||||
}
|
||||
runTestGroup(runPayload) {
|
||||
this.sendMessageNoReply({
|
||||
method: 'runTestGroup',
|
||||
params: runPayload
|
||||
});
|
||||
}
|
||||
hash() {
|
||||
return this._hash;
|
||||
}
|
||||
didFail() {
|
||||
return this._didFail;
|
||||
}
|
||||
}
|
||||
exports.WorkerHost = WorkerHost;
|
||||
111
node_modules/playwright/lib/third_party/tsconfig-loader.js
generated
vendored
Normal file
111
node_modules/playwright/lib/third_party/tsconfig-loader.js
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.loadTsConfig = loadTsConfig;
|
||||
var path = _interopRequireWildcard(require("path"));
|
||||
var fs = _interopRequireWildcard(require("fs"));
|
||||
var _utilsBundle = require("../utilsBundle");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Jonas Kello
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* Typing for the parts of tsconfig that we care about
|
||||
*/
|
||||
|
||||
function loadTsConfig(configPath) {
|
||||
try {
|
||||
const references = [];
|
||||
const config = innerLoadTsConfig(configPath, references);
|
||||
return [config, ...references];
|
||||
} catch (e) {
|
||||
throw new Error(`Failed to load tsconfig file at ${configPath}:\n${e.message}`);
|
||||
}
|
||||
}
|
||||
function resolveConfigFile(baseConfigFile, referencedConfigFile) {
|
||||
if (!referencedConfigFile.endsWith('.json')) referencedConfigFile += '.json';
|
||||
const currentDir = path.dirname(baseConfigFile);
|
||||
let resolvedConfigFile = path.resolve(currentDir, referencedConfigFile);
|
||||
// TODO: I don't see how this makes sense, delete in the next minor release.
|
||||
if (referencedConfigFile.includes('/') && referencedConfigFile.includes('.') && !fs.existsSync(resolvedConfigFile)) resolvedConfigFile = path.join(currentDir, 'node_modules', referencedConfigFile);
|
||||
return resolvedConfigFile;
|
||||
}
|
||||
function innerLoadTsConfig(configFilePath, references, visited = new Map()) {
|
||||
var _parsedConfig$compile, _parsedConfig$compile2, _parsedConfig$compile3;
|
||||
if (visited.has(configFilePath)) return visited.get(configFilePath);
|
||||
let result = {
|
||||
tsConfigPath: configFilePath
|
||||
};
|
||||
// Retain result instance below, so that caching works.
|
||||
visited.set(configFilePath, result);
|
||||
if (!fs.existsSync(configFilePath)) return result;
|
||||
const configString = fs.readFileSync(configFilePath, 'utf-8');
|
||||
const cleanedJson = StripBom(configString);
|
||||
const parsedConfig = _utilsBundle.json5.parse(cleanedJson);
|
||||
const extendsArray = Array.isArray(parsedConfig.extends) ? parsedConfig.extends : parsedConfig.extends ? [parsedConfig.extends] : [];
|
||||
for (const extendedConfig of extendsArray) {
|
||||
const extendedConfigPath = resolveConfigFile(configFilePath, extendedConfig);
|
||||
const base = innerLoadTsConfig(extendedConfigPath, references, visited);
|
||||
// Retain result instance, so that caching works.
|
||||
Object.assign(result, base, {
|
||||
tsConfigPath: configFilePath
|
||||
});
|
||||
}
|
||||
if (((_parsedConfig$compile = parsedConfig.compilerOptions) === null || _parsedConfig$compile === void 0 ? void 0 : _parsedConfig$compile.allowJs) !== undefined) result.allowJs = parsedConfig.compilerOptions.allowJs;
|
||||
if (((_parsedConfig$compile2 = parsedConfig.compilerOptions) === null || _parsedConfig$compile2 === void 0 ? void 0 : _parsedConfig$compile2.paths) !== undefined) {
|
||||
// We must store pathsBasePath from the config that defines "paths" and later resolve
|
||||
// based on this absolute path, when no "baseUrl" is specified. See tsc for reference:
|
||||
// https://github.com/microsoft/TypeScript/blob/353ccb7688351ae33ccf6e0acb913aa30621eaf4/src/compiler/commandLineParser.ts#L3129
|
||||
// https://github.com/microsoft/TypeScript/blob/353ccb7688351ae33ccf6e0acb913aa30621eaf4/src/compiler/moduleSpecifiers.ts#L510
|
||||
result.paths = {
|
||||
mapping: parsedConfig.compilerOptions.paths,
|
||||
pathsBasePath: path.dirname(configFilePath)
|
||||
};
|
||||
}
|
||||
if (((_parsedConfig$compile3 = parsedConfig.compilerOptions) === null || _parsedConfig$compile3 === void 0 ? void 0 : _parsedConfig$compile3.baseUrl) !== undefined) {
|
||||
// Follow tsc and resolve all relative file paths in the config right away.
|
||||
// This way it is safe to inherit paths between the configs.
|
||||
result.absoluteBaseUrl = path.resolve(path.dirname(configFilePath), parsedConfig.compilerOptions.baseUrl);
|
||||
}
|
||||
for (const ref of parsedConfig.references || []) references.push(innerLoadTsConfig(resolveConfigFile(configFilePath, ref.path), references, visited));
|
||||
if (path.basename(configFilePath) === 'jsconfig.json' && result.allowJs === undefined) result.allowJs = true;
|
||||
return result;
|
||||
}
|
||||
function StripBom(string) {
|
||||
if (typeof string !== 'string') {
|
||||
throw new TypeError(`Expected a string, got ${typeof string}`);
|
||||
}
|
||||
|
||||
// Catches EFBBBF (UTF-8 BOM) because the buffer-to-string
|
||||
// conversion translates it to FEFF (UTF-16 BOM).
|
||||
if (string.charCodeAt(0) === 0xFEFF) {
|
||||
return string.slice(1);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
28
node_modules/playwright/lib/transform/babelBundle.js
generated
vendored
Normal file
28
node_modules/playwright/lib/transform/babelBundle.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.types = exports.traverse = exports.declare = exports.codeFrameColumns = exports.babelTransform = exports.babelParse = void 0;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const codeFrameColumns = exports.codeFrameColumns = require('./babelBundleImpl').codeFrameColumns;
|
||||
const declare = exports.declare = require('./babelBundleImpl').declare;
|
||||
const types = exports.types = require('./babelBundleImpl').types;
|
||||
const traverse = exports.traverse = require('./babelBundleImpl').traverse;
|
||||
const babelTransform = exports.babelTransform = require('./babelBundleImpl').babelTransform;
|
||||
const babelParse = exports.babelParse = require('./babelBundleImpl').babelParse;
|
||||
2032
node_modules/playwright/lib/transform/babelBundleImpl.js
generated
vendored
Normal file
2032
node_modules/playwright/lib/transform/babelBundleImpl.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
254
node_modules/playwright/lib/transform/compilationCache.js
generated
vendored
Normal file
254
node_modules/playwright/lib/transform/compilationCache.js
generated
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.addToCompilationCache = addToCompilationCache;
|
||||
exports.affectedTestFiles = affectedTestFiles;
|
||||
exports.belongsToNodeModules = belongsToNodeModules;
|
||||
exports.cacheDir = void 0;
|
||||
exports.collectAffectedTestFiles = collectAffectedTestFiles;
|
||||
exports.currentFileDepsCollector = currentFileDepsCollector;
|
||||
exports.dependenciesForTestFile = dependenciesForTestFile;
|
||||
exports.fileDependenciesForTest = fileDependenciesForTest;
|
||||
exports.getFromCompilationCache = getFromCompilationCache;
|
||||
exports.getUserData = getUserData;
|
||||
exports.installSourceMapSupport = installSourceMapSupport;
|
||||
exports.internalDependenciesForTestFile = internalDependenciesForTestFile;
|
||||
exports.serializeCompilationCache = serializeCompilationCache;
|
||||
exports.setExternalDependencies = setExternalDependencies;
|
||||
exports.startCollectingFileDeps = startCollectingFileDeps;
|
||||
exports.stopCollectingFileDeps = stopCollectingFileDeps;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _os = _interopRequireDefault(require("os"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _utilsBundle = require("../utilsBundle");
|
||||
var _globals = require("../common/globals");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Assumptions for the compilation cache:
|
||||
// - Files in the temp directory we work with can disappear at any moment, either some of them or all together.
|
||||
// - Multiple workers can be trying to read from the compilation cache at the same time.
|
||||
// - There is a single invocation of the test runner at a time.
|
||||
//
|
||||
// Therefore, we implement the following logic:
|
||||
// - Never assume that file is present, always try to read it to determine whether it's actually present.
|
||||
// - Never write to the cache from worker processes to avoid "multiple writers" races.
|
||||
// - Since we perform all static imports in the runner beforehand, most of the time
|
||||
// workers should be able to read from the cache.
|
||||
// - For workers-only dynamic imports or some cache problems, we will re-transpile files in
|
||||
// each worker anew.
|
||||
|
||||
const cacheDir = exports.cacheDir = process.env.PWTEST_CACHE_DIR || ((_process$geteuid, _process) => {
|
||||
if (process.platform === 'win32') return _path.default.join(_os.default.tmpdir(), `playwright-transform-cache`);
|
||||
// Use `geteuid()` instead of more natural `os.userInfo().username`
|
||||
// since `os.userInfo()` is not always available.
|
||||
// Note: `process.geteuid()` is not available on windows.
|
||||
// See https://github.com/microsoft/playwright/issues/22721
|
||||
return _path.default.join(_os.default.tmpdir(), `playwright-transform-cache-` + ((_process$geteuid = (_process = process).geteuid) === null || _process$geteuid === void 0 ? void 0 : _process$geteuid.call(_process)));
|
||||
})();
|
||||
const sourceMaps = new Map();
|
||||
const memoryCache = new Map();
|
||||
// Dependencies resolved by the loader.
|
||||
const fileDependencies = new Map();
|
||||
// Dependencies resolved by the external bundler.
|
||||
const externalDependencies = new Map();
|
||||
function installSourceMapSupport() {
|
||||
Error.stackTraceLimit = 200;
|
||||
_utilsBundle.sourceMapSupport.install({
|
||||
environment: 'node',
|
||||
handleUncaughtExceptions: false,
|
||||
retrieveSourceMap(source) {
|
||||
if (!sourceMaps.has(source)) return null;
|
||||
const sourceMapPath = sourceMaps.get(source);
|
||||
try {
|
||||
return {
|
||||
map: JSON.parse(_fs.default.readFileSync(sourceMapPath, 'utf-8')),
|
||||
url: source
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function _innerAddToCompilationCacheAndSerialize(filename, entry) {
|
||||
sourceMaps.set(entry.moduleUrl || filename, entry.sourceMapPath);
|
||||
memoryCache.set(filename, entry);
|
||||
return {
|
||||
sourceMaps: [[entry.moduleUrl || filename, entry.sourceMapPath]],
|
||||
memoryCache: [[filename, entry]],
|
||||
fileDependencies: [],
|
||||
externalDependencies: []
|
||||
};
|
||||
}
|
||||
function getFromCompilationCache(filename, hash, moduleUrl) {
|
||||
// First check the memory cache by filename, this cache will always work in the worker,
|
||||
// because we just compiled this file in the loader.
|
||||
const cache = memoryCache.get(filename);
|
||||
if (cache !== null && cache !== void 0 && cache.codePath) {
|
||||
try {
|
||||
return {
|
||||
cachedCode: _fs.default.readFileSync(cache.codePath, 'utf-8')
|
||||
};
|
||||
} catch {
|
||||
// Not able to read the file - fall through.
|
||||
}
|
||||
}
|
||||
|
||||
// Then do the disk cache, this cache works between the Playwright Test runs.
|
||||
const cachePath = calculateCachePath(filename, hash);
|
||||
const codePath = cachePath + '.js';
|
||||
const sourceMapPath = cachePath + '.map';
|
||||
const dataPath = cachePath + '.data';
|
||||
try {
|
||||
const cachedCode = _fs.default.readFileSync(codePath, 'utf8');
|
||||
const serializedCache = _innerAddToCompilationCacheAndSerialize(filename, {
|
||||
codePath,
|
||||
sourceMapPath,
|
||||
dataPath,
|
||||
moduleUrl
|
||||
});
|
||||
return {
|
||||
cachedCode,
|
||||
serializedCache
|
||||
};
|
||||
} catch {}
|
||||
return {
|
||||
addToCache: (code, map, data) => {
|
||||
if ((0, _globals.isWorkerProcess)()) return {};
|
||||
_fs.default.mkdirSync(_path.default.dirname(cachePath), {
|
||||
recursive: true
|
||||
});
|
||||
if (map) _fs.default.writeFileSync(sourceMapPath, JSON.stringify(map), 'utf8');
|
||||
if (data.size) _fs.default.writeFileSync(dataPath, JSON.stringify(Object.fromEntries(data.entries()), undefined, 2), 'utf8');
|
||||
_fs.default.writeFileSync(codePath, code, 'utf8');
|
||||
const serializedCache = _innerAddToCompilationCacheAndSerialize(filename, {
|
||||
codePath,
|
||||
sourceMapPath,
|
||||
dataPath,
|
||||
moduleUrl
|
||||
});
|
||||
return {
|
||||
serializedCache
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
function serializeCompilationCache() {
|
||||
return {
|
||||
sourceMaps: [...sourceMaps.entries()],
|
||||
memoryCache: [...memoryCache.entries()],
|
||||
fileDependencies: [...fileDependencies.entries()].map(([filename, deps]) => [filename, [...deps]]),
|
||||
externalDependencies: [...externalDependencies.entries()].map(([filename, deps]) => [filename, [...deps]])
|
||||
};
|
||||
}
|
||||
function addToCompilationCache(payload) {
|
||||
for (const entry of payload.sourceMaps) sourceMaps.set(entry[0], entry[1]);
|
||||
for (const entry of payload.memoryCache) memoryCache.set(entry[0], entry[1]);
|
||||
for (const entry of payload.fileDependencies) {
|
||||
const existing = fileDependencies.get(entry[0]) || [];
|
||||
fileDependencies.set(entry[0], new Set([...entry[1], ...existing]));
|
||||
}
|
||||
for (const entry of payload.externalDependencies) {
|
||||
const existing = externalDependencies.get(entry[0]) || [];
|
||||
externalDependencies.set(entry[0], new Set([...entry[1], ...existing]));
|
||||
}
|
||||
}
|
||||
function calculateCachePath(filePath, hash) {
|
||||
const fileName = _path.default.basename(filePath, _path.default.extname(filePath)).replace(/\W/g, '') + '_' + hash;
|
||||
return _path.default.join(cacheDir, hash[0] + hash[1], fileName);
|
||||
}
|
||||
|
||||
// Since ESM and CJS collect dependencies differently,
|
||||
// we go via the global state to collect them.
|
||||
let depsCollector;
|
||||
function startCollectingFileDeps() {
|
||||
depsCollector = new Set();
|
||||
}
|
||||
function stopCollectingFileDeps(filename) {
|
||||
if (!depsCollector) return;
|
||||
depsCollector.delete(filename);
|
||||
for (const dep of depsCollector) {
|
||||
if (belongsToNodeModules(dep)) depsCollector.delete(dep);
|
||||
}
|
||||
fileDependencies.set(filename, depsCollector);
|
||||
depsCollector = undefined;
|
||||
}
|
||||
function currentFileDepsCollector() {
|
||||
return depsCollector;
|
||||
}
|
||||
function setExternalDependencies(filename, deps) {
|
||||
const depsSet = new Set(deps.filter(dep => !belongsToNodeModules(dep) && dep !== filename));
|
||||
externalDependencies.set(filename, depsSet);
|
||||
}
|
||||
function fileDependenciesForTest() {
|
||||
return fileDependencies;
|
||||
}
|
||||
function collectAffectedTestFiles(changedFile, testFileCollector) {
|
||||
const isTestFile = file => fileDependencies.has(file);
|
||||
if (isTestFile(changedFile)) testFileCollector.add(changedFile);
|
||||
for (const [testFile, deps] of fileDependencies) {
|
||||
if (deps.has(changedFile)) testFileCollector.add(testFile);
|
||||
}
|
||||
for (const [importingFile, depsOfImportingFile] of externalDependencies) {
|
||||
if (depsOfImportingFile.has(changedFile)) {
|
||||
if (isTestFile(importingFile)) testFileCollector.add(importingFile);
|
||||
for (const [testFile, depsOfTestFile] of fileDependencies) {
|
||||
if (depsOfTestFile.has(importingFile)) testFileCollector.add(testFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function affectedTestFiles(changes) {
|
||||
const result = new Set();
|
||||
for (const change of changes) collectAffectedTestFiles(change, result);
|
||||
return [...result];
|
||||
}
|
||||
function internalDependenciesForTestFile(filename) {
|
||||
return fileDependencies.get(filename);
|
||||
}
|
||||
function dependenciesForTestFile(filename) {
|
||||
const result = new Set();
|
||||
for (const testDependency of fileDependencies.get(filename) || []) {
|
||||
result.add(testDependency);
|
||||
for (const externalDependency of externalDependencies.get(testDependency) || []) result.add(externalDependency);
|
||||
}
|
||||
for (const dep of externalDependencies.get(filename) || []) result.add(dep);
|
||||
return result;
|
||||
}
|
||||
|
||||
// This is only used in the dev mode, specifically excluding
|
||||
// files from packages/playwright*. In production mode, node_modules covers
|
||||
// that.
|
||||
const kPlaywrightInternalPrefix = _path.default.resolve(__dirname, '../../../playwright');
|
||||
function belongsToNodeModules(file) {
|
||||
if (file.includes(`${_path.default.sep}node_modules${_path.default.sep}`)) return true;
|
||||
if (file.startsWith(kPlaywrightInternalPrefix) && (file.endsWith('.js') || file.endsWith('.mjs'))) return true;
|
||||
return false;
|
||||
}
|
||||
async function getUserData(pluginName) {
|
||||
const result = new Map();
|
||||
for (const [fileName, cache] of memoryCache) {
|
||||
if (!cache.dataPath) continue;
|
||||
if (!_fs.default.existsSync(cache.dataPath)) continue;
|
||||
const data = JSON.parse(await _fs.default.promises.readFile(cache.dataPath, 'utf8'));
|
||||
if (data[pluginName]) result.set(fileName, data[pluginName]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
117
node_modules/playwright/lib/transform/esmLoader.js
generated
vendored
Normal file
117
node_modules/playwright/lib/transform/esmLoader.js
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
"use strict";
|
||||
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _url = _interopRequireDefault(require("url"));
|
||||
var _compilationCache = require("./compilationCache");
|
||||
var _transform = require("./transform");
|
||||
var _portTransport = require("./portTransport");
|
||||
var _util = require("../util");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Node < 18.6: defaultResolve takes 3 arguments.
|
||||
// Node >= 18.6: nextResolve from the chain takes 2 arguments.
|
||||
async function resolve(specifier, context, defaultResolve) {
|
||||
var _currentFileDepsColle;
|
||||
if (context.parentURL && context.parentURL.startsWith('file://')) {
|
||||
const filename = _url.default.fileURLToPath(context.parentURL);
|
||||
const resolved = (0, _transform.resolveHook)(filename, specifier);
|
||||
if (resolved !== undefined) specifier = _url.default.pathToFileURL(resolved).toString();
|
||||
}
|
||||
const result = await defaultResolve(specifier, context, defaultResolve);
|
||||
// Note: we collect dependencies here that will be sent to the main thread
|
||||
// (and optionally runner process) after the loading finishes.
|
||||
if (result !== null && result !== void 0 && result.url && result.url.startsWith('file://')) (_currentFileDepsColle = (0, _compilationCache.currentFileDepsCollector)()) === null || _currentFileDepsColle === void 0 || _currentFileDepsColle.add(_url.default.fileURLToPath(result.url));
|
||||
return result;
|
||||
}
|
||||
|
||||
// Node < 18.6: defaultLoad takes 3 arguments.
|
||||
// Node >= 18.6: nextLoad from the chain takes 2 arguments.
|
||||
async function load(moduleUrl, context, defaultLoad) {
|
||||
var _transport;
|
||||
// Bail out for wasm, json, etc.
|
||||
// non-js files have context.format === undefined
|
||||
if (context.format !== 'commonjs' && context.format !== 'module' && context.format !== undefined) return defaultLoad(moduleUrl, context, defaultLoad);
|
||||
|
||||
// Bail for built-in modules.
|
||||
if (!moduleUrl.startsWith('file://')) return defaultLoad(moduleUrl, context, defaultLoad);
|
||||
const filename = _url.default.fileURLToPath(moduleUrl);
|
||||
// Bail for node_modules.
|
||||
if (!(0, _transform.shouldTransform)(filename)) return defaultLoad(moduleUrl, context, defaultLoad);
|
||||
const code = _fs.default.readFileSync(filename, 'utf-8');
|
||||
const transformed = (0, _transform.transformHook)(code, filename, moduleUrl);
|
||||
|
||||
// Flush the source maps to the main thread, so that errors during import() are source-mapped.
|
||||
if (transformed.serializedCache) await ((_transport = transport) === null || _transport === void 0 ? void 0 : _transport.send('pushToCompilationCache', {
|
||||
cache: transformed.serializedCache
|
||||
}));
|
||||
|
||||
// Output format is required, so we determine it manually when unknown.
|
||||
// shortCircuit is required by Node >= 18.6 to designate no more loaders should be called.
|
||||
return {
|
||||
format: context.format || ((0, _util.fileIsModule)(filename) ? 'module' : 'commonjs'),
|
||||
source: transformed.code,
|
||||
shortCircuit: true
|
||||
};
|
||||
}
|
||||
let transport;
|
||||
|
||||
// Node.js < 20
|
||||
function globalPreload(context) {
|
||||
transport = createTransport(context.port);
|
||||
return `
|
||||
globalThis.__esmLoaderPortPreV20 = port;
|
||||
`;
|
||||
}
|
||||
|
||||
// Node.js >= 20
|
||||
function initialize(data) {
|
||||
transport = createTransport(data === null || data === void 0 ? void 0 : data.port);
|
||||
}
|
||||
function createTransport(port) {
|
||||
return new _portTransport.PortTransport(port, async (method, params) => {
|
||||
if (method === 'setSingleTSConfig') {
|
||||
(0, _transform.setSingleTSConfig)(params.tsconfig);
|
||||
return;
|
||||
}
|
||||
if (method === 'setTransformConfig') {
|
||||
(0, _transform.setTransformConfig)(params.config);
|
||||
return;
|
||||
}
|
||||
if (method === 'addToCompilationCache') {
|
||||
(0, _compilationCache.addToCompilationCache)(params.cache);
|
||||
return;
|
||||
}
|
||||
if (method === 'getCompilationCache') return {
|
||||
cache: (0, _compilationCache.serializeCompilationCache)()
|
||||
};
|
||||
if (method === 'startCollectingFileDeps') {
|
||||
(0, _compilationCache.startCollectingFileDeps)();
|
||||
return;
|
||||
}
|
||||
if (method === 'stopCollectingFileDeps') {
|
||||
(0, _compilationCache.stopCollectingFileDeps)(params.file);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
module.exports = {
|
||||
resolve,
|
||||
load,
|
||||
globalPreload,
|
||||
initialize
|
||||
};
|
||||
32
node_modules/playwright/lib/transform/esmUtils.js
generated
vendored
Normal file
32
node_modules/playwright/lib/transform/esmUtils.js
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.execArgvWithExperimentalLoaderOptions = execArgvWithExperimentalLoaderOptions;
|
||||
exports.execArgvWithoutExperimentalLoaderOptions = execArgvWithoutExperimentalLoaderOptions;
|
||||
var _url = _interopRequireDefault(require("url"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const kExperimentalLoaderOptions = ['--no-warnings', `--experimental-loader=${_url.default.pathToFileURL(require.resolve('playwright/lib/transform/esmLoader')).toString()}`];
|
||||
function execArgvWithExperimentalLoaderOptions() {
|
||||
return [...process.execArgv, ...kExperimentalLoaderOptions];
|
||||
}
|
||||
function execArgvWithoutExperimentalLoaderOptions() {
|
||||
return process.execArgv.filter(arg => !kExperimentalLoaderOptions.includes(arg));
|
||||
}
|
||||
81
node_modules/playwright/lib/transform/portTransport.js
generated
vendored
Normal file
81
node_modules/playwright/lib/transform/portTransport.js
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.PortTransport = void 0;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class PortTransport {
|
||||
constructor(port, handler) {
|
||||
this._lastId = 0;
|
||||
this._port = void 0;
|
||||
this._callbacks = new Map();
|
||||
this._port = port;
|
||||
port.addEventListener('message', async event => {
|
||||
const message = event.data;
|
||||
const {
|
||||
id,
|
||||
ackId,
|
||||
method,
|
||||
params,
|
||||
result
|
||||
} = message;
|
||||
if (id) {
|
||||
const result = await handler(method, params);
|
||||
this._port.postMessage({
|
||||
ackId: id,
|
||||
result
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (ackId) {
|
||||
const callback = this._callbacks.get(ackId);
|
||||
this._callbacks.delete(ackId);
|
||||
this._resetRef();
|
||||
callback === null || callback === void 0 || callback(result);
|
||||
return;
|
||||
}
|
||||
});
|
||||
// Make sure to unref **after** adding a 'message' event listener.
|
||||
// https://nodejs.org/api/worker_threads.html#portref
|
||||
this._resetRef();
|
||||
}
|
||||
async send(method, params) {
|
||||
return await new Promise(f => {
|
||||
const id = ++this._lastId;
|
||||
this._callbacks.set(id, f);
|
||||
this._resetRef();
|
||||
this._port.postMessage({
|
||||
id,
|
||||
method,
|
||||
params
|
||||
});
|
||||
});
|
||||
}
|
||||
_resetRef() {
|
||||
if (this._callbacks.size) {
|
||||
// When we are waiting for a response, ref the port to prevent this process from exiting.
|
||||
this._port.ref();
|
||||
} else {
|
||||
// When we are not waiting for a response, unref the port to prevent this process
|
||||
// from hanging forever.
|
||||
this._port.unref();
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.PortTransport = PortTransport;
|
||||
293
node_modules/playwright/lib/transform/transform.js
generated
vendored
Normal file
293
node_modules/playwright/lib/transform/transform.js
generated
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.requireOrImport = requireOrImport;
|
||||
exports.resolveHook = resolveHook;
|
||||
exports.setSingleTSConfig = setSingleTSConfig;
|
||||
exports.setTransformConfig = setTransformConfig;
|
||||
exports.setTransformData = setTransformData;
|
||||
exports.shouldTransform = shouldTransform;
|
||||
exports.singleTSConfig = singleTSConfig;
|
||||
exports.transformConfig = transformConfig;
|
||||
exports.transformHook = transformHook;
|
||||
exports.wrapFunctionWithLocation = wrapFunctionWithLocation;
|
||||
var _crypto = _interopRequireDefault(require("crypto"));
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _url = _interopRequireDefault(require("url"));
|
||||
var _utilsBundle = require("../utilsBundle");
|
||||
var _tsconfigLoader = require("../third_party/tsconfig-loader");
|
||||
var _module = _interopRequireDefault(require("module"));
|
||||
var _util = require("../util");
|
||||
var _compilationCache = require("./compilationCache");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const version = require('../../package.json').version;
|
||||
const cachedTSConfigs = new Map();
|
||||
let _transformConfig = {
|
||||
babelPlugins: [],
|
||||
external: []
|
||||
};
|
||||
let _externalMatcher = () => false;
|
||||
function setTransformConfig(config) {
|
||||
_transformConfig = config;
|
||||
_externalMatcher = (0, _util.createFileMatcher)(_transformConfig.external);
|
||||
}
|
||||
function transformConfig() {
|
||||
return _transformConfig;
|
||||
}
|
||||
let _singleTSConfigPath;
|
||||
let _singleTSConfig;
|
||||
function setSingleTSConfig(value) {
|
||||
_singleTSConfigPath = value;
|
||||
}
|
||||
function singleTSConfig() {
|
||||
return _singleTSConfigPath;
|
||||
}
|
||||
function validateTsConfig(tsconfig) {
|
||||
var _tsconfig$absoluteBas, _tsconfig$paths, _tsconfig$paths2;
|
||||
// When no explicit baseUrl is set, resolve paths relative to the tsconfig file.
|
||||
// See https://www.typescriptlang.org/tsconfig#paths
|
||||
const pathsBase = (_tsconfig$absoluteBas = tsconfig.absoluteBaseUrl) !== null && _tsconfig$absoluteBas !== void 0 ? _tsconfig$absoluteBas : (_tsconfig$paths = tsconfig.paths) === null || _tsconfig$paths === void 0 ? void 0 : _tsconfig$paths.pathsBasePath;
|
||||
// Only add the catch-all mapping when baseUrl is specified
|
||||
const pathsFallback = tsconfig.absoluteBaseUrl ? [{
|
||||
key: '*',
|
||||
values: ['*']
|
||||
}] : [];
|
||||
return {
|
||||
allowJs: !!tsconfig.allowJs,
|
||||
pathsBase,
|
||||
paths: Object.entries(((_tsconfig$paths2 = tsconfig.paths) === null || _tsconfig$paths2 === void 0 ? void 0 : _tsconfig$paths2.mapping) || {}).map(([key, values]) => ({
|
||||
key,
|
||||
values
|
||||
})).concat(pathsFallback)
|
||||
};
|
||||
}
|
||||
function loadAndValidateTsconfigsForFile(file) {
|
||||
if (_singleTSConfigPath && !_singleTSConfig) _singleTSConfig = (0, _tsconfigLoader.loadTsConfig)(_singleTSConfigPath).map(validateTsConfig);
|
||||
if (_singleTSConfig) return _singleTSConfig;
|
||||
return loadAndValidateTsconfigsForFolder(_path.default.dirname(file));
|
||||
}
|
||||
function loadAndValidateTsconfigsForFolder(folder) {
|
||||
const foldersWithConfig = [];
|
||||
let currentFolder = _path.default.resolve(folder);
|
||||
let result;
|
||||
while (true) {
|
||||
const cached = cachedTSConfigs.get(currentFolder);
|
||||
if (cached) {
|
||||
result = cached;
|
||||
break;
|
||||
}
|
||||
foldersWithConfig.push(currentFolder);
|
||||
for (const name of ['tsconfig.json', 'jsconfig.json']) {
|
||||
const configPath = _path.default.join(currentFolder, name);
|
||||
if (_fs.default.existsSync(configPath)) {
|
||||
const loaded = (0, _tsconfigLoader.loadTsConfig)(configPath);
|
||||
result = loaded.map(validateTsConfig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result) break;
|
||||
const parentFolder = _path.default.resolve(currentFolder, '../');
|
||||
if (currentFolder === parentFolder) break;
|
||||
currentFolder = parentFolder;
|
||||
}
|
||||
result = result || [];
|
||||
for (const folder of foldersWithConfig) cachedTSConfigs.set(folder, result);
|
||||
return result;
|
||||
}
|
||||
const pathSeparator = process.platform === 'win32' ? ';' : ':';
|
||||
const builtins = new Set(_module.default.builtinModules);
|
||||
function resolveHook(filename, specifier) {
|
||||
if (specifier.startsWith('node:') || builtins.has(specifier)) return;
|
||||
if (!shouldTransform(filename)) return;
|
||||
if (isRelativeSpecifier(specifier)) return (0, _util.resolveImportSpecifierAfterMapping)(_path.default.resolve(_path.default.dirname(filename), specifier), false);
|
||||
|
||||
/**
|
||||
* TypeScript discourages path-mapping into node_modules:
|
||||
* https://www.typescriptlang.org/docs/handbook/modules/reference.html#paths-should-not-point-to-monorepo-packages-or-node_modules-packages
|
||||
* However, if path-mapping doesn't yield a result, TypeScript falls back to the default resolution through node_modules.
|
||||
*/
|
||||
const isTypeScript = filename.endsWith('.ts') || filename.endsWith('.tsx');
|
||||
const tsconfigs = loadAndValidateTsconfigsForFile(filename);
|
||||
for (const tsconfig of tsconfigs) {
|
||||
if (!isTypeScript && !tsconfig.allowJs) continue;
|
||||
let longestPrefixLength = -1;
|
||||
let pathMatchedByLongestPrefix;
|
||||
for (const {
|
||||
key,
|
||||
values
|
||||
} of tsconfig.paths) {
|
||||
let matchedPartOfSpecifier = specifier;
|
||||
const [keyPrefix, keySuffix] = key.split('*');
|
||||
if (key.includes('*')) {
|
||||
// * If pattern contains '*' then to match pattern "<prefix>*<suffix>" module name must start with the <prefix> and end with <suffix>.
|
||||
// * <MatchedStar> denotes part of the module name between <prefix> and <suffix>.
|
||||
// * If module name can be matches with multiple patterns then pattern with the longest prefix will be picked.
|
||||
// https://github.com/microsoft/TypeScript/blob/f82d0cb3299c04093e3835bc7e29f5b40475f586/src/compiler/moduleNameResolver.ts#L1049
|
||||
if (keyPrefix) {
|
||||
if (!specifier.startsWith(keyPrefix)) continue;
|
||||
matchedPartOfSpecifier = matchedPartOfSpecifier.substring(keyPrefix.length, matchedPartOfSpecifier.length);
|
||||
}
|
||||
if (keySuffix) {
|
||||
if (!specifier.endsWith(keySuffix)) continue;
|
||||
matchedPartOfSpecifier = matchedPartOfSpecifier.substring(0, matchedPartOfSpecifier.length - keySuffix.length);
|
||||
}
|
||||
} else {
|
||||
if (specifier !== key) continue;
|
||||
matchedPartOfSpecifier = specifier;
|
||||
}
|
||||
if (keyPrefix.length <= longestPrefixLength) continue;
|
||||
for (const value of values) {
|
||||
let candidate = value;
|
||||
if (value.includes('*')) candidate = candidate.replace('*', matchedPartOfSpecifier);
|
||||
candidate = _path.default.resolve(tsconfig.pathsBase, candidate);
|
||||
const existing = (0, _util.resolveImportSpecifierAfterMapping)(candidate, true);
|
||||
if (existing) {
|
||||
longestPrefixLength = keyPrefix.length;
|
||||
pathMatchedByLongestPrefix = existing;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pathMatchedByLongestPrefix) return pathMatchedByLongestPrefix;
|
||||
}
|
||||
if (_path.default.isAbsolute(specifier)) {
|
||||
// Handle absolute file paths like `import '/path/to/file'`
|
||||
// Do not handle module imports like `import 'fs'`
|
||||
return (0, _util.resolveImportSpecifierAfterMapping)(specifier, false);
|
||||
}
|
||||
}
|
||||
function shouldTransform(filename) {
|
||||
if (_externalMatcher(filename)) return false;
|
||||
return !(0, _compilationCache.belongsToNodeModules)(filename);
|
||||
}
|
||||
let transformData;
|
||||
function setTransformData(pluginName, value) {
|
||||
transformData.set(pluginName, value);
|
||||
}
|
||||
function transformHook(originalCode, filename, moduleUrl) {
|
||||
const hasPreprocessor = process.env.PW_TEST_SOURCE_TRANSFORM && process.env.PW_TEST_SOURCE_TRANSFORM_SCOPE && process.env.PW_TEST_SOURCE_TRANSFORM_SCOPE.split(pathSeparator).some(f => filename.startsWith(f));
|
||||
const pluginsPrologue = _transformConfig.babelPlugins;
|
||||
const pluginsEpilogue = hasPreprocessor ? [[process.env.PW_TEST_SOURCE_TRANSFORM]] : [];
|
||||
const hash = calculateHash(originalCode, filename, !!moduleUrl, pluginsPrologue, pluginsEpilogue);
|
||||
const {
|
||||
cachedCode,
|
||||
addToCache,
|
||||
serializedCache
|
||||
} = (0, _compilationCache.getFromCompilationCache)(filename, hash, moduleUrl);
|
||||
if (cachedCode !== undefined) return {
|
||||
code: cachedCode,
|
||||
serializedCache
|
||||
};
|
||||
|
||||
// We don't use any browserslist data, but babel checks it anyway.
|
||||
// Silence the annoying warning.
|
||||
process.env.BROWSERSLIST_IGNORE_OLD_DATA = 'true';
|
||||
const {
|
||||
babelTransform
|
||||
} = require('./babelBundle');
|
||||
transformData = new Map();
|
||||
const {
|
||||
code,
|
||||
map
|
||||
} = babelTransform(originalCode, filename, !!moduleUrl, pluginsPrologue, pluginsEpilogue);
|
||||
if (!code) return {
|
||||
code: '',
|
||||
serializedCache
|
||||
};
|
||||
const added = addToCache(code, map, transformData);
|
||||
return {
|
||||
code,
|
||||
serializedCache: added.serializedCache
|
||||
};
|
||||
}
|
||||
function calculateHash(content, filePath, isModule, pluginsPrologue, pluginsEpilogue) {
|
||||
const hash = _crypto.default.createHash('sha1').update(isModule ? 'esm' : 'no_esm').update(content).update(filePath).update(version).update(pluginsPrologue.map(p => p[0]).join(',')).update(pluginsEpilogue.map(p => p[0]).join(',')).digest('hex');
|
||||
return hash;
|
||||
}
|
||||
async function requireOrImport(file) {
|
||||
installTransformIfNeeded();
|
||||
const isModule = (0, _util.fileIsModule)(file);
|
||||
const esmImport = () => eval(`import(${JSON.stringify(_url.default.pathToFileURL(file))})`);
|
||||
if (isModule) return await esmImport();
|
||||
const result = require(file);
|
||||
const depsCollector = (0, _compilationCache.currentFileDepsCollector)();
|
||||
if (depsCollector) {
|
||||
const module = require.cache[file];
|
||||
if (module) collectCJSDependencies(module, depsCollector);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
let transformInstalled = false;
|
||||
function installTransformIfNeeded() {
|
||||
if (transformInstalled) return;
|
||||
transformInstalled = true;
|
||||
(0, _compilationCache.installSourceMapSupport)();
|
||||
const originalResolveFilename = _module.default._resolveFilename;
|
||||
function resolveFilename(specifier, parent, ...rest) {
|
||||
if (parent) {
|
||||
const resolved = resolveHook(parent.filename, specifier);
|
||||
if (resolved !== undefined) specifier = resolved;
|
||||
}
|
||||
return originalResolveFilename.call(this, specifier, parent, ...rest);
|
||||
}
|
||||
_module.default._resolveFilename = resolveFilename;
|
||||
_utilsBundle.pirates.addHook((code, filename) => {
|
||||
if (!shouldTransform(filename)) return code;
|
||||
return transformHook(code, filename).code;
|
||||
}, {
|
||||
exts: ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.mts', '.cjs', '.cts']
|
||||
});
|
||||
}
|
||||
const collectCJSDependencies = (module, dependencies) => {
|
||||
module.children.forEach(child => {
|
||||
if (!(0, _compilationCache.belongsToNodeModules)(child.filename) && !dependencies.has(child.filename)) {
|
||||
dependencies.add(child.filename);
|
||||
collectCJSDependencies(child, dependencies);
|
||||
}
|
||||
});
|
||||
};
|
||||
function wrapFunctionWithLocation(func) {
|
||||
return (...args) => {
|
||||
const oldPrepareStackTrace = Error.prepareStackTrace;
|
||||
Error.prepareStackTrace = (error, stackFrames) => {
|
||||
const frame = _utilsBundle.sourceMapSupport.wrapCallSite(stackFrames[1]);
|
||||
const fileName = frame.getFileName();
|
||||
// Node error stacks for modules use file:// urls instead of paths.
|
||||
const file = fileName && fileName.startsWith('file://') ? _url.default.fileURLToPath(fileName) : fileName;
|
||||
return {
|
||||
file,
|
||||
line: frame.getLineNumber(),
|
||||
column: frame.getColumnNumber()
|
||||
};
|
||||
};
|
||||
const oldStackTraceLimit = Error.stackTraceLimit;
|
||||
Error.stackTraceLimit = 2;
|
||||
const obj = {};
|
||||
Error.captureStackTrace(obj);
|
||||
const location = obj.stack;
|
||||
Error.stackTraceLimit = oldStackTraceLimit;
|
||||
Error.prepareStackTrace = oldPrepareStackTrace;
|
||||
return func(location, ...args);
|
||||
};
|
||||
}
|
||||
function isRelativeSpecifier(specifier) {
|
||||
return specifier === '.' || specifier === '..' || specifier.startsWith('./') || specifier.startsWith('../');
|
||||
}
|
||||
371
node_modules/playwright/lib/util.js
generated
vendored
Normal file
371
node_modules/playwright/lib/util.js
generated
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.addSuffixToFilePath = addSuffixToFilePath;
|
||||
exports.callLogText = void 0;
|
||||
exports.createFileFiltersFromArguments = createFileFiltersFromArguments;
|
||||
exports.createFileMatcher = createFileMatcher;
|
||||
exports.createFileMatcherFromArguments = createFileMatcherFromArguments;
|
||||
exports.createTitleMatcher = createTitleMatcher;
|
||||
exports.debugTest = void 0;
|
||||
exports.errorWithFile = errorWithFile;
|
||||
exports.expectTypes = expectTypes;
|
||||
exports.fileIsModule = fileIsModule;
|
||||
exports.filterStackFile = filterStackFile;
|
||||
exports.filterStackTrace = filterStackTrace;
|
||||
exports.filteredStackTrace = filteredStackTrace;
|
||||
exports.forceRegExp = forceRegExp;
|
||||
exports.formatLocation = formatLocation;
|
||||
exports.getContainedPath = getContainedPath;
|
||||
exports.getPackageJsonPath = getPackageJsonPath;
|
||||
exports.mergeObjects = mergeObjects;
|
||||
exports.normalizeAndSaveAttachment = normalizeAndSaveAttachment;
|
||||
exports.relativeFilePath = relativeFilePath;
|
||||
exports.removeDirAndLogToConsole = removeDirAndLogToConsole;
|
||||
exports.resolveImportSpecifierAfterMapping = resolveImportSpecifierAfterMapping;
|
||||
exports.resolveReporterOutputPath = resolveReporterOutputPath;
|
||||
exports.sanitizeFilePathBeforeExtension = sanitizeFilePathBeforeExtension;
|
||||
exports.serializeError = serializeError;
|
||||
exports.trimLongString = trimLongString;
|
||||
exports.windowsFilesystemFriendlyLength = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _util = _interopRequireDefault(require("util"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _url = _interopRequireDefault(require("url"));
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const PLAYWRIGHT_TEST_PATH = _path.default.join(__dirname, '..');
|
||||
const PLAYWRIGHT_CORE_PATH = _path.default.dirname(require.resolve('playwright-core/package.json'));
|
||||
function filterStackTrace(e) {
|
||||
var _e$stack;
|
||||
const name = e.name ? e.name + ': ' : '';
|
||||
const cause = e.cause instanceof Error ? filterStackTrace(e.cause) : undefined;
|
||||
if (process.env.PWDEBUGIMPL) return {
|
||||
message: name + e.message,
|
||||
stack: e.stack || '',
|
||||
cause
|
||||
};
|
||||
const stackLines = (0, _utils.stringifyStackFrames)(filteredStackTrace(((_e$stack = e.stack) === null || _e$stack === void 0 ? void 0 : _e$stack.split('\n')) || []));
|
||||
return {
|
||||
message: name + e.message,
|
||||
stack: `${name}${e.message}${stackLines.map(line => '\n' + line).join('')}`,
|
||||
cause
|
||||
};
|
||||
}
|
||||
function filterStackFile(file) {
|
||||
if (!process.env.PWDEBUGIMPL && file.startsWith(PLAYWRIGHT_TEST_PATH)) return false;
|
||||
if (!process.env.PWDEBUGIMPL && file.startsWith(PLAYWRIGHT_CORE_PATH)) return false;
|
||||
return true;
|
||||
}
|
||||
function filteredStackTrace(rawStack) {
|
||||
const frames = [];
|
||||
for (const line of rawStack) {
|
||||
const frame = (0, _utilsBundle.parseStackTraceLine)(line);
|
||||
if (!frame || !frame.file) continue;
|
||||
if (!filterStackFile(frame.file)) continue;
|
||||
frames.push(frame);
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
function serializeError(error) {
|
||||
if (error instanceof Error) return filterStackTrace(error);
|
||||
return {
|
||||
value: _util.default.inspect(error)
|
||||
};
|
||||
}
|
||||
function createFileFiltersFromArguments(args) {
|
||||
return args.map(arg => {
|
||||
const match = /^(.*?):(\d+):?(\d+)?$/.exec(arg);
|
||||
return {
|
||||
re: forceRegExp(match ? match[1] : arg),
|
||||
line: match ? parseInt(match[2], 10) : null,
|
||||
column: match !== null && match !== void 0 && match[3] ? parseInt(match[3], 10) : null
|
||||
};
|
||||
});
|
||||
}
|
||||
function createFileMatcherFromArguments(args) {
|
||||
const filters = createFileFiltersFromArguments(args);
|
||||
return createFileMatcher(filters.map(filter => filter.re || filter.exact || ''));
|
||||
}
|
||||
function createFileMatcher(patterns) {
|
||||
const reList = [];
|
||||
const filePatterns = [];
|
||||
for (const pattern of Array.isArray(patterns) ? patterns : [patterns]) {
|
||||
if ((0, _utils.isRegExp)(pattern)) {
|
||||
reList.push(pattern);
|
||||
} else {
|
||||
if (!pattern.startsWith('**/')) filePatterns.push('**/' + pattern);else filePatterns.push(pattern);
|
||||
}
|
||||
}
|
||||
return filePath => {
|
||||
for (const re of reList) {
|
||||
re.lastIndex = 0;
|
||||
if (re.test(filePath)) return true;
|
||||
}
|
||||
// Windows might still receive unix style paths from Cygwin or Git Bash.
|
||||
// Check against the file url as well.
|
||||
if (_path.default.sep === '\\') {
|
||||
const fileURL = _url.default.pathToFileURL(filePath).href;
|
||||
for (const re of reList) {
|
||||
re.lastIndex = 0;
|
||||
if (re.test(fileURL)) return true;
|
||||
}
|
||||
}
|
||||
for (const pattern of filePatterns) {
|
||||
if ((0, _utilsBundle.minimatch)(filePath, pattern, {
|
||||
nocase: true,
|
||||
dot: true
|
||||
})) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
function createTitleMatcher(patterns) {
|
||||
const reList = Array.isArray(patterns) ? patterns : [patterns];
|
||||
return value => {
|
||||
for (const re of reList) {
|
||||
re.lastIndex = 0;
|
||||
if (re.test(value)) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
function mergeObjects(a, b, c) {
|
||||
const result = {
|
||||
...a
|
||||
};
|
||||
for (const x of [b, c].filter(Boolean)) {
|
||||
for (const [name, value] of Object.entries(x)) {
|
||||
if (!Object.is(value, undefined)) result[name] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function forceRegExp(pattern) {
|
||||
const match = pattern.match(/^\/(.*)\/([gi]*)$/);
|
||||
if (match) return new RegExp(match[1], match[2]);
|
||||
return new RegExp(pattern, 'gi');
|
||||
}
|
||||
function relativeFilePath(file) {
|
||||
if (!_path.default.isAbsolute(file)) return file;
|
||||
return _path.default.relative(process.cwd(), file);
|
||||
}
|
||||
function formatLocation(location) {
|
||||
return relativeFilePath(location.file) + ':' + location.line + ':' + location.column;
|
||||
}
|
||||
function errorWithFile(file, message) {
|
||||
return new Error(`${relativeFilePath(file)}: ${message}`);
|
||||
}
|
||||
function expectTypes(receiver, types, matcherName) {
|
||||
if (typeof receiver !== 'object' || !types.includes(receiver.constructor.name)) {
|
||||
const commaSeparated = types.slice();
|
||||
const lastType = commaSeparated.pop();
|
||||
const typesString = commaSeparated.length ? commaSeparated.join(', ') + ' or ' + lastType : lastType;
|
||||
throw new Error(`${matcherName} can be only used with ${typesString} object${types.length > 1 ? 's' : ''}`);
|
||||
}
|
||||
}
|
||||
const windowsFilesystemFriendlyLength = exports.windowsFilesystemFriendlyLength = 60;
|
||||
function trimLongString(s, length = 100) {
|
||||
if (s.length <= length) return s;
|
||||
const hash = (0, _utils.calculateSha1)(s);
|
||||
const middle = `-${hash.substring(0, 5)}-`;
|
||||
const start = Math.floor((length - middle.length) / 2);
|
||||
const end = length - middle.length - start;
|
||||
return s.substring(0, start) + middle + s.slice(-end);
|
||||
}
|
||||
function addSuffixToFilePath(filePath, suffix) {
|
||||
const ext = _path.default.extname(filePath);
|
||||
const base = filePath.substring(0, filePath.length - ext.length);
|
||||
return base + suffix + ext;
|
||||
}
|
||||
function sanitizeFilePathBeforeExtension(filePath) {
|
||||
const ext = _path.default.extname(filePath);
|
||||
const base = filePath.substring(0, filePath.length - ext.length);
|
||||
return (0, _utils.sanitizeForFilePath)(base) + ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns absolute path contained within parent directory.
|
||||
*/
|
||||
function getContainedPath(parentPath, subPath = '') {
|
||||
const resolvedPath = _path.default.resolve(parentPath, subPath);
|
||||
if (resolvedPath === parentPath || resolvedPath.startsWith(parentPath + _path.default.sep)) return resolvedPath;
|
||||
return null;
|
||||
}
|
||||
const debugTest = exports.debugTest = (0, _utilsBundle.debug)('pw:test');
|
||||
const callLogText = exports.callLogText = _utils.formatCallLog;
|
||||
const folderToPackageJsonPath = new Map();
|
||||
function getPackageJsonPath(folderPath) {
|
||||
const cached = folderToPackageJsonPath.get(folderPath);
|
||||
if (cached !== undefined) return cached;
|
||||
const packageJsonPath = _path.default.join(folderPath, 'package.json');
|
||||
if (_fs.default.existsSync(packageJsonPath)) {
|
||||
folderToPackageJsonPath.set(folderPath, packageJsonPath);
|
||||
return packageJsonPath;
|
||||
}
|
||||
const parentFolder = _path.default.dirname(folderPath);
|
||||
if (folderPath === parentFolder) {
|
||||
folderToPackageJsonPath.set(folderPath, '');
|
||||
return '';
|
||||
}
|
||||
const result = getPackageJsonPath(parentFolder);
|
||||
folderToPackageJsonPath.set(folderPath, result);
|
||||
return result;
|
||||
}
|
||||
function resolveReporterOutputPath(defaultValue, configDir, configValue) {
|
||||
if (configValue) return _path.default.resolve(configDir, configValue);
|
||||
let basePath = getPackageJsonPath(configDir);
|
||||
basePath = basePath ? _path.default.dirname(basePath) : process.cwd();
|
||||
return _path.default.resolve(basePath, defaultValue);
|
||||
}
|
||||
async function normalizeAndSaveAttachment(outputPath, name, options = {}) {
|
||||
if (options.path === undefined && options.body === undefined) return {
|
||||
name,
|
||||
contentType: 'text/plain'
|
||||
};
|
||||
if ((options.path !== undefined ? 1 : 0) + (options.body !== undefined ? 1 : 0) !== 1) throw new Error(`Exactly one of "path" and "body" must be specified`);
|
||||
if (options.path !== undefined) {
|
||||
var _options$contentType;
|
||||
const hash = (0, _utils.calculateSha1)(options.path);
|
||||
if (!(0, _utils.isString)(name)) throw new Error('"name" should be string.');
|
||||
const sanitizedNamePrefix = (0, _utils.sanitizeForFilePath)(name) + '-';
|
||||
const dest = _path.default.join(outputPath, 'attachments', sanitizedNamePrefix + hash + _path.default.extname(options.path));
|
||||
await _fs.default.promises.mkdir(_path.default.dirname(dest), {
|
||||
recursive: true
|
||||
});
|
||||
await _fs.default.promises.copyFile(options.path, dest);
|
||||
const contentType = (_options$contentType = options.contentType) !== null && _options$contentType !== void 0 ? _options$contentType : _utilsBundle.mime.getType(_path.default.basename(options.path)) || 'application/octet-stream';
|
||||
return {
|
||||
name,
|
||||
contentType,
|
||||
path: dest
|
||||
};
|
||||
} else {
|
||||
var _options$contentType2;
|
||||
const contentType = (_options$contentType2 = options.contentType) !== null && _options$contentType2 !== void 0 ? _options$contentType2 : typeof options.body === 'string' ? 'text/plain' : 'application/octet-stream';
|
||||
return {
|
||||
name,
|
||||
contentType,
|
||||
body: typeof options.body === 'string' ? Buffer.from(options.body) : options.body
|
||||
};
|
||||
}
|
||||
}
|
||||
function fileIsModule(file) {
|
||||
if (file.endsWith('.mjs') || file.endsWith('.mts')) return true;
|
||||
if (file.endsWith('.cjs') || file.endsWith('.cts')) return false;
|
||||
const folder = _path.default.dirname(file);
|
||||
return folderIsModule(folder);
|
||||
}
|
||||
function folderIsModule(folder) {
|
||||
const packageJsonPath = getPackageJsonPath(folder);
|
||||
if (!packageJsonPath) return false;
|
||||
// Rely on `require` internal caching logic.
|
||||
return require(packageJsonPath).type === 'module';
|
||||
}
|
||||
const packageJsonMainFieldCache = new Map();
|
||||
function getMainFieldFromPackageJson(packageJsonPath) {
|
||||
if (!packageJsonMainFieldCache.has(packageJsonPath)) {
|
||||
let mainField;
|
||||
try {
|
||||
mainField = JSON.parse(_fs.default.readFileSync(packageJsonPath, 'utf8')).main;
|
||||
} catch {}
|
||||
packageJsonMainFieldCache.set(packageJsonPath, mainField);
|
||||
}
|
||||
return packageJsonMainFieldCache.get(packageJsonPath);
|
||||
}
|
||||
|
||||
// This method performs "file extension subsitution" to find the ts, js or similar source file
|
||||
// based on the import specifier, which might or might not have an extension. See TypeScript docs:
|
||||
// https://www.typescriptlang.org/docs/handbook/modules/reference.html#file-extension-substitution.
|
||||
const kExtLookups = new Map([['.js', ['.jsx', '.ts', '.tsx']], ['.jsx', ['.tsx']], ['.cjs', ['.cts']], ['.mjs', ['.mts']], ['', ['.js', '.ts', '.jsx', '.tsx', '.cjs', '.mjs', '.cts', '.mts']]]);
|
||||
function resolveImportSpecifierExtension(resolved) {
|
||||
if (fileExists(resolved)) return resolved;
|
||||
for (const [ext, others] of kExtLookups) {
|
||||
if (!resolved.endsWith(ext)) continue;
|
||||
for (const other of others) {
|
||||
const modified = resolved.substring(0, resolved.length - ext.length) + other;
|
||||
if (fileExists(modified)) return modified;
|
||||
}
|
||||
break; // Do not try '' when a more specific extension like '.jsx' matched.
|
||||
}
|
||||
}
|
||||
|
||||
// This method resolves directory imports and performs "file extension subsitution".
|
||||
// It is intended to be called after the path mapping resolution.
|
||||
//
|
||||
// Directory imports follow the --moduleResolution=bundler strategy from tsc.
|
||||
// https://www.typescriptlang.org/docs/handbook/modules/reference.html#directory-modules-index-file-resolution
|
||||
// https://www.typescriptlang.org/docs/handbook/modules/reference.html#bundler
|
||||
//
|
||||
// See also Node.js "folder as module" behavior:
|
||||
// https://nodejs.org/dist/latest-v20.x/docs/api/modules.html#folders-as-modules.
|
||||
function resolveImportSpecifierAfterMapping(resolved, afterPathMapping) {
|
||||
const resolvedFile = resolveImportSpecifierExtension(resolved);
|
||||
if (resolvedFile) return resolvedFile;
|
||||
if (dirExists(resolved)) {
|
||||
const packageJsonPath = _path.default.join(resolved, 'package.json');
|
||||
if (afterPathMapping) {
|
||||
// Most notably, the module resolution algorithm is not performed after the path mapping.
|
||||
// This means no node_modules lookup or package.json#exports.
|
||||
//
|
||||
// Only the "folder as module" Node.js behavior is respected:
|
||||
// - consult `package.json#main`;
|
||||
// - look for `index.js` or similar.
|
||||
const mainField = getMainFieldFromPackageJson(packageJsonPath);
|
||||
const mainFieldResolved = mainField ? resolveImportSpecifierExtension(_path.default.resolve(resolved, mainField)) : undefined;
|
||||
return mainFieldResolved || resolveImportSpecifierExtension(_path.default.join(resolved, 'index'));
|
||||
}
|
||||
|
||||
// If we import a package, let Node.js figure out the correct import based on package.json.
|
||||
// This also covers the "main" field for "folder as module".
|
||||
if (fileExists(packageJsonPath)) return resolved;
|
||||
|
||||
// Implement the "folder as module" Node.js behavior.
|
||||
// Note that we do not delegate to Node.js, because we support this for ESM as well,
|
||||
// following the TypeScript "bundler" mode.
|
||||
const dirImport = _path.default.join(resolved, 'index');
|
||||
return resolveImportSpecifierExtension(dirImport);
|
||||
}
|
||||
}
|
||||
function fileExists(resolved) {
|
||||
var _fs$statSync;
|
||||
return (_fs$statSync = _fs.default.statSync(resolved, {
|
||||
throwIfNoEntry: false
|
||||
})) === null || _fs$statSync === void 0 ? void 0 : _fs$statSync.isFile();
|
||||
}
|
||||
function dirExists(resolved) {
|
||||
var _fs$statSync2;
|
||||
return (_fs$statSync2 = _fs.default.statSync(resolved, {
|
||||
throwIfNoEntry: false
|
||||
})) === null || _fs$statSync2 === void 0 ? void 0 : _fs$statSync2.isDirectory();
|
||||
}
|
||||
async function removeDirAndLogToConsole(dir) {
|
||||
try {
|
||||
if (!_fs.default.existsSync(dir)) return;
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Removing ${await _fs.default.promises.realpath(dir)}`);
|
||||
await _fs.default.promises.rm(dir, {
|
||||
recursive: true,
|
||||
force: true
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
29
node_modules/playwright/lib/utilsBundle.js
generated
vendored
Normal file
29
node_modules/playwright/lib/utilsBundle.js
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.stoppable = exports.sourceMapSupport = exports.pirates = exports.json5 = exports.getEastAsianWidth = exports.enquirer = exports.chokidar = void 0;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const json5 = exports.json5 = require('./utilsBundleImpl').json5;
|
||||
const pirates = exports.pirates = require('./utilsBundleImpl').pirates;
|
||||
const sourceMapSupport = exports.sourceMapSupport = require('./utilsBundleImpl').sourceMapSupport;
|
||||
const stoppable = exports.stoppable = require('./utilsBundleImpl').stoppable;
|
||||
const enquirer = exports.enquirer = require('./utilsBundleImpl').enquirer;
|
||||
const chokidar = exports.chokidar = require('./utilsBundleImpl').chokidar;
|
||||
const getEastAsianWidth = exports.getEastAsianWidth = require('./utilsBundleImpl').getEastAsianWidth;
|
||||
102
node_modules/playwright/lib/utilsBundleImpl.js
generated
vendored
Normal file
102
node_modules/playwright/lib/utilsBundleImpl.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
279
node_modules/playwright/lib/worker/fixtureRunner.js
generated
vendored
Normal file
279
node_modules/playwright/lib/worker/fixtureRunner.js
generated
vendored
Normal file
@@ -0,0 +1,279 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.FixtureRunner = void 0;
|
||||
var _util = require("../util");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _fixtures = require("../common/fixtures");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Fixture {
|
||||
constructor(runner, registration) {
|
||||
this.runner = void 0;
|
||||
this.registration = void 0;
|
||||
this.value = void 0;
|
||||
this.failed = false;
|
||||
this._useFuncFinished = void 0;
|
||||
this._selfTeardownComplete = void 0;
|
||||
this._setupDescription = void 0;
|
||||
this._teardownDescription = void 0;
|
||||
this._stepInfo = void 0;
|
||||
this._deps = new Set();
|
||||
this._usages = new Set();
|
||||
this.runner = runner;
|
||||
this.registration = registration;
|
||||
this.value = null;
|
||||
const shouldGenerateStep = !this.registration.box && !this.registration.option;
|
||||
const isUserFixture = this.registration.location && (0, _util.filterStackFile)(this.registration.location.file);
|
||||
const title = this.registration.customTitle || this.registration.name;
|
||||
const location = isUserFixture ? this.registration.location : undefined;
|
||||
this._stepInfo = shouldGenerateStep ? {
|
||||
category: 'fixture',
|
||||
location
|
||||
} : undefined;
|
||||
this._setupDescription = {
|
||||
title,
|
||||
phase: 'setup',
|
||||
location,
|
||||
slot: this.registration.timeout === undefined ? undefined : {
|
||||
timeout: this.registration.timeout,
|
||||
elapsed: 0
|
||||
}
|
||||
};
|
||||
this._teardownDescription = {
|
||||
...this._setupDescription,
|
||||
phase: 'teardown'
|
||||
};
|
||||
}
|
||||
async setup(testInfo, runnable) {
|
||||
this.runner.instanceForId.set(this.registration.id, this);
|
||||
if (typeof this.registration.fn !== 'function') {
|
||||
this.value = this.registration.fn;
|
||||
return;
|
||||
}
|
||||
await testInfo._runAsStage({
|
||||
title: `fixture: ${this.registration.name}`,
|
||||
runnable: {
|
||||
...runnable,
|
||||
fixture: this._setupDescription
|
||||
},
|
||||
stepInfo: this._stepInfo
|
||||
}, async () => {
|
||||
await this._setupInternal(testInfo);
|
||||
});
|
||||
}
|
||||
async _setupInternal(testInfo) {
|
||||
const params = {};
|
||||
for (const name of this.registration.deps) {
|
||||
const registration = this.runner.pool.resolve(name, this.registration);
|
||||
const dep = this.runner.instanceForId.get(registration.id);
|
||||
if (!dep) {
|
||||
this.failed = true;
|
||||
return;
|
||||
}
|
||||
// Fixture teardown is root => leaves, when we need to teardown a fixture,
|
||||
// it recursively tears down its usages first.
|
||||
dep._usages.add(this);
|
||||
// Don't forget to decrement all usages when fixture goes.
|
||||
// Otherwise worker-scope fixtures will retain test-scope fixtures forever.
|
||||
this._deps.add(dep);
|
||||
params[name] = dep.value;
|
||||
if (dep.failed) {
|
||||
this.failed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
let called = false;
|
||||
const useFuncStarted = new _utils.ManualPromise();
|
||||
const useFunc = async value => {
|
||||
if (called) throw new Error(`Cannot provide fixture value for the second time`);
|
||||
called = true;
|
||||
this.value = value;
|
||||
this._useFuncFinished = new _utils.ManualPromise();
|
||||
useFuncStarted.resolve();
|
||||
await this._useFuncFinished;
|
||||
};
|
||||
const workerInfo = {
|
||||
config: testInfo.config,
|
||||
parallelIndex: testInfo.parallelIndex,
|
||||
workerIndex: testInfo.workerIndex,
|
||||
project: testInfo.project
|
||||
};
|
||||
const info = this.registration.scope === 'worker' ? workerInfo : testInfo;
|
||||
this._selfTeardownComplete = (async () => {
|
||||
try {
|
||||
await this.registration.fn(params, useFunc, info);
|
||||
} catch (error) {
|
||||
this.failed = true;
|
||||
if (!useFuncStarted.isDone()) useFuncStarted.reject(error);else throw error;
|
||||
}
|
||||
})();
|
||||
await useFuncStarted;
|
||||
}
|
||||
async teardown(testInfo, runnable) {
|
||||
try {
|
||||
const fixtureRunnable = {
|
||||
...runnable,
|
||||
fixture: this._teardownDescription
|
||||
};
|
||||
// Do not even start the teardown for a fixture that does not have any
|
||||
// time remaining in the time slot. This avoids cascading timeouts.
|
||||
if (!testInfo._timeoutManager.isTimeExhaustedFor(fixtureRunnable)) {
|
||||
await testInfo._runAsStage({
|
||||
title: `fixture: ${this.registration.name}`,
|
||||
runnable: fixtureRunnable,
|
||||
stepInfo: this._stepInfo
|
||||
}, async () => {
|
||||
await this._teardownInternal();
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
// To preserve fixtures integrity, forcefully cleanup fixtures
|
||||
// that cannnot teardown due to a timeout or an error.
|
||||
for (const dep of this._deps) dep._usages.delete(this);
|
||||
this.runner.instanceForId.delete(this.registration.id);
|
||||
}
|
||||
}
|
||||
async _teardownInternal() {
|
||||
if (typeof this.registration.fn !== 'function') return;
|
||||
if (this._usages.size !== 0) {
|
||||
// TODO: replace with assert.
|
||||
console.error('Internal error: fixture integrity at', this._teardownDescription.title); // eslint-disable-line no-console
|
||||
this._usages.clear();
|
||||
}
|
||||
if (this._useFuncFinished) {
|
||||
this._useFuncFinished.resolve();
|
||||
this._useFuncFinished = undefined;
|
||||
await this._selfTeardownComplete;
|
||||
}
|
||||
}
|
||||
_collectFixturesInTeardownOrder(scope, collector) {
|
||||
if (this.registration.scope !== scope) return;
|
||||
for (const fixture of this._usages) fixture._collectFixturesInTeardownOrder(scope, collector);
|
||||
collector.add(this);
|
||||
}
|
||||
}
|
||||
class FixtureRunner {
|
||||
constructor() {
|
||||
this.testScopeClean = true;
|
||||
this.pool = void 0;
|
||||
this.instanceForId = new Map();
|
||||
}
|
||||
setPool(pool) {
|
||||
if (!this.testScopeClean) throw new Error('Did not teardown test scope');
|
||||
if (this.pool && pool.digest !== this.pool.digest) {
|
||||
throw new Error([`Playwright detected inconsistent test.use() options.`, `Most common mistakes that lead to this issue:`, ` - Calling test.use() outside of the test file, for example in a common helper.`, ` - One test file imports from another test file.`].join('\n'));
|
||||
}
|
||||
this.pool = pool;
|
||||
}
|
||||
_collectFixturesInSetupOrder(registration, collector) {
|
||||
if (collector.has(registration)) return;
|
||||
for (const name of registration.deps) {
|
||||
const dep = this.pool.resolve(name, registration);
|
||||
this._collectFixturesInSetupOrder(dep, collector);
|
||||
}
|
||||
collector.add(registration);
|
||||
}
|
||||
async teardownScope(scope, testInfo, runnable) {
|
||||
// Teardown fixtures in the reverse order.
|
||||
const fixtures = Array.from(this.instanceForId.values()).reverse();
|
||||
const collector = new Set();
|
||||
for (const fixture of fixtures) fixture._collectFixturesInTeardownOrder(scope, collector);
|
||||
let firstError;
|
||||
for (const fixture of collector) {
|
||||
try {
|
||||
await fixture.teardown(testInfo, runnable);
|
||||
} catch (error) {
|
||||
var _firstError;
|
||||
firstError = (_firstError = firstError) !== null && _firstError !== void 0 ? _firstError : error;
|
||||
}
|
||||
}
|
||||
if (scope === 'test') this.testScopeClean = true;
|
||||
if (firstError) throw firstError;
|
||||
}
|
||||
async resolveParametersForFunction(fn, testInfo, autoFixtures, runnable) {
|
||||
const collector = new Set();
|
||||
|
||||
// Collect automatic fixtures.
|
||||
const auto = [];
|
||||
for (const registration of this.pool.autoFixtures()) {
|
||||
let shouldRun = true;
|
||||
if (autoFixtures === 'all-hooks-only') shouldRun = registration.scope === 'worker' || registration.auto === 'all-hooks-included';else if (autoFixtures === 'worker') shouldRun = registration.scope === 'worker';
|
||||
if (shouldRun) auto.push(registration);
|
||||
}
|
||||
auto.sort((r1, r2) => (r1.scope === 'worker' ? 0 : 1) - (r2.scope === 'worker' ? 0 : 1));
|
||||
for (const registration of auto) this._collectFixturesInSetupOrder(registration, collector);
|
||||
|
||||
// Collect used fixtures.
|
||||
const names = getRequiredFixtureNames(fn);
|
||||
for (const name of names) this._collectFixturesInSetupOrder(this.pool.resolve(name), collector);
|
||||
|
||||
// Setup fixtures.
|
||||
for (const registration of collector) await this._setupFixtureForRegistration(registration, testInfo, runnable);
|
||||
|
||||
// Create params object.
|
||||
const params = {};
|
||||
for (const name of names) {
|
||||
const registration = this.pool.resolve(name);
|
||||
const fixture = this.instanceForId.get(registration.id);
|
||||
if (!fixture || fixture.failed) return null;
|
||||
params[name] = fixture.value;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
async resolveParametersAndRunFunction(fn, testInfo, autoFixtures, runnable) {
|
||||
const params = await this.resolveParametersForFunction(fn, testInfo, autoFixtures, runnable);
|
||||
if (params === null) {
|
||||
// Do not run the function when fixture setup has already failed.
|
||||
return null;
|
||||
}
|
||||
await testInfo._runAsStage({
|
||||
title: 'run function',
|
||||
runnable
|
||||
}, async () => {
|
||||
await fn(params, testInfo);
|
||||
});
|
||||
}
|
||||
async _setupFixtureForRegistration(registration, testInfo, runnable) {
|
||||
if (registration.scope === 'test') this.testScopeClean = false;
|
||||
let fixture = this.instanceForId.get(registration.id);
|
||||
if (fixture) return fixture;
|
||||
fixture = new Fixture(this, registration);
|
||||
await fixture.setup(testInfo, runnable);
|
||||
return fixture;
|
||||
}
|
||||
dependsOnWorkerFixturesOnly(fn, location) {
|
||||
const names = getRequiredFixtureNames(fn, location);
|
||||
for (const name of names) {
|
||||
const registration = this.pool.resolve(name);
|
||||
if (registration.scope !== 'worker') return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
exports.FixtureRunner = FixtureRunner;
|
||||
function getRequiredFixtureNames(fn, location) {
|
||||
return (0, _fixtures.fixtureParameterNames)(fn, location !== null && location !== void 0 ? location : {
|
||||
file: '<unknown>',
|
||||
line: 1,
|
||||
column: 1
|
||||
}, e => {
|
||||
throw new Error(`${(0, _util.formatLocation)(e.location)}: ${e.message}`);
|
||||
});
|
||||
}
|
||||
398
node_modules/playwright/lib/worker/testInfo.js
generated
vendored
Normal file
398
node_modules/playwright/lib/worker/testInfo.js
generated
vendored
Normal file
@@ -0,0 +1,398 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.TestInfoImpl = exports.SkipError = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _timeoutManager = require("./timeoutManager");
|
||||
var _util = require("../util");
|
||||
var _testTracing = require("./testTracing");
|
||||
var _util2 = require("./util");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class TestInfoImpl {
|
||||
get error() {
|
||||
return this.errors[0];
|
||||
}
|
||||
set error(e) {
|
||||
if (e === undefined) throw new Error('Cannot assign testInfo.error undefined value!');
|
||||
this.errors[0] = e;
|
||||
}
|
||||
get timeout() {
|
||||
return this._timeoutManager.defaultSlot().timeout;
|
||||
}
|
||||
set timeout(timeout) {
|
||||
// Ignored.
|
||||
}
|
||||
_deadlineForMatcher(timeout) {
|
||||
const startTime = (0, _utils.monotonicTime)();
|
||||
const matcherDeadline = timeout ? startTime + timeout : _timeoutManager.kMaxDeadline;
|
||||
const testDeadline = this._timeoutManager.currentSlotDeadline() - 250;
|
||||
const matcherMessage = `Timeout ${timeout}ms exceeded while waiting on the predicate`;
|
||||
const testMessage = `Test timeout of ${this.timeout}ms exceeded`;
|
||||
return {
|
||||
deadline: Math.min(testDeadline, matcherDeadline),
|
||||
timeoutMessage: testDeadline < matcherDeadline ? testMessage : matcherMessage
|
||||
};
|
||||
}
|
||||
static _defaultDeadlineForMatcher(timeout) {
|
||||
return {
|
||||
deadline: timeout ? (0, _utils.monotonicTime)() + timeout : 0,
|
||||
timeoutMessage: `Timeout ${timeout}ms exceeded while waiting on the predicate`
|
||||
};
|
||||
}
|
||||
constructor(configInternal, projectInternal, workerParams, test, retry, onStepBegin, onStepEnd, onAttach) {
|
||||
var _test$id, _test$_requireFile, _test$title, _test$titlePath, _test$location$file, _test$location$line, _test$location$column, _test$tags, _test$fn, _test$expectedStatus;
|
||||
this._onStepBegin = void 0;
|
||||
this._onStepEnd = void 0;
|
||||
this._onAttach = void 0;
|
||||
this._timeoutManager = void 0;
|
||||
this._startTime = void 0;
|
||||
this._startWallTime = void 0;
|
||||
this._tracing = void 0;
|
||||
this._wasInterrupted = false;
|
||||
this._lastStepId = 0;
|
||||
this._requireFile = void 0;
|
||||
this._projectInternal = void 0;
|
||||
this._configInternal = void 0;
|
||||
this._steps = [];
|
||||
this._onDidFinishTestFunction = void 0;
|
||||
this._hasNonRetriableError = false;
|
||||
this._hasUnhandledError = false;
|
||||
this._allowSkips = false;
|
||||
// ------------ TestInfo fields ------------
|
||||
this.testId = void 0;
|
||||
this.repeatEachIndex = void 0;
|
||||
this.retry = void 0;
|
||||
this.workerIndex = void 0;
|
||||
this.parallelIndex = void 0;
|
||||
this.project = void 0;
|
||||
this.config = void 0;
|
||||
this.title = void 0;
|
||||
this.titlePath = void 0;
|
||||
this.file = void 0;
|
||||
this.line = void 0;
|
||||
this.tags = void 0;
|
||||
this.column = void 0;
|
||||
this.fn = void 0;
|
||||
this.expectedStatus = void 0;
|
||||
this.duration = 0;
|
||||
this.annotations = [];
|
||||
this.attachments = [];
|
||||
this.status = 'passed';
|
||||
this.snapshotSuffix = '';
|
||||
this.outputDir = void 0;
|
||||
this.snapshotDir = void 0;
|
||||
this.errors = [];
|
||||
this._attachmentsPush = void 0;
|
||||
this.testId = (_test$id = test === null || test === void 0 ? void 0 : test.id) !== null && _test$id !== void 0 ? _test$id : '';
|
||||
this._onStepBegin = onStepBegin;
|
||||
this._onStepEnd = onStepEnd;
|
||||
this._onAttach = onAttach;
|
||||
this._startTime = (0, _utils.monotonicTime)();
|
||||
this._startWallTime = Date.now();
|
||||
this._requireFile = (_test$_requireFile = test === null || test === void 0 ? void 0 : test._requireFile) !== null && _test$_requireFile !== void 0 ? _test$_requireFile : '';
|
||||
this.repeatEachIndex = workerParams.repeatEachIndex;
|
||||
this.retry = retry;
|
||||
this.workerIndex = workerParams.workerIndex;
|
||||
this.parallelIndex = workerParams.parallelIndex;
|
||||
this._projectInternal = projectInternal;
|
||||
this.project = projectInternal.project;
|
||||
this._configInternal = configInternal;
|
||||
this.config = configInternal.config;
|
||||
this.title = (_test$title = test === null || test === void 0 ? void 0 : test.title) !== null && _test$title !== void 0 ? _test$title : '';
|
||||
this.titlePath = (_test$titlePath = test === null || test === void 0 ? void 0 : test.titlePath()) !== null && _test$titlePath !== void 0 ? _test$titlePath : [];
|
||||
this.file = (_test$location$file = test === null || test === void 0 ? void 0 : test.location.file) !== null && _test$location$file !== void 0 ? _test$location$file : '';
|
||||
this.line = (_test$location$line = test === null || test === void 0 ? void 0 : test.location.line) !== null && _test$location$line !== void 0 ? _test$location$line : 0;
|
||||
this.column = (_test$location$column = test === null || test === void 0 ? void 0 : test.location.column) !== null && _test$location$column !== void 0 ? _test$location$column : 0;
|
||||
this.tags = (_test$tags = test === null || test === void 0 ? void 0 : test.tags) !== null && _test$tags !== void 0 ? _test$tags : [];
|
||||
this.fn = (_test$fn = test === null || test === void 0 ? void 0 : test.fn) !== null && _test$fn !== void 0 ? _test$fn : () => {};
|
||||
this.expectedStatus = (_test$expectedStatus = test === null || test === void 0 ? void 0 : test.expectedStatus) !== null && _test$expectedStatus !== void 0 ? _test$expectedStatus : 'skipped';
|
||||
this._timeoutManager = new _timeoutManager.TimeoutManager(this.project.timeout);
|
||||
if (configInternal.configCLIOverrides.debug) this._setDebugMode();
|
||||
this.outputDir = (() => {
|
||||
const relativeTestFilePath = _path.default.relative(this.project.testDir, this._requireFile.replace(/\.(spec|test)\.(js|ts|jsx|tsx|mjs|mts|cjs|cts)$/, ''));
|
||||
const sanitizedRelativePath = relativeTestFilePath.replace(process.platform === 'win32' ? new RegExp('\\\\', 'g') : new RegExp('/', 'g'), '-');
|
||||
const fullTitleWithoutSpec = this.titlePath.slice(1).join(' ');
|
||||
let testOutputDir = (0, _util.trimLongString)(sanitizedRelativePath + '-' + (0, _utils.sanitizeForFilePath)(fullTitleWithoutSpec), _util.windowsFilesystemFriendlyLength);
|
||||
if (projectInternal.id) testOutputDir += '-' + (0, _utils.sanitizeForFilePath)(projectInternal.id);
|
||||
if (this.retry) testOutputDir += '-retry' + this.retry;
|
||||
if (this.repeatEachIndex) testOutputDir += '-repeat' + this.repeatEachIndex;
|
||||
return _path.default.join(this.project.outputDir, testOutputDir);
|
||||
})();
|
||||
this.snapshotDir = (() => {
|
||||
const relativeTestFilePath = _path.default.relative(this.project.testDir, this._requireFile);
|
||||
return _path.default.join(this.project.snapshotDir, relativeTestFilePath + '-snapshots');
|
||||
})();
|
||||
this._attachmentsPush = this.attachments.push.bind(this.attachments);
|
||||
this.attachments.push = (...attachments) => {
|
||||
for (const a of attachments) this._attach(a.name, a);
|
||||
return this.attachments.length;
|
||||
};
|
||||
this._tracing = new _testTracing.TestTracing(this, workerParams.artifactsDir);
|
||||
}
|
||||
_modifier(type, modifierArgs) {
|
||||
if (typeof modifierArgs[1] === 'function') {
|
||||
throw new Error(['It looks like you are calling test.skip() inside the test and pass a callback.', 'Pass a condition instead and optional description instead:', `test('my test', async ({ page, isMobile }) => {`, ` test.skip(isMobile, 'This test is not applicable on mobile');`, `});`].join('\n'));
|
||||
}
|
||||
if (modifierArgs.length >= 1 && !modifierArgs[0]) return;
|
||||
const description = modifierArgs[1];
|
||||
this.annotations.push({
|
||||
type,
|
||||
description
|
||||
});
|
||||
if (type === 'slow') {
|
||||
this._timeoutManager.slow();
|
||||
} else if (type === 'skip' || type === 'fixme') {
|
||||
this.expectedStatus = 'skipped';
|
||||
throw new SkipError('Test is skipped: ' + (description || ''));
|
||||
} else if (type === 'fail') {
|
||||
if (this.expectedStatus !== 'skipped') this.expectedStatus = 'failed';
|
||||
}
|
||||
}
|
||||
_findLastStageStep(steps) {
|
||||
// Find the deepest step that is marked as isStage and has not finished yet.
|
||||
for (let i = steps.length - 1; i >= 0; i--) {
|
||||
const child = this._findLastStageStep(steps[i].steps);
|
||||
if (child) return child;
|
||||
if (steps[i].isStage && !steps[i].endWallTime) return steps[i];
|
||||
}
|
||||
}
|
||||
_addStep(data, parentStep) {
|
||||
var _parentStep, _parentStep2;
|
||||
const stepId = `${data.category}@${++this._lastStepId}`;
|
||||
if (data.isStage) {
|
||||
// Predefined stages form a fixed hierarchy - use the current one as parent.
|
||||
parentStep = this._findLastStageStep(this._steps);
|
||||
} else {
|
||||
if (!parentStep) parentStep = _utils.zones.zoneData('stepZone');
|
||||
if (!parentStep) {
|
||||
// If no parent step on stack, assume the current stage as parent.
|
||||
parentStep = this._findLastStageStep(this._steps);
|
||||
}
|
||||
}
|
||||
const filteredStack = (0, _util.filteredStackTrace)((0, _utils.captureRawStack)());
|
||||
data.boxedStack = (_parentStep = parentStep) === null || _parentStep === void 0 ? void 0 : _parentStep.boxedStack;
|
||||
if (!data.boxedStack && data.box) {
|
||||
data.boxedStack = filteredStack.slice(1);
|
||||
data.location = data.location || data.boxedStack[0];
|
||||
}
|
||||
data.location = data.location || filteredStack[0];
|
||||
const step = {
|
||||
stepId,
|
||||
...data,
|
||||
steps: [],
|
||||
complete: result => {
|
||||
if (step.endWallTime) return;
|
||||
step.endWallTime = Date.now();
|
||||
if (result.error) {
|
||||
var _result$error;
|
||||
if (typeof result.error === 'object' && !((_result$error = result.error) !== null && _result$error !== void 0 && _result$error[stepSymbol])) result.error[stepSymbol] = step;
|
||||
const error = (0, _util2.testInfoError)(result.error);
|
||||
if (data.boxedStack) error.stack = `${error.message}\n${(0, _utils.stringifyStackFrames)(data.boxedStack).join('\n')}`;
|
||||
step.error = error;
|
||||
}
|
||||
if (!step.error) {
|
||||
// Soft errors inside try/catch will make the test fail.
|
||||
// In order to locate the failing step, we are marking all the parent
|
||||
// steps as failing unconditionally.
|
||||
for (const childStep of step.steps) {
|
||||
if (childStep.error && childStep.infectParentStepsWithError) {
|
||||
step.error = childStep.error;
|
||||
step.infectParentStepsWithError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const payload = {
|
||||
testId: this.testId,
|
||||
stepId,
|
||||
wallTime: step.endWallTime,
|
||||
error: step.error,
|
||||
suggestedRebaseline: result.suggestedRebaseline
|
||||
};
|
||||
this._onStepEnd(payload);
|
||||
const errorForTrace = step.error ? {
|
||||
name: '',
|
||||
message: step.error.message || '',
|
||||
stack: step.error.stack
|
||||
} : undefined;
|
||||
this._tracing.appendAfterActionForStep(stepId, errorForTrace, result.attachments);
|
||||
}
|
||||
};
|
||||
const parentStepList = parentStep ? parentStep.steps : this._steps;
|
||||
parentStepList.push(step);
|
||||
const payload = {
|
||||
testId: this.testId,
|
||||
stepId,
|
||||
parentStepId: parentStep ? parentStep.stepId : undefined,
|
||||
title: data.title,
|
||||
category: data.category,
|
||||
wallTime: Date.now(),
|
||||
location: data.location
|
||||
};
|
||||
this._onStepBegin(payload);
|
||||
this._tracing.appendBeforeActionForStep(stepId, (_parentStep2 = parentStep) === null || _parentStep2 === void 0 ? void 0 : _parentStep2.stepId, data.apiName || data.title, data.params, data.location ? [data.location] : []);
|
||||
return step;
|
||||
}
|
||||
_interrupt() {
|
||||
// Mark as interrupted so we can ignore TimeoutError thrown by interrupt() call.
|
||||
this._wasInterrupted = true;
|
||||
this._timeoutManager.interrupt();
|
||||
// Do not overwrite existing failure (for example, unhandled rejection) with "interrupted".
|
||||
if (this.status === 'passed') this.status = 'interrupted';
|
||||
}
|
||||
_failWithError(error) {
|
||||
if (this.status === 'passed' || this.status === 'skipped') this.status = error instanceof _timeoutManager.TimeoutManagerError ? 'timedOut' : 'failed';
|
||||
const serialized = (0, _util2.testInfoError)(error);
|
||||
const step = typeof error === 'object' ? error === null || error === void 0 ? void 0 : error[stepSymbol] : undefined;
|
||||
if (step && step.boxedStack) serialized.stack = `${error.name}: ${error.message}\n${(0, _utils.stringifyStackFrames)(step.boxedStack).join('\n')}`;
|
||||
this.errors.push(serialized);
|
||||
this._tracing.appendForError(serialized);
|
||||
}
|
||||
async _runAsStage(stage, cb) {
|
||||
if (_util.debugTest.enabled) {
|
||||
var _stage$runnable;
|
||||
const location = (_stage$runnable = stage.runnable) !== null && _stage$runnable !== void 0 && _stage$runnable.location ? ` at "${(0, _util.formatLocation)(stage.runnable.location)}"` : ``;
|
||||
(0, _util.debugTest)(`started stage "${stage.title}"${location}`);
|
||||
}
|
||||
stage.step = stage.stepInfo ? this._addStep({
|
||||
...stage.stepInfo,
|
||||
title: stage.title,
|
||||
isStage: true
|
||||
}) : undefined;
|
||||
try {
|
||||
var _stage$step;
|
||||
await this._timeoutManager.withRunnable(stage.runnable, async () => {
|
||||
try {
|
||||
await cb();
|
||||
} catch (e) {
|
||||
// Only handle errors directly thrown by the user code.
|
||||
if (!stage.runnable) throw e;
|
||||
if (this._allowSkips && e instanceof SkipError) {
|
||||
if (this.status === 'passed') this.status = 'skipped';
|
||||
} else {
|
||||
// Unfortunately, we have to handle user errors and timeout errors differently.
|
||||
// Consider the following scenario:
|
||||
// - locator.click times out
|
||||
// - all stages containing the test function finish with TimeoutManagerError
|
||||
// - test finishes, the page is closed and this triggers locator.click error
|
||||
// - we would like to present the locator.click error to the user
|
||||
// - therefore, we need a try/catch inside the "run with timeout" block and capture the error
|
||||
this._failWithError(e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
(_stage$step = stage.step) === null || _stage$step === void 0 || _stage$step.complete({});
|
||||
} catch (error) {
|
||||
var _stage$step2;
|
||||
// When interrupting, we arrive here with a TimeoutManagerError, but we should not
|
||||
// consider it a timeout.
|
||||
if (!this._wasInterrupted && error instanceof _timeoutManager.TimeoutManagerError && stage.runnable) this._failWithError(error);
|
||||
(_stage$step2 = stage.step) === null || _stage$step2 === void 0 || _stage$step2.complete({
|
||||
error
|
||||
});
|
||||
throw error;
|
||||
} finally {
|
||||
(0, _util.debugTest)(`finished stage "${stage.title}"`);
|
||||
}
|
||||
}
|
||||
_isFailure() {
|
||||
return this.status !== 'skipped' && this.status !== this.expectedStatus;
|
||||
}
|
||||
_currentHookType() {
|
||||
const type = this._timeoutManager.currentSlotType();
|
||||
return ['beforeAll', 'afterAll', 'beforeEach', 'afterEach'].includes(type) ? type : undefined;
|
||||
}
|
||||
_setDebugMode() {
|
||||
this._timeoutManager.setIgnoreTimeouts();
|
||||
}
|
||||
|
||||
// ------------ TestInfo methods ------------
|
||||
|
||||
async attach(name, options = {}) {
|
||||
this._attach(name, await (0, _util.normalizeAndSaveAttachment)(this.outputPath(), name, options));
|
||||
}
|
||||
_attach(name, attachment) {
|
||||
var _attachment$body;
|
||||
const step = this._addStep({
|
||||
title: `attach "${name}"`,
|
||||
category: 'attach'
|
||||
});
|
||||
this._attachmentsPush(attachment);
|
||||
this._onAttach({
|
||||
testId: this.testId,
|
||||
name: attachment.name,
|
||||
contentType: attachment.contentType,
|
||||
path: attachment.path,
|
||||
body: (_attachment$body = attachment.body) === null || _attachment$body === void 0 ? void 0 : _attachment$body.toString('base64')
|
||||
});
|
||||
step.complete({
|
||||
attachments: [attachment]
|
||||
});
|
||||
}
|
||||
outputPath(...pathSegments) {
|
||||
const outputPath = this._getOutputPath(...pathSegments);
|
||||
_fs.default.mkdirSync(this.outputDir, {
|
||||
recursive: true
|
||||
});
|
||||
return outputPath;
|
||||
}
|
||||
_getOutputPath(...pathSegments) {
|
||||
const joinedPath = _path.default.join(...pathSegments);
|
||||
const outputPath = (0, _util.getContainedPath)(this.outputDir, joinedPath);
|
||||
if (outputPath) return outputPath;
|
||||
throw new Error(`The outputPath is not allowed outside of the parent directory. Please fix the defined path.\n\n\toutputPath: ${joinedPath}`);
|
||||
}
|
||||
_fsSanitizedTestName() {
|
||||
const fullTitleWithoutSpec = this.titlePath.slice(1).join(' ');
|
||||
return (0, _utils.sanitizeForFilePath)((0, _util.trimLongString)(fullTitleWithoutSpec));
|
||||
}
|
||||
snapshotPath(...pathSegments) {
|
||||
const subPath = _path.default.join(...pathSegments);
|
||||
const parsedSubPath = _path.default.parse(subPath);
|
||||
const relativeTestFilePath = _path.default.relative(this.project.testDir, this._requireFile);
|
||||
const parsedRelativeTestFilePath = _path.default.parse(relativeTestFilePath);
|
||||
const projectNamePathSegment = (0, _utils.sanitizeForFilePath)(this.project.name);
|
||||
const snapshotPath = (this._projectInternal.snapshotPathTemplate || '').replace(/\{(.)?testDir\}/g, '$1' + this.project.testDir).replace(/\{(.)?snapshotDir\}/g, '$1' + this.project.snapshotDir).replace(/\{(.)?snapshotSuffix\}/g, this.snapshotSuffix ? '$1' + this.snapshotSuffix : '').replace(/\{(.)?testFileDir\}/g, '$1' + parsedRelativeTestFilePath.dir).replace(/\{(.)?platform\}/g, '$1' + process.platform).replace(/\{(.)?projectName\}/g, projectNamePathSegment ? '$1' + projectNamePathSegment : '').replace(/\{(.)?testName\}/g, '$1' + this._fsSanitizedTestName()).replace(/\{(.)?testFileName\}/g, '$1' + parsedRelativeTestFilePath.base).replace(/\{(.)?testFilePath\}/g, '$1' + relativeTestFilePath).replace(/\{(.)?arg\}/g, '$1' + _path.default.join(parsedSubPath.dir, parsedSubPath.name)).replace(/\{(.)?ext\}/g, parsedSubPath.ext ? '$1' + parsedSubPath.ext : '');
|
||||
return _path.default.normalize(_path.default.resolve(this._configInternal.configDir, snapshotPath));
|
||||
}
|
||||
skip(...args) {
|
||||
this._modifier('skip', args);
|
||||
}
|
||||
fixme(...args) {
|
||||
this._modifier('fixme', args);
|
||||
}
|
||||
fail(...args) {
|
||||
this._modifier('fail', args);
|
||||
}
|
||||
slow(...args) {
|
||||
this._modifier('slow', args);
|
||||
}
|
||||
setTimeout(timeout) {
|
||||
this._timeoutManager.setTimeout(timeout);
|
||||
}
|
||||
}
|
||||
exports.TestInfoImpl = TestInfoImpl;
|
||||
class SkipError extends Error {}
|
||||
exports.SkipError = SkipError;
|
||||
const stepSymbol = Symbol('step');
|
||||
319
node_modules/playwright/lib/worker/testTracing.js
generated
vendored
Normal file
319
node_modules/playwright/lib/worker/testTracing.js
generated
vendored
Normal file
@@ -0,0 +1,319 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.testTraceEntryName = exports.TestTracing = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _zipBundle = require("playwright-core/lib/zipBundle");
|
||||
var _util = require("../util");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const testTraceEntryName = exports.testTraceEntryName = 'test.trace';
|
||||
const version = 7;
|
||||
let traceOrdinal = 0;
|
||||
class TestTracing {
|
||||
constructor(testInfo, artifactsDir) {
|
||||
this._testInfo = void 0;
|
||||
this._options = void 0;
|
||||
this._liveTraceFile = void 0;
|
||||
this._traceEvents = [];
|
||||
this._temporaryTraceFiles = [];
|
||||
this._artifactsDir = void 0;
|
||||
this._tracesDir = void 0;
|
||||
this._contextCreatedEvent = void 0;
|
||||
this._testInfo = testInfo;
|
||||
this._artifactsDir = artifactsDir;
|
||||
this._tracesDir = _path.default.join(this._artifactsDir, 'traces');
|
||||
this._contextCreatedEvent = {
|
||||
version,
|
||||
type: 'context-options',
|
||||
origin: 'testRunner',
|
||||
browserName: '',
|
||||
options: {},
|
||||
platform: process.platform,
|
||||
wallTime: Date.now(),
|
||||
monotonicTime: (0, _utils.monotonicTime)(),
|
||||
sdkLanguage: 'javascript'
|
||||
};
|
||||
this._appendTraceEvent(this._contextCreatedEvent);
|
||||
}
|
||||
_shouldCaptureTrace() {
|
||||
var _this$_options, _this$_options2, _this$_options3, _this$_options4, _this$_options5;
|
||||
if (process.env.PW_TEST_DISABLE_TRACING) return false;
|
||||
if (((_this$_options = this._options) === null || _this$_options === void 0 ? void 0 : _this$_options.mode) === 'on') return true;
|
||||
if (((_this$_options2 = this._options) === null || _this$_options2 === void 0 ? void 0 : _this$_options2.mode) === 'retain-on-failure') return true;
|
||||
if (((_this$_options3 = this._options) === null || _this$_options3 === void 0 ? void 0 : _this$_options3.mode) === 'on-first-retry' && this._testInfo.retry === 1) return true;
|
||||
if (((_this$_options4 = this._options) === null || _this$_options4 === void 0 ? void 0 : _this$_options4.mode) === 'on-all-retries' && this._testInfo.retry > 0) return true;
|
||||
if (((_this$_options5 = this._options) === null || _this$_options5 === void 0 ? void 0 : _this$_options5.mode) === 'retain-on-first-failure' && this._testInfo.retry === 0) return true;
|
||||
return false;
|
||||
}
|
||||
async startIfNeeded(value) {
|
||||
const defaultTraceOptions = {
|
||||
screenshots: true,
|
||||
snapshots: true,
|
||||
sources: true,
|
||||
attachments: true,
|
||||
_live: false,
|
||||
mode: 'off'
|
||||
};
|
||||
if (!value) {
|
||||
this._options = defaultTraceOptions;
|
||||
} else if (typeof value === 'string') {
|
||||
this._options = {
|
||||
...defaultTraceOptions,
|
||||
mode: value === 'retry-with-trace' ? 'on-first-retry' : value
|
||||
};
|
||||
} else {
|
||||
const mode = value.mode || 'off';
|
||||
this._options = {
|
||||
...defaultTraceOptions,
|
||||
...value,
|
||||
mode: mode === 'retry-with-trace' ? 'on-first-retry' : mode
|
||||
};
|
||||
}
|
||||
if (!this._shouldCaptureTrace()) {
|
||||
this._options = undefined;
|
||||
return;
|
||||
}
|
||||
if (!this._liveTraceFile && this._options._live) {
|
||||
// Note that trace name must start with testId for live tracing to work.
|
||||
this._liveTraceFile = {
|
||||
file: _path.default.join(this._tracesDir, `${this._testInfo.testId}-test.trace`),
|
||||
fs: new _utils.SerializedFS()
|
||||
};
|
||||
this._liveTraceFile.fs.mkdir(_path.default.dirname(this._liveTraceFile.file));
|
||||
const data = this._traceEvents.map(e => JSON.stringify(e)).join('\n') + '\n';
|
||||
this._liveTraceFile.fs.writeFile(this._liveTraceFile.file, data);
|
||||
}
|
||||
}
|
||||
artifactsDir() {
|
||||
return this._artifactsDir;
|
||||
}
|
||||
tracesDir() {
|
||||
return this._tracesDir;
|
||||
}
|
||||
traceTitle() {
|
||||
return [_path.default.relative(this._testInfo.project.testDir, this._testInfo.file) + ':' + this._testInfo.line, ...this._testInfo.titlePath.slice(1)].join(' › ');
|
||||
}
|
||||
generateNextTraceRecordingName() {
|
||||
const ordinalSuffix = traceOrdinal ? `-recording${traceOrdinal}` : '';
|
||||
++traceOrdinal;
|
||||
const retrySuffix = this._testInfo.retry ? `-retry${this._testInfo.retry}` : '';
|
||||
// Note that trace name must start with testId for live tracing to work.
|
||||
return `${this._testInfo.testId}${retrySuffix}${ordinalSuffix}`;
|
||||
}
|
||||
generateNextTraceRecordingPath() {
|
||||
const file = _path.default.join(this._artifactsDir, (0, _utils.createGuid)() + '.zip');
|
||||
this._temporaryTraceFiles.push(file);
|
||||
return file;
|
||||
}
|
||||
traceOptions() {
|
||||
return this._options;
|
||||
}
|
||||
async stopIfNeeded() {
|
||||
var _this$_liveTraceFile, _this$_options6, _this$_options7;
|
||||
if (!this._options) return;
|
||||
const error = await ((_this$_liveTraceFile = this._liveTraceFile) === null || _this$_liveTraceFile === void 0 ? void 0 : _this$_liveTraceFile.fs.syncAndGetError());
|
||||
if (error) throw error;
|
||||
const testFailed = this._testInfo.status !== this._testInfo.expectedStatus;
|
||||
const shouldAbandonTrace = !testFailed && (this._options.mode === 'retain-on-failure' || this._options.mode === 'retain-on-first-failure');
|
||||
if (shouldAbandonTrace) {
|
||||
for (const file of this._temporaryTraceFiles) await _fs.default.promises.unlink(file).catch(() => {});
|
||||
return;
|
||||
}
|
||||
const zipFile = new _zipBundle.yazl.ZipFile();
|
||||
if (!((_this$_options6 = this._options) !== null && _this$_options6 !== void 0 && _this$_options6.attachments)) {
|
||||
for (const event of this._traceEvents) {
|
||||
if (event.type === 'after') delete event.attachments;
|
||||
}
|
||||
}
|
||||
if ((_this$_options7 = this._options) !== null && _this$_options7 !== void 0 && _this$_options7.sources) {
|
||||
const sourceFiles = new Set();
|
||||
for (const event of this._traceEvents) {
|
||||
if (event.type === 'before') {
|
||||
for (const frame of event.stack || []) sourceFiles.add(frame.file);
|
||||
}
|
||||
}
|
||||
for (const sourceFile of sourceFiles) {
|
||||
await _fs.default.promises.readFile(sourceFile, 'utf8').then(source => {
|
||||
zipFile.addBuffer(Buffer.from(source), 'resources/src@' + (0, _utils.calculateSha1)(sourceFile) + '.txt');
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
const sha1s = new Set();
|
||||
for (const event of this._traceEvents.filter(e => e.type === 'after')) {
|
||||
for (const attachment of event.attachments || []) {
|
||||
let contentPromise;
|
||||
if (attachment.path) contentPromise = _fs.default.promises.readFile(attachment.path).catch(() => undefined);else if (attachment.base64) contentPromise = Promise.resolve(Buffer.from(attachment.base64, 'base64'));
|
||||
const content = await contentPromise;
|
||||
if (content === undefined) continue;
|
||||
const sha1 = (0, _utils.calculateSha1)(content);
|
||||
attachment.sha1 = sha1;
|
||||
delete attachment.path;
|
||||
delete attachment.base64;
|
||||
if (sha1s.has(sha1)) continue;
|
||||
sha1s.add(sha1);
|
||||
zipFile.addBuffer(content, 'resources/' + sha1);
|
||||
}
|
||||
}
|
||||
const traceContent = Buffer.from(this._traceEvents.map(e => JSON.stringify(e)).join('\n'));
|
||||
zipFile.addBuffer(traceContent, testTraceEntryName);
|
||||
await new Promise(f => {
|
||||
zipFile.end(undefined, () => {
|
||||
zipFile.outputStream.pipe(_fs.default.createWriteStream(this.generateNextTraceRecordingPath())).on('close', f);
|
||||
});
|
||||
});
|
||||
const tracePath = this._testInfo.outputPath('trace.zip');
|
||||
await mergeTraceFiles(tracePath, this._temporaryTraceFiles);
|
||||
this._testInfo.attachments.push({
|
||||
name: 'trace',
|
||||
path: tracePath,
|
||||
contentType: 'application/zip'
|
||||
});
|
||||
}
|
||||
appendForError(error) {
|
||||
var _error$stack;
|
||||
const rawStack = ((_error$stack = error.stack) === null || _error$stack === void 0 ? void 0 : _error$stack.split('\n')) || [];
|
||||
const stack = rawStack ? (0, _util.filteredStackTrace)(rawStack) : [];
|
||||
this._appendTraceEvent({
|
||||
type: 'error',
|
||||
message: this._formatError(error),
|
||||
stack
|
||||
});
|
||||
}
|
||||
_formatError(error) {
|
||||
const parts = [error.message || String(error.value)];
|
||||
if (error.cause) parts.push('[cause]: ' + this._formatError(error.cause));
|
||||
return parts.join('\n');
|
||||
}
|
||||
appendStdioToTrace(type, chunk) {
|
||||
this._appendTraceEvent({
|
||||
type,
|
||||
timestamp: (0, _utils.monotonicTime)(),
|
||||
text: typeof chunk === 'string' ? chunk : undefined,
|
||||
base64: typeof chunk === 'string' ? undefined : chunk.toString('base64')
|
||||
});
|
||||
}
|
||||
appendBeforeActionForStep(callId, parentId, apiName, params, stack) {
|
||||
this._appendTraceEvent({
|
||||
type: 'before',
|
||||
callId,
|
||||
parentId,
|
||||
startTime: (0, _utils.monotonicTime)(),
|
||||
class: 'Test',
|
||||
method: 'step',
|
||||
apiName,
|
||||
params: Object.fromEntries(Object.entries(params || {}).map(([name, value]) => [name, generatePreview(value)])),
|
||||
stack
|
||||
});
|
||||
}
|
||||
appendAfterActionForStep(callId, error, attachments = []) {
|
||||
this._appendTraceEvent({
|
||||
type: 'after',
|
||||
callId,
|
||||
endTime: (0, _utils.monotonicTime)(),
|
||||
attachments: serializeAttachments(attachments),
|
||||
error
|
||||
});
|
||||
}
|
||||
_appendTraceEvent(event) {
|
||||
this._traceEvents.push(event);
|
||||
if (this._liveTraceFile) this._liveTraceFile.fs.appendFile(this._liveTraceFile.file, JSON.stringify(event) + '\n', true);
|
||||
}
|
||||
}
|
||||
exports.TestTracing = TestTracing;
|
||||
function serializeAttachments(attachments) {
|
||||
return attachments.filter(a => a.name !== 'trace').map(a => {
|
||||
var _a$body;
|
||||
return {
|
||||
name: a.name,
|
||||
contentType: a.contentType,
|
||||
path: a.path,
|
||||
base64: (_a$body = a.body) === null || _a$body === void 0 ? void 0 : _a$body.toString('base64')
|
||||
};
|
||||
});
|
||||
}
|
||||
function generatePreview(value, visited = new Set()) {
|
||||
if (visited.has(value)) return '';
|
||||
visited.add(value);
|
||||
if (typeof value === 'string') return value;
|
||||
if (typeof value === 'number') return value.toString();
|
||||
if (typeof value === 'boolean') return value.toString();
|
||||
if (value === null) return 'null';
|
||||
if (value === undefined) return 'undefined';
|
||||
if (Array.isArray(value)) return '[' + value.map(v => generatePreview(v, visited)).join(', ') + ']';
|
||||
if (typeof value === 'object') return 'Object';
|
||||
return String(value);
|
||||
}
|
||||
async function mergeTraceFiles(fileName, temporaryTraceFiles) {
|
||||
temporaryTraceFiles = temporaryTraceFiles.filter(file => _fs.default.existsSync(file));
|
||||
if (temporaryTraceFiles.length === 1) {
|
||||
await _fs.default.promises.rename(temporaryTraceFiles[0], fileName);
|
||||
return;
|
||||
}
|
||||
const mergePromise = new _utils.ManualPromise();
|
||||
const zipFile = new _zipBundle.yazl.ZipFile();
|
||||
const entryNames = new Set();
|
||||
zipFile.on('error', error => mergePromise.reject(error));
|
||||
for (let i = temporaryTraceFiles.length - 1; i >= 0; --i) {
|
||||
const tempFile = temporaryTraceFiles[i];
|
||||
const promise = new _utils.ManualPromise();
|
||||
_zipBundle.yauzl.open(tempFile, (err, inZipFile) => {
|
||||
if (err) {
|
||||
promise.reject(err);
|
||||
return;
|
||||
}
|
||||
let pendingEntries = inZipFile.entryCount;
|
||||
inZipFile.on('entry', entry => {
|
||||
let entryName = entry.fileName;
|
||||
if (entry.fileName === testTraceEntryName) {
|
||||
// Keep the name for test traces so that the last test trace
|
||||
// that contains most of the information is kept in the trace.
|
||||
// Note the reverse order of the iteration (from new traces to old).
|
||||
} else if (entry.fileName.match(/[\d-]*trace\./)) {
|
||||
entryName = i + '-' + entry.fileName;
|
||||
}
|
||||
if (entryNames.has(entryName)) {
|
||||
if (--pendingEntries === 0) promise.resolve();
|
||||
return;
|
||||
}
|
||||
entryNames.add(entryName);
|
||||
inZipFile.openReadStream(entry, (err, readStream) => {
|
||||
if (err) {
|
||||
promise.reject(err);
|
||||
return;
|
||||
}
|
||||
zipFile.addReadStream(readStream, entryName);
|
||||
if (--pendingEntries === 0) promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
await promise;
|
||||
}
|
||||
zipFile.end(undefined, () => {
|
||||
zipFile.outputStream.pipe(_fs.default.createWriteStream(fileName)).on('close', () => {
|
||||
void Promise.all(temporaryTraceFiles.map(tempFile => _fs.default.promises.unlink(tempFile))).then(() => {
|
||||
mergePromise.resolve();
|
||||
}).catch(error => mergePromise.reject(error));
|
||||
}).on('error', error => mergePromise.reject(error));
|
||||
});
|
||||
await mergePromise;
|
||||
}
|
||||
153
node_modules/playwright/lib/worker/timeoutManager.js
generated
vendored
Normal file
153
node_modules/playwright/lib/worker/timeoutManager.js
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.kMaxDeadline = exports.TimeoutManagerError = exports.TimeoutManager = void 0;
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const kMaxDeadline = exports.kMaxDeadline = 2147483647; // 2^31-1
|
||||
|
||||
class TimeoutManager {
|
||||
constructor(timeout) {
|
||||
this._defaultSlot = void 0;
|
||||
this._running = void 0;
|
||||
this._ignoreTimeouts = false;
|
||||
this._defaultSlot = {
|
||||
timeout,
|
||||
elapsed: 0
|
||||
};
|
||||
}
|
||||
setIgnoreTimeouts() {
|
||||
this._ignoreTimeouts = true;
|
||||
if (this._running) this._updateTimeout(this._running);
|
||||
}
|
||||
interrupt() {
|
||||
if (this._running) this._running.timeoutPromise.reject(this._createTimeoutError(this._running));
|
||||
}
|
||||
isTimeExhaustedFor(runnable) {
|
||||
var _runnable$fixture;
|
||||
const slot = ((_runnable$fixture = runnable.fixture) === null || _runnable$fixture === void 0 ? void 0 : _runnable$fixture.slot) || runnable.slot || this._defaultSlot;
|
||||
// Note: the "-1" here matches the +1 in _updateTimeout.
|
||||
return slot.timeout > 0 && slot.elapsed >= slot.timeout - 1;
|
||||
}
|
||||
async withRunnable(runnable, cb) {
|
||||
var _runnable$fixture2;
|
||||
if (!runnable) return await cb();
|
||||
if (this._running) throw new Error(`Internal error: duplicate runnable`);
|
||||
const running = this._running = {
|
||||
runnable,
|
||||
slot: ((_runnable$fixture2 = runnable.fixture) === null || _runnable$fixture2 === void 0 ? void 0 : _runnable$fixture2.slot) || runnable.slot || this._defaultSlot,
|
||||
start: (0, _utils.monotonicTime)(),
|
||||
deadline: kMaxDeadline,
|
||||
timer: undefined,
|
||||
timeoutPromise: new _utils.ManualPromise()
|
||||
};
|
||||
try {
|
||||
this._updateTimeout(running);
|
||||
return await Promise.race([cb(), running.timeoutPromise]);
|
||||
} finally {
|
||||
if (running.timer) clearTimeout(running.timer);
|
||||
running.timer = undefined;
|
||||
running.slot.elapsed += (0, _utils.monotonicTime)() - running.start;
|
||||
this._running = undefined;
|
||||
}
|
||||
}
|
||||
_updateTimeout(running) {
|
||||
if (running.timer) clearTimeout(running.timer);
|
||||
running.timer = undefined;
|
||||
if (this._ignoreTimeouts || !running.slot.timeout) {
|
||||
running.deadline = kMaxDeadline;
|
||||
return;
|
||||
}
|
||||
running.deadline = running.start + (running.slot.timeout - running.slot.elapsed);
|
||||
// Compensate for Node.js troubles with timeouts that can fire too early.
|
||||
// We add an extra millisecond which seems to be enough.
|
||||
// See https://github.com/nodejs/node/issues/26578.
|
||||
const timeout = running.deadline - (0, _utils.monotonicTime)() + 1;
|
||||
if (timeout <= 0) running.timeoutPromise.reject(this._createTimeoutError(running));else running.timer = setTimeout(() => running.timeoutPromise.reject(this._createTimeoutError(running)), timeout);
|
||||
}
|
||||
defaultSlot() {
|
||||
return this._defaultSlot;
|
||||
}
|
||||
slow() {
|
||||
const slot = this._running ? this._running.slot : this._defaultSlot;
|
||||
slot.timeout = slot.timeout * 3;
|
||||
if (this._running) this._updateTimeout(this._running);
|
||||
}
|
||||
setTimeout(timeout) {
|
||||
const slot = this._running ? this._running.slot : this._defaultSlot;
|
||||
slot.timeout = timeout;
|
||||
if (this._running) this._updateTimeout(this._running);
|
||||
}
|
||||
currentSlotDeadline() {
|
||||
return this._running ? this._running.deadline : kMaxDeadline;
|
||||
}
|
||||
currentSlotType() {
|
||||
return this._running ? this._running.runnable.type : 'test';
|
||||
}
|
||||
_createTimeoutError(running) {
|
||||
var _runnable$fixture3;
|
||||
let message = '';
|
||||
const timeout = running.slot.timeout;
|
||||
const runnable = running.runnable;
|
||||
switch (runnable.type) {
|
||||
case 'test':
|
||||
{
|
||||
if (runnable.fixture) {
|
||||
if (runnable.fixture.phase === 'setup') message = `Test timeout of ${timeout}ms exceeded while setting up "${runnable.fixture.title}".`;else message = `Tearing down "${runnable.fixture.title}" exceeded the test timeout of ${timeout}ms.`;
|
||||
} else {
|
||||
message = `Test timeout of ${timeout}ms exceeded.`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'afterEach':
|
||||
case 'beforeEach':
|
||||
message = `Test timeout of ${timeout}ms exceeded while running "${runnable.type}" hook.`;
|
||||
break;
|
||||
case 'beforeAll':
|
||||
case 'afterAll':
|
||||
message = `"${runnable.type}" hook timeout of ${timeout}ms exceeded.`;
|
||||
break;
|
||||
case 'teardown':
|
||||
{
|
||||
if (runnable.fixture) message = `Worker teardown timeout of ${timeout}ms exceeded while ${runnable.fixture.phase === 'setup' ? 'setting up' : 'tearing down'} "${runnable.fixture.title}".`;else message = `Worker teardown timeout of ${timeout}ms exceeded.`;
|
||||
break;
|
||||
}
|
||||
case 'skip':
|
||||
case 'slow':
|
||||
case 'fixme':
|
||||
case 'fail':
|
||||
message = `"${runnable.type}" modifier timeout of ${timeout}ms exceeded.`;
|
||||
break;
|
||||
}
|
||||
const fixtureWithSlot = (_runnable$fixture3 = runnable.fixture) !== null && _runnable$fixture3 !== void 0 && _runnable$fixture3.slot ? runnable.fixture : undefined;
|
||||
if (fixtureWithSlot) message = `Fixture "${fixtureWithSlot.title}" timeout of ${timeout}ms exceeded during ${fixtureWithSlot.phase}.`;
|
||||
message = _utilsBundle.colors.red(message);
|
||||
const location = (fixtureWithSlot || runnable).location;
|
||||
const error = new TimeoutManagerError(message);
|
||||
error.name = '';
|
||||
// Include location for hooks, modifiers and fixtures to distinguish between them.
|
||||
error.stack = message + (location ? `\n at ${location.file}:${location.line}:${location.column}` : '');
|
||||
return error;
|
||||
}
|
||||
}
|
||||
exports.TimeoutManager = TimeoutManager;
|
||||
class TimeoutManagerError extends Error {}
|
||||
exports.TimeoutManagerError = TimeoutManagerError;
|
||||
29
node_modules/playwright/lib/worker/util.js
generated
vendored
Normal file
29
node_modules/playwright/lib/worker/util.js
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.testInfoError = testInfoError;
|
||||
var _matcherHint = require("../matchers/matcherHint");
|
||||
var _util = require("../util");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function testInfoError(error) {
|
||||
const result = (0, _util.serializeError)(error);
|
||||
if (error instanceof _matcherHint.ExpectError) result.matcherResult = error.matcherResult;
|
||||
return result;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user