@sun-asterisk/sungen 2.6.1 → 2.6.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 (50) hide show
  1. package/dist/cli/commands/dashboard.d.ts +10 -0
  2. package/dist/cli/commands/dashboard.d.ts.map +1 -0
  3. package/dist/cli/commands/dashboard.js +171 -0
  4. package/dist/cli/commands/dashboard.js.map +1 -0
  5. package/dist/cli/index.js +4 -2
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/dashboard/history-store.d.ts +27 -0
  8. package/dist/dashboard/history-store.d.ts.map +1 -0
  9. package/dist/dashboard/history-store.js +112 -0
  10. package/dist/dashboard/history-store.js.map +1 -0
  11. package/dist/dashboard/html-renderer.d.ts +30 -0
  12. package/dist/dashboard/html-renderer.d.ts.map +1 -0
  13. package/dist/dashboard/html-renderer.js +111 -0
  14. package/dist/dashboard/html-renderer.js.map +1 -0
  15. package/dist/dashboard/snapshot-builder.d.ts +30 -0
  16. package/dist/dashboard/snapshot-builder.d.ts.map +1 -0
  17. package/dist/dashboard/snapshot-builder.js +263 -0
  18. package/dist/dashboard/snapshot-builder.js.map +1 -0
  19. package/dist/dashboard/templates/index.html +287 -0
  20. package/dist/dashboard/types.d.ts +122 -0
  21. package/dist/dashboard/types.d.ts.map +1 -0
  22. package/dist/dashboard/types.js +11 -0
  23. package/dist/dashboard/types.js.map +1 -0
  24. package/dist/exporters/json-exporter.d.ts +25 -0
  25. package/dist/exporters/json-exporter.d.ts.map +1 -0
  26. package/dist/exporters/json-exporter.js +135 -0
  27. package/dist/exporters/json-exporter.js.map +1 -0
  28. package/dist/exporters/playwright-report-parser.d.ts +2 -1
  29. package/dist/exporters/playwright-report-parser.d.ts.map +1 -1
  30. package/dist/exporters/playwright-report-parser.js +12 -5
  31. package/dist/exporters/playwright-report-parser.js.map +1 -1
  32. package/dist/exporters/spec-parser.d.ts.map +1 -1
  33. package/dist/exporters/spec-parser.js +8 -3
  34. package/dist/exporters/spec-parser.js.map +1 -1
  35. package/dist/orchestrator/templates/playwright.config.d.ts.map +1 -1
  36. package/dist/orchestrator/templates/playwright.config.js +9 -1
  37. package/dist/orchestrator/templates/playwright.config.js.map +1 -1
  38. package/dist/orchestrator/templates/playwright.config.ts +11 -1
  39. package/package.json +4 -3
  40. package/src/cli/commands/dashboard.ts +158 -0
  41. package/src/cli/index.ts +4 -2
  42. package/src/dashboard/history-store.ts +86 -0
  43. package/src/dashboard/html-renderer.ts +90 -0
  44. package/src/dashboard/snapshot-builder.ts +273 -0
  45. package/src/dashboard/templates/index.html +287 -0
  46. package/src/dashboard/types.ts +148 -0
  47. package/src/exporters/json-exporter.ts +162 -0
  48. package/src/exporters/playwright-report-parser.ts +12 -5
  49. package/src/exporters/spec-parser.ts +8 -3
  50. package/src/orchestrator/templates/playwright.config.ts +11 -1
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `sungen dashboard` CLI command.
3
+ *
4
+ * Builds qa/dashboard/index.html — a single-file, share-ready test report —
5
+ * from existing Gherkin features, compiled .spec.ts files, and Playwright
6
+ * results. Snapshots are persisted under qa/dashboard/history/ (max 20).
7
+ */
8
+ import { Command } from 'commander';
9
+ export declare function registerDashboardCommand(program: Command): void;
10
+ //# sourceMappingURL=dashboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/dashboard.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwDpC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiF/D"}
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ /**
3
+ * `sungen dashboard` CLI command.
4
+ *
5
+ * Builds qa/dashboard/index.html — a single-file, share-ready test report —
6
+ * from existing Gherkin features, compiled .spec.ts files, and Playwright
7
+ * results. Snapshots are persisted under qa/dashboard/history/ (max 20).
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.registerDashboardCommand = registerDashboardCommand;
44
+ const path = __importStar(require("path"));
45
+ const fs = __importStar(require("fs"));
46
+ const child_process_1 = require("child_process");
47
+ const snapshot_builder_1 = require("../../dashboard/snapshot-builder");
48
+ const history_store_1 = require("../../dashboard/history-store");
49
+ const html_renderer_1 = require("../../dashboard/html-renderer");
50
+ const COLOR = {
51
+ reset: '\x1b[0m',
52
+ gray: '\x1b[90m',
53
+ green: '\x1b[32m',
54
+ red: '\x1b[31m',
55
+ yellow: '\x1b[33m',
56
+ cyan: '\x1b[36m',
57
+ bold: '\x1b[1m',
58
+ };
59
+ function log(msg) { console.log(msg); }
60
+ function getEnvironment(cwd) {
61
+ let baseURL = '';
62
+ let projectName = 'chromium';
63
+ const configPath = path.join(cwd, 'playwright.config.ts');
64
+ if (fs.existsSync(configPath)) {
65
+ const c = fs.readFileSync(configPath, 'utf-8');
66
+ const baseMatch = c.match(/baseURL:\s*['"]([^'"]+)['"]/);
67
+ if (baseMatch)
68
+ baseURL = baseMatch[1];
69
+ const projMatch = c.match(/name:\s*['"]([^'"]+)['"]/);
70
+ if (projMatch)
71
+ projectName = projMatch[1];
72
+ }
73
+ let executor = process.env.CI_USER || process.env.USER || '';
74
+ try {
75
+ const gitUser = (0, child_process_1.execSync)('git config user.name', { cwd, encoding: 'utf-8' }).trim();
76
+ if (gitUser)
77
+ executor = gitUser;
78
+ }
79
+ catch { /* ignore */ }
80
+ return { baseURL, projectName, executor };
81
+ }
82
+ function registerDashboardCommand(program) {
83
+ program
84
+ .command('dashboard')
85
+ .description('Build a single-file HTML dashboard summarising all test cases & results')
86
+ .argument('[names...]', 'Specific screen or flow names. Omit to include all.')
87
+ .option('--no-history', 'Do not persist this run under qa/dashboard/history/')
88
+ .option('--max-history <n>', `Cap retained history files (default: ${history_store_1.DEFAULT_MAX_HISTORY})`)
89
+ .option('--open', 'Open the rendered HTML in the default browser when done')
90
+ .action(async (names, options) => {
91
+ try {
92
+ const cwd = process.cwd();
93
+ // 1. Scope detection
94
+ let targets;
95
+ if (names && names.length > 0) {
96
+ targets = names.map((n) => (0, snapshot_builder_1.resolveTargetType)(cwd, n));
97
+ }
98
+ else {
99
+ targets = (0, snapshot_builder_1.listDashboardTargets)(cwd);
100
+ if (targets.length === 0) {
101
+ console.error(`${COLOR.red}No screens or flows found in qa/screens/ or qa/flows/${COLOR.reset}`);
102
+ process.exit(1);
103
+ }
104
+ }
105
+ const labels = targets.map((t) => t.isFlow ? `flow/${t.name}` : t.name);
106
+ log(`${COLOR.bold}sungen dashboard${COLOR.reset} — building report for ${targets.length} target(s): ${labels.join(', ')}`);
107
+ // 2. Build snapshot
108
+ const env = getEnvironment(cwd);
109
+ const snapshot = (0, snapshot_builder_1.buildDashboardSnapshot)({
110
+ cwd,
111
+ targets,
112
+ env,
113
+ continueOnError: true,
114
+ });
115
+ log(`\n${COLOR.bold}Snapshot${COLOR.reset} ${snapshot.runId} ` +
116
+ `${COLOR.gray}(${snapshot.screens.length} screens, ${snapshot.summary.total} TCs)${COLOR.reset}`);
117
+ // 3. Persist history (unless disabled)
118
+ if (!options.noHistory) {
119
+ const max = options.maxHistory ? Math.max(1, parseInt(options.maxHistory, 10) || history_store_1.DEFAULT_MAX_HISTORY) : history_store_1.DEFAULT_MAX_HISTORY;
120
+ const result = (0, history_store_1.writeSnapshotToHistory)(cwd, snapshot, max);
121
+ log(` ${COLOR.green}✓${COLOR.reset} history ${path.relative(cwd, result.written)}`);
122
+ if (result.pruned.length > 0) {
123
+ log(` ${COLOR.gray} pruned ${result.pruned.length} older snapshot(s)${COLOR.reset}`);
124
+ }
125
+ }
126
+ // 4. Render HTML
127
+ const payload = (0, html_renderer_1.buildPayload)(cwd, snapshot);
128
+ const rendered = (0, html_renderer_1.renderDashboardHtml)(cwd, payload);
129
+ log(` ${COLOR.green}✓${COLOR.reset} html ${path.relative(cwd, rendered.outputPath)} ${COLOR.gray}(${(rendered.bytes / 1024 / 1024).toFixed(2)} MB)${COLOR.reset}`);
130
+ // 5. Print quick summary
131
+ const s = snapshot.summary;
132
+ log('');
133
+ log(` ${COLOR.bold}Summary${COLOR.reset}`);
134
+ log(` Total: ${s.total}`);
135
+ log(` ${COLOR.green}Passed${COLOR.reset}: ${s.passed}`);
136
+ log(` ${COLOR.red}Failed${COLOR.reset}: ${s.failed}`);
137
+ log(` Pending: ${s.pending}`);
138
+ log(` N/A: ${s.na}`);
139
+ if (s.notCompiled > 0) {
140
+ log(` ${COLOR.yellow}Not compiled${COLOR.reset}: ${s.notCompiled}`);
141
+ }
142
+ const passRatePct = (s.passRate * 100).toFixed(1);
143
+ log(` Pass rate: ${passRatePct}% ${COLOR.gray}(of executed)${COLOR.reset}`);
144
+ log('');
145
+ // 6. Optionally open in browser
146
+ if (options.open) {
147
+ openInBrowser(rendered.outputPath);
148
+ }
149
+ }
150
+ catch (err) {
151
+ console.error(`${COLOR.red}Fatal:${COLOR.reset} ${err instanceof Error ? err.message : err}`);
152
+ process.exit(1);
153
+ }
154
+ });
155
+ }
156
+ function openInBrowser(filePath) {
157
+ const fileUrl = `file://${filePath}`;
158
+ const platform = process.platform;
159
+ try {
160
+ if (platform === 'darwin')
161
+ (0, child_process_1.execSync)(`open "${fileUrl}"`);
162
+ else if (platform === 'win32')
163
+ (0, child_process_1.execSync)(`start "" "${fileUrl}"`);
164
+ else
165
+ (0, child_process_1.execSync)(`xdg-open "${fileUrl}"`);
166
+ }
167
+ catch {
168
+ // ignore — user can open manually
169
+ }
170
+ }
171
+ //# sourceMappingURL=dashboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../../src/cli/commands/dashboard.ts"],"names":[],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -14,16 +14,17 @@ const update_1 = require("./commands/update");
14
14
  const delivery_1 = require("./commands/delivery");
