@playwright/test 1.35.0-alpha-jun-5-2023 → 1.35.0-alpha-jun-7-2023

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc.js CHANGED
@@ -1,3 +1,15 @@
1
+ const path = require('path');
2
+
1
3
  module.exports = {
2
- extends: "../.eslintrc-with-ts-config.js",
4
+ extends: '../.eslintrc.js',
5
+ parser: "@typescript-eslint/parser",
6
+ plugins: ["@typescript-eslint", "notice"],
7
+ parserOptions: {
8
+ ecmaVersion: 9,
9
+ sourceType: "module",
10
+ project: path.join(__dirname, '..', '..', 'tsconfig.json'),
11
+ },
12
+ rules: {
13
+ '@typescript-eslint/no-floating-promises': 'error',
14
+ },
3
15
  };
@@ -99,7 +99,6 @@ This project incorporates components from the projects listed below. The origina
99
99
  - color-name@1.1.4 (https://github.com/colorjs/color-name)
100
100
  - convert-source-map@1.8.0 (https://github.com/thlorenz/convert-source-map)
101
101
  - debug@4.3.4 (https://github.com/debug-js/debug)
102
- - define-lazy-prop@2.0.0 (https://github.com/sindresorhus/define-lazy-prop)
103
102
  - diff-sequences@27.5.1 (https://github.com/facebook/jest)
104
103
  - electron-to-chromium@1.4.337 (https://github.com/kilian/electron-to-chromium)
105
104
  - enquirer@2.3.6 (https://github.com/enquirer/enquirer)
@@ -115,11 +114,9 @@ This project incorporates components from the projects listed below. The origina
115
114
  - has-flag@3.0.0 (https://github.com/sindresorhus/has-flag)
116
115
  - has-flag@4.0.0 (https://github.com/sindresorhus/has-flag)
117
116
  - is-binary-path@2.1.0 (https://github.com/sindresorhus/is-binary-path)
118
- - is-docker@2.2.1 (https://github.com/sindresorhus/is-docker)
119
117
  - is-extglob@2.1.1 (https://github.com/jonschlinkert/is-extglob)
120
118
  - is-glob@4.0.3 (https://github.com/micromatch/is-glob)
121
119
  - is-number@7.0.0 (https://github.com/jonschlinkert/is-number)
122
- - is-wsl@2.2.0 (https://github.com/sindresorhus/is-wsl)
123
120
  - jest-diff@27.5.1 (https://github.com/facebook/jest)
124
121
  - jest-get-type@27.5.1 (https://github.com/facebook/jest)
125
122
  - jest-matcher-utils@27.2.5 (https://github.com/facebook/jest)
@@ -133,7 +130,6 @@ This project incorporates components from the projects listed below. The origina
133
130
  - ms@2.1.2 (https://github.com/zeit/ms)
134
131
  - node-releases@2.0.10 (https://github.com/chicoxyzzy/node-releases)
135
132
  - normalize-path@3.0.0 (https://github.com/jonschlinkert/normalize-path)
136
- - open@8.4.0 (https://github.com/sindresorhus/open)
137
133
  - picocolors@1.0.0 (https://github.com/alexeyraspopov/picocolors)
138
134
  - picomatch@2.3.1 (https://github.com/micromatch/picomatch)
139
135
  - pirates@4.0.4 (https://github.com/danez/pirates)
@@ -3105,20 +3101,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3105
3101
  =========================================
3106
3102
  END OF debug@4.3.4 AND INFORMATION
3107
3103
 
3108
- %% define-lazy-prop@2.0.0 NOTICES AND INFORMATION BEGIN HERE
3109
- =========================================
3110
- MIT License
3111
-
3112
- Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
3113
-
3114
- 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:
3115
-
3116
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
3117
-
3118
- 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.
3119
- =========================================
3120
- END OF define-lazy-prop@2.0.0 AND INFORMATION
3121
-
3122
3104
  %% diff-sequences@27.5.1 NOTICES AND INFORMATION BEGIN HERE
3123
3105
  =========================================
3124
3106
  MIT License
@@ -3395,20 +3377,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
3395
3377
  =========================================
3396
3378
  END OF is-binary-path@2.1.0 AND INFORMATION
3397
3379
 
3398
- %% is-docker@2.2.1 NOTICES AND INFORMATION BEGIN HERE
3399
- =========================================
3400
- MIT License
3401
-
3402
- Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
3403
-
3404
- 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:
3405
-
3406
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
3407
-
3408
- 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.
3409
- =========================================
3410
- END OF is-docker@2.2.1 AND INFORMATION
3411
-
3412
3380
  %% is-extglob@2.1.1 NOTICES AND INFORMATION BEGIN HERE
3413
3381
  =========================================
3414
3382
  The MIT License (MIT)
@@ -3487,20 +3455,6 @@ THE SOFTWARE.
3487
3455
  =========================================
3488
3456
  END OF is-number@7.0.0 AND INFORMATION
3489
3457
 
3490
- %% is-wsl@2.2.0 NOTICES AND INFORMATION BEGIN HERE
3491
- =========================================
3492
- MIT License
3493
-
3494
- Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
3495
-
3496
- 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:
3497
-
3498
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
3499
-
3500
- 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.
3501
- =========================================
3502
- END OF is-wsl@2.2.0 AND INFORMATION
3503
-
3504
3458
  %% jest-diff@27.5.1 NOTICES AND INFORMATION BEGIN HERE
3505
3459
  =========================================
3506
3460
  MIT License
@@ -3834,20 +3788,6 @@ THE SOFTWARE.
3834
3788
  =========================================
3835
3789
  END OF normalize-path@3.0.0 AND INFORMATION
3836
3790
 
3837
- %% open@8.4.0 NOTICES AND INFORMATION BEGIN HERE
3838
- =========================================
3839
- MIT License
3840
-
3841
- Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
3842
-
3843
- 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:
3844
-
3845
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
3846
-
3847
- 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.
3848
- =========================================
3849
- END OF open@8.4.0 AND INFORMATION
3850
-
3851
3791
  %% picocolors@1.0.0 NOTICES AND INFORMATION BEGIN HERE
3852
3792
  =========================================
3853
3793
  ISC License
@@ -4284,6 +4224,6 @@ END OF yallist@3.1.1 AND INFORMATION
4284
4224
 
4285
4225
  SUMMARY BEGIN HERE
4286
4226
  =========================================
4287
- Total Packages: 149
4227
+ Total Packages: 145
4288
4228
  =========================================
4289
4229
  END OF SUMMARY
package/lib/cli.js CHANGED
@@ -126,7 +126,10 @@ async function runTests(args, opts) {
126
126
  config.cliPassWithNoTests = !!opts.passWithNoTests;
127
127
  const runner = new _runner.Runner(config);
128
128
  let status;
129
- if (opts.ui) status = await runner.uiAllTests();else if (process.env.PWTEST_WATCH) status = await runner.watchAllTests();else status = await runner.runAllTests();
129
+ if (opts.ui || opts.uiHost || opts.uiPort) status = await runner.uiAllTests({
130
+ host: opts.uiHost,
131
+ port: opts.uiPort ? +opts.uiPort : undefined
132
+ });else if (process.env.PWTEST_WATCH) status = await runner.watchAllTests();else status = await runner.runAllTests();
130
133
  await (0, _utils.stopProfiling)('runner');
131
134
  if (status === 'interrupted') process.exit(130);
132
135
  process.exit(status === 'passed' ? 0 : 1);
@@ -262,7 +265,7 @@ function restartWithExperimentalTsEsm(configFile) {
262
265
  return true;
263
266
  }
264
267
  const kTraceModes = ['on', 'off', 'on-first-retry', 'on-all-retries', 'retain-on-failure'];
265
- 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`], ['--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`], ['--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")`], ['--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 (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(', ')}`], ['--ui', `Run tests in interactive UI mode`], ['-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`]];
268
+ 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`], ['--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`], ['--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")`], ['--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 (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(', ')}`], ['--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`]];
266
269
  addTestCommand(_program.default);
267
270
  addShowReportCommand(_program.default);
268
271
  addListFilesCommand(_program.default);
@@ -57,7 +57,7 @@ process.on('message', async message => {
57
57
  } = message.params;
58
58
  setTtyParams(process.stdout, processParams.stdoutParams);
59
59
  setTtyParams(process.stderr, processParams.stderrParams);
60
- (0, _utils.startProfiling)();
60
+ void (0, _utils.startProfiling)();
61
61
  const {
62
62
  create
63
63
  } = require(runnerScript);
package/lib/index.js CHANGED
@@ -95,12 +95,12 @@ const playwrightFixtures = {
95
95
  scope: 'worker',
96
96
  option: true
97
97
  }],
98
- connectOptions: [({}, use) => {
98
+ connectOptions: [async ({}, use) => {
99
99
  // Usually, when connect options are specified (e.g, in the config or in the environment),
100
100
  // all launch() calls are turned into connect() calls.
101
101
  // However, when running in "reuse browser" mode and connecting to the reusable server,
102
102
  // only the default "browser" fixture should turn into reused browser.
103
- use(process.env.PW_TEST_REUSE_CONTEXT ? undefined : connectOptionsFromEnv());
103
+ await use(process.env.PW_TEST_REUSE_CONTEXT ? undefined : connectOptionsFromEnv());
104
104
  }, {
105
105
  scope: 'worker',
106
106
  option: true
@@ -149,6 +149,11 @@ class SnapshotHelper {
149
149
  if (isWriteMissingMode) {
150
150
  writeFileSync(this.snapshotPath, actual);
151
151
  writeFileSync(this.actualPath, actual);
152
+ this.testInfo.attachments.push({
153
+ name: (0, _util.addSuffixToFilePath)(this.snapshotName, '-actual'),
154
+ contentType: this.mimeType,
155
+ path: this.actualPath
156
+ });
152
157
  }
153
158
  const message = `A snapshot doesn't exist at ${this.snapshotPath}${isWriteMissingMode ? ', writing actual.' : '.'}`;
154
159
  if (this.updateSnapshots === 'all') {
@@ -283,6 +288,7 @@ async function toHaveScreenshot(pageOrLocator, nameOrOptions = {}, optOptions =
283
288
  caret: (_config$caret = config === null || config === void 0 ? void 0 : config.caret) !== null && _config$caret !== void 0 ? _config$caret : 'hide',
284
289
  ...helper.allOptions,
285
290
  mask: helper.allOptions.mask || [],
291
+ maskColor: helper.allOptions.maskColor,
286
292
  name: undefined,
287
293
  threshold: undefined,
288
294
  maxDiffPixels: undefined,
@@ -52,7 +52,7 @@ class GitHubReporter extends _base.BaseReporter {
52
52
  return false;
53
53
  }
54
54
  async onEnd(result) {
55
- super.onEnd(result);
55
+ await super.onEnd(result);
56
56
  this._printAnnotations();
57
57
  }
58
58
  onError(error) {
@@ -8,7 +8,6 @@ exports.showHTMLReport = showHTMLReport;
8
8
  exports.startHtmlReportServer = startHtmlReportServer;
9
9
  var _utilsBundle = require("playwright-core/lib/utilsBundle");
10
10
  var _fs = _interopRequireDefault(require("fs"));
11
- var _utilsBundle2 = require("../utilsBundle");
12
11
  var _path = _interopRequireDefault(require("path"));
13
12
  var _stream = require("stream");
14
13
  var _utils = require("playwright-core/lib/utils");
@@ -37,7 +36,6 @@ class HtmlReporter {
37
36
  constructor(options) {
38
37
  this.config = void 0;
39
38
  this.suite = void 0;
40
- this._montonicStartTime = 0;
41
39
  this._options = void 0;
42
40
  this._outputFolder = void 0;
43
41
  this._attachmentsBaseURL = void 0;
@@ -49,7 +47,6 @@ class HtmlReporter {
49
47
  return false;
50
48
  }
51
49
  onBegin(config, suite) {
52
- this._montonicStartTime = (0, _utils.monotonicTime)();
53
50
  this.config = config;
54
51
  const {
55
52
  outputFolder,
@@ -89,7 +86,6 @@ class HtmlReporter {
89
86
  };
90
87
  }
91
88
  async onEnd() {
92
- const duration = (0, _utils.monotonicTime)() - this._montonicStartTime;
93
89
  const projectSuites = this.suite.suites;
94
90
  const reports = projectSuites.map(suite => {
95
91
  const rawReporter = new _raw.default();
@@ -98,10 +94,7 @@ class HtmlReporter {
98
94
  });
99
95
  await (0, _utils.removeFolders)([this._outputFolder]);
100
96
  const builder = new HtmlBuilder(this._outputFolder, this._attachmentsBaseURL);
101
- this._buildResult = await builder.build({
102
- ...this.config.metadata,
103
- duration
104
- }, reports);
97
+ this._buildResult = await builder.build(this.config.metadata, reports);
105
98
  }
106
99
  async onExit() {
107
100
  if (process.env.CI || !this._buildResult) return;
@@ -153,9 +146,9 @@ async function showHTMLReport(reportFolder, host = 'localhost', port, testId) {
153
146
  console.log('');
154
147
  console.log(_utilsBundle.colors.cyan(` Serving HTML report at ${url}. Press Ctrl+C to quit.`));
155
148
  if (testId) url += `#?testId=${testId}`;
156
- await (0, _utilsBundle2.open)(url, {
149
+ await (0, _utilsBundle.open)(url, {
157
150
  wait: true
158
- }).catch(() => console.log(`Failed to open browser on ${url}`));
151
+ }).catch(() => {});
159
152
  await new Promise(() => {});
160
153
  }
161
154
  function startHtmlReportServer(folder) {
@@ -257,7 +250,7 @@ class HtmlBuilder {
257
250
  projectNames: rawReports.map(r => r.project.name),
258
251
  stats: {
259
252
  ...[...data.values()].reduce((a, e) => addStats(a, e.testFileSummary.stats), emptyStats()),
260
- duration: metadata.duration
253
+ duration: metadata.totalTime
261
254
  }
262
255
  };
263
256
  htmlReport.files.sort((f1, f2) => {
@@ -10,6 +10,7 @@ var _babelBundle = require("../transform/babelBundle");
10
10
  var _test = require("../common/test");
11
11
  var _multiplexer = require("./multiplexer");
12
12
  var _base = require("./base");
13
+ var _utils = require("playwright-core/lib/utils");
13
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
15
  /**
15
16
  * Copyright (c) Microsoft Corporation.
@@ -32,12 +33,14 @@ class InternalReporter {
32
33
  this._multiplexer = void 0;
33
34
  this._deferred = [];
34
35
  this._config = void 0;
36
+ this._montonicStartTime = 0;
35
37
  this._multiplexer = new _multiplexer.Multiplexer(reporters);
36
38
  }
37
39
  onConfigure(config) {
38
40
  this._config = config;
39
41
  }
40
42
  onBegin(config, suite) {
43
+ this._montonicStartTime = (0, _utils.monotonicTime)();
41
44
  this._multiplexer.onBegin(config, suite);
42
45
  const deferred = this._deferred;
43
46
  this._deferred = null;
@@ -80,7 +83,9 @@ class InternalReporter {
80
83
  this._addSnippetToTestErrors(test, result);
81
84
  this._multiplexer.onTestEnd(test, result);
82
85
  }
83
- async onEnd() {}
86
+ async onEnd() {
87
+ this._config.config.metadata.totalTime = (0, _utils.monotonicTime)() - this._montonicStartTime;
88
+ }
84
89
  async onExit(result) {
85
90
  if (this._deferred) {
86
91
  // onBegin was not reported, emit it.
@@ -28,6 +28,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
28
28
 
29
29
  async function createMergedReport(config, dir, reporterDescriptions, resolvePaths) {
30
30
  const shardFiles = await sortedShardFiles(dir);
31
+ if (shardFiles.length === 0) throw new Error(`No report files found in ${dir}`);
31
32
  const events = await mergeEvents(dir, shardFiles);
32
33
  if (resolvePaths) patchAttachmentPaths(events, dir);
33
34
  const reporters = await (0, _reporters.createReporters)(config, 'merge', reporterDescriptions);
@@ -69,7 +70,9 @@ function mergeBeginEvents(beginEvents) {
69
70
  configFile: undefined,
70
71
  globalTimeout: 0,
71
72
  maxFailures: 0,
72
- metadata: {},
73
+ metadata: {
74
+ totalTime: 0
75
+ },
73
76
  rootDir: '',
74
77
  version: '',
75
78
  workers: 0,
@@ -97,7 +100,8 @@ function mergeConfigs(to, from) {
97
100
  ...from,
98
101
  metadata: {
99
102
  ...to.metadata,
100
- ...from.metadata
103
+ ...from.metadata,
104
+ totalTime: to.metadata.totalTime + from.metadata.totalTime
101
105
  },
102
106
  workers: to.workers + from.workers
103
107
  };
@@ -88,7 +88,7 @@ class Dispatcher {
88
88
  this._checkFinished();
89
89
 
90
90
  // 5. We got a free worker - perhaps we can immediately start another job?
91
- this._scheduleJob();
91
+ void this._scheduleJob();
92
92
  }
93
93
  async _startJobInWorker(index, job) {
94
94
  let worker = this._workerSlots[index].worker;
@@ -156,7 +156,7 @@ class Dispatcher {
156
156
  busy: false
157
157
  });
158
158
  // 2. Schedule enough jobs.
159
- for (let i = 0; i < this._workerSlots.length; i++) this._scheduleJob();
159
+ for (let i = 0; i < this._workerSlots.length; i++) void this._scheduleJob();
160
160
  this._checkFinished();
161
161
  // 3. More jobs are scheduled when the worker becomes free, or a new job is added.
162
162
  // 4. Wait for all jobs to finish.
@@ -306,13 +306,13 @@ class Dispatcher {
306
306
  // - we are here not because something failed
307
307
  // - no unrecoverable worker error
308
308
  if (!remaining.length && !failedTestIds.size && !params.fatalErrors.length && !params.skipTestsDueToSetupFailure.length && !params.fatalUnknownTestIds && !params.unexpectedExitError) {
309
- if (this._isWorkerRedundant(worker)) worker.stop();
309
+ if (this._isWorkerRedundant(worker)) void worker.stop();
310
310
  doneWithJob();
311
311
  return;
312
312
  }
313
313
 
314
314
  // When worker encounters error, we will stop it and create a new one.
315
- worker.stop(true /* didFail */);
315
+ void worker.stop(true /* didFail */);
316
316
 
317
317
  const massSkipTestsFromRemaining = (testIds, errors, onlyStartedTests) => {
318
318
  remaining = remaining.filter(test => {
@@ -409,7 +409,7 @@ class Dispatcher {
409
409
  });
410
410
  this._queuedOrRunningHashCount.set(testGroup.workerHash, this._queuedOrRunningHashCount.get(testGroup.workerHash) + 1);
411
411
  // Perhaps we can immediately start the new job if there is a worker available?
412
- this._scheduleJob();
412
+ void this._scheduleJob();
413
413
  }
414
414
 
415
415
  // This job is over, we just scheduled another one.
@@ -93,12 +93,12 @@ class Runner {
93
93
  }));
94
94
  return await (0, _watchMode.runWatchModeLoop)(config);
95
95
  }
96
- async uiAllTests() {
96
+ async uiAllTests(options) {
97
97
  const config = this._config;
98
98
  (0, _webServerPlugin.webServerPluginsForConfig)(config).forEach(p => config.plugins.push({
99
99
  factory: p
100
100
  }));
101
- return await (0, _uiMode.runUIMode)(config);
101
+ return await (0, _uiMode.runUIMode)(config, options);
102
102
  }
103
103
  }
104
104
  exports.Runner = Runner;
@@ -12,6 +12,7 @@ var _teleEmitter = require("../reporters/teleEmitter");
12
12
  var _reporters = require("./reporters");
13
13
  var _tasks = require("./tasks");
14
14
  var _utilsBundle = require("../utilsBundle");
15
+ var _utilsBundle2 = require("playwright-core/lib/utilsBundle");
15
16
  var _list = _interopRequireDefault(require("../reporters/list"));
16
17
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
18
  /**
@@ -33,7 +34,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
33
34
  class UIMode {
34
35
  constructor(config) {
35
36
  this._config = void 0;
36
- this._page = void 0;
37
+ this._transport = void 0;
37
38
  this._testRun = void 0;
38
39
  this.globalCleanup = void 0;
39
40
  this._globalWatcher = void 0;
@@ -59,17 +60,12 @@ class UIMode {
59
60
  };
60
61
  this._originalStdoutWrite = process.stdout.write;
61
62
  this._originalStderrWrite = process.stderr.write;
62
- this._globalWatcher = new Watcher('deep', () => this._dispatchEvent({
63
- method: 'listChanged'
64
- }));
63
+ this._globalWatcher = new Watcher('deep', () => this._dispatchEvent('listChanged', {}));
65
64
  this._testWatcher = new Watcher('flat', events => {
66
65
  const collector = new Set();
67
66
  events.forEach(f => (0, _compilationCache.collectAffectedTestFiles)(f.file, collector));
68
- this._dispatchEvent({
69
- method: 'testFilesChanged',
70
- params: {
71
- testFileNames: [...collector]
72
- }
67
+ this._dispatchEvent('testFilesChanged', {
68
+ testFileNames: [...collector]
73
69
  });
74
70
  });
75
71
  }
@@ -92,61 +88,57 @@ class UIMode {
92
88
  this.globalCleanup = globalCleanup;
93
89
  return status;
94
90
  }
95
- async showUI() {
96
- this._page = await (0, _server.showTraceViewer)([], 'chromium', {
91
+ async showUI(options) {
92
+ const exitPromise = new _utils.ManualPromise();
93
+ let queue = Promise.resolve();
94
+ this._transport = {
95
+ dispatch: async (method, params) => {
96
+ if (method === 'exit') {
97
+ exitPromise.resolve();
98
+ return;
99
+ }
100
+ if (method === 'watch') {
101
+ this._watchFiles(params.fileNames);
102
+ return;
103
+ }
104
+ if (method === 'open' && params.location) {
105
+ (0, _utilsBundle2.open)('vscode://file/' + params.location).catch(e => this._originalStderrWrite.call(process.stderr, String(e)));
106
+ return;
107
+ }
108
+ if (method === 'resizeTerminal') {
109
+ process.stdout.columns = params.cols;
110
+ process.stdout.rows = params.rows;
111
+ process.stderr.columns = params.cols;
112
+ process.stderr.columns = params.rows;
113
+ return;
114
+ }
115
+ if (method === 'stop') {
116
+ void this._stopTests();
117
+ return;
118
+ }
119
+ queue = queue.then(() => this._queueListOrRun(method, params));
120
+ await queue;
121
+ },
122
+ onclose: () => exitPromise.resolve()
123
+ };
124
+ await (0, _server.showTraceViewer)([], 'chromium', {
97
125
  app: 'uiMode.html',
98
- headless: (0, _utils.isUnderTest)() && process.env.PWTEST_HEADED_FOR_TEST !== '1'
126
+ headless: (0, _utils.isUnderTest)() && process.env.PWTEST_HEADED_FOR_TEST !== '1',
127
+ transport: this._transport,
128
+ host: options.host,
129
+ port: options.port,
130
+ openInBrowser: options.host !== undefined || options.port !== undefined
99
131
  });
100
132
  if (!process.env.PWTEST_DEBUG) {
101
133
  process.stdout.write = chunk => {
102
- this._dispatchEvent({
103
- method: 'stdio',
104
- params: chunkToPayload('stdout', chunk)
105
- });
134
+ this._dispatchEvent('stdio', chunkToPayload('stdout', chunk));
106
135
  return true;
107
136
  };
108
137
  process.stderr.write = chunk => {
109
- this._dispatchEvent({
110
- method: 'stdio',
111
- params: chunkToPayload('stderr', chunk)
112
- });
138
+ this._dispatchEvent('stdio', chunkToPayload('stderr', chunk));
113
139
  return true;
114
140
  };
115
141
  }
116
- const exitPromise = new _utils.ManualPromise();
117
- this._page.on('close', () => exitPromise.resolve());
118
- let queue = Promise.resolve();
119
- this._page.exposeBinding('sendMessage', false, async (source, data) => {
120
- const {
121
- method,
122
- params
123
- } = data;
124
- if (method === 'exit') {
125
- exitPromise.resolve();
126
- return;
127
- }
128
- if (method === 'watch') {
129
- this._watchFiles(params.fileNames);
130
- return;
131
- }
132
- if (method === 'open' && params.location) {
133
- (0, _utilsBundle.open)('vscode://file/' + params.location).catch(e => this._originalStderrWrite.call(process.stderr, String(e)));
134
- return;
135
- }
136
- if (method === 'resizeTerminal') {
137
- process.stdout.columns = params.cols;
138
- process.stdout.rows = params.rows;
139
- process.stderr.columns = params.cols;
140
- process.stderr.columns = params.rows;
141
- return;
142
- }
143
- if (method === 'stop') {
144
- this._stopTests();
145
- return;
146
- }
147
- queue = queue.then(() => this._queueListOrRun(method, params));
148
- await queue;
149
- });
150
142
  await exitPromise;
151
143
  if (!process.env.PWTEST_DEBUG) {
152
144
  process.stdout.write = this._originalStdoutWrite;
@@ -157,14 +149,12 @@ class UIMode {
157
149
  if (method === 'list') await this._listTests();
158
150
  if (method === 'run') await this._runTests(params.testIds);
159
151
  }
160
- _dispatchEvent(message) {
161
- // eslint-disable-next-line no-console
162
- this._page.mainFrame().evaluateExpression(dispatchFuncSource, {
163
- isFunction: true
164
- }, message).catch(e => this._originalStderrWrite.call(process.stderr, String(e)));
152
+ _dispatchEvent(method, params) {
153
+ var _this$_transport$send, _this$_transport;
154
+ (_this$_transport$send = (_this$_transport = this._transport).sendEvent) === null || _this$_transport$send === void 0 ? void 0 : _this$_transport$send.call(_this$_transport, method, params);
165
155
  }
166
156
  async _listTests() {
167
- const listReporter = new _teleEmitter.TeleReporterEmitter(e => this._dispatchEvent(e));
157
+ const listReporter = new _teleEmitter.TeleReporterEmitter(e => this._dispatchEvent(e.method, e.params));
168
158
  const reporter = new _internalReporter.InternalReporter([listReporter]);
169
159
  this._config.cliListOnly = true;
170
160
  this._config.testIdMatcher = undefined;
@@ -188,7 +178,7 @@ class UIMode {
188
178
  this._config.cliListOnly = false;
189
179
  this._config.testIdMatcher = id => !testIdSet || testIdSet.has(id);
190
180
  const reporters = await (0, _reporters.createReporters)(this._config, 'ui');
191
- reporters.push(new _teleEmitter.TeleReporterEmitter(e => this._dispatchEvent(e)));
181
+ reporters.push(new _teleEmitter.TeleReporterEmitter(e => this._dispatchEvent(e.method, e.params)));
192
182
  const reporter = new _internalReporter.InternalReporter(reporters);
193
183
  const taskRunner = (0, _tasks.createTaskRunnerForWatch)(this._config, reporter);
194
184
  const testRun = new _tasks.TestRun(this._config, reporter);
@@ -209,7 +199,7 @@ class UIMode {
209
199
  };
210
200
  await run;
211
201
  }
212
- async _watchFiles(fileNames) {
202
+ _watchFiles(fileNames) {
213
203
  const files = new Set();
214
204
  for (const fileName of fileNames) {
215
205
  files.add(fileName);
@@ -223,15 +213,12 @@ class UIMode {
223
213
  await ((_this$_testRun2 = this._testRun) === null || _this$_testRun2 === void 0 ? void 0 : _this$_testRun2.run);
224
214
  }
225
215
  }
226
- const dispatchFuncSource = String(message => {
227
- window.dispatch(message);
228
- });
229
- async function runUIMode(config) {
216
+ async function runUIMode(config, options) {
230
217
  var _uiMode$globalCleanup;
231
218
  const uiMode = new UIMode(config);
232
219
  const status = await uiMode.runGlobalSetup();
233
220
  if (status !== 'passed') return status;
234
- await uiMode.showUI();
221
+ await uiMode.showUI(options);
235
222
  return (await ((_uiMode$globalCleanup = uiMode.globalCleanup) === null || _uiMode$globalCleanup === void 0 ? void 0 : _uiMode$globalCleanup.call(uiMode))) || 'passed';
236
223
  }
237
224
  function chunkToPayload(type, chunk) {
@@ -260,7 +247,7 @@ class Watcher {
260
247
  if (JSON.stringify(this._watchedFiles) === JSON.stringify(watchedFiles)) return;
261
248
  if (reportPending) this._reportEventsIfAny();
262
249
  this._watchedFiles = watchedFiles;
263
- (_this$_fsWatcher = this._fsWatcher) === null || _this$_fsWatcher === void 0 ? void 0 : _this$_fsWatcher.close().then(() => {});
250
+ void ((_this$_fsWatcher = this._fsWatcher) === null || _this$_fsWatcher === void 0 ? void 0 : _this$_fsWatcher.close());
264
251
  this._fsWatcher = undefined;
265
252
  this._collector.length = 0;
266
253
  clearTimeout(this._throttleTimer);
package/lib/util.js CHANGED
@@ -296,13 +296,30 @@ function envWithoutExperimentalLoaderOptions() {
296
296
  // https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-beta/#moduleresolution-bundler
297
297
  const kExtLookups = new Map([['.js', ['.jsx', '.ts', '.tsx']], ['.jsx', ['.tsx']], ['.cjs', ['.cts']], ['.mjs', ['.mts']], ['', ['.js', '.ts', '.jsx', '.tsx', '.cjs', '.mjs', '.cts', '.mts']]]);
298
298
  function resolveImportSpecifierExtension(resolved) {
299
- if (_fs.default.existsSync(resolved)) return resolved;
299
+ if (fileExists(resolved)) return resolved;
300
300
  for (const [ext, others] of kExtLookups) {
301
301
  if (!resolved.endsWith(ext)) continue;
302
302
  for (const other of others) {
303
303
  const modified = resolved.substring(0, resolved.length - ext.length) + other;
304
- if (_fs.default.existsSync(modified)) return modified;
304
+ if (fileExists(modified)) return modified;
305
305
  }
306
306
  break; // Do not try '' when a more specific extesion like '.jsx' matched.
307
307
  }
308
+ // try directory imports last
309
+ if (dirExists(resolved)) {
310
+ const dirImport = _path.default.join(resolved, 'index');
311
+ return resolveImportSpecifierExtension(dirImport);
312
+ }
313
+ }
314
+ function fileExists(resolved) {
315
+ var _fs$statSync;
316
+ return (_fs$statSync = _fs.default.statSync(resolved, {
317
+ throwIfNoEntry: false
318
+ })) === null || _fs$statSync === void 0 ? void 0 : _fs$statSync.isFile();
319
+ }
320
+ function dirExists(resolved) {
321
+ var _fs$statSync2;
322
+ return (_fs$statSync2 = _fs.default.statSync(resolved, {
323
+ throwIfNoEntry: false
324
+ })) === null || _fs$statSync2 === void 0 ? void 0 : _fs$statSync2.isDirectory();
308
325
  }