@playwright/test 1.37.0-alpha-aug-2-2023 → 1.37.0-alpha-aug-4-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/lib/cli.js +10 -5
- package/lib/common/process.js +3 -0
- package/lib/matchers/toMatchSnapshot.js +1 -1
- package/lib/plugins/webServerPlugin.js +0 -2
- package/lib/reporters/blob.js +18 -7
- package/lib/reporters/html.js +1 -1
- package/lib/reporters/merge.js +31 -10
- package/lib/reporters/raw.js +1 -2
- package/lib/runner/processHost.js +5 -1
- package/lib/runner/reporters.js +1 -1
- package/lib/util.js +9 -17
- package/lib/worker/testInfo.js +4 -4
- package/package.json +2 -2
- package/types/test.d.ts +4 -1
- package/types/testReporter.d.ts +3 -1
package/lib/cli.js
CHANGED
|
@@ -169,7 +169,9 @@ async function mergeReports(reportDir, opts) {
|
|
|
169
169
|
const configLoader = new _configLoader.ConfigLoader();
|
|
170
170
|
const config = await (configFile ? configLoader.loadConfigFile(configFile) : configLoader.loadEmptyConfig(process.cwd()));
|
|
171
171
|
const dir = _path.default.resolve(process.cwd(), reportDir || '');
|
|
172
|
-
|
|
172
|
+
const dirStat = await _fs.default.promises.stat(dir).catch(e => null);
|
|
173
|
+
if (!dirStat) throw new Error('Directory does not exist: ' + dir);
|
|
174
|
+
if (!dirStat.isDirectory()) throw new Error(`"${dir}" is not a directory`);
|
|
173
175
|
let reporterDescriptions = resolveReporterOption(opts.reporter);
|
|
174
176
|
if (!reporterDescriptions && configFile) reporterDescriptions = config.config.reporter;
|
|
175
177
|
if (!reporterDescriptions) reporterDescriptions = [[_config.defaultReporter]];
|
|
@@ -243,15 +245,18 @@ function restartWithExperimentalTsEsm(configFile) {
|
|
|
243
245
|
if (nodeVersion < 16) return false;
|
|
244
246
|
if (!configFile) return false;
|
|
245
247
|
if (process.env.PW_DISABLE_TS_ESM) return false;
|
|
246
|
-
if (process.env.PW_TS_ESM_ON)
|
|
248
|
+
if (process.env.PW_TS_ESM_ON) {
|
|
249
|
+
// clear execArgv after restart, so that childProcess.fork in user code does not inherit our loader.
|
|
250
|
+
process.execArgv = (0, _util.execArgvWithoutExperimentalLoaderOptions)();
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
247
253
|
if (!(0, _util.fileIsModule)(configFile)) return false;
|
|
248
|
-
const NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + (0, _util.experimentalLoaderOption)();
|
|
249
254
|
const innerProcess = require('child_process').fork(require.resolve('./cli'), process.argv.slice(2), {
|
|
250
255
|
env: {
|
|
251
256
|
...process.env,
|
|
252
|
-
NODE_OPTIONS,
|
|
253
257
|
PW_TS_ESM_ON: '1'
|
|
254
|
-
}
|
|
258
|
+
},
|
|
259
|
+
execArgv: (0, _util.execArgvWithExperimentalLoaderOptions)()
|
|
255
260
|
});
|
|
256
261
|
innerProcess.on('close', code => {
|
|
257
262
|
if (code !== 0 && code !== null) (0, _utils.gracefullyProcessExitDoNotHang)(code);
|
package/lib/common/process.js
CHANGED
|
@@ -43,6 +43,9 @@ sendMessageToParent({
|
|
|
43
43
|
process.on('disconnect', gracefullyCloseAndExit);
|
|
44
44
|
process.on('SIGINT', () => {});
|
|
45
45
|
process.on('SIGTERM', () => {});
|
|
46
|
+
|
|
47
|
+
// Clear execArgv immediately, so that the user-code does not inherit our loader.
|
|
48
|
+
process.execArgv = (0, _util.execArgvWithoutExperimentalLoaderOptions)();
|
|
46
49
|
let processRunner;
|
|
47
50
|
let processName;
|
|
48
51
|
const startingEnv = {
|
|
@@ -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,
|
|
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;
|
|
@@ -8,7 +8,6 @@ var _path = _interopRequireDefault(require("path"));
|
|
|
8
8
|
var _net = _interopRequireDefault(require("net"));
|
|
9
9
|
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
|
10
10
|
var _utils = require("playwright-core/lib/utils");
|
|
11
|
-
var _util = require("../util");
|
|
12
11
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
12
|
/**
|
|
14
13
|
* Copyright (c) Microsoft Corporation.
|
|
@@ -79,7 +78,6 @@ class WebServerPlugin {
|
|
|
79
78
|
command: this._options.command,
|
|
80
79
|
env: {
|
|
81
80
|
...DEFAULT_ENVIRONMENT_VARIABLES,
|
|
82
|
-
...(0, _util.envWithoutExperimentalLoaderOptions)(),
|
|
83
81
|
...this._options.env
|
|
84
82
|
},
|
|
85
83
|
cwd: this._options.cwd,
|
package/lib/reporters/blob.js
CHANGED
|
@@ -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
|
|
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:
|
|
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 ${
|
|
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,
|
|
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,
|
|
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;
|
package/lib/reporters/html.js
CHANGED
|
@@ -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,
|
|
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 {
|
package/lib/reporters/merge.js
CHANGED
|
@@ -10,6 +10,8 @@ var _teleReceiver = require("../isomorphic/teleReceiver");
|
|
|
10
10
|
var _reporters = require("../runner/reporters");
|
|
11
11
|
var _multiplexer = require("./multiplexer");
|
|
12
12
|
var _utils = require("playwright-core/lib/utils");
|
|
13
|
+
var _blob = require("./blob");
|
|
14
|
+
var _util = require("../util");
|
|
13
15
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
14
16
|
/**
|
|
15
17
|
* Copyright (c) Microsoft Corporation.
|
|
@@ -28,13 +30,24 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
28
30
|
*/
|
|
29
31
|
|
|
30
32
|
async function createMergedReport(config, dir, reporterDescriptions) {
|
|
33
|
+
const reporters = await (0, _reporters.createReporters)(config, 'merge', reporterDescriptions);
|
|
34
|
+
const multiplexer = new _multiplexer.Multiplexer(reporters);
|
|
35
|
+
const receiver = new _teleReceiver.TeleReporterReceiver(_path.default.sep, multiplexer, false, config.config);
|
|
36
|
+
let printStatus = () => {};
|
|
37
|
+
if (!multiplexer.printsToStdio()) {
|
|
38
|
+
printStatus = printStatusToStdout;
|
|
39
|
+
printStatus(`merging reports from ${dir}`);
|
|
40
|
+
}
|
|
31
41
|
const shardFiles = await sortedShardFiles(dir);
|
|
32
42
|
if (shardFiles.length === 0) throw new Error(`No report files found in ${dir}`);
|
|
33
|
-
const events = await mergeEvents(dir, shardFiles);
|
|
43
|
+
const events = await mergeEvents(dir, shardFiles, printStatus);
|
|
34
44
|
patchAttachmentPaths(events, dir);
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
45
|
+
printStatus(`processing ${events.length} test events`);
|
|
46
|
+
for (const event of events) {
|
|
47
|
+
if (event.method === 'onEnd') printStatus(`building final report`);
|
|
48
|
+
await receiver.dispatch(event);
|
|
49
|
+
if (event.method === 'onEnd') printStatus(`finished building report`);
|
|
50
|
+
}
|
|
38
51
|
}
|
|
39
52
|
function patchAttachmentPaths(events, resourceDir) {
|
|
40
53
|
for (const event of events) {
|
|
@@ -48,13 +61,15 @@ function patchAttachmentPaths(events, resourceDir) {
|
|
|
48
61
|
function parseEvents(reportJsonl) {
|
|
49
62
|
return reportJsonl.toString().split('\n').filter(line => line.length).map(line => JSON.parse(line));
|
|
50
63
|
}
|
|
51
|
-
async function extractAndParseReports(dir, shardFiles) {
|
|
64
|
+
async function extractAndParseReports(dir, shardFiles, printStatus) {
|
|
52
65
|
const shardEvents = [];
|
|
53
66
|
await _fs.default.promises.mkdir(_path.default.join(dir, 'resources'), {
|
|
54
67
|
recursive: true
|
|
55
68
|
});
|
|
56
69
|
for (const file of shardFiles) {
|
|
57
|
-
const
|
|
70
|
+
const absolutePath = _path.default.join(dir, file);
|
|
71
|
+
printStatus(`extracting: ${(0, _util.relativeFilePath)(absolutePath)}`);
|
|
72
|
+
const zipFile = new _utils.ZipFile(absolutePath);
|
|
58
73
|
const entryNames = await zipFile.entries();
|
|
59
74
|
for (const entryName of entryNames) {
|
|
60
75
|
const content = await zipFile.read(entryName);
|
|
@@ -76,14 +91,16 @@ async function extractAndParseReports(dir, shardFiles) {
|
|
|
76
91
|
function findMetadata(events, file) {
|
|
77
92
|
var _events$;
|
|
78
93
|
if (((_events$ = events[0]) === null || _events$ === void 0 ? void 0 : _events$.method) !== 'onBlobReportMetadata') throw new Error(`No metadata event found in ${file}`);
|
|
79
|
-
|
|
94
|
+
const metadata = events[0].params;
|
|
95
|
+
if (metadata.version > _blob.currentBlobReportVersion) throw new Error(`Blob report ${file} was created with a newer version of Playwright.`);
|
|
96
|
+
return metadata;
|
|
80
97
|
}
|
|
81
|
-
async function mergeEvents(dir, shardReportFiles) {
|
|
98
|
+
async function mergeEvents(dir, shardReportFiles, printStatus) {
|
|
82
99
|
const events = [];
|
|
83
100
|
const configureEvents = [];
|
|
84
101
|
const beginEvents = [];
|
|
85
102
|
const endEvents = [];
|
|
86
|
-
const shardEvents = await extractAndParseReports(dir, shardReportFiles);
|
|
103
|
+
const shardEvents = await extractAndParseReports(dir, shardReportFiles, printStatus);
|
|
87
104
|
shardEvents.sort((a, b) => {
|
|
88
105
|
var _a$metadata$shard$cur, _a$metadata$shard, _b$metadata$shard$cur, _b$metadata$shard;
|
|
89
106
|
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;
|
|
@@ -91,6 +108,7 @@ async function mergeEvents(dir, shardReportFiles) {
|
|
|
91
108
|
return shardA - shardB;
|
|
92
109
|
});
|
|
93
110
|
const allTestIds = new Set();
|
|
111
|
+
printStatus(`merging events`);
|
|
94
112
|
for (const {
|
|
95
113
|
parsedEvents
|
|
96
114
|
} of shardEvents) {
|
|
@@ -183,7 +201,10 @@ function mergeEndEvents(endEvents) {
|
|
|
183
201
|
}
|
|
184
202
|
async function sortedShardFiles(dir) {
|
|
185
203
|
const files = await _fs.default.promises.readdir(dir);
|
|
186
|
-
return files.filter(file => file.startsWith('report
|
|
204
|
+
return files.filter(file => file.startsWith('report') && file.endsWith('.zip')).sort();
|
|
205
|
+
}
|
|
206
|
+
function printStatusToStdout(message) {
|
|
207
|
+
process.stdout.write(`${message}\n`);
|
|
187
208
|
}
|
|
188
209
|
class ProjectNamePatcher {
|
|
189
210
|
constructor(_allTestIds, _projectNameSuffix) {
|
package/lib/reporters/raw.js
CHANGED
|
@@ -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,
|
|
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) {}
|
|
@@ -7,6 +7,7 @@ exports.ProcessHost = void 0;
|
|
|
7
7
|
var _child_process = _interopRequireDefault(require("child_process"));
|
|
8
8
|
var _events = require("events");
|
|
9
9
|
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
|
10
|
+
var _util = require("../util");
|
|
10
11
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
12
|
/**
|
|
12
13
|
* Copyright Microsoft Corporation. All rights reserved.
|
|
@@ -49,7 +50,10 @@ class ProcessHost extends _events.EventEmitter {
|
|
|
49
50
|
...process.env,
|
|
50
51
|
...this._extraEnv
|
|
51
52
|
},
|
|
52
|
-
stdio: inheritStdio ? ['ignore', 'inherit', 'inherit', 'ipc'] : ['ignore', 'ignore', process.env.PW_RUNNER_DEBUG ? 'inherit' : 'ignore', 'ipc']
|
|
53
|
+
stdio: inheritStdio ? ['ignore', 'inherit', 'inherit', 'ipc'] : ['ignore', 'ignore', process.env.PW_RUNNER_DEBUG ? 'inherit' : 'ignore', 'ipc'],
|
|
54
|
+
...(process.env.PW_TS_ESM_ON ? {
|
|
55
|
+
execArgv: (0, _util.execArgvWithExperimentalLoaderOptions)()
|
|
56
|
+
} : {})
|
|
53
57
|
});
|
|
54
58
|
this.process.on('exit', (code, signal) => {
|
|
55
59
|
this.didExit = true;
|
package/lib/runner/reporters.js
CHANGED
|
@@ -72,7 +72,7 @@ async function createReporters(config, mode, descriptions) {
|
|
|
72
72
|
if (reporters.length && !someReporterPrintsToStdio) {
|
|
73
73
|
// Add a line/dot/list-mode reporter for convenience.
|
|
74
74
|
// Important to put it first, jsut in case some other reporter stalls onEnd.
|
|
75
|
-
if (mode === 'list') reporters.unshift(new ListModeReporter());else reporters.unshift(!process.env.CI ? new _line.default({
|
|
75
|
+
if (mode === 'list') reporters.unshift(new ListModeReporter());else if (mode !== 'merge') reporters.unshift(!process.env.CI ? new _line.default({
|
|
76
76
|
omitFailures: true
|
|
77
77
|
}) : new _dot.default());
|
|
78
78
|
}
|
package/lib/util.js
CHANGED
|
@@ -10,10 +10,10 @@ exports.createFileMatcher = createFileMatcher;
|
|
|
10
10
|
exports.createFileMatcherFromArguments = createFileMatcherFromArguments;
|
|
11
11
|
exports.createTitleMatcher = createTitleMatcher;
|
|
12
12
|
exports.debugTest = void 0;
|
|
13
|
-
exports.envWithoutExperimentalLoaderOptions = envWithoutExperimentalLoaderOptions;
|
|
14
13
|
exports.errorWithFile = errorWithFile;
|
|
14
|
+
exports.execArgvWithExperimentalLoaderOptions = execArgvWithExperimentalLoaderOptions;
|
|
15
|
+
exports.execArgvWithoutExperimentalLoaderOptions = execArgvWithoutExperimentalLoaderOptions;
|
|
15
16
|
exports.expectTypes = expectTypes;
|
|
16
|
-
exports.experimentalLoaderOption = experimentalLoaderOption;
|
|
17
17
|
exports.fileIsModule = fileIsModule;
|
|
18
18
|
exports.filterStackFile = filterStackFile;
|
|
19
19
|
exports.filterStackTrace = filterStackTrace;
|
|
@@ -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
|
|
@@ -287,16 +283,12 @@ function folderIsModule(folder) {
|
|
|
287
283
|
// Rely on `require` internal caching logic.
|
|
288
284
|
return require(packageJsonPath).type === 'module';
|
|
289
285
|
}
|
|
290
|
-
|
|
291
|
-
|
|
286
|
+
const kExperimentalLoaderOptions = ['--no-warnings', `--experimental-loader=${_url.default.pathToFileURL(require.resolve('@playwright/test/lib/transform/esmLoader')).toString()}`];
|
|
287
|
+
function execArgvWithExperimentalLoaderOptions() {
|
|
288
|
+
return [...process.execArgv, ...kExperimentalLoaderOptions];
|
|
292
289
|
}
|
|
293
|
-
function
|
|
294
|
-
|
|
295
|
-
const result = {
|
|
296
|
-
...process.env
|
|
297
|
-
};
|
|
298
|
-
if (result.NODE_OPTIONS) result.NODE_OPTIONS = result.NODE_OPTIONS.replace(substring, '').trim() || undefined;
|
|
299
|
-
return result;
|
|
290
|
+
function execArgvWithoutExperimentalLoaderOptions() {
|
|
291
|
+
return process.execArgv.filter(arg => !kExperimentalLoaderOptions.includes(arg));
|
|
300
292
|
}
|
|
301
293
|
|
|
302
294
|
// This follows the --moduleResolution=bundler strategy from tsc.
|
package/lib/worker/testInfo.js
CHANGED
|
@@ -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,
|
|
134
|
-
if (projectInternal.id) testOutputDir += '-' + (0,
|
|
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,
|
|
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,
|
|
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
|
+
"version": "1.37.0-alpha-aug-4-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-
|
|
44
|
+
"playwright-core": "1.37.0-alpha-aug-4-2023"
|
|
45
45
|
},
|
|
46
46
|
"optionalDependencies": {
|
|
47
47
|
"fsevents": "2.3.2"
|
package/types/test.d.ts
CHANGED
|
@@ -3351,7 +3351,10 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
|
|
|
3351
3351
|
* ```js
|
|
3352
3352
|
* test('example test', async ({ page }) => {
|
|
3353
3353
|
* // ...
|
|
3354
|
-
* await test.info().attach('screenshot', {
|
|
3354
|
+
* await test.info().attach('screenshot', {
|
|
3355
|
+
* body: await page.screenshot(),
|
|
3356
|
+
* contentType: 'image/png',
|
|
3357
|
+
* });
|
|
3355
3358
|
* });
|
|
3356
3359
|
* ```
|
|
3357
3360
|
*
|
package/types/testReporter.d.ts
CHANGED
|
@@ -316,7 +316,9 @@ export interface FullResult {
|
|
|
316
316
|
*
|
|
317
317
|
* ```js
|
|
318
318
|
* // my-awesome-reporter.ts
|
|
319
|
-
* import type {
|
|
319
|
+
* import type {
|
|
320
|
+
* Reporter, FullConfig, Suite, TestCase, TestResult, FullResult
|
|
321
|
+
* } from '@playwright/test/reporter';
|
|
320
322
|
*
|
|
321
323
|
* class MyReporter implements Reporter {
|
|
322
324
|
* constructor(options: { customOption?: string } = {}) {
|