Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions e2e/auth-helpers.ts
23 changes: 23 additions & 0 deletions e2e/auth.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { test, expect } from "@playwright/test"
import {
createAccount,
signIn,
signOut,
waitForEditorBoot,
} from "./auth-helpers"

test("auth flow: create account, sign out, sign in", async ({ page }) => {
let boot = await waitForEditorBoot(page)
expect(boot.ok).toBe(true)

let created = await createAccount(page)
expect(created.ok).toBe(true)
expect(created.signedIn).toBe(true)
expect(created.passphrase.trim().length).toBeGreaterThan(10)

let signedOut = await signOut(page)
expect(signedOut).toEqual({ ok: true, signedIn: false })

let signedIn = await signIn(page, { passphrase: created.passphrase })
expect(signedIn).toEqual({ ok: true, signedIn: true })
})
1 change: 1 addition & 0 deletions e2e/doc-helpers.ts
52 changes: 52 additions & 0 deletions e2e/doc.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { test, expect } from "@playwright/test"
import { waitForEditorBoot, createAccount } from "./auth-helpers"
import { create, readById, updateById, list, deleteById } from "./doc-helpers"

test("document CRUD helpers return JSON", async ({ page }) => {
await waitForEditorBoot(page)
await createAccount(page)

let before = await list(page)
expect(before.ok).toBe(true)

let created = await create(page, {
title: "CRUD JSON Doc",
tags: ["e2e", "json"],
path: "tests",
body: "create body",
})
expect(created.ok).toBe(true)
expect(created.id.length).toBeGreaterThan(10)

let read = await readById(page, { id: created.id })
expect(read.ok).toBe(true)
expect(read.document.id).toBe(created.id)
expect(read.document.title).toContain("CRUD JSON Doc")

let updated = await updateById(page, {
id: created.id,
title: "CRUD JSON Doc Updated",
body: "updated body",
tags: ["e2e", "updated"],
path: "tests/updated",
})
expect(updated.ok).toBe(true)
expect(updated.document.title).toContain("CRUD JSON Doc Updated")
expect(updated.document.content).toContain("updated body")

let filtered = await list(page, { search: "CRUD JSON Doc Updated" })
expect(filtered.ok).toBe(true)
expect(filtered.items.some(item => item.id === created.id)).toBe(true)

let deleted = await deleteById(page, { id: created.id })
expect(deleted).toEqual({
ok: true,
id: created.id,
spaceId: null,
deleted: true,
})

let after = await list(page)
expect(after.ok).toBe(true)
expect(after.count).toBe(before.count)
})
1 change: 1 addition & 0 deletions e2e/document-collab-helpers.ts
59 changes: 59 additions & 0 deletions e2e/document-collab.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { expect, test } from "@playwright/test"
import { createAccount, waitForEditorBoot } from "./auth-helpers"
import { create } from "./doc-helpers"
import {
acceptDocumentInvite,
createDocumentInvite,
listDocumentInvites,
revokeDocumentInvite,
} from "./document-collab-helpers"

test("document invite CRUD helpers return JSON", async ({ page }) => {
await waitForEditorBoot(page)
await createAccount(page)

let created = await create(page, {
title: "Doc Invite CRUD",
body: "content",
})

let invite = await createDocumentInvite(page, {
docId: created.id,
role: "writer",
})
expect(invite.ok).toBe(true)

let pending = await listDocumentInvites(page, {
docId: created.id,
})
expect(pending.ok).toBe(true)
expect(
pending.items.some(item => item.inviteGroupId === invite.inviteGroupId),
).toBe(true)

let revoked = await revokeDocumentInvite(page, {
docId: created.id,
inviteGroupId: invite.inviteGroupId ?? undefined,
})
expect(revoked.ok).toBe(true)
})

test("document invite accept helper returns JSON", async ({ page }) => {
await waitForEditorBoot(page)
await createAccount(page)

let created = await create(page, {
title: "Doc Invite Accept",
body: "acceptance",
})

let invite = await createDocumentInvite(page, {
docId: created.id,
role: "reader",
})

let accepted = await acceptDocumentInvite(page, { link: invite.link })
expect(accepted.ok).toBe(true)
expect(accepted.docId).toBe(created.id)
expect(accepted.url).toContain(`/app/doc/${created.id}`)
})
1 change: 1 addition & 0 deletions e2e/space-helpers.ts
73 changes: 73 additions & 0 deletions e2e/space.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { expect, test } from "@playwright/test"
import { createAccount, waitForEditorBoot } from "./auth-helpers"
import {
acceptSpaceInvite,
createSpace,
createSpaceInvite,
deleteSpaceById,
listSpaceInvites,
listSpaces,
readSpaceById,
revokeSpaceInvite,
updateSpaceById,
} from "./space-helpers"

