@mostajs/mjs-unit 0.2.0 → 0.3.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/bin/cli.mjs CHANGED
@@ -2,24 +2,61 @@
2
2
  /**
3
3
  * @mostajs/mjs-unit — runner CLI. Active le resolver (imports sans extension), importe les fichiers
4
4
  * de test passés en argument (chacun enregistre ses cas via `test()`), puis exécute.
5
- * Usage : mjs-unit <fichier-de-test...> (ex. mjs-unit test-scripts/*.test.mjs)
5
+ *
6
+ * Usage : mjs-unit <fichier-de-test...> [options]
7
+ * --report[=fichier] écrit une preuve HTML (défaut : test-report.html)
8
+ * --json[=fichier] écrit un rapport JSON (défaut : test-report.json)
9
+ * --title="…" titre du rapport HTML
10
+ *
6
11
  * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
7
12
  */
8
13
  import { register } from "node:module";
9
14
  import { pathToFileURL } from "node:url";
15
+ import { writeFileSync } from "node:fs";
10
16
 
11
17
  register(new URL("../loader.mjs", import.meta.url));
12
18
  const { run, pending } = await import("../dist/index.js");
13
19
 
14
- const files = process.argv.slice(2);
15
- if (!files.length) { console.error("usage: mjs-unit <fichier-de-test...>"); process.exit(2); }
20
+ // ── Découpe arguments / options ──────────────────────────────────────────────
21
+ const argv = process.argv.slice(2);
22
+ const opt = (name) => {
23
+ const hit = argv.find((a) => a === `--${name}` || a.startsWith(`--${name}=`));
24
+ if (!hit) return undefined;
25
+ const eq = hit.indexOf("=");
26
+ return eq === -1 ? "" : hit.slice(eq + 1);
27
+ };
28
+ const htmlOut = opt("report"); // "" si présent sans valeur, undefined si absent
29
+ const jsonOut = opt("json");
30
+ const title = opt("title");
31
+ const files = argv.filter((a) => !a.startsWith("--"));
32
+ if (!files.length) { console.error("usage: mjs-unit <fichier-de-test...> [--report[=f]] [--json[=f]]"); process.exit(2); }
16
33
 
34
+ // ── Exécution, une suite par fichier ─────────────────────────────────────────
17
35
  let totalFail = 0;
36
+ const suites = [];
18
37
  for (const f of files) {
19
38
  await import(pathToFileURL(f).href); // le fichier enregistre ses cas (et peut appeler run() lui-même)
20
39
  if (pending() > 0) { // sinon, on exécute les cas restants groupés par fichier
21
40
  const r = await run(f);
22
41
  totalFail += r.failed;
42
+ suites.push({ file: f, results: r.results });
23
43
  }
24
44
  }
45
+
46
+ // ── Reporters optionnels ─────────────────────────────────────────────────────
47
+ if (htmlOut !== undefined || jsonOut !== undefined) {
48
+ const { renderHtmlReport, renderJsonReport } = await import("../dist/report.js");
49
+ const report = { suites, generatedAt: new Date().toISOString(), title: title || undefined };
50
+ if (htmlOut !== undefined) {
51
+ const out = htmlOut || "test-report.html";
52
+ writeFileSync(out, renderHtmlReport(report));
53
+ console.log(`\n📄 Preuve HTML : ${out}`);
54
+ }
55
+ if (jsonOut !== undefined) {
56
+ const out = jsonOut || "test-report.json";
57
+ writeFileSync(out, renderJsonReport(report));
58
+ console.log(`📄 Rapport JSON : ${out}`);
59
+ }
60
+ }
61
+
25
62
  if (totalFail > 0) process.exitCode = 1;
package/dist/index.d.ts CHANGED
@@ -31,10 +31,19 @@ export declare function expect(actual: unknown): {
31
31
  toBeFalsy: (m?: string) => void;
32
32
  toContain: (sub: string, m?: string) => void;
33
33
  };
34
+ /** Issue d'un cas de test (consommé par les reporters, ex. `./report`). */
35
+ export interface TestResult {
36
+ name: string;
37
+ status: "pass" | "fail" | "skip";
38
+ error?: string;
39
+ durationMs: number;
40
+ }
34
41
  export interface RunResult {
35
42
  passed: number;
36
43
  failed: number;
37
44
  skipped: number;
45
+ label: string;
46
+ results: TestResult[];
38
47
  }
39
48
  /** Exécute les tests enregistrés, affiche le rapport, positionne `process.exitCode` si échec. */
