135 lines
5.6 KiB
TypeScript
135 lines
5.6 KiB
TypeScript
import { expect } from "@playwright/test";
|
|
import { test } from "../fixtures";
|
|
import { registerAndLogin, createList, createMealWithIngredients } from "../helpers";
|
|
|
|
/**
|
|
* Opens the carry-over modal and waits for htmx to finish swapping it in, so
|
|
* the source `<select>`'s `change` trigger is bound before interacting with it.
|
|
*/
|
|
async function openCarryModal(page: import("@playwright/test").Page) {
|
|
await page.click(".carry-over-button");
|
|
const carry = page.locator(".meal-picker-backdrop");
|
|
await expect(carry).toBeVisible();
|
|
// Let htmx finish processing the freshly-swapped modal before selecting a
|
|
// source; otherwise the change event can be missed and no hx-get fires.
|
|
await page.waitForTimeout(300);
|
|
return carry;
|
|
}
|
|
|
|
test("a user can carry meals over from a previous list without re-adding ingredients", async ({
|
|
page,
|
|
}) => {
|
|
await registerAndLogin(page, "alice@example.com");
|
|
await createMealWithIngredients(page, "Spaghetti Bolognese", [
|
|
{ name: "Penne", quantity: "500g" },
|
|
{ name: "Tomato", quantity: "2" },
|
|
]);
|
|
|
|
// Last week's list has the meal added (with its ingredients as items).
|
|
await createList(page, "Last week");
|
|
await page.click(".add-meal-button");
|
|
const picker = page.locator(".meal-picker-backdrop");
|
|
await picker
|
|
.locator(".meal-picker-button")
|
|
.filter({ hasText: "Spaghetti Bolognese" })
|
|
.click();
|
|
await expect(page.locator(".item-row").filter({ hasText: "Penne" })).toBeVisible();
|
|
|
|
// Create this week's (new) list.
|
|
await createList(page, "This week");
|
|
await expect(page.locator(".item-row")).toHaveCount(0);
|
|
|
|
// Open the carry-over modal and pick the source list.
|
|
const carry = await openCarryModal(page);
|
|
await expect(carry.locator(".carry-intro")).toContainText("already purchased");
|
|
await carry.locator("#carry-source").selectOption({ label: "Last week" });
|
|
|
|
// The source list's meals appear as selectable rows.
|
|
const row = carry.locator(".carry-row").filter({ hasText: "Spaghetti Bolognese" });
|
|
await expect(row).toBeVisible();
|
|
|
|
// Select the meal and finish.
|
|
await row.locator('input[type="checkbox"]').check();
|
|
await carry.locator(".carry-submit").click();
|
|
|
|
// The modal closes, the meal appears in the meals panel, but no items are added.
|
|
await expect(carry).toHaveCount(0);
|
|
const panel = page.locator("#list-meals-panel");
|
|
await expect(panel.locator(".list-meal-row").filter({ hasText: "Spaghetti Bolognese" })).toBeVisible();
|
|
await expect(page.locator(".item-row")).toHaveCount(0);
|
|
});
|
|
|
|
test("the carry-over modal lists active lists newest first and excludes the current list", async ({
|
|
page,
|
|
}) => {
|
|
await registerAndLogin(page, "alice@example.com");
|
|
await createList(page, "First list");
|
|
await createList(page, "Second list");
|
|
|
|
// Open carry-over on the second (current) list.
|
|
await page.click(".carry-over-button");
|
|
const carry = page.locator(".meal-picker-backdrop");
|
|
await expect(carry).toBeVisible();
|
|
|
|
const options = carry.locator("#carry-source option");
|
|
// The current list is excluded; only the other list remains.
|
|
await expect(options).toHaveCount(2); // placeholder + one source
|
|
await expect(carry.locator("#carry-source")).toContainText("First list");
|
|
await expect(carry.locator("#carry-source")).not.toContainText("Second list");
|
|
});
|
|
|
|
test("carrying a meal does not remove it from the source list", async ({ page }) => {
|
|
await registerAndLogin(page, "alice@example.com");
|
|
await createMealWithIngredients(page, "Pasta", [{ name: "Penne" }]);
|
|
await createList(page, "Last week");
|
|
await page.click(".add-meal-button");
|
|
await page
|
|
.locator(".meal-picker-backdrop .meal-picker-button")
|
|
.filter({ hasText: "Pasta" })
|
|
.click();
|
|
await expect(page.locator(".item-row").filter({ hasText: "Penne" })).toBeVisible();
|
|
|
|
await createList(page, "This week");
|
|
const carry = await openCarryModal(page);
|
|
|
|
// Select the source list; the meal rows load via hx-get.
|
|
await carry.locator("#carry-source").selectOption({ label: "Last week" });
|
|
const row = carry.locator(".carry-row").filter({ hasText: "Pasta" });
|
|
await expect(row).toBeVisible({ timeout: 10_000 });
|
|
await row.locator('input[type="checkbox"]').check();
|
|
await carry.locator(".carry-submit").click();
|
|
await expect(carry).toHaveCount(0);
|
|
|
|
// The source list still has its meal and items.
|
|
await page.goto("/lists");
|
|
await page.locator(".list-card").filter({ hasText: "Last week" }).click();
|
|
await expect(page.locator("#list-meals-panel .list-meal-row").filter({ hasText: "Pasta" })).toBeVisible();
|
|
await expect(page.locator(".item-row").filter({ hasText: "Penne" })).toBeVisible();
|
|
});
|
|
|
|
test("submitting carry-over with no selection does not add a meal", async ({ page }) => {
|
|
await registerAndLogin(page, "alice@example.com");
|
|
await createMealWithIngredients(page, "Pasta", [{ name: "Penne" }]);
|
|
await createList(page, "Last week");
|
|
await page.click(".add-meal-button");
|
|
await page
|
|
.locator(".meal-picker-backdrop .meal-picker-button")
|
|
.filter({ hasText: "Pasta" })
|
|
.click();
|
|
await expect(page.locator(".item-row").filter({ hasText: "Penne" })).toBeVisible();
|
|
|
|
await createList(page, "This week");
|
|
const carry = await openCarryModal(page);
|
|
|
|
// Select the source list; the meal rows load via hx-get.
|
|
await carry.locator("#carry-source").selectOption({ label: "Last week" });
|
|
await expect(carry.locator(".carry-row").filter({ hasText: "Pasta" })).toBeVisible({
|
|
timeout: 10_000,
|
|
});
|
|
|
|
// Submit with nothing selected; the modal stays open and no meal is carried.
|
|
await carry.locator(".carry-submit").click();
|
|
await expect(carry).toBeVisible();
|
|
await expect(page.locator("#list-meals-panel .list-meal-row")).toHaveCount(0);
|
|
});
|