@playwright/test 1.37.0-alpha-aug-3-2023 → 1.37.0-alpha-aug-5-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.
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.StringInternPool = void 0;
7
+ /**
8
+ * Copyright (c) Microsoft Corporation.
9
+ *
10
+ * Licensed under the Apache License, Version 2.0 (the "License");
11
+ * you may not use this file except in compliance with the License.
12
+ * You may obtain a copy of the License at
13
+ *
14
+ * http://www.apache.org/licenses/LICENSE-2.0
15
+ *
16
+ * Unless required by applicable law or agreed to in writing, software
17
+ * distributed under the License is distributed on an "AS IS" BASIS,
18
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ * See the License for the specific language governing permissions and
20
+ * limitations under the License.
21
+ */
22
+
23
+ class StringInternPool {
24
+ constructor() {
25
+ this._stringCache = new Map();
26
+ }
27
+ internString(s) {
28
+ let result = this._stringCache.get(s);
29
+ if (!result) {
30
+ this._stringCache.set(s, s);
31
+ result = s;
32
+ }
33
+ return result;
34
+ }
35
+ }
36
+ exports.StringInternPool = StringInternPool;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.baseFullConfig = exports.TeleTestCase = exports.TeleSuite = exports.TeleReporterReceiver = void 0;
7
7
  exports.parseRegexPatterns = parseRegexPatterns;
8
8
  exports.serializeRegexPatterns = serializeRegexPatterns;