test("space CRUD + invite helpers return JSON", async ({ page }) => {
await waitForEditorBoot(page)
await createAccount(page)

let created = await createSpace(page, { name: "E2E Space" })
expect(created.ok).toBe(true)

let listed = await listSpaces(page)
expect(listed.ok).toBe(true)
expect(listed.items.some(space => space.id === created.id)).toBe(true)

let read = await readSpaceById(page, { spaceId: created.id })
expect(read.ok).toBe(true)
expect(read.space.name).toBe("E2E Space")

let updated = await updateSpaceById(page, {
spaceId: created.id,
name: "E2E Space Updated",
})
expect(updated.ok).toBe(true)

let invite = await createSpaceInvite(page, {
spaceId: created.id,
role: "reader",
})
expect(invite.ok).toBe(true)
expect(invite.link).toContain("invite")

let pending = await listSpaceInvites(page, { spaceId: created.id })
expect(pending.ok).toBe(true)
expect(
pending.items.some(i => i.inviteGroupId === invite.inviteGroupId),
).toBe(true)

let revoked = await revokeSpaceInvite(page, {
spaceId: created.id,
inviteGroupId: invite.inviteGroupId ?? undefined,
})
expect(revoked.ok).toBe(true)

let removed = await deleteSpaceById(page, { spaceId: created.id })
expect(removed.ok).toBe(true)
})

test("space invite accept helper returns JSON", async ({ page }) => {
await waitForEditorBoot(page)
await createAccount(page)

let created = await createSpace(page, { name: "Invite Accept Space" })
let invite = await createSpaceInvite(page, {
spaceId: created.id,
role: "reader",
})

let accepted = await acceptSpaceInvite(page, { link: invite.link })
expect(accepted.ok).toBe(true)
expect(accepted.spaceId).toBe(created.id)
expect(accepted.url).toContain(`/app/spaces/${created.id}`)
})
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
"check:types": "astro check",
"check:format": "prettier --check .",
"check:test": "bun test src/editor src/lib/backup src/lib/documents src/lib/export src/lib/import src/lib/presentation src/lib/spaces && bun vitest run src/lib/theme-sanitize.test.ts",
"test:e2e": "playwright test",
"test:e2e:auth": "playwright test e2e/auth.spec.ts",
"test:e2e:spaces": "playwright test e2e/space.spec.ts",
"test:e2e:doc-collab": "playwright test e2e/document-collab.spec.ts",
"test:e2e:doc": "playwright test e2e/doc.spec.ts",
"test:e2e:headed": "playwright test --headed",
"preview": "astro preview",
"format": "prettier --write .",
"test": "vitest"
Expand Down Expand Up @@ -78,6 +84,7 @@
"@astrojs/vercel": "^9.0.4",
"@eslint/js": "^9.39.2",
"@happy-dom/global-registrator": "^20.3.7",
"@playwright/test": "^1.58.2",
"@types/dompurify": "^3.2.0",
"@types/node": "^24.10.9",
"@types/react": "^19.2.9",
Expand Down
32 changes: 32 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { defineConfig, devices } from "@playwright/test"

let port = Number.parseInt(process.env.PLAYWRIGHT_PORT ?? "4173", 10)
let baseUrl = `http://127.0.0.1:${port}`

export default defineConfig({
testDir: "./e2e",
timeout: 60_000,
expect: {
timeout: 10_000,
},
fullyParallel: true,
retries: process.env.CI ? 2 : 0,
reporter: "list",
use: {
baseURL: baseUrl,
trace: "on-first-retry",
permissions: ["clipboard-read", "clipboard-write"],
},
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
],
webServer: {
command: `sh -c 'if ! lsof -iTCP:4200 -sTCP:LISTEN >/dev/null 2>&1; then bunx jazz-run sync --in-memory & fi; bun run dev --host 127.0.0.1 --port ${port}'`,
url: `${baseUrl}/app`,
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},
})
Loading