Skip to content

Commit 219c524

Browse files
committed
feat(new-deepnotes): Playwright demo session E2E, CI migrate and dev.vars, plan progress
1 parent 78d6ce5 commit 219c524

9 files changed

Lines changed: 157 additions & 9 deletions

File tree

.github/workflows/new-deepnotes-ci.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ jobs:
5151
- name: Install
5252
run: pnpm install --frozen-lockfile
5353

54+
- name: API e2e vars (Wrangler)
55+
run: cp e2e/dev.vars.ci apps/api-worker/.dev.vars
56+
57+
- name: Migrate Postgres (app DB for worker + e2e)
58+
run: pnpm db:migrate
59+
5460
- name: Lint
5561
run: pnpm lint
5662

@@ -65,3 +71,9 @@ jobs:
6571

6672
- name: Build
6773
run: pnpm build
74+
75+
- name: Install Playwright (Chromium)
76+
run: pnpm --filter @deepnotes/web exec playwright install chromium --with-deps
77+
78+
- name: E2E (Playwright)
79+
run: pnpm test:e2e

new-deepnotes/PLAN_PROGRESS.md

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Checklist for [docs/RESTART_PLAN.md](../docs/RESTART_PLAN.md). **Procedure-level map:** [docs/TRPC_REST_MAP.md](./docs/TRPC_REST_MAP.md) (legacy tRPC + app-server WebSocket rows are marked **implemented** or **removed**).
44

5-
**Last reviewed:** 2026-04-27
5+
**Last reviewed:** 2026-04-28
66

77
---
88

@@ -14,7 +14,7 @@ Checklist for [docs/RESTART_PLAN.md](../docs/RESTART_PLAN.md). **Procedure-level
1414
| **1** — Legacy repo hygiene | **Optional** | Only if still editing the old monorepo. |
1515
| **2** — Repo bootstrap | **Done** | Turbo, CI, template DB tests, deploy notes. |
1616
| **3** — REST + Drizzle | **In progress** | All **HTTP** items in TRPC_REST_MAP through **slice 10** (`…/collab-updates`), **Stripe**, **2FA**, membership, crypto bootstrap routes. **Not started:** **live Yjs collab WebSocket** and **realtime** (legacy `realtime-server` / msgpackr — separate from collab). |
17-
| **4** — Client MVP | **In progress** | Auth, register (E2EE), pages list, Tiptap+Yjs+REST collab, groups + invites/join E2EE, notifications (list/read; **no** body decrypt), MSW contract smoke, ESLint import walls. **Next:** Playwright (or broader MSW) for cookies; optional demo keyring for CI. |
17+
| **4** — Client MVP | **In progress** | Auth, register (E2EE), pages list, Tiptap+Yjs+REST collab, groups + invites/join E2EE, notifications (list/read; **no** body decrypt), MSW contract smoke, ESLint import walls. **Done:** Playwright smoke for **demo** session (httpOnly tokens, `loggedIn` hint, reload → bootstrap/refresh). **Next:** password-login E2E (registered user fixture or flow); optional demo keyring assertions; marketing `vite-ssg` per plan. |
1818
| **5** — Cutover | **Not started** | Canary, retire `/trpc` when safe. |
1919

2020
---
@@ -62,9 +62,20 @@ Cross-check [TRPC_REST_MAP.md](./docs/TRPC_REST_MAP.md): **sessions**, **users.a
6262
- [x] `/groups`, `/groups/:id`, invite landing `/invite`, join `/join`, membership crypto
6363
- [x] `/notifications` (ciphertext not decrypted in UI)
6464
- [x] MSW: `GET /api/health`, `GET /api/users/me`; ESLint `no-restricted-imports` on `src/**/*.ts`
65-
- [ ] Playwright (or equivalent) E2E for httpOnly session
65+
- [x] Playwright: demo session against real `wrangler dev` + Vite (see **E2E / Playwright** below)
66+
- [ ] Password-login (or registered-user) Playwright path; optional/crypto assertions beyond cookies
6667
- [ ] Capacitor / Tauri after web MVP
6768

69+
### E2E / Playwright (detail)
70+
71+
| Item | Detail |
72+
|------|--------|
73+
| **Spec** | [`apps/web/e2e/session.spec.ts`](./apps/web/e2e/session.spec.ts) — “Try demo”, assert `accessToken` / `refreshToken` **not** in `document.cookie`, `loggedIn` hint present, **reload** then still “Signed in” (refresh + `/me`). |
74+
| **Config** | [`apps/web/playwright.config.ts`](./apps/web/playwright.config.ts)`webServer`: `pnpm --filter @deepnotes/api-worker dev` (waits on `GET /api/health`) + `pnpm --filter @deepnotes/web dev` on `127.0.0.1:5174`. |
75+
| **CI secrets file** | [`e2e/dev.vars.ci`](./e2e/dev.vars.ci) — copied to `apps/api-worker/.dev.vars` in GitHub Actions (non-production test-only values, same shape as `account-flows.integration.test.ts`). |
76+
| **CI DB** | Job runs `pnpm db:migrate` on the service Postgres **before** lint/test so the worker’s Hyperdrive DB matches the session integration template schema. |
77+
| **Local** | Docker Compose Postgres on **5433** (matches `wrangler.toml` `localConnectionString`), `pnpm db:migrate`, copy `e2e/dev.vars.ci``apps/api-worker/.dev.vars`, then `pnpm test:e2e` from repo root. |
78+
6879
---
6980

