diff --git a/e2e/work-orders/completion-uploads-mobile.spec.ts b/e2e/work-orders/completion-uploads-mobile.spec.ts index ee771695..a8fde88f 100644 --- a/e2e/work-orders/completion-uploads-mobile.spec.ts +++ b/e2e/work-orders/completion-uploads-mobile.spec.ts @@ -1,6 +1,6 @@ import { devices, expect, test, type Page, type Request, type Route } from "@playwright/test"; -// SH-368 / SH-370: completion uploads from a mobile-sized viewport must reach the API as +// Completion uploads from a mobile-sized viewport must reach the API as // multipart/form-data with a boundary, or ASP.NET cannot bind the [FromForm] file/category. test.use({ ...devices["Pixel 7"] }); @@ -50,6 +50,18 @@ function multipartBoundary(request: Request): string | undefined { return /^multipart\/form-data; boundary=(.+)$/.exec(contentType)?.[1]; } +async function hideQueryDevtools(page: Page) { + await page.addStyleTag({ + content: + ".tsqd-parent-container { display: none !important; pointer-events: none !important; }", + }); + await page.locator(".tsqd-parent-container").evaluateAll((nodes) => { + for (const node of nodes) { + node.remove(); + } + }); +} + async function mockBoard(page: Page, row: ReturnType) { await page.route("**/api/**", (route) => { const pathname = new URL(route.request().url()).pathname; @@ -76,6 +88,7 @@ async function openCompletionTab(page: Page, woNumber: string) { await page.clock.resume(); await page.addInitScript((user) => localStorage.setItem("auth", JSON.stringify(user)), TEST_USER); await page.goto("/workorders"); + await hideQueryDevtools(page); const row = page.getByRole("row").filter({ hasText: woNumber }); await row.getByRole("button", { name: "View details" }).click(); const panel = page.getByRole("dialog", { name: "Work order details" }); diff --git a/src/api/api.ts b/src/api/api.ts index 8478e2ec..e85e8e18 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -16,7 +16,7 @@ export const api: KyInstance = ky.create({ // hazard: no instance-wide Content-Type. Ky sets application/json for `json` bodies, and the // runtime must set `multipart/form-data; boundary=…` for FormData while it builds the Request. // A default here is baked into that Request; deleting it in a hook leaves the multipart body - // with no Content-Type at all, so ASP.NET cannot bind [FromForm] fields (SH-368). + // with no Content-Type at all, so ASP.NET cannot bind [FromForm] fields. headers: { Accept: "application/json", }, diff --git a/src/test/api/api-formdata-content-type.test.ts b/src/test/api/api-formdata-content-type.test.ts index f0fd61c7..94312aba 100644 --- a/src/test/api/api-formdata-content-type.test.ts +++ b/src/test/api/api-formdata-content-type.test.ts @@ -38,7 +38,7 @@ function lastRequest(): Captured { return request; } -describe("api multipart uploads (SH-368)", () => { +describe("api multipart uploads", () => { it("sends FormData with a multipart Content-Type that carries the body's boundary", async () => { const { apiRequestRaw } = await import("@/api/api"); const form = new FormData();