Files
kca/e2e/tests/core.spec.js
Walter Oggioni a2457ab1f4
CI / Build and push docker image (push) Successful in 1m56s
CI / Build and push OCSP image (push) Successful in 33s
Move certificate transforms into header dropdown
2026-10-27 07:39:13 +08:00

181 lines
9.5 KiB
JavaScript

// End-to-end smoke test for the KCA PKI workflow.
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).toHaveURL(/\/$/);
await expect(page.getByRole("heading", { name: "Vaults" })).toBeVisible();
}
test("core PKI workflow", async ({ page }) => {
const user = unique("e2e");
const vaultName = unique("Vault");
const caName = unique("E2E CA");
const leafName = unique("www.e2e.example");
await login(page, user);
// Vault
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();
await expect(page.getByRole("heading", { name: vaultName })).toBeVisible();
const vaultUrl = page.url();
// Key
await page.getByRole("link", { name: "New key" }).click();
await page.getByPlaceholder("webserver-key").fill(`${caName} key`);
await page.getByRole("button", { name: "Generate key" }).click();
await expect(page.getByRole("heading", { name: `${caName} key` })).toBeVisible();
// CA certificate
await page.goto(`${vaultUrl}/certificates/new`);
await expect(page.getByRole("heading", { name: "New certificate" })).toBeVisible();
await (await field(page, "Internal name")).fill(caName);
await (await field(page, "Common name (CN)")).fill(caName);
await (await field(page, "Organization (O)")).fill("KCA E2E");
await (await field(page, "Country (C)")).fill("DE");
await page.locator("#new-key").check();
await (await field(page, "Basic constraints")).selectOption("ca");
await page.locator("label").filter({ hasText: "Certificate sign" }).locator("..").locator("input[type=checkbox]").check();
await page.locator("label").filter({ hasText: "CRL sign" }).locator("..").locator("input[type=checkbox]").check();
await page.getByRole("button", { name: "Create certificate" }).click();
await expect(page.getByText("CA certificate", { exact: false })).toBeVisible();
const caUrl = page.url();
await page.locator(".nav-tabs").getByRole("tab", { name: "CA", exact: true }).click();
await (await field(page, "CA · default signing template")).selectOption("builtin:tls-server");
await page.getByRole("button", { name: "Save CA" }).click();
await expect(page.getByText("CA properties saved.")).toBeVisible();
// Leaf signed by the CA
await page.goto(`${vaultUrl}/certificates/new`);
await (await field(page, "Internal name")).fill(leafName);
await (await field(page, "Issuer")).selectOption({ label: caName });
await (await field(page, "Common name (CN)")).fill(leafName);
await (await field(page, "Organization (O)")).fill("KCA E2E");
await (await field(page, "Country (C)")).fill("DE");
await page.locator("#new-key").check();
await expect(page.getByText("CA default template: TLS server")).toBeVisible();
await page.getByRole("button", { name: "Apply CA default" }).click();
await expect(page.locator("label").filter({ hasText: "TLS server" }).locator("..").locator("input[type=checkbox]")).toBeChecked();
await page.locator("input[placeholder*='DNS:example.com']").fill(`DNS:${leafName}`);
await page.getByRole("button", { name: "Create certificate" }).click();
await expect(page.getByRole("heading", { name: leafName })).toBeVisible();
await page.locator(".nav-tabs").getByRole("tab", { name: "Extensions", exact: true }).click();
await expect(page.getByText("serverAuth", { exact: false })).toBeVisible();
// Certificate list links to the detail route in both views.
const certDetailUrl = /\/vaults\/[^/]+\/certificates\/[^/]+$/;
await page.goto(`${vaultUrl}/certificates`);
await expect(page.getByRole("button", { name: "Select all revocable" })).toHaveCount(0);
await expect(page.getByRole("button", { name: "Revoke selected" })).toHaveCount(0);
await expect(page.locator("input[data-cert-id]")).toHaveCount(0);
await page.getByRole("link", { name: leafName, exact: true }).click();
await expect(page).toHaveURL(certDetailUrl);
await expect(page.getByRole("heading", { name: leafName })).toBeVisible();
await page.goto(`${vaultUrl}/certificates`);
await page.getByRole("button", { name: "Plain view" }).click();
await expect(page.getByRole("button", { name: "Select all revocable" })).toHaveCount(0);
await expect(page.getByRole("button", { name: "Revoke selected" })).toHaveCount(0);
await expect(page.locator("input[data-cert-id]")).toHaveCount(0);
await page.getByRole("link", { name: leafName, exact: true }).click();
await expect(page).toHaveURL(certDetailUrl);
await expect(page.getByRole("heading", { name: leafName })).toBeVisible();
// Search
await page.goto(`${vaultUrl}/search`);
await page.locator("input[placeholder='Search…']").fill(leafName);
await page.getByRole("button", { name: "Search" }).click();
await expect(page.getByRole("link", { name: leafName, exact: true })).toBeVisible();
// Certificate header controls replace the Export, Revoke and Transform tabs.
await page.getByRole("link", { name: leafName, exact: true }).click();
const leafUrl = page.url();
const certificateTabs = page.locator(".nav-tabs");
await expect(certificateTabs.getByRole("tab", { name: "Export", exact: true })).toHaveCount(0);
await expect(certificateTabs.getByRole("tab", { name: "Revoke", exact: true })).toHaveCount(0);
await expect(certificateTabs.getByRole("tab", { name: "Transform", exact: true })).toHaveCount(0);
const downloadPromise = page.waitForEvent("download");
await page.getByRole("button", { name: "Export", exact: true }).click();
const exportDialog = page.getByRole("dialog", { name: "Export certificate", exact: true });
await expect(exportDialog).toBeVisible();
await exportDialog.getByLabel("Format", { exact: true }).selectOption("pem");
await exportDialog.getByRole("button", { name: "Download", exact: true }).click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe(`${leafName}.crt`);
await expect(exportDialog).toHaveCount(0);
const transformButton = page.getByRole("button", { name: "Transform", exact: true });
await transformButton.click();
for (const name of ["Extract public key", "Create request", "Create template", "Similar certificate"]) {
await expect(page.getByRole("menuitem", { name, exact: true })).toBeVisible();
}
await page.getByRole("menuitem", { name: "Extract public key", exact: true }).click();
await expect(page).toHaveURL(/\/keys\/[^/]+$/);
await page.goto(leafUrl);
await transformButton.click();
await page.getByRole("menuitem", { name: "Create request", exact: true }).click();
await expect(page).toHaveURL(/\/requests\/[^/]+$/);
await page.goto(leafUrl);
await transformButton.click();
await page.getByRole("menuitem", { name: "Create template", exact: true }).click();
await expect(page).toHaveURL(/\/templates\/[^/]+$/);
await page.goto(leafUrl);
await transformButton.click();
await page.getByRole("menuitem", { name: "Similar certificate", exact: true }).click();
await expect(page).toHaveURL(`${leafUrl}/similar`);
await expect(page.getByRole("heading", { name: "New certificate" })).toBeVisible();
await page.goto(leafUrl);
await page.getByRole("button", { name: "Actions", exact: true }).click();
await expect(page.getByRole("menuitem", { name: "Renew", exact: true })).toBeVisible();
await expect(page.getByRole("menuitem", { name: "Delete", exact: true })).toBeVisible();
const revokeAction = page.getByRole("menuitem", { name: "Revoke", exact: true });
await expect(revokeAction).toBeEnabled();
await revokeAction.click();
const revokeDialog = page.getByRole("dialog", { name: "Revoke certificate", exact: true });
await expect(revokeDialog).toBeVisible();
await revokeDialog.getByLabel("Reason", { exact: true }).selectOption("keyCompromise");
await revokeDialog.getByRole("button", { name: "Revoke certificate", exact: true }).click();
await expect(revokeDialog).toHaveCount(0);
await expect(page.getByText("revoked", { exact: true })).toBeVisible();
await page.getByRole("button", { name: "Actions", exact: true }).click();
await expect(page.getByRole("menuitem", { name: "Revoke", exact: true })).toBeDisabled();
// CRL from the CA
await page.goto(caUrl);
await expect(page.getByRole("heading", { name: caName })).toBeVisible();
await page.getByRole("button", { name: "Actions", exact: true }).click();
await expect(page.getByRole("menuitem", { name: "Revoke", exact: true })).toBeDisabled();
await page.getByRole("button", { name: "Actions", exact: true }).click();
await page.locator(".nav-tabs").getByRole("tab", { name: "CA", exact: true }).click();
await page.getByRole("button", { name: "Generate CRL" }).click();
await expect(page).toHaveURL(/\/crls\//);
await expect(page.getByText(/CRL number \d+ · 1 revoked/)).toBeVisible();
// API token
await page.goto("/tokens");
await page.getByPlaceholder("CI uploader").fill(unique("token"));
await page.getByRole("button", { name: "Create" }).click();
await expect(page.getByText("Token created", { exact: false })).toBeVisible();
});