145 lines
4.8 KiB
TypeScript
145 lines
4.8 KiB
TypeScript
import { test, expect, type Page } from "@playwright/test";
|
|
|
|
test.describe("Todo lifecycle E2E", () => {
|
|
test.describe.configure({ mode: "serial" });
|
|
|
|
const title = `E2E Todo ${Date.now()}`;
|
|
const description = "E2E Desc";
|
|
|
|
// Hitta create-formen
|
|
const getCreateForm = (page: Page) =>
|
|
page.locator("form", { has: page.locator('input[name="title"]') });
|
|
|
|
// Hitta tabellraden för en titel
|
|
const getRowByTitle = (page: Page, t: string) =>
|
|
page.locator("tr", { has: page.locator(".fw-semibold", { hasText: t }) });
|
|
|
|
test("user can create a todo and see it on dashboard", async ({ page }) => {
|
|
await page.goto("/dashboard");
|
|
|
|
const createForm = getCreateForm(page);
|
|
|
|
// Fyll i formuläret
|
|
await createForm.locator('input[name="title"]').fill(title);
|
|
await createForm.locator('textarea[name="description"]').fill(description);
|
|
await createForm.locator('select[name="status"]').selectOption("not-started");
|
|
|
|
// Skicka
|
|
await createForm.locator('button[type="submit"]').click();
|
|
|
|
// Syns i listan
|
|
await expect(page.locator(".fw-semibold", { hasText: title })).toBeVisible();
|
|
});
|
|
|
|
test("user can update status to done", async ({ page }) => {
|
|
await page.goto("/dashboard");
|
|
|
|
const row = getRowByTitle(page, title);
|
|
await expect(row).toBeVisible();
|
|
|
|
// Ändra status
|
|
const statusSelect = row.locator('select[name="status"]');
|
|
await Promise.all([
|
|
page.waitForLoadState("networkidle"),
|
|
statusSelect.selectOption("done"),
|
|
]);
|
|
|
|
// Kontrollera att status är done
|
|
const rowAfter = getRowByTitle(page, title);
|
|
await expect(rowAfter.locator('select[name="status"]')).toHaveValue("done");
|
|
});
|
|
|
|
test("user can change status multiple times", async ({ page }) => {
|
|
await page.goto("/dashboard");
|
|
|
|
const row = getRowByTitle(page, title);
|
|
await expect(row).toBeVisible();
|
|
|
|
const statusSelect = row.locator('select[name="status"]');
|
|
|
|
// not-started -> in-progress
|
|
await Promise.all([
|
|
page.waitForLoadState("networkidle"),
|
|
statusSelect.selectOption("in-progress"),
|
|
]);
|
|
await expect(getRowByTitle(page, title).locator('select[name="status"]')).toHaveValue(
|
|
"in-progress"
|
|
);
|
|
|
|
// in-progress -> done
|
|
await Promise.all([
|
|
page.waitForLoadState("networkidle"),
|
|
statusSelect.selectOption("done"),
|
|
]);
|
|
await expect(getRowByTitle(page, title).locator('select[name="status"]')).toHaveValue("done");
|
|
});
|
|
|
|
test("cannot create todo without required fields", async ({ page }) => {
|
|
await page.goto("/dashboard");
|
|
|
|
const createForm = getCreateForm(page);
|
|
|
|
// Tomma fält (bara mellanslag)
|
|
await createForm.locator('input[name="title"]').fill(" ");
|
|
await createForm.locator('textarea[name="description"]').fill(" ");
|
|
await createForm.locator('button[type="submit"]').click();
|
|
|
|
// Felmeddelande visas
|
|
await expect(page.getByText("Title och description krävs.")).toBeVisible();
|
|
});
|
|
|
|
test("cannot create todo when description is missing", async ({ page }) => {
|
|
const badTitle = `E2E Missing Desc ${Date.now()}`;
|
|
await page.goto("/dashboard");
|
|
|
|
const createForm = getCreateForm(page);
|
|
|
|
await createForm.locator('input[name="title"]').fill(badTitle);
|
|
await createForm.locator('textarea[name="description"]').fill(" ");
|
|
await createForm.locator('button[type="submit"]').click();
|
|
|
|
// Felmeddelande + ingen rad skapas
|
|
await expect(page.getByText("Title och description krävs.")).toBeVisible();
|
|
await expect(page.locator(".fw-semibold", { hasText: badTitle })).toHaveCount(0);
|
|
});
|
|
|
|
test("invalid status is normalized to not-started on create", async ({ page }) => {
|
|
const t = `E2E Weird Status ${Date.now()}`;
|
|
await page.goto("/dashboard");
|
|
|
|
const createForm = getCreateForm(page);
|
|
|
|
await createForm.locator('input[name="title"]').fill(t);
|
|
await createForm.locator('textarea[name="description"]').fill("desc");
|
|
|
|
// Sätt ett ogiltigt status-värde
|
|
await createForm.locator('select[name="status"]').evaluate((sel: HTMLSelectElement) => {
|
|
sel.value = "hax-status";
|
|
});
|
|
|
|
await createForm.locator('button[type="submit"]').click();
|
|
|
|
// Ska defaulta till not-started
|
|
const row = getRowByTitle(page, t);
|
|
await expect(row).toBeVisible();
|
|
await expect(row.locator('select[name="status"]')).toHaveValue("not-started");
|
|
|
|
// Städa
|
|
await row.locator('button[title="Delete"]').click();
|
|
await expect(page.locator(".fw-semibold", { hasText: t })).toHaveCount(0);
|
|
});
|
|
|
|
test("user can delete todo", async ({ page }) => {
|
|
await page.goto("/dashboard");
|
|
|
|
const row = getRowByTitle(page, title);
|
|
await expect(row).toBeVisible();
|
|
|
|
// Ta bort
|
|
await row.locator('button[title="Delete"]').click();
|
|
|
|
// Ska vara borta
|
|
await expect(page.locator(".fw-semibold", { hasText: title })).toHaveCount(0);
|
|
});
|
|
});
|