@piwitests/reporter 0.58.0 → 0.60.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.
@@ -35,7 +35,7 @@ __export(global_setup_module_exports, {
35
35
  module.exports = __toCommonJS(global_setup_module_exports);
36
36
 
37
37
  // src/public/global-setup.ts
38
- var path7 = __toESM(require("path"));
38
+ var path8 = __toESM(require("path"));
39
39
 
40
40
  // src/internal/support/errors.ts
41
41
  function errorMessage(error) {
@@ -44,7 +44,10 @@ function errorMessage(error) {
44
44
  }
45
45
 
46
46
  // src/public/global-setup.ts
47
- var fs6 = __toESM(require("fs"));
47
+ var fs7 = __toESM(require("fs"));
48
+
49
+ // src/internal/config/env.ts
50
+ var path2 = __toESM(require("path"));
48
51
 
49
52
  // src/internal/config/desktop.ts
50
53
  var fs = __toESM(require("fs"));
@@ -53,6 +56,14 @@ var path = __toESM(require("path"));
53
56
  function defaultDesktopConfigPath() {
54
57
  return path.join(os.homedir(), ".piwi", "desktop.json");
55
58
  }
59
+ function processAlive(pid) {
60
+ try {
61
+ process.kill(pid, 0);
62
+ return true;
63
+ } catch (error) {
64
+ return error.code === "EPERM";
65
+ }
66
+ }
56
67
  function readDesktopConfig(filePath) {
57
68
  try {
58
69
  const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
@@ -60,12 +71,75 @@ function readDesktopConfig(filePath) {
60
71
  const token = parsed?.token;
61
72
  if (typeof url !== "string" || !url) return null;
62
73
  if (typeof token !== "string" || !token) return null;
74
+ if (Number.isInteger(parsed?.pid) && parsed.pid > 0 && !processAlive(parsed.pid)) return null;
63
75
  return { url, token };
64
76
  } catch {
65
77
  return null;
66
78
  }
67
79
  }
68
80
 
81
+ // src/internal/config/dotenv.ts
82
+ var fs2 = __toESM(require("fs"));
83
+
84
+ // ../core/src/dotenv.ts
85
+ function parseDotEnv(text) {
86
+ const out = {};
87
+ for (const raw of text.split(/\r?\n/)) {
88
+ const m = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/.exec(raw);
89
+ if (!m) continue;
90
+ let value = m[2];
91
+ const quote = value[0];
92
+ if ((quote === '"' || quote === "'") && value.length > 1 && value.endsWith(quote)) {
93
+ value = value.slice(1, -1);
94
+ if (quote === '"') value = value.replace(/\\n/g, "\n").replace(/\\"/g, '"');
95
+ } else {
96
+ value = value.replace(/\s+#.*$/, "");
97
+ }
98
+ out[m[1]] = value;
99
+ }
100
+ return out;
101
+ }
102
+
103
+ // src/internal/config/dotenv.ts
104
+ function readDotEnv(filePath) {
105
+ try {
106
+ return parseDotEnv(fs2.readFileSync(filePath, "utf-8"));
107
+ } catch {
108
+ return {};
109
+ }
110
+ }
111
+
112
+ // src/internal/support/ci.ts
113
+ function detectCi(env) {
114
+ return Boolean(
115
+ env.JENKINS_URL || env.GITHUB_ACTIONS || env.GITLAB_CI || env.CIRCLECI || env.TRAVIS || env.TF_BUILD || env.BITBUCKET_BUILD_NUMBER || env.TEAMCITY_VERSION || env.CODEBUILD_BUILD_ID || env.bamboo_buildKey || env.CI && env.CI.toLowerCase() !== "false" && env.CI !== "0"
116
+ );
117
+ }
118
+ function detectCiIds(env) {
119
+ if (env.GITHUB_ACTIONS && env.GITHUB_RUN_ID) {
120
+ const run = env.GITHUB_RUN_ATTEMPT ? `${env.GITHUB_RUN_ID}-${env.GITHUB_RUN_ATTEMPT}` : env.GITHUB_RUN_ID;
121
+ return { run, job: env.GITHUB_JOB || null };
122
+ }
123
+ if (env.GITLAB_CI && env.CI_PIPELINE_ID) return { run: env.CI_PIPELINE_ID, job: env.CI_JOB_ID || null };
124
+ if (env.CIRCLECI && env.CIRCLE_WORKFLOW_ID) return { run: env.CIRCLE_WORKFLOW_ID, job: env.CIRCLE_BUILD_NUM || null };
125
+ if (env.TRAVIS && env.TRAVIS_BUILD_ID) return { run: env.TRAVIS_BUILD_ID, job: env.TRAVIS_JOB_ID || null };
126
+ if (env.TF_BUILD && env.BUILD_BUILDID) return { run: env.BUILD_BUILDID, job: env.SYSTEM_JOBID || null };
127
+ if (env.JENKINS_URL && env.BUILD_ID) return { run: env.BUILD_ID, job: null };
128
+ if (env.BUILDKITE_BUILD_ID) return { run: env.BUILDKITE_BUILD_ID, job: env.BUILDKITE_JOB_ID || null };
129
+ if (env.TEAMCITY_BUILD_ID) return { run: env.TEAMCITY_BUILD_ID, job: null };
130
+ if (env.BITBUCKET_BUILD_NUMBER) return { run: env.BITBUCKET_BUILD_NUMBER, job: env.BITBUCKET_STEP_UUID || null };
131
+ if (env.SEMAPHORE_WORKFLOW_ID) return { run: env.SEMAPHORE_WORKFLOW_ID, job: env.SEMAPHORE_JOB_ID || null };
132
+ if (env.APPVEYOR_BUILD_ID) return { run: env.APPVEYOR_BUILD_ID, job: env.APPVEYOR_JOB_ID || null };
133
+ if (env.DRONE_BUILD_NUMBER) return { run: env.DRONE_BUILD_NUMBER, job: null };
134
+ return null;
135
+ }
136
+ function resolveRunLabel(configured, sharded) {
137
+ if (configured) return configured;
138
+ const ci = detectCiIds(process.env);
139
+ if (!ci) return null;
140
+ return !sharded && ci.job ? `${ci.run}|${ci.job}` : ci.run;
141
+ }
142
+
69
143
  // src/internal/config/env.ts
70
144
  var DEFAULTS = {
71
145
  projectName: "default-project",
@@ -102,6 +176,7 @@ var DEFAULTS = {
102
176
  };
103
177
  var PIWI_ENV_KEYS = {
104
178
  serverUrl: "PIWI_DASHBOARD_URL",
179
+ desktopApp: "PIWI_DESKTOP_APP",
105
180
  projectName: "PIWI_PROJECT_NAME",
106
181
  verbose: "PIWI_VERBOSE",
107
182
  apiKey: "PIWI_API_KEY",
@@ -188,8 +263,18 @@ var ENV_FALLBACK_SPECS = [
188
263
  { option: "pickLocatorOnFailure", env: PIWI_ENV_KEYS.pickLocatorOnFailure, kind: "bool" },
189
264
  { option: "outputFile", env: PIWI_ENV_KEYS.outputFile, kind: "string" }
190
265
  ];
266
+ var DOTENV_OPTIONS = ["serverUrl", "apiKey", "projectName"];
267
+ var DESKTOP_APP_MODES = ["prefer", "fallback", "off"];
268
+ function desktopAppMode(option, envValue) {
269
+ const fromEnv = envValue?.trim().toLowerCase();
270
+ if (DESKTOP_APP_MODES.includes(fromEnv)) return fromEnv;
271
+ if (DESKTOP_APP_MODES.includes(option)) return option;
272
+ return "fallback";
273
+ }
191
274
  var desktopDiscovered = false;
192
- function resolveOptions(raw) {
275
+ var desktopReplacedServerUrl = null;
276
+ var dotEnvUse = null;
277
+ function resolveOptions(raw, configDir) {
193
278
  const env = process.env;
194
279
  const mergedRaw = { ...raw };
195
280
  for (const spec of ENV_FALLBACK_SPECS) {
@@ -201,14 +286,35 @@ function resolveOptions(raw) {
201
286
  mergedRaw[spec.option] = spec.kind === "number" ? Number(value) : value;
202
287
  }
203
288
  }
289
+ dotEnvUse = null;
290
+ if (configDir) {
291
+ const file = path2.join(configDir, ".env");
292
+ const dotEnv = readDotEnv(file);
293
+ const keys = [];
294
+ for (const option of DOTENV_OPTIONS) {
295
+ const value = dotEnv[PIWI_ENV_KEYS[option]];
296
+ if (mergedRaw[option] !== void 0 || !value) continue;
297
+ mergedRaw[option] = value;
298
+ keys.push(PIWI_ENV_KEYS[option]);
299
+ }
300
+ if (keys.length > 0) dotEnvUse = { file, keys };
301
+ }
204
302
  desktopDiscovered = false;
303
+ desktopReplacedServerUrl = null;
304
+ const mode = desktopAppMode(mergedRaw.desktopApp, env[PIWI_ENV_KEYS.desktopApp]);
305
+ mergedRaw.desktopApp = mode;
205
306
  const serverUrlUnset = mergedRaw.serverUrl === void 0;
206
307
  const apiKeyUnset = mergedRaw.apiKey === void 0 || mergedRaw.apiKey === null;
207
- if (serverUrlUnset && apiKeyUnset) {
308
+ const nothingConfigured = serverUrlUnset && apiKeyUnset;
309
+ const preferLocally = mode === "prefer" && !detectCi(env);
310
+ if (mode !== "off" && (nothingConfigured || preferLocally)) {
208
311
  const desktop = readDesktopConfig(env[PIWI_DESKTOP_CONFIG_ENV] || defaultDesktopConfigPath());
209
312
  if (desktop) {
313
+ if (!serverUrlUnset && mergedRaw.serverUrl !== desktop.url) desktopReplacedServerUrl = mergedRaw.serverUrl;
210
314
  mergedRaw.serverUrl = desktop.url;
211
315
  mergedRaw.apiKey = desktop.token;
316
+ mergedRaw.username = null;
317
+ mergedRaw.password = null;
212
318
  desktopDiscovered = true;
213
319
  }
214
320
  }
@@ -528,32 +634,6 @@ function computeInstanceId(projectName, runLabel) {
528
634
  return crypto.createHash("sha256").update(seed).digest("hex").slice(0, 16);
529
635
  }
530
636
 
531
- // src/internal/support/ci.ts
532
- function detectCiIds(env) {
533
- if (env.GITHUB_ACTIONS && env.GITHUB_RUN_ID) {
534
- const run = env.GITHUB_RUN_ATTEMPT ? `${env.GITHUB_RUN_ID}-${env.GITHUB_RUN_ATTEMPT}` : env.GITHUB_RUN_ID;
535
- return { run, job: env.GITHUB_JOB || null };
536
- }
537
- if (env.GITLAB_CI && env.CI_PIPELINE_ID) return { run: env.CI_PIPELINE_ID, job: env.CI_JOB_ID || null };
538
- if (env.CIRCLECI && env.CIRCLE_WORKFLOW_ID) return { run: env.CIRCLE_WORKFLOW_ID, job: env.CIRCLE_BUILD_NUM || null };
539
- if (env.TRAVIS && env.TRAVIS_BUILD_ID) return { run: env.TRAVIS_BUILD_ID, job: env.TRAVIS_JOB_ID || null };
540
- if (env.TF_BUILD && env.BUILD_BUILDID) return { run: env.BUILD_BUILDID, job: env.SYSTEM_JOBID || null };
541
- if (env.JENKINS_URL && env.BUILD_ID) return { run: env.BUILD_ID, job: null };
542
- if (env.BUILDKITE_BUILD_ID) return { run: env.BUILDKITE_BUILD_ID, job: env.BUILDKITE_JOB_ID || null };
543
- if (env.TEAMCITY_BUILD_ID) return { run: env.TEAMCITY_BUILD_ID, job: null };
544
- if (env.BITBUCKET_BUILD_NUMBER) return { run: env.BITBUCKET_BUILD_NUMBER, job: env.BITBUCKET_STEP_UUID || null };
545
- if (env.SEMAPHORE_WORKFLOW_ID) return { run: env.SEMAPHORE_WORKFLOW_ID, job: env.SEMAPHORE_JOB_ID || null };
546
- if (env.APPVEYOR_BUILD_ID) return { run: env.APPVEYOR_BUILD_ID, job: env.APPVEYOR_JOB_ID || null };
547
- if (env.DRONE_BUILD_NUMBER) return { run: env.DRONE_BUILD_NUMBER, job: null };
548
- return null;
549
- }
550
- function resolveRunLabel(configured, sharded) {
551
- if (configured) return configured;
552
- const ci = detectCiIds(process.env);
553
- if (!ci) return null;
554
- return !sharded && ci.job ? `${ci.run}|${ci.job}` : ci.run;
555
- }
556
-
557
637
  // src/internal/support/shard-info.ts
558
638
  function parseShardSpec(raw) {
559
639
  const match = raw?.trim().match(/^(\d+)\s*\/\s*(\d+)$/);
@@ -572,9 +652,9 @@ function resolveShardInfo(config, env = process.env) {
572
652
  var import_node_child_process = require("child_process");
573
653
 
574
654
  // src/internal/support/test-paths.ts
575
- var path2 = __toESM(require("path"));
655
+ var path3 = __toESM(require("path"));
576
656
  function configDirFromConfig(config) {
577
- if (config?.configFile) return path2.dirname(config.configFile);
657
+ if (config?.configFile) return path3.dirname(config.configFile);
578
658
  if (config?.rootDir) return config.rootDir;
579
659
  return process.cwd();
580
660
  }
@@ -609,32 +689,32 @@ function normalizeRef(ref) {
609
689
  }
610
690
 
611
691
  // src/internal/support/setup-file.ts
612
- var path3 = __toESM(require("path"));
692
+ var path4 = __toESM(require("path"));
613
693
  var os3 = __toESM(require("os"));
614
- var fs2 = __toESM(require("fs"));
694
+ var fs3 = __toESM(require("fs"));
615
695
  function getSetupFilePath(projectName) {
616
- return path3.join(os3.tmpdir(), `piwi-dashboard-setup-${hashForProject(projectName)}.json`);
696
+ return path4.join(os3.tmpdir(), `piwi-dashboard-setup-${hashForProject(projectName)}.json`);
617
697
  }
618
698
 
619
699
  // src/internal/support/aria-sampling.ts
620
- var path4 = __toESM(require("path"));
700
+ var path5 = __toESM(require("path"));
621
701
  var os4 = __toESM(require("os"));
622
- var fs3 = __toESM(require("fs"));
702
+ var fs4 = __toESM(require("fs"));
623
703
  function ariaSampleIdentity(filePath, title) {
624
704
  return `${filePath}\0${title}`;
625
705
  }
626
706
  function getAriaSampleFilePath(projectName) {
627
- return path4.join(os4.tmpdir(), `piwi-dashboard-aria-sample-${hashForProject(projectName)}.json`);
707
+ return path5.join(os4.tmpdir(), `piwi-dashboard-aria-sample-${hashForProject(projectName)}.json`);
628
708
  }
629
709
  function writeAriaSampleFile(projectName, identities) {
630
710
  try {
631
- fs3.writeFileSync(getAriaSampleFilePath(projectName), JSON.stringify({ projectName, identities }));
711
+ fs4.writeFileSync(getAriaSampleFilePath(projectName), JSON.stringify({ projectName, identities }));
632
712
  } catch {
633
713
  }
634
714
  }
635
715
  function clearAriaSampleFile(projectName) {
636
716
  try {
637
- fs3.rmSync(getAriaSampleFilePath(projectName), { force: true });
717
+ fs4.rmSync(getAriaSampleFilePath(projectName), { force: true });
638
718
  } catch {
639
719
  }
640
720
  }
@@ -788,8 +868,8 @@ async function checkBaseUrls(config, options = {}) {
788
868
  }
789
869
 
790
870
  // src/internal/manifest/collect.ts
791
- var fs4 = __toESM(require("fs"));
792
- var path5 = __toESM(require("path"));
871
+ var fs5 = __toESM(require("fs"));
872
+ var path6 = __toESM(require("path"));
793
873
 
794
874
  // src/internal/transport/backend-request.ts
795
875
  var http2 = __toESM(require("http"));
@@ -823,10 +903,10 @@ function hasInstrumentationHeader(headers) {
823
903
  return INSTRUMENTATION_HEADERS.some((h) => get(h) != null && get(h) !== "");
824
904
  }
825
905
  function readCommittedManifest(configDir) {
826
- const file = path5.join(configDir, COMMITTED_MANIFEST_FILE);
906
+ const file = path6.join(configDir, COMMITTED_MANIFEST_FILE);
827
907
  try {
828
- if (!fs4.existsSync(file)) return null;
829
- return parseManifestJson(fs4.readFileSync(file, "utf8"));
908
+ if (!fs5.existsSync(file)) return null;
909
+ return parseManifestJson(fs5.readFileSync(file, "utf8"));
830
910
  } catch {
831
911
  return null;
832
912
  }
@@ -836,8 +916,8 @@ var MAX_MANIFEST_BYTES = 4 * 1024 * 1024;
836
916
  function instrumentationManifestUrls(baseUrl) {
837
917
  const base = new URL(baseUrl);
838
918
  const root = new URL(INSTRUMENTATION_MANIFEST_PATH, base).toString();
839
- const path8 = base.pathname.replace(/\/+$/, "");
840
- return path8 ? [new URL(`${path8}${INSTRUMENTATION_MANIFEST_PATH}`, base).toString(), root] : [root];
919
+ const path9 = base.pathname.replace(/\/+$/, "");
920
+ return path9 ? [new URL(`${path9}${INSTRUMENTATION_MANIFEST_PATH}`, base).toString(), root] : [root];
841
921
  }
842
922
  async function fetchInstrumentationManifest(baseUrl, fetchImpl = fetch) {
843
923
  try {
@@ -870,8 +950,8 @@ async function uploadManifest(httpClient, auth, projectId, upload, logger) {
870
950
 
871
951
  // src/internal/collect/source-checksums.ts
872
952
  var crypto2 = __toESM(require("crypto"));
873
- var fs5 = __toESM(require("fs"));
874
- var path6 = __toESM(require("path"));
953
+ var fs6 = __toESM(require("fs"));
954
+ var path7 = __toESM(require("path"));
875
955
 
876
956
  // src/internal/collect/server-coverage.ts
877
957
  var DEFAULT_COLLECTOR_LIMITS = {
@@ -893,14 +973,14 @@ function baseUrlsFromConfig(config) {
893
973
  // src/public/global-setup.ts
894
974
  function createGlobalSetup(options, userSetup) {
895
975
  return async function globalSetupFn(config) {
896
- const piwiReporterPath = path7.resolve(__dirname, "./index.js");
976
+ const piwiReporterPath = path8.resolve(__dirname, "./index.js");
897
977
  let inlineReporterOptions = {};
898
978
  if (Array.isArray(config?.reporter)) {
899
979
  for (const r of config.reporter) {
900
980
  if (!Array.isArray(r) || typeof r[0] !== "string") continue;
901
981
  const isPiwi = r[0].toLowerCase().includes("piwi") || (() => {
902
982
  try {
903
- return path7.resolve(require.resolve(r[0])) === piwiReporterPath;
983
+ return path8.resolve(require.resolve(r[0])) === piwiReporterPath;
904
984
  } catch {
905
985
  return false;
906
986
  }
@@ -911,7 +991,10 @@ function createGlobalSetup(options, userSetup) {
911
991
  }
912
992
  }
913
993
  }
914
- const opts = resolveOptions({ ...inlineReporterOptions, ...options });
994
+ const opts = resolveOptions(
995
+ { ...inlineReporterOptions, ...options },
996
+ configDirFromConfig(config)
997
+ );
915
998
  const logger = new Logger(opts.verbose ?? false);
916
999
  if (isUiMode()) {
917
1000
  logger.debug("UI mode detected \u2014 skipping run registration.");
@@ -936,7 +1019,7 @@ function createGlobalSetup(options, userSetup) {
936
1019
  if (!Array.isArray(r) || typeof r[0] !== "string") return false;
937
1020
  if (r[0].toLowerCase().includes("piwi")) return true;
938
1021
  try {
939
- return path7.resolve(require.resolve(r[0])) === piwiReporterPath;
1022
+ return path8.resolve(require.resolve(r[0])) === piwiReporterPath;
940
1023
  } catch {
941
1024
  return false;
942
1025
  }
@@ -966,7 +1049,7 @@ function createGlobalSetup(options, userSetup) {
966
1049
  auth
967
1050
  );
968
1051
  if (response?.runId && response?.setupToken) {
969
- fs6.writeFileSync(
1052
+ fs7.writeFileSync(
970
1053
  getSetupFilePath(opts.projectName),
971
1054
  JSON.stringify({
972
1055
  runId: response.runId,
package/dist/index.d.ts CHANGED
@@ -19,6 +19,18 @@ interface PiwiDashboardOptions {
19
19
  enabled?: boolean;
20
20
  /** URL of the Piwi Dashboard server */
21
21
  serverUrl?: string;
22
+ /**
23
+ * When results go to the Piwi desktop app running on this machine instead.
24
+ * `'fallback'` uses the app only when nothing sets a server: no `serverUrl`,
25
+ * no `apiKey`, and neither `PIWI_DASHBOARD_URL` nor `PIWI_API_KEY`.
26
+ * `'prefer'` uses it whenever it is running and the run is not on CI, even
27
+ * with `serverUrl` set, so local runs land in the app while CI runs, and
28
+ * local runs with the app closed, go to `serverUrl` with its credentials.
29
+ * `'off'` never uses it. Defaults to `'fallback'`. `PIWI_DESKTOP_APP`
30
+ * overrides this option, so setting it to `off` for one run sends that
31
+ * local run to `serverUrl`.
32
+ */
33
+ desktopApp?: 'prefer' | 'fallback' | 'off';
22
34
  /** Name of the project to report results under. Defaults to `'default-project'`. */
23
35
  projectName?: string;
24
36
  /** Optional description of the project */
@@ -495,6 +507,10 @@ declare class PiwiDashboardReporter {
495
507
  private readonly listMode;
496
508
  /** True when the server URL and API key came from the desktop app, not from config. */
497
509
  private viaDesktopApp;
510
+ /** The configured server `desktopApp: 'prefer'` set aside for the desktop app, if any. */
511
+ private readonly desktopReplaced;
512
+ /** The config folder's `.env` file and the connection variables taken from it, if any. */
513
+ private readonly viaDotEnv;
498
514
  private isFullRun;
499
515
  private filterDetails;
500
516
  /** Configured `maxFailures` (0 = unlimited) — disambiguates an interrupted run's unrun reason. */