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:
parent
1d4be33326
commit
c02072863a
4 changed files with 41 additions and 25 deletions
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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`)
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue