Files
kca/e2e/tests/san.spec.js

111 lines
5.4 KiB
JavaScript

// XCA-style subject alternative name editor on the certificate form.
const { test, expect } = require("@playwright/test");
function unique(prefix) {
return `${prefix}-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
}
async function field(page, label) {
return page.locator("label").filter({ hasText: label }).first().locator("..").locator("input, select, textarea").first();
}
async function login(page, username) {
await page.goto("/login");
await page.getByRole("link", { name: "Sign in with default" }).click();
await page.getByPlaceholder("Enter any user/subject").fill(username);
await page.getByRole("button", { name: "Sign-in" }).click();
await expect(page.getByRole("heading", { name: "Vaults" })).toBeVisible();
}
test("XCA-style subject alternative name editor", async ({ page }) => {
const user = unique("san");
const vaultName = unique("SAN");
const certName = unique("san.example");
await login(page, user);
await page.getByRole("button", { name: "New vault" }).click();
await page.getByPlaceholder("Production PKI").fill(vaultName);
await page.getByRole("button", { name: "Create vault" }).click();
await page.getByRole("link", { name: vaultName }).click();
const vaultUrl = page.url();
const settingsResponse = page.waitForResponse(
(response) => response.url().includes("/api/vaults/") && response.url().endsWith("/settings") && response.ok()
);
await page.goto(`${vaultUrl}/certificates/new`);
await settingsResponse;
await (await field(page, "Internal name")).fill(certName);
await (await field(page, "Common name (CN)")).fill(certName);
await page.locator("#new-key").check();
// Cancelling discards the draft rows and leaves the original input untouched.
await page.locator("#edit-san").click();
let dialog = page.getByRole("dialog", { name: "Subject alternative names" });
await expect(dialog).toBeVisible();
await dialog.getByRole("button", { name: "Add", exact: true }).click();
await dialog.locator(".san-entry .san-value").fill("discarded.example");
await dialog.getByRole("button", { name: "Cancel", exact: true }).click();
await expect(dialog).toHaveCount(0);
await expect(page.locator("#san-text")).toHaveValue("");
await page.locator("#edit-san").click();
dialog = page.getByRole("dialog", { name: "Subject alternative names" });
await expect(dialog.locator(".san-entry")).toHaveCount(0);
await dialog.getByLabel("Critical", { exact: true }).check();
await dialog.getByLabel("Copy Common Name", { exact: true }).check();
await dialog.getByRole("button", { name: "Add", exact: true }).click();
await dialog.locator(".san-entry").nth(0).locator("select").selectOption("DNS");
await dialog.locator(".san-entry").nth(0).locator(".san-value").fill(`www.${certName}`);
await dialog.getByRole("button", { name: "Add", exact: true }).click();
await dialog.locator(".san-entry").nth(1).locator("select").selectOption("IP");
await dialog.locator(".san-entry").nth(1).locator(".san-value").fill("192.0.2.10");
// Row deletion works without disturbing the remaining entries.
await dialog.getByRole("button", { name: "Add", exact: true }).click();
await dialog.locator(".san-entry").nth(2).locator(".san-value").fill("discarded.example");
await dialog.locator(".san-entry").nth(2).getByRole("button", { name: "Delete", exact: true }).click();
await expect(dialog.locator(".san-entry")).toHaveCount(2);
await dialog.getByRole("button", { name: "Apply", exact: true }).click();
await expect(dialog).toHaveCount(0);
await expect(page.locator("#san-text")).toHaveValue(
`DNS:copycn, DNS:www.${certName}, IP:192.0.2.10`
);
// Reopening reconstructs the draft from the applied value and criticality.
await page.locator("#edit-san").click();
dialog = page.getByRole("dialog", { name: "Subject alternative names" });
await expect(dialog.getByLabel("Critical", { exact: true })).toBeChecked();
await expect(dialog.getByLabel("Copy Common Name", { exact: true })).toBeChecked();
await expect(dialog.locator(".san-entry")).toHaveCount(2);
await expect(dialog.locator(".san-entry").nth(0).locator("select")).toHaveValue("DNS");
await expect(dialog.locator(".san-entry").nth(0).locator(".san-value")).toHaveValue(`www.${certName}`);
await expect(dialog.locator(".san-entry").nth(1).locator("select")).toHaveValue("IP");
await expect(dialog.locator(".san-entry").nth(1).locator(".san-value")).toHaveValue("192.0.2.10");
await dialog.getByRole("button", { name: "Cancel", exact: true }).click();
await page.getByRole("button", { name: "Validate extensions" }).click();
await expect(page.getByText("Extension preview")).toBeVisible();
await expect(page.getByText("192.0.2.10", { exact: false })).toBeVisible();
await page.getByRole("button", { name: "Create certificate" }).click();
await expect(page.getByRole("heading", { name: certName })).toBeVisible();
const certApiUrl = page.url().replace(/\/vaults\//, "/api/vaults/");
const detail = await page.evaluate(async (url) => {
const response = await fetch(url);
return response.json();
}, certApiUrl);
expect(detail.extension_spec.subject_alt_name).toEqual({
critical: true,
entries: [
{ type: "dns", value: certName },
{ type: "dns", value: `www.${certName}` },
{ type: "ip", value: "192.0.2.10" },
],
});
await page.locator(".nav-tabs").getByRole("tab", { name: "Extensions", exact: true }).click();
await expect(page.getByText("192.0.2.10", { exact: false })).toBeVisible();
});