@vizzly-testing/cli 0.21.2 → 0.22.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/dist/cli.js +43 -13
- package/dist/commands/tdd-daemon.js +3 -2
- package/dist/reporter/reporter-bundle.css +1 -1
- package/dist/reporter/reporter-bundle.iife.js +28 -28
- package/dist/reporter-ssr/ssr-entry.js +558 -0
- package/dist/server/handlers/tdd-handler.js +22 -26
- package/dist/services/static-report-generator.js +205 -0
- package/dist/utils/browser.js +24 -4
- package/package.json +3 -2
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { renderToString } from "react-dom/server";
|
|
3
|
+
const statusConfig = {
|
|
4
|
+
failed: {
|
|
5
|
+
label: "Changed",
|
|
6
|
+
borderClass: "border-l-red-500",
|
|
7
|
+
badgeClass: "bg-red-500/15 text-red-400 ring-red-500/30",
|
|
8
|
+
dotClass: "bg-red-500",
|
|
9
|
+
sectionTitle: "Visual Changes",
|
|
10
|
+
sectionIcon: "◐"
|
|
11
|
+
},
|
|
12
|
+
new: {
|
|
13
|
+
label: "New",
|
|
14
|
+
borderClass: "border-l-blue-500",
|
|
15
|
+
badgeClass: "bg-blue-500/15 text-blue-400 ring-blue-500/30",
|
|
16
|
+
dotClass: "bg-blue-500",
|
|
17
|
+
sectionTitle: "New Screenshots",
|
|
18
|
+
sectionIcon: "+"
|
|
19
|
+
},
|
|
20
|
+
passed: {
|
|
21
|
+
label: "Passed",
|
|
22
|
+
borderClass: "border-l-emerald-500",
|
|
23
|
+
badgeClass: "bg-emerald-500/15 text-emerald-400 ring-emerald-500/30",
|
|
24
|
+
dotClass: "bg-emerald-500",
|
|
25
|
+
sectionTitle: "Passed",
|
|
26
|
+
sectionIcon: "✓"
|
|
27
|
+
},
|
|
28
|
+
error: {
|
|
29
|
+
label: "Error",
|
|
30
|
+
borderClass: "border-l-orange-500",
|
|
31
|
+
badgeClass: "bg-orange-500/15 text-orange-400 ring-orange-500/30",
|
|
32
|
+
dotClass: "bg-orange-500",
|
|
33
|
+
sectionTitle: "Errors",
|
|
34
|
+
sectionIcon: "!"
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
function StatusBadge({ status }) {
|
|
38
|
+
let config = statusConfig[status] || statusConfig.error;
|
|
39
|
+
return /* @__PURE__ */ jsxs(
|
|
40
|
+
"span",
|
|
41
|
+
{
|
|
42
|
+
className: `inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs font-medium ring-1 ring-inset ${config.badgeClass}`,
|
|
43
|
+
children: [
|
|
44
|
+
/* @__PURE__ */ jsx("span", { className: `w-1.5 h-1.5 rounded-full ${config.dotClass}` }),
|
|
45
|
+
config.label
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
function DiffBadge({ percentage }) {
|
|
51
|
+
if (percentage === void 0 || percentage === null || percentage === 0)
|
|
52
|
+
return null;
|
|
53
|
+
return /* @__PURE__ */ jsxs("span", { className: "font-mono text-xs text-red-400/80 tabular-nums", children: [
|
|
54
|
+
percentage.toFixed(2),
|
|
55
|
+
"%"
|
|
56
|
+
] });
|
|
57
|
+
}
|
|
58
|
+
function MetaInfo({ properties }) {
|
|
59
|
+
if (!properties) return null;
|
|
60
|
+
let { viewport_width, viewport_height, browser } = properties;
|
|
61
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-slate-500", children: [
|
|
62
|
+
viewport_width && viewport_height && /* @__PURE__ */ jsxs("span", { className: "font-mono tabular-nums", children: [
|
|
63
|
+
viewport_width,
|
|
64
|
+
"×",
|
|
65
|
+
viewport_height
|
|
66
|
+
] }),
|
|
67
|
+
browser && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
68
|
+
/* @__PURE__ */ jsx("span", { className: "text-slate-600", children: "·" }),
|
|
69
|
+
/* @__PURE__ */ jsx("span", { className: "capitalize", children: browser })
|
|
70
|
+
] })
|
|
71
|
+
] });
|
|
72
|
+
}
|
|
73
|
+
function FailedComparison({ comparison, isEven }) {
|
|
74
|
+
let { name, status, current, baseline, diff, diffPercentage, properties } = comparison;
|
|
75
|
+
let config = statusConfig[status] || statusConfig.failed;
|
|
76
|
+
return /* @__PURE__ */ jsxs("details", { className: "group", children: [
|
|
77
|
+
/* @__PURE__ */ jsxs(
|
|
78
|
+
"summary",
|
|
79
|
+
{
|
|
80
|
+
className: `
|
|
81
|
+
flex items-center gap-4 p-4 cursor-pointer
|
|
82
|
+
${isEven ? "bg-slate-800/30" : "bg-slate-800/50"}
|
|
83
|
+
hover:bg-slate-700/50
|
|
84
|
+
border-l-4 ${config.borderClass}
|
|
85
|
+
rounded-r-lg transition-all duration-150
|
|
86
|
+
list-none [&::-webkit-details-marker]:hidden
|
|
87
|
+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-500/50 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-900
|
|
88
|
+
`,
|
|
89
|
+
children: [
|
|
90
|
+
/* @__PURE__ */ jsxs("div", { className: "relative w-16 h-10 flex-shrink-0 rounded overflow-hidden bg-slate-900", children: [
|
|
91
|
+
(current || baseline) && /* @__PURE__ */ jsx(
|
|
92
|
+
"img",
|
|
93
|
+
{
|
|
94
|
+
src: current || baseline,
|
|
95
|
+
alt: "",
|
|
96
|
+
className: "w-full h-full object-cover object-top"
|
|
97
|
+
}
|
|
98
|
+
),
|
|
99
|
+
diff && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-red-500/20 flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "w-2 h-2 bg-red-500 rounded-full animate-pulse" }) })
|
|
100
|
+
] }),
|
|
101
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
102
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
103
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-white truncate", children: name }),
|
|
104
|
+
/* @__PURE__ */ jsx(DiffBadge, { percentage: diffPercentage })
|
|
105
|
+
] }),
|
|
106
|
+
/* @__PURE__ */ jsx(MetaInfo, { properties })
|
|
107
|
+
] }),
|
|
108
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
109
|
+
/* @__PURE__ */ jsx(StatusBadge, { status }),
|
|
110
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-slate-500 group-hover:text-slate-400 transition-colors", children: [
|
|
111
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs hidden sm:inline opacity-0 group-hover:opacity-100 transition-opacity", children: "Details" }),
|
|
112
|
+
/* @__PURE__ */ jsx(
|
|
113
|
+
"svg",
|
|
114
|
+
{
|
|
115
|
+
className: "w-5 h-5 transition-transform duration-200 group-open:rotate-180",
|
|
116
|
+
fill: "none",
|
|
117
|
+
viewBox: "0 0 24 24",
|
|
118
|
+
stroke: "currentColor",
|
|
119
|
+
"aria-hidden": "true",
|
|
120
|
+
children: /* @__PURE__ */ jsx(
|
|
121
|
+
"path",
|
|
122
|
+
{
|
|
123
|
+
strokeLinecap: "round",
|
|
124
|
+
strokeLinejoin: "round",
|
|
125
|
+
strokeWidth: 2,
|
|
126
|
+
d: "M19 9l-7 7-7-7"
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
] })
|
|
132
|
+
] })
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
),
|
|
136
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 p-4 bg-slate-800/20 rounded-lg border border-slate-700/30 animate-[fadeIn_150ms_ease-out]", children: [
|
|
137
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-4", children: [
|
|
138
|
+
baseline && /* @__PURE__ */ jsxs("div", { children: [
|
|
139
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
140
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-slate-400 uppercase tracking-wider", children: "Baseline" }),
|
|
141
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-slate-600", children: "Expected" })
|
|
142
|
+
] }),
|
|
143
|
+
/* @__PURE__ */ jsx(
|
|
144
|
+
"a",
|
|
145
|
+
{
|
|
146
|
+
href: baseline,
|
|
147
|
+
target: "_blank",
|
|
148
|
+
rel: "noopener noreferrer",
|
|
149
|
+
className: "block rounded-lg overflow-hidden border border-slate-700/50 hover:border-slate-600 transition-colors",
|
|
150
|
+
children: /* @__PURE__ */ jsx(
|
|
151
|
+
"img",
|
|
152
|
+
{
|
|
153
|
+
src: baseline,
|
|
154
|
+
alt: `${name} baseline`,
|
|
155
|
+
className: "w-full"
|
|
156
|
+
}
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
] }),
|
|
161
|
+
current && /* @__PURE__ */ jsxs("div", { children: [
|
|
162
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
163
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-slate-400 uppercase tracking-wider", children: "Current" }),
|
|
164
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-slate-600", children: "Actual" })
|
|
165
|
+
] }),
|
|
166
|
+
/* @__PURE__ */ jsx(
|
|
167
|
+
"a",
|
|
168
|
+
{
|
|
169
|
+
href: current,
|
|
170
|
+
target: "_blank",
|
|
171
|
+
rel: "noopener noreferrer",
|
|
172
|
+
className: "block rounded-lg overflow-hidden border border-slate-700/50 hover:border-slate-600 transition-colors",
|
|
173
|
+
children: /* @__PURE__ */ jsx("img", { src: current, alt: `${name} current`, className: "w-full" })
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
] })
|
|
177
|
+
] }),
|
|
178
|
+
diff && /* @__PURE__ */ jsxs("div", { className: "mt-4 pt-4 border-t border-slate-700/30", children: [
|
|
179
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
180
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-red-400 uppercase tracking-wider", children: "Difference" }),
|
|
181
|
+
diffPercentage > 0 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-red-400/60 font-mono", children: [
|
|
182
|
+
diffPercentage.toFixed(2),
|
|
183
|
+
"% of pixels changed"
|
|
184
|
+
] })
|
|
185
|
+
] }),
|
|
186
|
+
/* @__PURE__ */ jsx(
|
|
187
|
+
"a",
|
|
188
|
+
{
|
|
189
|
+
href: diff,
|
|
190
|
+
target: "_blank",
|
|
191
|
+
rel: "noopener noreferrer",
|
|
192
|
+
className: "block rounded-lg overflow-hidden border border-red-500/30 hover:border-red-500/50 transition-colors max-w-2xl",
|
|
193
|
+
children: /* @__PURE__ */ jsx("img", { src: diff, alt: `${name} diff`, className: "w-full" })
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
] })
|
|
197
|
+
] })
|
|
198
|
+
] });
|
|
199
|
+
}
|
|
200
|
+
function NewComparison({ comparison, isEven }) {
|
|
201
|
+
let { name, current, baseline, properties } = comparison;
|
|
202
|
+
let imageSrc = current || baseline;
|
|
203
|
+
return /* @__PURE__ */ jsxs("details", { className: "group", children: [
|
|
204
|
+
/* @__PURE__ */ jsxs(
|
|
205
|
+
"summary",
|
|
206
|
+
{
|
|
207
|
+
className: `
|
|
208
|
+
flex items-center gap-4 p-4 cursor-pointer
|
|
209
|
+
${isEven ? "bg-slate-800/30" : "bg-slate-800/50"}
|
|
210
|
+
hover:bg-slate-700/50
|
|
211
|
+
border-l-4 border-l-blue-500
|
|
212
|
+
rounded-r-lg transition-all duration-150
|
|
213
|
+
list-none [&::-webkit-details-marker]:hidden
|
|
214
|
+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-500/50 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-900
|
|
215
|
+
`,
|
|
216
|
+
children: [
|
|
217
|
+
/* @__PURE__ */ jsx("div", { className: "w-16 h-10 flex-shrink-0 rounded overflow-hidden bg-slate-900", children: imageSrc && /* @__PURE__ */ jsx(
|
|
218
|
+
"img",
|
|
219
|
+
{
|
|
220
|
+
src: imageSrc,
|
|
221
|
+
alt: "",
|
|
222
|
+
className: "w-full h-full object-cover object-top"
|
|
223
|
+
}
|
|
224
|
+
) }),
|
|
225
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
226
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-white truncate block", children: name }),
|
|
227
|
+
/* @__PURE__ */ jsx(MetaInfo, { properties })
|
|
228
|
+
] }),
|
|
229
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
230
|
+
/* @__PURE__ */ jsx(StatusBadge, { status: "new" }),
|
|
231
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-slate-500 group-hover:text-slate-400 transition-colors", children: [
|
|
232
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs hidden sm:inline opacity-0 group-hover:opacity-100 transition-opacity", children: "Preview" }),
|
|
233
|
+
/* @__PURE__ */ jsx(
|
|
234
|
+
"svg",
|
|
235
|
+
{
|
|
236
|
+
className: "w-5 h-5 transition-transform duration-200 group-open:rotate-180",
|
|
237
|
+
fill: "none",
|
|
238
|
+
viewBox: "0 0 24 24",
|
|
239
|
+
stroke: "currentColor",
|
|
240
|
+
"aria-hidden": "true",
|
|
241
|
+
children: /* @__PURE__ */ jsx(
|
|
242
|
+
"path",
|
|
243
|
+
{
|
|
244
|
+
strokeLinecap: "round",
|
|
245
|
+
strokeLinejoin: "round",
|
|
246
|
+
strokeWidth: 2,
|
|
247
|
+
d: "M19 9l-7 7-7-7"
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
] })
|
|
253
|
+
] })
|
|
254
|
+
]
|
|
255
|
+
}
|
|
256
|
+
),
|
|
257
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 p-4 bg-slate-800/20 rounded-lg border border-slate-700/30 animate-[fadeIn_150ms_ease-out]", children: [
|
|
258
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
259
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-blue-400 uppercase tracking-wider", children: "New Screenshot" }),
|
|
260
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-slate-600", children: "No baseline to compare" })
|
|
261
|
+
] }),
|
|
262
|
+
imageSrc && /* @__PURE__ */ jsx(
|
|
263
|
+
"a",
|
|
264
|
+
{
|
|
265
|
+
href: imageSrc,
|
|
266
|
+
target: "_blank",
|
|
267
|
+
rel: "noopener noreferrer",
|
|
268
|
+
className: "block rounded-lg overflow-hidden border border-blue-500/30 hover:border-blue-500/50 transition-colors max-w-2xl",
|
|
269
|
+
children: /* @__PURE__ */ jsx("img", { src: imageSrc, alt: name, className: "w-full" })
|
|
270
|
+
}
|
|
271
|
+
)
|
|
272
|
+
] })
|
|
273
|
+
] });
|
|
274
|
+
}
|
|
275
|
+
function PassedComparison({ comparison }) {
|
|
276
|
+
let { name, properties } = comparison;
|
|
277
|
+
return /* @__PURE__ */ jsxs(
|
|
278
|
+
"div",
|
|
279
|
+
{
|
|
280
|
+
className: `
|
|
281
|
+
flex items-center gap-3 px-4 py-2.5
|
|
282
|
+
bg-slate-800/20 hover:bg-slate-800/30
|
|
283
|
+
border-l-4 border-l-emerald-500/50
|
|
284
|
+
rounded-r-lg transition-colors
|
|
285
|
+
`,
|
|
286
|
+
children: [
|
|
287
|
+
/* @__PURE__ */ jsx("div", { className: "w-5 h-5 rounded-full bg-emerald-500/20 flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsx(
|
|
288
|
+
"svg",
|
|
289
|
+
{
|
|
290
|
+
className: "w-3 h-3 text-emerald-400",
|
|
291
|
+
fill: "currentColor",
|
|
292
|
+
viewBox: "0 0 20 20",
|
|
293
|
+
"aria-hidden": "true",
|
|
294
|
+
children: /* @__PURE__ */ jsx(
|
|
295
|
+
"path",
|
|
296
|
+
{
|
|
297
|
+
fillRule: "evenodd",
|
|
298
|
+
d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z",
|
|
299
|
+
clipRule: "evenodd"
|
|
300
|
+
}
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
) }),
|
|
304
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-sm text-slate-300 truncate", children: name }),
|
|
305
|
+
/* @__PURE__ */ jsx(MetaInfo, { properties })
|
|
306
|
+
]
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
function SectionHeader({ title, count, icon, colorClass }) {
|
|
311
|
+
if (count === 0) return null;
|
|
312
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-3", children: [
|
|
313
|
+
/* @__PURE__ */ jsx("span", { className: `text-lg font-mono ${colorClass}`, children: icon }),
|
|
314
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-slate-300 uppercase tracking-wider", children: title }),
|
|
315
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm text-slate-500 font-mono", children: [
|
|
316
|
+
"(",
|
|
317
|
+
count,
|
|
318
|
+
")"
|
|
319
|
+
] })
|
|
320
|
+
] });
|
|
321
|
+
}
|
|
322
|
+
function SummaryBar({ stats }) {
|
|
323
|
+
let hasIssues = stats.failed > 0 || stats.new > 0;
|
|
324
|
+
return /* @__PURE__ */ jsxs(
|
|
325
|
+
"div",
|
|
326
|
+
{
|
|
327
|
+
className: `
|
|
328
|
+
flex flex-wrap items-center gap-6 p-4 rounded-xl mb-8
|
|
329
|
+
${hasIssues ? "bg-slate-800/60 border border-slate-700/50" : "bg-emerald-500/10 border border-emerald-500/20"}
|
|
330
|
+
`,
|
|
331
|
+
children: [
|
|
332
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: hasIssues ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
333
|
+
/* @__PURE__ */ jsx("div", { className: "w-10 h-10 rounded-full bg-amber-500/20 flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "text-amber-400 text-lg", children: "◐" }) }),
|
|
334
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
335
|
+
/* @__PURE__ */ jsx("div", { className: "text-white font-semibold", children: "Review Required" }),
|
|
336
|
+
/* @__PURE__ */ jsxs("div", { className: "text-sm text-slate-400", children: [
|
|
337
|
+
stats.failed + (stats.new || 0),
|
|
338
|
+
" items need attention"
|
|
339
|
+
] })
|
|
340
|
+
] })
|
|
341
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
342
|
+
/* @__PURE__ */ jsx("div", { className: "w-10 h-10 rounded-full bg-emerald-500/20 flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
343
|
+
"svg",
|
|
344
|
+
{
|
|
345
|
+
className: "w-5 h-5 text-emerald-400",
|
|
346
|
+
fill: "currentColor",
|
|
347
|
+
viewBox: "0 0 20 20",
|
|
348
|
+
"aria-hidden": "true",
|
|
349
|
+
children: /* @__PURE__ */ jsx(
|
|
350
|
+
"path",
|
|
351
|
+
{
|
|
352
|
+
fillRule: "evenodd",
|
|
353
|
+
d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z",
|
|
354
|
+
clipRule: "evenodd"
|
|
355
|
+
}
|
|
356
|
+
)
|
|
357
|
+
}
|
|
358
|
+
) }),
|
|
359
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
360
|
+
/* @__PURE__ */ jsx("div", { className: "text-white font-semibold", children: "All Tests Passed" }),
|
|
361
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm text-slate-400", children: "No visual changes detected" })
|
|
362
|
+
] })
|
|
363
|
+
] }) }),
|
|
364
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-6 ml-auto", children: [
|
|
365
|
+
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
366
|
+
/* @__PURE__ */ jsx("div", { className: "text-2xl font-bold text-white tabular-nums", children: stats.total }),
|
|
367
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 uppercase tracking-wider", children: "Total" })
|
|
368
|
+
] }),
|
|
369
|
+
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
370
|
+
/* @__PURE__ */ jsx("div", { className: "text-2xl font-bold text-emerald-400 tabular-nums", children: stats.passed }),
|
|
371
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 uppercase tracking-wider", children: "Passed" })
|
|
372
|
+
] }),
|
|
373
|
+
stats.failed > 0 && /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
374
|
+
/* @__PURE__ */ jsx("div", { className: "text-2xl font-bold text-red-400 tabular-nums", children: stats.failed }),
|
|
375
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 uppercase tracking-wider", children: "Changed" })
|
|
376
|
+
] }),
|
|
377
|
+
(stats.new || 0) > 0 && /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
378
|
+
/* @__PURE__ */ jsx("div", { className: "text-2xl font-bold text-blue-400 tabular-nums", children: stats.new }),
|
|
379
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 uppercase tracking-wider", children: "New" })
|
|
380
|
+
] })
|
|
381
|
+
] })
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
function formatTimestamp(timestamp) {
|
|
387
|
+
if (!timestamp) return null;
|
|
388
|
+
let date = new Date(timestamp);
|
|
389
|
+
return date.toLocaleDateString(void 0, {
|
|
390
|
+
year: "numeric",
|
|
391
|
+
month: "short",
|
|
392
|
+
day: "numeric",
|
|
393
|
+
hour: "2-digit",
|
|
394
|
+
minute: "2-digit"
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
function Header({ timestamp }) {
|
|
398
|
+
return /* @__PURE__ */ jsx("header", { className: "bg-slate-950/80 backdrop-blur-sm border-b border-slate-800/60 sticky top-0 z-10", children: /* @__PURE__ */ jsxs("div", { className: "max-w-6xl mx-auto px-6 py-4 flex items-center justify-between", children: [
|
|
399
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
400
|
+
/* @__PURE__ */ jsx("div", { className: "w-9 h-9 bg-gradient-to-br from-amber-400 to-amber-600 rounded-lg flex items-center justify-center shadow-lg shadow-amber-500/20", children: /* @__PURE__ */ jsxs(
|
|
401
|
+
"svg",
|
|
402
|
+
{
|
|
403
|
+
className: "w-5 h-5 text-slate-900",
|
|
404
|
+
viewBox: "0 0 24 24",
|
|
405
|
+
fill: "currentColor",
|
|
406
|
+
"aria-hidden": "true",
|
|
407
|
+
children: [
|
|
408
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" }),
|
|
409
|
+
/* @__PURE__ */ jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" })
|
|
410
|
+
]
|
|
411
|
+
}
|
|
412
|
+
) }),
|
|
413
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
414
|
+
/* @__PURE__ */ jsx("div", { className: "text-lg font-semibold text-white tracking-tight", children: "Vizzly" }),
|
|
415
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500", children: "Visual Test Report" })
|
|
416
|
+
] })
|
|
417
|
+
] }),
|
|
418
|
+
timestamp && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 tabular-nums", children: formatTimestamp(timestamp) })
|
|
419
|
+
] }) });
|
|
420
|
+
}
|
|
421
|
+
function StaticReportView({ reportData }) {
|
|
422
|
+
if (!reportData || !reportData.comparisons) {
|
|
423
|
+
return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-slate-900 text-white flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
424
|
+
/* @__PURE__ */ jsx("div", { className: "text-xl font-medium mb-2", children: "No Report Data" }),
|
|
425
|
+
/* @__PURE__ */ jsx("p", { className: "text-slate-400", children: "No comparison data available for this report." })
|
|
426
|
+
] }) });
|
|
427
|
+
}
|
|
428
|
+
let { comparisons } = reportData;
|
|
429
|
+
let stats = {
|
|
430
|
+
total: comparisons.length,
|
|
431
|
+
passed: comparisons.filter((c) => c.status === "passed").length,
|
|
432
|
+
failed: comparisons.filter((c) => c.status === "failed").length,
|
|
433
|
+
new: comparisons.filter((c) => c.status === "new").length,
|
|
434
|
+
error: comparisons.filter((c) => c.status === "error").length
|
|
435
|
+
};
|
|
436
|
+
let failed = comparisons.filter((c) => c.status === "failed");
|
|
437
|
+
let newItems = comparisons.filter((c) => c.status === "new");
|
|
438
|
+
let passed = comparisons.filter((c) => c.status === "passed");
|
|
439
|
+
let errors = comparisons.filter((c) => c.status === "error");
|
|
440
|
+
return /* @__PURE__ */ jsxs("div", { className: "min-h-screen bg-slate-900 text-white", children: [
|
|
441
|
+
/* @__PURE__ */ jsx(Header, { timestamp: reportData.timestamp }),
|
|
442
|
+
/* @__PURE__ */ jsxs("main", { className: "max-w-6xl mx-auto px-6 py-8", children: [
|
|
443
|
+
/* @__PURE__ */ jsx(SummaryBar, { stats }),
|
|
444
|
+
failed.length > 0 && /* @__PURE__ */ jsxs("section", { className: "mb-8", children: [
|
|
445
|
+
/* @__PURE__ */ jsx(
|
|
446
|
+
SectionHeader,
|
|
447
|
+
{
|
|
448
|
+
title: "Visual Changes",
|
|
449
|
+
count: failed.length,
|
|
450
|
+
icon: "◐",
|
|
451
|
+
colorClass: "text-red-400"
|
|
452
|
+
}
|
|
453
|
+
),
|
|
454
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: failed.map((comparison, index) => /* @__PURE__ */ jsx(
|
|
455
|
+
FailedComparison,
|
|
456
|
+
{
|
|
457
|
+
comparison,
|
|
458
|
+
isEven: index % 2 === 0
|
|
459
|
+
},
|
|
460
|
+
comparison.id || comparison.signature || `failed-${index}`
|
|
461
|
+
)) })
|
|
462
|
+
] }),
|
|
463
|
+
newItems.length > 0 && /* @__PURE__ */ jsxs("section", { className: "mb-8", children: [
|
|
464
|
+
/* @__PURE__ */ jsx(
|
|
465
|
+
SectionHeader,
|
|
466
|
+
{
|
|
467
|
+
title: "New Screenshots",
|
|
468
|
+
count: newItems.length,
|
|
469
|
+
icon: "+",
|
|
470
|
+
colorClass: "text-blue-400"
|
|
471
|
+
}
|
|
472
|
+
),
|
|
473
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: newItems.map((comparison, index) => /* @__PURE__ */ jsx(
|
|
474
|
+
NewComparison,
|
|
475
|
+
{
|
|
476
|
+
comparison,
|
|
477
|
+
isEven: index % 2 === 0
|
|
478
|
+
},
|
|
479
|
+
comparison.id || comparison.signature || `new-${index}`
|
|
480
|
+
)) })
|
|
481
|
+
] }),
|
|
482
|
+
errors.length > 0 && /* @__PURE__ */ jsxs("section", { className: "mb-8", children: [
|
|
483
|
+
/* @__PURE__ */ jsx(
|
|
484
|
+
SectionHeader,
|
|
485
|
+
{
|
|
486
|
+
title: "Errors",
|
|
487
|
+
count: errors.length,
|
|
488
|
+
icon: "!",
|
|
489
|
+
colorClass: "text-orange-400"
|
|
490
|
+
}
|
|
491
|
+
),
|
|
492
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: errors.map((comparison, index) => /* @__PURE__ */ jsx(
|
|
493
|
+
FailedComparison,
|
|
494
|
+
{
|
|
495
|
+
comparison,
|
|
496
|
+
isEven: index % 2 === 0
|
|
497
|
+
},
|
|
498
|
+
comparison.id || comparison.signature || `error-${index}`
|
|
499
|
+
)) })
|
|
500
|
+
] }),
|
|
501
|
+
passed.length > 0 && /* @__PURE__ */ jsx("section", { className: "mb-8", children: /* @__PURE__ */ jsxs("details", { className: "group", children: [
|
|
502
|
+
/* @__PURE__ */ jsx("summary", { className: "cursor-pointer list-none [&::-webkit-details-marker]:hidden", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-3", children: [
|
|
503
|
+
/* @__PURE__ */ jsx("span", { className: "text-lg font-mono text-emerald-400", children: "✓" }),
|
|
504
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-slate-300 uppercase tracking-wider", children: "Passed" }),
|
|
505
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm text-slate-500 font-mono", children: [
|
|
506
|
+
"(",
|
|
507
|
+
passed.length,
|
|
508
|
+
")"
|
|
509
|
+
] }),
|
|
510
|
+
/* @__PURE__ */ jsx(
|
|
511
|
+
"svg",
|
|
512
|
+
{
|
|
513
|
+
className: "w-4 h-4 text-slate-500 transition-transform group-open:rotate-180 ml-auto",
|
|
514
|
+
fill: "none",
|
|
515
|
+
viewBox: "0 0 24 24",
|
|
516
|
+
stroke: "currentColor",
|
|
517
|
+
"aria-hidden": "true",
|
|
518
|
+
children: /* @__PURE__ */ jsx(
|
|
519
|
+
"path",
|
|
520
|
+
{
|
|
521
|
+
strokeLinecap: "round",
|
|
522
|
+
strokeLinejoin: "round",
|
|
523
|
+
strokeWidth: 2,
|
|
524
|
+
d: "M19 9l-7 7-7-7"
|
|
525
|
+
}
|
|
526
|
+
)
|
|
527
|
+
}
|
|
528
|
+
)
|
|
529
|
+
] }) }),
|
|
530
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1", children: passed.map((comparison, index) => /* @__PURE__ */ jsx(
|
|
531
|
+
PassedComparison,
|
|
532
|
+
{
|
|
533
|
+
comparison
|
|
534
|
+
},
|
|
535
|
+
comparison.id || comparison.signature || `passed-${index}`
|
|
536
|
+
)) })
|
|
537
|
+
] }) }),
|
|
538
|
+
/* @__PURE__ */ jsx("footer", { className: "mt-12 pt-6 border-t border-slate-800/60 text-center", children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-slate-500", children: [
|
|
539
|
+
"Visual regression report generated by",
|
|
540
|
+
" ",
|
|
541
|
+
/* @__PURE__ */ jsx(
|
|
542
|
+
"a",
|
|
543
|
+
{
|
|
544
|
+
href: "https://vizzly.dev",
|
|
545
|
+
className: "text-amber-400 hover:text-amber-300 transition-colors",
|
|
546
|
+
children: "Vizzly"
|
|
547
|
+
}
|
|
548
|
+
)
|
|
549
|
+
] }) })
|
|
550
|
+
] })
|
|
551
|
+
] });
|
|
552
|
+
}
|
|
553
|
+
function renderStaticReport(reportData) {
|
|
554
|
+
return renderToString(/* @__PURE__ */ jsx(StaticReportView, { reportData }));
|
|
555
|
+
}
|
|
556
|
+
export {
|
|
557
|
+
renderStaticReport
|
|
558
|
+
};
|
|
@@ -403,21 +403,21 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
403
403
|
|
|
404
404
|
// Update comparison in report data file
|
|
405
405
|
updateComparison(newComparison);
|
|
406
|
+
|
|
407
|
+
// Visual diffs return 200 with status: 'diff' - they're not errors
|
|
408
|
+
// The SDK/user can decide whether to fail tests based on this
|
|
406
409
|
if (comparison.status === 'failed') {
|
|
407
410
|
return {
|
|
408
|
-
statusCode:
|
|
411
|
+
statusCode: 200,
|
|
409
412
|
body: {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
diffPercentage: comparison.diffPercentage,
|
|
419
|
-
threshold: comparison.threshold
|
|
420
|
-
},
|
|
413
|
+
status: 'diff',
|
|
414
|
+
name: comparison.name,
|
|
415
|
+
message: `Visual difference detected for '${name}'`,
|
|
416
|
+
baseline: comparison.baseline,
|
|
417
|
+
current: comparison.current,
|
|
418
|
+
diff: comparison.diff,
|
|
419
|
+
diffPercentage: comparison.diffPercentage,
|
|
420
|
+
threshold: comparison.threshold,
|
|
421
421
|
tddMode: true
|
|
422
422
|
}
|
|
423
423
|
};
|
|
@@ -426,14 +426,11 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
426
426
|
return {
|
|
427
427
|
statusCode: 200,
|
|
428
428
|
body: {
|
|
429
|
-
status: '
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
baseline: comparison.baseline,
|
|
435
|
-
current: comparison.current
|
|
436
|
-
},
|
|
429
|
+
status: 'baseline-updated',
|
|
430
|
+
name: comparison.name,
|
|
431
|
+
message: `Baseline updated for '${name}'`,
|
|
432
|
+
baseline: comparison.baseline,
|
|
433
|
+
current: comparison.current,
|
|
437
434
|
tddMode: true
|
|
438
435
|
}
|
|
439
436
|
};
|
|
@@ -448,15 +445,14 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
448
445
|
};
|
|
449
446
|
}
|
|
450
447
|
|
|
451
|
-
//
|
|
448
|
+
// Match or new baseline
|
|
452
449
|
return {
|
|
453
450
|
statusCode: 200,
|
|
454
451
|
body: {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
},
|
|
452
|
+
status: comparison.status === 'new' ? 'new' : 'match',
|
|
453
|
+
name: comparison.name,
|
|
454
|
+
baseline: comparison.baseline,
|
|
455
|
+
current: comparison.current,
|
|
460
456
|
tddMode: true
|
|
461
457
|
}
|
|
462
458
|
};
|