mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 10:23:11 +00:00
The shared ky instance declared Content-Type: application/json for every request. Ky bakes instance headers into the Request it builds, so a FormData body left with that header, and the beforeRequest hook that deleted it ran after the boundary had already been dropped: the multipart body went out with no Content-Type at all. ASP.NET then cannot bind [FromForm] fields, so the completion-doc upload (dialog and slide-over "Upload signed PDF") returned 400 "The file field is required.", media category updates arrived without category or workOrderVersion, and the legacy edit form post lost its fields. Drop the instance-wide Content-Type. Ky still sets application/json for json bodies, and the runtime sets multipart/form-data with the boundary. The old unit test tolerated a missing header, and ran under jsdom whose FormData the Node Request cannot serialize. It now runs in the node environment and requires a multipart Content-Type whose boundary matches the body. A Pixel 7 viewport e2e covers the signed PDF upload and JPG/MP4/MOV media upload plus categorize.
65 lines
1.6 KiB
TypeScript
65 lines
1.6 KiB
TypeScript
import "@testing-library/jest-dom/vitest";
|
|
import { cleanup } from "@testing-library/react";
|
|
import { afterEach, vi } from "vitest";
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
});
|
|
|
|
function localStorageIsUsable(): boolean {
|
|
try {
|
|
const probe = "__vitest_ls_probe__";
|
|
globalThis.localStorage.setItem(probe, "1");
|
|
const ok = globalThis.localStorage.getItem(probe) === "1";
|
|
globalThis.localStorage.removeItem(probe);
|
|
return ok;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Node 25+ / some harnesses lack a stable localStorage — provide an in-memory mock when needed.
|
|
if (!localStorageIsUsable()) {
|
|
const store = new Map<string, string>();
|
|
vi.stubGlobal("localStorage", {
|
|
getItem: (key: string) => store.get(key) ?? null,
|
|
setItem: (key: string, value: string) => {
|
|
store.set(key, String(value));
|
|
},
|
|
removeItem: (key: string) => {
|
|
store.delete(key);
|
|
},
|
|
clear: () => {
|
|
store.clear();
|
|
},
|
|
get length() {
|
|
return store.size;
|
|
},
|
|
key: (index: number) => [...store.keys()][index] ?? null,
|
|
});
|
|
}
|
|
|
|
// Files that opt into `@vitest-environment node` (real fetch/FormData semantics) have no window.
|
|
if (typeof window !== "undefined") {
|
|
Object.defineProperty(window, "matchMedia", {
|
|
writable: true,
|
|
value: (query: string) => ({
|
|
matches: false,
|
|
media: query,
|
|
onchange: null,
|
|
addListener: () => {},
|
|
removeListener: () => {},
|
|
addEventListener: () => {},
|
|
removeEventListener: () => {},
|
|
dispatchEvent: () => false,
|
|
}),
|
|
});
|
|
|
|
class ResizeObserverMock {
|
|
observe() {}
|
|
unobserve() {}
|
|
disconnect() {}
|
|
}
|
|
|
|
window.ResizeObserver = ResizeObserverMock;
|
|
}
|