What a test is
A unit test is a small program that calls one piece of your code, a unit, with inputs whose right answer you already know. It checks the answer automatically, so you can rerun every check after every change.
price.js exports one function. discount(price, percent) works out the amount off, then returns the price minus it. export lets other files, like the test file, import it.
The test file and the runner
The test file imports three things: test from node:test, assert from node:assert/strict (both built into Node.js, nothing to install), and discount from ./price.js, the code it checks.
Run node --test in the project folder. It searches for test files, such as names ending in .test.js, and runs each one as its own process (a separate running program). With "test": "node --test" in package.json, npm test does the same.
test() hands the runner a name and a function. The runner calls the function. If it returns without throwing, the test passes. If anything inside throws, the test fails. That is the whole rule.
A passing test
Line 5 calls the real function: discount(50, 10). off = 50 * 10 / 100 = 5, and line 3 returns 50 - 5 = 45. That 45 is the actual value; the test expects 45.
assert.equal(45, 45) compares with ===, strict equality. 45 === 45 is true, so the assertion does nothing at all. The function returns, and the runner prints ✔ 10% off 50 is 45.
A failing test
The second test asks for 150% off. off = 50 * 150 / 100 = 75, so line 3 returns 50 - 75 = -25. The test expects 0: a price should never go below zero.
-25 === 0 is false, so assert.equal throws an AssertionError: -25 !== 0. The runner catches it, prints ✖ never below 0, and ends with exit code 1. An exit code is the number a program finishes with: 0 means success.
Fix and run again
The failing test points at a real bug: discount can return a negative price. Fix the code, not the test: line 3 becomes Math.max(0, price - off), which never returns less than 0.
Run node --test again. discount(50, 150) now returns 0, so both assertions hold: pass 2, fail 0, exit code 0. CI servers run the same command on every push and block changes that fail.
What happens
// step 1/11 · why unit one small piece of code, here a function test call it with known input assertion check the result; throw if it is wrong runner run every test, report pass or fail
node:assert/strict is the strict mode of node:assert: assert.equal there is assert.strictEqual. The lesson says it compares with ===; precisely, it uses Object.is(), which works like === except that NaN equals NaN and 0 differs from -0 (so assert.strictEqual(NaN, NaN) passes and assert.strictEqual(0, -0) throws). For arrays and objects use assert.deepEqual (deepStrictEqual), which compares contents instead of identity. The three files are real. Put price.js, price.test.js and a package.json with "type": "module" in one folder and run node --test (Node.js 20 or newer).