diff --git a/tests/e2e/ui/helpers/roundTrip.ts b/tests/e2e/ui/helpers/roundTrip.ts index 8d6e264e622..ee484b8d512 100644 --- a/tests/e2e/ui/helpers/roundTrip.ts +++ b/tests/e2e/ui/helpers/roundTrip.ts @@ -12,17 +12,41 @@ export async function captureRequestBody( match: { method: string; urlIncludes: string }, action: () => Promise, ): Promise> { - const pending = page.waitForRequest((req) => req.method() === match.method && req.url().includes(match.urlIncludes)); + const pending = page.waitForRequest( + (req) => + req.method() === match.method && req.url().includes(match.urlIncludes), + ); await action(); const request = await pending; return JSON.parse(request.postData() ?? "{}") as Record; } /** Reads an endpoint as the master key, so a failure is bad data and not an expired UI token. */ -export async function readBack(page: Page, endpoint: string): Promise { +export async function readBack( + page: Page, + endpoint: string, +): Promise { const res = await page.request.get(endpoint, { headers: { Authorization: `Bearer ${masterKey()}` }, }); expect(res.ok(), `GET ${endpoint}`).toBe(true); return (await res.json()) as T; } + +export async function runWithCleanup( + action: () => Promise, + cleanup: () => Promise, +): Promise { + const outcome = await action().then( + () => ({ status: "success" as const }), + (error: unknown) => ({ status: "failure" as const, error }), + ); + try { + if (outcome.status === "failure") throw outcome.error; + } finally { + const cleanupSucceeded = await cleanup().catch(() => false); + if (outcome.status === "success" && !cleanupSucceeded) { + throw new Error("Failed to clean up UI E2E resource"); + } + } +} diff --git a/tests/e2e/ui/tests/prompts/addPrompt.spec.ts b/tests/e2e/ui/tests/prompts/addPrompt.spec.ts index b601b7e8c06..891739fda28 100644 --- a/tests/e2e/ui/tests/prompts/addPrompt.spec.ts +++ b/tests/e2e/ui/tests/prompts/addPrompt.spec.ts @@ -3,6 +3,7 @@ import { test, expect } from "@playwright/test"; import { ADMIN_STORAGE_PATH } from "../../constants"; import { Page as DashboardPage } from "../../fixtures/pages"; import { navigateToPage } from "../../helpers/navigation"; +import { runWithCleanup } from "../../helpers/roundTrip"; import { masterKey, uniqueSuffix } from "../../helpers/traffic"; test.use({ storageState: ADMIN_STORAGE_PATH }); @@ -13,21 +14,9 @@ test.describe("Prompt upload form", () => { }) => { const promptId = `e2e-prompt-${uniqueSuffix()}`; const promptContent = "Hello {{name}}"; - const cleanup = async (): Promise => { - try { - const response = await page.request.delete( - `/prompts/${encodeURIComponent(promptId)}?environment=development`, - { - headers: { Authorization: `Bearer ${masterKey()}` }, - }, - ); - return response.ok(); - } catch { - return false; - } - }; - const testOutcome = await (async () => { - try { + + await runWithCleanup( + async () => { await navigateToPage(page, DashboardPage.Prompts); await page.getByRole("button", { name: "Upload .prompt File" }).click(); await expect( @@ -71,17 +60,16 @@ test.describe("Prompt upload form", () => { }) .toContain(promptContent); await expect(page.getByText(promptId, { exact: true })).toBeVisible(); - return { passed: true as const }; - } catch (error) { - return { passed: false as const, error }; - } - })(); - - try { - if (!testOutcome.passed) throw testOutcome.error; - } finally { - const cleanupSucceeded = await cleanup(); - if (testOutcome.passed) expect(cleanupSucceeded).toBe(true); - } + }, + async () => { + const response = await page.request.delete( + `/prompts/${encodeURIComponent(promptId)}?environment=development`, + { + headers: { Authorization: `Bearer ${masterKey()}` }, + }, + ); + return response.ok(); + }, + ); }); }); diff --git a/tests/e2e/ui/tests/tagManagement/tagManagement.spec.ts b/tests/e2e/ui/tests/tagManagement/tagManagement.spec.ts index 4223324ff09..bf46b5ea161 100644 --- a/tests/e2e/ui/tests/tagManagement/tagManagement.spec.ts +++ b/tests/e2e/ui/tests/tagManagement/tagManagement.spec.ts @@ -3,7 +3,11 @@ import { test, expect } from "@playwright/test"; import { ADMIN_STORAGE_PATH } from "../../constants"; import { Page as DashboardPage } from "../../fixtures/pages"; import { navigateToPage } from "../../helpers/navigation"; -import { captureRequestBody, readBack } from "../../helpers/roundTrip"; +import { + captureRequestBody, + readBack, + runWithCleanup, +} from "../../helpers/roundTrip"; import { masterKey, uniqueSuffix } from "../../helpers/traffic"; test.use({ storageState: ADMIN_STORAGE_PATH }); @@ -13,22 +17,9 @@ test.describe("Tag management", () => { const tagName = `e2e-tag-${uniqueSuffix()}`; const description = "synthetic tag description"; const updatedDescription = `${description} updated`; - const cleanup = async (): Promise => { - try { - const response = await page.request.post("/tag/delete", { - headers: { - Authorization: `Bearer ${masterKey()}`, - "Content-Type": "application/json", - }, - data: { name: tagName }, - }); - return response.ok(); - } catch { - return false; - } - }; - const testOutcome = await (async () => { - try { + + await runWithCleanup( + async () => { await navigateToPage(page, DashboardPage.TagManagement); await page.getByRole("button", { name: "+ Create New Tag" }).click(); await expect( @@ -76,17 +67,17 @@ test.describe("Tag management", () => { return info[tagName]?.description; }) .toBe(updatedDescription); - return { passed: true as const }; - } catch (error) { - return { passed: false as const, error }; - } - })(); - - try { - if (!testOutcome.passed) throw testOutcome.error; - } finally { - const cleanupSucceeded = await cleanup(); - if (testOutcome.passed) expect(cleanupSucceeded).toBe(true); - } + }, + async () => { + const response = await page.request.post("/tag/delete", { + headers: { + Authorization: `Bearer ${masterKey()}`, + "Content-Type": "application/json", + }, + data: { name: tagName }, + }); + return response.ok(); + }, + ); }); });