40
49
  export declare function run(label?: string): Promise<RunResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,QAAQ;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE;AAI1F,gDAAgD;AAChD,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAEvE;yBAFe,IAAI;qBAID,MAAM,MAAM,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAG,IAAI;;AAEhE,yCAAyC;AACzC,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,GAAG,IAAI,CAI9D;AAMD,wBAAgB,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,SAAiB,GAAG,IAAI,CAEhE;AACD,wBAAgB,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAEhF;AACD,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAEpF;AACD,wBAAsB,WAAW,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,SAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrG;AACD,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,SAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAElH;AAED,2FAA2F;AAC3F,wBAAgB,MAAM,CAAC,MAAM,EAAE,OAAO;cAExB,OAAO,MAAM,MAAM;iBAChB,OAAO,MAAM,MAAM;qBACf,MAAM;oBACP,MAAM;qBACL,MAAM,MAAM,MAAM;EAEtC;AAGD,MAAM,WAAW,SAAS;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AAE9E,iGAAiG;AACjG,wBAAsB,GAAG,CAAC,KAAK,SAAK,GAAG,OAAO,CAAC,SAAS,CAAC,CAYxD;AAED,+CAA+C;AAC/C,wBAAgB,OAAO,IAAI,MAAM,CAA0B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,QAAQ;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE;AAI1F,gDAAgD;AAChD,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAEvE;yBAFe,IAAI;qBAID,MAAM,MAAM,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAG,IAAI;;AAEhE,yCAAyC;AACzC,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,GAAG,IAAI,CAI9D;AAMD,wBAAgB,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,SAAiB,GAAG,IAAI,CAEhE;AACD,wBAAgB,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAEhF;AACD,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAEpF;AACD,wBAAsB,WAAW,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,SAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrG;AACD,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,SAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAElH;AAED,2FAA2F;AAC3F,wBAAgB,MAAM,CAAC,MAAM,EAAE,OAAO;cAExB,OAAO,MAAM,MAAM;iBAChB,OAAO,MAAM,MAAM;qBACf,MAAM;oBACP,MAAM;qBACL,MAAM,MAAM,MAAM;EAEtC;AAGD,2EAA2E;AAC3E,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE;AAClH,MAAM,WAAW,SAAS;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,UAAU,EAAE,CAAA;CAAE;AAIpH,iGAAiG;AACjG,wBAAsB,GAAG,CAAC,KAAK,SAAK,GAAG,OAAO,CAAC,SAAS,CAAC,CAcxD;AAED,+CAA+C;AAC/C,wBAAgB,OAAO,IAAI,MAAM,CAA0B"}
package/dist/index.js CHANGED
@@ -69,32 +69,41 @@ export function expect(actual) {
69
69
  toContain: (sub, m) => ok(typeof actual === "string" && actual.includes(sub), m ?? `« ${fmt(actual)} » ne contient pas « ${sub} »`),
70
70
  };
71
71
  }
72
+ const _now = () => (typeof performance !== "undefined" ? performance.now() : Date.now());
72
73
  /** Exécute les tests enregistrés, affiche le rapport, positionne `process.exitCode` si échec. */
73
74
  export async function run(label = "") {
74
75
  let passed = 0, failed = 0, skipped = 0;
76
+ const results = [];
75
77
  if (label)
76
78
  console.log(`\n▶ ${label}`);
77
79
  for (const t of _suite) {
78
80
  if (t.skip) {
79
81
  console.log(` ⊘ ${t.name} (ignoré)`);
80
82
  skipped++;
83
+ results.push({ name: t.name, status: "skip", durationMs: 0 });
81
84
  continue;
82
85
  }
86
+ const t0 = _now();
83
87
  try {
84
88
  await t.fn();
89
+ const d = _now() - t0;
85
90
  console.log(` ✓ ${t.name}`);
86
91
  passed++;
92
+ results.push({ name: t.name, status: "pass", durationMs: d });
87
93
  }
88
94
  catch (e) {
89
- console.log(` ✗ ${t.name}\n ${e?.message ?? e}`);
95
+ const d = _now() - t0;
96
+ const msg = e?.message ?? String(e);
97
+ console.log(` ✗ ${t.name}\n ${msg}`);
90
98
  failed++;
99
+ results.push({ name: t.name, status: "fail", error: msg, durationMs: d });
91
100
  }
92
101
  }
93
102
  console.log(`\n${failed === 0 ? "✅" : "❌"} ${passed} passé(s), ${failed} échoué(s)${skipped ? `, ${skipped} ignoré(s)` : ""}`);
94
103
  if (failed > 0 && typeof process !== "undefined")
95
104
  process.exitCode = 1;
96
105
  _suite.length = 0; // réinitialise pour un éventuel run suivant
97
- return { passed, failed, skipped };
106
+ return { passed, failed, skipped, label, results };
98
107
  }
