Files
HiveGrid-Wireframes/hivegrid-cloud/tests/e2e/register.a11y.spec.ts
T

157 lines
7.4 KiB
TypeScript

import { expect, test } from "./utils/test";
import { expectNoCriticalA11yViolations } from "./utils/axe";
import { expectAllPasswordRequirementsMet, expectPasswordRequirementsVisible, expectSomePasswordRequirementsMissing } from "./utils/auth";
import { expectNoHorizontalOverflow, narrowMobileViewport } from "./utils/mobile";
import { skipOrFailWhenE2eFixtureMissing } from "./utils/readiness";
test.describe("register page a11y", () => {
test("/register has no critical or serious axe violations", async ({ page }) => {
await page.goto("/register", { waitUntil: "domcontentloaded" });
if (/\/login(?:[?#].*)?$/u.test(page.url())) {
await expect(page.locator('input[type="email"], input[name="email"], input#email').first()).toBeVisible();
await expectNoCriticalA11yViolations(page);
return;
}
await expect(page.locator("body")).toBeVisible();
await expectNoCriticalA11yViolations(page);
});
test("/register announces password requirement status through accessible names", async ({ page }) => {
await page.goto("/register", { waitUntil: "domcontentloaded" });
if (/\/login(?:[?#].*)?$/u.test(page.url())) {
skipOrFailWhenE2eFixtureMissing("Registration redirects to login in this environment");
}
const passwordInput = page.locator('input[type="password"], input[name="password"], input#password').first();
if ((await passwordInput.count()) === 0 || !(await passwordInput.isVisible())) {
skipOrFailWhenE2eFixtureMissing("Registration password input is not rendered in this environment");
}
const requirements = page.locator('[aria-live="polite"]').first();
await expectPasswordRequirementsVisible(requirements);
await passwordInput.fill("short");
await expectSomePasswordRequirementsMissing(requirements);
await passwordInput.fill("LongEnough1!");
await expectAllPasswordRequirementsMet(requirements);
});
test("/register submit contract posts expected fields without creating an account", async ({ page }) => {
const redirectTo = "/app/dashboard";
await page.goto(`/register?redirect=${encodeURIComponent(redirectTo)}`, { waitUntil: "domcontentloaded" });
if (/\/login(?:[?#].*)?$/u.test(page.url())) {
skipOrFailWhenE2eFixtureMissing("Registration redirects to login in this environment");
}
const form = page.locator("form").first();
const emailInput = form.locator('[data-ui-id="ui.auth.register.email"], input[type="email"], input[name="email"], input#email').first();
const passwordInput = form.locator('[data-ui-id="ui.auth.register.password"], input[type="password"], input[name="password"], input#password').first();
if ((await form.count()) === 0 || (await emailInput.count()) === 0 || (await passwordInput.count()) === 0) {
skipOrFailWhenE2eFixtureMissing("Registration form is not rendered in this environment");
}
const email = `contract-${Date.now()}@example.test`;
const firstName = "E2E";
const lastName = "Contract";
const company = "E2E Contract";
await emailInput.fill(email);
await passwordInput.fill("LongEnough1!");
const companyInput = form.locator('[data-ui-id="ui.auth.register.company"], input[name="company"]').first();
const hasCompanyInput = (await companyInput.count()) > 0;
if (hasCompanyInput) await companyInput.fill(company);
const firstNameInput = form.locator('[data-ui-id="ui.auth.register.firstName"], input[name="first-name"]').first();
const lastNameInput = form.locator('[data-ui-id="ui.auth.register.lastName"], input[name="last-name"]').first();
const hasFirstNameInput = (await firstNameInput.count()) > 0;
const hasLastNameInput = (await lastNameInput.count()) > 0;
if (hasFirstNameInput) await firstNameInput.fill(firstName);
if (hasLastNameInput) await lastNameInput.fill(lastName);
const currentPathname = new URL(page.url()).pathname;
let resolveSubmitted!: (formData: URLSearchParams) => void;
const submitted = new Promise<URLSearchParams>((resolve) => {
resolveSubmitted = resolve;
});
await page.route("**/*", async (route) => {
const request = route.request();
const requestUrl = new URL(request.url());
// React Router single-fetch posts form actions to `${pathname}.data`, so
// match both the route path and its `.data` sibling.
const matchesRoute = requestUrl.pathname === currentPathname || requestUrl.pathname === `${currentPathname}.data`;
if (request.method() !== "POST" || !matchesRoute) {
await route.continue();
return;
}
const formData = new URLSearchParams(request.postData() ?? "");
resolveSubmitted(formData);
await route.fulfill({
status: 200,
contentType: "text/html",
body: "<!doctype html><title>Registration intercepted</title>",
});
});
await form.locator('button[type="submit"]').first().click();
const formData = await submitted;
expect(formData.get("csrf") ?? "").not.toBe("");
expect(formData.get("email")).toBe(email);
expect(formData.get("password")).toBe("LongEnough1!");
expect(formData.get("redirectTo")).toBe(redirectTo);
if (hasCompanyInput) expect(formData.get("company")).toBe(company);
if (hasFirstNameInput) expect(formData.get("first-name")).toBe(firstName);
if (hasLastNameInput) expect(formData.get("last-name")).toBe(lastName);
});
});
test.describe("register validation mobile a11y", () => {
test.use({ viewport: narrowMobileViewport, isMobile: true, hasTouch: true });
test("/register exposes validation feedback accessibly on mobile", async ({ page }) => {
await page.goto("/register", { waitUntil: "domcontentloaded" });
if (/\/login(?:[?#].*)?$/u.test(page.url())) {
skipOrFailWhenE2eFixtureMissing("Registration redirects to login in this environment");
}
const emailInput = page.locator('input[type="email"], input[name="email"], input#email').first();
const passwordInput = page.locator('input[type="password"], input[name="password"], input#password').first();
if ((await emailInput.count()) === 0 || (await passwordInput.count()) === 0) {
skipOrFailWhenE2eFixtureMissing("Registration form is not rendered in this environment");
}
const companyInput = page.locator('input[name="company"]').first();
if ((await companyInput.count()) > 0) await companyInput.fill("E2E Validation");
const firstNameInput = page.locator('input[name="first-name"]').first();
if ((await firstNameInput.count()) > 0) await firstNameInput.fill("E2E");
const lastNameInput = page.locator('input[name="last-name"]').first();
if ((await lastNameInput.count()) > 0) await lastNameInput.fill("Validation");
await emailInput.fill("not-an-email");
await passwordInput.fill("short");
await page.locator('button[type="submit"]').first().click();
const formError = page.locator('#form-error-message [role="alert"]').first();
const nativeEmailInvalid = await emailInput.evaluate((input) => (input as HTMLInputElement).validationMessage.length > 0);
if ((await formError.count()) > 0 && (await formError.isVisible())) {
await expect(formError).toBeVisible();
} else {
expect(nativeEmailInvalid).toBe(true);
}
const requirements = page.locator('[aria-live="polite"]').first();
if ((await requirements.count()) > 0) {
await expectSomePasswordRequirementsMissing(requirements);
}
await expectNoHorizontalOverflow(page);
await expectNoCriticalA11yViolations(page);
});
});