The specs run against a development stack, so every run left a test_flow_* in someone's flow list. Each spec now tears down what it made in an afterAll, which runs whether or not the tests passed, and the three copies of the API helper move into tests/utils/api.ts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H7LwYgJfpkbLCTeiAf8U4A
42 lines
1.3 KiB
TypeScript
42 lines
1.3 KiB
TypeScript
import type { Browser, Page } from "@playwright/test"
|
|
|
|
/**
|
|
* Talking to the API directly, for the setup and teardown around a spec.
|
|
*
|
|
* The specs run against a development stack, so whatever they create has to go
|
|
* again — the same bargain the backend suite strikes with its throwaway
|
|
* database. What a spec leaves behind is in someone's flow list tomorrow.
|
|
*/
|
|
|
|
const authFile = "playwright/.auth/user.json"
|
|
const apiUrl = process.env.VITE_API_URL || "http://api.localhost"
|
|
|
|
/** Call the API as the logged-in user of *page*. */
|
|
export async function api(
|
|
page: Page,
|
|
path: string,
|
|
init: Record<string, unknown> = {},
|
|
) {
|
|
const token = await page.evaluate(() => localStorage.getItem("access_token"))
|
|
return page.request.fetch(`${apiUrl}/api/v1${path}`, {
|
|
...init,
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
/** An authenticated page for a `beforeAll`/`afterAll`, which get no `page`. */
|
|
export async function apiPage(browser: Browser) {
|
|
const page = await browser.newPage({ storageState: authFile })
|
|
await page.goto("/")
|
|
return page
|
|
}
|
|
|
|
/** Delete the given API paths, whether or not they are still there. */
|
|
export async function deleteAll(browser: Browser, paths: string[]) {
|
|
const page = await apiPage(browser)
|
|
for (const path of paths) {
|
|
await api(page, path, { method: "DELETE" })
|
|
}
|
|
await page.close()
|
|
}
|