@testivai/witness 1.0.1 → 1.0.2

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 (88) hide show
  1. package/README.md +44 -13
  2. package/dist/baselines/index.d.ts +6 -0
  3. package/dist/baselines/index.d.ts.map +1 -0
  4. package/dist/baselines/index.js +9 -0
  5. package/dist/baselines/index.js.map +1 -0
  6. package/dist/baselines/store.d.ts +79 -0
  7. package/dist/baselines/store.d.ts.map +1 -0
  8. package/dist/baselines/store.js +234 -0
  9. package/dist/baselines/store.js.map +1 -0
  10. package/dist/bin/testivai.js +2 -0
  11. package/dist/bin/testivai.js.map +1 -1
  12. package/dist/browser/binding.d.ts.map +1 -1
  13. package/dist/browser/binding.js +173 -42
  14. package/dist/browser/binding.js.map +1 -1
  15. package/dist/browser/capture.d.ts.map +1 -1
  16. package/dist/browser/capture.js +7 -4
  17. package/dist/browser/capture.js.map +1 -1
  18. package/dist/commands/approve.d.ts +3 -0
  19. package/dist/commands/approve.d.ts.map +1 -0
  20. package/dist/commands/approve.js +94 -0
  21. package/dist/commands/approve.js.map +1 -0
  22. package/dist/commands/init.d.ts.map +1 -1
  23. package/dist/commands/init.js +53 -0
  24. package/dist/commands/init.js.map +1 -1
  25. package/dist/commands/run.d.ts.map +1 -1
  26. package/dist/commands/run.js +98 -0
  27. package/dist/commands/run.js.map +1 -1
  28. package/dist/config/index.d.ts +6 -0
  29. package/dist/config/index.d.ts.map +1 -0
  30. package/dist/config/index.js +14 -0
  31. package/dist/config/index.js.map +1 -0
  32. package/dist/config/local-config.d.ts +54 -0
  33. package/dist/config/local-config.d.ts.map +1 -0
  34. package/dist/config/local-config.js +130 -0
  35. package/dist/config/local-config.js.map +1 -0
  36. package/dist/diff/diff.d.ts +27 -0
  37. package/dist/diff/diff.d.ts.map +1 -0
  38. package/dist/diff/diff.js +216 -0
  39. package/dist/diff/diff.js.map +1 -0
  40. package/dist/diff/ignore.d.ts +22 -0
  41. package/dist/diff/ignore.d.ts.map +1 -0
  42. package/dist/diff/ignore.js +43 -0
  43. package/dist/diff/ignore.js.map +1 -0
  44. package/dist/diff/index.d.ts +16 -0
  45. package/dist/diff/index.d.ts.map +1 -0
  46. package/dist/diff/index.js +23 -0
  47. package/dist/diff/index.js.map +1 -0
  48. package/dist/diff/minimap.d.ts +23 -0
  49. package/dist/diff/minimap.d.ts.map +1 -0
  50. package/dist/diff/minimap.js +43 -0
  51. package/dist/diff/minimap.js.map +1 -0
  52. package/dist/diff/regions.d.ts +23 -0
  53. package/dist/diff/regions.d.ts.map +1 -0
  54. package/dist/diff/regions.js +129 -0
  55. package/dist/diff/regions.js.map +1 -0
  56. package/dist/diff/resize.d.ts +27 -0
  57. package/dist/diff/resize.d.ts.map +1 -0
  58. package/dist/diff/resize.js +82 -0
  59. package/dist/diff/resize.js.map +1 -0
  60. package/dist/diff/types.d.ts +79 -0
  61. package/dist/diff/types.d.ts.map +1 -0
  62. package/dist/diff/types.js +10 -0
  63. package/dist/diff/types.js.map +1 -0
  64. package/dist/index.d.ts +8 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +23 -2
  67. package/dist/index.js.map +1 -1
  68. package/dist/report/compare.d.ts +18 -0
  69. package/dist/report/compare.d.ts.map +1 -0
  70. package/dist/report/compare.js +163 -0
  71. package/dist/report/compare.js.map +1 -0
  72. package/dist/report/generator.d.ts +31 -0
  73. package/dist/report/generator.d.ts.map +1 -0
  74. package/dist/report/generator.js +123 -0
  75. package/dist/report/generator.js.map +1 -0
  76. package/dist/report/index.d.ts +10 -0
  77. package/dist/report/index.d.ts.map +1 -0
  78. package/dist/report/index.js +13 -0
  79. package/dist/report/index.js.map +1 -0
  80. package/dist/report/results.d.ts +35 -0
  81. package/dist/report/results.d.ts.map +1 -0
  82. package/dist/report/results.js +8 -0
  83. package/dist/report/results.js.map +1 -0
  84. package/dist/report/template.d.ts +14 -0
  85. package/dist/report/template.d.ts.map +1 -0
  86. package/dist/report/template.js +229 -0
  87. package/dist/report/template.js.map +1 -0
  88. package/package.json +58 -15
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ /**
3
+ * TestivAI Diff Engine — Size mismatch handling
4
+ *
5
+ * When baseline and candidate have different dimensions,
6
+ * normalize them before diffing.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.normalizeDimensions = normalizeDimensions;
10
+ /**
11
+ * Normalize two pixel buffers to the same dimensions.
12
+ *
13
+ * @param baseline - Baseline pixel buffer (RGBA)
14
+ * @param bw - Baseline width
15
+ * @param bh - Baseline height
16
+ * @param candidate - Candidate pixel buffer (RGBA)
17
+ * @param cw - Candidate width
18
+ * @param ch - Candidate height
19
+ * @param mode - 'pad' (fill with transparent), 'crop' (use min dims), or 'error'
20
+ */
21
+ function normalizeDimensions(baseline, bw, bh, candidate, cw, ch, mode) {
22
+ if (bw === cw && bh === ch) {
23
+ return {
24
+ normalizedBaseline: new Uint8ClampedArray(baseline),
25
+ normalizedCandidate: new Uint8ClampedArray(candidate),
26
+ width: bw,
27
+ height: bh,
28
+ sizeMismatch: {
29
+ baseline: { width: bw, height: bh },
30
+ candidate: { width: cw, height: ch },
31
+ normalized: { width: bw, height: bh },
32
+ },
33
+ };
34
+ }
35
+ if (mode === 'error') {
36
+ throw new Error(`Size mismatch: baseline is ${bw}×${bh}, candidate is ${cw}×${ch}. ` +
37
+ `Set handleSizeMismatch to 'pad' or 'crop' to handle this automatically.`);
38
+ }
39
+ let targetWidth;
40
+ let targetHeight;
41
+ if (mode === 'pad') {
42
+ targetWidth = Math.max(bw, cw);
43
+ targetHeight = Math.max(bh, ch);
44
+ }
45
+ else {
46
+ // crop
47
+ targetWidth = Math.min(bw, cw);
48
+ targetHeight = Math.min(bh, ch);
49
+ }
50
+ const normalizedBaseline = new Uint8ClampedArray(targetWidth * targetHeight * 4);
51
+ const normalizedCandidate = new Uint8ClampedArray(targetWidth * targetHeight * 4);
52
+ // Copy baseline rows
53
+ const copyWidthB = Math.min(bw, targetWidth);
54
+ const copyHeightB = Math.min(bh, targetHeight);
55
+ for (let y = 0; y < copyHeightB; y++) {
56
+ const srcStart = y * bw * 4;
57
+ const dstStart = y * targetWidth * 4;
58
+ const bytesToCopy = copyWidthB * 4;
59
+ normalizedBaseline.set(baseline.slice(srcStart, srcStart + bytesToCopy), dstStart);
60
+ }
61
+ // Copy candidate rows
62
+ const copyWidthC = Math.min(cw, targetWidth);
63
+ const copyHeightC = Math.min(ch, targetHeight);
64
+ for (let y = 0; y < copyHeightC; y++) {
65
+ const srcStart = y * cw * 4;
66
+ const dstStart = y * targetWidth * 4;
67
+ const bytesToCopy = copyWidthC * 4;
68
+ normalizedCandidate.set(candidate.slice(srcStart, srcStart + bytesToCopy), dstStart);
69
+ }
70
+ return {
71
+ normalizedBaseline,
72
+ normalizedCandidate,
73
+ width: targetWidth,
74
+ height: targetHeight,
75
+ sizeMismatch: {
76
+ baseline: { width: bw, height: bh },
77
+ candidate: { width: cw, height: ch },
78
+ normalized: { width: targetWidth, height: targetHeight },
79
+ },
80
+ };
81
+ }
82
+ //# sourceMappingURL=resize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resize.js","sourceRoot":"","sources":["../../src/diff/resize.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAuBH,kDAkFC;AA7FD;;;;;;;;;;GAUG;AACH,SAAgB,mBAAmB,CACjC,QAAwC,EACxC,EAAU,EACV,EAAU,EACV,SAAyC,EACzC,EAAU,EACV,EAAU,EACV,IAA8B;IAE9B,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3B,OAAO;YACL,kBAAkB,EAAE,IAAI,iBAAiB,CAAC,QAAQ,CAAC;YACnD,mBAAmB,EAAE,IAAI,iBAAiB,CAAC,SAAS,CAAC;YACrD,KAAK,EAAE,EAAE;YACT,MAAM,EAAE,EAAE;YACV,YAAY,EAAE;gBACZ,QAAQ,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;gBACnC,SAAS,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;gBACpC,UAAU,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;aACtC;SACF,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,8BAA8B,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,IAAI;YACpE,yEAAyE,CAC1E,CAAC;IACJ,CAAC;IAED,IAAI,WAAmB,CAAC;IACxB,IAAI,YAAoB,CAAC;IAEzB,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/B,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAClC,CAAC;SAAM,CAAC;QACN,OAAO;QACP,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/B,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,kBAAkB,GAAG,IAAI,iBAAiB,CAAC,WAAW,GAAG,YAAY,GAAG,CAAC,CAAC,CAAC;IACjF,MAAM,mBAAmB,GAAG,IAAI,iBAAiB,CAAC,WAAW,GAAG,YAAY,GAAG,CAAC,CAAC,CAAC;IAElF,qBAAqB;IACrB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;IAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,CAAC,GAAG,WAAW,GAAG,CAAC,CAAC;QACrC,MAAM,WAAW,GAAG,UAAU,GAAG,CAAC,CAAC;QACnC,kBAAkB,CAAC,GAAG,CACpB,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,GAAG,WAAW,CAAC,EAChD,QAAQ,CACT,CAAC;IACJ,CAAC;IAED,sBAAsB;IACtB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;IAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,CAAC,GAAG,WAAW,GAAG,CAAC,CAAC;QACrC,MAAM,WAAW,GAAG,UAAU,GAAG,CAAC,CAAC;QACnC,mBAAmB,CAAC,GAAG,CACrB,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,GAAG,WAAW,CAAC,EACjD,QAAQ,CACT,CAAC;IACJ,CAAC;IAED,OAAO;QACL,kBAAkB;QAClB,mBAAmB;QACnB,KAAK,EAAE,WAAW;QAClB,MAAM,EAAE,YAAY;QACpB,YAAY,EAAE;YACZ,QAAQ,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;YACnC,SAAS,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;YACpC,UAAU,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE;SACzD;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * TestivAI Diff Engine Types
3
+ *
4
+ * Originally forked from pixel-buffer-diff (Pbd) by @p01.
5
+ * Extended with ignore regions, size mismatch handling,
6
+ * region detection, and TypeScript support.
7
+ */
8
+ export interface DiffOptions {
9
+ /** Individual pixel matching threshold (0-1). Default: 0.1 */
10
+ threshold?: number;
11
+ /** Cumulated pixel matching threshold for anti-aliasing filtering. Default: 0.5 */
12
+ cumulatedThreshold?: number;
13
+ /** Enable low-resolution minimap overlay on diff output. Default: false */
14
+ enableMinimap?: boolean;
15
+ /** Coordinate-based regions to ignore during comparison */
16
+ ignoreRegions?: IgnoreRegion[];
17
+ /** Detect and return bounding boxes of changed regions. Default: false */
18
+ detectRegions?: boolean;
19
+ /** How to handle size mismatches between baseline and candidate */
20
+ handleSizeMismatch?: 'pad' | 'crop' | 'error';
21
+ }
22
+ export interface IgnoreRegion {
23
+ x: number;
24
+ y: number;
25
+ width: number;
26
+ height: number;
27
+ }
28
+ export interface DiffResult {
29
+ /** Number of pixels that exceeded the threshold */
30
+ diffCount: number;
31
+ /** Cumulated difference of every pixel change (for anti-aliasing detection) */
32
+ cumulatedDiff: number;
33
+ /** Numeric hash of pixel changes for deduplication */
34
+ hash: number;
35
+ /** Percentage of pixels that differ (0-100) */
36
+ diffPercent: number;
37
+ /** Total number of pixels compared */
38
+ totalPixels: number;
39
+ /** True if images are identical (within thresholds) */
40
+ isIdentical: boolean;
41
+ /** Bounding boxes of change clusters (if detectRegions was enabled) */
42
+ regions?: DiffRegion[];
43
+ /** Size mismatch info (if dimensions differed and were normalized) */
44
+ sizeMismatch?: SizeMismatch;
45
+ }
46
+ export interface DiffRegion {
47
+ x: number;
48
+ y: number;
49
+ width: number;
50
+ height: number;
51
+ /** Number of changed pixels within this region's bounding box */
52
+ diffPixels: number;
53
+ /** Percentage of changed pixels within this region's bounding box */
54
+ diffPercent: number;
55
+ }
56
+ export interface SizeMismatch {
57
+ baseline: {
58
+ width: number;
59
+ height: number;
60
+ };
61
+ candidate: {
62
+ width: number;
63
+ height: number;
64
+ };
65
+ normalized: {
66
+ width: number;
67
+ height: number;
68
+ };
69
+ }
70
+ /**
71
+ * Raw result from the core Pbd diff loop (before extensions).
72
+ * @internal
73
+ */
74
+ export interface PbdRawResult {
75
+ diff: number;
76
+ cumulatedDiff: number;
77
+ hash: number;
78
+ }
79
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/diff/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,WAAW;IAC1B,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2EAA2E;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,0EAA0E;IAC1E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mEAAmE;IACnE,kBAAkB,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;CAC/C;AAED,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,aAAa,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,WAAW,EAAE,OAAO,CAAC;IACrB,uEAAuE;IACvE,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,sEAAsE;IACtE,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /**
3
+ * TestivAI Diff Engine Types
4
+ *
5
+ * Originally forked from pixel-buffer-diff (Pbd) by @p01.
6
+ * Extended with ignore regions, size mismatch handling,
7
+ * region detection, and TypeScript support.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/diff/types.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG"}
package/dist/index.d.ts CHANGED
@@ -11,10 +11,18 @@ export { logger, createLogger } from './utils/logger';
11
11
  export { toSafeFilename, generateUniqueFilename, extractNameFromUrl, sanitizeTestName, isSafeFilename } from './utils/file-naming';
12
12
  export { ProcessManager, spawnProcess, setupSignalHandlers, commandExists } from './utils/process';
13
13
  export { FrameworkDetector } from './utils/framework-detect';
14
+ export * from './diff';
15
+ export { BaselineStore } from './baselines';
16
+ export type { BaselineMetadata } from './baselines';
17
+ export { loadLocalConfig, createDefaultConfig, localConfigExists, isLocalMode, getConfigPath, getDefaultConfig, } from './config';
18
+ export type { LocalConfig } from './config';
19
+ export { generateReport, compareAll, renderHtml } from './report';
20
+ export type { GenerateReportOptions, CompareOptions, ReportData, ReportSummary, SnapshotResult, SnapshotStatus } from './report';
14
21
  export { authCommand } from './commands/auth';
15
22
  export { initCommand } from './commands/init';
16
23
  export { runCommand } from './commands/run';
17
24
  export { witnessCommand } from './commands/capture';
25
+ export { approveCommand } from './commands/approve';
18
26
  export { CoreApiClient, DEFAULT_CORE_API_URL, saveCredentials, loadCredentials, deleteCredentials, getApiKey, isAuthenticated, findConfigFile, loadConfig, configExists, getOutputDir, CompressionHelper, compressionHelper, } from '@testivai/common';
19
27
  export declare const VERSION: any;
20
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAG9E,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACnI,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnG,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,cAAc,EACd,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,eAAO,MAAM,OAAO,KAAwC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAG9E,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACnI,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnG,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,cAAc,QAAQ,CAAC;AAGvB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,gBAAgB,GACjB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAClE,YAAY,EAAE,qBAAqB,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAGjI,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,cAAc,EACd,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAI1B,eAAO,MAAM,OAAO,KAAqC,CAAC"}
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
18
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.VERSION = exports.compressionHelper = exports.CompressionHelper = exports.getOutputDir = exports.configExists = exports.loadConfig = exports.findConfigFile = exports.isAuthenticated = exports.getApiKey = exports.deleteCredentials = exports.loadCredentials = exports.saveCredentials = exports.DEFAULT_CORE_API_URL = exports.CoreApiClient = exports.witnessCommand = exports.runCommand = exports.initCommand = exports.authCommand = exports.FrameworkDetector = exports.commandExists = exports.setupSignalHandlers = exports.spawnProcess = exports.ProcessManager = exports.isSafeFilename = exports.sanitizeTestName = exports.extractNameFromUrl = exports.generateUniqueFilename = exports.toSafeFilename = exports.createLogger = exports.logger = exports.BrowserDiscoveryError = exports.BrowserDiscovery = exports.BrowserBinding = exports.BrowserCapture = exports.BrowserClient = void 0;
21
+ exports.VERSION = exports.compressionHelper = exports.CompressionHelper = exports.getOutputDir = exports.configExists = exports.loadConfig = exports.findConfigFile = exports.isAuthenticated = exports.getApiKey = exports.deleteCredentials = exports.loadCredentials = exports.saveCredentials = exports.DEFAULT_CORE_API_URL = exports.CoreApiClient = exports.approveCommand = exports.witnessCommand = exports.runCommand = exports.initCommand = exports.authCommand = exports.renderHtml = exports.compareAll = exports.generateReport = exports.getDefaultConfig = exports.getConfigPath = exports.isLocalMode = exports.localConfigExists = exports.createDefaultConfig = exports.loadLocalConfig = exports.BaselineStore = exports.FrameworkDetector = exports.commandExists = exports.setupSignalHandlers = exports.spawnProcess = exports.ProcessManager = exports.isSafeFilename = exports.sanitizeTestName = exports.extractNameFromUrl = exports.generateUniqueFilename = exports.toSafeFilename = exports.createLogger = exports.logger = exports.BrowserDiscoveryError = exports.BrowserDiscovery = exports.BrowserBinding = exports.BrowserCapture = exports.BrowserClient = void 0;
22
22
  // Export types
23
23
  __exportStar(require("./types"), exports);
24
24
  // Export browser modules
@@ -48,6 +48,24 @@ Object.defineProperty(exports, "setupSignalHandlers", { enumerable: true, get: f
48
48
  Object.defineProperty(exports, "commandExists", { enumerable: true, get: function () { return process_1.commandExists; } });
49
49
  var framework_detect_1 = require("./utils/framework-detect");
50
50
  Object.defineProperty(exports, "FrameworkDetector", { enumerable: true, get: function () { return framework_detect_1.FrameworkDetector; } });
51
+ // Export diff engine
52
+ __exportStar(require("./diff"), exports);
53
+ // Export baselines
54
+ var baselines_1 = require("./baselines");
55
+ Object.defineProperty(exports, "BaselineStore", { enumerable: true, get: function () { return baselines_1.BaselineStore; } });
56
+ // Export local config
57
+ var config_1 = require("./config");
58
+ Object.defineProperty(exports, "loadLocalConfig", { enumerable: true, get: function () { return config_1.loadLocalConfig; } });
59
+ Object.defineProperty(exports, "createDefaultConfig", { enumerable: true, get: function () { return config_1.createDefaultConfig; } });
60
+ Object.defineProperty(exports, "localConfigExists", { enumerable: true, get: function () { return config_1.localConfigExists; } });
61
+ Object.defineProperty(exports, "isLocalMode", { enumerable: true, get: function () { return config_1.isLocalMode; } });
62
+ Object.defineProperty(exports, "getConfigPath", { enumerable: true, get: function () { return config_1.getConfigPath; } });
63
+ Object.defineProperty(exports, "getDefaultConfig", { enumerable: true, get: function () { return config_1.getDefaultConfig; } });
64
+ // Export report generator
65
+ var report_1 = require("./report");
66
+ Object.defineProperty(exports, "generateReport", { enumerable: true, get: function () { return report_1.generateReport; } });
67
+ Object.defineProperty(exports, "compareAll", { enumerable: true, get: function () { return report_1.compareAll; } });
68
+ Object.defineProperty(exports, "renderHtml", { enumerable: true, get: function () { return report_1.renderHtml; } });
51
69
  // Export commands (for programmatic use)
52
70
  var auth_1 = require("./commands/auth");
53
71
  Object.defineProperty(exports, "authCommand", { enumerable: true, get: function () { return auth_1.authCommand; } });
@@ -57,6 +75,8 @@ var run_1 = require("./commands/run");
57
75
  Object.defineProperty(exports, "runCommand", { enumerable: true, get: function () { return run_1.runCommand; } });
58
76
  var capture_2 = require("./commands/capture");
59
77
  Object.defineProperty(exports, "witnessCommand", { enumerable: true, get: function () { return capture_2.witnessCommand; } });
78
+ var approve_1 = require("./commands/approve");
79
+ Object.defineProperty(exports, "approveCommand", { enumerable: true, get: function () { return approve_1.approveCommand; } });
60
80
  // Re-export common utilities
61
81
  var common_1 = require("@testivai/common");
62
82
  Object.defineProperty(exports, "CoreApiClient", { enumerable: true, get: function () { return common_1.CoreApiClient; } });
@@ -73,5 +93,6 @@ Object.defineProperty(exports, "getOutputDir", { enumerable: true, get: function
73
93
  Object.defineProperty(exports, "CompressionHelper", { enumerable: true, get: function () { return common_1.CompressionHelper; } });
74
94
  Object.defineProperty(exports, "compressionHelper", { enumerable: true, get: function () { return common_1.compressionHelper; } });
75
95
  // Version
76
- exports.VERSION = require('../../package.json').version;
96
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
97
+ exports.VERSION = require('../package.json').version;
77
98
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;AAEH,eAAe;AACf,0CAAwB;AAExB,yBAAyB;AACzB,2CAAiD;AAAxC,uGAAA,aAAa,OAAA;AACtB,6CAAmD;AAA1C,yGAAA,cAAc,OAAA;AACvB,6CAAmD;AAA1C,yGAAA,cAAc,OAAA;AACvB,iDAA8E;AAArE,6GAAA,gBAAgB,OAAA;AAAE,kHAAA,qBAAqB,OAAA;AAEhD,mBAAmB;AACnB,yCAAsD;AAA7C,gGAAA,MAAM,OAAA;AAAE,sGAAA,YAAY,OAAA;AAC7B,mDAAmI;AAA1H,6GAAA,cAAc,OAAA;AAAE,qHAAA,sBAAsB,OAAA;AAAE,iHAAA,kBAAkB,OAAA;AAAE,+GAAA,gBAAgB,OAAA;AAAE,6GAAA,cAAc,OAAA;AACrG,2CAAmG;AAA1F,yGAAA,cAAc,OAAA;AAAE,uGAAA,YAAY,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAAE,wGAAA,aAAa,OAAA;AACzE,6DAA6D;AAApD,qHAAA,iBAAiB,OAAA;AAE1B,yCAAyC;AACzC,wCAA8C;AAArC,mGAAA,WAAW,OAAA;AACpB,wCAA8C;AAArC,mGAAA,WAAW,OAAA;AACpB,sCAA4C;AAAnC,iGAAA,UAAU,OAAA;AACnB,8CAAoD;AAA3C,yGAAA,cAAc,OAAA;AAEvB,6BAA6B;AAC7B,2CAc0B;AAbxB,uGAAA,aAAa,OAAA;AACb,8GAAA,oBAAoB,OAAA;AACpB,yGAAA,eAAe,OAAA;AACf,yGAAA,eAAe,OAAA;AACf,2GAAA,iBAAiB,OAAA;AACjB,mGAAA,SAAS,OAAA;AACT,yGAAA,eAAe,OAAA;AACf,wGAAA,cAAc,OAAA;AACd,oGAAA,UAAU,OAAA;AACV,sGAAA,YAAY,OAAA;AACZ,sGAAA,YAAY,OAAA;AACZ,2GAAA,iBAAiB,OAAA;AACjB,2GAAA,iBAAiB,OAAA;AAGnB,UAAU;AACG,QAAA,OAAO,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC,OAAO,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;AAEH,eAAe;AACf,0CAAwB;AAExB,yBAAyB;AACzB,2CAAiD;AAAxC,uGAAA,aAAa,OAAA;AACtB,6CAAmD;AAA1C,yGAAA,cAAc,OAAA;AACvB,6CAAmD;AAA1C,yGAAA,cAAc,OAAA;AACvB,iDAA8E;AAArE,6GAAA,gBAAgB,OAAA;AAAE,kHAAA,qBAAqB,OAAA;AAEhD,mBAAmB;AACnB,yCAAsD;AAA7C,gGAAA,MAAM,OAAA;AAAE,sGAAA,YAAY,OAAA;AAC7B,mDAAmI;AAA1H,6GAAA,cAAc,OAAA;AAAE,qHAAA,sBAAsB,OAAA;AAAE,iHAAA,kBAAkB,OAAA;AAAE,+GAAA,gBAAgB,OAAA;AAAE,6GAAA,cAAc,OAAA;AACrG,2CAAmG;AAA1F,yGAAA,cAAc,OAAA;AAAE,uGAAA,YAAY,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAAE,wGAAA,aAAa,OAAA;AACzE,6DAA6D;AAApD,qHAAA,iBAAiB,OAAA;AAE1B,qBAAqB;AACrB,yCAAuB;AAEvB,mBAAmB;AACnB,yCAA4C;AAAnC,0GAAA,aAAa,OAAA;AAGtB,sBAAsB;AACtB,mCAOkB;AANhB,yGAAA,eAAe,OAAA;AACf,6GAAA,mBAAmB,OAAA;AACnB,2GAAA,iBAAiB,OAAA;AACjB,qGAAA,WAAW,OAAA;AACX,uGAAA,aAAa,OAAA;AACb,0GAAA,gBAAgB,OAAA;AAIlB,0BAA0B;AAC1B,mCAAkE;AAAzD,wGAAA,cAAc,OAAA;AAAE,oGAAA,UAAU,OAAA;AAAE,oGAAA,UAAU,OAAA;AAG/C,yCAAyC;AACzC,wCAA8C;AAArC,mGAAA,WAAW,OAAA;AACpB,wCAA8C;AAArC,mGAAA,WAAW,OAAA;AACpB,sCAA4C;AAAnC,iGAAA,UAAU,OAAA;AACnB,8CAAoD;AAA3C,yGAAA,cAAc,OAAA;AACvB,8CAAoD;AAA3C,yGAAA,cAAc,OAAA;AAEvB,6BAA6B;AAC7B,2CAc0B;AAbxB,uGAAA,aAAa,OAAA;AACb,8GAAA,oBAAoB,OAAA;AACpB,yGAAA,eAAe,OAAA;AACf,yGAAA,eAAe,OAAA;AACf,2GAAA,iBAAiB,OAAA;AACjB,mGAAA,SAAS,OAAA;AACT,yGAAA,eAAe,OAAA;AACf,wGAAA,cAAc,OAAA;AACd,oGAAA,UAAU,OAAA;AACV,sGAAA,YAAY,OAAA;AACZ,sGAAA,YAAY,OAAA;AACZ,2GAAA,iBAAiB,OAAA;AACjB,2GAAA,iBAAiB,OAAA;AAGnB,UAAU;AACV,8DAA8D;AACjD,QAAA,OAAO,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * TestivAI Report — Compare all snapshots
3
+ *
4
+ * Diffs all temp captures against baselines using the Phase 1 diff engine.
5
+ */
6
+ import { SnapshotResult } from './results';
7
+ export interface CompareOptions {
8
+ threshold?: number;
9
+ projectRoot: string;
10
+ reportDir: string;
11
+ }
12
+ /**
13
+ * Compare all temp screenshots against their baselines.
14
+ *
15
+ * @returns Array of SnapshotResult for each snapshot
16
+ */
17
+ export declare function compareAll(options: CompareOptions): SnapshotResult[];
18
+ //# sourceMappingURL=compare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/report/compare.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,EAAE,cAAc,EAAkB,MAAM,WAAW,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,EAAE,CAuDpE"}
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ /**
3
+ * TestivAI Report — Compare all snapshots
4
+ *
5
+ * Diffs all temp captures against baselines using the Phase 1 diff engine.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.compareAll = compareAll;
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ const diff_1 = require("../diff");
45
+ const store_1 = require("../baselines/store");
46
+ /**
47
+ * Compare all temp screenshots against their baselines.
48
+ *
49
+ * @returns Array of SnapshotResult for each snapshot
50
+ */
51
+ function compareAll(options) {
52
+ const { projectRoot, reportDir, threshold = 0.1 } = options;
53
+ const store = new store_1.BaselineStore(projectRoot);
54
+ const tempNames = store.listTemp();
55
+ const results = [];
56
+ const imagesDir = path.join(reportDir, 'images');
57
+ for (const name of tempNames) {
58
+ const snapshotImagesDir = path.join(imagesDir, name);
59
+ fs.mkdirSync(snapshotImagesDir, { recursive: true });
60
+ const tempBuffer = store.readTemp(name);
61
+ if (!tempBuffer)
62
+ continue;
63
+ // Write current image to report
64
+ const currentPath = path.join(snapshotImagesDir, 'current.png');
65
+ fs.writeFileSync(currentPath, tempBuffer);
66
+ if (!store.exists(name)) {
67
+ // New snapshot — no baseline to compare against
68
+ results.push({
69
+ name,
70
+ status: 'new',
71
+ diffPercent: 0,
72
+ diffCount: 0,
73
+ totalPixels: 0,
74
+ currentPath: `images/${name}/current.png`,
75
+ });
76
+ continue;
77
+ }
78
+ // Read baseline
79
+ const baselineBuffer = store.read(name);
80
+ const baselinePath = path.join(snapshotImagesDir, 'baseline.png');
81
+ fs.writeFileSync(baselinePath, baselineBuffer);
82
+ // Parse raw RGBA from PNG buffers
83
+ // For now, we compare raw buffers directly. In a real implementation
84
+ // we'd decode PNGs to RGBA pixel data. Since temp/baseline are raw
85
+ // screenshots from the browser, we need to handle the PNG decoding.
86
+ // For simplicity and zero-dep, we do a byte-level comparison first,
87
+ // then fall back to diff engine for actual pixel data.
88
+ const result = compareBuffers(baselineBuffer, tempBuffer, name, snapshotImagesDir, threshold);
89
+ results.push(result);
90
+ }
91
+ return results;
92
+ }
93
+ /**
94
+ * Compare two PNG buffers.
95
+ *
96
+ * If buffers are identical bytes, returns passed immediately.
97
+ * Otherwise attempts pixel-level diff using the diff engine.
98
+ */
99
+ function compareBuffers(baselineBuffer, candidateBuffer, name, snapshotImagesDir, threshold) {
100
+ // Quick byte-level comparison
101
+ if (baselineBuffer.equals(candidateBuffer)) {
102
+ return {
103
+ name,
104
+ status: 'passed',
105
+ diffPercent: 0,
106
+ diffCount: 0,
107
+ totalPixels: 0,
108
+ baselinePath: `images/${name}/baseline.png`,
109
+ currentPath: `images/${name}/current.png`,
110
+ };
111
+ }
112
+ // Buffers differ — attempt pixel-level diff
113
+ // We assume raw RGBA pixel data with known dimensions
114
+ // In practice, we need to decode PNGs first. For now, treat as raw RGBA.
115
+ // The actual PNG decoding will be added when fast-png is available.
116
+ try {
117
+ // Try to use as raw RGBA data
118
+ // Calculate dimensions assuming square-ish aspect ratio
119
+ const pixelCount = baselineBuffer.length / 4;
120
+ const width = Math.ceil(Math.sqrt(pixelCount));
121
+ const height = Math.ceil(pixelCount / width);
122
+ const expectedLen = width * height * 4;
123
+ // Pad buffers if needed
124
+ const baseline8 = new Uint8ClampedArray(expectedLen);
125
+ baseline8.set(new Uint8ClampedArray(baselineBuffer.buffer, baselineBuffer.byteOffset, Math.min(baselineBuffer.length, expectedLen)));
126
+ const candidate8 = new Uint8ClampedArray(expectedLen);
127
+ candidate8.set(new Uint8ClampedArray(candidateBuffer.buffer, candidateBuffer.byteOffset, Math.min(candidateBuffer.length, expectedLen)));
128
+ const diffOutput = new Uint8ClampedArray(expectedLen);
129
+ const diffResult = (0, diff_1.diff)(baseline8, candidate8, diffOutput, width, height, { threshold });
130
+ // Write diff image (raw RGBA for now)
131
+ const diffPath = path.join(snapshotImagesDir, 'diff.png');
132
+ fs.writeFileSync(diffPath, Buffer.from(diffOutput.buffer));
133
+ // We already know the raw bytes differ (byte-equal returns early above).
134
+ // If the pixel diff still reports identical (sub-threshold), the images
135
+ // are visually equivalent but binary-different — still flag as "changed"
136
+ // so the user is aware.
137
+ const status = 'changed';
138
+ const diffPercent = diffResult.diffPercent > 0 ? diffResult.diffPercent : 0.01;
139
+ return {
140
+ name,
141
+ status,
142
+ diffPercent,
143
+ diffCount: diffResult.diffCount,
144
+ totalPixels: diffResult.totalPixels,
145
+ baselinePath: `images/${name}/baseline.png`,
146
+ currentPath: `images/${name}/current.png`,
147
+ diffPath: `images/${name}/diff.png`,
148
+ };
149
+ }
150
+ catch {
151
+ // If pixel diff fails (e.g., not raw RGBA), fall back to binary comparison
152
+ return {
153
+ name,
154
+ status: 'changed',
155
+ diffPercent: 100,
156
+ diffCount: 0,
157
+ totalPixels: 0,
158
+ baselinePath: `images/${name}/baseline.png`,
159
+ currentPath: `images/${name}/current.png`,
160
+ };
161
+ }
162
+ }
163
+ //# sourceMappingURL=compare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/report/compare.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * TestivAI Report — Generator
3
+ *
4
+ * Orchestrator: compareAll → build ReportData → write results.json
5
+ * → render HTML → optionally auto-open browser.
6
+ */
7
+ import { ReportData } from './results';
8
+ export interface GenerateReportOptions {
9
+ projectRoot: string;
10
+ /** Directory for report output. Default: 'visual-report' */
11
+ reportDir?: string;
12
+ /** Diff threshold (0-1). Default: 0.1 */
13
+ threshold?: number;
14
+ /** Auto-open browser after generating. Default: true */
15
+ autoOpen?: boolean;
16
+ /** SDK version string for the report header */
17
+ version?: string;
18
+ }
19
+ /**
20
+ * Generate a complete visual regression report.
21
+ *
22
+ * 1. Compare all temp screenshots against baselines
23
+ * 2. Build ReportData with summary
24
+ * 3. Write results.json (machine-readable)
25
+ * 4. Render and write index.html
26
+ * 5. Optionally open in browser
27
+ *
28
+ * @returns The report data
29
+ */
30
+ export declare function generateReport(options: GenerateReportOptions): ReportData;
31
+ //# sourceMappingURL=generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/report/generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAE,UAAU,EAAiB,MAAM,WAAW,CAAC;AAGtD,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,UAAU,CAuDzE"}
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ /**
3
+ * TestivAI Report — Generator
4
+ *
5
+ * Orchestrator: compareAll → build ReportData → write results.json
6
+ * → render HTML → optionally auto-open browser.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.generateReport = generateReport;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const child_process_1 = require("child_process");
46
+ const compare_1 = require("./compare");
47
+ const template_1 = require("./template");
48
+ /**
49
+ * Generate a complete visual regression report.
50
+ *
51
+ * 1. Compare all temp screenshots against baselines
52
+ * 2. Build ReportData with summary
53
+ * 3. Write results.json (machine-readable)
54
+ * 4. Render and write index.html
55
+ * 5. Optionally open in browser
56
+ *
57
+ * @returns The report data
58
+ */
59
+ function generateReport(options) {
60
+ const { projectRoot, reportDir: reportDirRelative = 'visual-report', threshold = 0.1, autoOpen = true, version = '2.0.0', } = options;
61
+ const reportDir = path.isAbsolute(reportDirRelative)
62
+ ? reportDirRelative
63
+ : path.join(projectRoot, reportDirRelative);
64
+ // Ensure report directory exists
65
+ fs.mkdirSync(reportDir, { recursive: true });
66
+ // 1. Compare all snapshots
67
+ const compareOptions = {
68
+ projectRoot,
69
+ reportDir,
70
+ threshold,
71
+ };
72
+ const snapshots = (0, compare_1.compareAll)(compareOptions);
73
+ // 2. Build summary
74
+ const summary = {
75
+ total: snapshots.length,
76
+ passed: snapshots.filter((s) => s.status === 'passed').length,
77
+ changed: snapshots.filter((s) => s.status === 'changed').length,
78
+ newSnapshots: snapshots.filter((s) => s.status === 'new').length,
79
+ };
80
+ // 3. Build report data
81
+ const reportData = {
82
+ version,
83
+ timestamp: new Date().toISOString(),
84
+ summary,
85
+ snapshots,
86
+ };
87
+ // 4. Write results.json
88
+ const resultsPath = path.join(reportDir, 'results.json');
89
+ fs.writeFileSync(resultsPath, JSON.stringify(reportData, null, 2));
90
+ // 5. Render and write HTML
91
+ const html = (0, template_1.renderHtml)(reportData);
92
+ const htmlPath = path.join(reportDir, 'index.html');
93
+ fs.writeFileSync(htmlPath, html);
94
+ // 6. Auto-open browser
95
+ if (autoOpen) {
96
+ openInBrowser(htmlPath);
97
+ }
98
+ return reportData;
99
+ }
100
+ /**
101
+ * Open a file in the default browser.
102
+ * Best-effort — does not throw on failure.
103
+ */
104
+ function openInBrowser(filePath) {
105
+ const url = `file://${filePath}`;
106
+ const platform = process.platform;
107
+ let cmd;
108
+ if (platform === 'darwin') {
109
+ cmd = `open "${url}"`;
110
+ }
111
+ else if (platform === 'win32') {
112
+ cmd = `start "" "${url}"`;
113
+ }
114
+ else {
115
+ cmd = `xdg-open "${url}"`;
116
+ }
117
+ (0, child_process_1.exec)(cmd, (err) => {
118
+ if (err) {
119
+ // Silently ignore — CI environments won't have a browser
120
+ }
121
+ });
122
+ }
123
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../src/report/generator.ts"],"names":[],"mappings":"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"}