Files
dashboard/e2e/tests/team-users-approval-and-billing.spec.ts
Maycon Santos 7653e3411c Merge NetBird cloud edition into the dashboard (#674)
Brings the unified dashboard into the open-source repo. Premium features
ship in the open code, gated at runtime via NETBIRD_CLOUD and
NETBIRD_LICENSED, with upgrade prompts for unlicensed self-hosted
deployments. Adds the cloud-only feature areas (billing, integrations,
MSP, traffic events, notifications) and the Playwright e2e suite.
2026-06-21 16:01:08 +02:00

151 lines
5.3 KiB
TypeScript

import { expect, test } from "../helpers/fixtures";
import { loginToApp, navigateTo } from "../helpers/auth";
import { deleteUserByEmail } from "../helpers/api";
test.setTimeout(60_000);
test.describe.serial("User Approval & Billing Admin @team", () => {
// ── User Approval ────────────────────────────────────────────────────
test("Should show approval pending for the second user", async ({
browser,
dashboardAsOwner: ownerPage,
}) => {
// Clean up user from previous runs so approval flow starts fresh
await deleteUserByEmail(ownerPage, "user@localhost.test");
const context = await browser.newContext({
storageState: "e2e/fixtures/auth/user.json",
});
const page = await context.newPage();
await loginToApp(page, "user");
await expect(page.getByText("User Approval Pending")).toBeVisible();
await context.close();
});
test("Should approve the pending user", async ({
dashboardAsOwner: page,
}) => {
await navigateTo(page, "/team/users");
const pendingRow = page.locator("tr").filter({ hasText: "Pending" });
await expect(pendingRow).toBeVisible();
await pendingRow.getByRole("button", { name: "Approve" }).click();
await expect(pendingRow).not.toBeVisible();
});
test("Should delete the approved user", async ({
dashboardAsOwner: page,
}) => {
const userRow = page
.locator("tr")
.filter({ hasText: "user@localhost.test" });
await expect(userRow).toBeVisible();
// Row actions are now behind a dropdown menu.
await userRow.getByTestId("user-actions").click({ force: true });
await page.getByTestId("delete-user").click({ force: true });
await page.getByTestId("confirmation.confirm").click();
await expect(userRow).not.toBeVisible();
});
// ── Billing Admin ────────────────────────────────────────────────────
test("Should login as second user to trigger registration", async ({
browser,
}) => {
const context = await browser.newContext({
storageState: "e2e/fixtures/auth/user.json",
});
const page = await context.newPage();
await loginToApp(page, "user");
await context.close();
});
test("Should approve user and assign Billing Admin role", async ({
dashboardAsOwner: page,
}) => {
await navigateTo(page, "/team/users");
const pendingRow = page.locator("tr").filter({ hasText: "Pending" });
if (await pendingRow.isVisible({ timeout: 5_000 }).catch(() => false)) {
await pendingRow.getByRole("button", { name: "Approve" }).click();
await expect(pendingRow).not.toBeVisible();
}
const userRow = page
.locator("tr")
.filter({ hasText: "user@localhost.test" });
await expect(userRow).toBeVisible();
await userRow.getByTestId("user-name-cell").click();
await expect(
page.getByTestId("breadcrumb-item").filter({ hasText: /^user/i }),
).toBeVisible();
await expect(page.getByTestId("user-role-selector")).toBeEnabled({
timeout: 15_000,
});
const currentRole = await page
.getByTestId("user-role-selector")
.textContent();
if (!currentRole?.includes("Billing Admin")) {
await page.getByTestId("user-role-selector").click();
await page
.getByTestId("user-role-selector-item")
.filter({ hasText: "Billing Admin" })
.click();
await page.getByTestId("save-changes").click();
}
});
test("Should show Plans & Billing and Invoices for the Billing Admin", async ({
browser,
}) => {
const context = await browser.newContext({
storageState: "e2e/fixtures/auth/user.json",
});
const page = await context.newPage();
await loginToApp(page, "user");
await expect(page.getByTestId("user-dropdown")).toBeVisible({
timeout: 15_000,
});
await page.getByTestId("user-dropdown").click({ force: true });
await page.getByText("Plans & Billing").click();
await expect(
page.getByTestId("settings-tab-plans-and-billing"),
).toBeVisible({ timeout: 10_000 });
await expect(page.getByTestId("settings-tab-invoices")).toBeVisible();
await expect(
page.getByTestId("settings-content-plans-and-billing"),
).toBeVisible();
await page.getByTestId("settings-tab-invoices").click();
await expect(page.getByTestId("settings-content-invoices")).toBeVisible();
await expect(
page.getByTestId("settings-tab-authentication"),
).not.toBeVisible();
await expect(
page.getByTestId("settings-tab-permissions"),
).not.toBeVisible();
await expect(page.getByTestId("settings-tab-clients")).not.toBeVisible();
await context.close();
});
test("Should delete the second user", async ({ dashboardAsOwner: page }) => {
await navigateTo(page, "/team/users");
const userRow = page
.locator("tr")
.filter({ hasText: "user@localhost.test" });
await expect(userRow).toBeVisible();
// Row actions are now behind a dropdown menu.
await userRow.getByTestId("user-actions").click({ force: true });
await page.getByTestId("delete-user").click({ force: true });
await page.getByTestId("confirmation.confirm").click();
await expect(userRow).not.toBeVisible();
});
});