99
108
  /** Nombre de tests en attente (diagnostic). */
100
109
  export function pending() { return _suite.length; }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @mostajs/mjs-unit/report — Reporters de preuve de test (HTML & JSON).
3
+ *
4
+ * Fonctions PURES : prennent un agrégat de résultats (`TestReport`) et rendent
5
+ * une chaîne. Aucune E/S, aucun accès disque — l'appelant (CLI ou script) écrit
6
+ * le fichier. Réutilisable par tout module @mostajs/* : plus de `report.mjs`
7
+ * dupliqué par dépôt.
8
+ *
9
+ * @author Dr Hamid MADANI <drmdh@msn.com>
10
+ * @license AGPL-3.0-or-later
11
+ */
12
+ import type { TestResult } from "./index";
13
+ /** Une suite = les résultats d'un fichier de test. */
14
+ export interface ReportSuite {
15
+ file: string;
16
+ results: TestResult[];
17
+ }
18
+ /** Agrégat rendu par les reporters. */
19
+ export interface TestReport {
20
+ suites: ReportSuite[];
21
+ generatedAt: string;
22
+ title?: string;
23
+ }
24
+ /** Totaux sur l'ensemble des suites. */
25
+ export declare function summarize(report: TestReport): {
26
+ total: number;
27
+ suites: number;
28
+ passed: number;
29
+ failed: number;
30
+ skipped: number;
31
+ };
32
+ /** Rapport JSON structuré (machine-lisible, ex. CI). */
33
+ export declare function renderJsonReport(report: TestReport): string;
34
+ /** Rapport HTML autonome (thème sombre, cartes de synthèse + tables par suite). */
35
+ export declare function renderHtmlReport(report: TestReport): string;
36
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,sDAAsD;AACtD,MAAM,WAAW,WAAW;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,UAAU,EAAE,CAAA;CAAE;AACpE,uCAAuC;AACvC,MAAM,WAAW,UAAU;IAAG,MAAM,EAAE,WAAW,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;AAW1F,wCAAwC;AACxC,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU;;;;;;EAG3C;AAED,wDAAwD;AACxD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAE3D;AAED,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAmD3D"}
package/dist/report.js ADDED
@@ -0,0 +1,67 @@
1
+ const esc = (s) => String(s).replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c]));
2
+ const tally = (results) => ({
3
+ passed: results.filter((r) => r.status === "pass").length,
4
+ failed: results.filter((r) => r.status === "fail").length,
5
+ skipped: results.filter((r) => r.status === "skip").length,
6
+ });
7
+ /** Totaux sur l'ensemble des suites. */
8
+ export function summarize(report) {
9
+ const all = report.suites.flatMap((s) => s.results);
10
+ return { ...tally(all), total: all.length, suites: report.suites.length };
11
+ }
12
+ /** Rapport JSON structuré (machine-lisible, ex. CI). */
13
+ export function renderJsonReport(report) {
14
+ return JSON.stringify({ ...report, summary: summarize(report) }, null, 2);
15
+ }
16
+ /** Rapport HTML autonome (thème sombre, cartes de synthèse + tables par suite). */
17
+ export function renderHtmlReport(report) {
18
+ const sum = summarize(report);
19
+ const title = report.title ?? "@mostajs — Preuve de test";
20
+ const accent = sum.failed ? "#ef4444" : "#22c55e";
21
+ const icon = (s) => (s === "pass" ? "✓" : s === "fail" ? "✗" : "⊘");
22
+ const suiteHtml = report.suites
23
+ .map((s) => {
24
+ const t = tally(s.results);
25
+ const ok = t.failed === 0;
26
+ const rows = s.results
27
+ .map((r) => {
28
+ const dur = r.durationMs ? `<span class="dur">${r.durationMs.toFixed(1)} ms</span>` : "";
29
+ const main = `<tr class="${r.status}"><td>${icon(r.status)}</td><td>${esc(r.name)} ${dur}</td></tr>`;
30
+ return r.status === "fail" ? main + `<tr class="err"><td></td><td>${esc(r.error ?? "")}</td></tr>` : main;
31
+ })
32
+ .join("");
33
+ return `<section><h2>${esc(s.file)} <span class="badge ${ok ? "ok" : "ko"}">${t.passed}/${s.results.length}</span></h2><table>${rows}</table></section>`;
34
+ })
35
+ .join("");
36
+ return `<!doctype html><html lang="fr"><head><meta charset="utf-8">
37
+ <title>${esc(title)}</title>
38
+ <style>
39
+ body{font-family:system-ui,Segoe UI,Arial,sans-serif;margin:0;background:#0f172a;color:#e2e8f0}
40
+ header{padding:24px 32px;background:#1e293b;border-bottom:3px solid ${accent}}
41
+ h1{margin:0 0 4px;font-size:1.3rem}.sub{color:#94a3b8;font-size:.85rem}
42
+ .summary{display:flex;gap:16px;margin-top:16px;flex-wrap:wrap}
43
+ .card{background:#0b1220;border:1px solid #334155;border-radius:10px;padding:12px 18px;min-width:90px}
44
+ .card .n{font-size:1.6rem;font-weight:700}.card.ok .n{color:#22c55e}.card.ko .n{color:#ef4444}
45
+ main{padding:24px 32px;max-width:960px}section{margin-bottom:20px}
46
+ h2{font-size:1rem;border-bottom:1px solid #334155;padding-bottom:6px}
47
+ .badge{font-size:.75rem;padding:2px 8px;border-radius:999px;background:#22c55e22;color:#22c55e}.badge.ko{background:#ef444422;color:#ef4444}
48
+ table{width:100%;border-collapse:collapse;font-size:.9rem}td{padding:4px 8px;border-bottom:1px solid #1e293b;vertical-align:top}
49
+ td:first-child{width:24px;text-align:center;font-weight:700}
50
+ tr.pass td:first-child{color:#22c55e}tr.fail td:first-child{color:#ef4444}tr.skip td:first-child{color:#94a3b8}
51
+ tr.err td{color:#fca5a5;font-family:monospace;font-size:.8rem}.dur{color:#64748b;font-size:.75rem}
52
+ footer{padding:16px 32px;color:#64748b;font-size:.8rem}
53
+ </style></head><body>
54
+ <header><h1>${esc(title)}</h1>
55
+ <div class="sub">Généré le ${esc(report.generatedAt)} · moteur @mostajs/mjs-unit</div>
56
+ <div class="summary">
57
+ <div class="card"><div class="n">${sum.total}</div><div>tests</div></div>
58
+ <div class="card ok"><div class="n">${sum.passed}</div><div>réussis</div></div>
59
+ <div class="card ko"><div class="n">${sum.failed}</div><div>échoués</div></div>
60
+ ${sum.skipped ? `<div class="card"><div class="n">${sum.skipped}</div><div>ignorés</div></div>` : ""}
61
+ <div class="card"><div class="n">${sum.suites}</div><div>suites</div></div>
62
+ </div></header>
63
+ <main>${suiteHtml}</main>
64
+ <footer>Auteur : Dr Hamid MADANI &lt;drmdh@msn.com&gt; · AGPL-3.0-or-later</footer>
65
+ </body></html>`;
66
+ }
67
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
package/llms.txt CHANGED
@@ -3,7 +3,7 @@
3
3
  > Micro-framework de test ZÉRO DÉPENDANCE pour l'écosystème @mostajs/* : API test/expect, runner CLI,
