executable-stories-formatters 0.7.11 → 0.7.13
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/dist/adapters.cjs +9 -2
- package/dist/adapters.cjs.map +1 -1
- package/dist/adapters.d.cts +1 -1
- package/dist/adapters.d.ts +1 -1
- package/dist/adapters.js +9 -2
- package/dist/adapters.js.map +1 -1
- package/dist/cli.js +206 -20
- package/dist/cli.js.map +1 -1
- package/dist/{index-C0OOaaiK.d.cts → index-fqrm5-Xr.d.cts} +34 -24
- package/dist/{index-C0OOaaiK.d.ts → index-fqrm5-Xr.d.ts} +34 -24
- package/dist/index.cjs +215 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -9
- package/dist/index.d.ts +7 -9
- package/dist/index.js +215 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import { S as StoryMeta, C as CIInfo$1, a as StoryStep, D as DocEntry, N as NormalizedTicket, O as OtelSpan, b as CIProvider,
|
|
2
|
-
export { i as DocPhase, J as JestAdapterOptions, j as JestAggregatedResult, k as JestFileResult, l as JestTestResult, m as OtelAttributeValue, P as PlaywrightAdapterOptions, n as PlaywrightAnnotation, o as PlaywrightAttachment, p as PlaywrightError, q as PlaywrightLocation, r as PlaywrightStatus, s as PlaywrightTestCase, t as PlaywrightTestResult, u as RawStepEvent, v as RawTestCase, w as STORY_META_KEY, x as StepKeyword, y as StepMode, z as StoryFileReport, V as VitestAdapterOptions, A as VitestSerializedError, B as VitestState, E as VitestTestCase, F as VitestTestModule, G as VitestTestResult, H as toCIInfo, I as toRawCIInfo } from './index-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Canonical types for Layer 2: Anti-Corruption Layer output.
|
|
6
|
-
*
|
|
7
|
-
* These types are strict and have all required fields populated.
|
|
8
|
-
* Formatters (Layer 3) accept only these canonical types.
|
|
9
|
-
*/
|
|
1
|
+
import { S as StoryMeta, R as RawStatus, C as CIInfo$1, a as StoryStep, D as DocEntry, N as NormalizedTicket, O as OtelSpan, b as CIProvider, c as RawAttachment, d as RawRun, e as RawCIInfo, f as adaptJestRun, g as adaptPlaywrightRun, h as adaptVitestRun } from './index-fqrm5-Xr.cjs';
|
|
2
|
+
export { i as DocPhase, J as JestAdapterOptions, j as JestAggregatedResult, k as JestFileResult, l as JestTestResult, m as OtelAttributeValue, P as PlaywrightAdapterOptions, n as PlaywrightAnnotation, o as PlaywrightAttachment, p as PlaywrightError, q as PlaywrightLocation, r as PlaywrightStatus, s as PlaywrightTestCase, t as PlaywrightTestResult, u as RawStepEvent, v as RawTestCase, w as STORY_META_KEY, x as StepKeyword, y as StepMode, z as StoryFileReport, V as VitestAdapterOptions, A as VitestSerializedError, B as VitestState, E as VitestTestCase, F as VitestTestModule, G as VitestTestResult, H as toCIInfo, I as toRawCIInfo } from './index-fqrm5-Xr.cjs';
|
|
10
3
|
|
|
11
4
|
/** Canonical test status (Cucumber-compatible) */
|
|
12
5
|
type TestStatus = "passed" | "failed" | "skipped" | "pending";
|
|
@@ -14,6 +7,8 @@ type TestStatus = "passed" | "failed" | "skipped" | "pending";
|
|
|
14
7
|
interface StepResult {
|
|
15
8
|
/** Step index (0-based) */
|
|
16
9
|
index: number;
|
|
10
|
+
/** Stable step ID when available */
|
|
11
|
+
stepId?: string;
|
|
17
12
|
/** Step status */
|
|
18
13
|
status: TestStatus;
|
|
19
14
|
/** Duration in milliseconds (default 0) */
|
|
@@ -57,6 +52,8 @@ interface TestCaseResult {
|
|
|
57
52
|
sourceLine: number;
|
|
58
53
|
/** Test status (required) */
|
|
59
54
|
status: TestStatus;
|
|
55
|
+
/** Original adapter/framework status (preserved for diagnostics). */
|
|
56
|
+
rawStatus?: RawStatus;
|
|
60
57
|
/** Duration in milliseconds (required, default 0) */
|
|
61
58
|
durationMs: number;
|
|
62
59
|
/** Error message if failed */
|
|
@@ -1094,6 +1091,7 @@ declare function deriveStepResults(steps: StoryStep[], scenarioStatus: TestStatu
|
|
|
1094
1091
|
*/
|
|
1095
1092
|
declare function mergeStepResults(derived: StepResult[], events?: Array<{
|
|
1096
1093
|
index?: number;
|
|
1094
|
+
stepId?: string;
|
|
1097
1095
|
status?: string;
|
|
1098
1096
|
durationMs?: number;
|
|
1099
1097
|
errorMessage?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import { S as StoryMeta, C as CIInfo$1, a as StoryStep, D as DocEntry, N as NormalizedTicket, O as OtelSpan, b as CIProvider,
|
|
2
|
-
export { i as DocPhase, J as JestAdapterOptions, j as JestAggregatedResult, k as JestFileResult, l as JestTestResult, m as OtelAttributeValue, P as PlaywrightAdapterOptions, n as PlaywrightAnnotation, o as PlaywrightAttachment, p as PlaywrightError, q as PlaywrightLocation, r as PlaywrightStatus, s as PlaywrightTestCase, t as PlaywrightTestResult, u as RawStepEvent, v as RawTestCase, w as STORY_META_KEY, x as StepKeyword, y as StepMode, z as StoryFileReport, V as VitestAdapterOptions, A as VitestSerializedError, B as VitestState, E as VitestTestCase, F as VitestTestModule, G as VitestTestResult, H as toCIInfo, I as toRawCIInfo } from './index-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Canonical types for Layer 2: Anti-Corruption Layer output.
|
|
6
|
-
*
|
|
7
|
-
* These types are strict and have all required fields populated.
|
|
8
|
-
* Formatters (Layer 3) accept only these canonical types.
|
|
9
|
-
*/
|
|
1
|
+
import { S as StoryMeta, R as RawStatus, C as CIInfo$1, a as StoryStep, D as DocEntry, N as NormalizedTicket, O as OtelSpan, b as CIProvider, c as RawAttachment, d as RawRun, e as RawCIInfo, f as adaptJestRun, g as adaptPlaywrightRun, h as adaptVitestRun } from './index-fqrm5-Xr.js';
|
|
2
|
+
export { i as DocPhase, J as JestAdapterOptions, j as JestAggregatedResult, k as JestFileResult, l as JestTestResult, m as OtelAttributeValue, P as PlaywrightAdapterOptions, n as PlaywrightAnnotation, o as PlaywrightAttachment, p as PlaywrightError, q as PlaywrightLocation, r as PlaywrightStatus, s as PlaywrightTestCase, t as PlaywrightTestResult, u as RawStepEvent, v as RawTestCase, w as STORY_META_KEY, x as StepKeyword, y as StepMode, z as StoryFileReport, V as VitestAdapterOptions, A as VitestSerializedError, B as VitestState, E as VitestTestCase, F as VitestTestModule, G as VitestTestResult, H as toCIInfo, I as toRawCIInfo } from './index-fqrm5-Xr.js';
|
|
10
3
|
|
|
11
4
|
/** Canonical test status (Cucumber-compatible) */
|
|
12
5
|
type TestStatus = "passed" | "failed" | "skipped" | "pending";
|
|
@@ -14,6 +7,8 @@ type TestStatus = "passed" | "failed" | "skipped" | "pending";
|
|
|
14
7
|
interface StepResult {
|
|
15
8
|
/** Step index (0-based) */
|
|
16
9
|
index: number;
|
|
10
|
+
/** Stable step ID when available */
|
|
11
|
+
stepId?: string;
|
|
17
12
|
/** Step status */
|
|
18
13
|
status: TestStatus;
|
|
19
14
|
/** Duration in milliseconds (default 0) */
|
|
@@ -57,6 +52,8 @@ interface TestCaseResult {
|
|
|
57
52
|
sourceLine: number;
|
|
58
53
|
/** Test status (required) */
|
|
59
54
|
status: TestStatus;
|
|
55
|
+
/** Original adapter/framework status (preserved for diagnostics). */
|
|
56
|
+
rawStatus?: RawStatus;
|
|
60
57
|
/** Duration in milliseconds (required, default 0) */
|
|
61
58
|
durationMs: number;
|
|
62
59
|
/** Error message if failed */
|
|
@@ -1094,6 +1091,7 @@ declare function deriveStepResults(steps: StoryStep[], scenarioStatus: TestStatu
|
|
|
1094
1091
|
*/
|
|
1095
1092
|
declare function mergeStepResults(derived: StepResult[], events?: Array<{
|
|
1096
1093
|
index?: number;
|
|
1094
|
+
stepId?: string;
|
|
1097
1095
|
status?: string;
|
|
1098
1096
|
durationMs?: number;
|
|
1099
1097
|
errorMessage?: string;
|
package/dist/index.js
CHANGED
|
@@ -51,6 +51,7 @@ function deriveStepResults(steps, scenarioStatus, error) {
|
|
|
51
51
|
if (scenarioStatus === "passed") {
|
|
52
52
|
return steps.map((_, index) => ({
|
|
53
53
|
index,
|
|
54
|
+
stepId: steps[index].id,
|
|
54
55
|
status: "passed",
|
|
55
56
|
durationMs: 0
|
|
56
57
|
}));
|
|
@@ -58,6 +59,7 @@ function deriveStepResults(steps, scenarioStatus, error) {
|
|
|
58
59
|
if (scenarioStatus === "skipped" || scenarioStatus === "pending") {
|
|
59
60
|
return steps.map((_, index) => ({
|
|
60
61
|
index,
|
|
62
|
+
stepId: steps[index].id,
|
|
61
63
|
status: scenarioStatus,
|
|
62
64
|
durationMs: 0
|
|
63
65
|
}));
|
|
@@ -65,16 +67,17 @@ function deriveStepResults(steps, scenarioStatus, error) {
|
|
|
65
67
|
const failingIndex = findFailingStepIndex(steps, error);
|
|
66
68
|
return steps.map((_, index) => {
|
|
67
69
|
if (index < failingIndex) {
|
|
68
|
-
return { index, status: "passed", durationMs: 0 };
|
|
70
|
+
return { index, stepId: steps[index].id, status: "passed", durationMs: 0 };
|
|
69
71
|
} else if (index === failingIndex) {
|
|
70
72
|
return {
|
|
71
73
|
index,
|
|
74
|
+
stepId: steps[index].id,
|
|
72
75
|
status: "failed",
|
|
73
76
|
durationMs: 0,
|
|
74
77
|
errorMessage: error?.message
|
|
75
78
|
};
|
|
76
79
|
} else {
|
|
77
|
-
return { index, status: "skipped", durationMs: 0 };
|
|
80
|
+
return { index, stepId: steps[index].id, status: "skipped", durationMs: 0 };
|
|
78
81
|
}
|
|
79
82
|
});
|
|
80
83
|
}
|
|
@@ -96,18 +99,23 @@ function mergeStepResults(derived, events) {
|
|
|
96
99
|
return derived;
|
|
97
100
|
}
|
|
98
101
|
const actualByIndex = /* @__PURE__ */ new Map();
|
|
102
|
+
const actualByStepId = /* @__PURE__ */ new Map();
|
|
99
103
|
for (const event of events) {
|
|
100
104
|
if (event.index !== void 0) {
|
|
101
105
|
actualByIndex.set(event.index, event);
|
|
102
106
|
}
|
|
107
|
+
if (event.stepId) {
|
|
108
|
+
actualByStepId.set(event.stepId, event);
|
|
109
|
+
}
|
|
103
110
|
}
|
|
104
111
|
return derived.map((step) => {
|
|
105
|
-
const actual = actualByIndex.get(step.index);
|
|
112
|
+
const actual = (step.stepId ? actualByStepId.get(step.stepId) : void 0) ?? actualByIndex.get(step.index);
|
|
106
113
|
if (!actual) {
|
|
107
114
|
return step;
|
|
108
115
|
}
|
|
109
116
|
return {
|
|
110
117
|
index: step.index,
|
|
118
|
+
stepId: step.stepId ?? actual.stepId,
|
|
111
119
|
status: normalizeStepStatus(actual.status) ?? step.status,
|
|
112
120
|
durationMs: actual.durationMs ?? step.durationMs,
|
|
113
121
|
errorMessage: actual.errorMessage ?? step.errorMessage
|
|
@@ -241,6 +249,7 @@ function canonicalizeTestCase(raw, options, projectRoot) {
|
|
|
241
249
|
derivedSteps,
|
|
242
250
|
raw.stepEvents?.map((e) => ({
|
|
243
251
|
index: e.index,
|
|
252
|
+
stepId: e.stepId,
|
|
244
253
|
status: e.status,
|
|
245
254
|
durationMs: e.durationMs,
|
|
246
255
|
errorMessage: e.errorMessage
|
|
@@ -262,6 +271,7 @@ function canonicalizeTestCase(raw, options, projectRoot) {
|
|
|
262
271
|
sourceFile,
|
|
263
272
|
sourceLine: raw.sourceLine ?? 1,
|
|
264
273
|
status,
|
|
274
|
+
rawStatus: raw.status,
|
|
265
275
|
durationMs: raw.durationMs ?? 0,
|
|
266
276
|
errorMessage: raw.error?.message,
|
|
267
277
|
errorStack: raw.error?.stack,
|
|
@@ -629,19 +639,34 @@ ${doc.markdown}`,
|
|
|
629
639
|
}
|
|
630
640
|
};
|
|
631
641
|
}
|
|
632
|
-
case "
|
|
642
|
+
case "custom":
|
|
643
|
+
if (doc.type === "visual" && doc.data && typeof doc.data === "object") {
|
|
644
|
+
const data = doc.data;
|
|
645
|
+
const status = typeof data.status === "string" ? data.status : "unknown";
|
|
646
|
+
const lines = [`Visual Check (${status})`];
|
|
647
|
+
if (typeof data.baseline === "string") lines.push(`Baseline: ${data.baseline}`);
|
|
648
|
+
if (typeof data.actual === "string") lines.push(`Actual: ${data.actual}`);
|
|
649
|
+
if (typeof data.diff === "string") lines.push(`Diff: ${data.diff}`);
|
|
650
|
+
return {
|
|
651
|
+
doc_string: {
|
|
652
|
+
content: lines.join("\n"),
|
|
653
|
+
content_type: "text/plain",
|
|
654
|
+
line: 0
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
}
|
|
633
658
|
return {
|
|
634
659
|
doc_string: {
|
|
635
|
-
content: doc.
|
|
636
|
-
content_type: "
|
|
660
|
+
content: JSON.stringify(doc.data, null, 2),
|
|
661
|
+
content_type: "application/json",
|
|
637
662
|
line: 0
|
|
638
663
|
}
|
|
639
664
|
};
|
|
640
|
-
case "
|
|
665
|
+
case "tag":
|
|
641
666
|
return {
|
|
642
667
|
doc_string: {
|
|
643
|
-
content:
|
|
644
|
-
content_type: "
|
|
668
|
+
content: doc.names.map((n) => `@${n}`).join(" "),
|
|
669
|
+
content_type: "text/plain",
|
|
645
670
|
line: 0
|
|
646
671
|
}
|
|
647
672
|
};
|
|
@@ -2159,6 +2184,14 @@ body {
|
|
|
2159
2184
|
font-family: var(--font-mono);
|
|
2160
2185
|
}
|
|
2161
2186
|
|
|
2187
|
+
.outcome-tag {
|
|
2188
|
+
background: var(--status-fail-bg, color-mix(in srgb, var(--destructive, #ef4444) 12%, transparent));
|
|
2189
|
+
color: var(--destructive, #b91c1c);
|
|
2190
|
+
border-color: color-mix(in srgb, var(--destructive, #ef4444) 35%, transparent);
|
|
2191
|
+
text-transform: uppercase;
|
|
2192
|
+
letter-spacing: 0.04em;
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2162
2195
|
.scenario-duration {
|
|
2163
2196
|
font-size: 0.75rem;
|
|
2164
2197
|
color: var(--muted-foreground);
|
|
@@ -2315,6 +2348,27 @@ body {
|
|
|
2315
2348
|
border: 1px solid var(--border);
|
|
2316
2349
|
}
|
|
2317
2350
|
|
|
2351
|
+
.attachment-unavailable {
|
|
2352
|
+
padding: 0.75rem 1rem;
|
|
2353
|
+
border: 1px dashed var(--border);
|
|
2354
|
+
border-radius: calc(var(--radius) - 2px);
|
|
2355
|
+
background: var(--muted, transparent);
|
|
2356
|
+
color: var(--muted-foreground);
|
|
2357
|
+
font-size: 0.8125rem;
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
.attachment-unavailable-label {
|
|
2361
|
+
font-weight: 600;
|
|
2362
|
+
margin-bottom: 0.25rem;
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
.attachment-unavailable-path {
|
|
2366
|
+
font-family: var(--font-mono, ui-monospace, monospace);
|
|
2367
|
+
font-size: 0.75rem;
|
|
2368
|
+
word-break: break-all;
|
|
2369
|
+
opacity: 0.8;
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2318
2372
|
/* ============================================================================
|
|
2319
2373
|
Chevron Icon - smooth rotation
|
|
2320
2374
|
============================================================================ */
|
|
@@ -2880,6 +2934,84 @@ body {
|
|
|
2880
2934
|
font-style: italic;
|
|
2881
2935
|
}
|
|
2882
2936
|
|
|
2937
|
+
.doc-screenshot-missing {
|
|
2938
|
+
padding: 0.75rem 1rem;
|
|
2939
|
+
border: 1px dashed var(--border);
|
|
2940
|
+
border-radius: calc(var(--radius) - 2px);
|
|
2941
|
+
background: var(--muted, transparent);
|
|
2942
|
+
color: var(--muted-foreground);
|
|
2943
|
+
font-size: 0.8125rem;
|
|
2944
|
+
}
|
|
2945
|
+
|
|
2946
|
+
.doc-screenshot-missing-label {
|
|
2947
|
+
font-weight: 600;
|
|
2948
|
+
margin-bottom: 0.25rem;
|
|
2949
|
+
}
|
|
2950
|
+
|
|
2951
|
+
.doc-screenshot-missing-path {
|
|
2952
|
+
font-family: var(--font-mono, ui-monospace, monospace);
|
|
2953
|
+
font-size: 0.75rem;
|
|
2954
|
+
word-break: break-all;
|
|
2955
|
+
opacity: 0.8;
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2958
|
+
/* ============================================================================
|
|
2959
|
+
Documentation Entries - Visual Check
|
|
2960
|
+
============================================================================ */
|
|
2961
|
+
.doc-visual {
|
|
2962
|
+
margin-bottom: 0.5rem;
|
|
2963
|
+
padding: 0.75rem;
|
|
2964
|
+
border: 1px solid var(--border);
|
|
2965
|
+
border-radius: calc(var(--radius) - 2px);
|
|
2966
|
+
background: var(--muted, transparent);
|
|
2967
|
+
}
|
|
2968
|
+
|
|
2969
|
+
.doc-visual:last-child {
|
|
2970
|
+
margin-bottom: 0;
|
|
2971
|
+
}
|
|
2972
|
+
|
|
2973
|
+
.doc-visual-header {
|
|
2974
|
+
font-size: 0.8125rem;
|
|
2975
|
+
font-weight: 600;
|
|
2976
|
+
margin-bottom: 0.5rem;
|
|
2977
|
+
display: flex;
|
|
2978
|
+
align-items: center;
|
|
2979
|
+
gap: 0.5rem;
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
.doc-visual-status {
|
|
2983
|
+
font-size: 0.6875rem;
|
|
2984
|
+
font-family: var(--font-mono);
|
|
2985
|
+
font-weight: 500;
|
|
2986
|
+
padding: 0.125rem 0.5rem;
|
|
2987
|
+
border-radius: 9999px;
|
|
2988
|
+
background: var(--tag-bg);
|
|
2989
|
+
color: var(--tag-color);
|
|
2990
|
+
border: 1px solid var(--tag-border);
|
|
2991
|
+
text-transform: uppercase;
|
|
2992
|
+
letter-spacing: 0.04em;
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
.doc-visual-grid {
|
|
2996
|
+
display: grid;
|
|
2997
|
+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
2998
|
+
gap: 0.5rem;
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
.doc-visual-item {
|
|
3002
|
+
display: flex;
|
|
3003
|
+
flex-direction: column;
|
|
3004
|
+
gap: 0.25rem;
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
.doc-visual-label {
|
|
3008
|
+
font-size: 0.6875rem;
|
|
3009
|
+
font-weight: 600;
|
|
3010
|
+
color: var(--muted-foreground);
|
|
3011
|
+
text-transform: uppercase;
|
|
3012
|
+
letter-spacing: 0.04em;
|
|
3013
|
+
}
|
|
3014
|
+
|
|
2883
3015
|
/* ============================================================================
|
|
2884
3016
|
Documentation Entries - Custom
|
|
2885
3017
|
============================================================================ */
|
|
@@ -12857,11 +12989,18 @@ function renderTagBar(args, deps) {
|
|
|
12857
12989
|
</div>`;
|
|
12858
12990
|
}
|
|
12859
12991
|
|
|
12992
|
+
// src/notifiers/ansi-strip.ts
|
|
12993
|
+
function stripAnsi(text2) {
|
|
12994
|
+
return text2.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
|
|
12995
|
+
}
|
|
12996
|
+
|
|
12860
12997
|
// src/formatters/html/renderers/error-box.ts
|
|
12861
12998
|
function renderErrorBox(args, deps) {
|
|
12862
|
-
const
|
|
12999
|
+
const message = stripAnsi(args.message);
|
|
13000
|
+
const stack = args.stack != null ? stripAnsi(args.stack) : void 0;
|
|
13001
|
+
const body = stack != null ? `${deps.escapeHtml(message)}
|
|
12863
13002
|
|
|
12864
|
-
${deps.escapeHtml(
|
|
13003
|
+
${deps.escapeHtml(stack)}` : deps.escapeHtml(message);
|
|
12865
13004
|
return `<div class="error-box">${body}</div>`;
|
|
12866
13005
|
}
|
|
12867
13006
|
|
|
@@ -12874,6 +13013,7 @@ function renderAttachments(args, deps) {
|
|
|
12874
13013
|
const isImage = att.mediaType.startsWith("image/");
|
|
12875
13014
|
const isVideo = att.mediaType.startsWith("video/");
|
|
12876
13015
|
const isBase64 = att.contentEncoding === "BASE64";
|
|
13016
|
+
const isUnreachableFsPath = deps.embedScreenshots && !isBase64 && typeof att.body === "string" && /^(?:[/\\]|[A-Za-z]:[/\\])/.test(att.body);
|
|
12877
13017
|
if (isImage && deps.embedScreenshots && isBase64) {
|
|
12878
13018
|
return `
|
|
12879
13019
|
<div class="attachment">
|
|
@@ -12881,12 +13021,19 @@ function renderAttachments(args, deps) {
|
|
|
12881
13021
|
<img class="attachment-image" src="data:${att.mediaType};base64,${att.body}" alt="${deps.escapeHtml(att.name)}" />
|
|
12882
13022
|
</div>`;
|
|
12883
13023
|
}
|
|
12884
|
-
if (isVideo && deps.embedScreenshots) {
|
|
13024
|
+
if (isVideo && deps.embedScreenshots && !isUnreachableFsPath) {
|
|
12885
13025
|
const src = isBase64 ? `data:${att.mediaType};base64,${att.body}` : att.body;
|
|
12886
13026
|
return `
|
|
12887
13027
|
<div class="attachment">
|
|
12888
13028
|
${deps.escapeHtml(att.name)}
|
|
12889
13029
|
<video class="attachment-video" controls src="${deps.escapeHtml(src)}"></video>
|
|
13030
|
+
</div>`;
|
|
13031
|
+
}
|
|
13032
|
+
if (isUnreachableFsPath) {
|
|
13033
|
+
return `
|
|
13034
|
+
<div class="attachment attachment-unavailable">
|
|
13035
|
+
<div class="attachment-unavailable-label">${deps.escapeHtml(att.name)} unavailable</div>
|
|
13036
|
+
<div class="attachment-unavailable-path">${deps.escapeHtml(att.body)}</div>
|
|
12890
13037
|
</div>`;
|
|
12891
13038
|
}
|
|
12892
13039
|
const href = isBase64 ? `data:${att.mediaType};base64,${att.body}` : att.body;
|
|
@@ -12969,13 +13116,40 @@ function renderDocScreenshot(entry, deps) {
|
|
|
12969
13116
|
const alt = entry.alt ?? "Screenshot";
|
|
12970
13117
|
const embedEnabled = deps.embedScreenshots ?? true;
|
|
12971
13118
|
const isRemote = /^(?:https?:|data:)/i.test(entry.path);
|
|
12972
|
-
const
|
|
13119
|
+
const embedAttempted = !isRemote && embedEnabled && !!deps.readScreenshot;
|
|
13120
|
+
const inlined = embedAttempted ? deps.readScreenshot(entry.path) : void 0;
|
|
13121
|
+
const isAbsoluteFsPath = !isRemote && /^(?:[/\\]|[A-Za-z]:[/\\])/.test(entry.path);
|
|
13122
|
+
if (embedAttempted && inlined === void 0 && isAbsoluteFsPath) {
|
|
13123
|
+
const captionHtml = entry.alt ? `<div class="doc-screenshot-caption">${deps.escapeHtml(entry.alt)}</div>` : "";
|
|
13124
|
+
return `<div class="doc-screenshot doc-screenshot-missing">
|
|
13125
|
+
<div class="doc-screenshot-missing-label">Screenshot unavailable</div>
|
|
13126
|
+
<div class="doc-screenshot-missing-path">${deps.escapeHtml(entry.path)}</div>
|
|
13127
|
+
${captionHtml}
|
|
13128
|
+
</div>`;
|
|
13129
|
+
}
|
|
13130
|
+
const src = inlined ?? entry.path;
|
|
12973
13131
|
return `<div class="doc-screenshot">
|
|
12974
13132
|
<img src="${deps.escapeHtml(src)}" alt="${deps.escapeHtml(alt)}" class="doc-screenshot-img" />
|
|
12975
13133
|
${entry.alt ? `<div class="doc-screenshot-caption">${deps.escapeHtml(entry.alt)}</div>` : ""}
|
|
12976
13134
|
</div>`;
|
|
12977
13135
|
}
|
|
12978
13136
|
function renderDocCustom(entry, deps) {
|
|
13137
|
+
if (entry.type === "visual" && entry.data && typeof entry.data === "object") {
|
|
13138
|
+
const data = entry.data;
|
|
13139
|
+
const status = typeof data.status === "string" ? data.status : "unknown";
|
|
13140
|
+
const baseline = typeof data.baseline === "string" ? data.baseline : void 0;
|
|
13141
|
+
const actual = typeof data.actual === "string" ? data.actual : void 0;
|
|
13142
|
+
const diff = typeof data.diff === "string" ? data.diff : void 0;
|
|
13143
|
+
const maybeImg = (src, label) => src ? `<div class="doc-visual-item"><div class="doc-visual-label">${deps.escapeHtml(label ?? "")}</div><img src="${deps.escapeHtml(src)}" alt="${deps.escapeHtml(label ?? "visual image")}" class="doc-screenshot-img" /></div>` : "";
|
|
13144
|
+
return `<div class="doc-visual">
|
|
13145
|
+
<div class="doc-visual-header">Visual Check <span class="doc-visual-status">${deps.escapeHtml(status)}</span></div>
|
|
13146
|
+
<div class="doc-visual-grid">
|
|
13147
|
+
${maybeImg(baseline, "Baseline")}
|
|
13148
|
+
${maybeImg(actual, "Actual")}
|
|
13149
|
+
${maybeImg(diff, "Diff")}
|
|
13150
|
+
</div>
|
|
13151
|
+
</div>`;
|
|
13152
|
+
}
|
|
12979
13153
|
const dataStr = JSON.stringify(entry.data, null, 2);
|
|
12980
13154
|
return `<div class="doc-custom">
|
|
12981
13155
|
<div class="doc-custom-type">${deps.escapeHtml(entry.type)}</div>
|
|
@@ -13044,8 +13218,11 @@ function renderStep(step, stepResult, index, deps) {
|
|
|
13044
13218
|
</div>${stepDocs}`;
|
|
13045
13219
|
}
|
|
13046
13220
|
function renderSteps(args, deps) {
|
|
13221
|
+
const byStepId = new Map(
|
|
13222
|
+
args.stepResults.filter((sr) => typeof sr.stepId === "string" && sr.stepId.length > 0).map((sr) => [sr.stepId, sr])
|
|
13223
|
+
);
|
|
13047
13224
|
const stepsHtml = args.steps.map((step, index) => {
|
|
13048
|
-
const stepResult = args.stepResults.find((sr) => sr.index === index);
|
|
13225
|
+
const stepResult = (step.id ? byStepId.get(step.id) : void 0) ?? args.stepResults.find((sr) => sr.index === index);
|
|
13049
13226
|
return renderStep(step, stepResult, index, deps);
|
|
13050
13227
|
}).join("");
|
|
13051
13228
|
return `<div class="steps">${stepsHtml}</div>`;
|
|
@@ -13098,6 +13275,7 @@ function renderScenario(args, deps) {
|
|
|
13098
13275
|
const duration = tc.durationMs > 0 ? `${(tc.durationMs / 1e3).toFixed(2)}s` : "";
|
|
13099
13276
|
const tags = tc.tags.map((t) => `<span class="tag">${deps.escapeHtml(t)}</span>`).join("");
|
|
13100
13277
|
const tickets = (tc.story.tickets ?? []).map((t) => renderTicket(t, deps.ticketUrlTemplate, deps.escapeHtml)).join("");
|
|
13278
|
+
const outcomeBadge = tc.rawStatus === "timeout" || tc.rawStatus === "interrupted" ? `<span class="tag outcome-tag">${deps.escapeHtml(tc.rawStatus)}</span>` : "";
|
|
13101
13279
|
const otelMeta = tc.story.meta?.otel;
|
|
13102
13280
|
let traceBadge = "";
|
|
13103
13281
|
if (otelMeta?.traceId) {
|
|
@@ -13165,7 +13343,7 @@ function renderScenario(args, deps) {
|
|
|
13165
13343
|
<span class="status-icon ${statusClass}">${statusIcon2}</span>
|
|
13166
13344
|
<span class="scenario-name">${deps.escapeHtml(tc.story.scenario)}</span>
|
|
13167
13345
|
</div>
|
|
13168
|
-
<div class="scenario-meta">${tags}${tickets}${sourceLink}${traceBadge}${metricBadges}</div>
|
|
13346
|
+
<div class="scenario-meta">${tags}${tickets}${outcomeBadge}${sourceLink}${traceBadge}${metricBadges}</div>
|
|
13169
13347
|
</div>
|
|
13170
13348
|
<div class="scenario-actions">
|
|
13171
13349
|
<button class="copy-scenario-btn" onclick="copyScenarioAsMarkdown('scenario-${tc.id}')" aria-label="Copy scenario as markdown" title="Copy as Markdown"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>
|
|
@@ -14169,6 +14347,9 @@ var MarkdownFormatter = class {
|
|
|
14169
14347
|
});
|
|
14170
14348
|
meta.push(`Tickets: ${ticketLinks.join(", ")}`);
|
|
14171
14349
|
}
|
|
14350
|
+
if (tc.rawStatus === "timeout" || tc.rawStatus === "interrupted") {
|
|
14351
|
+
meta.push(`Outcome: \`${tc.rawStatus}\``);
|
|
14352
|
+
}
|
|
14172
14353
|
const otelMeta = tc.story.meta?.otel;
|
|
14173
14354
|
if (otelMeta?.traceId) {
|
|
14174
14355
|
const traceTemplate = this.options.traceUrlTemplate;
|
|
@@ -14331,6 +14512,16 @@ var MarkdownFormatter = class {
|
|
|
14331
14512
|
lines.push(`${indent}`);
|
|
14332
14513
|
break;
|
|
14333
14514
|
case "custom":
|
|
14515
|
+
if (entry.type === "visual" && entry.data && typeof entry.data === "object") {
|
|
14516
|
+
const data = entry.data;
|
|
14517
|
+
const status = typeof data.status === "string" ? data.status : "unknown";
|
|
14518
|
+
lines.push(`${indent}**Visual Check** (${status})`);
|
|
14519
|
+
if (typeof data.baseline === "string") lines.push(`${indent}- Baseline: ${data.baseline}`);
|
|
14520
|
+
if (typeof data.actual === "string") lines.push(`${indent}- Actual: ${data.actual}`);
|
|
14521
|
+
if (typeof data.diff === "string") lines.push(`${indent}- Diff: ${data.diff}`);
|
|
14522
|
+
lines.push(`${indent}`);
|
|
14523
|
+
break;
|
|
14524
|
+
}
|
|
14334
14525
|
lines.push(`${indent}**[${entry.type}]**`);
|
|
14335
14526
|
lines.push(`${indent}`);
|
|
14336
14527
|
lines.push(`${indent}\`\`\`json`);
|
|
@@ -16982,6 +17173,14 @@ function adaptPlaywrightRun(testResults, options = {}) {
|
|
|
16982
17173
|
message: result.errors[0].message,
|
|
16983
17174
|
stack: result.errors[0].stack
|
|
16984
17175
|
} : void 0;
|
|
17176
|
+
const stepEvents = result.stepEvents?.length ? result.stepEvents : story.steps.map(
|
|
17177
|
+
(step, index) => step.durationMs !== void 0 ? {
|
|
17178
|
+
index,
|
|
17179
|
+
stepId: step.id,
|
|
17180
|
+
title: step.text,
|
|
17181
|
+
durationMs: step.durationMs
|
|
17182
|
+
} : void 0
|
|
17183
|
+
).filter((e) => e !== void 0);
|
|
16985
17184
|
testCases.push({
|
|
16986
17185
|
externalId: test.titlePath().join(" > "),
|
|
16987
17186
|
title: story.scenario,
|
|
@@ -16992,8 +17191,7 @@ function adaptPlaywrightRun(testResults, options = {}) {
|
|
|
16992
17191
|
status: mapPlaywrightStatus(result.status),
|
|
16993
17192
|
durationMs: result.duration,
|
|
16994
17193
|
error,
|
|
16995
|
-
stepEvents: void 0,
|
|
16996
|
-
// Playwright could provide step info, but we don't capture it yet
|
|
17194
|
+
stepEvents: stepEvents.length > 0 ? stepEvents : void 0,
|
|
16997
17195
|
attachments,
|
|
16998
17196
|
meta: {
|
|
16999
17197
|
playwrightStatus: result.status,
|
|
@@ -18013,11 +18211,6 @@ function resolveTraceUrl(template, traceId) {
|
|
|
18013
18211
|
return template.replace(/\{traceId\}/g, traceId);
|
|
18014
18212
|
}
|
|
18015
18213
|
|
|
18016
|
-
// src/notifiers/ansi-strip.ts
|
|
18017
|
-
function stripAnsi(text2) {
|
|
18018
|
-
return text2.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
|
|
18019
|
-
}
|
|
18020
|
-
|
|
18021
18214
|
// src/notifiers/slack.ts
|
|
18022
18215
|
function truncate(text2, maxLen) {
|
|
18023
18216
|
if (text2.length <= maxLen) return text2;
|