@mostajs/mjs-unit 0.2.0 → 0.3.1

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
@@ -22,19 +22,36 @@ export declare function ok(cond: unknown, message?: string): void;
22
22
  export declare function equal(actual: unknown, expected: unknown, message?: string): void;
23
23
  export declare function deepEqual(actual: unknown, expected: unknown, message?: string): void;
24
24
  export declare function throwsAsync(fn: () => Promise<unknown>, message?: string): Promise<void>;
25
+ /** Assertion synchrone : `fn` doit lever ; `matcher` (RegExp ou sous-chaîne) valide le message. Renvoie l'erreur. */
26
+ export declare function throws(fn: () => unknown, matcher?: RegExp | string, message?: string): unknown;
25
27
  export declare function doesNotThrowAsync(fn: () => Promise<unknown>, message?: string): Promise<void>;
26
- /** API fluide : `expect(x).toBe(y)`, `.toEqual(obj)`, `.toBeTruthy()`, `.toContain(s)`. */
28
+ /** API fluide : `expect(x).toBe(y)`, `.toEqual(obj)`, `.toBeTruthy()`, `.toContain(s)`, `.toMatch(re)`. */
27
29
  export declare function expect(actual: unknown): {
28
30
  toBe: (e: unknown, m?: string) => void;
29
31
  toEqual: (e: unknown, m?: string) => void;
30
32
  toBeTruthy: (m?: string) => void;
31
33
  toBeFalsy: (m?: string) => void;
32
34
  toContain: (sub: string, m?: string) => void;
35
+ /** Chaîne matchée par une RegExp (ou incluant une sous-chaîne). */
36
+ toMatch: (re: RegExp | string, m?: string) => void;
37
+ /** Longueur d'une chaîne/tableau. */
38
+ toHaveLength: (n: number, m?: string) => void;
39
+ /** Nombre ≥ borne. */
40
+ toBeGreaterThanOrEqual: (n: number, m?: string) => void;
33
41
  };
42
+ /** Issue d'un cas de test (consommé par les reporters, ex. `./report`). */
43
+ export interface TestResult {
44
+ name: string;
45
+ status: "pass" | "fail" | "skip";
46
+ error?: string;
47
+ durationMs: number;
48
+ }
34
49
  export interface RunResult {
35
50
  passed: number;
36
51
  failed: number;
37
52
  skipped: number;
53
+ label: string;
54
+ results: TestResult[];
38
55
  }
39
56
  /** Exécute les tests enregistrés, affiche le rapport, positionne `process.exitCode` si échec. */
40
57
  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,qHAAqH;AACrH,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAS9F;AACD,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,SAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAElH;AAED,2GAA2G;AAC3G,wBAAgB,MAAM,CAAC,MAAM,EAAE,OAAO;cAExB,OAAO,MAAM,MAAM;iBAChB,OAAO,MAAM,MAAM;qBACf,MAAM;oBACP,MAAM;qBACL,MAAM,MAAM,MAAM;IACnC,mEAAmE;kBACrD,MAAM,GAAG,MAAM,MAAM,MAAM;IACzC,qCAAqC;sBACnB,MAAM,MAAM,MAAM;IACpC,sBAAsB;gCACM,MAAM,MAAM,MAAM;EAEjD;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
@@ -51,6 +51,26 @@ export async function throwsAsync(fn, message = "devait lever") {
51
51
  }
52
52
  throw new AssertionError(message);
53
53
  }
