@facet-coverage/core 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -574
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +224 -25
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/serve.d.ts +12 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +76 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/index.js +10 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.js +60 -0
- package/dist/core/FacetParser.d.ts +37 -1
- package/dist/core/FacetParser.d.ts.map +1 -1
- package/dist/core/FacetParser.js +123 -5
- package/dist/core/FacetParser.js.map +1 -1
- package/dist/core/TestScanner.d.ts +4 -1
- package/dist/core/TestScanner.d.ts.map +1 -1
- package/dist/core/TestScanner.js +42 -13
- package/dist/core/TestScanner.js.map +1 -1
- package/dist/core/Validator.d.ts +4 -0
- package/dist/core/Validator.d.ts.map +1 -1
- package/dist/core/Validator.js +22 -4
- package/dist/core/Validator.js.map +1 -1
- package/dist/server/DevServer.d.ts +48 -0
- package/dist/server/DevServer.d.ts.map +1 -0
- package/dist/server/DevServer.js +126 -0
- package/dist/server/DevServer.js.map +1 -0
- package/dist/server/HotReloadManager.d.ts +49 -0
- package/dist/server/HotReloadManager.d.ts.map +1 -0
- package/dist/server/HotReloadManager.js +187 -0
- package/dist/server/HotReloadManager.js.map +1 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +6 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/routes.d.ts +12 -0
- package/dist/server/routes.d.ts.map +1 -0
- package/dist/server/routes.js +272 -0
- package/dist/server/routes.js.map +1 -0
- package/dist/server/views/Dashboard.d.ts +6 -0
- package/dist/server/views/Dashboard.d.ts.map +1 -0
- package/dist/server/views/Dashboard.js +121 -0
- package/dist/server/views/Dashboard.js.map +1 -0
- package/dist/server/views/FacetPage.d.ts +6 -0
- package/dist/server/views/FacetPage.d.ts.map +1 -0
- package/dist/server/views/FacetPage.js +190 -0
- package/dist/server/views/FacetPage.js.map +1 -0
- package/dist/server/views/FeaturePage.d.ts +6 -0
- package/dist/server/views/FeaturePage.d.ts.map +1 -0
- package/dist/server/views/FeaturePage.js +98 -0
- package/dist/server/views/FeaturePage.js.map +1 -0
- package/dist/server/views/Layout.d.ts +17 -0
- package/dist/server/views/Layout.d.ts.map +1 -0
- package/dist/server/views/Layout.js +59 -0
- package/dist/server/views/Layout.js.map +1 -0
- package/dist/server/views/Sidebar.d.ts +6 -0
- package/dist/server/views/Sidebar.d.ts.map +1 -0
- package/dist/server/views/Sidebar.js +59 -0
- package/dist/server/views/Sidebar.js.map +1 -0
- package/dist/server/views/TestPanel.d.ts +6 -0
- package/dist/server/views/TestPanel.d.ts.map +1 -0
- package/dist/server/views/TestPanel.js +69 -0
- package/dist/server/views/TestPanel.js.map +1 -0
- package/dist/server/views/scripts.d.ts +5 -0
- package/dist/server/views/scripts.d.ts.map +1 -0
- package/dist/server/views/scripts.js +203 -0
- package/dist/server/views/scripts.js.map +1 -0
- package/dist/server/views/styles.d.ts +6 -0
- package/dist/server/views/styles.d.ts.map +1 -0
- package/dist/server/views/styles.js +608 -0
- package/dist/server/views/styles.js.map +1 -0
- package/dist/types.d.ts +27 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +9 -4
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderFeaturePage = renderFeaturePage;
|
|
4
|
+
const Layout_js_1 = require("./Layout.js");
|
|
5
|
+
const Sidebar_js_1 = require("./Sidebar.js");
|
|
6
|
+
/**
|
|
7
|
+
* Render a feature detail page
|
|
8
|
+
*/
|
|
9
|
+
function renderFeaturePage(feature, report) {
|
|
10
|
+
const coverageClass = (0, Layout_js_1.getCoverageClass)(feature.percentage);
|
|
11
|
+
// Group facets by type
|
|
12
|
+
const facetsByType = {};
|
|
13
|
+
for (const fc of feature.facets) {
|
|
14
|
+
const type = fc.facet.type;
|
|
15
|
+
if (!facetsByType[type]) {
|
|
16
|
+
facetsByType[type] = [];
|
|
17
|
+
}
|
|
18
|
+
facetsByType[type].push(fc);
|
|
19
|
+
}
|
|
20
|
+
// Type sections
|
|
21
|
+
const typeSectionsHtml = Object.entries(facetsByType)
|
|
22
|
+
.map(([type, facets]) => {
|
|
23
|
+
const covered = facets.filter(fc => fc.covered).length;
|
|
24
|
+
const total = facets.length;
|
|
25
|
+
const typePercentage = Math.round((covered / total) * 100);
|
|
26
|
+
const typeClass = (0, Layout_js_1.getCoverageClass)(typePercentage);
|
|
27
|
+
const facetsHtml = facets
|
|
28
|
+
.map(fc => {
|
|
29
|
+
const statusClass = fc.covered ? 'covered' : 'uncovered';
|
|
30
|
+
const testsCount = fc.coveredBy.length;
|
|
31
|
+
return `
|
|
32
|
+
<a href="/facet/${encodeURIComponent(fc.facet.id)}" class="facet-item ${statusClass}">
|
|
33
|
+
<div class="facet-header">
|
|
34
|
+
<span class="facet-id">${fc.covered ? '✓' : '✗'} ${(0, Layout_js_1.escapeHtml)(fc.facet.id)}</span>
|
|
35
|
+
${testsCount > 0 ? `<span class="badge success">${testsCount} test${testsCount !== 1 ? 's' : ''}</span>` : ''}
|
|
36
|
+
</div>
|
|
37
|
+
<div class="facet-source">${(0, Layout_js_1.escapeHtml)(fc.facet.source.file)}#${(0, Layout_js_1.escapeHtml)(fc.facet.source.section)}</div>
|
|
38
|
+
</a>
|
|
39
|
+
`;
|
|
40
|
+
})
|
|
41
|
+
.join('');
|
|
42
|
+
return `
|
|
43
|
+
<div class="card">
|
|
44
|
+
<h2 style="display: flex; justify-content: space-between; align-items: center;">
|
|
45
|
+
<span style="text-transform: capitalize;">${(0, Layout_js_1.escapeHtml)(type)}</span>
|
|
46
|
+
<span class="badge ${typeClass}">${typePercentage}% (${covered}/${total})</span>
|
|
47
|
+
</h2>
|
|
48
|
+
<div class="facet-list">
|
|
49
|
+
${facetsHtml}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
`;
|
|
53
|
+
})
|
|
54
|
+
.join('');
|
|
55
|
+
return `
|
|
56
|
+
<div class="app">
|
|
57
|
+
${(0, Sidebar_js_1.renderSidebar)(report, feature.feature)}
|
|
58
|
+
|
|
59
|
+
<main class="main">
|
|
60
|
+
<div class="doc-panel">
|
|
61
|
+
<div class="breadcrumb">
|
|
62
|
+
<a href="/">Dashboard</a>
|
|
63
|
+
<span class="breadcrumb-separator">/</span>
|
|
64
|
+
<span>${(0, Layout_js_1.escapeHtml)(feature.feature)}</span>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div class="card">
|
|
68
|
+
<h2 style="display: flex; justify-content: space-between; align-items: center;">
|
|
69
|
+
<span>${(0, Layout_js_1.escapeHtml)(feature.feature)}</span>
|
|
70
|
+
<span class="badge ${coverageClass}">${feature.percentage}%</span>
|
|
71
|
+
</h2>
|
|
72
|
+
<div class="stats-grid" style="margin-top: 1rem;">
|
|
73
|
+
<div class="stat">
|
|
74
|
+
<div class="stat-value ${coverageClass}">${feature.percentage}%</div>
|
|
75
|
+
<div class="stat-label">Coverage</div>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="stat">
|
|
78
|
+
<div class="stat-value">${feature.totalFacets}</div>
|
|
79
|
+
<div class="stat-label">Total Facets</div>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="stat">
|
|
82
|
+
<div class="stat-value success">${feature.coveredFacets}</div>
|
|
83
|
+
<div class="stat-label">Covered</div>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="stat">
|
|
86
|
+
<div class="stat-value error">${feature.totalFacets - feature.coveredFacets}</div>
|
|
87
|
+
<div class="stat-label">Uncovered</div>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
${typeSectionsHtml}
|
|
93
|
+
</div>
|
|
94
|
+
</main>
|
|
95
|
+
</div>
|
|
96
|
+
`;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=FeaturePage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FeaturePage.js","sourceRoot":"","sources":["../../../src/server/views/FeaturePage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface LayoutOptions {
|
|
2
|
+
title?: string;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Render the main HTML layout shell
|
|
6
|
+
*/
|
|
7
|
+
export declare function renderLayout(content: string, options?: LayoutOptions): string;
|
|
8
|
+
/**
|
|
9
|
+
* Escape HTML special characters
|
|
10
|
+
*/
|
|
11
|
+
export declare function escapeHtml(text: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Get CSS class for coverage percentage
|
|
14
|
+
*/
|
|
15
|
+
export declare function getCoverageClass(percentage: number): string;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=Layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Layout.d.ts","sourceRoot":"","sources":["../../../src/server/views/Layout.ts"],"names":[],"mappings":"AAGA,UAAU,aAAa;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CA2BjF;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO/C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAI3D"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderLayout = renderLayout;
|
|
4
|
+
exports.escapeHtml = escapeHtml;
|
|
5
|
+
exports.getCoverageClass = getCoverageClass;
|
|
6
|
+
const styles_js_1 = require("./styles.js");
|
|
7
|
+
const scripts_js_1 = require("./scripts.js");
|
|
8
|
+
/**
|
|
9
|
+
* Render the main HTML layout shell
|
|
10
|
+
*/
|
|
11
|
+
function renderLayout(content, options = {}) {
|
|
12
|
+
const title = options.title || 'Facet Documentation';
|
|
13
|
+
return `<!DOCTYPE html>
|
|
14
|
+
<html lang="en">
|
|
15
|
+
<head>
|
|
16
|
+
<meta charset="UTF-8">
|
|
17
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
18
|
+
<title>${escapeHtml(title)}</title>
|
|
19
|
+
<style>
|
|
20
|
+
${(0, styles_js_1.getStyles)()}
|
|
21
|
+
</style>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
${content}
|
|
25
|
+
|
|
26
|
+
<!-- Status indicator for hot reload -->
|
|
27
|
+
<div id="status-indicator" class="status-indicator">
|
|
28
|
+
<div class="spinner"></div>
|
|
29
|
+
<span id="status-text">Connecting...</span>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<script>
|
|
33
|
+
${(0, scripts_js_1.getScripts)()}
|
|
34
|
+
</script>
|
|
35
|
+
</body>
|
|
36
|
+
</html>`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Escape HTML special characters
|
|
40
|
+
*/
|
|
41
|
+
function escapeHtml(text) {
|
|
42
|
+
return text
|
|
43
|
+
.replace(/&/g, '&')
|
|
44
|
+
.replace(/</g, '<')
|
|
45
|
+
.replace(/>/g, '>')
|
|
46
|
+
.replace(/"/g, '"')
|
|
47
|
+
.replace(/'/g, ''');
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get CSS class for coverage percentage
|
|
51
|
+
*/
|
|
52
|
+
function getCoverageClass(percentage) {
|
|
53
|
+
if (percentage >= 80)
|
|
54
|
+
return 'success';
|
|
55
|
+
if (percentage >= 50)
|
|
56
|
+
return 'warning';
|
|
57
|
+
return 'error';
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=Layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Layout.js","sourceRoot":"","sources":["../../../src/server/views/Layout.ts"],"names":[],"mappings":";;AAUA,oCA2BC;AAKD,gCAOC;AAKD,4CAIC;AA1DD,2CAAwC;AACxC,6CAA0C;AAM1C;;GAEG;AACH,SAAgB,YAAY,CAAC,OAAe,EAAE,UAAyB,EAAE;IACvE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,qBAAqB,CAAC;IAErD,OAAO;;;;;WAKE,UAAU,CAAC,KAAK,CAAC;;MAEtB,IAAA,qBAAS,GAAE;;;;IAIb,OAAO;;;;;;;;;MASL,IAAA,uBAAU,GAAE;;;QAGV,CAAC;AACT,CAAC;AAED;;GAEG;AACH,SAAgB,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI;SACR,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,SAAgB,gBAAgB,CAAC,UAAkB;IACjD,IAAI,UAAU,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC;IACvC,IAAI,UAAU,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC;IACvC,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../../src/server/views/Sidebar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAqD1G"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderSidebar = renderSidebar;
|
|
4
|
+
const Layout_js_1 = require("./Layout.js");
|
|
5
|
+
/**
|
|
6
|
+
* Render the sidebar navigation
|
|
7
|
+
*/
|
|
8
|
+
function renderSidebar(report, activeFeature, activeFacet) {
|
|
9
|
+
const featuresHtml = report.features
|
|
10
|
+
.map(feature => {
|
|
11
|
+
const coverageClass = (0, Layout_js_1.getCoverageClass)(feature.percentage);
|
|
12
|
+
const isActive = feature.feature === activeFeature;
|
|
13
|
+
const facetsHtml = feature.facets
|
|
14
|
+
.map(fc => {
|
|
15
|
+
const facetActive = fc.facet.id === activeFacet;
|
|
16
|
+
const coverStatus = fc.covered ? 'covered' : 'uncovered';
|
|
17
|
+
return `
|
|
18
|
+
<a href="/facet/${encodeURIComponent(fc.facet.id)}"
|
|
19
|
+
class="nav-item ${coverStatus}${facetActive ? ' active' : ''}"
|
|
20
|
+
title="${(0, Layout_js_1.escapeHtml)(fc.facet.id)}">
|
|
21
|
+
${(0, Layout_js_1.escapeHtml)(fc.facet.id.split(':').pop() || fc.facet.id)}
|
|
22
|
+
</a>
|
|
23
|
+
`;
|
|
24
|
+
})
|
|
25
|
+
.join('');
|
|
26
|
+
return `
|
|
27
|
+
<div class="nav-section">
|
|
28
|
+
<a href="/feature/${encodeURIComponent(feature.feature)}" class="nav-section-header">
|
|
29
|
+
<span class="nav-section-title">${(0, Layout_js_1.escapeHtml)(feature.feature)}</span>
|
|
30
|
+
<span class="nav-section-badge ${coverageClass}">${feature.percentage}%</span>
|
|
31
|
+
</a>
|
|
32
|
+
<div class="nav-items">
|
|
33
|
+
${facetsHtml}
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
`;
|
|
37
|
+
})
|
|
38
|
+
.join('');
|
|
39
|
+
return `
|
|
40
|
+
<aside class="sidebar">
|
|
41
|
+
<div class="sidebar-header">
|
|
42
|
+
<h1>Facet Docs</h1>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="sidebar-search">
|
|
45
|
+
<input type="text" id="sidebar-search" placeholder="Search facets... (Ctrl+K)" />
|
|
46
|
+
</div>
|
|
47
|
+
<nav class="sidebar-nav">
|
|
48
|
+
<div class="nav-section">
|
|
49
|
+
<a href="/" class="nav-section-header">
|
|
50
|
+
<span class="nav-section-title">Dashboard</span>
|
|
51
|
+
<span class="nav-section-badge ${(0, Layout_js_1.getCoverageClass)(report.summary.percentage)}">${report.summary.percentage}%</span>
|
|
52
|
+
</a>
|
|
53
|
+
</div>
|
|
54
|
+
${featuresHtml}
|
|
55
|
+
</nav>
|
|
56
|
+
</aside>
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=Sidebar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Sidebar.js","sourceRoot":"","sources":["../../../src/server/views/Sidebar.ts"],"names":[],"mappings":";;AAMA,sCAqDC;AA1DD,2CAA2D;AAE3D;;GAEG;AACH,SAAgB,aAAa,CAAC,MAAsB,EAAE,aAAsB,EAAE,WAAoB;IAChG,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ;SACjC,GAAG,CAAC,OAAO,CAAC,EAAE;QACb,MAAM,aAAa,GAAG,IAAA,4BAAgB,EAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC3D,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,KAAK,aAAa,CAAC;QAEnD,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM;aAC9B,GAAG,CAAC,EAAE,CAAC,EAAE;YACR,MAAM,WAAW,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,WAAW,CAAC;YAChD,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC;YACzD,OAAO;8BACa,kBAAkB,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;iCAC5B,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;wBACnD,IAAA,sBAAU,EAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC/B,IAAA,sBAAU,EAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;;WAE5D,CAAC;QACJ,CAAC,CAAC;aACD,IAAI,CAAC,EAAE,CAAC,CAAC;QAEZ,OAAO;;8BAEiB,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC;8CACnB,IAAA,sBAAU,EAAC,OAAO,CAAC,OAAO,CAAC;6CAC5B,aAAa,KAAK,OAAO,CAAC,UAAU;;;cAGnE,UAAU;;;OAGjB,CAAC;IACJ,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO;;;;;;;;;;;;6CAYoC,IAAA,4BAAgB,EAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,MAAM,CAAC,OAAO,CAAC,UAAU;;;UAG5G,YAAY;;;GAGnB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestPanel.d.ts","sourceRoot":"","sources":["../../../src/server/views/TestPanel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG/C;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CA4CzD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderTestPanel = renderTestPanel;
|
|
4
|
+
const Layout_js_1 = require("./Layout.js");
|
|
5
|
+
/**
|
|
6
|
+
* Render the test panel showing tests that cover a facet
|
|
7
|
+
*/
|
|
8
|
+
function renderTestPanel(tests) {
|
|
9
|
+
if (tests.length === 0) {
|
|
10
|
+
return `
|
|
11
|
+
<aside class="test-panel">
|
|
12
|
+
<div class="test-panel-header">Tests (0)</div>
|
|
13
|
+
<div class="test-panel-content">
|
|
14
|
+
<div class="empty-state">
|
|
15
|
+
<div class="empty-state-icon">🔍</div>
|
|
16
|
+
<p>No tests cover this facet</p>
|
|
17
|
+
<p style="margin-top: 0.5rem; font-size: 0.8125rem;">
|
|
18
|
+
Add a <code>facet()</code> call in your tests to link them.
|
|
19
|
+
</p>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</aside>
|
|
23
|
+
`;
|
|
24
|
+
}
|
|
25
|
+
const testsHtml = tests
|
|
26
|
+
.map((test, index) => `
|
|
27
|
+
<div class="test-item${index === 0 ? ' active' : ''}"
|
|
28
|
+
data-file="${(0, Layout_js_1.escapeHtml)(test.file)}"
|
|
29
|
+
data-line="${test.line || ''}">
|
|
30
|
+
<div class="test-file">${(0, Layout_js_1.escapeHtml)(test.file)}</div>
|
|
31
|
+
<div class="test-title">${(0, Layout_js_1.escapeHtml)(test.title)}</div>
|
|
32
|
+
${test.line ? `<div class="test-line">Line ${test.line}</div>` : ''}
|
|
33
|
+
</div>
|
|
34
|
+
`)
|
|
35
|
+
.join('');
|
|
36
|
+
return `
|
|
37
|
+
<aside class="test-panel">
|
|
38
|
+
<div class="test-panel-header">Tests (${tests.length})</div>
|
|
39
|
+
<div class="test-panel-content">
|
|
40
|
+
<div class="test-list">
|
|
41
|
+
${testsHtml}
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div id="test-code" style="margin-top: 1rem;">
|
|
45
|
+
${tests.length > 0 ? renderInitialTestCode(tests[0]) : ''}
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</aside>
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Render placeholder for initial test code (will be loaded via JS)
|
|
53
|
+
*/
|
|
54
|
+
function renderInitialTestCode(test) {
|
|
55
|
+
return `
|
|
56
|
+
<div class="code-block">
|
|
57
|
+
<div class="code-header">
|
|
58
|
+
<span>${(0, Layout_js_1.escapeHtml)(test.file)}</span>
|
|
59
|
+
<button class="copy-btn" onclick="copyToClipboard('bun test ${(0, Layout_js_1.escapeHtml)(test.file)}')">Copy run command</button>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="code-content">
|
|
62
|
+
<div class="empty-state" style="padding: 1rem;">
|
|
63
|
+
Click a test to view source code
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
`;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=TestPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestPanel.js","sourceRoot":"","sources":["../../../src/server/views/TestPanel.ts"],"names":[],"mappings":";;AAMA,0CA4CC;AAjDD,2CAAyC;AAEzC;;GAEG;AACH,SAAgB,eAAe,CAAC,KAAiB;IAC/C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;;;;;;;;;;;;;KAaN,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,KAAK;SACpB,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;6BACG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;wBACjC,IAAA,sBAAU,EAAC,IAAI,CAAC,IAAI,CAAC;wBACrB,IAAI,CAAC,IAAI,IAAI,EAAE;iCACN,IAAA,sBAAU,EAAC,IAAI,CAAC,IAAI,CAAC;kCACpB,IAAA,sBAAU,EAAC,IAAI,CAAC,KAAK,CAAC;UAC9C,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,+BAA+B,IAAI,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE;;KAEtE,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO;;8CAEqC,KAAK,CAAC,MAAM;;;YAG9C,SAAS;;;;YAIT,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;;;;GAIhE,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAAC,IAAc;IAC3C,OAAO;;;gBAGO,IAAA,sBAAU,EAAC,IAAI,CAAC,IAAI,CAAC;sEACiC,IAAA,sBAAU,EAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;GAQxF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scripts.d.ts","sourceRoot":"","sources":["../../../src/server/views/scripts.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAmMnC"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getScripts = getScripts;
|
|
4
|
+
/**
|
|
5
|
+
* Client-side JavaScript for hot reload and interactivity
|
|
6
|
+
*/
|
|
7
|
+
function getScripts() {
|
|
8
|
+
return `
|
|
9
|
+
(function() {
|
|
10
|
+
// WebSocket connection for hot reload
|
|
11
|
+
let ws = null;
|
|
12
|
+
let reconnectAttempts = 0;
|
|
13
|
+
const maxReconnectAttempts = 10;
|
|
14
|
+
const reconnectDelay = 1000;
|
|
15
|
+
|
|
16
|
+
const statusIndicator = document.getElementById('status-indicator');
|
|
17
|
+
const statusText = document.getElementById('status-text');
|
|
18
|
+
|
|
19
|
+
function showStatus(message, type) {
|
|
20
|
+
if (!statusIndicator) return;
|
|
21
|
+
statusIndicator.className = 'status-indicator visible ' + type;
|
|
22
|
+
if (statusText) statusText.textContent = message;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function hideStatus() {
|
|
26
|
+
if (statusIndicator) statusIndicator.className = 'status-indicator';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function connect() {
|
|
30
|
+
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
31
|
+
ws = new WebSocket(protocol + '//' + window.location.host + '/ws');
|
|
32
|
+
|
|
33
|
+
ws.onopen = function() {
|
|
34
|
+
console.log('[Facet] WebSocket connected');
|
|
35
|
+
reconnectAttempts = 0;
|
|
36
|
+
showStatus('Connected', 'connected');
|
|
37
|
+
setTimeout(hideStatus, 2000);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
ws.onclose = function() {
|
|
41
|
+
console.log('[Facet] WebSocket disconnected');
|
|
42
|
+
ws = null;
|
|
43
|
+
|
|
44
|
+
if (reconnectAttempts < maxReconnectAttempts) {
|
|
45
|
+
reconnectAttempts++;
|
|
46
|
+
const delay = reconnectDelay * Math.min(reconnectAttempts, 5);
|
|
47
|
+
console.log('[Facet] Reconnecting in ' + delay + 'ms...');
|
|
48
|
+
setTimeout(connect, delay);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
ws.onerror = function(error) {
|
|
53
|
+
console.error('[Facet] WebSocket error:', error);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
ws.onmessage = function(event) {
|
|
57
|
+
try {
|
|
58
|
+
const message = JSON.parse(event.data);
|
|
59
|
+
handleMessage(message);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
console.error('[Facet] Failed to parse message:', e);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function handleMessage(message) {
|
|
67
|
+
switch (message.type) {
|
|
68
|
+
case 'connected':
|
|
69
|
+
console.log('[Facet] Server acknowledged connection');
|
|
70
|
+
break;
|
|
71
|
+
|
|
72
|
+
case 'file-change':
|
|
73
|
+
showStatus('File changed: ' + message.data.path, 'updating');
|
|
74
|
+
break;
|
|
75
|
+
|
|
76
|
+
case 'coverage-update':
|
|
77
|
+
console.log('[Facet] Coverage updated:', message.data.summary);
|
|
78
|
+
showStatus('Refreshing...', 'updating');
|
|
79
|
+
// Reload the page to show new coverage
|
|
80
|
+
setTimeout(function() {
|
|
81
|
+
window.location.reload();
|
|
82
|
+
}, 300);
|
|
83
|
+
break;
|
|
84
|
+
|
|
85
|
+
case 'error':
|
|
86
|
+
console.error('[Facet] Server error:', message.data.message);
|
|
87
|
+
showStatus('Error: ' + message.data.message, 'error');
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Start WebSocket connection
|
|
93
|
+
connect();
|
|
94
|
+
|
|
95
|
+
// Search functionality
|
|
96
|
+
const searchInput = document.getElementById('sidebar-search');
|
|
97
|
+
if (searchInput) {
|
|
98
|
+
searchInput.addEventListener('input', function(e) {
|
|
99
|
+
const query = e.target.value.toLowerCase();
|
|
100
|
+
const items = document.querySelectorAll('.nav-item');
|
|
101
|
+
|
|
102
|
+
items.forEach(function(item) {
|
|
103
|
+
const text = item.textContent.toLowerCase();
|
|
104
|
+
const section = item.closest('.nav-section');
|
|
105
|
+
|
|
106
|
+
if (query === '' || text.includes(query)) {
|
|
107
|
+
item.style.display = '';
|
|
108
|
+
} else {
|
|
109
|
+
item.style.display = 'none';
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Show/hide sections based on visible items
|
|
114
|
+
document.querySelectorAll('.nav-section').forEach(function(section) {
|
|
115
|
+
const visibleItems = section.querySelectorAll('.nav-item:not([style*="display: none"])');
|
|
116
|
+
section.style.display = visibleItems.length > 0 || query === '' ? '' : 'none';
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Test panel - load test source on click
|
|
122
|
+
document.querySelectorAll('.test-item').forEach(function(item) {
|
|
123
|
+
item.addEventListener('click', function() {
|
|
124
|
+
const file = item.dataset.file;
|
|
125
|
+
const line = item.dataset.line;
|
|
126
|
+
|
|
127
|
+
if (!file) return;
|
|
128
|
+
|
|
129
|
+
// Mark as active
|
|
130
|
+
document.querySelectorAll('.test-item').forEach(function(i) {
|
|
131
|
+
i.classList.remove('active');
|
|
132
|
+
});
|
|
133
|
+
item.classList.add('active');
|
|
134
|
+
|
|
135
|
+
// Load test source
|
|
136
|
+
loadTestSource(file, line);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
async function loadTestSource(file, line) {
|
|
141
|
+
const codeContainer = document.getElementById('test-code');
|
|
142
|
+
if (!codeContainer) return;
|
|
143
|
+
|
|
144
|
+
codeContainer.innerHTML = '<div class="empty-state">Loading...</div>';
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
const response = await fetch('/api/test-source?file=' + encodeURIComponent(file) + (line ? '&line=' + line : ''));
|
|
148
|
+
const data = await response.json();
|
|
149
|
+
|
|
150
|
+
if (data.error) {
|
|
151
|
+
codeContainer.innerHTML = '<div class="empty-state">' + data.error + '</div>';
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const lines = data.content.split('\\n');
|
|
156
|
+
let html = '<div class="code-block">';
|
|
157
|
+
html += '<div class="code-header"><span>' + data.file + '</span>';
|
|
158
|
+
html += '<button class="copy-btn" onclick="copyToClipboard(\\'bun test ' + data.file + '\\')">Copy run command</button>';
|
|
159
|
+
html += '</div>';
|
|
160
|
+
html += '<div class="code-content">';
|
|
161
|
+
|
|
162
|
+
lines.forEach(function(lineContent, index) {
|
|
163
|
+
const lineNum = data.startLine + index;
|
|
164
|
+
const isHighlight = lineNum === data.highlightLine;
|
|
165
|
+
html += '<div class="code-line' + (isHighlight ? ' highlight' : '') + '">';
|
|
166
|
+
html += '<span class="code-line-number">' + lineNum + '</span>';
|
|
167
|
+
html += '<span class="code-line-content">' + escapeHtml(lineContent) + '</span>';
|
|
168
|
+
html += '</div>';
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
html += '</div></div>';
|
|
172
|
+
codeContainer.innerHTML = html;
|
|
173
|
+
} catch (e) {
|
|
174
|
+
codeContainer.innerHTML = '<div class="empty-state">Failed to load source</div>';
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function escapeHtml(text) {
|
|
179
|
+
const div = document.createElement('div');
|
|
180
|
+
div.textContent = text;
|
|
181
|
+
return div.innerHTML;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Copy to clipboard helper
|
|
185
|
+
window.copyToClipboard = function(text) {
|
|
186
|
+
navigator.clipboard.writeText(text).then(function() {
|
|
187
|
+
// Could show a toast here
|
|
188
|
+
console.log('[Facet] Copied to clipboard');
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Keyboard shortcuts
|
|
193
|
+
document.addEventListener('keydown', function(e) {
|
|
194
|
+
// Ctrl/Cmd + K to focus search
|
|
195
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
if (searchInput) searchInput.focus();
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
})();
|
|
201
|
+
`;
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=scripts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scripts.js","sourceRoot":"","sources":["../../../src/server/views/scripts.ts"],"names":[],"mappings":";;AAGA,gCAmMC;AAtMD;;GAEG;AACH,SAAgB,UAAU;IACxB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiMN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/server/views/styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAulBlC"}
|