@qualflare/cucumberjs 0.4.0 → 0.5.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.
@@ -1004,7 +1004,7 @@ function timestampMs(ts) {
1004
1004
  import * as os from "os";
1005
1005
 
1006
1006
  // src/config/version.ts
1007
- var PACKAGE_VERSION = "0.4.0";
1007
+ var PACKAGE_VERSION = "0.5.0";
1008
1008
 
1009
1009
  // src/formatter/collect-builder.ts
1010
1010
  function resolveOs(config) {
@@ -1131,23 +1131,52 @@ function buildHookIndex(supportCodeLibrary) {
1131
1131
 
1132
1132
  // src/formatter/run-hook-tracker.ts
1133
1133
  var RunHookTracker = class {
1134
+ constructor(config, budget) {
1135
+ this.config = config;
1136
+ this.budget = budget;
1137
+ }
1138
+ config;
1139
+ budget;
1134
1140
  /** `testRunHookStartedId` -> the hook it started, so `finish()` can look
1135
1141
  * up its kind/name. `TestRunHookFinished.result.duration` already gives
1136
1142
  * an accurate duration directly — no start/finish timestamp delta needed. */
1137
1143
  started = /* @__PURE__ */ new Map();
1138
1144
  failed = [];
1145
+ /** `testRunHookStartedId` -> attachments made during that hook, unresolved. */
1146
+ pending = /* @__PURE__ */ new Map();
1139
1147
  start(e) {
1140
1148
  this.started.set(e.id, e.hookId);
1141
1149
  }
1150
+ /**
1151
+ * Buffers one attachment made from inside a `BeforeAll`/`AfterAll`. Returns
1152
+ * whether it was claimed, so the caller can tell a run-hook attachment from
1153
+ * one that simply has no home.
1154
+ *
1155
+ * `testRunHookStartedId` is optional in the message schema and only populated
1156
+ * by newer cucumber-js; when it is absent this returns false and the
1157
+ * attachment is dropped exactly as it was before.
1158
+ */
1159
+ attachment(e) {
1160
+ if (!e.testRunHookStartedId) {
1161
+ return false;
1162
+ }
1163
+ const content = e.contentEncoding === "BASE64" ? e.body : Buffer.from(e.body, "utf8").toString("base64");
1164
+ const list = this.pending.get(e.testRunHookStartedId) ?? [];
1165
+ list.push({ name: e.fileName || "attachment", mimeType: e.mediaType, content });
1166
+ this.pending.set(e.testRunHookStartedId, list);
1167
+ return true;
1168
+ }
1142
1169
  finish(e, hookIndex) {
1143
1170
  const hookId = this.started.get(e.testRunHookStartedId);
1144
1171
  this.started.delete(e.testRunHookStartedId);
1145
1172
  const status = mapStatus(e.result.status);
1146
1173
  if (status === "passed" || status === "skipped") {
1174
+ this.pending.delete(e.testRunHookStartedId);
1147
1175
  return;
1148
1176
  }
1149
1177
  const hook = hookId ? hookIndex.get(hookId) : void 0;
1150
1178
  const label = hook?.kind === "afterAll" ? "AfterAll hook" : "BeforeAll hook";
1179
+ const attachments = this.takeAttachments(e.testRunHookStartedId);
1151
1180
  this.failed.push({
1152
1181
  id: `global-hook:${e.testRunHookStartedId}`,
1153
1182
  name: hook?.name || label,
@@ -1156,9 +1185,17 @@ var RunHookTracker = class {
1156
1185
  // `result.message` first — see `step-mapper.ts`'s `formatError()` doc
1157
1186
  // comment for why (verified empirically to be the version-safe field
1158
1187
  // across the peer-dependency range; `exception.stackTrace` is not).
1159
- error: e.result.message || e.result.exception?.stackTrace || e.result.exception?.message
1188
+ error: e.result.message || e.result.exception?.stackTrace || e.result.exception?.message,
1189
+ ...attachments.length > 0 ? { attachments } : {}
1160
1190
  });
1161
1191
  }
1192
+ /** Resolves and clears the buffer for one hook. Called only for a FAILED
1193
+ * hook, so the budget is never spent on a passing one's attachments. */
1194
+ takeAttachments(testRunHookStartedId) {
1195
+ const buffered = this.pending.get(testRunHookStartedId) ?? [];
1196
+ this.pending.delete(testRunHookStartedId);
1197
+ return buffered.map((pending) => resolvePendingAttachment(pending, this.config, this.budget)).filter((a) => a !== void 0);
1198
+ }
1162
1199
  /** Returns `undefined` if no run-hook failed — see the class doc comment
1163
1200
  * for why a passing BeforeAll/AfterAll produces no Case at all. */
1164
1201
  buildSuite() {
@@ -1230,7 +1267,7 @@ var QualflareCucumberFormatter = class extends Formatter {
1230
1267
  testCaseIndex = /* @__PURE__ */ new Map();
1231
1268
  attachmentBudget;
1232
1269
  attemptTracker;
1233
- runHookTracker = new RunHookTracker();
1270
+ runHookTracker;
1234
1271
  finishedCases = [];
1235
1272
  /** One promise per `testCaseFinished` envelope, resolving once that
1236
1273
  * scenario's `AttemptTracker.finish()` (which itself awaits any pending
@@ -1245,6 +1282,7 @@ var QualflareCucumberFormatter = class extends Formatter {
1245
1282
  this.config = resolveConfig(options.parsedArgvOptions);
1246
1283
  this.hookIndex = buildHookIndex(options.supportCodeLibrary);
1247
1284
  this.attachmentBudget = new AttachmentBudget(this.config.maxTotalAttachmentBytes);
1285
+ this.runHookTracker = new RunHookTracker(this.config, this.attachmentBudget);
1248
1286
  this.attemptTracker = new AttemptTracker(
1249
1287
  this.hookIndex,
1250
1288
  this.gherkin,