15
15
  const figma_1 = require("./commands/figma");
16
16
  const add_flow_1 = require("./commands/add-flow");
17
+ const dashboard_1 = require("./commands/dashboard");
17
18
  async function main() {
18
19
  const program = new commander_1.Command();
19
20
  program
20
21
  .name('sungen')
21
22
  .description('Deterministic E2E Test Compiler — Gherkin + Selectors → Playwright')
22
- .version('2.6.1');
23
+ .version('2.6.2');
23
24
  // Global options
24
25
  program
25
26
  .option('-v, --verbose', 'Enable verbose logging');
26
- // Register commands (7)
27
+ // Register commands (9)
27
28
  (0, init_1.registerInitCommand)(program);
28
29
  (0, add_1.registerAddCommand)(program);
29
30
  (0, generate_1.registerGenerateCommand)(program);
@@ -32,6 +33,7 @@ async function main() {
32
33
  (0, delivery_1.registerDeliveryCommand)(program);
33
34
  (0, figma_1.registerFigmaCommand)(program);
34
35
  (0, add_flow_1.registerAddFlowCommand)(program);
36
+ (0, dashboard_1.registerDashboardCommand)(program);
35
37
  await program.parseAsync(process.argv);
36
38
  }
37
39
  main().catch((error) => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";;AACA;;;GAGG;;AAEH,yCAAoC;AACpC,0CAAsD;AACtD,wCAAoD;AACpD,kDAA8D;AAC9D,kDAA8D;AAC9D,8CAA0D;AAC1D,kDAA8D;AAC9D,4CAAwD;AACxD,kDAA6D;AAE7D,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,QAAQ,CAAC;SACd,WAAW,CAAC,oEAAoE,CAAC;SACjF,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,iBAAiB;IACjB,OAAO;SACJ,MAAM,CAAC,eAAe,EAAE,wBAAwB,CAAC,CAAC;IAErD,wBAAwB;IACxB,IAAA,0BAAmB,EAAC,OAAO,CAAC,CAAC;IAC7B,IAAA,wBAAkB,EAAC,OAAO,CAAC,CAAC;IAC5B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,4BAAoB,EAAC,OAAO,CAAC,CAAC;IAC9B,IAAA,iCAAsB,EAAC,OAAO,CAAC,CAAC;IAEhC,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";;AACA;;;GAGG;;AAEH,yCAAoC;AACpC,0CAAsD;AACtD,wCAAoD;AACpD,kDAA8D;AAC9D,kDAA8D;AAC9D,8CAA0D;AAC1D,kDAA8D;AAC9D,4CAAwD;AACxD,kDAA6D;AAC7D,oDAAgE;AAEhE,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,QAAQ,CAAC;SACd,WAAW,CAAC,oEAAoE,CAAC;SACjF,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,iBAAiB;IACjB,OAAO;SACJ,MAAM,CAAC,eAAe,EAAE,wBAAwB,CAAC,CAAC;IAErD,wBAAwB;IACxB,IAAA,0BAAmB,EAAC,OAAO,CAAC,CAAC;IAC7B,IAAA,wBAAkB,EAAC,OAAO,CAAC,CAAC;IAC5B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,4BAAoB,EAAC,OAAO,CAAC,CAAC;IAC9B,IAAA,iCAAsB,EAAC,OAAO,CAAC,CAAC;IAChC,IAAA,oCAAwB,EAAC,OAAO,CAAC,CAAC;IAElC,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Persist dashboard snapshots under qa/dashboard/history/ and prune to a fixed
3
+ * max count (oldest by mtime are deleted). Used by `sungen dashboard` to
4
+ * accumulate runs for Trends / Compare views.
5
+ */
6
+ import { DashboardSnapshot } from './types';
7
+ export declare const DEFAULT_MAX_HISTORY = 20;
8
+ export interface HistoryWriteResult {
9
+ written: string;
10
+ pruned: string[];
11
+ retained: string[];
12
+ }
13
+ export declare function historyDir(cwd: string): string;
14
+ /**
15
+ * Write `snapshot` as <runId>.json and prune older files until at most `max`
16
+ * files remain. Files are sorted by mtime so manual edits (rare) still work.
17
+ */
18
+ export declare function writeSnapshotToHistory(cwd: string, snapshot: DashboardSnapshot, max?: number): HistoryWriteResult;
19
+ /**
20
+ * Return absolute paths of every JSON file under history/, sorted newest → oldest.
21
+ */
22
+ export declare function listHistoryFiles(dir: string): string[];
23
+ /**
24
+ * Read every history JSON, oldest → newest. Skips files that fail to parse.
25
+ */
26
+ export declare function readHistory(cwd: string): DashboardSnapshot[];
27
+ //# sourceMappingURL=history-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history-store.d.ts","sourceRoot":"","sources":["../../src/dashboard/history-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE5C,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,iBAAiB,EAC3B,GAAG,GAAE,MAA4B,GAChC,kBAAkB,CAYpB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAMtD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAa5D"}
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ /**
3
+ * Persist dashboard snapshots under qa/dashboard/history/ and prune to a fixed
4
+ * max count (oldest by mtime are deleted). Used by `sungen dashboard` to
5
+ * accumulate runs for Trends / Compare views.
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.DEFAULT_MAX_HISTORY = void 0;
42
+ exports.historyDir = historyDir;
43
+ exports.writeSnapshotToHistory = writeSnapshotToHistory;
44
+ exports.listHistoryFiles = listHistoryFiles;
45
+ exports.readHistory = readHistory;
46
+ const fs = __importStar(require("fs"));
47
+ const path = __importStar(require("path"));
48
+ exports.DEFAULT_MAX_HISTORY = 20;
49
+ function historyDir(cwd) {
50
+ return path.join(cwd, 'qa', 'dashboard', 'history');
51
+ }
52
+ /**
53
+ * Write `snapshot` as <runId>.json and prune older files until at most `max`
54
+ * files remain. Files are sorted by mtime so manual edits (rare) still work.
55
+ */
56
+ function writeSnapshotToHistory(cwd, snapshot, max = exports.DEFAULT_MAX_HISTORY) {
57
+ const dir = historyDir(cwd);
58
+ fs.mkdirSync(dir, { recursive: true });
59
+ const filename = `${snapshot.runId}.json`;
60
+ const target = path.join(dir, filename);
61
+ fs.writeFileSync(target, JSON.stringify(snapshot, null, 2), 'utf-8');
62
+ const pruned = pruneHistory(dir, max);
63
+ const retained = listHistoryFiles(dir);
64
+ return { written: target, pruned, retained };
65
+ }
66
+ /**
67
+ * Return absolute paths of every JSON file under history/, sorted newest → oldest.
68
+ */
69
+ function listHistoryFiles(dir) {
70
+ if (!fs.existsSync(dir))
71
+ return [];
72
+ return fs.readdirSync(dir)
73
+ .filter((f) => f.endsWith('.json'))
74
+ .map((f) => path.join(dir, f))
75
+ .sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
76
+ }
77
+ /**
78
+ * Read every history JSON, oldest → newest. Skips files that fail to parse.
79
+ */
80
+ function readHistory(cwd) {
81
+ const dir = historyDir(cwd);
82
+ const files = listHistoryFiles(dir).reverse(); // oldest → newest
83
+ const out = [];
84
+ for (const f of files) {
85
+ try {
86
+ const parsed = JSON.parse(fs.readFileSync(f, 'utf-8'));
87
+ out.push(parsed);
88
+ }
89
+ catch {
90
+ // ignore malformed
91
+ }
92
+ }
93
+ return out;
94
+ }
95
+ /**
96
+ * Delete oldest files in `dir` until at most `max` remain.
97
+ * Returns the absolute paths removed.
98
+ */
99
+ function pruneHistory(dir, max) {
100
+ const files = listHistoryFiles(dir); // newest first
101
+ if (files.length <= max)
102
+ return [];
103
+ const toRemove = files.slice(max);
104
+ for (const f of toRemove) {
105
+ try {
106
+ fs.unlinkSync(f);
107
+ }
108
+ catch { /* ignore */ }
109
+ }
110
+ return toRemove;
111
+ }
112
+ //# sourceMappingURL=history-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history-store.js","sourceRoot":"","sources":["../../src/dashboard/history-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Inject a DashboardPayload into the pre-built single-file HTML template,
3
+ * then write the result to qa/dashboard/index.html.
4
+ *
5
+ * The template lives at src/dashboard/templates/index.html (built by
6
+ * dashboard/, then copied during `npm run build`). It must contain
7
+ * the placeholder line:
8
+ *
9
+ * <script id="__SUNGEN_DASHBOARD__" type="application/json">{}</script>
10
+ *
11
+ * The renderer replaces the JSON content with the payload — no other
12
+ * mutation. This keeps the template itself fully static, so opening it
13
+ * directly (without running the CLI) shows an empty-state dashboard.
14
+ */
15
+ import { DashboardPayload, DashboardSnapshot } from './types';
16
+ /**
17
+ * Locate the bundled template. Looks first in dist/ (production), then src/
18
+ * (when running via tsx in dev).
19
+ */
20
+ export declare function resolveTemplatePath(): string;
21
+ export declare function buildPayload(cwd: string, current: DashboardSnapshot): DashboardPayload;
22
+ export interface RenderResult {
23
+ outputPath: string;
24
+ bytes: number;
25
+ }
26
+ /**
27
+ * Render `payload` into the template and write to qa/dashboard/index.html.
28
+ */
29
+ export declare function renderDashboardHtml(cwd: string, payload: DashboardPayload): RenderResult;
30
+ //# sourceMappingURL=html-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-renderer.d.ts","sourceRoot":"","sources":["../../src/dashboard/html-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAM9D;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAe5C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAKtF;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,YAAY,CAaxF"}
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ /**
3
+ * Inject a DashboardPayload into the pre-built single-file HTML template,
4
+ * then write the result to qa/dashboard/index.html.
5
+ *
6
+ * The template lives at src/dashboard/templates/index.html (built by
7
+ * dashboard/, then copied during `npm run build`). It must contain
8
+ * the placeholder line:
9
+ *
10
+ * <script id="__SUNGEN_DASHBOARD__" type="application/json">{}</script>
11
+ *
12
+ * The renderer replaces the JSON content with the payload — no other
13
+ * mutation. This keeps the template itself fully static, so opening it
14
+ * directly (without running the CLI) shows an empty-state dashboard.
15
+ */
16
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ var desc = Object.getOwnPropertyDescriptor(m, k);
19
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
20
+ desc = { enumerable: true, get: function() { return m[k]; } };
21
+ }
22
+ Object.defineProperty(o, k2, desc);
23
+ }) : (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ o[k2] = m[k];
26
+ }));
27
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
28
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
29
+ }) : function(o, v) {
30
+ o["default"] = v;
31
+ });
32
+ var __importStar = (this && this.__importStar) || (function () {
33
+ var ownKeys = function(o) {
34
+ ownKeys = Object.getOwnPropertyNames || function (o) {
35
+ var ar = [];
36
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
37
+ return ar;
38
+ };
39
+ return ownKeys(o);
40
+ };
41
+ return function (mod) {
42
+ if (mod && mod.__esModule) return mod;
43
+ var result = {};
44
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
45
+ __setModuleDefault(result, mod);
46
+ return result;
47
+ };
48
+ })();
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.resolveTemplatePath = resolveTemplatePath;
51
+ exports.buildPayload = buildPayload;
52
+ exports.renderDashboardHtml = renderDashboardHtml;
53
+ const fs = __importStar(require("fs"));
54
+ const path = __importStar(require("path"));
55
+ const history_store_1 = require("./history-store");
56
+ const PAYLOAD_TAG_OPEN = '<script id="__SUNGEN_DASHBOARD__" type="application/json">';
57
+ const PAYLOAD_TAG_CLOSE = '</script>';
58
+ /**
59
+ * Locate the bundled template. Looks first in dist/ (production), then src/
60
+ * (when running via tsx in dev).
61
+ */
62
+ function resolveTemplatePath() {
63
+ const candidates = [
64
+ // Compiled package: dist/dashboard/templates/index.html (copy-templates)
65
+ path.join(__dirname, 'templates', 'index.html'),
66
+ // Dev: src/dashboard/templates/index.html
67
+ path.join(__dirname, '..', '..', 'src', 'dashboard', 'templates', 'index.html'),
68
+ ];
69
+ for (const c of candidates) {
70
+ if (fs.existsSync(c))
71
+ return c;
72
+ }
73
+ throw new Error('Dashboard HTML template not found. Build it via:\n' +
74
+ ' cd dashboard && npm install && npm run build\n' +
75
+ 'then copy dashboard/dist/index.html → src/dashboard/templates/index.html');
76
+ }
77
+ function buildPayload(cwd, current) {
78
+ const all = (0, history_store_1.readHistory)(cwd);
79
+ // Exclude `current` from history if it's been written there already.
80
+ const history = all.filter((s) => s.runId !== current.runId);
81
+ return { current, history };
82
+ }
83
+ /**
84
+ * Render `payload` into the template and write to qa/dashboard/index.html.
85
+ */
86
+ function renderDashboardHtml(cwd, payload) {
87
+ const templatePath = resolveTemplatePath();
88
+ const template = fs.readFileSync(templatePath, 'utf-8');
89
+ const json = JSON.stringify(payload);
90
+ const replaced = injectPayload(template, json);
91
+ const outDir = path.join(cwd, 'qa', 'dashboard');
92
+ fs.mkdirSync(outDir, { recursive: true });
93
+ const outputPath = path.join(outDir, 'index.html');
94
+ fs.writeFileSync(outputPath, replaced, 'utf-8');
95
+ return { outputPath, bytes: Buffer.byteLength(replaced, 'utf-8') };
96
+ }
97
+ function injectPayload(template, json) {
98
+ const start = template.indexOf(PAYLOAD_TAG_OPEN);
99
+ if (start === -1) {
100
+ throw new Error(`Template is missing the payload placeholder ${PAYLOAD_TAG_OPEN}…${PAYLOAD_TAG_CLOSE}`);
101
+ }
102
+ const contentStart = start + PAYLOAD_TAG_OPEN.length;
103
+ const contentEnd = template.indexOf(PAYLOAD_TAG_CLOSE, contentStart);
104
+ if (contentEnd === -1) {
105
+ throw new Error('Template payload placeholder is unterminated');
106
+ }
107
+ // Escape </script> inside JSON to prevent breaking the surrounding script tag.
108
+ const safe = json.replace(/<\/script>/gi, '<\\/script>');
109
+ return template.slice(0, contentStart) + safe + template.slice(contentEnd);
110
+ }
111
+ //# sourceMappingURL=html-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-renderer.js","sourceRoot":"","sources":["../../src/dashboard/html-renderer.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcH,kDAeC;AAED,oCAKC;AAUD,kDAaC;AAzDD,uCAAyB;AACzB,2CAA6B;AAE7B,mDAA8C;AAE9C,MAAM,gBAAgB,GAAG,4DAA4D,CAAC;AACtF,MAAM,iBAAiB,GAAG,WAAW,CAAC;AAEtC;;;GAGG;AACH,SAAgB,mBAAmB;IACjC,MAAM,UAAU,GAAG;QACjB,yEAAyE;QACzE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,YAAY,CAAC;QAC/C,0CAA0C;QAC1C,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,CAAC;KAChF,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC;IACjC,CAAC;IACD,MAAM,IAAI,KAAK,CACb,oDAAoD;QACpD,oDAAoD;QACpD,0EAA0E,CAC3E,CAAC;AACJ,CAAC;AAED,SAAgB,YAAY,CAAC,GAAW,EAAE,OAA0B;IAClE,MAAM,GAAG,GAAG,IAAA,2BAAW,EAAC,GAAG,CAAC,CAAC;IAC7B,qEAAqE;IACrE,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;IAC7D,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC;AAOD;;GAEG;AACH,SAAgB,mBAAmB,CAAC,GAAW,EAAE,OAAyB;IACxE,MAAM,YAAY,GAAG,mBAAmB,EAAE,CAAC;IAC3C,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAExD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAE/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IACjD,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACnD,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAEhD,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;AACrE,CAAC;AAED,SAAS,aAAa,CAAC,QAAgB,EAAE,IAAY;IACnD,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACjD,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,+CAA+C,gBAAgB,IAAI,iBAAiB,EAAE,CACvF,CAAC;IACJ,CAAC;IACD,MAAM,YAAY,GAAG,KAAK,GAAG,gBAAgB,CAAC,MAAM,CAAC;IACrD,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC;IACrE,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IACD,+EAA+E;IAC/E,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;IACzD,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,GAAG,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AAC7E,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Orchestrate dashboard snapshot construction across all screens/flows.
3
+ *
4
+ * Inputs are read from the same on-disk sources used by `sungen delivery`:
5
+ * qa/screens/<name>/... feature, test-data, selectors, requirements
6
+ * qa/flows/<name>/...
7
+ * specs/generated/<...>/... compiled .spec.ts + per-target test-result.json
8
+ * test-results/results.json global fallback
9
+ *
10
+ * Output is a DashboardSnapshot ready to be embedded in HTML or written as
11
+ * a history entry under qa/dashboard/history/<runId>.json.
12
+ */
13
+ import { EnvironmentInfo } from '../exporters/types';
14
+ import { DashboardSnapshot, ScenarioSnapshot } from './types';
15
+ export interface DashboardTarget {
16
+ name: string;
17
+ isFlow: boolean;
18
+ }
19
+ export interface BuildSnapshotOptions {
20
+ cwd: string;
21
+ targets: DashboardTarget[];
22
+ env: EnvironmentInfo;
23
+ /** Skip targets where the .feature/.spec.ts pair fails to parse. */
24
+ continueOnError?: boolean;
25
+ }
26
+ export declare function buildDashboardSnapshot(options: BuildSnapshotOptions): DashboardSnapshot;
27
+ export declare function listDashboardTargets(cwd: string): DashboardTarget[];
28
+ export declare function resolveTargetType(cwd: string, name: string): DashboardTarget;
29
+ export type { ScenarioSnapshot };
30
+ //# sourceMappingURL=snapshot-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-builder.d.ts","sourceRoot":"","sources":["../../src/dashboard/snapshot-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAYH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAEL,iBAAiB,EACjB,gBAAgB,EAGjB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,GAAG,EAAE,eAAe,CAAC;IACrB,oEAAoE;IACpE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,iBAAiB,CAwBvF;AAkHD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE,CAkBnE;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,CAK5E;AAgED,YAAY,EAAE,gBAAgB,EAAE,CAAC"}