diff --git a/.claude/skills/next/SKILL.md b/.claude/skills/next/SKILL.md index a71bc17..d2905db 100644 --- a/.claude/skills/next/SKILL.md +++ b/.claude/skills/next/SKILL.md @@ -10,7 +10,7 @@ Advance the tutorial to the next lesson. Do the following steps in order: 2. Get the current lesson number from the `currentLesson` field 3. Add the current lesson number to the `completedLessons` array if it is not already there 4. Increment `currentLesson` by 1 -5. Read the `track` field from state (default `"core"` if missing). If `track` is `"qa"`, cap `currentLesson` at 21. If `track` is `"core"`, cap at 16. (Set it back to the cap value if it exceeds it.) +5. Read the `track` field from state (default `"core"` if missing). If `track` is `"qa"`, cap `currentLesson` at 22. If `track` is `"core"`, cap at 17. (Set it back to the cap value if it exceeds it.) 6. Write the updated JSON back to `tutorial/state.json` 7. Determine the next lesson filename: look in `tutorial/lessons/` for the file whose name starts with the zero-padded lesson number (e.g., lesson 3 becomes "03-", lesson 7 becomes "07-") 8. Read that lesson file diff --git a/.claude/skills/progress/SKILL.md b/.claude/skills/progress/SKILL.md index 433bbb8..d973893 100644 --- a/.claude/skills/progress/SKILL.md +++ b/.claude/skills/progress/SKILL.md @@ -20,9 +20,10 @@ Show the user their tutorial progress. Do the following: - 9: Subagents & Parallel Exploration - 10: Hooks in Action - 12: Session Management - - 14: CLI Tools & MCP Servers - - 15: Working with Images - - 16: Graduation + Bonus + - 14: Remote Tasks + - 15: CLI Tools & MCP Servers + - 16: Working with Images + - 17: Graduation + Bonus **Developer:** - 4: Code Modification @@ -32,11 +33,11 @@ Show the user their tutorial progress. Do the following: - 13: Headless Mode & Automation 4. If `track` is `"qa"`, also show a **QA Track:** section: - - 17: Test Planning from Requirements - - 18: Test Case Design & Coverage Analysis - - 19: Automated Test Generation - - 20: E2E Testing with Playwright - - 21: QA Track Graduation + - 18: Test Planning from Requirements + - 19: Test Case Design & Coverage Analysis + - 20: Automated Test Generation + - 21: E2E Testing with Playwright + - 22: QA Track Graduation If `track` is NOT `"qa"`, show a note instead: "Type /qa to start the QA track" @@ -45,4 +46,4 @@ Show the user their tutorial progress. Do the following: - `[>]` if it is the `currentLesson` (the one they're on right now) - `[~]` if it is in the `skippedLessons` array - `[ ]` if it hasn't been reached yet -6. Show a summary line. If on the core track: "Progress: N of 16 lessons completed". If on the QA track: "Progress: N of 21 lessons completed". +6. Show a summary line. If on the core track: "Progress: N of 17 lessons completed". If on the QA track: "Progress: N of 22 lessons completed". diff --git a/.claude/skills/qa/SKILL.md b/.claude/skills/qa/SKILL.md index 3a3113e..3c0560c 100644 --- a/.claude/skills/qa/SKILL.md +++ b/.claude/skills/qa/SKILL.md @@ -4,12 +4,12 @@ description: Switch to the QA track disable-model-invocation: true --- -Switch to the QA track (lessons 17–21). Do the following steps in order: +Switch to the QA track (lessons 18–22). Do the following steps in order: 1. Read the file `tutorial/state.json` 2. Set the `track` field to `"qa"` -3. If `currentLesson` is less than 17, set `currentLesson` to 17 (otherwise keep it as-is) +3. If `currentLesson` is less than 18, set `currentLesson` to 18 (otherwise keep it as-is) 4. Write the updated JSON back to `tutorial/state.json` -5. Determine the lesson filename: look in `tutorial/lessons/` for the file whose name starts with the zero-padded lesson number (e.g., lesson 17 becomes "17-") +5. Determine the lesson filename: look in `tutorial/lessons/` for the file whose name starts with the zero-padded lesson number (e.g., lesson 18 becomes "18-") 6. Read that lesson file 7. Present its contents to the user. Mention that they've switched to the QA track. If the lesson has an Exercise section, make it visually distinct so the user knows exactly what to do next. diff --git a/.claude/skills/skip/SKILL.md b/.claude/skills/skip/SKILL.md index 4a2e72e..f94f4af 100644 --- a/.claude/skills/skip/SKILL.md +++ b/.claude/skills/skip/SKILL.md @@ -8,7 +8,7 @@ Skip the current exercise and move to the next lesson. Do the following steps: 1. Read `tutorial/state.json` 2. Add the current lesson number to the `skippedLessons` array if not already there -3. Increment `currentLesson` by 1. Read the `track` field (default `"core"` if missing). Cap at 21 if `track` is `"qa"`, or at 16 if `track` is `"core"`. +3. Increment `currentLesson` by 1. Read the `track` field (default `"core"` if missing). Cap at 22 if `track` is `"qa"`, or at 17 if `track` is `"core"`. 4. Write the updated state back to `tutorial/state.json` 5. Read the next lesson file from `tutorial/lessons/` (zero-padded prefix) 6. Present it to the user. Briefly acknowledge the skip in a neutral way — something like "Moving on!" — without making the user feel bad. Everyone learns at their own pace. diff --git a/CLAUDE.md b/CLAUDE.md index 152f99e..85fdfa9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,26 +27,26 @@ You are running as an interactive tutorial guide for Claude Code. Your job is to 7. **Exit tutorial mode only if the user explicitly says so** — e.g., "exit tutorial" or "I'm done with the tutorial". Then behave as a normal Claude Code instance. -## QA Track (Lessons 17–21) +## QA Track (Lessons 18–22) The tutorial includes an optional QA track for QA engineers. It is controlled by the `track` field in `tutorial/state.json`: -- `"core"` (default) — Lessons 0–16, the standard developer-focused track -- `"qa"` — Lessons 17–21, focused on QA workflows +- `"core"` (default) — Lessons 0–17, the standard developer-focused track +- `"qa"` — Lessons 18–22, focused on QA workflows ### QA Track Skills | Command | What it does | |---------|-------------| -| `/qa` | Switch to the QA track (starts at lesson 17) | +| `/qa` | Switch to the QA track (starts at lesson 18) | | `/core` | Switch back to the core track | ### QA Lessons -- **17: Test Planning from Requirements** — Analyze a feature request and create a structured test plan -- **18: Test Case Design & Coverage** — Identify coverage gaps and design test cases using QA techniques -- **19: Automated Test Generation** — Turn test cases into running Vitest tests -- **20: E2E Testing with Playwright** — Write Playwright tests for the web UI -- **21: QA Track Graduation** — Recap and bonus challenges +- **18: Test Planning from Requirements** — Analyze a feature request and create a structured test plan +- **19: Test Case Design & Coverage** — Identify coverage gaps and design test cases using QA techniques +- **20: Automated Test Generation** — Turn test cases into running Vitest tests +- **21: E2E Testing with Playwright** — Write Playwright tests for the web UI +- **22: QA Track Graduation** — Recap and bonus challenges The `/next`, `/skip`, and `/progress` skills are track-aware — they respect the active track's lesson range. The hooks (`session-start.sh`, `prompt-context.sh`, `validate-exercise.sh`) work with any lesson number automatically. diff --git a/tutorial/lessons/00-welcome.md b/tutorial/lessons/00-welcome.md index ba8702b..a33d88c 100644 --- a/tutorial/lessons/00-welcome.md +++ b/tutorial/lessons/00-welcome.md @@ -27,9 +27,10 @@ This tutorial runs entirely inside Claude Code. You interact with me (Claude) na 11. **Git Workflow** — Commits, PRs, and code review with Claude 12. **Session Management** — Resume, rewind, and organize your work 13. **Headless Mode** — Run Claude from scripts and CI/CD -14. **CLI Tools & MCP Servers** — Use any CLI tool and structured tool access -15. **Working with Images** — Debug from screenshots and mockups -16. **Graduation** — Bonus challenges and where to go next +14. **Remote Tasks** — Delegate work to Claude in the cloud +15. **CLI Tools & MCP Servers** — Use any CLI tool and structured tool access +16. **Working with Images** — Debug from screenshots and mockups +17. **Graduation** — Bonus challenges and where to go next ## Coming from IDE Copilots? @@ -40,7 +41,7 @@ If you've used GitHub Copilot, Cursor, or other AI coding tools, here's how Clau - **Runs commands** — Claude executes tests, builds, type-checkers, and git commands, then reads the output and reacts. It's not just suggesting code — it's verifying it works. - **Persistent memory** — `CLAUDE.md` files store your conventions, architecture decisions, and rules. Claude reads them every session — no re-explaining. - **Plans before acting** — Planning mode (Shift+Tab) lets Claude explore and design an approach before writing a single line of code. You review and approve the plan first. -- **Works with images** — drag-and-drop screenshots, mockups, or error dialogs directly into the conversation (details in Lesson 15). +- **Works with images** — drag-and-drop screenshots, mockups, or error dialogs directly into the conversation (details in Lesson 16). None of this replaces what you already know — it builds on it. If you've been using AI for fixing errors, generating boilerplate, or understanding code, Claude Code handles all of those. This tutorial shows you what's different. diff --git a/tutorial/lessons/03-context-and-claude-md.md b/tutorial/lessons/03-context-and-claude-md.md index 5661b3a..d9f8ba8 100644 --- a/tutorial/lessons/03-context-and-claude-md.md +++ b/tutorial/lessons/03-context-and-claude-md.md @@ -84,6 +84,23 @@ This keeps you in flow without the hard reset of `/clear`. If you catch yourself giving Claude the same instruction in multiple sessions — "we use Prisma", "always run tests", "use early returns" — move it to CLAUDE.md. If context is getting long mid-session but you're not done, use `/compact "Focus on [your current task]"` to compress without losing the thread. These two habits together solve most "Claude forgets things" frustrations. +### Resist the manual refactor + +When Claude makes a mistake — wrong naming convention, incorrect pattern, missing a project rule — it's tempting to manually fix the code and move on. Resist that instinct. + +Instead, tell Claude to update `CLAUDE.md` with the rule it missed. For example: + +- "We use camelCase for variables in this project. Add that to CLAUDE.md so you follow it from now on." +- "You put the test file in the wrong directory. Add a note to CLAUDE.md about where tests go." + +Why this matters: + +- **A manual fix solves it once.** A `CLAUDE.md` update solves it forever — across every future session, for every person on the team. +- **Corrections in chat don't persist.** Even if Claude gets it right after you correct it, that knowledge vanishes with `/clear` or a new session. `CLAUDE.md` survives. +- **It compounds.** Each rule you add makes Claude more aligned with your project. After a few weeks, Claude already knows your conventions before you start typing. + +Think of it this way: every time you catch Claude doing something wrong, that's not just a bug to fix — it's a rule to capture. The goal is to make each mistake happen only once. + ## Completion Criteria Use an @file mention to reference a file in the sample app. Then type `/next` to continue. diff --git a/tutorial/lessons/14-remote-tasks.md b/tutorial/lessons/14-remote-tasks.md new file mode 100644 index 0000000..d138c22 --- /dev/null +++ b/tutorial/lessons/14-remote-tasks.md @@ -0,0 +1,72 @@ +# Lesson 14: Remote Tasks + +## What You'll Learn + +How to delegate work to Claude in the cloud so you can keep coding — or fire off a task at the end of the day and come back to the results tomorrow. Remote tasks are how you parallelize yourself. + +## Concepts + +### The difference: local vs cloud + +In lesson 13 you saw headless mode (`-p`): Claude runs locally, blocks your terminal, and exits when done. That's great for scripts and CI. + +Remote tasks are different. They push work to Claude.ai to run in the cloud. You get a URL back immediately, keep working, and check the results whenever you're ready. + +### Sending a remote task + +From inside a Claude Code session, use `/task`: + +``` +/task Refactor the error handling in src/api.ts to use a consistent pattern +``` + +Claude sends the task to a cloud session and gives you a URL. The work happens in the background — your local session stays free. + +### When to use remote tasks + +**Good fits:** +- Large refactors that touch many files +- Codebase-wide audits (find all TODO comments, audit error handling, check for security issues) +- Generating tests across a module +- End-of-day tasks — queue up work before you log off and review the results in the morning +- Any task where you'd otherwise sit and watch Claude work for several minutes + +**Not ideal for:** +- Quick questions you need answered right now +- Tasks that need back-and-forth conversation +- Work where you want to guide Claude's approach step by step + +### The mental model + +Think of remote tasks like assigning work to a colleague. You describe what you want, hand it off, and check in later. The key skill is writing a clear enough description that Claude can work independently — the same skill you practised in lesson 2 (effective prompting). + +### Comparison + +| | Headless (`-p`) | Remote (`/task`) | +|---|---|---| +| Runs | Locally | In the cloud | +| Blocks terminal | Yes | No | +| Best for | Scripts, CI, quick queries | Longer tasks you don't need to babysit | +| Output | stdout | Claude.ai session | + +## Exercise + +**Think about a task you'd delegate.** + +You don't need to run a remote task right now (it requires a Claude.ai account with the feature enabled). Instead, think about this: + +1. Pick a task from your real work that takes Claude more than a couple of minutes +2. Write a prompt clear enough that Claude could complete it without any follow-up questions +3. Consider: what context would Claude need? Would you reference specific files? Set constraints? + +The goal is to practise the skill of writing self-contained task descriptions — that's the bottleneck for effective delegation, whether to Claude or to a human. + +## Hints + +1. Good remote task prompts are specific about scope: "Refactor error handling in `src/api/`" is better than "improve error handling" +2. Include constraints: "Don't change the public API" or "Use the existing AppError class" +3. If you find yourself writing a paragraph of context, consider whether CLAUDE.md should hold that context permanently instead + +## Completion Criteria + +This is a conceptual lesson — there is nothing to validate automatically. Type `/next` when you are ready to continue. diff --git a/tutorial/lessons/14-cli-tools-and-mcp.md b/tutorial/lessons/15-cli-tools-and-mcp.md similarity index 99% rename from tutorial/lessons/14-cli-tools-and-mcp.md rename to tutorial/lessons/15-cli-tools-and-mcp.md index 4e67c4a..fa31931 100644 --- a/tutorial/lessons/14-cli-tools-and-mcp.md +++ b/tutorial/lessons/15-cli-tools-and-mcp.md @@ -1,4 +1,4 @@ -# Lesson 14: CLI Tools & MCP Servers +# Lesson 15: CLI Tools & MCP Servers ## What You'll Learn diff --git a/tutorial/lessons/15-working-with-images.md b/tutorial/lessons/16-working-with-images.md similarity index 98% rename from tutorial/lessons/15-working-with-images.md rename to tutorial/lessons/16-working-with-images.md index 878f4f2..0290964 100644 --- a/tutorial/lessons/15-working-with-images.md +++ b/tutorial/lessons/16-working-with-images.md @@ -1,4 +1,4 @@ -# Lesson 15: Working with Images +# Lesson 16: Working with Images ## What You'll Learn diff --git a/tutorial/lessons/16-graduation.md b/tutorial/lessons/17-graduation.md similarity index 98% rename from tutorial/lessons/16-graduation.md rename to tutorial/lessons/17-graduation.md index aeb01ef..28af907 100644 --- a/tutorial/lessons/16-graduation.md +++ b/tutorial/lessons/17-graduation.md @@ -1,4 +1,4 @@ -# Lesson 16: Graduation +# Lesson 17: Graduation ## You Did It @@ -46,7 +46,7 @@ Pick a small feature — for example, "sort tasks by priority". Ask me to implem ### 5. MCP Integration -In Lesson 14 you used `curl` via the CLI to fetch API data. Now try configuring the [GitHub MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/github) in `.claude/settings.json` and using it to list issues or PRs on a repo. Then do the same thing with the `gh` CLI. Compare the experience — how does structured MCP tool access differ from raw CLI? When would you prefer one over the other? +In Lesson 15 you used `curl` via the CLI to fetch API data. Now try configuring the [GitHub MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/github) in `.claude/settings.json` and using it to list issues or PRs on a repo. Then do the same thing with the `gh` CLI. Compare the experience — how does structured MCP tool access differ from raw CLI? When would you prefer one over the other? ## Common Frustrations (and What to Do) diff --git a/tutorial/lessons/17-test-planning.md b/tutorial/lessons/18-test-planning.md similarity index 98% rename from tutorial/lessons/17-test-planning.md rename to tutorial/lessons/18-test-planning.md index c96da1b..b94e0fd 100644 --- a/tutorial/lessons/17-test-planning.md +++ b/tutorial/lessons/18-test-planning.md @@ -1,4 +1,4 @@ -# Lesson 17: Test Planning from Requirements +# Lesson 18: Test Planning from Requirements ## What You'll Learn diff --git a/tutorial/lessons/18-test-case-design.md b/tutorial/lessons/19-test-case-design.md similarity index 98% rename from tutorial/lessons/18-test-case-design.md rename to tutorial/lessons/19-test-case-design.md index e67640b..6ade12b 100644 --- a/tutorial/lessons/18-test-case-design.md +++ b/tutorial/lessons/19-test-case-design.md @@ -1,4 +1,4 @@ -# Lesson 18: Test Case Design & Coverage Analysis +# Lesson 19: Test Case Design & Coverage Analysis ## What You'll Learn diff --git a/tutorial/lessons/19-automated-test-generation.md b/tutorial/lessons/20-automated-test-generation.md similarity index 98% rename from tutorial/lessons/19-automated-test-generation.md rename to tutorial/lessons/20-automated-test-generation.md index 277b3f9..682eefd 100644 --- a/tutorial/lessons/19-automated-test-generation.md +++ b/tutorial/lessons/20-automated-test-generation.md @@ -1,4 +1,4 @@ -# Lesson 19: Automated Test Generation +# Lesson 20: Automated Test Generation ## What You'll Learn diff --git a/tutorial/lessons/20-e2e-testing-playwright.md b/tutorial/lessons/21-e2e-testing-playwright.md similarity index 98% rename from tutorial/lessons/20-e2e-testing-playwright.md rename to tutorial/lessons/21-e2e-testing-playwright.md index 2b85db1..05f3842 100644 --- a/tutorial/lessons/20-e2e-testing-playwright.md +++ b/tutorial/lessons/21-e2e-testing-playwright.md @@ -1,4 +1,4 @@ -# Lesson 20: E2E Testing with Playwright +# Lesson 21: E2E Testing with Playwright ## What You'll Learn diff --git a/tutorial/lessons/21-qa-track-graduation.md b/tutorial/lessons/22-qa-track-graduation.md similarity index 98% rename from tutorial/lessons/21-qa-track-graduation.md rename to tutorial/lessons/22-qa-track-graduation.md index 954c78b..c008c17 100644 --- a/tutorial/lessons/21-qa-track-graduation.md +++ b/tutorial/lessons/22-qa-track-graduation.md @@ -1,4 +1,4 @@ -# Lesson 21: QA Track Graduation +# Lesson 22: QA Track Graduation ## You Did It diff --git a/tutorial/validators/validate-14.sh b/tutorial/validators/validate-15.sh similarity index 66% rename from tutorial/validators/validate-14.sh rename to tutorial/validators/validate-15.sh index 2812bf8..f53acca 100755 --- a/tutorial/validators/validate-14.sh +++ b/tutorial/validators/validate-15.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Validator for Lesson 14: CLI Tools & MCP Servers +# Validator for Lesson 15: CLI Tools & MCP Servers # Checks that api-response.json exists with valid JSON content. set -euo pipefail @@ -10,25 +10,25 @@ JSON_FILE="$REPO_ROOT/exercises/sample-app/api-response.json" # Check 1: api-response.json must exist if [ ! -f "$JSON_FILE" ]; then - echo "[LESSON 14] INCOMPLETE: exercises/sample-app/api-response.json does not exist" + echo "[LESSON 15] INCOMPLETE: exercises/sample-app/api-response.json does not exist" exit 1 fi -echo "[LESSON 14] api-response.json found" +echo "[LESSON 15] api-response.json found" # Check 2: Must contain valid JSON (use Node.js — guaranteed available) if ! node -e "JSON.parse(require('fs').readFileSync('$JSON_FILE', 'utf8'))" 2>/dev/null; then - echo "[LESSON 14] INCOMPLETE: api-response.json does not contain valid JSON" + echo "[LESSON 15] INCOMPLETE: api-response.json does not contain valid JSON" exit 1 fi -echo "[LESSON 14] valid JSON" +echo "[LESSON 15] valid JSON" # Check 3: Must be a non-empty array (length > 0) ENTRY_COUNT=$(node -e "const d = JSON.parse(require('fs').readFileSync('$JSON_FILE', 'utf8')); if (!Array.isArray(d)) { process.exit(1); } console.log(d.length);" 2>/dev/null || echo "0") if [ "$ENTRY_COUNT" -ge 1 ]; then - echo "[LESSON 14] JSON contains $ENTRY_COUNT entry/entries" + echo "[LESSON 15] JSON contains $ENTRY_COUNT entry/entries" else - echo "[LESSON 14] INCOMPLETE: api-response.json appears to be empty" + echo "[LESSON 15] INCOMPLETE: api-response.json appears to be empty" exit 1 fi -echo "[LESSON 14] PASSED: Lesson 14 exercise complete" +echo "[LESSON 15] PASSED: Lesson 15 exercise complete" diff --git a/tutorial/validators/validate-17.sh b/tutorial/validators/validate-17.sh deleted file mode 100755 index 60486b6..0000000 --- a/tutorial/validators/validate-17.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -# Validator for Lesson 17: Test Planning from Requirements -# Checks that TEST-PLAN.md exists with structured test cases. - -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" -PLAN_FILE="$REPO_ROOT/exercises/sample-app/TEST-PLAN.md" - -# Check 1: TEST-PLAN.md must exist -if [ ! -f "$PLAN_FILE" ]; then - echo "[LESSON 17] INCOMPLETE: exercises/sample-app/TEST-PLAN.md does not exist" - exit 1 -fi -echo "[LESSON 17] TEST-PLAN.md found" - -# Check 2: Must contain test case IDs -if grep -qiE "TC-[0-9]{3}" "$PLAN_FILE"; then - echo "[LESSON 17] Test case IDs found" -else - echo "[LESSON 17] INCOMPLETE: No test case IDs found (expected TC-001 format)" - exit 1 -fi - -# Check 3: Must have structural markers -if grep -qi "precondition" "$PLAN_FILE" && grep -qi "expected" "$PLAN_FILE"; then - echo "[LESSON 17] Structural markers (preconditions, expected results) found" -else - echo "[LESSON 17] INCOMPLETE: Missing structural markers — include Preconditions and Expected Results for each test case" - exit 1 -fi - -echo "[LESSON 17] PASSED: Lesson 17 exercise complete" diff --git a/tutorial/validators/validate-18.sh b/tutorial/validators/validate-18.sh index def7fdc..2b2b0d6 100755 --- a/tutorial/validators/validate-18.sh +++ b/tutorial/validators/validate-18.sh @@ -1,41 +1,33 @@ #!/usr/bin/env bash -# Validator for Lesson 18: Test Case Design & Coverage Analysis -# Checks that TEST-CASES.md exists and references the untested modules. +# Validator for Lesson 18: Test Planning from Requirements +# Checks that TEST-PLAN.md exists with structured test cases. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" -CASES_FILE="$REPO_ROOT/exercises/sample-app/TEST-CASES.md" +PLAN_FILE="$REPO_ROOT/exercises/sample-app/TEST-PLAN.md" -# Check 1: TEST-CASES.md must exist -if [ ! -f "$CASES_FILE" ]; then - echo "[LESSON 18] INCOMPLETE: exercises/sample-app/TEST-CASES.md does not exist" +# Check 1: TEST-PLAN.md must exist +if [ ! -f "$PLAN_FILE" ]; then + echo "[LESSON 18] INCOMPLETE: exercises/sample-app/TEST-PLAN.md does not exist" exit 1 fi -echo "[LESSON 18] TEST-CASES.md found" +echo "[LESSON 18] TEST-PLAN.md found" -# Check 2: Must reference formatters.ts -if grep -qi "formatters" "$CASES_FILE"; then - echo "[LESSON 18] References formatters module" +# Check 2: Must contain test case IDs +if grep -qiE "TC-[0-9]{3}" "$PLAN_FILE"; then + echo "[LESSON 18] Test case IDs found" else - echo "[LESSON 18] INCOMPLETE: TEST-CASES.md should reference the formatters module" + echo "[LESSON 18] INCOMPLETE: No test case IDs found (expected TC-001 format)" exit 1 fi -# Check 3: Must reference utils.ts -if grep -qi "utils\|capitalize\|truncate\|groupBy" "$CASES_FILE"; then - echo "[LESSON 18] References utils module" +# Check 3: Must have structural markers +if grep -qi "precondition" "$PLAN_FILE" && grep -qi "expected" "$PLAN_FILE"; then + echo "[LESSON 18] Structural markers (preconditions, expected results) found" else - echo "[LESSON 18] INCOMPLETE: TEST-CASES.md should reference the utils module" - exit 1 -fi - -# Check 4: Must reference validators.ts -if grep -qi "validators\|isValidTitle\|isValidEmail" "$CASES_FILE"; then - echo "[LESSON 18] References validators module" -else - echo "[LESSON 18] INCOMPLETE: TEST-CASES.md should reference the validators module" + echo "[LESSON 18] INCOMPLETE: Missing structural markers — include Preconditions and Expected Results for each test case" exit 1 fi diff --git a/tutorial/validators/validate-19.sh b/tutorial/validators/validate-19.sh index 33e7b39..3690495 100755 --- a/tutorial/validators/validate-19.sh +++ b/tutorial/validators/validate-19.sh @@ -1,40 +1,41 @@ #!/usr/bin/env bash -# Validator for Lesson 19: Automated Test Generation -# Checks that formatters.test.ts exists with describe blocks and all tests pass. +# Validator for Lesson 19: Test Case Design & Coverage Analysis +# Checks that TEST-CASES.md exists and references the untested modules. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" -TEST_FILE="$REPO_ROOT/exercises/sample-app/tests/formatters.test.ts" +CASES_FILE="$REPO_ROOT/exercises/sample-app/TEST-CASES.md" -# Check 1: formatters.test.ts must exist -if [ ! -f "$TEST_FILE" ]; then - echo "[LESSON 19] INCOMPLETE: exercises/sample-app/tests/formatters.test.ts does not exist" +# Check 1: TEST-CASES.md must exist +if [ ! -f "$CASES_FILE" ]; then + echo "[LESSON 19] INCOMPLETE: exercises/sample-app/TEST-CASES.md does not exist" exit 1 fi -echo "[LESSON 19] formatters.test.ts found" +echo "[LESSON 19] TEST-CASES.md found" -# Check 2: Must contain describe blocks -if grep -q "describe" "$TEST_FILE"; then - echo "[LESSON 19] Describe blocks found" +# Check 2: Must reference formatters.ts +if grep -qi "formatters" "$CASES_FILE"; then + echo "[LESSON 19] References formatters module" else - echo "[LESSON 19] INCOMPLETE: No describe blocks found in formatters.test.ts" + echo "[LESSON 19] INCOMPLETE: TEST-CASES.md should reference the formatters module" exit 1 fi -# Check 3: Tests must pass -cd "$REPO_ROOT/exercises/sample-app" - -if [ ! -d node_modules ]; then - echo "[LESSON 19] Installing dependencies..." - npm install --silent 2>/dev/null +# Check 3: Must reference utils.ts +if grep -qi "utils\|capitalize\|truncate\|groupBy" "$CASES_FILE"; then + echo "[LESSON 19] References utils module" +else + echo "[LESSON 19] INCOMPLETE: TEST-CASES.md should reference the utils module" + exit 1 fi -if npx vitest run; then - echo "[LESSON 19] All tests pass" +# Check 4: Must reference validators.ts +if grep -qi "validators\|isValidTitle\|isValidEmail" "$CASES_FILE"; then + echo "[LESSON 19] References validators module" else - echo "[LESSON 19] INCOMPLETE: Some tests are failing — run npm test to see the output" + echo "[LESSON 19] INCOMPLETE: TEST-CASES.md should reference the validators module" exit 1 fi diff --git a/tutorial/validators/validate-20.sh b/tutorial/validators/validate-20.sh index 7447896..27b7f8f 100755 --- a/tutorial/validators/validate-20.sh +++ b/tutorial/validators/validate-20.sh @@ -1,29 +1,29 @@ #!/usr/bin/env bash -# Validator for Lesson 20: E2E Testing with Playwright -# Checks that tasks.spec.ts exists with Playwright patterns and tests pass. +# Validator for Lesson 20: Automated Test Generation +# Checks that formatters.test.ts exists with describe blocks and all tests pass. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" -E2E_FILE="$REPO_ROOT/exercises/sample-app/tests/e2e/tasks.spec.ts" +TEST_FILE="$REPO_ROOT/exercises/sample-app/tests/formatters.test.ts" -# Check 1: tasks.spec.ts must exist in e2e directory -if [ ! -f "$E2E_FILE" ]; then - echo "[LESSON 20] INCOMPLETE: exercises/sample-app/tests/e2e/tasks.spec.ts does not exist" +# Check 1: formatters.test.ts must exist +if [ ! -f "$TEST_FILE" ]; then + echo "[LESSON 20] INCOMPLETE: exercises/sample-app/tests/formatters.test.ts does not exist" exit 1 fi -echo "[LESSON 20] tasks.spec.ts found" +echo "[LESSON 20] formatters.test.ts found" -# Check 2: Must contain Playwright test patterns -if grep -q "test(" "$E2E_FILE" && grep -q "expect(" "$E2E_FILE" && grep -q "page\." "$E2E_FILE"; then - echo "[LESSON 20] Playwright test patterns found" +# Check 2: Must contain describe blocks +if grep -q "describe" "$TEST_FILE"; then + echo "[LESSON 20] Describe blocks found" else - echo "[LESSON 20] INCOMPLETE: tasks.spec.ts should contain Playwright test patterns (test, expect, page)" + echo "[LESSON 20] INCOMPLETE: No describe blocks found in formatters.test.ts" exit 1 fi -# Check 3: Playwright tests must pass +# Check 3: Tests must pass cd "$REPO_ROOT/exercises/sample-app" if [ ! -d node_modules ]; then @@ -31,10 +31,10 @@ if [ ! -d node_modules ]; then npm install --silent 2>/dev/null fi -if npx playwright test; then - echo "[LESSON 20] E2E tests pass" +if npx vitest run; then + echo "[LESSON 20] All tests pass" else - echo "[LESSON 20] INCOMPLETE: E2E tests are failing — run npx playwright test to see the output" + echo "[LESSON 20] INCOMPLETE: Some tests are failing — run npm test to see the output" exit 1 fi diff --git a/tutorial/validators/validate-21.sh b/tutorial/validators/validate-21.sh new file mode 100755 index 0000000..1f3132c --- /dev/null +++ b/tutorial/validators/validate-21.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# Validator for Lesson 21: E2E Testing with Playwright +# Checks that tasks.spec.ts exists with Playwright patterns and tests pass. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +E2E_FILE="$REPO_ROOT/exercises/sample-app/tests/e2e/tasks.spec.ts" + +# Check 1: tasks.spec.ts must exist in e2e directory +if [ ! -f "$E2E_FILE" ]; then + echo "[LESSON 21] INCOMPLETE: exercises/sample-app/tests/e2e/tasks.spec.ts does not exist" + exit 1 +fi +echo "[LESSON 21] tasks.spec.ts found" + +# Check 2: Must contain Playwright test patterns +if grep -q "test(" "$E2E_FILE" && grep -q "expect(" "$E2E_FILE" && grep -q "page\." "$E2E_FILE"; then + echo "[LESSON 21] Playwright test patterns found" +else + echo "[LESSON 21] INCOMPLETE: tasks.spec.ts should contain Playwright test patterns (test, expect, page)" + exit 1 +fi + +# Check 3: Playwright tests must pass +cd "$REPO_ROOT/exercises/sample-app" + +if [ ! -d node_modules ]; then + echo "[LESSON 21] Installing dependencies..." + npm install --silent 2>/dev/null +fi + +if npx playwright test; then + echo "[LESSON 21] E2E tests pass" +else + echo "[LESSON 21] INCOMPLETE: E2E tests are failing — run npx playwright test to see the output" + exit 1 +fi + +echo "[LESSON 21] PASSED: Lesson 21 exercise complete"