4
4
  > et un resolver ESM qui exécute sous Node les dist à imports SANS extension (moduleResolution=bundler).
5
5
 
6
- - Version: 0.2.0 · Licence: AGPL-3.0-or-later · Auteur: Dr Hamid MADANI <drmdh@msn.com>
6
+ - Version: 0.3.0 · Licence: AGPL-3.0-or-later · Auteur: Dr Hamid MADANI <drmdh@msn.com>
7
7
  - Chemin: mostajs/mosta-mjs-unit
8
8
 
9
9
  ## RÔLE
@@ -14,13 +14,20 @@ réécrit `./x` → `./x.js` puis `./x/index.js`.
14
14
 
15
15
  ## EXPORTS
16
16
  - `.` (dist/index.js) : test, test.skip, describe, expect, ok, equal, deepEqual, throwsAsync,
17
- doesNotThrowAsync, run, pending ; type TestCase, RunResult.
17
+ doesNotThrowAsync, run, pending ; types TestCase, RunResult, TestResult.
18
+ `run()` retourne désormais `results: TestResult[]` (name, status pass|fail|skip, error?, durationMs).
19
+ - `./report` (dist/report.js) : reporters de PREUVE de test, fonctions PURES (zéro E/S) —
20
+ renderHtmlReport(report), renderJsonReport(report), summarize(report) ;
21
+ types TestReport {suites:{file,results}[], generatedAt, title?}, ReportSuite.
22
+ Centralise la génération de `test-report.html` : plus de `report.mjs` dupliqué par dépôt.
18
23
  - `./web` (dist/web.js) : moteur de test web GÉNÉRIQUE, zéro dépendance (node:http/net) —
