shiplightai 0.1.21 → 0.1.22

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.
Files changed (49) hide show
  1. package/dist/agentHelpers-ZM2YPYUG.js +3 -0
  2. package/dist/agentLogin-TVTERJCC.js +3 -0
  3. package/dist/chunk-47OHTDHW.js +18 -0
  4. package/dist/chunk-4MP3EHVU.js +11 -0
  5. package/dist/{chunk-32JFHFFG.js → chunk-AEEQXF3N.js} +29 -29
  6. package/dist/chunk-C6BVAOFE.js +3 -0
  7. package/dist/{chunk-RTTIJBGI.js → chunk-CSHDULY4.js} +28 -28
  8. package/dist/chunk-EEEG4LZX.js +3 -0
  9. package/dist/chunk-HJNJZWUA.js +5 -0
  10. package/dist/chunk-ID5JNGPZ.js +18 -0
  11. package/dist/{chunk-YHOTGR6H.js → chunk-J4TZ3OFZ.js} +1 -1
  12. package/dist/chunk-JQ2K2XJ4.js +61 -0
  13. package/dist/chunk-L5JSAS2D.js +3 -0
  14. package/dist/{chunk-YDR4P3GA.js → chunk-NLZ3YJX4.js} +1 -1
  15. package/dist/{chunk-JHSENQ4F.js → chunk-SFCNGCFT.js} +27 -27
  16. package/dist/{chunk-2F3YRAA7.js → chunk-VLVDPV2E.js} +13 -13
  17. package/dist/cjs/debugger-pw.cjs +7 -7
  18. package/dist/cjs/fixture.cjs +162 -147
  19. package/dist/cjs/index.cjs +229 -309
  20. package/dist/cjs/reporter.cjs +725 -0
  21. package/dist/cli.js +2107 -939
  22. package/dist/config-BUSI76YU.js +3 -0
  23. package/dist/debugger-pw.d.ts +5 -0
  24. package/dist/debugger-pw.js +1 -1
  25. package/dist/dist-3HGFS7M3-T7FMZHX4.js +3 -0
  26. package/dist/dist-K7NWI5BS.js +3 -0
  27. package/dist/dist-SIZHXBHX.js +3 -0
  28. package/dist/fixture.js +1 -1
  29. package/dist/handler-J6CCKSPE.js +3 -0
  30. package/dist/index.d.ts +1 -48
  31. package/dist/index.js +4 -11
  32. package/dist/{intRunner-5A6M6JSJ.js → intRunner-ZYEEZ6MK.js} +1 -1
  33. package/dist/reporter.d.ts +28 -0
  34. package/dist/reporter.js +723 -0
  35. package/dist/task-EDC5BGTS.js +192 -0
  36. package/dist/testFlow-HYGLHAP6.js +3 -0
  37. package/package.json +8 -1
  38. package/dist/agentHelpers-2TII7YCW.js +0 -3
  39. package/dist/agentLogin-GDOU6BCP.js +0 -3
  40. package/dist/chunk-DJDHFWEV.js +0 -3
  41. package/dist/chunk-JNRJXAJS.js +0 -18
  42. package/dist/chunk-LPSNOKYP.js +0 -61
  43. package/dist/chunk-THVHM4KG.js +0 -11
  44. package/dist/chunk-USRSZQWN.js +0 -5
  45. package/dist/dist-CXOVVE77.js +0 -3
  46. package/dist/dist-SRXGJZ7P.js +0 -3
  47. package/dist/handler-BAP4TGW6.js +0 -3
  48. package/dist/task-VW6MUEKX.js +0 -192
  49. package/dist/testFlow-ZLC5L5GT.js +0 -3
