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
206 lines
7.5 KiB
TypeScript
206 lines
7.5 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import {
|
|
mockConfig,
|
|
mockHouseholdAndStoreShell,
|
|
seedAuthStorage,
|
|
} from "./helpers/e2e";
|
|
|
|
test("assigned items render selected users and keep the picker menu outside the modal", async ({ page }) => {
|
|
await seedAuthStorage(page, { username: "assignment-user" });
|
|
await mockConfig(page);
|
|
|
|
const members = [
|
|
{ id: 1, username: "owner", name: "Owner User", display_name: "Owner User", role: "owner" },
|
|
{ id: 2, username: "casey", name: "Casey Client", display_name: "Casey Client", role: "member" },
|
|
{ id: 3, username: "jordan", name: "Jordan Client", display_name: "Jordan Client", role: "member" },
|
|
{ id: 4, username: "alex", name: "Alex Member", display_name: "Alex Member", role: "member" },
|
|
{ id: 5, username: "morgan", name: "Morgan Member", display_name: "Morgan Member", role: "member" },
|
|
{ id: 6, username: "sam", name: "Sam Member", display_name: "Sam Member", role: "member" },
|
|
{ id: 7, username: "jamie", name: "Jamie Member", display_name: "Jamie Member", role: "member" },
|
|
{ id: 8, username: "pat", name: "Pat Member", display_name: "Pat Member", role: "member" },
|
|
{ id: 9, username: "drew", name: "Drew Member", display_name: "Drew Member", role: "member" },
|
|
{ id: 10, username: "kai", name: "Kai Member", display_name: "Kai Member", role: "member" },
|
|
{ id: 11, username: "blair", name: "Blair Member", display_name: "Blair Member", role: "member" },
|
|
{ id: 12, username: "quinn", name: "Quinn Member", display_name: "Quinn Member", role: "member" },
|
|
{ id: 13, username: "rowan", name: "Rowan Member", display_name: "Rowan Member", role: "member" },
|
|
{ id: 14, username: "sage", name: "Sage Member", display_name: "Sage Member", role: "member" },
|
|
{ id: 15, username: "taylor", name: "Taylor Member", display_name: "Taylor Member", role: "member" },
|
|
{ id: 16, username: "river", name: "River Member", display_name: "River Member", role: "member" },
|
|
];
|
|
|
|
let listItems: Array<{
|
|
id: number;
|
|
item_id: number;
|
|
item_name: string;
|
|
quantity: number;
|
|
bought: boolean;
|
|
item_image: string | null;
|
|
image_mime_type: string | null;
|
|
added_by_users: string[];
|
|
last_added_on: string;
|
|
item_type: string | null;
|
|
item_group: string | null;
|
|
zone: string | null;
|
|
}> = [];
|
|
let addCallCount = 0;
|
|
|
|
await mockHouseholdAndStoreShell(page, {
|
|
household: { name: "Assignment House" },
|
|
});
|
|
|
|
await page.route("**/households/1/members", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify(members),
|
|
});
|
|
});
|
|
|
|
await page.route("**/households/1/stores/10/list/recent", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify([]),
|
|
});
|
|
});
|
|
|
|
await page.route("**/households/1/stores/10/list/suggestions**", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify([]),
|
|
});
|
|
});
|
|
|
|
await page.route("**/households/1/stores/10/list/item**", async (route) => {
|
|
const url = new URL(route.request().url());
|
|
const itemName = (url.searchParams.get("item_name") || "").toLowerCase();
|
|
const item = listItems.find((candidate) => candidate.item_name === itemName);
|
|
|
|
await route.fulfill({
|
|
status: item ? 200 : 404,
|
|
contentType: "application/json",
|
|
body: JSON.stringify(item || { message: "Item not found" }),
|
|
});
|
|
});
|
|
|
|
await page.route("**/households/1/stores/10/list/add", async (route) => {
|
|
addCallCount += 1;
|
|
|
|
if (addCallCount === 1) {
|
|
listItems = [
|
|
{
|
|
id: 201,
|
|
item_id: 501,
|
|
item_name: "bananas",
|
|
quantity: 1,
|
|
bought: false,
|
|
item_image: null,
|
|
image_mime_type: null,
|
|
added_by_users: ["Casey Client"],
|
|
last_added_on: "2026-03-28T12:00:00.000Z",
|
|
item_type: null,
|
|
item_group: null,
|
|
zone: null,
|
|
},
|
|
];
|
|
} else {
|
|
listItems = [
|
|
{
|
|
id: 201,
|
|
item_id: 501,
|
|
item_name: "bananas",
|
|
quantity: 2,
|
|
bought: false,
|
|
item_image: null,
|
|
image_mime_type: null,
|
|
added_by_users: ["Casey Client", "Jordan Client"],
|
|
last_added_on: "2026-03-28T12:05:00.000Z",
|
|
item_type: null,
|
|
item_group: null,
|
|
zone: null,
|
|
},
|
|
];
|
|
}
|
|
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
message: addCallCount === 1 ? "Item added" : "Item updated",
|
|
item: {
|
|
id: 201,
|
|
item_name: "bananas",
|
|
quantity: addCallCount === 1 ? 1 : 2,
|
|
bought: false,
|
|
},
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.route("**/households/1/stores/10/list", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ items: listItems }),
|
|
});
|
|
});
|
|
|
|
await page.goto("/");
|
|
|
|
await expect(page.getByRole("heading", { name: "Grocery List" })).toBeVisible();
|
|
await page.getByPlaceholder("Enter item name").fill("bananas");
|
|
await page.getByRole("button", { name: "Others" }).click();
|
|
|
|
const assignModal = page.locator(".assign-item-for-modal");
|
|
await expect(assignModal).toBeVisible();
|
|
|
|
await assignModal.locator(".assign-item-for-dropdown-trigger").click();
|
|
|
|
const portalMenu = page.locator("body > .assign-item-for-dropdown-menu");
|
|
await expect(portalMenu).toBeVisible();
|
|
await expect(page.locator(".assign-item-for-modal .assign-item-for-dropdown-menu")).toHaveCount(0);
|
|
|
|
const dropdownMetrics = await portalMenu.evaluate((element) => {
|
|
const menu = element as HTMLDivElement;
|
|
return {
|
|
position: window.getComputedStyle(menu).position,
|
|
scrollable: menu.scrollHeight > menu.clientHeight,
|
|
};
|
|
});
|
|
|
|
expect(dropdownMetrics.position).toBe("fixed");
|
|
expect(dropdownMetrics.scrollable).toBe(true);
|
|
|
|
await portalMenu.getByRole("option", { name: "Casey Client" }).click();
|
|
await expect(portalMenu).toHaveCount(0);
|
|
await assignModal.getByRole("button", { name: "Confirm" }).click();
|
|
await expect(assignModal).toHaveCount(0);
|
|
|
|
await expect(page.getByText("Adding for: Casey Client")).toBeVisible();
|
|
await page.getByRole("button", { name: "Create + Add" }).click();
|
|
await page.getByRole("button", { name: "Skip All" }).click();
|
|
|
|
const bananasRow = page.locator(".glist-li").filter({ hasText: "bananas" });
|
|
await expect(bananasRow).toContainText("Casey Client");
|
|
await expect(page.locator(".action-toast.action-toast-success")).toContainText("Added item");
|
|
|
|
await page.getByPlaceholder("Enter item name").fill("bananas");
|
|
await page.getByRole("button", { name: "Others" }).click();
|
|
await assignModal.locator(".assign-item-for-dropdown-trigger").click();
|
|
await portalMenu.getByRole("option", { name: "Jordan Client" }).click();
|
|
await expect(portalMenu).toHaveCount(0);
|
|
await assignModal.getByRole("button", { name: "Confirm" }).click();
|
|
await expect(assignModal).toHaveCount(0);
|
|
|
|
await expect(page.getByText("Adding for: Jordan Client")).toBeVisible();
|
|
await page.getByRole("button", { name: "Create + Add" }).click();
|
|
await page.getByRole("button", { name: "Update Quantity" }).click();
|
|
|
|
await expect(bananasRow).toContainText("Casey Client");
|
|
await expect(bananasRow).toContainText("Jordan Client");
|
|
await expect(
|
|
page.locator(".action-toast.action-toast-success").filter({ hasText: "Updated item quantity" })
|
|
).toContainText("Updated item quantity");
|
|
});
|