9
+ var _stringInternPool = require("./stringInternPool");
9
10
  /**
10
11
  * Copyright (c) Microsoft Corporation.
11
12
  *
@@ -34,6 +35,7 @@ class TeleReporterReceiver {
34
35
  this._reuseTestCases = void 0;
35
36
  this._reportConfig = void 0;
36
37
  this._config = void 0;
38
+ this._stringPool = new _stringInternPool.StringInternPool();
37
39
  this._rootSuite = new TeleSuite('', 'root');
38
40
  this._pathSeparator = pathSeparator;
39
41
  this._reporter = reporter;
@@ -129,7 +131,7 @@ class TeleReporterReceiver {
129
131
  testResult.retry = payload.retry;
130
132
  testResult.workerIndex = payload.workerIndex;
131
133
  testResult.parallelIndex = payload.parallelIndex;
132
- testResult.startTime = new Date(payload.startTime);
134
+ testResult.setStartTimeNumber(payload.startTime);
133
135
  testResult.statusEx = 'running';
134
136
  (_this$_reporter$onTes = (_this$_reporter2 = this._reporter).onTestBegin) === null || _this$_reporter$onTes === void 0 ? void 0 : _this$_reporter$onTes.call(_this$_reporter2, test, testResult);
135
137
  }
@@ -147,25 +149,16 @@ class TeleReporterReceiver {
147
149
  result.error = (_result$errors = result.errors) === null || _result$errors === void 0 ? void 0 : _result$errors[0];
148
150
  result.attachments = this._parseAttachments(payload.attachments);
149
151
  (_this$_reporter$onTes2 = (_this$_reporter3 = this._reporter).onTestEnd) === null || _this$_reporter$onTes2 === void 0 ? void 0 : _this$_reporter$onTes2.call(_this$_reporter3, test, result);
152
+ // Free up the memory as won't see these step ids.
153
+ result.stepMap = new Map();
150
154
  }
151
155
  _onStepBegin(testId, resultId, payload) {
152
156
  var _this$_reporter$onSte, _this$_reporter4;
153
157
  const test = this._tests.get(testId);
154
158
  const result = test.resultsMap.get(resultId);
155
159
  const parentStep = payload.parentStepId ? result.stepMap.get(payload.parentStepId) : undefined;
156
- const step = {
157
- titlePath: () => {
158
- const parentPath = (parentStep === null || parentStep === void 0 ? void 0 : parentStep.titlePath()) || [];
159
- return [...parentPath, payload.title];
160
- },
161
- title: payload.title,
162
- category: payload.category,
163
- location: this._absoluteLocation(payload.location),
164
- parent: parentStep,
165
- startTime: new Date(payload.startTime),
166
- duration: -1,
167
- steps: []
168
- };
160
+ const location = this._absoluteLocation(payload.location);
161
+ const step = new TeleTestStep(payload, parentStep, location);
169
162
  if (parentStep) parentStep.steps.push(step);else result.steps.push(step);
170
163
  result.stepMap.set(payload.id, step);
171
164
  (_this$_reporter$onSte = (_this$_reporter4 = this._reporter).onStepBegin) === null || _this$_reporter$onSte === void 0 ? void 0 : _this$_reporter$onSte.call(_this$_reporter4, test, result, step);
@@ -203,6 +196,8 @@ class TeleReporterReceiver {
203
196
  }
204
197
  _onExit() {
205
198
  var _this$_reporter$onExi, _this$_reporter10;
199
+ // Free up the memory from the string pool.
200
+ this._stringPool = new _stringInternPool.StringInternPool();
206
201
  return (_this$_reporter$onExi = (_this$_reporter10 = this._reporter).onExit) === null || _this$_reporter$onExi === void 0 ? void 0 : _this$_reporter$onExi.call(_this$_reporter10);
207
202
  }
208
203
  _parseConfig(config) {
@@ -288,7 +283,7 @@ class TeleReporterReceiver {
288
283
  }
289
284
  _absolutePath(relativePath) {
290
285
  if (!relativePath) return relativePath;
291
- return this._rootDir + this._pathSeparator + relativePath;
286
+ return this._stringPool.internString(this._rootDir + this._pathSeparator + relativePath);
292
287
  }
293
288
  }
294
289
  exports.TeleReporterReceiver = TeleReporterReceiver;
@@ -368,27 +363,68 @@ class TeleTestCase {
368
363
  this.resultsMap.clear();
369
364
  }
370
365
  _createTestResult(id) {
371
- const result = {
372
- retry: this.results.length,
373
- parallelIndex: -1,
374
- workerIndex: -1,
375
- duration: -1,
376
- startTime: new Date(),
377
- stdout: [],
378
- stderr: [],
379
- attachments: [],
380
- status: 'skipped',
381
- statusEx: 'scheduled',
382
- steps: [],
383
- errors: [],
384
- stepMap: new Map()
385
- };
366
+ const result = new TeleTestResult(this.results.length);
386
367
  this.results.push(result);
387
368
  this.resultsMap.set(id, result);
388
369
  return result;
389
370
  }
390
371
  }
391
372
  exports.TeleTestCase = TeleTestCase;
373
+ class TeleTestStep {
374
+ constructor(payload, parentStep, location) {
375
+ this.title = void 0;
376
+ this.category = void 0;
377
+ this.location = void 0;
378
+ this.parent = void 0;
379
+ this.duration = -1;
380
+ this.steps = [];
381
+ this._startTime = 0;
382
+ this.title = payload.title;
383
+ this.category = payload.category;
384
+ this.location = location;
385
+ this.parent = parentStep;
386
+ this._startTime = payload.startTime;
387
+ }
388
+ titlePath() {
389
+ var _this$parent;
390
+ const parentPath = ((_this$parent = this.parent) === null || _this$parent === void 0 ? void 0 : _this$parent.titlePath()) || [];
391
+ return [...parentPath, this.title];
392
+ }
393
+ get startTime() {
394
+ return new Date(this._startTime);
395
+ }
396
+ set startTime(value) {
397
+ this._startTime = +value;
398
+ }
399
+ }
400
+ class TeleTestResult {
401
+ constructor(retry) {
402
+ this.retry = void 0;
403
+ this.parallelIndex = -1;
404
+ this.workerIndex = -1;
405
+ this.duration = -1;
406
+ this.stdout = [];
407
+ this.stderr = [];
408
+ this.attachments = [];
409
+ this.status = 'skipped';
410
+ this.steps = [];
411
+ this.errors = [];
412
+ this.error = void 0;
413
+ this.stepMap = new Map();
414
+ this.statusEx = 'scheduled';
415
+ this._startTime = 0;
416
+ this.retry = retry;
417
+ }
418
+ setStartTimeNumber(startTime) {
419
+ this._startTime = startTime;
420
+ }
421
+ get startTime() {
422
+ return new Date(this._startTime);
423
+ }
424
+ set startTime(value) {
425
+ this._startTime = +value;
426
+ }
427
+ }
392
428
  const baseFullConfig = {
393
429
  forbidOnly: false,
394
430
  fullyParallel: false,
@@ -76,7 +76,7 @@ class SnapshotHelper {
76
76
  let actualModifier = '';
77
77
  if (!name) {
78
78
  const fullTitleWithoutSpec = [...testInfo.titlePath.slice(1), ++snapshotNames.anonymousSnapshotIndex].join(' ');
79
- name = (0, _util.sanitizeForFilePath)((0, _util.trimLongString)(fullTitleWithoutSpec)) + '.' + anonymousSnapshotExtension;
79
+ name = (0, _utils.sanitizeForFilePath)((0, _util.trimLongString)(fullTitleWithoutSpec)) + '.' + anonymousSnapshotExtension;
80
80
  this.snapshotName = name;
81
81
  } else {
82
82
  const joinedName = Array.isArray(name) ? name.join(_path.default.sep) : name;
@@ -33,7 +33,7 @@ const DEFAULT_ENVIRONMENT_VARIABLES = {
33
33
  const debugWebServer = (0, _utilsBundle.debug)('pw:webserver');
34
34
  class WebServerPlugin {
35
35
  constructor(options, checkPortOnly) {
36
- this._isAvailable = void 0;
36
+ this._isAvailableCallback = void 0;
37
37
  this._killProcess = void 0;
38
38
  this._processExitedPromise = void 0;
39
39
  this._options = void 0;
@@ -46,7 +46,7 @@ class WebServerPlugin {
46
46
  async setup(config, configDir, reporter) {
47
47
  var _this$_reporter$onStd;
48
48
  this._reporter = reporter;
49
- this._isAvailable = 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));
49
+ 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;
50
50
  this._options.cwd = this._options.cwd ? _path.default.resolve(configDir, this._options.cwd) : configDir;
51
51
  try {
52
52
  await this._startProcess();
@@ -61,14 +61,15 @@ class WebServerPlugin {
61
61
  await ((_this$_killProcess = this._killProcess) === null || _this$_killProcess === void 0 ? void 0 : _this$_killProcess.call(this));
62
62
  }
63
63
  async _startProcess() {
64
+ var _this$_isAvailableCal;
64
65
  let processExitedReject = error => {};
65
66
  this._processExitedPromise = new Promise((_, reject) => processExitedReject = reject);
66
- const isAlreadyAvailable = await this._isAvailable();
67
+ const isAlreadyAvailable = await ((_this$_isAvailableCal = this._isAvailableCallback) === null || _this$_isAvailableCal === void 0 ? void 0 : _this$_isAvailableCal.call(this));
67
68
  if (isAlreadyAvailable) {
68
69
  var _this$_options$url;
69
70
  debugWebServer(`WebServer is already available`);
70
71
  if (this._options.reuseExistingServer) return;
71
- const port = new URL(this._options.url);
72
+ const port = new URL(this._options.url).port;
72
73
  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.`);
73
74
  }
74
75
  debugWebServer(`Starting WebServer process ${this._options.command}...`);
@@ -104,20 +105,21 @@ class WebServerPlugin {
104
105
  });
105
106
  }
106
107
  async _waitForProcess() {
108
+ if (!this._isAvailableCallback) {
109
+ this._processExitedPromise.catch(() => {});
110
+ return;
111
+ }
107
112
  debugWebServer(`Waiting for availability...`);
108
- await this._waitForAvailability();
109
- debugWebServer(`WebServer available`);
110
- }
111
- async _waitForAvailability() {
112
113
  const launchTimeout = this._options.timeout || 60 * 1000;
113
114
  const cancellationToken = {
114
115
  canceled: false
115
116
  };
116
117
  const {
117
118
  timedOut
118
- } = await Promise.race([(0, _utils.raceAgainstDeadline)(() => waitFor(this._isAvailable, cancellationToken), (0, _utils.monotonicTime)() + launchTimeout), this._processExitedPromise]);
119
+ } = await Promise.race([(0, _utils.raceAgainstDeadline)(() => waitFor(this._isAvailableCallback, cancellationToken), (0, _utils.monotonicTime)() + launchTimeout), this._processExitedPromise]);
119
120
  cancellationToken.canceled = true;
120
121
  if (timedOut) throw new Error(`Timed out waiting ${launchTimeout}ms from config.webServer.`);
122
+ debugWebServer(`WebServer available`);
121
123
  }
122
124
  }
123
125
  exports.WebServerPlugin = WebServerPlugin;
@@ -188,11 +190,14 @@ const webServerPluginsForConfig = config => {
188
190
  const shouldSetBaseUrl = !!config.config.webServer;
189
191
  const webServerPlugins = [];
190
192
  for (const webServerConfig of config.webServers) {
191
- if (!webServerConfig.port && !webServerConfig.url || webServerConfig.port && webServerConfig.url) throw new Error(`Exactly one of 'port' or 'url' is required in config.webServer.`);
192
- const url = webServerConfig.url || `http://localhost:${webServerConfig.port}`;
193
+ if (webServerConfig.port && webServerConfig.url) throw new Error(`Either 'port' or 'url' should be specified in config.webServer.`);
194
+ let url;
195
+ if (webServerConfig.port || webServerConfig.url) {
196
+ url = webServerConfig.url || `http://localhost:${webServerConfig.port}`;
193
197
 
194
- // We only set base url when only the port is given. That's a legacy mode we have regrets about.
195
- if (shouldSetBaseUrl && !webServerConfig.url) process.env.PLAYWRIGHT_TEST_BASE_URL = url;
198
+ // We only set base url when only the port is given. That's a legacy mode we have regrets about.
199
+ if (shouldSetBaseUrl && !webServerConfig.url) process.env.PLAYWRIGHT_TEST_BASE_URL = url;
200
+ }
196
201
  webServerPlugins.push(new WebServerPlugin({
197
202
  ...webServerConfig,
198
203
  url
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.BlobReporter = void 0;
6
+ exports.currentBlobReportVersion = exports.BlobReporter = void 0;
7
7
  var _fs = _interopRequireDefault(require("fs"));
8
8
  var _path = _interopRequireDefault(require("path"));
9
9
  var _utils = require("playwright-core/lib/utils");
@@ -29,7 +29,8 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
29
29
  * limitations under the License.
30
30
  */
