Files
dashboard/e2e/tests/dns-nameservers.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

169 lines
7.6 KiB
TypeScript

import { test, expect } from "../helpers/fixtures";
import { navigateTo } from "../helpers/auth";
import { generateRandomName } from "../helpers/utils";
import { deleteGroupsByPrefix, deleteNameserverGroupsByPrefix } from "../helpers/api";
let nsName = "";
let nsDomain = "";
let nsGroup1 = "";
let nsGroup2 = "";
test.describe.serial("DNS - Nameservers @dns", () => {
test("Should show all 4 DNS presets and create a custom nameserver", async ({
dashboardAsOwner: page,
}) => {
// Clean up stale nameservers and groups from previous runs
await deleteNameserverGroupsByPrefix(page, "test-ns-");
await deleteNameserverGroupsByPrefix(page, "renamed-ns-");
await deleteGroupsByPrefix(page, "ns-group-");
await deleteGroupsByPrefix(page, "ns-domain-");
await navigateTo(page, "/dns/nameservers");
await page.getByTestId("open-add-nameserver").click();
await expect(page.getByTestId("nameserver-preset-google")).toBeVisible();
await expect(page.getByTestId("nameserver-preset-cloudflare")).toBeVisible();
await expect(page.getByTestId("nameserver-preset-quad9")).toBeVisible();
await expect(page.getByTestId("nameserver-preset-custom")).toBeVisible();
// Create via Custom DNS
await page.getByTestId("nameserver-preset-custom").click();
await page.getByTestId("nameserver-ip-input").first().fill("10.0.0.1");
await page.getByTestId("add-nameserver-row").click();
await page.getByTestId("nameserver-ip-input").last().fill("10.0.0.2");
await page.getByTestId("nameserver-port-input").last().fill("5353");
const groupName = generateRandomName("ns-group-");
nsGroup1 = groupName;
await page.getByTestId("nameserver-groups-selector").click();
await page.getByTestId("nameserver-groups-selector-search").fill(groupName);
await page.getByTestId("nameserver-groups-selector-search").press("Enter");
await page.getByTestId("nameserver-groups-selector-search").press("Escape");
await page.getByTestId("nameserver-continue").click();
// Domains tab
const d = generateRandomName("ns-domain-");
nsDomain = `${d}.internal`;
await page.getByTestId("add-match-domain").click();
await page.getByTestId("domain-input").last().fill(nsDomain);
await page.getByTestId("nameserver-mark-search-domains").click();
await page.getByTestId("nameserver-continue").click();
// General tab
const name = generateRandomName("test-ns-");
nsName = name;
await page.getByTestId("nameserver-name-input").fill(name);
await page.getByTestId("nameserver-description-input").fill("Test nameserver");
await page.getByTestId("submit-nameserver").click();
});
test("Should verify the nameserver in the table", async ({ dashboardAsOwner: page }) => {
const row = page.locator("tr").filter({ hasText: nsName });
await expect(row).toBeVisible({ timeout: 10_000 });
await expect(row.getByText(nsDomain)).toBeVisible();
await expect(row.getByText("10.0.0.1")).toBeVisible();
await expect(row.getByText("10.0.0.2")).toBeVisible();
await expect(row.getByText(nsGroup1)).toBeVisible();
// Active state moved into the row action menu: a freshly-created
// nameserver is enabled, so the toggle item reads "Disable".
await row.getByTestId("nameserver-actions").click({ force: true });
await expect(page.getByTestId("nameserver-active-toggle")).toContainText(
"Disable",
);
await page.keyboard.press("Escape");
});
test("Should edit the nameserver", async ({ dashboardAsOwner: page }) => {
await page.locator("tr").filter({ hasText: nsName }).getByTestId("nameserver-name-cell").click({ force: true });
// Nameserver tab — change IPs and add group
await page.getByTestId("nameserver-tab-nameserver").click({ force: true });
await expect(page.getByTestId("nameserver-ip-input").first()).toBeVisible();
await page.getByTestId("nameserver-ip-input").first().fill("192.168.1.1");
await page.getByTestId("nameserver-ip-input").last().fill("192.168.1.2");
const groupName = generateRandomName("ns-group-");
nsGroup2 = groupName;
await page.getByTestId("nameserver-groups-selector").click();
await page.getByTestId("nameserver-groups-selector-search").fill(groupName);
await page.getByTestId("nameserver-groups-selector-search").press("Enter");
await page.getByTestId("nameserver-groups-selector-search").press("Escape");
// Domains tab — remove domain
await page.getByTestId("nameserver-tab-domains").click({ force: true });
await page.getByTestId("domain-input-remove").click({ force: true });
// General tab — rename
await page.getByTestId("nameserver-tab-general").click({ force: true });
const newName = generateRandomName("renamed-ns-");
await page.getByTestId("nameserver-name-input").fill(newName);
await page.getByTestId("nameserver-description-input").fill("Updated");
await page.getByTestId("submit-nameserver").click();
await expect(page.getByText("successfully").first()).toBeVisible({ timeout: 10_000 });
// Verify the renamed nameserver appears in the table
await expect(page.locator("tr").filter({ hasText: newName })).toBeVisible({ timeout: 10_000 });
nsName = newName;
});
test("Should verify edits and toggle active state", async ({ dashboardAsOwner: page }) => {
await navigateTo(page, "/dns/nameservers");
const row = page.locator("tr").filter({ hasText: nsName });
await expect(row).toBeVisible({ timeout: 10_000 });
await expect(row.getByText("192.168.1.1")).toBeVisible();
await expect(row.getByText("192.168.1.2")).toBeVisible();
// Distribution-groups cell now renders a count badge (2 groups after edit).
await expect(row.getByText("2 Groups")).toBeVisible();
// Toggle active off and back on via the row action menu.
// Two races to defend against on each toggle:
// 1. Radix leaves `pointer-events: none` on body briefly during the
// close transition — re-opening without `force: true` makes
// Playwright auto-wait for the body to accept pointer events.
// 2. The toast fires before SWR refetches `/dns/nameservers`, so the
// row's `ns.enabled` is stale and the re-opened menu shows the
// old label. Wait for the GET refetch before re-opening.
const actions = row.getByTestId("nameserver-actions");
const toggle = page.getByTestId("nameserver-active-toggle");
const waitForRefetch = () =>
page.waitForResponse(
(r) =>
r.url().includes("/api/dns/nameservers") &&
r.request().method() === "GET" &&
r.ok(),
{ timeout: 10_000 },
);
await actions.click({ force: true });
let refetch = waitForRefetch();
await toggle.click({ force: true });
await expect(page.getByText("successfully disabled").first()).toBeVisible();
await refetch;
await expect(toggle).toBeHidden();
await actions.click();
await expect(toggle).toContainText("Enable");
refetch = waitForRefetch();
await toggle.click({ force: true });
await expect(page.getByText("successfully enabled").first()).toBeVisible();
await refetch;
await expect(toggle).toBeHidden();
});
test("Should delete the nameserver and groups", async ({ dashboardAsOwner: page }) => {
await page.locator("tr").filter({ hasText: nsName }).getByTestId("nameserver-actions").click({ force: true });
await page.getByTestId("delete-nameserver").click({ force: true });
await page.getByTestId("confirmation.confirm").click({ force: true });
await expect(page.locator("tr").filter({ hasText: nsName })).not.toBeVisible();
for (const group of [nsGroup1, nsGroup2]) {
if (!group) continue;
await deleteGroupsByPrefix(page, group);
}
});
});