19
24
  startServer, createHttpTester, fetchRequest, waitForEvent ; types TestServer, HttpTester,
20
25
  TestResponse, RequestOptions, MinimalEmitter.
21
26
  - `./register` (register.mjs) : enregistre le resolver via `node --import`.
22
27
  - `./loader` (loader.mjs) : le hook `resolve` (pour usage avancé).
23
28
  - bin `mjs-unit` (bin/cli.mjs) : runner — active le resolver, importe les fichiers, exécute.
29
+ Options : `--report[=fichier]` (preuve HTML, défaut test-report.html), `--json[=fichier]`,
30
+ `--title="…"`. Ex. : `mjs-unit test-scripts/*.test.mjs --report --title="Mon module"`.
24
31
 
25
32
  ## API
26
33
  - test(name, fn): void // fn sync ou async ; enregistre un cas
@@ -29,9 +36,17 @@ réécrit `./x` → `./x.js` puis `./x/index.js`.
29
36
  - expect(v): { toBe(e), toEqual(e), toBeTruthy(), toBeFalsy(), toContain(sub) }
30
37
  - ok(cond, msg?) · equal(a, e, msg?) · deepEqual(a, e, msg?)
31
38
  - throwsAsync(fn, msg?) · doesNotThrowAsync(fn, msg?)
32
- - run(label?): Promise<{passed, failed, skipped}> // exécute les cas en attente ; exitCode=1 si échec
39
+ - run(label?): Promise<{passed, failed, skipped, label, results}> // exécute les cas ; exitCode=1 si échec
40
+ results: { name, status: 'pass'|'fail'|'skip', error?, durationMs }[] // consommé par ./report
33
41
  - pending(): number
34
42
 
43
+ ## API — REPORT (./report, preuve de test, fonctions pures)
44
+ - renderHtmlReport(report: TestReport): string // document HTML autonome (thème sombre, cartes + tables par suite, durées, échappement anti-injection)
45
+ - renderJsonReport(report: TestReport): string // JSON { ...report, summary }
46
+ - summarize(report): { passed, failed, skipped, total, suites }
47
+ - TestReport = { suites: { file, results: TestResult[] }[], generatedAt: string, title?: string }
48
+ L'appelant écrit le fichier ; le CLI le fait via `--report`/`--json`.
49
+
35
50
  ## API — WEB (./web, générique, zéro dépendance)
36
51
  - startServer(target): Promise<TestServer> // target = http.Server | RequestListener | app Express
37
52
  → { url, port, server, close() } ; écoute sur 127.0.0.1:0 ; close() ne ferme que les serveurs créés par mjs-unit
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mostajs/mjs-unit",
3
- "version": "0.2.0",
4
- "description": "Micro-framework de test pour l'écosystème @mostajs/* : zéro dépendance, API minimale (test/expect/run), runner CLI, et un resolver ESM qui exécute sous Node les dist à imports sans extension (convention moduleResolution=bundler).",
3
+ "version": "0.3.0",
4
+ "description": "Micro-framework de test pour l'écosystème @mostajs/* : zéro dépendance, API minimale (test/expect/run), runner CLI (preuves HTML/JSON via --report), reporters réutilisables (./report), et un resolver ESM qui exécute sous Node les dist à imports sans extension (convention moduleResolution=bundler).",
5
5
  "author": "Dr Hamid MADANI <drmdh@msn.com>",
6
6
  "license": "AGPL-3.0-or-later",
7
7
  "type": "module",
@@ -21,6 +21,11 @@
21
21
  "import": "./dist/web.js",
22
22
  "default": "./dist/web.js"
23
23
  },
24
+ "./report": {
25
+ "types": "./dist/report.d.ts",
26
+ "import": "./dist/report.js",
27
+ "default": "./dist/report.js"
28
+ },
24
29
  "./register": {
25
30
  "default": "./register.mjs"
26
31
  },
@@ -39,7 +44,8 @@
39
44
  ],
40
45
  "scripts": {
41
46
  "build": "tsc -p tsconfig.json",
42
- "test": "bash test-scripts/run-tests.sh"
47
+ "test": "bash test-scripts/run-tests.sh",
48
+ "report": "npm run build && node bin/cli.mjs test-scripts/*.test.mjs --report=test-report.html --title=\"@mostajs/mjs-unit — auto-preuve\""
43
49
  },
44
50
  "devDependencies": {
45
51
  "typescript": "^5.6.0",