|
| 1 | +# Post‑MVP – Future Issues Backlog |
| 2 | + |
| 3 | +This file collects pre‑scoped issue drafts you can copy/paste into new GitHub issues. Each draft includes labels, milestone, acceptance criteria, and a small test plan. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## Draft: Playwright E2E – Visibility‑based session refresh cooldown |
| 8 | + |
| 9 | +Title |
| 10 | +- E2E(auth): visibility‑based session refresh respected with 30s cooldown |
| 11 | + |
| 12 | +Milestone |
| 13 | +- v0.4.0 |
| 14 | + |
| 15 | +Labels |
| 16 | +- area:auth, e2e, type:test, roadmap |
| 17 | + |
| 18 | +Background |
| 19 | +- The app refreshes session state on `visibilitychange` when a tab becomes visible, provided: |
| 20 | + - no auth request is currently in flight, and |
| 21 | + - the last successful fetch was ≥ 30 seconds ago. |
| 22 | +- The cooldown logic lives in a pure helper `shouldRefreshOnVisibility(lastFetchedMs: number, nowMs: number, minIntervalMs = 30000)` and is exercised by unit tests. We want a browser‑level E2E to validate the real visibility lifecycle and interaction with the app shell. |
| 23 | + |
| 24 | +Goals |
| 25 | +- Confirm that switching a tab from hidden→visible triggers a session refresh only when past the 30s threshold. |
| 26 | +- Confirm that repeated visibility events within the 30s window do not trigger additional refreshes. |
| 27 | +- Confirm that a refresh is skipped when already in‑flight. |
| 28 | + |
| 29 | +Acceptance Criteria |
| 30 | +- Given an authenticated session and last refresh < 30s ago, when the tab becomes visible, then no network call to `/api/me` is made. |
| 31 | +- Given an authenticated session and last refresh ≥ 30s ago, when the tab becomes visible, then exactly one network call to `/api/me` is made and store `lastFetched` updates. |
| 32 | +- Given `refresh()` is currently in‑flight, when the tab becomes visible, then no additional `/api/me` call is made until the current one completes. |
| 33 | +- Test runs headless and passes in CI (GitHub Actions) with Playwright’s Chrome channel. |
| 34 | + |
| 35 | +Out of scope |
| 36 | +- Backend behavior beyond returning a static user JSON. |
| 37 | +- Full login UI flow; use API seeding or cookie injection for session state. |
| 38 | + |
| 39 | +Implementation Notes |
| 40 | +- Use Playwright test runner with `chromium`. |
| 41 | +- Seed an authenticated state by stubbing `/api/me` with a route handler (Playwright `page.route`) and setting a cookie if needed. |
| 42 | +- Control time via Playwright’s `page.addInitScript(() => { Date.now = () => FIXED; })` or by injecting a time shim. Alternatively, wait using `page.waitForTimeout(30000)` but prefer time control to keep tests fast. |
| 43 | +- Use the document visibility API to flip visibility: |
| 44 | + - Either open a second tab (Page 2) to cover the current tab, then bring Page 1 to front, or |
| 45 | + - Programmatically dispatch `visibilitychange` by toggling `document.hidden` via a stub (requires page init script to override the getter) and dispatching the event. |
| 46 | +- Spy on fetch calls by intercepting `/api/me` and counting invocations; assert using expect counters. |
| 47 | + |
| 48 | +Test Plan (Playwright pseudo‑code) |
| 49 | +```ts |
| 50 | +import { test, expect } from '@playwright/test'; |
| 51 | + |
| 52 | +const USER = { id: 'u1', email: 'a@b.com' }; |
| 53 | + |
| 54 | +function mockMe(page, countRef) { |
| 55 | + page.route('**/api/me', async route => { |
| 56 | + countRef.count++; |
| 57 | + await route.fulfill({ |
| 58 | + status: 200, |
| 59 | + contentType: 'application/json', |
| 60 | + body: JSON.stringify(USER), |
| 61 | + }); |
| 62 | + }); |
| 63 | +} |
| 64 | + |
| 65 | +test.describe('visibility refresh cooldown', () => { |
| 66 | + test('does not refresh within 30s window; refreshes after 30s', async ({ page }) => { |
| 67 | + const calls = { count: 0 }; |
| 68 | + await mockMe(page, calls); |
| 69 | + |
| 70 | + // Start app |
| 71 | + await page.goto('http://localhost:5173'); |
| 72 | + |
| 73 | + // Bootstrap fires once |
| 74 | + await expect.poll(() => calls.count).toBe(1); |
| 75 | + |
| 76 | + // Within cooldown: simulate visible event → no extra call |
| 77 | + await page.evaluate(() => document.dispatchEvent(new Event('visibilitychange'))); |
| 78 | + await page.waitForTimeout(200); // settle |
| 79 | + expect(calls.count).toBe(1); |
| 80 | + |
| 81 | + // Move time forward ≥30s and trigger again |
| 82 | + await page.addInitScript(() => { |
| 83 | + const start = Date.now(); |
| 84 | + Date.now = () => start + 31_000; |
| 85 | + }); |
| 86 | + await page.reload(); |
| 87 | + await expect.poll(() => calls.count).toBeGreaterThanOrEqual(2); |
| 88 | + }); |
| 89 | +}); |
| 90 | +``` |
| 91 | + |
| 92 | +Risks & Mitigations |
| 93 | +- Dispatching `visibilitychange` in real browsers may be gated—inject a visibility getter shim if needed or use multi‑page focus handoff. |
| 94 | +- Time manipulation must be consistent across app and test; prefer overriding `Date.now()` early via `addInitScript`. |
| 95 | + |
| 96 | +Estimation |
| 97 | +- Small (0.5–1d) including CI wiring. |
| 98 | + |
| 99 | +Definition of Done |
| 100 | +- Playwright test checked in under `e2e/` and passing locally and on CI. |
| 101 | +- Docs updated (testing section) with a short note on the E2E and how to run it. |
| 102 | +- Milestone v0.4.0 linked and labels applied. |
| 103 | + |
| 104 | +--- |
| 105 | + |
| 106 | +## Draft: Automate README “Changelog” badge to latest section on release |
| 107 | + |
| 108 | +Title |
| 109 | +- chore(tooling): auto‑update README “Changelog” badge to latest section on release |
| 110 | + |
| 111 | +Milestone |
| 112 | +- v0.3.1 (tooling) |
| 113 | + |
| 114 | +Labels |
| 115 | +- area:tooling, type:chore, docs, release |
| 116 | + |
| 117 | +Background |
| 118 | +- README currently includes a static badge linking directly to the 0.3.0 section of `CHANGELOG.md`. After each release we must update this anchor manually. We can automate this as part of the release flow so the badge always points at the newest version section. |
| 119 | + |
| 120 | +Goals |
| 121 | +- Ensure the README “Changelog” badge link targets the most recent version section in `CHANGELOG.md` immediately after a release is cut/tagged. |
| 122 | + |
| 123 | +Approach (pick one) |
| 124 | +1) Script integration: |
| 125 | + - Enhance `scripts/release.mjs` to compute the anchor for the new section header `## [x.y.z] - YYYY-MM-DD` using GitHub’s slug rules (lowercase, strip punctuation like brackets/dots, spaces→`-`). |
| 126 | + - Update or insert the badge link in `README.md` to `CHANGELOG.md#<computed-anchor>` as part of the release commit. |
| 127 | +2) GitHub Action: |
| 128 | + - On `release` (published), run a tiny Node step that reads `CHANGELOG.md`, finds the latest `## [x.y.z] - YYYY-MM-DD` header, computes the anchor, updates README, and opens a PR (or pushes with a workflow token). |
| 129 | +3) Fallback (if anchor calc is brittle): |
| 130 | + - Link badge to the Releases page or to `[Unreleased]` and keep the deep link optional. Prefer (1) or (2) for true deep‑linking. |
| 131 | + |
| 132 | +Acceptance Criteria |
| 133 | +- After running the release flow for a new version, README’s “Changelog” badge points to the new section anchor (e.g., `CHANGELOG.md#040---2025-11-15`). |
| 134 | +- No manual edits required post‑release; changes are part of the release commit or an automated PR. |
| 135 | +- If the badge is missing, the automation inserts it below the Release badge. |
| 136 | + |
| 137 | +Test Plan |
| 138 | +- Dry run locally by pretending to cut a test version (e.g., bump to `0.3.1` in a throwaway branch) and confirm README link updates to the computed anchor. |
| 139 | +- Validate anchor by clicking through in GitHub UI. |
| 140 | + |
| 141 | +Risks & Mitigations |
| 142 | +- GitHub slug rules nuance: prefer deriving the anchor by reading the exact header text and applying the same slug transform used for headings (remove `[]()`, replace spaces with `-`, drop punctuation like `.`). Add a small unit test for the slug helper. |
| 143 | +- Workflow token push permissions: if using an Action, use a PAT or set `permissions: contents: write` on the workflow. |
| 144 | + |
| 145 | +Definition of Done |
| 146 | +- Automation implemented via script or Action. |
| 147 | +- README badge reliably targets the latest CHANGELOG section after release. |
| 148 | +- Documented in `docs/developer-workflow-checklist.md` under the Release steps. |
0 commit comments