286 lines
8.3 KiB
TypeScript
286 lines
8.3 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
|
|
function seedAuthStorage(page: import("@playwright/test").Page) {
|
|
return page.addInitScript(() => {
|
|
localStorage.setItem("token", "test-token");
|
|
localStorage.setItem("userId", "1");
|
|
localStorage.setItem("role", "admin");
|
|
localStorage.setItem("username", "toast-user");
|
|
});
|
|
}
|
|
|
|
async function mockConfig(page: import("@playwright/test").Page) {
|
|
await page.route("**/config", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
maxFileSizeMB: 20,
|
|
maxImageDimension: 800,
|
|
imageQuality: 85,
|
|
}),
|
|
});
|
|
});
|
|
}
|
|
|
|
test("login failure shows inline error and error toast", async ({ page }) => {
|
|
await mockConfig(page);
|
|
await page.route("**/auth/login", async (route) => {
|
|
await route.fulfill({
|
|
status: 401,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ message: "Invalid credentials" }),
|
|
});
|
|
});
|
|
|
|
await page.goto("/login");
|
|
await page.getByPlaceholder("Username").fill("bad-user");
|
|
await page.getByPlaceholder("Password").fill("bad-password");
|
|
await page.getByRole("button", { name: "Login" }).click();
|
|
|
|
await expect(page.getByText("Invalid credentials", { exact: true })).toBeVisible();
|
|
await expect(page.locator(".action-toast.action-toast-error")).toContainText("Login failed");
|
|
await expect(page.locator(".action-toast.action-toast-error")).toContainText("Invalid credentials");
|
|
});
|
|
|
|
test("manage stores add success shows success toast", async ({ page }) => {
|
|
await seedAuthStorage(page);
|
|
await mockConfig(page);
|
|
|
|
let stores = [
|
|
{
|
|
id: 10,
|
|
household_store_id: 100,
|
|
name: "Costco",
|
|
location_name: "Default Location",
|
|
display_name: "Costco",
|
|
is_default: true,
|
|
},
|
|
];
|
|
|
|
await page.route("**/households", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify([{ id: 1, name: "Toast Home", role: "admin", invite_code: "ABCD1234" }]),
|
|
});
|
|
});
|
|
|
|
await page.route("**/households/1/stores", async (route) => {
|
|
const request = route.request();
|
|
if (request.method() === "GET") {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify(stores),
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (request.method() === "POST") {
|
|
const body = request.postDataJSON() as { name?: string };
|
|
const name = body.name || "New Store";
|
|
stores = [
|
|
...stores,
|
|
{
|
|
id: 11,
|
|
household_store_id: 101,
|
|
name,
|
|
location_name: "Default Location",
|
|
display_name: name,
|
|
is_default: false,
|
|
},
|
|
];
|
|
await route.fulfill({
|
|
status: 201,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ store: stores[stores.length - 1] }),
|
|
});
|
|
return;
|
|
}
|
|
|
|
await route.fulfill({ status: 405 });
|
|
});
|
|
|
|
await page.route("**/households/1/locations/10/zones", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ zones: [] }),
|
|
});
|
|
});
|
|
|
|
await page.goto("/manage?tab=stores");
|
|
const addStoreForm = page.locator(".add-store-inline");
|
|
await addStoreForm.getByLabel("Store name").fill("Stater Bros");
|
|
await addStoreForm.getByRole("button", { name: "Add" }).click();
|
|
|
|
await expect(page.locator(".action-toast.action-toast-success")).toContainText("Created store");
|
|
await expect(page.locator(".action-toast.action-toast-success")).toContainText("Stater Bros");
|
|
});
|
|
|
|
test("manage stores add failure shows normalized error toast", async ({ page }) => {
|
|
await seedAuthStorage(page);
|
|
await mockConfig(page);
|
|
|
|
await page.route("**/households", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify([{ id: 1, name: "Toast Home", role: "admin", invite_code: "ABCD1234" }]),
|
|
});
|
|
});
|
|
|
|
await page.route("**/households/1/stores", async (route) => {
|
|
const request = route.request();
|
|
if (request.method() === "GET") {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify([
|
|
{
|
|
id: 10,
|
|
household_store_id: 100,
|
|
name: "Costco",
|
|
location_name: "Default Location",
|
|
display_name: "Costco",
|
|
is_default: true,
|
|
},
|
|
]),
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (request.method() === "POST") {
|
|
await route.fulfill({
|
|
status: 400,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
error: { message: "Store already linked to household" },
|
|
}),
|
|
});
|
|
return;
|
|
}
|
|
|
|
await route.fulfill({ status: 405 });
|
|
});
|
|
|
|
await page.route("**/households/1/locations/10/zones", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ zones: [] }),
|
|
});
|
|
});
|
|
|
|
await page.goto("/manage?tab=stores");
|
|
const addStoreForm = page.locator(".add-store-inline");
|
|
await addStoreForm.getByLabel("Store name").fill("Costco");
|
|
await addStoreForm.getByRole("button", { name: "Add" }).click();
|
|
|
|
await expect(page.locator(".action-toast.action-toast-error")).toContainText("Create store failed");
|
|
await expect(page.locator(".action-toast.action-toast-error")).toContainText("Store already linked to household");
|
|
});
|
|
|
|
test("invite accept JOINED shows success toast", async ({ page }) => {
|
|
await seedAuthStorage(page);
|
|
await mockConfig(page);
|
|
|
|
await page.route("**/api/invite-links/toast-token", async (route) => {
|
|
const request = route.request();
|
|
|
|
if (request.method() === "GET") {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
link: {
|
|
token: "toast-token",
|
|
status: "ACTIVE",
|
|
viewerStatus: null,
|
|
active_policy: "AUTO_ACCEPT",
|
|
group_name: "Toast Group",
|
|
},
|
|
}),
|
|
});
|
|
return;
|
|
}
|
|
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
result: {
|
|
status: "JOINED",
|
|
group: { name: "Toast Group" },
|
|
},
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.route("**/households", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify([{ id: 1, name: "Toast Home", role: "member", invite_code: "ABCD1234" }]),
|
|
});
|
|
});
|
|
|
|
await page.goto("/invite/toast-token");
|
|
await page.getByRole("button", { name: "Join Group" }).click();
|
|
|
|
await expect(page.locator(".action-toast.action-toast-success")).toContainText("Joined group");
|
|
await expect(page.locator(".action-toast.action-toast-success")).toContainText("Toast Group");
|
|
});
|
|
|
|
test("invite accept PENDING shows info toast", async ({ page }) => {
|
|
await seedAuthStorage(page);
|
|
await mockConfig(page);
|
|
|
|
await page.route("**/api/invite-links/pending-token", async (route) => {
|
|
const request = route.request();
|
|
|
|
if (request.method() === "GET") {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
link: {
|
|
token: "pending-token",
|
|
status: "ACTIVE",
|
|
viewerStatus: null,
|
|
active_policy: "APPROVAL_REQUIRED",
|
|
group_name: "Pending Group",
|
|
},
|
|
}),
|
|
});
|
|
return;
|
|
}
|
|
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
result: {
|
|
status: "PENDING",
|
|
group: { name: "Pending Group" },
|
|
},
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.route("**/households", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify([{ id: 1, name: "Toast Home", role: "member", invite_code: "ABCD1234" }]),
|
|
});
|
|
});
|
|
|
|
await page.goto("/invite/pending-token");
|
|
await page.getByRole("button", { name: "Join Group" }).click();
|
|
|
|
await expect(page.locator(".action-toast.action-toast-info")).toContainText("Join request sent");
|
|
await expect(page.locator(".action-toast.action-toast-info")).toContainText("Pending Group");
|
|
});
|