Make each state observable
Open the reading queue lab. Run it fresh, search web, and expect one result. Search a term with no matches and expect zero results without an error. Save a book before testing failures so you can verify saved data is preserved.
Choose Service error and submit: feedback should report the failure and Results should be empty. Switch to Success and resubmit: the error should clear. Choose Malformed data: the decoder must reject the response instead of displaying 42 as a title. The scenario control affects a new attempt, not one already started.
Choose Slow, search web, then immediately choose Fast and search testing. The final result must be Testing Useful Programs. Repeat the sequence with a slow failing attempt followed by fast success. An older failure must not erase the newer result or message. Cancel a slow attempt and confirm it stays idle after the delay would have elapsed.
Use Tab and Enter to submit and save/remove. Feedback uses role=status; the result region exposes aria-busy. The app returns focus to the query after a row action because rebuilding the list removes the old button. Manual keyboard checks complement controller tests; a pure test cannot prove a person can reach the buttons.
This is a contract matrix: successful empty data, transport-style error, malformed success, recovery, stale work, intentional cancellation, and interaction. Choose expected outcomes first, then compare. Do not call an app reliable merely because the first search worked.
Predict and trace
Write the output before running. Follow the values through each call; if your prediction differs, identify the first step that surprised you.
const cases = [
{ status: "ready", count: 0, error: "" },
{ status: "error", count: 0, error: "Service unavailable" }
];
for (const item of cases) {
console.log(item.status === "error" ? item.error : `${item.count} results`);
}Check your prediction
0 results
Service unavailableA successful empty result and a failure need different messages even though both lists have zero entries.
Compare failure and recovery through the interface
Start with a successful search, trigger one failure, then recover with another valid search. Record status, results and saved choices separately: the right error message does not prove unrelated data survived. Use the controlled tests for stale completion rather than guessing delays in the interface. Attach the old request’s handlers before rejecting its promise so the test does not create an unrelated unhandled rejection.
| Boundary or case | What to inspect |
|---|---|
| Service rejection | Error state explains failure without claiming empty success |
| Successful empty list | Ready-empty is different from the initial prompt |
| Failure then recovery | New success clears the old error and preserves saved choices |
Locate corresponding model, controller and interface checks. Change one service behavior at a time, predict the resulting state, and restore it before another experiment. Explain which test would detect a stale error overwriting newer success.
Build it yourself
Failure & Recovery: practise
Write describe(state) distinguishing idle, loading, error, and ready. Print an empty success and a failure; do not turn failure into a zero-result success.
// State shape matches the reading queue.Hint 1: the idea
Check status before formatting results.length.
Hint 2: a first step
An error branch should return its error message.
Show one solution
function describe(state) {
if (state.status === "idle") {
return "Start a search";
}
if (state.status === "loading") {
return "Searching";
}
if (state.status === "error") {
return state.error;
}
return `${state.results.length} results`;
}
console.log(describe({ status: "ready", results: [] }));
console.log(describe({ status: "error", error: "Try again", results: [] }));0 results
Try againWrite down the exact steps and observed result of one manual failure case. Then point to the automated test that covers the controller part of that behavior.
Test a changed requirement
Write down the exact steps and observed result of one manual failure case. Then point to the automated test that covers the controller part of that behavior.
Explain before moving on
Which claims require browser testing in addition to model tests? What is the expected result of cancelling a slow failure?
Completion is saved in this browser only. It does not measure mastery.