@@ -0,0 +1,723 @@
1
+ import { createRequire as __createRequire } from "module";
2
+ const require = __createRequire(import.meta.url);
3
+ import{a as T}from"./chunk-4MP3EHVU.js";import{Aa as b}from"./chunk-47OHTDHW.js";import"./chunk-CSINHOOD.js";import*as u from"fs";import*as c from"path";function p(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}function k(e){if(e<1e3)return`${e}ms`;if(e<6e4)return`${(e/1e3).toFixed(1)}s`;let r=Math.floor(e/6e4),s=(e%6e4/1e3).toFixed(0);return`${r}m ${s}s`}function I(e){switch(e){case"passed":case"success":return'<span class="status-icon passed">&#x2714;</span>';case"failed":case"failure":case"timedOut":return'<span class="status-icon failed">&#x2718;</span>';case"skipped":return'<span class="status-icon skipped">&#x2500;</span>';case"interrupted":return'<span class="status-icon failed">&#x26A0;</span>';default:return'<span class="status-icon pending">&#x25CB;</span>'}}function F(e){return`<span class="badge badge-${e}">${e}</span>`}function B(e){let r=e.duration!=null?`<span class="step-duration">${k(e.duration)}</span>`:"",s=I(e.status);if(e.screenshot||e.message||e.error){let l="";e.screenshot&&(l=`<img src="${p(e.screenshot)}" alt="Step screenshot" class="step-screenshot" />`);let i="";e.error&&(i=`<div class="step-error"><pre>${p(e.error)}</pre></div>`);let o="";e.message&&!e.error&&(o=`<div class="step-message">${p(e.message)}</div>`);let d=e.status==="failure"?" open":"";return`
4
+ <details class="step-details step step-${e.status}"${d}>
5
+ <summary class="step-header">
6
+ ${s}
7
+ <span class="step-id">${p(e.stepId)}</span>
8
+ <span class="step-description-collapsed">${p(e.description)}</span>
9
+ ${r}
10
+ </summary>
11
+ <div class="step-expanded">
12
+ ${l}
13
+ <div class="step-description-full">${p(e.description)}</div>
14
+ ${o}
15
+ ${i}
16
+ </div>
17
+ </details>`}return`
18
+ <div class="step step-${e.status}">
19
+ <div class="step-header">
20
+ ${s}
21
+ <span class="step-id">${p(e.stepId)}</span>
22
+ <span class="step-description">${p(e.description)}</span>
23
+ ${r}
24
+ </div>
25
+ </div>`}function D(e,r){let s=I(e.status),g=e.steps.map(B).join(`
26
+ `),l="";e.error&&!e.steps.some(a=>a.error)&&(l=`<div class="test-error"><pre>${p(e.error)}</pre></div>`);let i=[];e.videoPath&&i.push(`
27
+ <details class="artifact-section">
28
+ <summary class="artifact-summary">Video</summary>
29
+ <div class="artifact-content">
30
+ <div class="video-container">
31
+ <video controls preload="metadata" class="artifact-video">
32
+ <source src="${p(e.videoPath)}" type="video/webm" />
33
+ </video>
34
+ <button class="enlarge-btn" onclick="openVideoOverlay(this)" title="Enlarge">&#x26F6;</button>
35
+ </div>
36
+ </div>
37
+ </details>`);let o=e.steps.filter(a=>a.screenshot).map(a=>({src:a.screenshot,stepId:a.stepId,description:a.description,status:a.status,message:a.message||a.error||""}));if(o.length>0){let a=p(JSON.stringify(o)),t=o.map((n,y)=>`
38
+ <div class="screenshot-thumb" onclick="openGalleryAt(this, ${y})" data-gallery="${a}">
39
+ <img src="${p(n.src)}" alt="${p(n.stepId)}" />
40
+ <span class="thumb-label">${p(n.stepId)}</span>
41
+ </div>`).join("");i.push(`
42
+ <details class="artifact-section">
43
+ <summary class="artifact-summary">Screenshots (${o.length})</summary>
44
+ <div class="artifact-content">
45
+ <div class="screenshot-grid">${t}</div>
46
+ </div>
47
+ </details>`)}if(e.tracePath){let a=p(e.tracePath);i.push(`
48
+ <details class="artifact-section">
49
+ <summary class="artifact-summary">Trace</summary>
50
+ <div class="artifact-content">
51
+ <div class="trace-actions">
52
+ <code class="trace-command" id="trace-cmd-${r}">npx playwright show-trace ${a}</code>
53
+ <button class="copy-btn" onclick="copyTraceCmd(${r})" title="Copy command">Copy</button>
54
+ </div>
55
+ <p class="trace-hint">Run this command in your terminal to open the interactive Trace Viewer</p>
56
+ <p class="trace-hint"><a href="${a}" class="attachment-link" download>Download trace.zip</a></p>
57
+ </div>
58
+ </details>`)}let d="";return i.length>0&&(d=`<div class="test-artifacts">${i.join("")}</div>`),`
59
+ <details class="test-details" ${e.status==="failed"||e.status==="timedOut"?"open":""}>
60
+ <summary class="test-summary test-${e.status}">
61
+ ${s}
62
+ <span class="test-title">${p(e.title)}</span>
63
+ <span class="test-file">${p(e.file)}</span>
64
+ ${F(e.status)}
65
+ <span class="test-duration">${k(e.duration)}</span>
66
+ </summary>
67
+ <div class="test-body">
68
+ ${l}
69
+ <div class="steps-list">
70
+ ${g||'<div class="no-steps">No YAML step details available</div>'}
71
+ </div>
72
+ ${d}
73
+ </div>
74
+ </details>`}function R(e){let r=e.tests.filter(o=>o.status==="passed").length,s=e.tests.filter(o=>o.status==="failed"||o.status==="timedOut").length,g=e.tests.filter(o=>o.status==="skipped").length,l=e.tests.length,i=e.tests.map((o,d)=>D(o,d)).join(`
75
+ `);return`<!DOCTYPE html>
76
+ <html lang="en">
77
+ <head>
78
+ <meta charset="UTF-8" />
79
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
80
+ <title>Shiplight Test Report</title>
81
+ <style>
82
+ :root {
83
+ --color-bg: #1a1a2e;
84
+ --color-surface: #16213e;
85
+ --color-surface-hover: #1a2744;
86
+ --color-border: #2a3a5c;
87
+ --color-text: #e0e0e0;
88
+ --color-text-secondary: #8892a4;
89
+ --color-passed: #4caf50;
90
+ --color-failed: #f44336;
91
+ --color-skipped: #ff9800;
92
+ --color-pending: #9e9e9e;
93
+ --color-accent: #64b5f6;
94
+ }
95
+
96
+ * { margin: 0; padding: 0; box-sizing: border-box; }
97
+
98
+ body {
99
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
100
+ background: var(--color-bg);
101
+ color: var(--color-text);
102
+ line-height: 1.5;
103
+ padding: 24px;
104
+ }
105
+
106
+ .container { max-width: 960px; margin: 0 auto; }
107
+
108
+ .header {
109
+ margin-bottom: 24px;
110
+ padding-bottom: 16px;
111
+ border-bottom: 1px solid var(--color-border);
112
+ }
113
+
114
+ .header h1 {
115
+ font-size: 24px;
116
+ font-weight: 600;
117
+ margin-bottom: 12px;
118
+ }
119
+
120
+ .summary {
121
+ display: flex;
122
+ gap: 16px;
123
+ flex-wrap: wrap;
124
+ align-items: center;
125
+ }
126
+
127
+ .summary-stat {
128
+ font-size: 14px;
129
+ padding: 4px 12px;
130
+ border-radius: 4px;
131
+ background: var(--color-surface);
132
+ border: 1px solid var(--color-border);
133
+ }
134
+
135
+ .summary-stat.passed { border-color: var(--color-passed); }
136
+ .summary-stat.failed { border-color: var(--color-failed); }
137
+ .summary-stat.skipped { border-color: var(--color-skipped); }
138
+
139
+ .test-list { display: flex; flex-direction: column; gap: 8px; }
140
+
141
+ .test-details {
142
+ background: var(--color-surface);
143
+ border: 1px solid var(--color-border);
144
+ border-radius: 8px;
145
+ overflow: hidden;
146
+ }
147
+
148
+ .test-summary {
149
+ padding: 12px 16px;
150
+ cursor: pointer;
151
+ display: flex;
152
+ align-items: center;
153
+ gap: 8px;
154
+ user-select: none;
155
+ list-style: none;
156
+ }
157
+
158
+ .test-summary::-webkit-details-marker { display: none; }
159
+
160
+ .test-summary:hover { background: var(--color-surface-hover); }
161
+
162
+ .test-title { font-weight: 500; flex: 1; }
163
+
164
+ .test-file {
165
+ font-size: 12px;
166
+ color: var(--color-text-secondary);
167
+ max-width: 200px;
168
+ overflow: hidden;
169
+ text-overflow: ellipsis;
170
+ white-space: nowrap;
171
+ }
172
+
173
+ .test-duration {
174
+ font-size: 12px;
175
+ color: var(--color-text-secondary);
176
+ min-width: 50px;
177
+ text-align: right;
178
+ }
179
+
180
+ .badge {
181
+ font-size: 11px;
182
+ padding: 2px 8px;
183
+ border-radius: 4px;
184
+ text-transform: uppercase;
185
+ font-weight: 600;
186
+ }
187
+
188
+ .badge-passed { background: rgba(76,175,80,0.2); color: var(--color-passed); }
189
+ .badge-failed, .badge-timedOut { background: rgba(244,67,54,0.2); color: var(--color-failed); }
190
+ .badge-skipped { background: rgba(255,152,0,0.2); color: var(--color-skipped); }
191
+ .badge-interrupted { background: rgba(244,67,54,0.2); color: var(--color-failed); }
192
+
193
+ .test-body { padding: 0 16px 16px; }
194
+
195
+ .step-message {
196
+ font-size: 12px;
197
+ color: var(--color-text-secondary);
198
+ margin: 4px 0;
199
+ line-height: 1.4;
200
+ }
201
+
202
+ .test-error, .step-error {
203
+ background: rgba(244,67,54,0.1);
204
+ border: 1px solid rgba(244,67,54,0.3);
205
+ border-radius: 4px;
206
+ padding: 8px 12px;
207
+ margin: 8px 0;
208
+ }
209
+
210
+ .test-error pre, .step-error pre {
211
+ font-size: 12px;
212
+ white-space: pre-wrap;
213
+ word-break: break-word;
214
+ color: #ef9a9a;
215
+ }
216
+
217
+ .steps-list {
218
+ display: flex;
219
+ flex-direction: column;
220
+ gap: 4px;
221
+ margin-top: 8px;
222
+ }
223
+
224
+ .step, .step-details {
225
+ padding: 6px 12px;
226
+ border-radius: 4px;
227
+ border-left: 3px solid transparent;
228
+ }
229
+
230
+ .step-success { border-left-color: var(--color-passed); }
231
+ .step-failure { border-left-color: var(--color-failed); }
232
+ .step-skipped { border-left-color: var(--color-skipped); }
233
+ .step-pending { border-left-color: var(--color-pending); }
234
+
235
+ .step-details { cursor: pointer; }
236
+ .step-details > summary { list-style: none; }
237
+ .step-details > summary::-webkit-details-marker { display: none; }
238
+ .step-details > summary::before {
239
+ content: '\\25B6';
240
+ font-size: 9px;
241
+ color: var(--color-text-secondary);
242
+ margin-right: 4px;
243
+ display: inline-block;
244
+ transition: transform 0.15s;
245
+ }
246
+ .step-details[open] > summary::before {
247
+ transform: rotate(90deg);
248
+ }
249
+ .step-details:hover { background: var(--color-surface-hover); }
250
+
251
+ .step-header {
252
+ display: flex;
253
+ align-items: center;
254
+ gap: 8px;
255
+ font-size: 13px;
256
+ }
257
+
258
+ .step-expanded {
259
+ padding: 8px 0 4px 26px;
260
+ }
261
+
262
+ .step-id {
263
+ font-family: monospace;
264
+ font-size: 11px;
265
+ color: var(--color-text-secondary);
266
+ min-width: 60px;
267
+ }
268
+
269
+ .step-description { flex: 1; }
270
+ .step-description-collapsed { flex: 1; }
271
+ .step-details[open] .step-description-collapsed { display: none; }
272
+
273
+ .step-description-full {
274
+ font-size: 13px;
275
+ margin-bottom: 4px;
276
+ }
277
+
278
+ .step-duration {
279
+ font-size: 11px;
280
+ color: var(--color-text-secondary);
281
+ min-width: 40px;
282
+ text-align: right;
283
+ }
284
+
285
+ .status-icon { font-size: 14px; width: 18px; text-align: center; display: inline-block; }
286
+ .status-icon.passed { color: var(--color-passed); }
287
+ .status-icon.failed { color: var(--color-failed); }
288
+ .status-icon.skipped { color: var(--color-skipped); }
289
+ .status-icon.pending { color: var(--color-pending); }
290
+
291
+ .step-screenshot {
292
+ max-width: 100%;
293
+ border-radius: 4px;
294
+ margin-bottom: 8px;
295
+ border: 1px solid var(--color-border);
296
+ }
297
+
298
+ .test-artifacts {
299
+ margin-top: 12px;
300
+ padding-top: 8px;
301
+ border-top: 1px solid var(--color-border);
302
+ display: flex;
303
+ flex-direction: column;
304
+ gap: 4px;
305
+ }
306
+
307
+ .artifact-section {
308
+ border: 1px solid var(--color-border);
309
+ border-radius: 6px;
310
+ overflow: hidden;
311
+ }
312
+ .artifact-summary {
313
+ padding: 8px 12px;
314
+ font-size: 13px;
315
+ font-weight: 500;
316
+ cursor: pointer;
317
+ user-select: none;
318
+ color: var(--color-accent);
319
+ list-style: none;
320
+ }
321
+ .artifact-summary::-webkit-details-marker { display: none; }
322
+ .artifact-summary::before {
323
+ content: '\\25B6';
324
+ font-size: 9px;
325
+ color: var(--color-text-secondary);
326
+ margin-right: 6px;
327
+ display: inline-block;
328
+ transition: transform 0.15s;
329
+ }
330
+ .artifact-section[open] > .artifact-summary::before {
331
+ transform: rotate(90deg);
332
+ }
333
+ .artifact-summary:hover { background: var(--color-surface-hover); }
334
+ .artifact-content { padding: 8px 12px 12px; }
335
+
336
+ .video-container { position: relative; display: inline-block; max-width: 100%; }
337
+ .artifact-video {
338
+ max-width: 100%;
339
+ max-height: 400px;
340
+ border-radius: 4px;
341
+ border: 1px solid var(--color-border);
342
+ display: block;
343
+ }
344
+ .enlarge-btn {
345
+ position: absolute;
346
+ top: 8px;
347
+ right: 8px;
348
+ background: rgba(0,0,0,0.6);
349
+ border: 1px solid rgba(255,255,255,0.2);
350
+ color: white;
351
+ font-size: 16px;
352
+ width: 32px;
353
+ height: 32px;
354
+ border-radius: 4px;
355
+ cursor: pointer;
356
+ display: flex;
357
+ align-items: center;
358
+ justify-content: center;
359
+ opacity: 0;
360
+ transition: opacity 0.15s;
361
+ }
362
+ .video-container:hover .enlarge-btn { opacity: 1; }
363
+ .enlarge-btn:hover { background: rgba(0,0,0,0.8); }
364
+
365
+ .screenshot-grid {
366
+ display: grid;
367
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
368
+ gap: 8px;
369
+ }
370
+ .screenshot-thumb {
371
+ cursor: pointer;
372
+ border-radius: 4px;
373
+ border: 1px solid var(--color-border);
374
+ overflow: hidden;
375
+ transition: border-color 0.15s;
376
+ }
377
+ .screenshot-thumb:hover { border-color: var(--color-accent); }
378
+ .screenshot-thumb img {
379
+ width: 100%;
380
+ display: block;
381
+ aspect-ratio: 16/9;
382
+ object-fit: cover;
383
+ }
384
+ .thumb-label {
385
+ display: block;
386
+ font-size: 10px;
387
+ font-family: monospace;
388
+ color: var(--color-text-secondary);
389
+ padding: 3px 6px;
390
+ text-align: center;
391
+ background: var(--color-surface);
392
+ }
393
+
394
+ .trace-actions {
395
+ display: flex;
396
+ align-items: center;
397
+ gap: 8px;
398
+ }
399
+ .trace-command {
400
+ flex: 1;
401
+ background: var(--color-bg);
402
+ border: 1px solid var(--color-border);
403
+ border-radius: 4px;
404
+ padding: 8px 12px;
405
+ font-size: 12px;
406
+ color: var(--color-text);
407
+ white-space: nowrap;
408
+ overflow-x: auto;
409
+ }
410
+ .copy-btn {
411
+ background: var(--color-surface-hover);
412
+ border: 1px solid var(--color-border);
413
+ color: var(--color-accent);
414
+ font-size: 12px;
415
+ padding: 6px 12px;
416
+ border-radius: 4px;
417
+ cursor: pointer;
418
+ white-space: nowrap;
419
+ }
420
+ .copy-btn:hover { background: var(--color-border); }
421
+ .trace-hint {
422
+ font-size: 12px;
423
+ color: var(--color-text-secondary);
424
+ margin-top: 6px;
425
+ }
426
+
427
+ .attachment-link { color: var(--color-accent); text-decoration: none; }
428
+ .attachment-link:hover { text-decoration: underline; }
429
+
430
+ /* Video overlay */
431
+ .video-overlay {
432
+ display: none;
433
+ position: fixed;
434
+ inset: 0;
435
+ z-index: 1000;
436
+ background: rgba(0,0,0,0.92);
437
+ align-items: center;
438
+ justify-content: center;
439
+ }
440
+ .video-overlay.active { display: flex; }
441
+ .video-overlay video {
442
+ max-width: 90vw;
443
+ max-height: 90vh;
444
+ border-radius: 4px;
445
+ }
446
+ .video-overlay .gallery-close {
447
+ position: absolute;
448
+ top: 12px;
449
+ right: 16px;
450
+ }
451
+
452
+ .no-steps {
453
+ font-size: 13px;
454
+ color: var(--color-text-secondary);
455
+ font-style: italic;
456
+ padding: 8px 0;
457
+ }
458
+
459
+ .footer {
460
+ margin-top: 24px;
461
+ padding-top: 12px;
462
+ border-top: 1px solid var(--color-border);
463
+ font-size: 12px;
464
+ color: var(--color-text-secondary);
465
+ text-align: center;
466
+ }
467
+
468
+ /* Gallery lightbox */
469
+ .gallery-overlay {
470
+ display: none;
471
+ position: fixed;
472
+ inset: 0;
473
+ z-index: 1000;
474
+ background: rgba(0,0,0,0.92);
475
+ flex-direction: column;
476
+ align-items: center;
477
+ justify-content: center;
478
+ }
479
+ .gallery-overlay.active { display: flex; }
480
+
481
+ .gallery-top {
482
+ position: absolute;
483
+ top: 0;
484
+ left: 0;
485
+ right: 0;
486
+ padding: 16px 24px;
487
+ background: linear-gradient(rgba(0,0,0,0.6), transparent);
488
+ text-align: center;
489
+ }
490
+ .gallery-step-id {
491
+ font-family: monospace;
492
+ font-size: 13px;
493
+ color: var(--color-text-secondary);
494
+ margin-right: 8px;
495
+ }
496
+ .gallery-description {
497
+ font-size: 15px;
498
+ color: var(--color-text);
499
+ }
500
+ .gallery-counter {
501
+ font-size: 12px;
502
+ color: var(--color-text-secondary);
503
+ margin-top: 4px;
504
+ }
505
+
506
+ .gallery-img-container {
507
+ flex: 1;
508
+ display: flex;
509
+ align-items: center;
510
+ justify-content: center;
511
+ padding: 60px 80px;
512
+ min-height: 0;
513
+ width: 100%;
514
+ }
515
+ .gallery-img-container img {
516
+ max-width: 100%;
517
+ max-height: 100%;
518
+ object-fit: contain;
519
+ border-radius: 4px;
520
+ }
521
+
522
+ .gallery-bottom {
523
+ position: absolute;
524
+ bottom: 0;
525
+ left: 0;
526
+ right: 0;
527
+ padding: 16px 24px;
528
+ background: linear-gradient(transparent, rgba(0,0,0,0.6));
529
+ text-align: center;
530
+ }
531
+ .gallery-message {
532
+ font-size: 13px;
533
+ color: var(--color-text-secondary);
534
+ max-height: 80px;
535
+ overflow-y: auto;
536
+ line-height: 1.4;
537
+ }
538
+
539
+ .gallery-nav {
540
+ position: absolute;
541
+ top: 50%;
542
+ transform: translateY(-50%);
543
+ background: rgba(255,255,255,0.1);
544
+ border: 1px solid rgba(255,255,255,0.2);
545
+ color: white;
546
+ font-size: 24px;
547
+ width: 44px;
548
+ height: 44px;
549
+ border-radius: 50%;
550
+ cursor: pointer;
551
+ display: flex;
552
+ align-items: center;
553
+ justify-content: center;
554
+ user-select: none;
555
+ }
556
+ .gallery-nav:hover { background: rgba(255,255,255,0.2); }
557
+ .gallery-nav.prev { left: 16px; }
558
+ .gallery-nav.next { right: 16px; }
559
+
560
+ .gallery-close {
561
+ position: absolute;
562
+ top: 12px;
563
+ right: 16px;
564
+ background: none;
565
+ border: none;
566
+ color: white;
567
+ font-size: 28px;
568
+ cursor: pointer;
569
+ opacity: 0.7;
570
+ z-index: 1;
571
+ }
572
+ .gallery-close:hover { opacity: 1; }
573
+
574
+ .gallery-status {
575
+ display: inline-block;
576
+ width: 10px;
577
+ height: 10px;
578
+ border-radius: 50%;
579
+ margin-right: 6px;
580
+ vertical-align: middle;
581
+ }
582
+ .gallery-status.success { background: var(--color-passed); }
583
+ .gallery-status.failure { background: var(--color-failed); }
584
+ .gallery-status.skipped { background: var(--color-skipped); }
585
+ .gallery-status.pending { background: var(--color-pending); }
586
+ </style>
587
+ </head>
588
+ <body>
589
+ <div class="container">
590
+ <div class="header">
591
+ <h1>Shiplight Test Report</h1>
592
+ <div class="summary">
593
+ <span class="summary-stat">${l} test${l!==1?"s":""}</span>
594
+ ${r>0?`<span class="summary-stat passed">${r} passed</span>`:""}
595
+ ${s>0?`<span class="summary-stat failed">${s} failed</span>`:""}
596
+ ${g>0?`<span class="summary-stat skipped">${g} skipped</span>`:""}
597
+ <span class="summary-stat">${k(e.totalDuration)}</span>
598
+ </div>
599
+ </div>
600
+ <div class="test-list">
601
+ ${i}
602
+ </div>
603
+ <div class="footer">
604
+ Generated by Shiplight Reporter
605
+ </div>
606
+ </div>
607
+
608
+ <!-- Video overlay -->
609
+ <div class="video-overlay" id="videoOverlay">
610
+ <button class="gallery-close" onclick="closeVideoOverlay()">&times;</button>
611
+ <video controls autoplay id="overlayVideo"><source src="" type="video/webm" /></video>
612
+ </div>
613
+
614
+ <!-- Gallery lightbox -->
615
+ <div class="gallery-overlay" id="gallery">
616
+ <button class="gallery-close" onclick="closeGallery()">&times;</button>
617
+ <div class="gallery-top">
618
+ <div>
619
+ <span class="gallery-status" id="gallery-status"></span>
620
+ <span class="gallery-step-id" id="gallery-step-id"></span>
621
+ <span class="gallery-description" id="gallery-description"></span>
622
+ </div>
623
+ <div class="gallery-counter" id="gallery-counter"></div>
624
+ </div>
625
+ <button class="gallery-nav prev" onclick="galleryNav(-1)">&#x2039;</button>
626
+ <div class="gallery-img-container">
627
+ <img id="gallery-img" src="" alt="" />
628
+ </div>
629
+ <button class="gallery-nav next" onclick="galleryNav(1)">&#x203A;</button>
630
+ <div class="gallery-bottom">
631
+ <div class="gallery-message" id="gallery-message"></div>
632
+ </div>
633
+ </div>
634
+
635
+ <script>
636
+ /* Trace */
637
+ function copyTraceCmd(idx) {
638
+ const el = document.getElementById('trace-cmd-' + idx);
639
+ navigator.clipboard.writeText(el.textContent).then(function() {
640
+ const btn = el.nextElementSibling;
641
+ btn.textContent = 'Copied!';
642
+ setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
643
+ });
644
+ }
645
+
646
+ /* Gallery */
647
+ let galleryData = [];
648
+ let galleryIndex = 0;
649
+
650
+ function openGalleryAt(el, idx) {
651
+ galleryData = JSON.parse(el.dataset.gallery);
652
+ galleryIndex = idx || 0;
653
+ document.getElementById('gallery').classList.add('active');
654
+ renderGallerySlide();
655
+ }
656
+
657
+ function closeGallery() {
658
+ document.getElementById('gallery').classList.remove('active');
659
+ }
660
+
661
+ function galleryNav(dir) {
662
+ galleryIndex = (galleryIndex + dir + galleryData.length) % galleryData.length;
663
+ renderGallerySlide();
664
+ }
665
+
666
+ function renderGallerySlide() {
667
+ const s = galleryData[galleryIndex];
668
+ document.getElementById('gallery-img').src = s.src;
669
+ document.getElementById('gallery-step-id').textContent = s.stepId;
670
+ document.getElementById('gallery-description').textContent = s.description;
671
+ document.getElementById('gallery-counter').textContent =
672
+ (galleryIndex + 1) + ' / ' + galleryData.length;
673
+ document.getElementById('gallery-status').className = 'gallery-status ' + s.status;
674
+ const msgEl = document.getElementById('gallery-message');
675
+ msgEl.textContent = s.message || '';
676
+ msgEl.style.display = s.message ? 'block' : 'none';
677
+ }
678
+
679
+ document.getElementById('gallery').addEventListener('click', function(e) {
680
+ if (e.target === this) closeGallery();
681
+ });
682
+
683
+ /* Video overlay */
684
+ function openVideoOverlay(btn) {
685
+ const video = btn.closest('.video-container').querySelector('video');
686
+ const src = video.querySelector('source').src;
687
+ const overlay = document.getElementById('videoOverlay');
688
+ const overlayVideo = document.getElementById('overlayVideo');
689
+ overlayVideo.querySelector('source').src = src;
690
+ overlayVideo.load();
691
+ overlayVideo.currentTime = video.currentTime;
692
+ overlay.classList.add('active');
693
+ }
694
+
695
+ function closeVideoOverlay() {
696
+ const overlay = document.getElementById('videoOverlay');
697
+ overlay.classList.remove('active');
698
+ document.getElementById('overlayVideo').pause();
699
+ }
700
+
701
+ document.getElementById('videoOverlay').addEventListener('click', function(e) {
702
+ if (e.target === this) closeVideoOverlay();
703
+ });
704
+
705
+ /* Keyboard */
706
+ document.addEventListener('keydown', function(e) {
707
+ const galleryActive = document.getElementById('gallery').classList.contains('active');
708
+ const videoActive = document.getElementById('videoOverlay').classList.contains('active');
709
+ if (e.key === 'Escape') {
710
+ if (galleryActive) closeGallery();
711
+ if (videoActive) closeVideoOverlay();
712
+ }
713
+ if (galleryActive) {
714
+ if (e.key === 'ArrowLeft') galleryNav(-1);
715
+ else if (e.key === 'ArrowRight') galleryNav(1);
716
+ }
717
+ });
718
+ </script>
719
+ </body>
720
+ </html>`}var C={before:0,main:1,teardown:2,after:3};function z(e){let r=e.split(".")[0];return C[r]??1}function E(e){return e.split(".").map(r=>{let s=Number(r);return Number.isNaN(s)?0:s})}function A(e){return[...e].sort(([r],[s])=>{let g=z(r),l=z(s);if(g!==l)return g-l;let i=E(r),o=E(s);for(let d=0;d<Math.max(i.length,o.length);d++){let a=i[d]??-1,t=o[d]??-1;if(a!==t)return a-t}return 0})}var w=class{outputFolder;openMode;collected=[];config;constructor(r={}){this.outputFolder=r.outputFolder||"shiplight-report",this.openMode=r.open||"on-failure"}onBegin(r,s){this.config=r}onTestEnd(r,s){this.collected.push({test:r,result:s})}async onEnd(r){if(this.collected.length===0)return;let s=[];for(let{test:t,result:n}of this.collected){let y=t.location.file,m=await this.buildReportTest(t,n,y);s.push(m)}let g={tests:s,totalDuration:r.duration},l=c.isAbsolute(this.outputFolder)?this.outputFolder:c.join(process.cwd(),this.outputFolder);u.mkdirSync(l,{recursive:!0});let i=c.join(l,"screenshots"),o=!1;for(let t of g.tests)for(let n of t.steps)if(n.screenshot&&c.isAbsolute(n.screenshot))try{o||(u.mkdirSync(i,{recursive:!0}),o=!0);let y=`${n.stepId.replace(/\./g,"-")}.png`,m=c.join(i,y);u.copyFileSync(n.screenshot,m),n.screenshot=`screenshots/${y}`}catch{}let d=c.join(l,"report-data.json");u.writeFileSync(d,JSON.stringify(g,null,2),"utf-8");let a=c.join(l,"index.html");u.writeFileSync(a,R(g),"utf-8");for(let{result:t}of this.collected)for(let n of t.attachments)if(n.path&&(n.name==="video"||n.name==="trace")){let y=c.basename(n.path),m=c.join(l,y);try{u.copyFileSync(n.path,m)}catch{}}if(console.log(`
721
+ Shiplight report written to: ${a}`),this.openMode==="always"||this.openMode==="on-failure"&&r.status!=="passed")try{let t=(await import("open")).default;await t(a)}catch{}}printsToStdio(){return!1}async buildReportTest(r,s,g){let l={title:r.title,file:c.relative(process.cwd(),g),status:s.status,duration:s.duration,steps:[]};s.errors.length>0&&(l.error=s.errors.map(t=>t.message||t.stack||String(t)).join(`
722
+
723
+ `));for(let t of s.attachments)t.name==="video"&&t.path&&(l.videoPath=c.basename(t.path)),t.name==="trace"&&t.path&&(l.tracePath=c.basename(t.path));let i=s.attachments.find(t=>t.name==="shiplight-results"),o=null;if(i)try{if(i.body)o=JSON.parse(i.body.toString("utf-8"));else if(i.path){let t=u.readFileSync(i.path,"utf-8");o=JSON.parse(t)}}catch{}let d=g.replace(/\.yaml\.spec\.ts$/,".test.yaml"),a={};if(u.existsSync(d))try{let t=u.readFileSync(d,"utf-8"),n=T(t,d);if(n.suite){let y=n.suite.tests.find(m=>m.name===r.title);y&&(a=b(y.testFlow))}else n.testFlow&&(a=b(n.testFlow))}catch{}if(o||Object.keys(a).length>0){let t=new Set([...Object.keys(a),...Object.keys(o||{})]),n=Array.from(t).map(m=>[m,null]),y=A(n);for(let[m]of y){let $=a[m],f=o?.[m],h=$?.description;if(!h||h==="Action"||h==="Draft"){let v=$?.action_entity;h=v?.action_description||v?.action_data?.kwargs?.description||f?.description||m}let x={stepId:m,description:h,status:f?.status||"pending",duration:f?.duration};if(f?.message){let v=typeof f.message=="string"?f.message:JSON.stringify(f.message,null,2);f.status==="failure"?x.error=v:x.message=v}if(f?.screenshot){let v=f.screenshot,O=i?.path?c.dirname(i.path):"",S=c.isAbsolute(v)?v:c.join(O,v);u.existsSync(S)&&(x.screenshot=S)}l.steps.push(x)}}return l}},j=w;export{j as default};