Phase B: real data for the last fixture screens
Every remaining fixture-only surface now renders reconciled gitea data,
with the fixture kept only as the no-config demo fallback.
- lib/views/project-data.ts: ProjectData — the uniform input (reconciled
backlog + deps + timelines + calibration + workers + today) that AppShell
assembles once and every view builder consumes.
- lib/views/{issue-detail,gantt-view,deps-graph,standup-view,inbox-view}.ts:
pure builders, ProjectData → the fixture-shaped object each screen already
renders. Real signals only; honest degradation where a signal isn't derivable
(buffered p80 vs per-issue Monte Carlo; flat "idle in review"; no fabricated
inbox mentions/outages).
- Screens take an optional `data?` and fall back to the fixture; AppShell wires
the real view whenever the backlog is reconciled. Issue "blocks" chips and the
rail inbox badge now resolve from real data too.
- Dev-only rail surfaces (First run / States / Primitives) gated on
import.meta.env.DEV || demo — shown in dev + e2e, hidden in a packaged app.
Rail host label reflects the connected instance.
- Extended the live onboarding e2e to click Board → Gantt → Deps → Issue
sidecar → Standup → Inbox on real gitea data with a pageerror guard; 14
fixture e2e stay green, typecheck + prod build clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,11 @@ test.describe('live onboarding', () => {
|
||||
const win = await app.firstWindow()
|
||||
await win.waitForLoadState('domcontentloaded')
|
||||
|
||||
// Any uncaught render error in a real-data view builder must fail the test —
|
||||
// this is the "it actually works on real data" guarantee.
|
||||
const pageErrors: string[] = []
|
||||
win.on('pageerror', (e) => pageErrors.push(e.message))
|
||||
|
||||
// the connection gate, not the app
|
||||
await expect(win.getByText(/Connect your Gitea/)).toBeVisible({ timeout: 15000 })
|
||||
await win.getByPlaceholder('your-org').fill('christian')
|
||||
@@ -45,11 +50,35 @@ test.describe('live onboarding', () => {
|
||||
await win.getByPlaceholder(/gitea PAT/).fill(token!)
|
||||
await win.getByRole('button', { name: 'Connect' }).click()
|
||||
|
||||
// it validated + saved + reconciled into the real board — real issue titles prove it
|
||||
const rail = win.getByRole('navigation', { name: 'Primary' })
|
||||
const realTitle = /Model router|SQLite cache bootstrap|Purity\/rebuild/
|
||||
|
||||
// Board (real reconciled columns) — real issue titles prove the sync landed.
|
||||
await rail.getByRole('button', { name: 'The pot' }).click()
|
||||
await expect(win.getByText(/Model router|SQLite cache bootstrap|Purity\/rebuild/).first()).toBeVisible({ timeout: 30000 })
|
||||
await expect(win.getByText(realTitle).first()).toBeVisible({ timeout: 30000 })
|
||||
|
||||
// Gantt tab — scheduler-derived bars over the real open backlog.
|
||||
await win.getByRole('tab', { name: 'Gantt' }).click()
|
||||
await expect(win.getByText(realTitle).first()).toBeVisible()
|
||||
|
||||
// Dependencies tab — real dep graph. Just needs to render without throwing.
|
||||
await win.getByRole('tab', { name: 'Dependencies' }).click()
|
||||
await win.waitForTimeout(300)
|
||||
|
||||
// Issue drill-in — the machine-derived sidecar (lifecycle/forecast/deps) on a real issue.
|
||||
await win.getByRole('tab', { name: 'Board' }).click()
|
||||
await win.getByText(realTitle).first().click()
|
||||
await expect(win.getByText('Machine-derived')).toBeVisible()
|
||||
await win.getByRole('button', { name: 'Back' }).click()
|
||||
|
||||
// Standup + Inbox — scheduler plan / nag / inbox feed over real data.
|
||||
await rail.getByRole('button', { name: 'Standup' }).click()
|
||||
await expect(win.getByRole('heading', { name: 'Morning standup' })).toBeVisible()
|
||||
await rail.getByRole('button', { name: 'Inbox' }).click()
|
||||
await expect(win.getByRole('heading', { name: 'Inbox' })).toBeVisible()
|
||||
|
||||
await win.screenshot({ path: join(here, '.artifacts', 'screens', 'live-onboarding.png'), fullPage: true, animations: 'disabled' })
|
||||
expect(pageErrors, `uncaught render errors: ${pageErrors.join(' · ')}`).toEqual([])
|
||||
|
||||
await app.close()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user