7081
## Phase 2 & 5
@@ -78,18 +89,19 @@ Cross-check [TRPC_REST_MAP.md](./docs/TRPC_REST_MAP.md): **sessions**, **users.a
7889
## Tests & tooling (maintenance)
7990

8091
| Package | What runs | Notes |
81-
|---------|-----------|--------|
92+
|---------|-----------|-------|
8293
| `@deepnotes/session` | **`account-flows.integration.test.ts`****20** `it()` when `DATABASE_URL` + admin URL set; clones template DB | Account, 2FA, groups/pages, prefs, slices 4–10, membership, collab REST |
8394
| `@deepnotes/db` | `template-db.test.ts`**6** FK/clone cases | |
8495
| `@deepnotes/api` | `openapi.test.ts`, `schemas/users.test.ts`, … | |
8596
| `@deepnotes/api-worker` | **`index.test.ts`****71** route × 503 matrix + **1** extra `email-verification/confirm` 503 | Totals **72** env-missing smoke cases |
86-
| `@deepnotes/web` | Vitest + happy-dom; composable/API tests | Extend MSW/login matrix; Playwright later |
97+
| `@deepnotes/web` | Vitest + happy-dom; composable/API tests | MSW contracts; extend login matrix |
98+
| **Playwright** | `pnpm test:e2e` (root) → `@deepnotes/web` **1** spec (demo cookies + reload) | Needs migrated DB + `.dev.vars`; CI installs Chromium only |
8799

88100
Run session integration (from repo root):
89101
`pnpm --filter @deepnotes/session exec vitest run src/account-flows.integration.test.ts`
90102
(requires `new-deepnotes/.env` with DB URLs as in package README.)
91103

92-
**Follow-ups:** Redis failed-login integration; expired refresh JWT; optional OpenAPI snapshot CI drift check.
104+
**Follow-ups:** Redis failed-login integration; expired refresh JWT; optional OpenAPI snapshot CI drift check; second Playwright spec for email/password registration + login.
93105

94106
---
95107

@@ -102,14 +114,15 @@ Run session integration (from repo root):
102114
- [ ] Stripe (and other high-risk paths): deeper automated coverage where secrets allow.
103115
- [ ] Cloudflare staging: Hyperdrive + Postgres + Redis + chosen WS topology load-tested.
104116
- [x] Web: Vitest in CI; restricted imports from `apps/web` TS.
105-
- [ ] E2E smoke (cookies) before calling client MVP “done”.
117+
- [ ] E2E smoke **complete for client MVP:** demo path [x]; password/session edge cases [ ].
106118

107119
---
108120

109121
## Short log (newest first)
110122

111123
| Date | Change |
112124
|------|--------|
125+
| 2026-04-28 | Playwright demo E2E (`session.spec.ts`), `e2e/dev.vars.ci`, CI: migrate app DB, copy `.dev.vars`, Chromium + `pnpm test:e2e`; root `test:e2e`; tsconfig.node includes playwright + e2e. |
113126
| 2026-04-27 | Compacted PLAN_PROGRESS; verified legacy map vs `TRPC_REST_MAP` — HTTP + listed WS flows migrated; added **realtime** vs **collab WS** distinction, optional gaps (public page list, vite-ssg, scheduler). Corrected counts: **20** session integration `it()`, **72** worker 503 smokes (71-route matrix + confirm). |
114127
| 2026-04-27 | MSW contract tests + ESLint restricted imports on web `*.ts`. |
115128
| 2026-04-27 | Tiptap + Yjs editor; invite/join E2EE + crypto bootstrap API; members detail UI; notifications thin SPA; groups overview; collab REST + `passwordSalt`; Stripe. |
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { expect, test } from "@playwright/test";
2+
3+
test.describe("Demo session cookies (httpOnly + bootstrap)", () => {
4+
test("demo login stays signed in after reload; tokens not in document.cookie", async ({
5+
page,
6+
}) => {
7+
await page.goto("/login");
8+
await page.getByRole("button", { name: "Try demo" }).click();
9+
10+
await expect(page.getByRole("heading", { name: "Signed in" })).toBeVisible({
11+
timeout: 60_000,
12+
});
13+
14+
const cookieReport = await page.evaluate(() => ({
15+
documentCookie: document.cookie,
16+
hasAccessInDoc: document.cookie.includes("accessToken"),
17+
hasRefreshInDoc: document.cookie.includes("refreshToken"),
18+
hasLoggedInHint: document.cookie.includes("loggedIn"),
19+
}));
20+
21+
expect(cookieReport.hasAccessInDoc).toBe(false);
22+
expect(cookieReport.hasRefreshInDoc).toBe(false);
23+
expect(cookieReport.hasLoggedInHint).toBe(true);
24+
25+
await page.reload();
26+
await expect(page.getByRole("heading", { name: "Signed in" })).toBeVisible({
27+
timeout: 60_000,
28+
});
29+
});
30+
});

