@qualflare/cucumberjs 0.2.0 → 0.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/README.md CHANGED
@@ -106,11 +106,17 @@ Every option can be set either as a `formatOptions` entry or via a `QUALFLARE_*`
106
106
  variable. Full table, precedence rules, and auto-detection behavior (git branch/commit, CI
107
107
  provider/build/PR) in [`docs/CONFIGURATION.md`](./docs/CONFIGURATION.md).
108
108
 
109
+ One option is worth calling out because it fails late: `environment` is matched against the
110
+ environment's **uid (slug)**, not its display name, so **Staging** in the UI is `staging` here. A
111
+ wrong value cannot fail at run time — this package makes no network calls — so the run succeeds and
112
+ `collect` 404s afterwards. See
113
+ [the note in the configuration docs](./docs/CONFIGURATION.md#environment-is-matched-by-uid-not-display-name).
114
+
109
115
  ## Known limitations
110
116
 
111
- - **`outputDir` is merged blindly** — `qf collect` uploads every report file it finds, with no
112
- run-identity check, so a directory left over from a previous run is silently merged into the
113
- current one. Clear or freshly create `outputDir` at the start of each run.
117
+ - **A stale `outputDir` is refused, not merged** — each report carries a `runId`, and `qf collect`
118
+ errors rather than merging files from two different runs. Needs `@qualflare/cli` v0.1.19+; older
119
+ CLIs merge as before.
114
120
  - **`shardIndex` is best-effort** — cucumber-js routes its own `--shard` flag somewhere a formatter
115
121
  cannot read, so it is recovered from `QUALFLARE_SHARD_INDEX` or by scanning `process.argv`. It is
116
122
  only an attribution label; merging never depends on it. See
@@ -35,11 +35,14 @@ __export(formatter_exports, {
35
35
  module.exports = __toCommonJS(formatter_exports);
36
36
 
37
37
  // src/formatter/formatter.ts
38
- var import_node_crypto2 = require("crypto");
38
+ var import_node_crypto3 = require("crypto");
39
39
  var fs3 = __toESM(require("fs"), 1);
40
40
  var path4 = __toESM(require("path"), 1);
41
41
  var import_cucumber = require("@cucumber/cucumber");
42
42
 
43
+ // src/config/resolve-config.ts
44
+ var import_node_crypto = require("crypto");
45
+
43
46
  // src/shared/constants.ts
44
47
  var RESERVED_MESSAGE_MEDIA_TYPE = "application/vnd.qualflare.message+json";
45
48
  var MAX_SUITES_PER_LAUNCH = 2e3;
@@ -65,6 +68,7 @@ var PROVIDERS = [
65
68
  detect: (env) => env.GITHUB_ACTIONS === "true",
66
69
  providerName: "GitHub Actions",
67
70
  buildNumber: (env) => nonEmpty(env.GITHUB_RUN_NUMBER),
71
+ runId: (env) => nonEmpty(env.GITHUB_RUN_ID),
68
72
  runUrl: (env) => env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_RUN_ID ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}` : void 0,
69
73
  prNumber: (env) => {
70
74
  const match = /^refs\/pull\/(\d+)\/merge$/.exec(env.GITHUB_REF ?? "");
@@ -75,6 +79,7 @@ var PROVIDERS = [
75
79
  detect: (env) => env.GITLAB_CI === "true",
76
80
  providerName: "GitLab CI",
77
81
  buildNumber: (env) => nonEmpty(env.CI_PIPELINE_IID),
82
+ runId: (env) => nonEmpty(env.CI_PIPELINE_ID),
78
83
  runUrl: (env) => nonEmpty(env.CI_PIPELINE_URL),
79
84
  prNumber: (env) => parsePositiveInt(env.CI_MERGE_REQUEST_IID)
80
85
  },
@@ -82,6 +87,7 @@ var PROVIDERS = [
82
87
  detect: (env) => env.CIRCLECI === "true",
83
88
  providerName: "CircleCI",
84
89
  buildNumber: (env) => nonEmpty(env.CIRCLE_BUILD_NUM),
90
+ runId: (env) => nonEmpty(env.CIRCLE_WORKFLOW_ID ?? env.CIRCLE_BUILD_NUM),
85
91
  runUrl: (env) => nonEmpty(env.CIRCLE_BUILD_URL),
86
92
  prNumber: (env) => parsePositiveInt(env.CIRCLE_PR_NUMBER)
87
93
  },
@@ -89,6 +95,7 @@ var PROVIDERS = [
89
95
  detect: (env) => env.BUILDKITE === "true",
90
96
  providerName: "Buildkite",
91
97
  buildNumber: (env) => nonEmpty(env.BUILDKITE_BUILD_NUMBER),
98
+ runId: (env) => nonEmpty(env.BUILDKITE_BUILD_ID),
92
99
  runUrl: (env) => nonEmpty(env.BUILDKITE_BUILD_URL),
93
100
  prNumber: (env) => {
94
101
  const raw = env.BUILDKITE_PULL_REQUEST;
@@ -104,6 +111,7 @@ var PROVIDERS = [
104
111
  detect: (env) => Boolean(env.JENKINS_URL),
105
112
  providerName: "Jenkins",
106
113
  buildNumber: (env) => nonEmpty(env.BUILD_NUMBER),
114
+ runId: (env) => nonEmpty(env.BUILD_TAG ?? env.BUILD_NUMBER),
107
115
  runUrl: (env) => nonEmpty(env.BUILD_URL)
108
116
  // Jenkins has no standardized PR-number env var across its many PR
109
117
  // plugins (Multibranch, GitHub Branch Source, etc.) — deliberately
@@ -113,6 +121,7 @@ var PROVIDERS = [
113
121
  detect: (env) => env.TF_BUILD === "True" || env.TF_BUILD === "true",
114
122
  providerName: "Azure Pipelines",
115
123
  buildNumber: (env) => nonEmpty(env.BUILD_BUILDID),
124
+ runId: (env) => nonEmpty(env.BUILD_BUILDID),
116
125
  runUrl: (env) => {
117
126
  const collectionUri = env.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI;
118
127
  const project = env.SYSTEM_TEAMPROJECT;
@@ -128,6 +137,7 @@ var PROVIDERS = [
128
137
  detect: (env) => Boolean(env.BITBUCKET_BUILD_NUMBER),
129
138
  providerName: "Bitbucket Pipelines",
130
139
  buildNumber: (env) => nonEmpty(env.BITBUCKET_BUILD_NUMBER),
140
+ runId: (env) => nonEmpty(env.BITBUCKET_BUILD_NUMBER),
131
141
  runUrl: (env) => {
132
142
  const origin = env.BITBUCKET_GIT_HTTP_ORIGIN;
133
143
  if (!origin) {
@@ -149,6 +159,8 @@ function detectCi(env = process.env) {
149
159
  if (runUrl !== void 0) result.ciRunUrl = runUrl;
150
160
  const prNumber = provider.prNumber?.(env);
151
161
  if (prNumber !== void 0) result.ciPrNumber = prNumber;
162
+ const runId = provider.runId?.(env);
163
+ if (runId !== void 0) result.ciRunId = runId;
152
164
  return result;
153
165
  }
154
166
  if (ciInfo.name) {
@@ -256,6 +268,7 @@ function resolveConfig(options, deps = {}) {
256
268
  const ciBuildNumber = options.ciBuildNumber ?? detectedCi.ciBuildNumber;
257
269
  const ciRunUrl = options.ciRunUrl ?? detectedCi.ciRunUrl;
258
270
  const ciPrNumber = options.ciPrNumber ?? detectedCi.ciPrNumber;
271
+ const runId = options.runId ?? firstEnv2("QUALFLARE_RUN_ID") ?? detectedCi.ciRunId ?? (0, import_node_crypto.randomUUID)();
259
272
  return {
260
273
  // `||` (truthy check), not `??`, for these three REQUIRED-non-empty wire
261
274
  // fields — an explicit `''` option must not silently win over the
@@ -275,6 +288,7 @@ function resolveConfig(options, deps = {}) {
275
288
  ciBuildNumber,
276
289
  ciRunUrl,
277
290
  ciPrNumber,
291
+ runId,
278
292
  attachScreenshots: options.attachScreenshots ?? envBool("QUALFLARE_ATTACH_SCREENSHOTS") ?? true,
279
293
  includeStepHooks: options.includeStepHooks ?? envBool("QUALFLARE_INCLUDE_STEP_HOOKS") ?? false,
280
294
  maxAttachmentBytes: options.maxAttachmentBytes ?? envInt("QUALFLARE_MAX_ATTACHMENT_BYTES") ?? 15e5,
@@ -308,8 +322,8 @@ var logger = {
308
322
  var fs2 = __toESM(require("fs"), 1);
309
323
  var path2 = __toESM(require("path"), 1);
310
324
 
311
- // src/formatter/video-uploader.ts
312
- var import_node_crypto = require("crypto");
325
+ // src/formatter/video-writer.ts
326
+ var import_node_crypto2 = require("crypto");
313
327
  var fs = __toESM(require("fs"), 1);
314
328
  var path = __toESM(require("path"), 1);
315
329
  var VIDEO_MIME_TYPES_BY_EXTENSION = {
@@ -338,7 +352,7 @@ function writeVideoAttachment(pending, outputDir, maxVideoBytes) {
338
352
  logger.warn(`skipping video attachment "${pending.name}": unsupported video format.`);
339
353
  return void 0;
340
354
  }
341
- const localVideoPath = `${(0, import_node_crypto.randomUUID)()}${resolved.extension}`;
355
+ const localVideoPath = `${(0, import_node_crypto2.randomUUID)()}${resolved.extension}`;
342
356
  const destination = path.join(outputDir, localVideoPath);
343
357
  if (pending.path !== void 0) {
344
358
  let fileSize;
@@ -987,7 +1001,7 @@ function timestampMs(ts) {
987
1001
  var os = __toESM(require("os"), 1);
988
1002
 
989
1003
  // src/config/version.ts
990
- var PACKAGE_VERSION = "0.2.0";
1004
+ var PACKAGE_VERSION = "0.3.0";
991
1005
 
992
1006
  // src/formatter/collect-builder.ts
993
1007
  function resolveOs(config) {
@@ -1010,7 +1024,8 @@ function buildCollectPayload(suites, config) {
1010
1024
  metadata: {
1011
1025
  version: PACKAGE_VERSION,
1012
1026
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1013
- cliName: "qualflare-cucumberjs"
1027
+ cliName: "qualflare-cucumberjs",
1028
+ runId: config.runId
1014
1029
  },
1015
1030
  properties: config.properties,
1016
1031
  suites,
@@ -1343,7 +1358,7 @@ var QualflareCucumberFormatter = class extends import_cucumber.Formatter {
1343
1358
  }
1344
1359
  }
1345
1360
  fs3.mkdirSync(this.config.outputDir, { recursive: true });
1346
- const outputPath = path4.join(this.config.outputDir, `${(0, import_node_crypto2.randomUUID)()}.json`);
1361
+ const outputPath = path4.join(this.config.outputDir, `${(0, import_node_crypto3.randomUUID)()}.json`);
1347
1362
  fs3.writeFileSync(outputPath, JSON.stringify(payload));
1348
1363
  logger.info(
1349
1364
  `wrote Collect payload to ${outputPath} \u2014 run \`qualflare-cli collect ${this.config.outputDir}\` to upload it.`