@testivai/witness 1.0.1 → 1.0.3
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/README.md +44 -13
- package/dist/baselines/index.d.ts +6 -0
- package/dist/baselines/index.d.ts.map +1 -0
- package/dist/baselines/index.js +9 -0
- package/dist/baselines/index.js.map +1 -0
- package/dist/baselines/store.d.ts +100 -0
- package/dist/baselines/store.d.ts.map +1 -0
- package/dist/baselines/store.js +298 -0
- package/dist/baselines/store.js.map +1 -0
- package/dist/bin/testivai.js +2 -0
- package/dist/bin/testivai.js.map +1 -1
- package/dist/browser/binding.d.ts.map +1 -1
- package/dist/browser/binding.js +173 -42
- package/dist/browser/binding.js.map +1 -1
- package/dist/browser/capture.d.ts.map +1 -1
- package/dist/browser/capture.js +7 -4
- package/dist/browser/capture.js.map +1 -1
- package/dist/commands/approve.d.ts +3 -0
- package/dist/commands/approve.d.ts.map +1 -0
- package/dist/commands/approve.js +94 -0
- package/dist/commands/approve.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +53 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +98 -0
- package/dist/commands/run.js.map +1 -1
- package/dist/config/index.d.ts +6 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +14 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/local-config.d.ts +54 -0
- package/dist/config/local-config.d.ts.map +1 -0
- package/dist/config/local-config.js +130 -0
- package/dist/config/local-config.js.map +1 -0
- package/dist/diff/diff.d.ts +27 -0
- package/dist/diff/diff.d.ts.map +1 -0
- package/dist/diff/diff.js +216 -0
- package/dist/diff/diff.js.map +1 -0
- package/dist/diff/dom-diff.d.ts +85 -0
- package/dist/diff/dom-diff.d.ts.map +1 -0
- package/dist/diff/dom-diff.js +291 -0
- package/dist/diff/dom-diff.js.map +1 -0
- package/dist/diff/ignore.d.ts +22 -0
- package/dist/diff/ignore.d.ts.map +1 -0
- package/dist/diff/ignore.js +43 -0
- package/dist/diff/ignore.js.map +1 -0
- package/dist/diff/index.d.ts +18 -0
- package/dist/diff/index.d.ts.map +1 -0
- package/dist/diff/index.js +25 -0
- package/dist/diff/index.js.map +1 -0
- package/dist/diff/minimap.d.ts +23 -0
- package/dist/diff/minimap.d.ts.map +1 -0
- package/dist/diff/minimap.js +43 -0
- package/dist/diff/minimap.js.map +1 -0
- package/dist/diff/regions.d.ts +23 -0
- package/dist/diff/regions.d.ts.map +1 -0
- package/dist/diff/regions.js +129 -0
- package/dist/diff/regions.js.map +1 -0
- package/dist/diff/resize.d.ts +27 -0
- package/dist/diff/resize.d.ts.map +1 -0
- package/dist/diff/resize.js +82 -0
- package/dist/diff/resize.js.map +1 -0
- package/dist/diff/types.d.ts +79 -0
- package/dist/diff/types.d.ts.map +1 -0
- package/dist/diff/types.js +10 -0
- package/dist/diff/types.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -2
- package/dist/index.js.map +1 -1
- package/dist/report/compare.d.ts +18 -0
- package/dist/report/compare.d.ts.map +1 -0
- package/dist/report/compare.js +193 -0
- package/dist/report/compare.js.map +1 -0
- package/dist/report/generator.d.ts +31 -0
- package/dist/report/generator.d.ts.map +1 -0
- package/dist/report/generator.js +123 -0
- package/dist/report/generator.js.map +1 -0
- package/dist/report/index.d.ts +10 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +13 -0
- package/dist/report/index.js.map +1 -0
- package/dist/report/results.d.ts +56 -0
- package/dist/report/results.d.ts.map +1 -0
- package/dist/report/results.js +8 -0
- package/dist/report/results.js.map +1 -0
- package/dist/report/template.d.ts +14 -0
- package/dist/report/template.d.ts.map +1 -0
- package/dist/report/template.js +275 -0
- package/dist/report/template.js.map +1 -0
- package/package.json +58 -15
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
|
-
|
|
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;
|
|
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;AAOH,OAAO,EAAqB,cAAc,EAAkB,MAAM,WAAW,CAAC;AAE9E,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,CAmEpE"}
|
|
@@ -0,0 +1,193 @@
|
|
|
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 dom_diff_1 = require("../diff/dom-diff");
|
|
46
|
+
const store_1 = require("../baselines/store");
|
|
47
|
+
/**
|
|
48
|
+
* Compare all temp screenshots against their baselines.
|
|
49
|
+
*
|
|
50
|
+
* @returns Array of SnapshotResult for each snapshot
|
|
51
|
+
*/
|
|
52
|
+
function compareAll(options) {
|
|
53
|
+
const { projectRoot, reportDir, threshold = 0.1 } = options;
|
|
54
|
+
const store = new store_1.BaselineStore(projectRoot);
|
|
55
|
+
const tempNames = store.listTemp();
|
|
56
|
+
const results = [];
|
|
57
|
+
const imagesDir = path.join(reportDir, 'images');
|
|
58
|
+
for (const name of tempNames) {
|
|
59
|
+
const snapshotImagesDir = path.join(imagesDir, name);
|
|
60
|
+
fs.mkdirSync(snapshotImagesDir, { recursive: true });
|
|
61
|
+
const tempBuffer = store.readTemp(name);
|
|
62
|
+
if (!tempBuffer)
|
|
63
|
+
continue;
|
|
64
|
+
// Write current image to report
|
|
65
|
+
const currentPath = path.join(snapshotImagesDir, 'current.png');
|
|
66
|
+
fs.writeFileSync(currentPath, tempBuffer);
|
|
67
|
+
if (!store.exists(name)) {
|
|
68
|
+
// New snapshot — no baseline to compare against
|
|
69
|
+
results.push({
|
|
70
|
+
name,
|
|
71
|
+
status: 'new',
|
|
72
|
+
diffPercent: 0,
|
|
73
|
+
diffCount: 0,
|
|
74
|
+
totalPixels: 0,
|
|
75
|
+
currentPath: `images/${name}/current.png`,
|
|
76
|
+
});
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
// Read baseline
|
|
80
|
+
const baselineBuffer = store.read(name);
|
|
81
|
+
const baselinePath = path.join(snapshotImagesDir, 'baseline.png');
|
|
82
|
+
fs.writeFileSync(baselinePath, baselineBuffer);
|
|
83
|
+
// Parse raw RGBA from PNG buffers
|
|
84
|
+
// For now, we compare raw buffers directly. In a real implementation
|
|
85
|
+
// we'd decode PNGs to RGBA pixel data. Since temp/baseline are raw
|
|
86
|
+
// screenshots from the browser, we need to handle the PNG decoding.
|
|
87
|
+
// For simplicity and zero-dep, we do a byte-level comparison first,
|
|
88
|
+
// then fall back to diff engine for actual pixel data.
|
|
89
|
+
const result = compareBuffers(baselineBuffer, tempBuffer, name, snapshotImagesDir, threshold);
|
|
90
|
+
// DOM-level noise hint. Only meaningful when both sides captured DOM.
|
|
91
|
+
// For 'passed' (pixel-identical) we skip the work — it's already the
|
|
92
|
+
// strongest possible signal.
|
|
93
|
+
if (result.status === 'changed') {
|
|
94
|
+
const baselineDom = store.readDom(name);
|
|
95
|
+
const candidateDom = store.readTempDom(name);
|
|
96
|
+
const domSignal = computeDomSignal(baselineDom, candidateDom);
|
|
97
|
+
if (domSignal) {
|
|
98
|
+
result.dom = domSignal;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
results.push(result);
|
|
102
|
+
}
|
|
103
|
+
return results;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Run DOM diff and turn it into a report-shaped signal.
|
|
107
|
+
*
|
|
108
|
+
* Returns null when DOM data isn't available on either side — we don't
|
|
109
|
+
* want to confuse the user with "DOM unchanged" hints when the adapter
|
|
110
|
+
* never captured DOM in the first place.
|
|
111
|
+
*/
|
|
112
|
+
function computeDomSignal(baselineDom, candidateDom) {
|
|
113
|
+
if (!baselineDom || !candidateDom)
|
|
114
|
+
return null;
|
|
115
|
+
const result = (0, dom_diff_1.domDiff)(baselineDom, candidateDom);
|
|
116
|
+
return {
|
|
117
|
+
changed: result.domChanged,
|
|
118
|
+
summary: result.summary,
|
|
119
|
+
// The whole point: pixels differ, DOM does not → likely render noise.
|
|
120
|
+
noiseHint: !result.domChanged,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Compare two PNG buffers.
|
|
125
|
+
*
|
|
126
|
+
* If buffers are identical bytes, returns passed immediately.
|
|
127
|
+
* Otherwise attempts pixel-level diff using the diff engine.
|
|
128
|
+
*/
|
|
129
|
+
function compareBuffers(baselineBuffer, candidateBuffer, name, snapshotImagesDir, threshold) {
|
|
130
|
+
// Quick byte-level comparison
|
|
131
|
+
if (baselineBuffer.equals(candidateBuffer)) {
|
|
132
|
+
return {
|
|
133
|
+
name,
|
|
134
|
+
status: 'passed',
|
|
135
|
+
diffPercent: 0,
|
|
136
|
+
diffCount: 0,
|
|
137
|
+
totalPixels: 0,
|
|
138
|
+
baselinePath: `images/${name}/baseline.png`,
|
|
139
|
+
currentPath: `images/${name}/current.png`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
// Buffers differ — attempt pixel-level diff
|
|
143
|
+
// We assume raw RGBA pixel data with known dimensions
|
|
144
|
+
// In practice, we need to decode PNGs first. For now, treat as raw RGBA.
|
|
145
|
+
// The actual PNG decoding will be added when fast-png is available.
|
|
146
|
+
try {
|
|
147
|
+
// Try to use as raw RGBA data
|
|
148
|
+
// Calculate dimensions assuming square-ish aspect ratio
|
|
149
|
+
const pixelCount = baselineBuffer.length / 4;
|
|
150
|
+
const width = Math.ceil(Math.sqrt(pixelCount));
|
|
151
|
+
const height = Math.ceil(pixelCount / width);
|
|
152
|
+
const expectedLen = width * height * 4;
|
|
153
|
+
// Pad buffers if needed
|
|
154
|
+
const baseline8 = new Uint8ClampedArray(expectedLen);
|
|
155
|
+
baseline8.set(new Uint8ClampedArray(baselineBuffer.buffer, baselineBuffer.byteOffset, Math.min(baselineBuffer.length, expectedLen)));
|
|
156
|
+
const candidate8 = new Uint8ClampedArray(expectedLen);
|
|
157
|
+
candidate8.set(new Uint8ClampedArray(candidateBuffer.buffer, candidateBuffer.byteOffset, Math.min(candidateBuffer.length, expectedLen)));
|
|
158
|
+
const diffOutput = new Uint8ClampedArray(expectedLen);
|
|
159
|
+
const diffResult = (0, diff_1.diff)(baseline8, candidate8, diffOutput, width, height, { threshold });
|
|
160
|
+
// Write diff image (raw RGBA for now)
|
|
161
|
+
const diffPath = path.join(snapshotImagesDir, 'diff.png');
|
|
162
|
+
fs.writeFileSync(diffPath, Buffer.from(diffOutput.buffer));
|
|
163
|
+
// We already know the raw bytes differ (byte-equal returns early above).
|
|
164
|
+
// If the pixel diff still reports identical (sub-threshold), the images
|
|
165
|
+
// are visually equivalent but binary-different — still flag as "changed"
|
|
166
|
+
// so the user is aware.
|
|
167
|
+
const status = 'changed';
|
|
168
|
+
const diffPercent = diffResult.diffPercent > 0 ? diffResult.diffPercent : 0.01;
|
|
169
|
+
return {
|
|
170
|
+
name,
|
|
171
|
+
status,
|
|
172
|
+
diffPercent,
|
|
173
|
+
diffCount: diffResult.diffCount,
|
|
174
|
+
totalPixels: diffResult.totalPixels,
|
|
175
|
+
baselinePath: `images/${name}/baseline.png`,
|
|
176
|
+
currentPath: `images/${name}/current.png`,
|
|
177
|
+
diffPath: `images/${name}/diff.png`,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// If pixel diff fails (e.g., not raw RGBA), fall back to binary comparison
|
|
182
|
+
return {
|
|
183
|
+
name,
|
|
184
|
+
status: 'changed',
|
|
185
|
+
diffPercent: 100,
|
|
186
|
+
diffCount: 0,
|
|
187
|
+
totalPixels: 0,
|
|
188
|
+
baselinePath: `images/${name}/baseline.png`,
|
|
189
|
+
currentPath: `images/${name}/current.png`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
//# 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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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestivAI Report Generator
|
|
3
|
+
*/
|
|
4
|
+
export { generateReport } from './generator';
|
|
5
|
+
export type { GenerateReportOptions } from './generator';
|
|
6
|
+
export { compareAll } from './compare';
|
|
7
|
+
export type { CompareOptions } from './compare';
|
|
8
|
+
export { renderHtml } from './template';
|
|
9
|
+
export type { ReportData, ReportSummary, SnapshotResult, SnapshotStatus, SnapshotDomSignal, } from './results';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/report/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,YAAY,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACd,cAAc,EACd,iBAAiB,GAClB,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TestivAI Report Generator
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.renderHtml = exports.compareAll = exports.generateReport = void 0;
|
|
7
|
+
var generator_1 = require("./generator");
|
|
8
|
+
Object.defineProperty(exports, "generateReport", { enumerable: true, get: function () { return generator_1.generateReport; } });
|
|
9
|
+
var compare_1 = require("./compare");
|
|
10
|
+
Object.defineProperty(exports, "compareAll", { enumerable: true, get: function () { return compare_1.compareAll; } });
|
|
11
|
+
var template_1 = require("./template");
|
|
12
|
+
Object.defineProperty(exports, "renderHtml", { enumerable: true, get: function () { return template_1.renderHtml; } });
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/report/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,yCAA6C;AAApC,2GAAA,cAAc,OAAA;AAEvB,qCAAuC;AAA9B,qGAAA,UAAU,OAAA;AAEnB,uCAAwC;AAA/B,sGAAA,UAAU,OAAA"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestivAI Report — Result types
|
|
3
|
+
*
|
|
4
|
+
* Machine-readable output for the GitHub Action and other consumers.
|
|
5
|
+
*/
|
|
6
|
+
export type SnapshotStatus = 'passed' | 'changed' | 'new';
|
|
7
|
+
export interface SnapshotDomSignal {
|
|
8
|
+
/** True if the DOM differs structurally between baseline and candidate. */
|
|
9
|
+
changed: boolean;
|
|
10
|
+
/** Per-bucket counts; null when changed is false. */
|
|
11
|
+
summary: {
|
|
12
|
+
added: number;
|
|
13
|
+
removed: number;
|
|
14
|
+
attributeChanges: number;
|
|
15
|
+
} | null;
|
|
16
|
+
/**
|
|
17
|
+
* Noise hint: pixel diff is non-zero but DOM is structurally unchanged.
|
|
18
|
+
* Suggests render noise (anti-aliasing, font hinting, sub-pixel layout)
|
|
19
|
+
* rather than a real visual regression.
|
|
20
|
+
*/
|
|
21
|
+
noiseHint: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface SnapshotResult {
|
|
24
|
+
name: string;
|
|
25
|
+
status: SnapshotStatus;
|
|
26
|
+
/** Diff percentage (0-100). 0 for passed/new. */
|
|
27
|
+
diffPercent: number;
|
|
28
|
+
/** Number of changed pixels */
|
|
29
|
+
diffCount: number;
|
|
30
|
+
/** Total pixels compared */
|
|
31
|
+
totalPixels: number;
|
|
32
|
+
/** Relative path to baseline image (if any) */
|
|
33
|
+
baselinePath?: string;
|
|
34
|
+
/** Relative path to current (candidate) image */
|
|
35
|
+
currentPath?: string;
|
|
36
|
+
/** Relative path to diff image (if any) */
|
|
37
|
+
diffPath?: string;
|
|
38
|
+
/**
|
|
39
|
+
* DOM-level signal. Present only when both baseline and candidate
|
|
40
|
+
* captured DOM HTML alongside the screenshot.
|
|
41
|
+
*/
|
|
42
|
+
dom?: SnapshotDomSignal;
|
|
43
|
+
}
|
|
44
|
+
export interface ReportSummary {
|
|
45
|
+
total: number;
|
|
46
|
+
passed: number;
|
|
47
|
+
changed: number;
|
|
48
|
+
newSnapshots: number;
|
|
49
|
+
}
|
|
50
|
+
export interface ReportData {
|
|
51
|
+
version: string;
|
|
52
|
+
timestamp: string;
|
|
53
|
+
summary: ReportSummary;
|
|
54
|
+
snapshots: SnapshotResult[];
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=results.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../src/report/results.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;AAE1D,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAC;IACjB,qDAAqD;IACrD,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7E;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,GAAG,CAAC,EAAE,iBAAiB,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,aAAa,CAAC;IACvB,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"results.js","sourceRoot":"","sources":["../../src/report/results.ts"],"names":[],"mappings":";AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestivAI Report — HTML Template
|
|
3
|
+
*
|
|
4
|
+
* Single-file HTML with inlined CSS/JS.
|
|
5
|
+
* Dark navy sidebar, cyan accents, summary cards,
|
|
6
|
+
* Changed → New → Passed sections, 3-column diff view,
|
|
7
|
+
* approve command copy button, cloud upgrade CTA, image zoom overlay.
|
|
8
|
+
*/
|
|
9
|
+
import { ReportData } from './results';
|
|
10
|
+
/**
|
|
11
|
+
* Render a standalone HTML report from report data.
|
|
12
|
+
*/
|
|
13
|
+
export declare function renderHtml(data: ReportData): string;
|
|
14
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/report/template.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAkB,MAAM,WAAW,CAAC;AAEvD;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAuLnD"}
|