grocery-app/frontend/tests/helpers/e2e.ts
Nico a2c08aff45
All checks were successful
Build & Deploy Costco Grocery List / build (push) Successful in 1m36s
Build & Deploy Costco Grocery List / verify-images (push) Successful in 2s
Build & Deploy Costco Grocery List / deploy (push) Successful in 8s
Build & Deploy Costco Grocery List / notify (push) Successful in 0s
chore: harden reliability checks
2026-05-25 16:20:35 -07:00

121 lines
3.1 KiB
TypeScript

import { expect, type Page } from "@playwright/test";
type AuthSeed = {
token?: string;
userId?: string;
role?: string;
username?: string;
};
type HouseholdSeed = {
id?: number;
name?: string;
role?: string;
invite_code?: string;
};
type StoreSeed = {
id?: number;
name?: string;
location?: string;
is_default?: boolean;
};
const defaultConfig = {
maxFileSizeMB: 20,
maxImageDimension: 800,
imageQuality: 85,
};
export function seedAuthStorage(page: Page, seed: AuthSeed = {}) {
return page.addInitScript((authSeed) => {
localStorage.setItem("token", authSeed.token || "test-token");
localStorage.setItem("userId", authSeed.userId || "1");
localStorage.setItem("role", authSeed.role || "admin");
localStorage.setItem("username", authSeed.username || "test-user");
}, seed);
}
export async function mockConfig(page: Page, overrides = {}) {
await page.route("**/config", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ ...defaultConfig, ...overrides }),
});
});
}
export async function mockHouseholdAndStoreShell(
page: Page,
options: { household?: HouseholdSeed; stores?: StoreSeed[] } = {}
) {
const household = {
id: 1,
name: "Test Household",
role: "admin",
invite_code: "ABCD1234",
...options.household,
};
const stores = options.stores || [
{ id: 10, name: "Costco", location: "Warehouse", is_default: true },
];
await page.route("**/households", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify([household]),
});
});
await page.route(`**/stores/household/${household.id}`, async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(stores),
});
});
}
export async function confirmSlide(page: Page) {
const confirmModal = page.locator(".confirm-slide-modal");
await expect(confirmModal).toBeVisible();
const slider = confirmModal.locator(".confirm-slide-handle");
const track = confirmModal.locator(".confirm-slide-track");
const sliderBox = await slider.boundingBox();
const trackBox = await track.boundingBox();
if (!sliderBox || !trackBox) {
throw new Error("Confirm slide control was not measurable");
}
await page.mouse.move(sliderBox.x + sliderBox.width / 2, sliderBox.y + sliderBox.height / 2);
await page.mouse.down();
await page.mouse.move(trackBox.x + trackBox.width - 4, sliderBox.y + sliderBox.height / 2, {
steps: 8,
});
await page.mouse.up();
}
export function collectFailedApiRequests(page: Page) {
const failedRequests: string[] = [];
page.on("requestfailed", (request) => {
const url = request.url();
if (!url.startsWith("http://localhost:5000")) {
return;
}
const failure = request.failure()?.errorText || "unknown failure";
failedRequests.push(`${request.method()} ${url} :: ${failure}`);
});
return failedRequests;
}
export function expectNoFailedApiRequests(failedRequests: string[]) {
expect(failedRequests).toEqual([]);
}