@playwright/test 1.37.0-alpha-aug-1-2023 → 1.37.0-alpha-aug-3-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 +3 -1
- package/lib/reporters/blob.js +34 -33
- package/lib/reporters/merge.js +44 -21
- package/lib/runner/reporters.js +1 -1
- 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]];
|
package/lib/reporters/blob.js
CHANGED
|
@@ -29,28 +29,23 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
29
29
|
* limitations under the License.
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
|
+
const currentVersion = 1;
|
|
32
33
|
class BlobReporter extends _teleEmitter.TeleReporterEmitter {
|
|
33
34
|
constructor(options) {
|
|
34
35
|
super(message => this._messages.push(message), false);
|
|
35
36
|
this._messages = [];
|
|
37
|
+
this._attachments = [];
|
|
36
38
|
this._options = void 0;
|
|
37
39
|
this._salt = void 0;
|
|
38
|
-
this._copyFilePromises = new Set();
|
|
39
|
-
this._outputDir = void 0;
|
|
40
|
-
this._reportName = void 0;
|
|
41
40
|
this._options = options;
|
|
42
41
|
this._salt = (0, _utils.createGuid)();
|
|
43
42
|
}
|
|
44
43
|
onConfigure(config) {
|
|
45
|
-
|
|
46
|
-
const removePromise = process.env.PWTEST_BLOB_DO_NOT_REMOVE ? Promise.resolve() : (0, _utils.removeFolders)([outputDir]);
|
|
47
|
-
this._outputDir = removePromise.then(() => _fs.default.promises.mkdir(_path.default.join(outputDir, 'resources'), {
|
|
48
|
-
recursive: true
|
|
49
|
-
})).then(() => outputDir);
|
|
50
|
-
this._reportName = `report-${(0, _utils.createGuid)()}`;
|
|
44
|
+
var _config$shard;
|
|
51
45
|
const metadata = {
|
|
46
|
+
version: currentVersion,
|
|
52
47
|
projectSuffix: process.env.PWTEST_BLOB_SUFFIX,
|
|
53
|
-
shard: config.shard ?
|
|
48
|
+
shard: (_config$shard = config.shard) !== null && _config$shard !== void 0 ? _config$shard : undefined
|
|
54
49
|
};
|
|
55
50
|
this._messages.push({
|
|
56
51
|
method: 'onBlobReportMetadata',
|
|
@@ -60,48 +55,54 @@ class BlobReporter extends _teleEmitter.TeleReporterEmitter {
|
|
|
60
55
|
}
|
|
61
56
|
async onEnd(result) {
|
|
62
57
|
await super.onEnd(result);
|
|
63
|
-
const outputDir =
|
|
64
|
-
|
|
65
|
-
|
|
58
|
+
const outputDir = (0, _util.resolveReporterOutputPath)('blob-report', this._options.configDir, this._options.outputDir);
|
|
59
|
+
if (!process.env.PWTEST_BLOB_DO_NOT_REMOVE) await (0, _utils.removeFolders)([outputDir]);
|
|
60
|
+
await _fs.default.promises.mkdir(outputDir, {
|
|
61
|
+
recursive: true
|
|
62
|
+
});
|
|
63
|
+
const reportName = `report-${(0, _utils.createGuid)()}`;
|
|
66
64
|
const zipFile = new _zipBundle.yazl.ZipFile();
|
|
67
65
|
const zipFinishPromise = new _utils.ManualPromise();
|
|
66
|
+
const finishPromise = zipFinishPromise.catch(e => {
|
|
67
|
+
throw new Error(`Failed to write report ${reportName + '.zip'}: ` + e.message);
|
|
68
|
+
});
|
|
68
69
|
zipFile.on('error', error => zipFinishPromise.reject(error));
|
|
69
|
-
const zipFileName = _path.default.join(outputDir,
|
|
70
|
+
const zipFileName = _path.default.join(outputDir, reportName + '.zip');
|
|
70
71
|
zipFile.outputStream.pipe(_fs.default.createWriteStream(zipFileName)).on('close', () => {
|
|
71
72
|
zipFinishPromise.resolve(undefined);
|
|
72
73
|
}).on('error', error => zipFinishPromise.reject(error));
|
|
73
|
-
|
|
74
|
+
for (const {
|
|
75
|
+
originalPath,
|
|
76
|
+
zipEntryPath
|
|
77
|
+
} of this._attachments) {
|
|
78
|
+
var _fs$statSync;
|
|
79
|
+
if (!((_fs$statSync = _fs.default.statSync(originalPath, {
|
|
80
|
+
throwIfNoEntry: false
|
|
81
|
+
})) !== null && _fs$statSync !== void 0 && _fs$statSync.isFile())) continue;
|
|
82
|
+
zipFile.addFile(originalPath, zipEntryPath);
|
|
83
|
+
}
|
|
84
|
+
const lines = this._messages.map(m => JSON.stringify(m) + '\n');
|
|
85
|
+
const content = _stream.Readable.from(lines);
|
|
86
|
+
zipFile.addReadStream(content, reportName + '.jsonl');
|
|
74
87
|
zipFile.end();
|
|
75
|
-
await
|
|
76
|
-
// Requires Node v14.18.0+
|
|
77
|
-
zipFinishPromise.catch(e => {
|
|
78
|
-
throw new Error(`Failed to write report ${zipFileName}: ` + e.message);
|
|
79
|
-
})]);
|
|
88
|
+
await finishPromise;
|
|
80
89
|
}
|
|
81
90
|
_serializeAttachments(attachments) {
|
|
82
91
|
return super._serializeAttachments(attachments).map(attachment => {
|
|
83
|
-
|
|
84
|
-
if (!attachment.path || !((_fs$statSync = _fs.default.statSync(attachment.path, {
|
|
85
|
-
throwIfNoEntry: false
|
|
86
|
-
})) !== null && _fs$statSync !== void 0 && _fs$statSync.isFile())) return attachment;
|
|
92
|
+
if (!attachment.path) return attachment;
|
|
87
93
|
// Add run guid to avoid clashes between shards.
|
|
88
94
|
const sha1 = (0, _utils.calculateSha1)(attachment.path + this._salt);
|
|
89
95
|
const extension = _utilsBundle.mime.getExtension(attachment.contentType) || 'dat';
|
|
90
96
|
const newPath = `resources/${sha1}.${extension}`;
|
|
91
|
-
this.
|
|
97
|
+
this._attachments.push({
|
|
98
|
+
originalPath: attachment.path,
|
|
99
|
+
zipEntryPath: newPath
|
|
100
|
+
});
|
|
92
101
|
return {
|
|
93
102
|
...attachment,
|
|
94
103
|
path: newPath
|
|
95
104
|
};
|
|
96
105
|
});
|
|
97
106
|
}
|
|
98
|
-
_startCopyingFile(from, to) {
|
|
99
|
-
const copyPromise = this._outputDir.then(dir => _fs.default.promises.copyFile(from, _path.default.join(dir, to))).catch(e => {
|
|
100
|
-
console.error(`Failed to copy file from "${from}" to "${to}": ${e}`);
|
|
101
|
-
}).then(() => {
|
|
102
|
-
this._copyFilePromises.delete(copyPromise);
|
|
103
|
-
});
|
|
104
|
-
this._copyFilePromises.add(copyPromise);
|
|
105
|
-
}
|
|
106
107
|
}
|
|
107
108
|
exports.BlobReporter = BlobReporter;
|
package/lib/reporters/merge.js
CHANGED
|
@@ -10,6 +10,7 @@ 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 _util = require("../util");
|
|
13
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
14
15
|
/**
|
|
15
16
|
* Copyright (c) Microsoft Corporation.
|
|
@@ -28,13 +29,24 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
28
29
|
*/
|
|
29
30
|
|
|
30
31
|
async function createMergedReport(config, dir, reporterDescriptions) {
|
|
32
|
+
const reporters = await (0, _reporters.createReporters)(config, 'merge', reporterDescriptions);
|
|
33
|
+
const multiplexer = new _multiplexer.Multiplexer(reporters);
|
|
34
|
+
const receiver = new _teleReceiver.TeleReporterReceiver(_path.default.sep, multiplexer, false, config.config);
|
|
35
|
+
let printStatus = () => {};
|
|
36
|
+
if (!multiplexer.printsToStdio()) {
|
|
37
|
+
printStatus = printStatusToStdout;
|
|
38
|
+
printStatus(`merging reports from ${dir}`);
|
|
39
|
+
}
|
|
31
40
|
const shardFiles = await sortedShardFiles(dir);
|
|
32
41
|
if (shardFiles.length === 0) throw new Error(`No report files found in ${dir}`);
|
|
33
|
-
const events = await mergeEvents(dir, shardFiles);
|
|
42
|
+
const events = await mergeEvents(dir, shardFiles, printStatus);
|
|
34
43
|
patchAttachmentPaths(events, dir);
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
44
|
+
printStatus(`processing ${events.length} test events`);
|
|
45
|
+
for (const event of events) {
|
|
46
|
+
if (event.method === 'onEnd') printStatus(`building final report`);
|
|
47
|
+
await receiver.dispatch(event);
|
|
48
|
+
if (event.method === 'onEnd') printStatus(`finished building report`);
|
|
49
|
+
}
|
|
38
50
|
}
|
|
39
51
|
function patchAttachmentPaths(events, resourceDir) {
|
|
40
52
|
for (const event of events) {
|
|
@@ -48,37 +60,44 @@ function patchAttachmentPaths(events, resourceDir) {
|
|
|
48
60
|
function parseEvents(reportJsonl) {
|
|
49
61
|
return reportJsonl.toString().split('\n').filter(line => line.length).map(line => JSON.parse(line));
|
|
50
62
|
}
|
|
51
|
-
async function
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
async function extractAndParseReports(dir, shardFiles, printStatus) {
|
|
64
|
+
const shardEvents = [];
|
|
65
|
+
await _fs.default.promises.mkdir(_path.default.join(dir, 'resources'), {
|
|
66
|
+
recursive: true
|
|
67
|
+
});
|
|
68
|
+
for (const file of shardFiles) {
|
|
69
|
+
const absolutePath = _path.default.join(dir, file);
|
|
70
|
+
printStatus(`extracting: ${(0, _util.relativeFilePath)(absolutePath)}`);
|
|
71
|
+
const zipFile = new _utils.ZipFile(absolutePath);
|
|
72
|
+
const entryNames = await zipFile.entries();
|
|
55
73
|
for (const entryName of entryNames) {
|
|
56
|
-
|
|
74
|
+
const content = await zipFile.read(entryName);
|
|
75
|
+
if (entryName.endsWith('.jsonl')) {
|
|
76
|
+
const parsedEvents = parseEvents(content);
|
|
77
|
+
shardEvents.push({
|
|
78
|
+
metadata: findMetadata(parsedEvents, file),
|
|
79
|
+
parsedEvents
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
const fileName = _path.default.join(dir, entryName);
|
|
83
|
+
await _fs.default.promises.writeFile(fileName, content);
|
|
84
|
+
}
|
|
57
85
|
}
|
|
58
|
-
} finally {
|
|
59
86
|
zipFile.close();
|
|
60
87
|
}
|
|
61
|
-
|
|
88
|
+
return shardEvents;
|
|
62
89
|
}
|
|
63
90
|
function findMetadata(events, file) {
|
|
64
91
|
var _events$;
|
|
65
92
|
if (((_events$ = events[0]) === null || _events$ === void 0 ? void 0 : _events$.method) !== 'onBlobReportMetadata') throw new Error(`No metadata event found in ${file}`);
|
|
66
93
|
return events[0].params;
|
|
67
94
|
}
|
|
68
|
-
async function mergeEvents(dir, shardReportFiles) {
|
|
95
|
+
async function mergeEvents(dir, shardReportFiles, printStatus) {
|
|
69
96
|
const events = [];
|
|
70
97
|
const configureEvents = [];
|
|
71
98
|
const beginEvents = [];
|
|
72
99
|
const endEvents = [];
|
|
73
|
-
const shardEvents =
|
|
74
|
-
for (const reportFile of shardReportFiles) {
|
|
75
|
-
const reportJsonl = await extractReportFromZip(_path.default.join(dir, reportFile));
|
|
76
|
-
const parsedEvents = parseEvents(reportJsonl);
|
|
77
|
-
shardEvents.push({
|
|
78
|
-
metadata: findMetadata(parsedEvents, reportFile),
|
|
79
|
-
parsedEvents
|
|
80
|
-
});
|
|
81
|
-
}
|
|
100
|
+
const shardEvents = await extractAndParseReports(dir, shardReportFiles, printStatus);
|
|
82
101
|
shardEvents.sort((a, b) => {
|
|
83
102
|
var _a$metadata$shard$cur, _a$metadata$shard, _b$metadata$shard$cur, _b$metadata$shard;
|
|
84
103
|
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;
|
|
@@ -86,6 +105,7 @@ async function mergeEvents(dir, shardReportFiles) {
|
|
|
86
105
|
return shardA - shardB;
|
|
87
106
|
});
|
|
88
107
|
const allTestIds = new Set();
|
|
108
|
+
printStatus(`merging events`);
|
|
89
109
|
for (const {
|
|
90
110
|
parsedEvents
|
|
91
111
|
} of shardEvents) {
|
|
@@ -180,6 +200,9 @@ async function sortedShardFiles(dir) {
|
|
|
180
200
|
const files = await _fs.default.promises.readdir(dir);
|
|
181
201
|
return files.filter(file => file.startsWith('report-') && file.endsWith('.zip')).sort();
|
|
182
202
|
}
|
|
203
|
+
function printStatusToStdout(message) {
|
|
204
|
+
process.stdout.write(`${message}\n`);
|
|
205
|
+
}
|
|
183
206
|
class ProjectNamePatcher {
|
|
184
207
|
constructor(_allTestIds, _projectNameSuffix) {
|
|
185
208
|
this._testIds = new Set();
|
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/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-3-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-3-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 } = {}) {
|