new-deepnotes/apps/web/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"dev": "vite",
1010
"lint": "eslint vite.config.ts \"src/**/*.ts\"",
1111
"test": "vitest run",
12+
"test:e2e": "playwright test",
1213
"typecheck": "vue-tsc --noEmit -p tsconfig.app.json",
1314
"preview": "vite preview"
1415
},
@@ -36,6 +37,7 @@
3637
"yjs": "^13.6.30"
3738
},
3839
"devDependencies": {
40+
"@playwright/test": "^1.52.0",
3941
"@types/node": "^22.14.1",
4042
"@vitejs/plugin-vue": "^5.2.3",
4143
"@vue/test-utils": "^2.4.6",
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import path from "node:path";
2+
import { fileURLToPath } from "node:url";
3+
4+
import { defineConfig, devices } from "@playwright/test";
5+
6+
const appDir = path.dirname(fileURLToPath(import.meta.url));
7+
/** Monorepo root `new-deepnotes/` (parent of `apps/`). */
8+
const workspaceRoot = path.resolve(appDir, "..", "..");
9+
10+
export default defineConfig({
11+
testDir: "./e2e",
12+
fullyParallel: true,
13+
forbidOnly: !!process.env.CI,
14+
retries: process.env.CI ? 1 : 0,
15+
workers: process.env.CI ? 1 : undefined,
16+
reporter: "list",
17+
use: {
18+
baseURL: "http://127.0.0.1:5174",
19+
trace: "on-first-retry",
20+
...devices["Desktop Chrome"],
21+
},
22+
webServer: [
23+
{
24+
command: "pnpm --filter @deepnotes/api-worker dev",
25+
cwd: workspaceRoot,
26+
url: "http://127.0.0.1:8787/api/health",
27+
timeout: 180_000,
28+
reuseExistingServer: !process.env.CI,
29+
},
30+
{
31+
command:
32+
"pnpm --filter @deepnotes/web dev -- --host 127.0.0.1 --port 5174 --strictPort",
33+
cwd: workspaceRoot,
34+
url: "http://127.0.0.1:5174/",
35+
timeout: 180_000,
36+
reuseExistingServer: !process.env.CI,
37+
},
38+
],
39+
});
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"compilerOptions": {
33
"target": "ES2022",
4-
"lib": ["ES2023"],
4+
"lib": ["ES2023", "DOM"],
55
"module": "ESNext",
66
"moduleResolution": "Bundler",
77
"strict": true,
88
"skipLibCheck": true,
99
"noEmit": true,
1010
"verbatimModuleSyntax": true
1111
},
12-
"include": ["vite.config.ts"]
12+
"include": ["vite.config.ts", "playwright.config.ts", "e2e/**/*.ts"]
1313
}

new-deepnotes/e2e/dev.vars.ci

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Non-secret values for CI and local e2e only (matches @deepnotes/session integration test shape).
2+
# Copy to apps/api-worker/.dev.vars before `pnpm test:e2e`, or let CI copy it.
3+
ACCESS_SECRET=test-access-secret-min-32-chars-long!!
4+
REFRESH_SECRET=test-refresh-secret-min-32-chars-long!!
5+
USER_EMAIL_SECRET=test-user-email-secret-hmac-key!!
6+
USER_EMAIL_ENCRYPTION_KEY=AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=
7+
USER_REHASHED_LOGIN_HASH_ENCRYPTION_KEY=AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI=
8+
USER_AUTHENTICATOR_SECRET_ENCRYPTION_KEY=AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM=
9+
USER_RECOVERY_CODES_ENCRYPTION_KEY=BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ=
10+
GROUP_REHASHED_PASSWORD_HASH_ENCRYPTION_KEY=BQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU=
11+
DEV=true
12+
SEND_EMAILS=false
13+
PUBLIC_APP_URL=http://127.0.0.1:5174

new-deepnotes/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"lint": "turbo run lint",
1313
"typecheck": "turbo run typecheck",
1414
"test": "turbo run test",
15+
"test:e2e": "pnpm --filter @deepnotes/web run test:e2e",
1516
"db:generate": "pnpm --filter @deepnotes/db exec drizzle-kit generate",
1617
"db:migrate": "pnpm --filter @deepnotes/db exec drizzle-kit migrate",
1718
"db:studio": "pnpm --filter @deepnotes/db exec drizzle-kit studio",

new-deepnotes/pnpm-lock.yaml

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)