Before this lesson
Understand functions, exceptions and scope. The multi-file example needs a local HTTP server.
What you will be able to do
Separate calculation from UI effects and locate bugs with minimal reproductions and assertions.
Understand the mechanism
ES modules expose interfaces with export and declare dependencies with import. They have module scope and strict mode. Load with script type=module and serve multi-file examples over HTTP; file URLs commonly face browser restrictions. Never embed private production keys in frontend modules.
Distinguish syntax errors, runtime exceptions and incorrect results. Read the first relevant error and stack, reproduce it, then break where state changes. Inspect arguments, call stacks and responses instead of adding arbitrary delays. Logging observes; assertions specify expectations.
Cover normal cases, boundaries, invalid inputs and regressions. Pure functions without DOM, time or network dependencies are easiest to repeat. Integration tests then check the full input-to-feedback path. Test this single-file version first before splitting modules.
Runnable example
function percentage(done, total) {
if (!Number.isInteger(done) || !Number.isInteger(total) || total < 0 || done < 0 || done > total) {
throw new RangeError("Invalid progress");
}
return total === 0 ? 0 : Math.round(done / total * 100);
}
function assertEqual(actual, expected, label) {
if (actual !== expected) throw new Error(`${label}: ${actual} !== ${expected}`);
}
assertEqual(percentage(1, 8), 13, "rounding");
assertEqual(percentage(0, 0), 0, "empty course");
assertEqual(percentage(8, 8), 100, "complete");
let rejected = false;
try { percentage(9, 8); } catch (error) { rejected = error instanceof RangeError; }
assertEqual(rejected, true, "invalid range");
console.log("4 checks passed");
Trace and verify
Expect four checks passed. Replace Math.round with Math.floor and the rounding assertion should fail, proving sensitivity to behavior. Restore the implementation rather than changing expectations to hide a defect.
Common mistakes and boundaries
Tests that mirror implementation can miss requirements. Assert rules such as completed count not exceeding total. Preserve seeds or failing inputs when using randomized checks so failures can be reproduced.
Practice challenge
Export percentage from progress.js, import it in main.js, and load main.js as a module in HTML. Add negative and fractional input tests.
Reference solution and reasoning
Keep the exported calculation in progress.js and assertions or UI in main.js. Use the complete relative path ./progress.js and inspect the network response. A bundler is not required to understand modules.