@bigbinary/neeto-playwright-reporter 3.0.0-beta.1 → 3.0.0-beta.3
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/bin/extractReporterConfig.mjs +95 -0
- package/bin/neetoplaydash.mjs +106 -14
- package/index.cjs.js +1 -1
- package/index.cjs.js.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { pathToFileURL } from "url";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
|
|
7
|
+
const NEETO_REPORTER_NAME = "@bigbinary/neeto-playwright-reporter";
|
|
8
|
+
|
|
9
|
+
const findPlaywrightConfig = cwd => {
|
|
10
|
+
const configNames = [
|
|
11
|
+
"playwright.config.ts",
|
|
12
|
+
"playwright.config.js",
|
|
13
|
+
"playwright.config.mjs",
|
|
14
|
+
"playwright.config.mts",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
for (const name of configNames) {
|
|
18
|
+
const configPath = path.resolve(cwd, name);
|
|
19
|
+
if (fs.existsSync(configPath)) {
|
|
20
|
+
return configPath;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return null;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const extractFromReporterArray = reporters => {
|
|
28
|
+
if (!reporters) return null;
|
|
29
|
+
|
|
30
|
+
if (typeof reporters === "string") {
|
|
31
|
+
return reporters === NEETO_REPORTER_NAME ? {} : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (Array.isArray(reporters)) {
|
|
35
|
+
for (const reporter of reporters) {
|
|
36
|
+
if (typeof reporter === "string" && reporter === NEETO_REPORTER_NAME) {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (Array.isArray(reporter)) {
|
|
41
|
+
const [name, options] = reporter;
|
|
42
|
+
if (name === NEETO_REPORTER_NAME) {
|
|
43
|
+
return options || {};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return null;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const extractNeetoReporterOptions = config => {
|
|
53
|
+
const reporters = config.reporter || config.default?.reporter;
|
|
54
|
+
return extractFromReporterArray(reporters);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const main = async () => {
|
|
58
|
+
try {
|
|
59
|
+
const cwd = process.cwd();
|
|
60
|
+
const configArg = process.argv[2];
|
|
61
|
+
|
|
62
|
+
let configPath;
|
|
63
|
+
if (configArg) {
|
|
64
|
+
configPath = path.isAbsolute(configArg)
|
|
65
|
+
? configArg
|
|
66
|
+
: path.resolve(cwd, configArg);
|
|
67
|
+
} else {
|
|
68
|
+
configPath = findPlaywrightConfig(cwd);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!configPath || !fs.existsSync(configPath)) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const configUrl = pathToFileURL(configPath).href;
|
|
76
|
+
const configModule = await import(configUrl);
|
|
77
|
+
const config = configModule.default || configModule;
|
|
78
|
+
|
|
79
|
+
console.error("\n[neetoplaydash] Resolved Playwright config:");
|
|
80
|
+
console.error(JSON.stringify(config, null, 2));
|
|
81
|
+
|
|
82
|
+
const options = extractNeetoReporterOptions(config);
|
|
83
|
+
|
|
84
|
+
console.error("\n[neetoplaydash] Extracted reporter options:");
|
|
85
|
+
console.error(JSON.stringify(options, null, 2));
|
|
86
|
+
|
|
87
|
+
if (options) {
|
|
88
|
+
console.log(JSON.stringify({ success: true, options }));
|
|
89
|
+
}
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error("[neetoplaydash] Config extraction error:", error.message);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
main();
|
package/bin/neetoplaydash.mjs
CHANGED
|
@@ -1,18 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { spawn } from "child_process";
|
|
3
|
+
import { spawn, execSync } from "child_process";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
4
6
|
import * as R from "ramda";
|
|
5
7
|
import smartOrchestrationApi from "./smartOrchestration.js";
|
|
6
8
|
|
|
7
|
-
const
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
8
11
|
|
|
9
|
-
const
|
|
12
|
+
const buildFullTitle = (parentTitles, specTitle) =>
|
|
13
|
+
[...parentTitles, specTitle].join(" ");
|
|
14
|
+
|
|
15
|
+
const collectSpecs = (suite, parentTitles = []) =>
|
|
10
16
|
R.concat(
|
|
11
|
-
R.map(
|
|
12
|
-
|
|
17
|
+
R.map(
|
|
18
|
+
spec => ({
|
|
19
|
+
id: spec.id,
|
|
20
|
+
title: buildFullTitle(parentTitles, spec.title),
|
|
21
|
+
}),
|
|
22
|
+
suite.specs || []
|
|
23
|
+
),
|
|
24
|
+
R.chain(
|
|
25
|
+
childSuite =>
|
|
26
|
+
collectSpecs(childSuite, [...parentTitles, childSuite.title]),
|
|
27
|
+
suite.suites || []
|
|
28
|
+
)
|
|
13
29
|
);
|
|
14
30
|
|
|
15
|
-
const extractTestTitlesAndIds =
|
|
31
|
+
const extractTestTitlesAndIds = json =>
|
|
32
|
+
R.chain(
|
|
33
|
+
project => R.chain(file => collectSpecs(file, []), project.suites || []),
|
|
34
|
+
json.suites || []
|
|
35
|
+
);
|
|
16
36
|
|
|
17
37
|
const args = process.argv.slice(2);
|
|
18
38
|
|
|
@@ -57,9 +77,65 @@ const neetoOptions = {};
|
|
|
57
77
|
remainingArgs = filteredArgs;
|
|
58
78
|
});
|
|
59
79
|
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
const
|
|
80
|
+
const playwrightArgs = remainingArgs;
|
|
81
|
+
|
|
82
|
+
const extractConfigPath = args => {
|
|
83
|
+
const index = args.findIndex(
|
|
84
|
+
arg => arg === "--config" || arg === "-c" || arg.startsWith("--config=")
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (index === -1) return null;
|
|
88
|
+
|
|
89
|
+
const arg = args[index];
|
|
90
|
+
|
|
91
|
+
if (arg === "--config" || arg === "-c") {
|
|
92
|
+
return args[index + 1] || null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (arg.startsWith("--config=")) {
|
|
96
|
+
return arg.substring("--config=".length);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return null;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const extractReporterConfigFromPlaywright = configPath => {
|
|
103
|
+
try {
|
|
104
|
+
const extractorScript = path.join(__dirname, "extractReporterConfig.mjs");
|
|
105
|
+
const configArg = configPath ? `"${configPath}"` : "";
|
|
106
|
+
|
|
107
|
+
const command = `node --import tsx "${extractorScript}" ${configArg}`;
|
|
108
|
+
|
|
109
|
+
const result = execSync(command, {
|
|
110
|
+
encoding: "utf-8",
|
|
111
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
112
|
+
cwd: process.cwd(),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
if (!result || !result.trim()) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const parsed = JSON.parse(result.trim());
|
|
120
|
+
return parsed.success ? parsed.options : null;
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const configPath = extractConfigPath(playwrightArgs);
|
|
127
|
+
const reporterOptions = extractReporterConfigFromPlaywright(configPath) || {};
|
|
128
|
+
|
|
129
|
+
const projectKey =
|
|
130
|
+
neetoOptions.projectKey ||
|
|
131
|
+
reporterOptions.projectKey ||
|
|
132
|
+
process.env.NEETO_PROJECT_KEY;
|
|
133
|
+
const apiKey =
|
|
134
|
+
neetoOptions.apiKey || reporterOptions.apiKey || process.env.NEETO_API_KEY;
|
|
135
|
+
const ciBuildId =
|
|
136
|
+
neetoOptions.ciBuildId ||
|
|
137
|
+
reporterOptions.ciBuildId ||
|
|
138
|
+
process.env.NEETO_CI_BUILD_ID;
|
|
63
139
|
|
|
64
140
|
const missingOptions = [];
|
|
65
141
|
if (!projectKey) missingOptions.push("--projectKey or NEETO_PROJECT_KEY");
|
|
@@ -76,11 +152,10 @@ if (missingOptions.length > 0) {
|
|
|
76
152
|
console.error(
|
|
77
153
|
`Or set environment variables: NEETO_PROJECT_KEY, NEETO_API_KEY, NEETO_CI_BUILD_ID`
|
|
78
154
|
);
|
|
155
|
+
console.error(`Or define them in your Playwright config reporter options.`);
|
|
79
156
|
process.exit(1);
|
|
80
157
|
}
|
|
81
158
|
|
|
82
|
-
const playwrightArgs = remainingArgs;
|
|
83
|
-
|
|
84
159
|
const escapeShellArg = arg => {
|
|
85
160
|
if (arg.includes(" ") || arg.includes('"') || arg.includes("'")) {
|
|
86
161
|
return `"${arg.replace(/"/g, '\\"')}"`;
|
|
@@ -164,7 +239,25 @@ if (totalShards !== null) {
|
|
|
164
239
|
env.NEETO_PLAYDASH_TOTAL_SHARDS = totalShards.toString();
|
|
165
240
|
}
|
|
166
241
|
|
|
167
|
-
|
|
242
|
+
if (reporterOptions.tags) {
|
|
243
|
+
env.NEETO_PLAYDASH_TAGS = Array.isArray(reporterOptions.tags)
|
|
244
|
+
? reporterOptions.tags.join(",")
|
|
245
|
+
: reporterOptions.tags;
|
|
246
|
+
}
|
|
247
|
+
if (reporterOptions.title) {
|
|
248
|
+
env.NEETO_PLAYDASH_TITLE = reporterOptions.title;
|
|
249
|
+
}
|
|
250
|
+
if (reporterOptions.commitId) {
|
|
251
|
+
env.NEETO_PLAYDASH_COMMIT_ID = reporterOptions.commitId;
|
|
252
|
+
}
|
|
253
|
+
if (reporterOptions.author) {
|
|
254
|
+
env.NEETO_PLAYDASH_AUTHOR = reporterOptions.author;
|
|
255
|
+
}
|
|
256
|
+
if (reporterOptions.branch) {
|
|
257
|
+
env.NEETO_PLAYDASH_BRANCH = reporterOptions.branch;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const finalArgs = [...filteredPlaywrightArgs, "--reporter=json", "--list"];
|
|
168
261
|
const escapedFinalArgs = finalArgs.map(escapeShellArg);
|
|
169
262
|
const listCommand = `npx playwright ${escapedFinalArgs.join(" ")}`;
|
|
170
263
|
|
|
@@ -196,13 +289,12 @@ child.on("close", async code => {
|
|
|
196
289
|
try {
|
|
197
290
|
const json = JSON.parse(jsonString);
|
|
198
291
|
const tests = extractTestTitlesAndIds(json);
|
|
199
|
-
const historyIds = R.pluck("id", tests);
|
|
200
292
|
|
|
201
293
|
const smartOrchestrationPayload = {
|
|
202
294
|
total_shards: totalShards,
|
|
203
295
|
current_shard: currentShard,
|
|
204
296
|
ci_build_id: ciBuildId,
|
|
205
|
-
|
|
297
|
+
tests,
|
|
206
298
|
};
|
|
207
299
|
|
|
208
300
|
const smartOrchestrationResponse = await smartOrchestrationApi.create({
|