diff --git a/playwright.config.ts b/playwright.config.ts index fe70829..8875362 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -4,12 +4,12 @@ export default defineConfig({ testDir: "./tests", timeout: 30_000, use: { - baseURL: "http://localhost:3001", + baseURL: "http://localhost:3052", trace: "on-first-retry" }, webServer: { command: "npm run dev", - url: "http://localhost:3001", + url: "http://localhost:3052", reuseExistingServer: !process.env.CI } }); diff --git a/tests/dashboard.spec.ts b/tests/dashboard.spec.ts index c683d7f..78211ac 100644 --- a/tests/dashboard.spec.ts +++ b/tests/dashboard.spec.ts @@ -6,9 +6,21 @@ const apiOk = (data: unknown) => ({ error: null }); -test.beforeEach(async ({ page }) => { - await page.addInitScript(() => { - localStorage.setItem("ledgerone_user_id", "test_user"); +test.beforeEach(async ({ context, page }) => { + await context.addCookies([ + { name: "ledgerone_auth", value: "1", domain: "localhost", path: "/" }, + ]); + await page.route("**/api/auth/me", async (route) => { + await route.fulfill({ + contentType: "application/json", + body: JSON.stringify(apiOk({ user: { id: "user_1", email: "test@example.com", name: "Test User" } })), + }); + }); + await page.route("**/api/billing/subscription", async (route) => { + await route.fulfill({ + contentType: "application/json", + body: JSON.stringify(apiOk({ plan: "pro", status: "active" })), + }); }); }); @@ -41,16 +53,27 @@ test("dashboard renders summary, cashflow, and merchants", async ({ page }) => { ) }); }); - await page.route("**/api/accounts**", async (route) => { + await page.route("**/api/view/accounts**", async (route) => { await route.fulfill({ contentType: "application/json", - body: JSON.stringify(apiOk([{ id: "acct_1" }, { id: "acct_2" }])) + body: JSON.stringify(apiOk({ accounts: [{ viewRef: "acct_1" }, { viewRef: "acct_2" }], total: 2 })) + }); + }); + await page.route("**/api/view/transactions**", async (route) => { + await route.fulfill({ + contentType: "application/json", + body: JSON.stringify(apiOk({ + transactions: [ + { viewRef: "tx_1", date: "2025-09-10", description: "Acme Corp", category: "Office", amount: "-25.00" } + ], + total: 1 + })) }); }); await page.goto("/app"); await expect(page.getByText("Income (30d)")).toBeVisible(); await expect(page.getByText("Top Merchants")).toBeVisible(); - await expect(page.getByText("Acme Corp")).toBeVisible(); - await expect(page.getByText("2025-09")).toBeVisible(); + await expect(page.getByText("Acme Corp").first()).toBeVisible(); + await expect(page.getByText("Sep 2025")).toBeVisible(); }); diff --git a/tests/transactions.spec.ts b/tests/transactions.spec.ts index 7632e6d..abb5497 100644 --- a/tests/transactions.spec.ts +++ b/tests/transactions.spec.ts @@ -6,25 +6,42 @@ const apiOk = (data: unknown) => ({ error: null }); -test.beforeEach(async ({ page }) => { - await page.addInitScript(() => { - localStorage.setItem("ledgerone_user_id", "test_user"); +test.beforeEach(async ({ context, page }) => { + await context.addCookies([ + { name: "ledgerone_auth", value: "1", domain: "localhost", path: "/" }, + ]); + await page.route("**/api/auth/me", async (route) => { + await route.fulfill({ + contentType: "application/json", + body: JSON.stringify(apiOk({ user: { id: "user_1", email: "test@example.com", name: "Test User" } })), + }); + }); + await page.route("**/api/billing/subscription", async (route) => { + await route.fulfill({ + contentType: "application/json", + body: JSON.stringify(apiOk({ plan: "pro", status: "active" })), + }); }); }); test("transactions page supports filters, manual add, and edit", async ({ page }) => { - let listResponse = apiOk([ + let listResponse = apiOk({ + transactions: [ { - id: "tx_1", - name: "Coffee Bar", + viewRef: "tx_1", + description: "Coffee Bar", amount: "12.50", category: "Dining", note: "", + attribution: "mine", + split: { mode: "none", minePercent: 50, yoursPercent: 50, mineAmount: 0, yoursAmount: 0 }, status: "raw", hidden: false, date: "2025-01-10" } - ]); + ], + total: 1 + }); await page.route("**/api/transactions/summary**", async (route) => { await route.fulfill({ @@ -81,44 +98,53 @@ test("transactions page supports filters, manual add, and edit", async ({ page } await route.fulfill({ contentType: "application/json", body: JSON.stringify( - apiOk([ - { id: "acct_1", institutionName: "Chase", accountType: "checking", mask: "1234" } - ]) + apiOk({ + accounts: [ + { viewRef: "acct_1", institutionName: "Chase", accountType: "checking", mask: "1234" } + ], + total: 1 + }) ) }); }); await page.goto("/transactions"); - await expect(page.getByText("Sync transactions")).toBeVisible(); - await expect(page.getByText("Export CSV")).toBeVisible(); + await expect(page.getByRole("button", { name: "Sync" })).toBeVisible(); + await expect(page.getByRole("link", { name: "Export", exact: true })).toBeVisible(); - await page.getByRole("button", { name: "Show filters" }).click(); - await expect(page.getByPlaceholder("Search description")).toBeVisible(); + await page.getByRole("button", { name: "Filters" }).click(); + await expect(page.getByPlaceholder("Description...")).toBeVisible(); await page.getByRole("button", { name: "Add manual" }).click(); await expect(page.getByText("Manual transaction")).toBeVisible(); - await page.getByPlaceholder("Description").fill("Manual payment"); - await page.getByPlaceholder("0.00").fill("40.25"); - await page.getByRole("button", { name: "Save manual transaction" }).click(); + const manualForm = page.locator("form").first(); + await manualForm.getByText("Description").locator("..").getByRole("textbox").fill("Manual payment"); + await manualForm.getByPlaceholder("-42.50").fill("40.25"); + await manualForm.getByRole("button", { name: "Save" }).click(); await expect(page.getByText("Manual transaction saved.")).toBeVisible(); await page.getByRole("button", { name: "Edit" }).click(); await page.getByPlaceholder("Category").fill("Coffee"); await page.getByPlaceholder("Note").fill("Edited note"); - listResponse = apiOk([ + listResponse = apiOk({ + transactions: [ { - id: "tx_1", - name: "Coffee Bar", + viewRef: "tx_1", + description: "Coffee Bar", amount: "12.50", category: "Coffee", note: "Edited note", + attribution: "mine", + split: { mode: "none", minePercent: 50, yoursPercent: 50, mineAmount: 0, yoursAmount: 0 }, status: "user", hidden: false, date: "2025-01-10" } - ]); - await page.getByRole("button", { name: "Save edits" }).click(); + ], + total: 1 + }); + await page.getByRole("button", { name: "Save" }).click(); await expect(page.getByText("Transaction updated.")).toBeVisible(); });