feat: phase 1 public site skeleton — layout, routes, CSS, logo pipeline
Ship base Jinja layout (header/nav/main/footer with skip link and aria-current), mobile-first single-file CSS using the ROADMAP palette tokens, and four public routes: /, /about, /contact, /shop. Blog index renders via a stable PostService.list_published() stub returning [] — Phase 2 only swaps the body. About is static placeholder copy, /contact ships an inert form plus a mailto: link driven by ADMIN_CONTACT_EMAIL, /shop shows a "Coming soon" card. Adds a Pillow-based scripts/generate_static_assets.py producing resized logo PNG + WebP, multi-size favicon.ico, and a 180x180 apple-touch-icon on a cream background. Outputs committed for a reproducible build. Also ship docs/MANUAL_TESTING.md with per-route / responsive / a11y / static- asset checklists, and mark Phase 1 complete in docs/ROADMAP.md.
This commit is contained in:
90
docs/MANUAL_TESTING.md
Normal file
90
docs/MANUAL_TESTING.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Manual Testing Checklist
|
||||
|
||||
Living document. Each phase appends its own section — do not delete older
|
||||
sections when the code behind them changes; mark items as superseded
|
||||
instead so the audit trail stays intact.
|
||||
|
||||
Run the site locally before walking through the list:
|
||||
|
||||
```bash
|
||||
source venv/bin/activate
|
||||
uvicorn app.main:app --reload
|
||||
```
|
||||
|
||||
Then open `http://127.0.0.1:8000/` in a real browser (not just curl).
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — Public Site Skeleton
|
||||
|
||||
### Home (`/`)
|
||||
|
||||
- [ ] Page returns 200 and renders without console errors.
|
||||
- [ ] Header shows the Chicken Babies R Us logo at ~48px tall.
|
||||
- [ ] `<img>` `alt` attribute reads **Chicken Babies R Us**.
|
||||
- [ ] Nav items appear in order: Home · About · Contact · Shop.
|
||||
- [ ] "Home" is visibly the active nav link and carries `aria-current="page"`.
|
||||
- [ ] "Shop" nav link is visually muted (lower contrast) but still clickable.
|
||||
- [ ] Page intro ("Welcome to Chicken Babies R Us") is present.
|
||||
- [ ] With no posts in the DB, the empty-state reads **"No posts yet — check back soon!"**.
|
||||
- [ ] Footer shows "Chicken Babies R Us · Morrison, Tennessee".
|
||||
- [ ] No street address is visible anywhere on the page (CLAUDE.md constraint).
|
||||
|
||||
### About (`/about`)
|
||||
|
||||
- [ ] Page returns 200 and renders without console errors.
|
||||
- [ ] H1 reads **"About the farm"**.
|
||||
- [ ] Copy mentions Morrison, Tennessee by name.
|
||||
- [ ] Copy name-checks Head Hen.
|
||||
- [ ] No street address appears anywhere.
|
||||
- [ ] Nav marks "About" as active (`aria-current="page"`).
|
||||
|
||||
### Contact (`/contact`)
|
||||
|
||||
- [ ] Page returns 200 and renders without console errors.
|
||||
- [ ] H1 reads **"Get in touch"**.
|
||||
- [ ] When `ADMIN_CONTACT_EMAIL` is set, a `mailto:` link renders above the form.
|
||||
- [ ] When `ADMIN_CONTACT_EMAIL` is unset, the muted placeholder sentence appears and no `mailto:` link renders.
|
||||
- [ ] The note **"Secure contact form coming soon"** is visible.
|
||||
- [ ] Form fields (name, email, message) are visually disabled and cannot be typed into.
|
||||
- [ ] "Send message" button is visually disabled.
|
||||
- [ ] Form has no `method="POST"` attribute (view source).
|
||||
- [ ] Nav marks "Contact" as active.
|
||||
|
||||
### Shop (`/shop`)
|
||||
|
||||
- [ ] Page returns 200 and renders without console errors.
|
||||
- [ ] H1 reads **"Shop"**.
|
||||
- [ ] "Coming soon" card is visible with mention of eggs, chicks, and waterfowl.
|
||||
- [ ] Nav marks "Shop" as active.
|
||||
|
||||
### Responsive
|
||||
|
||||
Use the browser devtools responsive toolbar.
|
||||
|
||||
- [ ] **360 × 800 (mobile):** nav collapses behind a hamburger toggle; toggle opens/closes on click; logo remains legible; no horizontal scroll.
|
||||
- [ ] **768 × 1024 (tablet):** nav appears inline; layout uses full container width; no horizontal scroll.
|
||||
- [ ] **1280 × 800 (desktop):** content capped at `--max-width` (68rem ≈ 1088px); generous whitespace either side.
|
||||
|
||||
### Accessibility
|
||||
|
||||
- [ ] Tab-key order from top of page: skip link → logo → nav links → main content.
|
||||
- [ ] Pressing **Tab** from a cold page load reveals the skip link in the top-left corner.
|
||||
- [ ] Activating the skip link jumps focus into `<main>`.
|
||||
- [ ] Logo has a non-empty `alt` attribute ("Chicken Babies R Us").
|
||||
- [ ] Navigating with a screen reader announces each landmark (`header`, `nav`, `main`, `footer`).
|
||||
- [ ] Spot-check color contrast of `--c-ink` (#2B3A42) on `--c-cream` (#FAF3E7) — should be comfortably above WCAG AA for body text.
|
||||
|
||||
### Assets
|
||||
|
||||
- [ ] `/static/img/logo.png` loads and is roughly 256px tall.
|
||||
- [ ] `/static/img/logo.webp` loads with content-type `image/webp`.
|
||||
- [ ] `/static/img/favicon.ico` is requested by the browser and returns 200.
|
||||
- [ ] `/static/img/apple-touch-icon.png` is 180×180 and has a cream (#FAF3E7) background.
|
||||
|
||||
### Ops smoke
|
||||
|
||||
- [ ] `pytest -q` passes locally.
|
||||
- [ ] `python -c "from app.main import app"` exits cleanly.
|
||||
- [ ] `python scripts/generate_static_assets.py` regenerates the four asset files without error.
|
||||
- [ ] `docker compose config` still parses cleanly.
|
||||
@@ -37,13 +37,44 @@ High-level phased plan. Each phase ends in a mergeable `dev` state and a passing
|
||||
- **Verification run:** `python -c "from app.main import app"` ✓ · `pytest -q` 1 passed ✓ · `curl /healthz` returned both the default `"unknown"` payload and the real commit SHA when `GIT_COMMIT_SHA=$(git rev-parse HEAD)` was set ✓ · `docker compose config` exit 0 ✓.
|
||||
- **Branch:** built on `chore/phase-0-foundation` off `dev`; merged `--no-ff` into `dev` on completion. Not pushed.
|
||||
|
||||
## Phase 1 — Public Site Skeleton
|
||||
## Phase 1 — Public Site Skeleton ✅
|
||||
|
||||
- Base Jinja layout: header with logo, nav (Home · About · Contact · Shop (disabled)), footer.
|
||||
- Mobile-first responsive CSS, no JS framework. CSS custom properties from the palette below.
|
||||
- Routes: `/`, `/about`, `/contact`, `/shop` (shop shows "Coming soon" card, no form).
|
||||
- `/` renders the blog index from DB (empty list is acceptable this phase).
|
||||
- Manual test checklist → `docs/MANUAL_TESTING.md`.
|
||||
**Completed:** 2026-04-21
|
||||
|
||||
**Summary:** Shipped the public brochure site: base Jinja layout with logo + nav + footer, mobile-first single-file CSS using the ROADMAP palette, and four public routes (`/`, `/about`, `/contact`, `/shop`). Blog index renders via a service stub returning `[]`; Phase 2 swaps the body for SQLite without touching the route.
|
||||
|
||||
**Key files:**
|
||||
- `app/models/posts.py` — `PostSummary` `@dataclass(frozen=True)` with `slug/title/published_at/excerpt` — list-view projection used by the homepage; richer `Post` arrives in Phase 2 alongside.
|
||||
- `app/services/posts.py` — `PostService.list_published(limit=20) -> list[PostSummary]` stub returning `[]`; `get_post_service()` DI helper (Phase 2 keeps the signature, swaps the body).
|
||||
- `app/routes/public.py` — `APIRouter` with `GET /`, `/about`, `/contact`, `/shop`; pulls templates off `app.state.templates` via `get_templates()` DI helper.
|
||||
- `app/templates/public/base.html` — layout: skip link, `<header>`/`<nav>`/`<main>`/`<footer>`, `aria-current` on active nav item, `<picture>` logo (WebP + PNG fallback), favicon/apple-touch-icon links, mobile nav toggle via plain `addEventListener` script.
|
||||
- `app/templates/public/home.html` — blog index; loops `_post_card.html` or renders "No posts yet — check back soon!" on empty list.
|
||||
- `app/templates/public/about.html` — static placeholder copy (Head Hen rewrites via Phase 4 admin).
|
||||
- `app/templates/public/contact.html` — inert form: all inputs `disabled`, no `method="POST"`, `action=""`, shows `mailto:` link only if `settings.admin_contact_email` is truthy.
|
||||
- `app/templates/public/shop.html` — "Coming soon" card teasing eggs / chicks / waterfowl.
|
||||
- `app/templates/public/partials/_post_card.html` — single post-card partial.
|
||||
- `app/static/css/site.css` — single stylesheet: reset, `:root` palette tokens (`--c-sky`, `--c-sky-deep`, `--c-cream`, `--c-wheat`, `--c-ink`, `--c-leaf`) + spacing/radius scale, system font stacks, components, one 48rem breakpoint.
|
||||
- `app/static/img/logo.png` (573×256 RGBA), `logo.webp` (q=82, method=6), `favicon.ico` (16/32/48), `apple-touch-icon.png` (180×180 on `#FAF3E7`).
|
||||
- `scripts/generate_static_assets.py` — Pillow CLI `StaticAssetBuilder` that regenerates the four image assets from `Logo/chicken babies r us.png`; committed for reproducibility.
|
||||
- `docs/MANUAL_TESTING.md` — per-route + responsive (360/768/1280px) + a11y + static-assets checklist.
|
||||
- `tests/test_public_routes.py` — 7 tests (4 parametrized route smokes + empty-state copy + logo path + `aria-current`).
|
||||
- `app/main.py` — modified: `StaticFiles` mount at `/static`, `Jinja2Templates` instantiated once onto `app.state.templates`, `public_router` included; `create_app()` stays idempotent.
|
||||
|
||||
**Endpoints created:**
|
||||
- `GET /` — blog index (empty-state message until Phase 2 seeds content).
|
||||
- `GET /about` — static About page.
|
||||
- `GET /contact` — inert form + optional `mailto:` from `ADMIN_CONTACT_EMAIL`. Phase 5 replaces with a working POST.
|
||||
- `GET /shop` — "Coming soon" card.
|
||||
- `Mount /static` — `StaticFiles` serving `app/static/{css,img}` (and `fonts/` later if ever needed).
|
||||
|
||||
**Key details:**
|
||||
- **Stable seam for Phase 2:** `PostService.list_published()` + `PostSummary` names are fixed contracts. Phase 2 only changes the method body to hit SQLite.
|
||||
- **No DB, no CSRF, no CSP, no auth, no contact POST.** Scope held strictly to the roadmap's Phase 1 bullets.
|
||||
- **Templates live under `app/templates/public/`** to reserve `app/templates/admin/` and `app/templates/emails/` for Phases 3–5.
|
||||
- **Logo delivery:** `<picture><source type="image/webp"><img alt="Chicken Babies R Us" height="48"></picture>` — modern browsers pull the WebP, older ones fall back to PNG.
|
||||
- **Address still not rendered.** Only city + state ("Morrison, Tennessee") appear per CLAUDE.md's "address is intentionally not displayed" wording.
|
||||
- **No new packages.** Pillow / Jinja2 / Starlette StaticFiles were already in `requirements.txt` from Phase 0.
|
||||
- **Verification run:** `python -c "from app.main import app"` ✓ · `pytest -q` 8 passed ✓ · uvicorn smoke: `/`, `/about`, `/contact`, `/shop`, `/healthz`, `/static/css/site.css`, `/static/img/logo.webp` all 200 with correct content-types ✓ · homepage body contains "No posts yet" + logo paths ✓ · contact page has `disabled` inputs and no `method` attribute ✓ · `docker compose config` exit 0 ✓.
|
||||
|
||||
## Phase 2 — Content Model + Cache
|
||||
|
||||
|
||||
Reference in New Issue
Block a user