Files
app/frontend/tests/utils/api.ts
T

90 lines
2.7 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.
*/
import { apiUrl } from "../config.ts"
const authFile = "playwright/.auth/user.json"
/** 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
}
/**
* Create a flow the way the app does, with a POST, under its key as a pinned
* id, so a spec keeps addressing it by the name it chose. The suite signs in
* as a superuser, who may pin. The answer is the request's own.
*/
export function createFlow(
page: Page,
doc: { name: string; [field: string]: unknown },
) {
return api(page, "/flows/", {
method: "POST",
data: { id: doc.name, ...doc },
})
}
/**
* The same for a dashboard. Creating one takes only its key and title, so the
* rest of `doc` is saved over the new draft, and the answer is that save's.
*/
export async function createDashboard(
page: Page,
doc: { name: string; title?: string; [field: string]: unknown },
) {
const made = await api(page, "/dashboards/", {
method: "POST",
data: { id: doc.name, name: doc.name, title: doc.title },
})
const rest = Object.keys(doc).filter(
(key) => key !== "name" && key !== "title",
)
if (!made.ok() || rest.length === 0) return made
const { version } = await made.json()
return api(page, `/dashboards/${doc.name}`, {
method: "PUT",
data: { ...doc, version },
})
}
/**
* The id the server issued a flow just created in the app, read off the
* editor it opened: an opaque `f_` and eight characters, never the key.
*/
export async function openedFlowId(page: Page) {
await page.waitForURL(/\/flows\/f_[a-z2-7]{8}$/)
return new URL(page.url()).pathname.split("/").pop() ?? ""
}
/** 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()
}