Make the e2e suite boot, and fix the specs that outlived their UI (#403)

Co-authored-by: Coracle-Bot <npub1klq6260@nostr.local>
This commit is contained in:
Coracle-Bot 2026-09-02 22:21:18 +00:00 committed by hodlbod
parent 1d4be33326
commit c02072863a
4 changed files with 41 additions and 25 deletions

View file

@ -1,5 +1,5 @@
import type {BrowserContext} from "@playwright/test" import type {BrowserContext} from "@playwright/test"
import {MINUTE, int, ms} from "@welshman/lib" import {ms} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util" import type {TrustedEvent} from "@welshman/util"
import type {TestUser} from "../keys" import type {TestUser} from "../keys"
import {injectEvents, injectSession} from "./session" import {injectEvents, injectSession} from "./session"
@ -76,9 +76,18 @@ export const boot = async (
// is the first point at which the app is running. With a session injected, wait for the signed-in // is the first point at which the app is running. With a session injected, wait for the signed-in
// nav instead. A session the app rejected renders the landing dialog, and failing on that here // nav instead. A session the app rejected renders the landing dialog, and failing on that here
// reads far better than the assertions it would break later. // reads far better than the assertions it would break later.
await page const shell = page.locator(user ? ".primary-nav" : ".fl")
.locator(user ? ".primary-nav" : ".fl")
.waitFor({state: "attached", timeout: ms(int(1, MINUTE))}) for (let attempt = 0; ; attempt++) {
try {
await shell.waitFor({state: "attached", timeout: ms(15)})
break
} catch (e) {
if (attempt === 3) throw e
await page.reload()
}
}
// src/app/env.ts reads every VITE_ value as it is imported, so by now the app has resolved them // src/app/env.ts reads every VITE_ value as it is imported, so by now the app has resolved them
// against either the env above or .env's real relays. // against either the env above or .env's real relays.

View file

@ -144,11 +144,14 @@ test("US-092 edit a space's profile and featured content", async ({seed, as}) =>
await expect(editor).toBeVisible() await expect(editor).toBeVisible()
// The icon picker's file input is hidden inside its own label, so the chooser is opened by await editor.getByRole("button", {name: "Add an image"}).click()
// clicking the label rather than by writing to the input.
// The upload's file input is hidden inside its own label, so the chooser is opened by clicking
// the label rather than by writing to the input. Picking a file dismisses the picker.
const picker = dialog(admin, "Add an image")
const chooser = admin.waitForEvent("filechooser") const chooser = admin.waitForEvent("filechooser")
await editor.locator('label:has(input[type="file"])').click() await picker.locator('label:has(input[type="file"])').click()
await (await chooser).setFiles(ICON) await (await chooser).setFiles(ICON)
await expect(editor.getByText("Selected:")).toBeVisible() await expect(editor.getByText("Selected:")).toBeVisible()

View file

@ -43,7 +43,7 @@ const modal = (page: Page, title: string) =>
const composerForm = (page: Page) => const composerForm = (page: Page) =>
page.locator("form").filter({has: page.locator(".note-editor")}) page.locator("form").filter({has: page.locator(".note-editor")})
const editorOf = (scope: Locator) => scope.locator(".note-editor [contenteditable=true]") const editorOf = (scope: Locator | Page) => scope.locator(".note-editor [contenteditable=true]")
const pageBar = (page: Page) => page.locator('[data-component="PageBar"]') const pageBar = (page: Page) => page.locator('[data-component="PageBar"]')
@ -146,17 +146,17 @@ test("US-037 write and publish an article", async ({seed, as}) => {
await pageBar(page).getByRole("button", {name: "Write"}).click() await pageBar(page).getByRole("button", {name: "Write"}).click()
const composer = modal(page, "Write an Article") const publish = pageBar(page).getByRole("button", {name: "Publish"})
const title = composer.getByPlaceholder("What is this article about?") const title = page.getByPlaceholder("Title", {exact: true})
const body = editorOf(composer) const body = editorOf(page)
await expect(composer).toBeVisible() await expect(title).toBeVisible()
await composer.getByRole("button", {name: "Publish Article"}).click() await publish.click()
await expect(page.getByRole("alert")).toContainText("Please provide a title for your article.") await expect(page.getByRole("alert")).toContainText("Please provide a title for your article.")
await title.fill("Half Baked") await title.fill("Half Baked")
await composer.getByRole("button", {name: "Publish Article"}).click() await publish.click()
await expect(page.getByRole("alert")).toContainText("Please write something for your article.") await expect(page.getByRole("alert")).toContainText("Please write something for your article.")
await body.pressSequentially("Only the beginning.") await body.pressSequentially("Only the beginning.")
@ -164,8 +164,8 @@ test("US-037 write and publish an article", async ({seed, as}) => {
// Neither refusal put anything on the wire. // Neither refusal put anything on the wire.
expect(publishedEvents(page, LONG_FORM)).toEqual([]) expect(publishedEvents(page, LONG_FORM)).toEqual([])
await composer.getByRole("button", {name: "Go back"}).click() await page.goBack()
await expect(page.getByRole("heading", {name: "Write an Article"})).toHaveCount(0) await expect(articleCards(page)).toHaveCount(1)
// Reopening picks up where the composer was closed rather than starting over. // Reopening picks up where the composer was closed rather than starting over.
await pageBar(page).getByRole("button", {name: "Write"}).click() await pageBar(page).getByRole("button", {name: "Write"}).click()
@ -173,12 +173,16 @@ test("US-037 write and publish an article", async ({seed, as}) => {
await expect(body).toContainText("Only the beginning.") await expect(body).toContainText("Only the beginning.")
await title.fill("Signals in the Noise") await title.fill("Signals in the Noise")
await composer.getByRole("button", {name: "Publish Article"}).click() await publish.click()
// Publishing lands on the article itself rather than back on the list.
await expect(page.getByRole("heading", {name: "Signals in the Noise"}).first()).toBeVisible()
expect(publishedEvents(page, LONG_FORM)).toHaveLength(1)
await page.goto(`${spacePath(url)}/articles`)
await expect(page.getByRole("heading", {name: "Write an Article"})).toHaveCount(0)
await expect(articleCards(page)).toHaveCount(2) await expect(articleCards(page)).toHaveCount(2)
await expect(articleCards(page).first()).toContainText("Signals in the Noise") await expect(articleCards(page).first()).toContainText("Signals in the Noise")
expect(publishedEvents(page, LONG_FORM)).toHaveLength(1)
// A published article leaves the composer empty for the next one. // A published article leaves the composer empty for the next one.
await pageBar(page).getByRole("button", {name: "Write"}).click() await pageBar(page).getByRole("button", {name: "Write"}).click()
@ -560,15 +564,14 @@ test("US-041 publish an article from a room", async ({seed, as}) => {
await openComposeMenu(page) await openComposeMenu(page)
await page.getByRole("button", {name: "Write an Article"}).click() await page.getByRole("button", {name: "Write an Article"}).click()
const composer = modal(page, "Write an Article") await page.getByPlaceholder("Title", {exact: true}).fill("Repotting in Winter")
await editorOf(page).pressSequentially("Wait for a warm week.")
await pageBar(page).getByRole("button", {name: "Publish"}).click()
await composer.getByPlaceholder("What is this article about?").fill("Repotting in Winter") await expect(page.getByRole("heading", {name: "Repotting in Winter"}).first()).toBeVisible()
await editorOf(composer).pressSequentially("Wait for a warm week.")
await composer.getByRole("button", {name: "Publish Article"}).click()
await expect(page.getByRole("heading", {name: "Write an Article"})).toHaveCount(0)
// The room hears about the article without alice posting it a second time. // The room hears about the article without alice posting it a second time.
await page.goto(roomPath(url, "lounge"))
await expect(page.getByText("Repotting in Winter")).toBeVisible() await expect(page.getByText("Repotting in Winter")).toBeVisible()
await page.goto(`${spacePath(url)}/articles`) await page.goto(`${spacePath(url)}/articles`)

View file

@ -68,6 +68,7 @@
<ImageInputButton <ImageInputButton
onSelect={handleImageSelect} onSelect={handleImageSelect}
initialValue={initialUrlValue} initialValue={initialUrlValue}
aria-label="Add an image"
class="button button-neutral button-sm"> class="button button-neutral button-sm">
<Icon icon={UploadMinimalistic} size={4} /> <Icon icon={UploadMinimalistic} size={4} />
</ImageInputButton> </ImageInputButton>