tests/account-invoices.spec.ts
Rama main · réplica de lectura del repositorio original.
import { expect } from "@playwright/test";
import { Plan, TeamUser } from "../apps/backend/src/database/models";
import { createInvoicesScenario } from "../apps/backend/src/database/seeds";
import { loggedTest } from "./logged-test";
import { ensureTeamOwner, screenshot } from "./util";
loggedTest.beforeEach(async ({ auth, team }) => {
await ensureTeamOwner({ team: team.team, user: auth.user });
});
loggedTest("account invoices - billing history", async ({ page, team }) => {
await createInvoicesScenario({ accountId: team.account.id });
await page.goto(`/${team.account.slug}/settings/invoices`);
await expect(page.getByRole("heading", { name: "Invoices" })).toBeVisible();
// Most recent first, with what tells them apart: the month, the number the
// customer's accounting quotes, whether it is settled, and the amount.
const rows = page.getByRole("row");
await expect(rows).toHaveCount(5);
await expect(rows.first()).toContainText("August 2026");
await expect(rows.first()).toContainText("ARGOS-0005");
await expect(rows.first()).toContainText("Due");
await expect(rows.first()).toContainText("€249.00");
await expect(rows.nth(1)).toContainText("Paid");
await expect(rows.nth(2)).toContainText("Void");
// Stripe keeps a refunded invoice paid: the refund has to show regardless.
await expect(rows.nth(3)).toContainText("Partially refunded");
await expect(rows.nth(3)).toContainText("€50.00 refunded");
await expect(rows.nth(4)).toContainText("Refunded");
// Before the menu is touched: a trigger left focused would freeze its own
// hover state into the baseline.
await screenshot(page, "account-invoices");
// Both ways to the document itself hang off the row's menu.
await page
.getByRole("button", { name: "Invoice ARGOS-0004 actions" })
.click();
await expect(
page.getByRole("option", { name: "View invoice" }),
).toHaveAttribute("href", /\/invoices\/\d+\/view$/);
await expect(
page.getByRole("option", { name: "Download PDF" }),
).toHaveAttribute("href", /\/invoices\/\d+\/pdf$/);
});
loggedTest(
"account invoices - reachable from the settings nav",
async ({ page, team }) => {
await createInvoicesScenario({ accountId: team.account.id });
await page.goto(`/${team.account.slug}/settings`);
await page.getByRole("link", { name: "Invoices" }).click();
await expect(page.getByRole("heading", { name: "Invoices" })).toBeVisible();
await expect(page).toHaveURL(
new RegExp(`/${team.account.slug}/settings/invoices`),
);
},
);
loggedTest(
"account invoices - no tab for a member, nor for a yearly contract",
async ({ page, team, auth, plan }) => {
await createInvoicesScenario({ accountId: team.account.id });
// A yearly plan is a negotiated contract, invoiced by the sales side.
await Plan.query().patch({ interval: "year" }).where("id", plan.id);
await page.goto(`/${team.account.slug}/settings`);
await expect(page.getByRole("link", { name: "Billing" })).toBeVisible();
await expect(page.getByRole("link", { name: "Invoices" })).toHaveCount(0);
await Plan.query().patch({ interval: "month" }).where("id", plan.id);
await TeamUser.query()
.patch({ userLevel: "member" })
.where({ teamId: team.team.id, userId: auth.user.id });
await page.goto(`/${team.account.slug}/settings`);
await expect(page.getByRole("link", { name: "Invoices" })).toHaveCount(0);
},
);