31
31
 
32
- const currentVersion = 1;
32
+ const currentBlobReportVersion = 1;
33
+ exports.currentBlobReportVersion = currentBlobReportVersion;
33
34
  class BlobReporter extends _teleEmitter.TeleReporterEmitter {
34
35
  constructor(options) {
35
36
  super(message => this._messages.push(message), false);
@@ -37,13 +38,14 @@ class BlobReporter extends _teleEmitter.TeleReporterEmitter {
37
38
  this._attachments = [];
38
39
  this._options = void 0;
39
40
  this._salt = void 0;
41
+ this._reportName = void 0;
40
42
  this._options = options;
41
43
  this._salt = (0, _utils.createGuid)();
42
44
  }
43
45
  onConfigure(config) {
44
46
  var _config$shard;
45
47
  const metadata = {
46
- version: currentVersion,
48
+ version: currentBlobReportVersion,
47
49
  projectSuffix: process.env.PWTEST_BLOB_SUFFIX,
48
50
  shard: (_config$shard = config.shard) !== null && _config$shard !== void 0 ? _config$shard : undefined
49
51
  };
@@ -51,6 +53,7 @@ class BlobReporter extends _teleEmitter.TeleReporterEmitter {
51
53
  method: 'onBlobReportMetadata',
52
54
  params: metadata
53
55
  });
56
+ this._reportName = this._computeReportName(config);
54
57
  super.onConfigure(config);
55
58
  }
56
59
  async onEnd(result) {
@@ -60,14 +63,13 @@ class BlobReporter extends _teleEmitter.TeleReporterEmitter {
60
63
  await _fs.default.promises.mkdir(outputDir, {
61
64
  recursive: true
62
65
  });
63
- const reportName = `report-${(0, _utils.createGuid)()}`;
64
66
  const zipFile = new _zipBundle.yazl.ZipFile();
65
67
  const zipFinishPromise = new _utils.ManualPromise();
66
68
  const finishPromise = zipFinishPromise.catch(e => {
67
- throw new Error(`Failed to write report ${reportName + '.zip'}: ` + e.message);
69
+ throw new Error(`Failed to write report ${this._reportName + '.zip'}: ` + e.message);
68
70
  });
69
71
  zipFile.on('error', error => zipFinishPromise.reject(error));
70
- const zipFileName = _path.default.join(outputDir, reportName + '.zip');
72
+ const zipFileName = _path.default.join(outputDir, this._reportName + '.zip');
71
73
  zipFile.outputStream.pipe(_fs.default.createWriteStream(zipFileName)).on('close', () => {
72
74
  zipFinishPromise.resolve(undefined);
73
75
  }).on('error', error => zipFinishPromise.reject(error));
@@ -83,10 +85,19 @@ class BlobReporter extends _teleEmitter.TeleReporterEmitter {
83
85
  }
84
86
  const lines = this._messages.map(m => JSON.stringify(m) + '\n');
85
87
  const content = _stream.Readable.from(lines);
86
- zipFile.addReadStream(content, reportName + '.jsonl');
88
+ zipFile.addReadStream(content, this._reportName + '.jsonl');
87
89
  zipFile.end();
88
90
  await finishPromise;
89
91
  }
92
+ _computeReportName(config) {
93
+ let reportName = 'report';
94
+ if (process.env.PWTEST_BLOB_SUFFIX) reportName += (0, _utils.sanitizeForFilePath)(process.env.PWTEST_BLOB_SUFFIX);
95
+ if (config.shard) {
96
+ const paddedNumber = `${config.shard.current}`.padStart(`${config.shard.total}`.length, '0');
97
+ reportName += `-${paddedNumber}`;
98
+ }
99
+ return reportName;
100
+ }
90
101
  _serializeAttachments(attachments) {
91
102
  return super._serializeAttachments(attachments).map(attachment => {
92
103
  if (!attachment.path) return attachment;
@@ -401,7 +401,7 @@ class HtmlBuilder {
401
401
  _fs.default.mkdirSync(_path.default.join(this._reportFolder, 'data'), {
402
402
  recursive: true
403
403
  });
404
- const extension = (0, _util.sanitizeForFilePath)(_path.default.extname(a.name).replace(/^\./, '')) || _utilsBundle.mime.getExtension(a.contentType) || 'dat';
404
+ const extension = (0, _utils.sanitizeForFilePath)(_path.default.extname(a.name).replace(/^\./, '')) || _utilsBundle.mime.getExtension(a.contentType) || 'dat';
405
405
  const sha1 = (0, _utils.calculateSha1)(a.body) + '.' + extension;
406
406
  _fs.default.writeFileSync(_path.default.join(this._reportFolder, 'data', sha1), a.body);
407
407
  return {
@@ -7,9 +7,11 @@ exports.createMergedReport = createMergedReport;
7
7
  var _fs = _interopRequireDefault(require("fs"));
8
8
  var _path = _interopRequireDefault(require("path"));
9
9
  var _teleReceiver = require("../isomorphic/teleReceiver");
10
+ var _stringInternPool = require("../isomorphic/stringInternPool");
10
11
  var _reporters = require("../runner/reporters");
11
12
  var _multiplexer = require("./multiplexer");
12
13
  var _utils = require("playwright-core/lib/utils");
14
+ var _blob = require("./blob");
13
15
  var _util = require("../util");
14
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
17
  /**
@@ -90,7 +92,9 @@ async function extractAndParseReports(dir, shardFiles, printStatus) {
90
92
  function findMetadata(events, file) {
91
93
  var _events$;
92
94
  if (((_events$ = events[0]) === null || _events$ === void 0 ? void 0 : _events$.method) !== 'onBlobReportMetadata') throw new Error(`No metadata event found in ${file}`);
93
- return events[0].params;
95
+ const metadata = events[0].params;
96
+ if (metadata.version > _blob.currentBlobReportVersion) throw new Error(`Blob report ${file} was created with a newer version of Playwright.`);
97
+ return metadata;
94
98
  }
95
99
  async function mergeEvents(dir, shardReportFiles, printStatus) {
96
100
  const events = [];
@@ -198,7 +202,7 @@ function mergeEndEvents(endEvents) {
198
202
  }
199
203
  async function sortedShardFiles(dir) {
200
204
  const files = await _fs.default.promises.readdir(dir);
201
- return files.filter(file => file.startsWith('report-') && file.endsWith('.zip')).sort();
205
+ return files.filter(file => file.startsWith('report') && file.endsWith('.zip')).sort();
202
206
  }
203
207
  function printStatusToStdout(message) {
204
208
  process.stdout.write(`${message}\n`);
@@ -206,6 +210,7 @@ function printStatusToStdout(message) {
206
210
  class ProjectNamePatcher {
207
211
  constructor(_allTestIds, _projectNameSuffix) {
208
212
  this._testIds = new Set();
213
+ this._stringPool = new _stringInternPool.StringInternPool();
209
214
  this._allTestIds = _allTestIds;
210
215
  this._projectNameSuffix = _projectNameSuffix;
211
216
  }
@@ -263,6 +268,6 @@ class ProjectNamePatcher {
263
268
  // To avoid reporters being confused by clashing test ids, we automatically
264
269
  // make them unique and produce a separate test from each blob.
265
270
  while (this._allTestIds.has(testId)) testId = testId + '1';
266
- return testId;
271
+ return this._stringPool.internString(testId);
267
272
  }
268
273
  }
@@ -7,7 +7,6 @@ exports.default = void 0;
7
7
  var _fs = _interopRequireDefault(require("fs"));
8
8
  var _path = _interopRequireDefault(require("path"));
9
9
  var _utils = require("playwright-core/lib/utils");
10
- var _util = require("../util");
11
10
  var _base = require("./base");
12
11
  var _json = require("./json");
13
12
  var _babelBundle = require("../transform/babelBundle");
@@ -49,7 +48,7 @@ class RawReporter {
49
48
  });
50
49
  let reportFile;
51
50
  for (let i = 0; i < 10; ++i) {
52
- reportFile = _path.default.join(reportFolder, (0, _util.sanitizeForFilePath)(project.name || 'project') + (i ? '-' + i : '') + '.report');
51
+ reportFile = _path.default.join(reportFolder, (0, _utils.sanitizeForFilePath)(project.name || 'project') + (i ? '-' + i : '') + '.report');
53
52
  try {
54
53
  if (_fs.default.existsSync(reportFile)) continue;
55
54
  } catch (e) {}
@@ -203,7 +203,7 @@ class TeleReporterEmitter {
203
203
  retry: result.retry,
204
204
  workerIndex: result.workerIndex,
205
205
  parallelIndex: result.parallelIndex,
206
- startTime: result.startTime.toISOString()
206
+ startTime: +result.startTime
207
207
  };
208
208
  }
209
209
  _serializeResultEnd(result) {
@@ -231,7 +231,7 @@ class TeleReporterEmitter {
231
231
  parentStepId: (_step$parent = step.parent) === null || _step$parent === void 0 ? void 0 : _step$parent[idSymbol],
232
232
  title: step.title,
233
233
  category: step.category,
234
- startTime: step.startTime.toISOString(),
234
+ startTime: +step.startTime,
235
235
  location: this._relativeLocation(step.location)
236
236
  };
237
237
  }
package/lib/util.js CHANGED
@@ -27,7 +27,6 @@ exports.normalizeAndSaveAttachment = normalizeAndSaveAttachment;
27
27
  exports.relativeFilePath = relativeFilePath;
28
28
  exports.resolveImportSpecifierExtension = resolveImportSpecifierExtension;
29
29
  exports.resolveReporterOutputPath = resolveReporterOutputPath;
30
- exports.sanitizeForFilePath = sanitizeForFilePath;
31
30
  exports.serializeError = serializeError;
32
31
  exports.stringifyStackFrames = stringifyStackFrames;
33
32
  exports.trimLongString = trimLongString;
@@ -187,9 +186,6 @@ function expectTypes(receiver, types, matcherName) {
187
186
  throw new Error(`${matcherName} can be only used with ${typesString} object${types.length > 1 ? 's' : ''}`);
188
187
  }
189
188
  }
190
- function sanitizeForFilePath(s) {
191
- return s.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, '-');
192
- }
193
189
  function trimLongString(s, length = 100) {
194
190
  if (s.length <= length) return s;
195
191
  const hash = (0, _utils.calculateSha1)(s);
@@ -203,7 +199,7 @@ function addSuffixToFilePath(filePath, suffix, customExtension, sanitize = false
203
199
  const ext = _path.default.extname(filePath);
204
200
  const name = _path.default.basename(filePath, ext);
205
201
  const base = _path.default.join(dirname, name);
206
- return (sanitize ? sanitizeForFilePath(base) : base) + suffix + (customExtension || ext);
202
+ return (sanitize ? (0, _utils.sanitizeForFilePath)(base) : base) + suffix + (customExtension || ext);
207
203
  }
208
204
 
209
205
  /**
@@ -253,7 +249,7 @@ async function normalizeAndSaveAttachment(outputPath, name, options = {}) {
253
249
  var _options$contentType;
254
250
  const hash = (0, _utils.calculateSha1)(options.path);
255
251
  if (!(0, _utils.isString)(name)) throw new Error('"name" should be string.');
256
- const sanitizedNamePrefix = sanitizeForFilePath(name) + '-';
252
+ const sanitizedNamePrefix = (0, _utils.sanitizeForFilePath)(name) + '-';
257
253
  const dest = _path.default.join(outputPath, 'attachments', sanitizedNamePrefix + hash + _path.default.extname(options.path));
258
254
  await _fs.default.promises.mkdir(_path.default.dirname(dest), {
259
255
  recursive: true
@@ -130,8 +130,8 @@ class TestInfoImpl {
130
130
  const relativeTestFilePath = _path.default.relative(this.project.testDir, test._requireFile.replace(/\.(spec|test)\.(js|ts|mjs)$/, ''));
131
131
  const sanitizedRelativePath = relativeTestFilePath.replace(process.platform === 'win32' ? new RegExp('\\\\', 'g') : new RegExp('/', 'g'), '-');
132
132
  const fullTitleWithoutSpec = test.titlePath().slice(1).join(' ');
133
- let testOutputDir = (0, _util.trimLongString)(sanitizedRelativePath + '-' + (0, _util.sanitizeForFilePath)(fullTitleWithoutSpec));
134
- if (projectInternal.id) testOutputDir += '-' + (0, _util.sanitizeForFilePath)(projectInternal.id);
133
+ let testOutputDir = (0, _util.trimLongString)(sanitizedRelativePath + '-' + (0, _utils.sanitizeForFilePath)(fullTitleWithoutSpec));
134
+ if (projectInternal.id) testOutputDir += '-' + (0, _utils.sanitizeForFilePath)(projectInternal.id);
135
135
  if (this.retry) testOutputDir += '-retry' + this.retry;
136
136
  if (this.repeatEachIndex) testOutputDir += '-repeat' + this.repeatEachIndex;
137
137
  return _path.default.join(this.project.outputDir, testOutputDir);
@@ -364,14 +364,14 @@ class TestInfoImpl {
364
364
  }
365
365
  _fsSanitizedTestName() {
366
366
  const fullTitleWithoutSpec = this.titlePath.slice(1).join(' ');
367
- return (0, _util.sanitizeForFilePath)((0, _util.trimLongString)(fullTitleWithoutSpec));
367
+ return (0, _utils.sanitizeForFilePath)((0, _util.trimLongString)(fullTitleWithoutSpec));
368
368
  }
369
369
  snapshotPath(...pathSegments) {
370
370
  const subPath = _path.default.join(...pathSegments);
371
371
  const parsedSubPath = _path.default.parse(subPath);
372
372
  const relativeTestFilePath = _path.default.relative(this.project.testDir, this._test._requireFile);
373
373
  const parsedRelativeTestFilePath = _path.default.parse(relativeTestFilePath);
374
- const projectNamePathSegment = (0, _util.sanitizeForFilePath)(this.project.name);
374
+ const projectNamePathSegment = (0, _utils.sanitizeForFilePath)(this.project.name);
375
375
  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 : '');
376
376
  return _path.default.normalize(_path.default.resolve(this._configInternal.configDir, snapshotPath));
377
377
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playwright/test",
3
- "version": "1.37.0-alpha-aug-3-2023",
3
+ "version": "1.37.0-alpha-aug-5-2023",
4
4
  "description": "A high-level API to automate web browsers",
5
5
  "repository": "github:Microsoft/playwright",
6
6
  "homepage": "https://playwright.dev",
@@ -41,7 +41,7 @@
41
41
  "license": "Apache-2.0",
42
42
  "dependencies": {
43
43
  "@types/node": "*",
44
- "playwright-core": "1.37.0-alpha-aug-3-2023"
44
+ "playwright-core": "1.37.0-alpha-aug-5-2023"
45
45
  },
46
46
  "optionalDependencies": {
47
47
  "fsevents": "2.3.2"
package/types/test.d.ts CHANGED
@@ -6735,15 +6735,15 @@ interface TestConfigWebServer {
6735
6735
  command: string;
6736
6736
 
6737
6737
  /**
6738
- * The port that your http server is expected to appear on. It does wait until it accepts connections. Exactly one of
6739
- * `port` or `url` is required.
6738
+ * The port that your http server is expected to appear on. It does wait until it accepts connections. Either `port`
6739
+ * or `url` should be specified.
6740
6740
  */
6741
6741
  port?: number;
6742
6742
 
6743
6743
  /**
6744
6744
  * The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the
6745
6745
  * server is ready to accept connections. Redirects (3xx status codes) are being followed and the new location is
6746
- * checked. Exactly one of `port` or `url` is required.
6746
+ * checked. Either `port` or `url` should be specified.
6747
6747
  */
6748
6748
  url?: string;
6749
6749