@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.
@@ -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, '\\"')}"`;
@@ -164,7 +239,25 @@ if (totalShards !== null) {
164
239
  env.NEETO_PLAYDASH_TOTAL_SHARDS = totalShards.toString();
165
240
  }
166
241
 
167
- const finalArgs = [...playwrightArgs, "--reporter=json", "--list"];
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
- test_history_ids: historyIds,
297
+ tests,
206
298
  };
207
299
 
208
300
  const smartOrchestrationResponse = await smartOrchestrationApi.create({