@argos-ci/playwright 3.1.1-alpha.0 → 3.3.0
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/dist/reporter.d.ts +34 -4
- package/dist/reporter.mjs +88 -40
- package/package.json +4 -4
package/dist/reporter.d.ts
CHANGED
|
@@ -1,15 +1,39 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { FullConfig, FullResult, Reporter, Suite, TestCase, TestResult } from "@playwright/test/reporter";
|
|
3
3
|
import { UploadParameters } from "@argos-ci/core";
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Dynamic build name.
|
|
6
|
+
* We require all values in order to ensure it works correctly in parallel mode.
|
|
7
|
+
*/
|
|
8
|
+
type DynamicBuildName<T extends readonly string[]> = {
|
|
9
|
+
/**
|
|
10
|
+
* The values that the build name can take.
|
|
11
|
+
* It is required to ensure Argos will always upload
|
|
12
|
+
* for each build name in order to work in sharding mode.
|
|
13
|
+
*/
|
|
14
|
+
values: readonly [...T];
|
|
15
|
+
/**
|
|
16
|
+
* Get the build name for a test case.
|
|
17
|
+
* Returns any of the values in `values`.
|
|
18
|
+
*/
|
|
19
|
+
get: (test: TestCase) => T[number];
|
|
20
|
+
};
|
|
21
|
+
type ArgosReporterOptions<T extends string[] = string[]> = Omit<UploadParameters, "files" | "root" | "buildName"> & {
|
|
5
22
|
/**
|
|
6
23
|
* Upload the report to Argos.
|
|
7
24
|
* @default true
|
|
8
25
|
*/
|
|
9
26
|
uploadToArgos?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* The name of the build in Argos.
|
|
29
|
+
* Can be a string or a function that receives the test case and returns the build name.
|
|
30
|
+
*/
|
|
31
|
+
buildName?: string | DynamicBuildName<T> | null;
|
|
10
32
|
};
|
|
33
|
+
declare function createArgosReporterOptions<T extends string[]>(options: ArgosReporterOptions<T>): ArgosReporterOptions<T>;
|
|
11
34
|
declare class ArgosReporter implements Reporter {
|
|
12
|
-
|
|
35
|
+
rootUploadDirectoryPromise: null | Promise<string>;
|
|
36
|
+
uploadDirectoryPromises: Map<string, Promise<string>>;
|
|
13
37
|
config: ArgosReporterOptions;
|
|
14
38
|
playwrightConfig: FullConfig;
|
|
15
39
|
uploadToArgos: boolean;
|
|
@@ -36,11 +60,17 @@ declare class ArgosReporter implements Reporter {
|
|
|
36
60
|
*/
|
|
37
61
|
copyTraceIfFound(result: TestResult, path: string): Promise<void>;
|
|
38
62
|
getAutomaticScreenshotName(test: TestCase, result: TestResult): string;
|
|
39
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Get the root upload directory (cached).
|
|
65
|
+
*/
|
|
66
|
+
/**
|
|
67
|
+
* Get the root upload directory (cached).
|
|
68
|
+
*/
|
|
69
|
+
getRootUploadDirectory(): Promise<string>;
|
|
40
70
|
onBegin(config: FullConfig, _suite: Suite): void;
|
|
41
71
|
onTestEnd(test: TestCase, result: TestResult): Promise<void>;
|
|
42
72
|
onEnd(_result: FullResult): Promise<{
|
|
43
73
|
status: "failed";
|
|
44
74
|
} | undefined>;
|
|
45
75
|
}
|
|
46
|
-
export { ArgosReporter as default, ArgosReporterOptions };
|
|
76
|
+
export { ArgosReporter as default, ArgosReporterOptions, createArgosReporterOptions };
|
package/dist/reporter.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { upload, readConfig } from '@argos-ci/core';
|
|
3
3
|
import { randomBytes } from 'node:crypto';
|
|
4
|
-
import {
|
|
4
|
+
import { writeFile, copyFile, readdir, mkdir } from 'node:fs/promises';
|
|
5
5
|
import { tmpdir } from 'node:os';
|
|
6
6
|
import { relative, dirname, join } from 'node:path';
|
|
7
7
|
import { getGitRepositoryPath, readVersionFromPackage } from '@argos-ci/util';
|
|
@@ -107,16 +107,38 @@ async function getMetadataFromTestCase(testCase, testResult) {
|
|
|
107
107
|
const KEY = "@argos-ci/playwright";
|
|
108
108
|
const debug = createDebug(KEY);
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
const createDirectoryPromises = new Map();
|
|
111
|
+
/**
|
|
112
|
+
* Create a directory if it doesn't exist.
|
|
113
|
+
*/ async function createDirectory(pathname) {
|
|
114
|
+
let promise = createDirectoryPromises.get(pathname);
|
|
115
|
+
if (promise) {
|
|
116
|
+
return promise;
|
|
117
|
+
}
|
|
118
|
+
promise = mkdir(pathname, {
|
|
119
|
+
recursive: true
|
|
120
|
+
}).then(()=>{});
|
|
121
|
+
createDirectoryPromises.set(pathname, promise);
|
|
122
|
+
return promise;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create temporary directory.
|
|
126
|
+
*/ async function createTemporaryDirectory() {
|
|
127
|
+
debug("Creating temporary directory");
|
|
112
128
|
const osTmpDirectory = tmpdir();
|
|
113
129
|
const path = join(osTmpDirectory, "argos." + randomBytes(16).toString("hex"));
|
|
114
|
-
await
|
|
115
|
-
recursive: true
|
|
116
|
-
});
|
|
130
|
+
await createDirectory(path);
|
|
117
131
|
debug(`Temporary directory created: ${path}`);
|
|
118
132
|
return path;
|
|
119
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Check if the build name is dynamic.
|
|
136
|
+
*/ function checkIsDynamicBuildName(buildName) {
|
|
137
|
+
return Boolean(typeof buildName === "object" && buildName);
|
|
138
|
+
}
|
|
139
|
+
function createArgosReporterOptions(options) {
|
|
140
|
+
return options;
|
|
141
|
+
}
|
|
120
142
|
async function getParallelFromConfig(config) {
|
|
121
143
|
if (!config.shard) return null;
|
|
122
144
|
if (config.shard.total === 1) return null;
|
|
@@ -131,25 +153,21 @@ async function getParallelFromConfig(config) {
|
|
|
131
153
|
};
|
|
132
154
|
}
|
|
133
155
|
class ArgosReporter {
|
|
134
|
-
|
|
156
|
+
rootUploadDirectoryPromise;
|
|
157
|
+
uploadDirectoryPromises;
|
|
135
158
|
config;
|
|
136
159
|
playwrightConfig;
|
|
137
160
|
uploadToArgos;
|
|
138
161
|
constructor(config){
|
|
139
162
|
this.config = config;
|
|
140
163
|
this.uploadToArgos = config.uploadToArgos ?? true;
|
|
141
|
-
this.
|
|
164
|
+
this.rootUploadDirectoryPromise = null;
|
|
165
|
+
this.uploadDirectoryPromises = new Map();
|
|
142
166
|
}
|
|
143
167
|
/**
|
|
144
168
|
* Write a file to the temporary directory.
|
|
145
169
|
*/ async writeFile(path, body) {
|
|
146
|
-
|
|
147
|
-
const dir = dirname(path);
|
|
148
|
-
if (dir !== uploadDir) {
|
|
149
|
-
await mkdir(dir, {
|
|
150
|
-
recursive: true
|
|
151
|
-
});
|
|
152
|
-
}
|
|
170
|
+
await createDirectory(dirname(path));
|
|
153
171
|
debug(`Writing file to ${path}`);
|
|
154
172
|
await writeFile(path, body);
|
|
155
173
|
debug(`File written to ${path}`);
|
|
@@ -157,13 +175,7 @@ class ArgosReporter {
|
|
|
157
175
|
/**
|
|
158
176
|
* Copy a file to the temporary directory.
|
|
159
177
|
*/ async copyFile(from, to) {
|
|
160
|
-
|
|
161
|
-
const dir = dirname(to);
|
|
162
|
-
if (dir !== uploadDir) {
|
|
163
|
-
await mkdir(dir, {
|
|
164
|
-
recursive: true
|
|
165
|
-
});
|
|
166
|
-
}
|
|
178
|
+
await createDirectory(dirname(to));
|
|
167
179
|
debug(`Copying file from ${from} to ${to}`);
|
|
168
180
|
await copyFile(from, to);
|
|
169
181
|
debug(`File copied from ${from} to ${to}`);
|
|
@@ -182,18 +194,25 @@ class ArgosReporter {
|
|
|
182
194
|
name += result.status === "failed" || result.status === "timedOut" ? " (failed)" : "";
|
|
183
195
|
return name;
|
|
184
196
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Get the root upload directory (cached).
|
|
199
|
+
*/ getRootUploadDirectory() {
|
|
200
|
+
if (!this.rootUploadDirectoryPromise) {
|
|
201
|
+
this.rootUploadDirectoryPromise = createTemporaryDirectory();
|
|
188
202
|
}
|
|
189
|
-
return this.
|
|
203
|
+
return this.rootUploadDirectoryPromise;
|
|
190
204
|
}
|
|
191
205
|
onBegin(config, _suite) {
|
|
192
206
|
debug("ArgosReporter:onBegin");
|
|
193
207
|
this.playwrightConfig = config;
|
|
194
208
|
}
|
|
195
209
|
async onTestEnd(test, result) {
|
|
196
|
-
const
|
|
210
|
+
const buildName = checkIsDynamicBuildName(this.config.buildName) ? this.config.buildName.get(test) : this.config.buildName;
|
|
211
|
+
if (buildName === "") {
|
|
212
|
+
throw new Error('Argos "buildName" cannot be an empty string.');
|
|
213
|
+
}
|
|
214
|
+
const rootUploadDir = await this.getRootUploadDirectory();
|
|
215
|
+
const uploadDir = buildName ? join(rootUploadDir, buildName) : rootUploadDir;
|
|
197
216
|
debug("ArgosReporter:onTestEnd");
|
|
198
217
|
await Promise.all(result.attachments.map(async (attachment)=>{
|
|
199
218
|
if (checkIsArgosScreenshot(attachment) || checkIsArgosScreenshotMetadata(attachment)) {
|
|
@@ -220,10 +239,10 @@ class ArgosReporter {
|
|
|
220
239
|
}
|
|
221
240
|
async onEnd(_result) {
|
|
222
241
|
debug("ArgosReporter:onEnd");
|
|
223
|
-
const
|
|
242
|
+
const rootUploadDir = await this.getRootUploadDirectory();
|
|
224
243
|
if (!this.uploadToArgos) {
|
|
225
244
|
debug("Not uploading to Argos because uploadToArgos is false.");
|
|
226
|
-
debug(`Upload directory: ${
|
|
245
|
+
debug(`Upload directory: ${rootUploadDir}`);
|
|
227
246
|
return;
|
|
228
247
|
}
|
|
229
248
|
debug("Getting parallel from config");
|
|
@@ -233,17 +252,46 @@ class ArgosReporter {
|
|
|
233
252
|
} else {
|
|
234
253
|
debug("Non-parallel mode");
|
|
235
254
|
}
|
|
255
|
+
const buildNameConfig = this.config.buildName;
|
|
256
|
+
const uploadOptions = {
|
|
257
|
+
files: [
|
|
258
|
+
"**/*.png"
|
|
259
|
+
],
|
|
260
|
+
parallel: parallel ?? undefined,
|
|
261
|
+
...this.config
|
|
262
|
+
};
|
|
236
263
|
try {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
264
|
+
if (checkIsDynamicBuildName(buildNameConfig)) {
|
|
265
|
+
debug(`Dynamic build names, uploading to Argos for each build name: ${buildNameConfig.values.join()}`);
|
|
266
|
+
const directories = await readdir(rootUploadDir);
|
|
267
|
+
// Check if the buildName.values are consistent with the directories created
|
|
268
|
+
if (directories.some((dir)=>!buildNameConfig.values.includes(dir))) {
|
|
269
|
+
throw new Error(`The \`buildName.values\` (${buildNameConfig.values.join(", ")}) are inconsistent with the \`buildName.get\` returns values (${directories.join(", ")}). Please fix the configuration.`);
|
|
270
|
+
}
|
|
271
|
+
// In non-parallel mode, we iterate over the directories to avoid creating useless builds
|
|
272
|
+
const iteratesOnBuildNames = parallel ? buildNameConfig.values : directories;
|
|
273
|
+
// Iterate over each build name and upload the screenshots
|
|
274
|
+
for (const buildName of iteratesOnBuildNames){
|
|
275
|
+
const uploadDir = join(rootUploadDir, buildName);
|
|
276
|
+
await createDirectory(uploadDir);
|
|
277
|
+
debug(`Uploading to Argos for build: ${buildName}`);
|
|
278
|
+
const res = await upload({
|
|
279
|
+
...uploadOptions,
|
|
280
|
+
root: uploadDir,
|
|
281
|
+
buildName
|
|
282
|
+
});
|
|
283
|
+
console.log(chalk.green(`✅ Argos "${buildName}" build created: ${res.build.url}`));
|
|
284
|
+
}
|
|
285
|
+
} else {
|
|
286
|
+
debug("Uploading to Argos");
|
|
287
|
+
const uploadDir = buildNameConfig ? join(rootUploadDir, buildNameConfig) : rootUploadDir;
|
|
288
|
+
const res = await upload({
|
|
289
|
+
...uploadOptions,
|
|
290
|
+
root: uploadDir,
|
|
291
|
+
buildName: buildNameConfig ?? undefined
|
|
292
|
+
});
|
|
293
|
+
console.log(chalk.green(`✅ Argos build created: ${res.build.url}`));
|
|
294
|
+
}
|
|
247
295
|
} catch (error) {
|
|
248
296
|
console.error(error);
|
|
249
297
|
return {
|
|
@@ -254,4 +302,4 @@ class ArgosReporter {
|
|
|
254
302
|
}
|
|
255
303
|
}
|
|
256
304
|
|
|
257
|
-
export { ArgosReporter as default };
|
|
305
|
+
export { createArgosReporterOptions, ArgosReporter as default };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@argos-ci/playwright",
|
|
3
3
|
"description": "Visual testing solution to avoid visual regression. Playwright commands and utilities for Argos visual testing.",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.3.0",
|
|
5
5
|
"author": "Smooth Code",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -43,13 +43,13 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@argos-ci/browser": "2.1.2",
|
|
46
|
-
"@argos-ci/core": "2.
|
|
46
|
+
"@argos-ci/core": "2.3.0",
|
|
47
47
|
"@argos-ci/util": "2.0.0",
|
|
48
48
|
"chalk": "^5.3.0",
|
|
49
49
|
"debug": "^4.3.4"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@argos-ci/cli": "2.
|
|
52
|
+
"@argos-ci/cli": "2.2.0",
|
|
53
53
|
"@argos-ci/playwright": "workspace:.",
|
|
54
54
|
"@playwright/test": "^1.43.0",
|
|
55
55
|
"@types/debug": "^4.1.12",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"test": "pnpm exec -- playwright test",
|
|
62
62
|
"e2e": "UPLOAD_TO_ARGOS=true pnpm run test"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "5c09f1d861630af0d98200e52a0f44876bb7f891"
|
|
65
65
|
}
|