90 lines
2.7 KiB
TypeScript
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()
|
|
}
|