mcprigor 1.3.1 → 1.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 +19 -2
- package/dist/audit.d.ts +44 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +157 -0
- package/dist/audit.js.map +1 -0
- package/dist/cli.js +283 -10
- package/dist/cli.js.map +1 -1
- package/dist/composition.d.ts +42 -0
- package/dist/composition.d.ts.map +1 -0
- package/dist/composition.js +130 -0
- package/dist/composition.js.map +1 -0
- package/dist/coverage.d.ts +33 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/coverage.js +83 -0
- package/dist/coverage.js.map +1 -0
- package/dist/export.d.ts +20 -0
- package/dist/export.d.ts.map +1 -0
- package/dist/export.js +422 -0
- package/dist/export.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/language.js +1 -1
- package/dist/language.js.map +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +4 -1
- package/dist/loader.js.map +1 -1
- package/dist/monitor.d.ts +22 -0
- package/dist/monitor.d.ts.map +1 -0
- package/dist/monitor.js +71 -0
- package/dist/monitor.js.map +1 -0
- package/dist/perf.d.ts +37 -0
- package/dist/perf.d.ts.map +1 -0
- package/dist/perf.js +90 -0
- package/dist/perf.js.map +1 -0
- package/dist/publish.d.ts +19 -0
- package/dist/publish.d.ts.map +1 -0
- package/dist/publish.js +58 -0
- package/dist/publish.js.map +1 -0
- package/dist/qa-language.d.ts.map +1 -1
- package/dist/qa-language.js +46 -1
- package/dist/qa-language.js.map +1 -1
- package/dist/reporters.d.ts +5 -2
- package/dist/reporters.d.ts.map +1 -1
- package/dist/reporters.js +26 -5
- package/dist/reporters.js.map +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +31 -15
- package/dist/runner.js.map +1 -1
- package/dist/schema.d.ts +47 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +10 -1
- package/dist/schema.js.map +1 -1
- package/dist/timeline.d.ts +18 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +59 -0
- package/dist/timeline.js.map +1 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +73 -3
- package/dist/workspace.js.map +1 -1
- package/docs/CLI-REFERENCE.md +136 -1
- package/docs/COVERAGE.md +37 -0
- package/docs/ENGINEER-SETUP.md +1 -1
- package/docs/EVIDENCE.md +11 -0
- package/docs/GITHUB-ACTION.md +82 -0
- package/docs/LANGUAGE-SPEC.md +28 -7
- package/docs/MONITORING.md +62 -0
- package/docs/MULTI-SERVER-COMPOSITIONS.md +103 -0
- package/docs/PERFORMANCE-GOVERNANCE.md +85 -0
- package/docs/PLAIN-LANGUAGE-COOKBOOK.md +28 -0
- package/docs/PUBLISHING.md +53 -0
- package/docs/QA-WORKSPACE.md +5 -1
- package/docs/README.md +7 -0
- package/docs/SECURITY-AUDIT.md +77 -0
- package/editors/mcpr-language.json +2 -2
- package/editors/vscode/package.json +1 -1
- package/editors/vscode/syntaxes/mcpr.tmLanguage.json +2 -2
- package/package.json +1 -1
- package/workspace-assets/app.js +113 -1
- package/workspace-assets/index.html +17 -0
- package/workspace-assets/style.css +13 -2
package/workspace-assets/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const state = { csrf: '', path: '', etag: '', dirty: false, files: [], selected: new Set(), run: null, history: [], tab: 'run', runSel: -1 };
|
|
1
|
+
const state = { csrf: '', path: '', etag: '', dirty: false, files: [], selected: new Set(), run: null, history: [], tab: 'run', runSel: -1, canPublish: false, publishing: false };
|
|
2
2
|
const $ = id => document.getElementById(id);
|
|
3
3
|
|
|
4
4
|
/* ---------- API ---------- */
|
|
@@ -14,6 +14,7 @@ async function api(path, options = {}) {
|
|
|
14
14
|
async function start() {
|
|
15
15
|
const boot = await api('/api/v1/bootstrap');
|
|
16
16
|
state.csrf = boot.csrf;
|
|
17
|
+
state.canPublish = (boot.capabilities || []).includes('publish');
|
|
17
18
|
$('version').textContent = `v${boot.version}`;
|
|
18
19
|
$('workspace').textContent = `Folder: ${boot.root}`;
|
|
19
20
|
$('connection').classList.add('ok');
|
|
@@ -341,6 +342,7 @@ async function run(mode, paths) {
|
|
|
341
342
|
function renderRun() {
|
|
342
343
|
const run = state.run;
|
|
343
344
|
if (!run) return;
|
|
345
|
+
updateExportBar();
|
|
344
346
|
const query = $('results-filter').value.trim();
|
|
345
347
|
const list = $('run-list');
|
|
346
348
|
list.replaceChildren();
|
|
@@ -548,3 +550,113 @@ $('refresh').onclick = () => refreshList().then(n => { if (!n && !state.path) sh
|
|
|
548
550
|
$('filter').addEventListener('input', renderList);
|
|
549
551
|
setEnabled(false);
|
|
550
552
|
start().catch(show);
|
|
553
|
+
|
|
554
|
+
/* ---------- resizable panels ---------- */
|
|
555
|
+
(() => {
|
|
556
|
+
const mainEl = document.querySelector('main');
|
|
557
|
+
const KEY = 'mcprigor.panels';
|
|
558
|
+
const apply = (side, results) => {
|
|
559
|
+
if (side) mainEl.style.setProperty('--col-side', side);
|
|
560
|
+
if (results) mainEl.style.setProperty('--col-results', results);
|
|
561
|
+
};
|
|
562
|
+
try { const saved = JSON.parse(localStorage.getItem(KEY) || '{}'); apply(saved.side, saved.results); } catch {}
|
|
563
|
+
const save = () => {
|
|
564
|
+
try { localStorage.setItem(KEY, JSON.stringify({
|
|
565
|
+
side: mainEl.style.getPropertyValue('--col-side') || undefined,
|
|
566
|
+
results: mainEl.style.getPropertyValue('--col-results') || undefined,
|
|
567
|
+
})); } catch {}
|
|
568
|
+
};
|
|
569
|
+
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
570
|
+
const wire = (id, compute, keyboardStep) => {
|
|
571
|
+
const splitter = document.getElementById(id);
|
|
572
|
+
if (!splitter) return;
|
|
573
|
+
splitter.addEventListener('pointerdown', (event) => {
|
|
574
|
+
event.preventDefault();
|
|
575
|
+
splitter.setPointerCapture(event.pointerId);
|
|
576
|
+
splitter.classList.add('dragging');
|
|
577
|
+
document.body.classList.add('resizing');
|
|
578
|
+
const move = (ev) => compute(ev.clientX);
|
|
579
|
+
const stop = () => {
|
|
580
|
+
splitter.classList.remove('dragging');
|
|
581
|
+
document.body.classList.remove('resizing');
|
|
582
|
+
splitter.removeEventListener('pointermove', move);
|
|
583
|
+
splitter.removeEventListener('pointerup', stop);
|
|
584
|
+
splitter.removeEventListener('pointercancel', stop);
|
|
585
|
+
save();
|
|
586
|
+
};
|
|
587
|
+
splitter.addEventListener('pointermove', move);
|
|
588
|
+
splitter.addEventListener('pointerup', stop);
|
|
589
|
+
splitter.addEventListener('pointercancel', stop);
|
|
590
|
+
});
|
|
591
|
+
splitter.addEventListener('keydown', (event) => {
|
|
592
|
+
if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
|
|
593
|
+
event.preventDefault();
|
|
594
|
+
keyboardStep(event.key === 'ArrowLeft' ? -16 : 16);
|
|
595
|
+
save();
|
|
596
|
+
});
|
|
597
|
+
splitter.addEventListener('dblclick', () => {
|
|
598
|
+
mainEl.style.removeProperty('--col-side');
|
|
599
|
+
mainEl.style.removeProperty('--col-results');
|
|
600
|
+
try { localStorage.removeItem(KEY); } catch {}
|
|
601
|
+
});
|
|
602
|
+
};
|
|
603
|
+
const sideWidth = () => document.querySelector('.sidebar').getBoundingClientRect().width;
|
|
604
|
+
const resultsWidth = () => document.querySelector('.results').getBoundingClientRect().width;
|
|
605
|
+
wire('split-left',
|
|
606
|
+
(x) => apply(`${clamp(x - mainEl.getBoundingClientRect().left, 160, 480)}px`, null),
|
|
607
|
+
(delta) => apply(`${clamp(sideWidth() + delta, 160, 480)}px`, null));
|
|
608
|
+
wire('split-right',
|
|
609
|
+
(x) => { const rect = mainEl.getBoundingClientRect(); apply(null, `${clamp(rect.right - x, 240, rect.width - 500)}px`); },
|
|
610
|
+
(delta) => { const rect = mainEl.getBoundingClientRect(); apply(null, `${clamp(resultsWidth() - delta, 240, rect.width - 500)}px`); });
|
|
611
|
+
})();
|
|
612
|
+
|
|
613
|
+
/* ---------- report exports ---------- */
|
|
614
|
+
function updateExportBar() {
|
|
615
|
+
const run = state.run;
|
|
616
|
+
const bar = $('export-run');
|
|
617
|
+
const index = state.runSel >= 0 ? state.runSel : 0;
|
|
618
|
+
const item = run && run.items && run.items[index];
|
|
619
|
+
const ready = run && run.mode === 'test' && item && item.status !== 'running' && item.tests;
|
|
620
|
+
bar.hidden = !ready;
|
|
621
|
+
const publish = $('publish-run');
|
|
622
|
+
const link = $('published-link');
|
|
623
|
+
publish.hidden = !(ready && state.canPublish);
|
|
624
|
+
publish.disabled = state.publishing;
|
|
625
|
+
publish.textContent = state.publishing ? 'Publishing…' : (item && item.publishedUrl ? 'Publish again' : 'Publish');
|
|
626
|
+
link.hidden = !(ready && item.publishedUrl);
|
|
627
|
+
if (item && item.publishedUrl) link.href = item.publishedUrl;
|
|
628
|
+
}
|
|
629
|
+
function exportRun(format) {
|
|
630
|
+
const run = state.run;
|
|
631
|
+
if (!run) return;
|
|
632
|
+
const index = state.runSel >= 0 ? state.runSel : 0;
|
|
633
|
+
window.open(`/api/v1/export/run?id=${encodeURIComponent(run.id)}&item=${index}&format=${format}`, '_blank');
|
|
634
|
+
}
|
|
635
|
+
async function publishRun() {
|
|
636
|
+
const run = state.run;
|
|
637
|
+
if (!run || state.publishing) return;
|
|
638
|
+
const index = state.runSel >= 0 ? state.runSel : 0;
|
|
639
|
+
state.publishing = true; updateExportBar();
|
|
640
|
+
try {
|
|
641
|
+
const value = await api('/api/v1/publish', { method: 'POST', body: JSON.stringify({ id: run.id, item: index }) });
|
|
642
|
+
const item = run.items[index];
|
|
643
|
+
if (item) item.publishedUrl = value.url;
|
|
644
|
+
window.open(value.url, '_blank', 'noopener');
|
|
645
|
+
} catch (error) {
|
|
646
|
+
toast(error.message || 'Publish failed', true);
|
|
647
|
+
} finally {
|
|
648
|
+
state.publishing = false; updateExportBar();
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
$('export-run-html').onclick = () => {
|
|
652
|
+
const run = state.run;
|
|
653
|
+
if (!run) return;
|
|
654
|
+
window.open(`/api/v1/report?id=${encodeURIComponent(run.id)}&item=${state.runSel >= 0 ? state.runSel : 0}`, '_blank');
|
|
655
|
+
};
|
|
656
|
+
$('publish-run').onclick = publishRun;
|
|
657
|
+
$('export-run-pdf').onclick = () => exportRun('pdf');
|
|
658
|
+
$('export-run-csv').onclick = () => exportRun('csv');
|
|
659
|
+
$('export-run-junit').onclick = () => exportRun('junit');
|
|
660
|
+
$('export-trends-pdf').onclick = () => window.open('/api/v1/export/trends?format=pdf', '_blank');
|
|
661
|
+
$('export-trends-csv').onclick = () => window.open('/api/v1/export/trends?format=csv', '_blank');
|
|
662
|
+
$('export-trends-raw').onclick = () => window.open('/api/v1/export/trends?format=raw-csv', '_blank');
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
<a href="https://mcprigor.com/docs/plain-language-cookbook.html" target="_blank" rel="noreferrer">📖 Cookbook — copy-ready examples</a>
|
|
42
42
|
</div>
|
|
43
43
|
</aside>
|
|
44
|
+
<div class="splitter" id="split-left" role="separator" aria-orientation="vertical" aria-label="Resize file list" tabindex="0"></div>
|
|
44
45
|
|
|
45
46
|
<section class="workbench">
|
|
46
47
|
<div class="toolbar">
|
|
@@ -76,6 +77,7 @@
|
|
|
76
77
|
<div id="diagnostics" class="diagnostics" role="status" aria-live="polite" hidden></div>
|
|
77
78
|
</section>
|
|
78
79
|
|
|
80
|
+
<div class="splitter" id="split-right" role="separator" aria-orientation="vertical" aria-label="Resize results panel" tabindex="0"></div>
|
|
79
81
|
<aside class="results">
|
|
80
82
|
<div class="results-tabs" role="tablist">
|
|
81
83
|
<button id="tab-run" class="tab active" role="tab" aria-selected="true">Run</button>
|
|
@@ -88,6 +90,15 @@
|
|
|
88
90
|
<input id="results-filter" type="search" placeholder="Search tests and results…" aria-label="Search tests and results">
|
|
89
91
|
</div>
|
|
90
92
|
<div id="panel-run" class="panel" role="tabpanel">
|
|
93
|
+
<div id="export-run" class="export-bar" hidden>
|
|
94
|
+
<span class="export-label">Export report:</span>
|
|
95
|
+
<button id="export-run-pdf" class="ghost small" type="button">PDF</button>
|
|
96
|
+
<button id="export-run-csv" class="ghost small" type="button">CSV</button>
|
|
97
|
+
<button id="export-run-junit" class="ghost small" type="button">JUnit XML</button>
|
|
98
|
+
<button id="export-run-html" class="ghost small" type="button">HTML report</button>
|
|
99
|
+
<button id="publish-run" class="ghost small" type="button" hidden>Publish</button>
|
|
100
|
+
<a id="published-link" class="published-link" target="_blank" rel="noopener" hidden>View published ↗</a>
|
|
101
|
+
</div>
|
|
91
102
|
<div id="run-list"></div>
|
|
92
103
|
<pre id="output" class="output"><span class="muted">Nothing has run yet.
|
|
93
104
|
Open a test file and click ▶ Run tests — or select several files
|
|
@@ -97,6 +108,12 @@ with their checkboxes and run them together.</span></pre>
|
|
|
97
108
|
<div id="history-list" class="history-list"><p class="muted pad">No runs recorded yet. History appears after the first ▶ Run.</p></div>
|
|
98
109
|
</div>
|
|
99
110
|
<div id="panel-trends" class="panel" role="tabpanel" hidden>
|
|
111
|
+
<div class="export-bar">
|
|
112
|
+
<span class="export-label">Export trends:</span>
|
|
113
|
+
<button id="export-trends-pdf" class="ghost small" type="button">PDF</button>
|
|
114
|
+
<button id="export-trends-csv" class="ghost small" type="button">CSV</button>
|
|
115
|
+
<button id="export-trends-raw" class="ghost small" type="button">Raw history CSV</button>
|
|
116
|
+
</div>
|
|
100
117
|
<div id="trends" class="trends"><p class="muted pad">Trends appear after a few runs.</p></div>
|
|
101
118
|
</div>
|
|
102
119
|
</aside>
|
|
@@ -27,10 +27,15 @@ header{display:flex;align-items:center;justify-content:space-between;padding:0 1
|
|
|
27
27
|
footer{display:flex;align-items:center;justify-content:space-between;padding:0 18px;background:var(--bg-raised);border-top:1px solid var(--line);font-size:11.5px;color:var(--text-faint)}
|
|
28
28
|
|
|
29
29
|
/* ---------- layout ---------- */
|
|
30
|
-
main{display:grid;grid-template-columns:250px minmax(
|
|
30
|
+
main{display:grid;grid-template-columns:var(--col-side,250px) 5px minmax(360px,1fr) 5px var(--col-results,minmax(320px,36%));min-height:0}
|
|
31
|
+
.splitter{cursor:col-resize;background:transparent;position:relative;z-index:5}
|
|
32
|
+
.splitter::after{content:"";position:absolute;inset:0 1px;background:var(--line);transition:background .15s}
|
|
33
|
+
.splitter:hover::after,.splitter.dragging::after{background:var(--accent)}
|
|
34
|
+
body.resizing{cursor:col-resize;user-select:none}
|
|
35
|
+
body.resizing iframe,body.resizing textarea{pointer-events:none}
|
|
31
36
|
|
|
32
37
|
/* ---------- sidebar ---------- */
|
|
33
|
-
.sidebar{display:flex;flex-direction:column;gap:10px;padding:14px 12px;background:var(--bg-raised);
|
|
38
|
+
.sidebar{display:flex;flex-direction:column;gap:10px;padding:14px 12px;background:var(--bg-raised);min-height:0;overflow:hidden}
|
|
34
39
|
.search-row{display:flex;gap:6px}
|
|
35
40
|
#filter{flex:1;min-width:0;background:var(--bg-input);border:1px solid var(--line);border-radius:8px;color:var(--text);padding:7px 10px;font-size:13px;outline:none}
|
|
36
41
|
#filter:focus{border-color:var(--accent-strong)}
|
|
@@ -223,3 +228,9 @@ button.small{padding:4px 8px;font-size:11.5px;color:var(--text-faint)}
|
|
|
223
228
|
nav .suite-row button .rn{display:none;flex:none;font-size:10px;opacity:.6;padding:0 2px}
|
|
224
229
|
nav .suite-row:hover button .rn{display:inline}
|
|
225
230
|
nav .suite-row button .rn:hover{opacity:1}
|
|
231
|
+
|
|
232
|
+
.export-bar{display:flex;align-items:center;gap:6px;padding:8px 14px;border-bottom:1px solid var(--line-soft)}
|
|
233
|
+
.export-label{font-size:11px;color:var(--text-dim);margin-right:2px}
|
|
234
|
+
.export-bar .small{font-size:11px;padding:3px 10px}
|
|
235
|
+
.published-link{font-size:11px;margin-left:4px;color:var(--accent, #2563eb);text-decoration:none}
|
|
236
|
+
.published-link:hover{text-decoration:underline}
|