@bigbinary/neeto-playwright-reporter 3.0.0-beta.2 → 3.0.0-beta.4

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,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();
@@ -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 pickTest = R.pick(["id", "title"]);
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
8
11
 
9
- const collectSpecs = suite =>
12
+ const buildFullTitle = (parentTitles, specTitle) =>
13
+ [...parentTitles, specTitle].join(" ");
14
+
15
+ const collectSpecs = (suite, parentTitles = []) =>
10
16
  R.concat(
11
- R.map(pickTest, suite.specs || []),
12
- R.chain(collectSpecs, suite.suites || [])
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 = R.pipe(R.prop("suites"), R.chain(collectSpecs));
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 projectKey = neetoOptions.projectKey || process.env.NEETO_PROJECT_KEY;
61
- const apiKey = neetoOptions.apiKey || process.env.NEETO_API_KEY;
62
- const ciBuildId = neetoOptions.ciBuildId || process.env.NEETO_CI_BUILD_ID;
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, '\\"')}"`;
@@ -156,6 +231,10 @@ env.NEETO_PROJECT_KEY = projectKey;
156
231
  env.NEETO_API_KEY = apiKey;
157
232
  env.NEETO_CI_BUILD_ID = ciBuildId;
158
233
 
234
+ if (reporterOptions.baseURL) {
235
+ env.NEETO_PLAYDASH_API_BASE_URL = reporterOptions.baseURL;
236
+ }
237
+
159
238
  if (currentShard !== null) {
160
239
  env.NEETO_PLAYDASH_CURRENT_SHARD = currentShard.toString();
161
240
  }
@@ -164,6 +243,24 @@ if (totalShards !== null) {
164
243
  env.NEETO_PLAYDASH_TOTAL_SHARDS = totalShards.toString();
165
244
  }
166
245
 
246
+ if (reporterOptions.tags) {
247
+ env.NEETO_PLAYDASH_TAGS = Array.isArray(reporterOptions.tags)
248
+ ? reporterOptions.tags.join(",")
249
+ : reporterOptions.tags;
250
+ }
251
+ if (reporterOptions.title) {
252
+ env.NEETO_PLAYDASH_TITLE = reporterOptions.title;
253
+ }
254
+ if (reporterOptions.commitId) {
255
+ env.NEETO_PLAYDASH_COMMIT_ID = reporterOptions.commitId;
256
+ }
257
+ if (reporterOptions.author) {
258
+ env.NEETO_PLAYDASH_AUTHOR = reporterOptions.author;
259
+ }
260
+ if (reporterOptions.branch) {
261
+ env.NEETO_PLAYDASH_BRANCH = reporterOptions.branch;
262
+ }
263
+
167
264
  const finalArgs = [...filteredPlaywrightArgs, "--reporter=json", "--list"];
168
265
  const escapedFinalArgs = finalArgs.map(escapeShellArg);
169
266
  const listCommand = `npx playwright ${escapedFinalArgs.join(" ")}`;
@@ -196,13 +293,12 @@ child.on("close", async code => {
196
293
  try {
197
294
  const json = JSON.parse(jsonString);
198
295
  const tests = extractTestTitlesAndIds(json);
199
- const historyIds = R.pluck("id", tests);
200
296
 
201
297
  const smartOrchestrationPayload = {
202
298
  total_shards: totalShards,
203
299
  current_shard: currentShard,
204
300
  ci_build_id: ciBuildId,
205
- test_history_ids: historyIds,
301
+ tests,
206
302
  };
207
303
 
208
304
  const smartOrchestrationResponse = await smartOrchestrationApi.create({