shoc-frontend-new/e2e/vendors/vendor-operations.spec.ts
2026-07-23 19:18:06 -03:00

190 lines
6.5 KiB
TypeScript

import { expect, test, type Page, type Route } from "@playwright/test";
const TEST_USER = {
token: "vendor-operations-e2e-token",
expiration: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
email: "admin@seahavenind.com",
userRoles: "Admin",
phoneNumber: "5551234567",
fullname: "Vendor Operations Admin",
id: "1",
};
async function fulfillJson(route: Route, body: unknown, status = 200) {
await route.fulfill({ status, contentType: "application/json", body: JSON.stringify(body) });
}
async function authenticate(page: Page) {
await page.addInitScript((user) => localStorage.setItem("auth", JSON.stringify(user)), TEST_USER);
}
test.describe("Vendor operations", () => {
test("groups 48-hour reminders and overlap conflicts with direct work-order actions", async ({
page,
}) => {
await authenticate(page);
await page.route("**/api/vendor-operations/notifications", (route) =>
fulfillJson(route, {
items: [
{
id: "no-vendor-501",
type: "NoVendor",
severity: "Medium",
group: "No Vendor",
title: "No vendor assigned — 48h to service",
workOrderId: 501,
workOrderNumber: "WO-501",
triggeredAt: "2026-07-23T12:00:00Z",
scheduledAt: "2026-07-25T12:00:00Z",
action: { label: "Choose vendor", href: "/workorders/501?assignVendor=1" },
},
{
id: "vendor-conflict-801-802",
type: "VendorConflict",
severity: "Medium",
group: "Vendor Conflict",
title: "Vendor has overlapping appointments",
triggeredAt: "2026-07-23T12:00:00Z",
workOrders: [
{
workOrderId: 501,
workOrderNumber: "WO-501",
start: "2026-07-25T12:00:00Z",
end: "2026-07-25T14:00:00Z",
},
{
workOrderId: 502,
workOrderNumber: "WO-502",
start: "2026-07-25T13:00:00Z",
end: "2026-07-25T15:00:00Z",
},
],
actions: [
{ label: "WO-501", href: "/workorders/501" },
{ label: "WO-502", href: "/workorders/502" },
],
},
],
}),
);
await page.goto("/notifications");
await expect(page.getByRole("heading", { name: "Items requiring attention" })).toBeVisible();
await expect(page.getByRole("heading", { name: "No Vendor" })).toBeVisible();
await expect(page.getByRole("heading", { name: "Vendor Conflict" })).toBeVisible();
await expect(page.getByRole("link", { name: "Choose vendor" })).toHaveAttribute(
"href",
"/workorders/501?assignVendor=1",
);
await expect(page.getByRole("link", { name: "WO-501" })).toHaveAttribute(
"href",
"/workorders/501",
);
await expect(page.getByRole("link", { name: "WO-502" })).toHaveAttribute(
"href",
"/workorders/502",
);
});
test("filters insights and exposes both export formats", async ({ page }) => {
await authenticate(page);
const requestedUrls: string[] = [];
await page.route("**/api/vendor-operations/insights*", async (route) => {
requestedUrls.push(route.request().url());
if (route.request().url().endsWith(".csv")) {
await route.fulfill({ status: 200, contentType: "text/csv", body: "Vendor,Completed\n" });
return;
}
if (route.request().url().endsWith(".pdf")) {
await route.fulfill({ status: 200, contentType: "application/pdf", body: "%PDF-1.4" });
return;
}
await fulfillJson(route, {
data: [
{
vendorId: 7,
vendor: "Gateway Plumbing",
completedJobs: 12,
cancellationRefusalRate: 0.1,
onTimeArrivalRate: 0.9,
onTimeCompletionRate: 0.8,
averageCycleHours: 5.5,
},
],
});
});
await page.goto("/vendors/insights");
await expect(page.getByRole("cell", { name: "Gateway Plumbing" })).toBeVisible();
await page.getByLabel("Trade").fill("Plumbing");
await expect
.poll(() =>
requestedUrls.some((url) => new URL(url).searchParams.get("trade") === "Plumbing"),
)
.toBe(true);
await page.getByRole("button", { name: "Export CSV" }).click();
await page.getByRole("button", { name: "Export PDF" }).click();
await expect.poll(() => requestedUrls.some((url) => url.endsWith("insights.csv"))).toBe(true);
await expect.poll(() => requestedUrls.some((url) => url.endsWith("insights.pdf"))).toBe(true);
});
test("reorders and saves preferred vendors for a site", async ({ page }) => {
await authenticate(page);
let savedPreferences: unknown;
await page.route("**/api/Contact/GetLocations", (route) =>
fulfillJson(route, { data: [{ Id: 10, Name: "St. Louis HQ" }] }),
);
await page.route("**/api/Vendor/Dropdown*", (route) =>
fulfillJson(route, {
data: [
{ Id: 7, CompanyName: "Gateway Plumbing", TradeSpecialties: "Plumbing" },
{ Id: 8, CompanyName: "Metro HVAC", TradeSpecialties: "HVAC" },
],
}),
);
await page.route("**/api/vendor-operations/sites/10/preferred-vendors", async (route) => {
if (route.request().method() === "PUT") {
savedPreferences = route.request().postDataJSON();
await fulfillJson(route, savedPreferences);
return;
}
await fulfillJson(route, [
{
id: 1,
locationId: 10,
vendorId: 7,
vendorName: "Gateway Plumbing",
trade: "Plumbing",
sortOrder: 0,
isStale: false,
},
{
id: 2,
locationId: 10,
vendorId: 8,
vendorName: "Metro HVAC",
trade: "HVAC",
sortOrder: 1,
isStale: false,
},
]);
});
await page.goto("/vendors/preferences");
await page.getByLabel("Site").click();
await page.getByRole("option", { name: "St. Louis HQ" }).click();
await expect(page.getByText(/1\. Gateway Plumbing/)).toBeVisible();
await page.getByRole("button", { name: "Move Metro HVAC up" }).click();
await expect(page.getByText(/1\. Metro HVAC/)).toBeVisible();
await page.getByRole("button", { name: "Save preferences" }).click();
await expect
.poll(() => savedPreferences)
.toEqual([
{ vendorId: 8, trade: "HVAC", sortOrder: 0 },
{ vendorId: 7, trade: "Plumbing", sortOrder: 1 },
]);
});
});