1(function () {
2 const isNode = typeof module === "object" && module.exports;
3 const converter = isNode ? require("./convert.js") : window.UnitConverter;
4 const { convert, toBase, fromBase, parseInput } = converter;
5 const tests = [];
6
7 function test(name, fn) { tests.push({ name, fn }); }
8 function assertClose(actual, expected, epsilon = 1e-9) {
9 if (!Number.isFinite(actual) || Math.abs(actual - expected) > epsilon) {
10 throw new Error(`expected ${expected}, got ${actual}`);
11 }
12 }
13 function assertEqual(actual, expected) {
14 if (!Object.is(actual, expected)) throw new Error(`expected ${expected}, got ${actual}`);
15 }
16 function assertNaN(value) {
17 if (!Number.isNaN(value)) throw new Error(`expected NaN, got ${value}`);
18 }
19
20 test("length: meters to feet", () => assertClose(convert(1, "length", "m", "ft"), 3.280839895013123, 1e-12));
21 test("length: miles to kilometers", () => assertClose(convert(1, "length", "mi", "km"), 1.609344));
22 test("mass: pounds to kilograms", () => assertClose(convert(10, "mass", "lb", "kg"), 4.5359237));
23 test("mass: ounces to grams", () => assertClose(convert(16, "mass", "oz", "g"), 453.59237));
24 test("data: bytes to bits", () => assertClose(convert(1, "data", "B", "bit"), 8));
25 test("data: MiB to bytes", () => assertClose(convert(1, "data", "MiB", "B"), 1048576));
26 test("data: GB to GiB", () => assertClose(convert(1, "data", "GB", "GiB"), 0.9313225746154785));
27 test("time: hours to seconds", () => assertClose(convert(2.5, "time", "h", "s"), 9000));
28 test("time: weeks to days", () => assertClose(convert(3, "time", "week", "day"), 21));
29
30 test("temperature: water freezes C to F", () => assertClose(convert(0, "temperature", "C", "F"), 32));
31 test("temperature: water boils C to F", () => assertClose(convert(100, "temperature", "C", "F"), 212));
32 test("temperature: F to C at -40 crossover", () => assertClose(convert(-40, "temperature", "F", "C"), -40));
33 test("temperature: absolute zero C to K", () => assertClose(convert(-273.15, "temperature", "C", "K"), 0));
34 test("temperature: absolute zero F to K", () => assertClose(convert(-459.67, "temperature", "F", "K"), 0, 1e-10));
35 test("temperature: Kelvin to Rankine", () => assertClose(convert(300, "temperature", "K", "R"), 540));
36 test("temperature: Rankine to Fahrenheit", () => assertClose(convert(491.67, "temperature", "R", "F"), 32, 1e-10));
37 test("temperature: round trip preserves offset", () => assertClose(convert(convert(21.3, "temperature", "C", "F"), "temperature", "F", "C"), 21.3, 1e-12));
38
39 test("base helpers agree for linear units", () => assertClose(fromBase(toBase(42, "length", "cm"), "length", "m"), 0.42));
40 test("parseInput accepts commas", () => assertEqual(parseInput("1,234.5"), 1234.5));
41 test("convert returns NaN for invalid numeric input", () => assertNaN(convert("", "length", "m", "ft")));
42
43 function run() {
44 const results = [];
45 let failed = 0;
46 for (const t of tests) {
47 try {
48 t.fn();
49 results.push({ name: t.name, ok: true });
50 } catch (err) {
51 failed += 1;
52 results.push({ name: t.name, ok: false, error: err.message });
53 }
54 }
55 return { results, failed, passed: tests.length - failed, total: tests.length };
56 }
57
58 if (isNode) {
59 const summary = run();
60 for (const r of summary.results) console.log(`${r.ok ? "✓" : "✗"} ${r.name}${r.ok ? "" : ` — ${r.error}`}`);
61 console.log(`${summary.passed}/${summary.total} tests passed`);
62 if (summary.failed) process.exitCode = 1;
63 } else {
64 window.runUnitConverterTests = run;
65 window.addEventListener("DOMContentLoaded", () => {
66 const summary = run();
67 const target = document.getElementById("test-results");
68 target.innerHTML = "";
69 for (const r of summary.results) {
70 const li = document.createElement("li");
71 li.className = r.ok ? "pass" : "fail";
72 li.textContent = `${r.ok ? "✓" : "✗"} ${r.name}${r.ok ? "" : ` — ${r.error}`}`;
73 target.append(li);
74 }
75 document.getElementById("summary").textContent = `${summary.passed}/${summary.total} tests passed`;
76 });
77 }
78})();
79
Discussion
No comments yet. Start the discussion. Recorded by @patrick-toulme.