Files
intotheeast-com/docs/working/qa/test-plan.md
T

629 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# QA Test Plan
*Branch: experimental-polar-steps. Tester role: Senior Staff QA Engineer.*
---
## Scope
All features implemented in Phase 4 (Milestones 14):
- M1: Entry enrichment (location badge, weather badge, photo gallery, hero image)
- M2: Interactive map page
- M3: Statistics page
- M4: Mini-map on tracker feed
Test URLs:
- Desktop: http://localhost:8081
- Mobile: http://100.96.115.96:8081 (Tailscale — requires physical phone)
---
## Milestone 1 — Entry Enrichment
### TC-1.1: Location badge on entry page
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open http://localhost:8081/tracker/2026-06-17.entry | Entry page loads (200) |
| 2 | Look at entry header | `📍 Amsterdam, Netherlands` visible below date |
| 3 | Inspect HTML | `<p class="entry-location">` present with city and country |
**Automation:** grep for `.entry-location` and "Amsterdam" in curl output
---
### TC-1.2: Weather badge on entry page
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open http://localhost:8081/tracker/2026-06-17.entry | Entry page loads |
| 2 | Look at entry header | `⛅ Partly cloudy · 19°C` visible |
| 3 | Inspect HTML | `<p class="entry-weather">` present |
**Automation:** grep for `.entry-weather` and "Partly cloudy" and "19°C"
---
### TC-1.3: Location badge hidden when fields empty
| Step | Action | Expected Result |
|---|---|---|
| 1 | Create test entry with no location_city/location_country | — |
| 2 | Open that entry | No `📍` badge shown, no empty `<p>` rendered |
**Automation:** Check example entry before fields were added (not needed — fields are now set); create a second test entry without location
---
### TC-1.4: Weather badge hidden when fields empty
| Step | Action | Expected Result |
|---|---|---|
| 1 | Entry with no weather fields | No weather section in HTML |
**Automation:** grep for `entry-weather` in HTML — should only appear if value present
---
### TC-1.5: Hero image on tracker feed card
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open http://localhost:8081/tracker | Feed loads |
| 2 | Entry card for 2026-06-17 | No image shown (example entry has no photos) |
| 3 | Upload a photo to the entry via Admin media manager | — |
| 4 | Reload tracker | Hero image shows as 16:9 thumbnail |
**Manual verification required:** Photo upload requires browser Admin interaction
---
### TC-1.6: Location badge on tracker feed card
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open http://localhost:8081/tracker | Feed loads |
| 2 | Entry card | `📍 Amsterdam, Netherlands` visible |
**Automation:** grep feed HTML for `entry-location--card` and "Amsterdam"
---
### TC-1.7: Photo gallery renders on entry page (with photos)
| Step | Action | Expected Result |
|---|---|---|
| 1 | Upload 3 photos to the example entry via Admin | — |
| 2 | Open entry page | Gallery grid appears below entry body |
| 3 | Count thumbnails | 3 thumbnails in 2-col (mobile) / 3-col (desktop) grid |
| 4 | Click a thumbnail | Lightbox overlay opens with full-size image |
| 5 | Press Escape | Lightbox closes |
| 6 | Click left/right arrow buttons | Navigates between images |
| 7 | Click outside lightbox | Lightbox closes |
**Manual verification required:** Photo upload and interactive lightbox require browser
---
### TC-1.8: Post form has location and weather fields
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open http://localhost:8081/post (logged in) | Post form renders |
| 2 | Inspect form | `City` and `Country` text inputs present |
| 3 | Inspect form | `📍 Get Current Location` and `🌤 Get Weather` buttons present |
**Automation:** grep /post HTML for `location_city`, `location_country`, `get-location`, `get-weather`
---
### TC-1.9: Get Weather button fills fields
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open /post on phone | Post form loads |
| 2 | Tap "Get Current Location" | Lat/lng fields fill with coordinates |
| 3 | Tap "Get Weather" | Status shows "🌤 Weather set: [desc] · [temp]°C" |
| 4 | Submit form | New entry created with weather in frontmatter |
| 5 | Open entry in Admin | weather_temp_c and weather_desc fields populated |
**Manual verification required:** Geolocation and form submission require mobile browser
---
## Milestone 2 — Interactive Map
### TC-2.1: Map page loads
| Step | Action | Expected Result |
|---|---|---|
| 1 | GET http://localhost:8081/map | HTTP 200 |
| 2 | Inspect HTML | `<div id="trip-map">` present |
| 3 | Inspect HTML | Leaflet CSS and JS from CDN present |
**Automation:** curl + HTTP status check; grep for "trip-map" and "leaflet"
---
### TC-2.2: Entry with GPS appears in ENTRIES JSON
| Step | Action | Expected Result |
|---|---|---|
| 1 | curl http://localhost:8081/map | Map page HTML |
| 2 | grep for `var ENTRIES` | Array contains Amsterdam entry with lat 52.3676 |
| 3 | Check entry has title, date, url | All fields present |
**Automation:** grep output for ENTRIES and lat value
---
### TC-2.3: Map renders marker and route in browser
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open /map in browser | Map tiles load, marker visible |
| 2 | Click marker | Popup opens with "The Journey Begins" title and "Read entry →" link |
| 3 | Click "Read entry →" | Navigates to entry page |
**Manual verification required:** Leaflet rendering requires browser
---
### TC-2.4: Map navigation link in header
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open any page | Header shows Journal, Map, Stats nav links |
| 2 | Click Map | Navigates to /map |
**Automation:** grep base template output for "/map" nav link
---
### TC-2.5: Empty state (no GPS entries)
| Step | Action | Expected Result |
|---|---|---|
| 1 | Remove lat/lng from test entry temporarily | — |
| 2 | Visit /map | Map at world zoom, "No locations yet" message shown |
| 3 | Restore lat/lng | — |
**Manual verification required:** Requires temporarily editing entry
---
### TC-2.6: Map page is full-height on mobile
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open /map on mobile browser | Map fills screen below header |
| 2 | Pinch to zoom | Map zooms without page scrolling |
| 3 | Pan with finger | Map pans without page scrolling |
**Manual verification required:** Touch interaction requires physical device
---
## Milestone 3 — Statistics Page
### TC-3.1: Stats page loads
| Step | Action | Expected Result |
|---|---|---|
| 1 | GET http://localhost:8081/stats | HTTP 200 |
| 2 | Inspect HTML | Four stat blocks present |
**Automation:** curl + HTTP status + grep for "stat-block"
---
### TC-3.2: Days on the road count
| Step | Action | Expected Result |
|---|---|---|
| 1 | curl /stats | Page HTML |
| 2 | grep for "days" | Shows "1 day on the road" (entry date: 2026-06-17, today: 2026-06-18) |
**Automation:** grep stat-value output and compare to expected day count
---
### TC-3.3: Entries count
| Step | Action | Expected Result |
|---|---|---|
| 1 | curl /stats | grep for "entry posted" | Shows "1 entry posted" |
**Automation:** grep for "entry posted"
---
### TC-3.4: Countries visited
| Step | Action | Expected Result |
|---|---|---|
| 1 | curl /stats | grep for "Netherlands" | "Netherlands" appears in countries list |
| 2 | grep for "country visited" | Shows "1 country visited" |
**Automation:** grep output
---
### TC-3.5: Distance shows "—" for single GPS point
| Step | Action | Expected Result |
|---|---|---|
| 1 | curl /stats | grep for GPS_POINTS | One point in array |
| 2 | In browser, check stat-distance | Shows "—" (JS computes, needs browser) |
**Automation:** grep GPS_POINTS array length from page source
---
### TC-3.6: Stats navigation link
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open any page header | "Stats" link present in nav |
| 2 | Click Stats | Navigates to /stats |
**Automation:** grep any page HTML for "/stats" in nav
---
## Milestone 4 — Mini-map on Tracker Feed
### TC-4.1: Mini-map appears on tracker feed
| Step | Action | Expected Result |
|---|---|---|
| 1 | GET http://localhost:8081/tracker | HTTP 200 |
| 2 | grep for "feed-map" | Mini-map div present |
| 3 | grep for "FEED_ENTRIES" | JSON array with Amsterdam entry |
| 4 | grep for "View full map →" | Link to /map present |
**Automation:** curl + grep
---
### TC-4.2: Mini-map hidden when no GPS entries
| Step | Action | Expected Result |
|---|---|---|
| 1 | Remove lat/lng from example entry | — |
| 2 | curl /tracker | No "feed-map" div in output |
| 3 | Restore lat/lng | — |
**Manual verification:** Requires temporarily editing entry
---
### TC-4.3: Marker click navigates to entry (mobile)
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open /tracker on phone | Mini-map renders above entry list |
| 2 | Tap Amsterdam marker | Navigates to /tracker/2026-06-17.entry |
**Manual verification required:** Touch interaction requires browser
---
### TC-4.4: Entry list still visible below mini-map
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open /tracker | Mini-map shows, scroll down | Entry cards visible below map |
**Manual verification required:** Visual layout check
---
## Post Submission Flow
These scenarios cover the full round-trip: filling the form → saving → verifying values in the UI and on disk. Use the exact test values specified so that each assertion can be precise.
**Test data (use verbatim):**
| Field | Value |
|---|---|
| Title | `QA Test Entry` |
| Date & Time | `2026-06-18 10:00` |
| Content | `This is the QA test body. Second sentence for length.` |
| City | `Tokyo` |
| Country | `Japan` |
| Latitude | `35.689487` |
| Longitude | `139.691711` |
| Photos | none (keep simple for first run) |
**Expected slug:** `2026-06-18-1000-qa-test-entry`
**Expected folder:** `2026-06-18-1000-qa-test-entry.entry/`
**Expected URL:** `/tracker/2026-06-18-1000-qa-test-entry.entry`
The slug is built from `date(Y-m-d-Hi)` + title lowercased with `[^a-z0-9]+` replaced by hyphens.
---
### TC-P.1: Post form requires authentication
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open private/incognito tab (no session) | — |
| 2 | GET http://100.96.115.96:8081/post | Page loads at /post URL (no redirect) but renders the login form inline |
| 3 | Inspect page content | Login form fields (username, password) visible; post form fields absent |
**Automation:** curl /post without auth; assert `login-form-nonce` present AND `data[title]` absent
---
### TC-P.2: Post form renders all fields
| Step | Action | Expected Result |
|---|---|---|
| 1 | Log in at /login | Redirected to /tracker |
| 2 | Navigate to /post | Post form page loads (200) |
| 3 | Check form fields present | Title, Date & Time, description textarea, Photos upload |
| 4 | Check location fields | Latitude, Longitude, City, Country inputs visible |
| 5 | Check action buttons | `📍 Get Current Location` and `🌤 Get Weather` buttons visible |
| 6 | Check submit button | `Post Entry` button visible |
| 7 | Check date field default | Pre-filled with today's date and time (not blank) |
**Automation:** curl /post with auth; grep for `data[title]`, `data[lat]`, `data[location_city]`, `get-location`, `get-weather`
---
### TC-P.3: Required field validation
| Step | Action | Expected Result |
|---|---|---|
| 1 | Log in and open /post | Form loads |
| 2 | Leave Title blank, fill in only the description | — |
| 3 | Submit form | Page reloads with validation error on Title |
| 4 | Error message | Indicates title is required |
| 5 | Fill in Title, clear Description/Content, submit | Validation error on Content field |
| 6 | Confirm | No new entry file created in pages/01.tracker/ during failed submissions |
**Manual verification required:** Validation feedback requires browser
---
### TC-P.4: Successful post submission — all fields
| Step | Action | Expected Result |
|---|---|---|
| 1 | Log in and open /post | Form loads |
| 2 | Enter Title: `QA Test Entry` | — |
| 3 | Set Date to `2026-06-18 10:00` | — |
| 4 | Enter Content: `This is the QA test body. Second sentence for length.` | — |
| 5 | Enter City: `Tokyo`, Country: `Japan` | — |
| 6 | Enter Latitude: `35.689487`, Longitude: `139.691711` | — |
| 7 | Leave Photos empty | — |
| 8 | Click `Post Entry` | Form submits (POST to /post) |
| 9 | Observe result | Success message `Entry posted successfully!` shown on page |
| 10 | Form state | Form is reset / fields cleared |
**Manual verification required:** Form submission and success message require browser
---
### TC-P.5: Entry file created on disk with correct values
| Step | Action | Expected Result |
|---|---|---|
| 1 | After TC-P.4 completes | — |
| 2 | Check directory `user/pages/01.tracker/` | Folder `2026-06-18-1000-qa-test-entry.entry/` exists (add-page-by-form appends template name per `physical_template_name: true`) |
| 3 | Read `user/pages/01.tracker/2026-06-18-1000-qa-test-entry.entry/entry.md` | File exists |
| 4 | Verify frontmatter `title` | Equals `QA Test Entry` |
| 5 | Verify frontmatter `date` | Equals `2026-06-18 10:00` |
| 6 | Verify frontmatter `location_city` | Equals `Tokyo` |
| 7 | Verify frontmatter `location_country` | Equals `Japan` |
| 8 | Verify frontmatter `lat` | Equals `35.689487` |
| 9 | Verify frontmatter `lng` | Equals `139.691711` |
| 10 | Verify frontmatter `template` | Equals `entry` |
| 11 | Verify frontmatter `published` | Equals `true` |
| 12 | Verify page body | Contains `This is the QA test body. Second sentence for length.` |
**Automation:** Read file from filesystem; parse YAML frontmatter; assert each field value exactly
---
### TC-P.6: Entry appears in tracker feed
| Step | Action | Expected Result |
|---|---|---|
| 1 | BUG-001 fixed — no manual cache clear needed | — |
| 2 | GET http://100.96.115.96:8081/tracker | Page loads (200) |
| 3 | Entry card present | Card with title `QA Test Entry` visible |
| 4 | Date shown on card | `18 Jun 2026` |
| 5 | Location badge on card | `📍 Tokyo, Japan` visible |
| 6 | Entry card link | `href` points to `/tracker/2026-06-18-1000-qa-test-entry.entry` |
| 7 | Excerpt shown | Partial text of the body content visible |
**Automation:** curl /tracker; grep for "QA Test Entry", "18 Jun 2026", "Tokyo", "Japan", "/tracker/2026-06-18-1000-qa-test-entry.entry"
---
### TC-P.7: Entry detail page shows correct values
| Step | Action | Expected Result |
|---|---|---|
| 1 | GET http://100.96.115.96:8081/tracker/2026-06-18-1000-qa-test-entry.entry | Page loads (200) |
| 2 | Page title | `QA Test Entry` in `<h1>` |
| 3 | Date header | `Thursday, 18 June 2026` (or locale equivalent) |
| 4 | Location badge | `📍 Tokyo, Japan` |
| 5 | Body content | Full text `This is the QA test body. Second sentence for length.` rendered |
| 6 | No gallery | Photo gallery section absent (no photos were uploaded) |
| 7 | Back link | `← Back to journal` link present, points to /tracker |
**Automation:** curl /tracker/2026-06-18-1000-qa-test-entry.entry; grep for "QA Test Entry", "Tokyo", "Japan", "This is the QA test body", "Back to journal"
---
### TC-P.8: Entry appears on map and mini-map
| Step | Action | Expected Result |
|---|---|---|
| 1 | GET http://100.96.115.96:8081/tracker | Mini-map section visible |
| 2 | Inspect FEED_ENTRIES JSON | Contains entry with `lat: "35.689487"`, `lng: "139.691711"`, `title: "QA Test Entry"` |
| 3 | GET http://100.96.115.96:8081/map | Map page loads |
| 4 | Inspect ENTRIES JSON | Contains same entry |
**Automation:** curl /tracker and /map; grep FEED_ENTRIES and ENTRIES JSON for lat/lng values
---
### TC-P.9: Entry appears in stats
| Step | Action | Expected Result |
|---|---|---|
| 1 | GET http://100.96.115.96:8081/stats | Page loads (200) |
| 2 | Entries count | Shows `2` entries (existing test entry + new QA entry) |
| 3 | Countries list | `Japan` and `Netherlands` both listed |
**Automation:** curl /stats; grep entry count and country names
---
### TC-P.10: Two posts on the same day
| Step | Action | Expected Result |
|---|---|---|
| 1 | Submit a first post: date `2026-06-18 10:00`, title `Morning Update` | Success message shown |
| 2 | Submit a second post: date `2026-06-18 14:30`, title `Afternoon Update` | Success message shown |
| 3 | Check filesystem | Two separate folders exist: `2026-06-18-1000-morning-update.entry/` and `2026-06-18-1430-afternoon-update.entry/` |
| 4 | Visit /tracker | Both entries visible as separate cards |
**Note:** The slug encodes date + time + title, so same-day posts are fully supported as long as they have different times or titles. A true collision (same date, same time, same title) would silently fail — treat this as acceptable given solo use.
**Manual verification required:** Requires two browser submissions
---
## Cross-cutting Tests
### TC-X.1: Nav links present on all pages
| Page | Expected nav links |
|---|---|
| /tracker | Journal, Map, Stats |
| /map | Journal, Map, Stats |
| /stats | Journal, Map, Stats |
| /tracker/2026-06-17.entry | Journal, Map, Stats |
**Automation:** curl each page, grep for all three links
---
### TC-X.2: All pages return 200
| Page | Expected HTTP status |
|---|---|
| / (redirects to /tracker) | 200 or 302→200 |
| /tracker | 200 |
| /tracker/2026-06-17.entry | 200 |
| /map | 200 |
| /stats | 200 |
| /post | 200 (after login) or 302 (login redirect) |
**Automation:** curl HTTP status checks
---
### TC-X.3: Mobile touch targets ≥44px
| Element | Expected min height/width |
|---|---|
| Nav links | 44px height |
| Gallery thumbnails | 44px on shortest side |
| Lightbox close/prev/next buttons | 44px |
| Post form buttons | 44px height |
| "Get Location" button | 44px height |
| "Get Weather" button | 44px height |
**Manual verification required:** Inspect computed CSS or measure visually on device
---
### TC-X.4: No JS errors in browser console
| Page | Expected |
|---|---|
| /tracker | No console errors |
| /map | No console errors (may have tile 404s for tiles not in viewport — acceptable) |
| /stats | No console errors |
| /tracker/2026-06-17.entry | No console errors |
**Manual verification required:** Open browser DevTools
---
## Visual Design QA — Redesign Checklist
**Design spec:** `user/docs/design/design-spec.md`
**Implementation plan:** `docs/working/plans/2026-06-18-ui-redesign.md`
### Typography
- [ ] DM Serif Display loads for: entry titles, page headings (`h1`), stat numbers, site title
- [ ] DM Sans loads for: body text, nav links, labels, form fields, timestamps
- [ ] No fallback font (Georgia / system-sans) visible in place of custom fonts
- [ ] Body text font-size ≥ 16px (no iOS zoom on form focus)
### Colors
- [ ] Page background is warm paper (#F7F5F2), not pure white
- [ ] All links and CTAs use teal (#1F6B5A), not blue (#0066cc)
- [ ] Active nav link is teal and bold
- [ ] Map markers and route polylines are teal
### Header
- [ ] 3px teal border-top visible at top of header
- [ ] Site title renders in DM Serif Display ("into the east")
- [ ] Header sticks to top on scroll
- [ ] On 320px viewport: title and nav both visible without overlap
### Entry feed cards
- [ ] Cards with photos show full-bleed 16:9 image with rounded corners
- [ ] Date + location text overlay visible on gradient at bottom of photo
- [ ] Entry title below photo in DM Serif Display
- [ ] Subtle photo scale animation on hover (desktop)
- [ ] Cards without photos show date/location meta row above title
- [ ] "Read entry →" link is teal
### Single entry page
- [ ] If entry has photos: hero image spans full content width, max 480px tall
- [ ] Entry title in DM Serif Display at large size (~48px desktop)
- [ ] Thin border rule separates header from body text
- [ ] Body text at 18px (--text-md)
- [ ] "← Back to journal" footer link in teal
### Post form
- [ ] Lat/lng inputs NOT visible (hidden by CSS :has() selector)
- [ ] Inputs have rounded corners and correct border
- [ ] Focus ring on inputs is teal, not default browser blue
- [ ] "Post Entry" submit button is teal, full-width, ≥52px height
- [ ] After tapping "Get Location": status line shows "✓ Location captured · lat, lng" in teal
- [ ] After tapping "Get Weather": status line shows "✓ Weather set · desc · temp°C" in teal
- [ ] On error: status line shows in brick red, not teal
### Stats page
- [ ] Page heading "Trip Statistics" in DM Serif Display
- [ ] Stat numbers in DM Serif Display, teal color
- [ ] Stat cards on white background (not paper), with subtle shadow
- [ ] Labels uppercase, muted gray, small
### Map page
- [ ] Map fills viewport below header with no gap
- [ ] Map container height uses CSS variable (not hardcoded 61px)
- [ ] Markers are teal circles (not blue)
- [ ] Route polyline is teal
### Mobile (375px viewport)
- [ ] All pages scroll without horizontal overflow
- [ ] Header title and nav fit in one row
- [ ] Entry card photo fills full width
- [ ] Post form buttons are thumb-reachable (44px+ targets)
- [ ] Map page: map pans without page scrolling underneath (touch-action)
### Accessibility
- [ ] Focus ring visible on all interactive elements (keyboard navigation)
- [ ] With prefers-reduced-motion: no animations/transitions fire