7.9 — Checkpoint: todo app, inspected
Eight lessons, one small app. A todo list is deliberately simple — and secretly uses every single piece of Phase 7 machinery you now own, end to end. Walk through building it here, then put the tester hat back on: it's about to become your own personal practice range.
No new concepts today. Just proof that you can read, write, listen for, validate, save, and inspect a real interactive page — the whole job, in miniature.
// <input data-test="new-todo">
// <form> ... </form>
// <ul id="todos"></ul>
form.addEventListener("submit", (event) => {
event.preventDefault();
const li = document.createElement("li");
li.textContent = input.value;
li.className = "todo";
list.append(li);
saveTodos();
});
list.addEventListener("click", (event) => {
if (event.target.matches(".todo")) {
event.target.classList.toggle("done");
saveTodos();
}
});
function saveTodos() {
localStorage.setItem("todos", JSON.stringify(readTodos()));
}The checkpoint project: a todo app. Every concept from this phase — tree, selectors, mutation, events, delegation, forms, storage, rendering — lands somewhere in these twenty lines.
The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”
Why a todo app specifically: it's small enough to hold entirely in your head, yet it genuinely exercises reading (7.2), writing (7.3), listening (7.4/7.5), forms (7.6), persistence (7.7), and timing (7.8) — a "real" production app adds more of the same shapes, not new DOM concepts.
This is also, literally, real QA work in miniature: most of an automation tester's career is spent writing selectors against apps someone else built, using exactly the querySelector skills from 7.2, and reasoning about what a click actually triggers using exactly the delegation and event patterns from 7.4/7.5.
Where the curriculum goes next: Phase 8 teaches the modern tooling (modules, npm, a taste of TypeScript) that real todo apps — React, Vue, and friends — are actually built with. Phase 9 is Node.js — JS with the browser walls removed. Phase 10 teaches the testing MINDSET. Phase 11 is Playwright itself, automating exactly this kind of app for real.
⌨️ part 1 — the todo core, without mutation
Build the app’s core logic in miniature: add and toggle, both as pure functions that return a NEW array — the same shape a delegated listener would call.
requirements:
- An array
todos, starting empty. addTodo(todos, text): return a NEW array with{ text, done: false }appended (spread, 4.9 — never push onto the original).toggleTodo(todos, index): return a NEW array where ONLY the item atindexhas itsdoneflipped (map, 4.9).- Add
"milk", then add"eggs", then toggle index0. Print each todo as"text (done)"or"text (not done)", joined by", ".
when you press RUN, the console must show exactly:
⌨️ part 2 — surviving a reload
Model what runs on every page load: parse a saved JSON string back into real todo objects and summarize them.
requirements:
- A string
saved='[{"text":"milk","done":true},{"text":"eggs","done":false}]'— as if it just came fromlocalStorage.getItem("todos"). - Parse it into a real array with
JSON.parse(7.7). - Print the TOTAL number of todos, then how many are
done(filter’s length, 4.9).
when you press RUN, the console must show exactly:
✏️ Quick check 1
A todo’s delete button is clicked. Per 7.5, does the app need a listener wired to that SPECIFIC button? Type yes or no.
✏️ Quick check 2
The submit handler calls event.preventDefault() first. Per 7.6, what would happen WITHOUT that line? Type it in a few words.
✏️ Quick check 3
Todos are saved with JSON.stringify. Type the function (7.7) used to read them back into real objects:
🗣️ Now teach it back
Walk a friend through everything that happens from typing a todo and pressing Enter, to it surviving a page reload — naming every Phase 7 concept it rides on.
Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.