54
+ /** Assertion synchrone : `fn` doit lever ; `matcher` (RegExp ou sous-chaîne) valide le message. Renvoie l'erreur. */
55
+ export function throws(fn, matcher, message) {
56
+ let threw = false;
57
+ let err;
58
+ try {
59
+ fn();
60
+ }
61
+ catch (e) {
62
+ threw = true;
63
+ err = e;
64
+ }
65
+ if (!threw)
66
+ throw new AssertionError(message ?? "devait lever une exception");
67
+ const msg = err?.message ?? String(err);
68
+ if (matcher instanceof RegExp && !matcher.test(msg))
69
+ throw new AssertionError(message ?? `exception « ${msg} » ne matche pas ${matcher}`);
70
+ if (typeof matcher === "string" && !msg.includes(matcher))
71
+ throw new AssertionError(message ?? `exception « ${msg} » ne contient pas « ${matcher} »`);
72
+ return err;
73
+ }
54
74
  export async function doesNotThrowAsync(fn, message = "ne devait pas lever") {
55
75
  try {
56
76
  await fn();
@@ -59,7 +79,7 @@ export async function doesNotThrowAsync(fn, message = "ne devait pas lever") {
59
79
  throw new AssertionError(`${message} : ${e?.message}`);
60
80
  }
61
81
  }
62
- /** API fluide : `expect(x).toBe(y)`, `.toEqual(obj)`, `.toBeTruthy()`, `.toContain(s)`. */
82
+ /** API fluide : `expect(x).toBe(y)`, `.toEqual(obj)`, `.toBeTruthy()`, `.toContain(s)`, `.toMatch(re)`. */
63
83
  export function expect(actual) {
64
84
  return {
65
85
  toBe: (e, m) => equal(actual, e, m),
@@ -67,34 +87,49 @@ export function expect(actual) {
67
87
  toBeTruthy: (m) => ok(actual, m),
68
88
  toBeFalsy: (m) => ok(!actual, m ?? "attendu falsy"),
69
89
  toContain: (sub, m) => ok(typeof actual === "string" && actual.includes(sub), m ?? `« ${fmt(actual)} » ne contient pas « ${sub} »`),
90
+ /** Chaîne matchée par une RegExp (ou incluant une sous-chaîne). */
91
+ toMatch: (re, m) => ok(typeof actual === "string" && (re instanceof RegExp ? re.test(actual) : actual.includes(re)), m ?? `« ${fmt(actual)} » ne matche pas ${re}`),
92
+ /** Longueur d'une chaîne/tableau. */
93
+ toHaveLength: (n, m) => ok(!!actual && typeof actual.length === "number" && actual.length === n, m ?? `longueur attendue ${n}, reçu ${fmt(actual?.length)}`),
94
+ /** Nombre ≥ borne. */
95
+ toBeGreaterThanOrEqual: (n, m) => ok(typeof actual === "number" && actual >= n, m ?? `attendu ≥ ${n}, reçu ${fmt(actual)}`),
70
96
  };
71
97
  }
98
+ const _now = () => (typeof performance !== "undefined" ? performance.now() : Date.now());
72
99
  /** Exécute les tests enregistrés, affiche le rapport, positionne `process.exitCode` si échec. */
73
100
  export async function run(label = "") {
74
101
  let passed = 0, failed = 0, skipped = 0;
102
+ const results = [];
75
103
  if (label)
76
104
  console.log(`\n▶ ${label}`);
77
105
  for (const t of _suite) {
78
106
  if (t.skip) {
79
107
  console.log(` ⊘ ${t.name} (ignoré)`);
80
108
  skipped++;
109
+ results.push({ name: t.name, status: "skip", durationMs: 0 });
81
110
  continue;
82
111
  }
112
+ const t0 = _now();
83
113
  try {
84
114
  await t.fn();
115
+ const d = _now() - t0;
85
116
  console.log(` ✓ ${t.name}`);
86
117
  passed++;
118
+ results.push({ name: t.name, status: "pass", durationMs: d });
87
119
  }
88
120
  catch (e) {
89
- console.log(` ✗ ${t.name}\n ${e?.message ?? e}`);
121
+ const d = _now() - t0;
122
+ const msg = e?.message ?? String(e);
123
+ console.log(` ✗ ${t.name}\n ${msg}`);
90
124
  failed++;
125
+ results.push({ name: t.name, status: "fail", error: msg, durationMs: d });
91
126
  }
92
127
  }
93
128
  console.log(`\n${failed === 0 ? "✅" : "❌"} ${passed} passé(s), ${failed} échoué(s)${skipped ? `, ${skipped} ignoré(s)` : ""}`);
94
129
  if (failed > 0 && typeof process !== "undefined")
95
130
  process.exitCode = 1;
96
131
  _suite.length = 0; // réinitialise pour un éventuel run suivant
97
- return { passed, failed, skipped };
132
+ return { passed, failed, skipped, label, results };
98
133
  }
99
134
  /** Nombre de tests en attente (diagnostic). */
100
135
  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,24 +14,39 @@ 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
27
34
  - test.skip(name, fn): void
28
35
  - describe(group, body): void // préfixe les noms
29
- - expect(v): { toBe(e), toEqual(e), toBeTruthy(), toBeFalsy(), toContain(sub) }
36
+ - expect(v): { toBe(e), toEqual(e), toBeTruthy(), toBeFalsy(), toContain(sub), toMatch(re|sub), toHaveLength(n), toBeGreaterThanOrEqual(n) }
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.1",
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",