151 lines
4.7 KiB
TypeScript
151 lines
4.7 KiB
TypeScript
import { test, expect } from "@playwright/test";
|
|
|
|
const apiOk = (data: unknown) => ({
|
|
data,
|
|
meta: { timestamp: new Date().toISOString(), version: "v1" },
|
|
error: null
|
|
});
|
|
|
|
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({
|
|
transactions: [
|
|
{
|
|
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({
|
|
contentType: "application/json",
|
|
body: JSON.stringify(apiOk({ total: "12.50", count: 1, income: "0.00", expense: "12.50", net: "-12.50" }))
|
|
});
|
|
});
|
|
|
|
await page.route("**/api/transactions/merchants**", async (route) => {
|
|
await route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify(apiOk([]))
|
|
});
|
|
});
|
|
|
|
await page.route("**/api/transactions/cashflow**", async (route) => {
|
|
await route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify(apiOk([]))
|
|
});
|
|
});
|
|
|
|
await page.route("**/api/transactions?*", async (route) => {
|
|
await route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify(listResponse)
|
|
});
|
|
});
|
|
await page.route("**/api/transactions", async (route) => {
|
|
await route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify(listResponse)
|
|
});
|
|
});
|
|
await page.route("**/api/transactions/sync", async (route) => {
|
|
await route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify(apiOk({ created: 1 }))
|
|
});
|
|
});
|
|
await page.route("**/api/transactions/manual", async (route) => {
|
|
await route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify(apiOk({ id: "tx_2" }))
|
|
});
|
|
});
|
|
await page.route("**/api/transactions/*/derived", async (route) => {
|
|
await route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify(apiOk({}))
|
|
});
|
|
});
|
|
await page.route("**/api/accounts**", async (route) => {
|
|
await route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify(
|
|
apiOk({
|
|
accounts: [
|
|
{ viewRef: "acct_1", institutionName: "Chase", accountType: "checking", mask: "1234" }
|
|
],
|
|
total: 1
|
|
})
|
|
)
|
|
});
|
|
});
|
|
|
|
await page.goto("/transactions");
|
|
|
|
await expect(page.getByRole("button", { name: "Sync" })).toBeVisible();
|
|
await expect(page.getByRole("link", { name: "Export", exact: true })).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();
|
|
|
|
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({
|
|
transactions: [
|
|
{
|
|
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"
|
|
}
|
|
],
|
|
total: 1
|
|
});
|
|
await page.getByRole("button", { name: "Save" }).click();
|
|
await expect(page.getByText("Transaction updated.")).toBeVisible();
|
|
});
|