Files
RRS/tests/site.spec.js
T
2026-09-12 03:08:23 -04:00

266 lines
9.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, expect } from "@playwright/test";
import { whatsappUrl } from "../src/data.js";
const pages = [
["/", "Built to develop."],
["/fabrics", "Different yarns."],
["/about", "Built around"],
["/contact", "Lets talk"],
];
for (const width of [1440, 768, 390, 320]) {
for (const [path, heading] of pages) {
test(`${path} renders without errors at ${width}px`, async ({
page,
}, testInfo) => {
const errors = [];
page.on("pageerror", (error) => errors.push(error.message));
page.on("console", (message) => {
if (message.type() === "error") errors.push(message.text());
});
await page.setViewportSize({ width, height: 900 });
await page.goto(path);
await expect(page.getByRole("heading", { level: 1 })).toContainText(
heading,
);
await page.evaluate(() => document.fonts.ready);
for (const image of await page.locator("img").all()) {
await image.scrollIntoViewIfNeeded();
await expect(image).toHaveJSProperty("complete", true);
expect(
await image.evaluate((element) => element.naturalWidth),
).toBeGreaterThan(0);
}
await page.evaluate(() => window.scrollTo(0, 0));
await expect(page.locator("h1")).toHaveCount(1);
await expect(page.locator("[style]")).toHaveCount(0);
await expect(page.locator("form")).toHaveCount(0);
const metrics = await page.evaluate(() => ({
overflow: document.documentElement.scrollWidth > window.innerWidth,
unloadedImages: [...document.images].filter(
(image) => !image.complete || !image.naturalWidth,
).length,
fontsLoaded:
document.fonts.check('16px "Instrument Serif"') &&
document.fonts.check('16px "DM Sans"'),
contentWidthRatio:
document.querySelector("#main-content").getBoundingClientRect()
.width / window.innerWidth,
}));
expect(metrics).toMatchObject({
overflow: false,
unloadedImages: 0,
fontsLoaded: true,
});
expect(metrics.contentWidthRatio).toBeGreaterThan(0.85);
expect(errors).toEqual([]);
if (width === 1440 || width === 390) {
await page.screenshot({
path: testInfo.outputPath("page.png"),
fullPage: true,
});
}
});
}
}
test("desktop navigation, anchored CTA and browser back work", async ({
page,
}) => {
await page.goto("/");
await page
.getByRole("link", { name: "Explore our fabrics", exact: true })
.click();
await expect(page).toHaveURL("/fabrics");
await expect(
page
.getByRole("navigation", { name: "Main navigation" })
.getByRole("link", { name: "Fabrics", exact: true }),
).toHaveAttribute("aria-current", "page");
await page
.getByRole("link", { name: "Talk to us on WhatsApp", exact: true })
.click();
await expect(page).toHaveURL("/contact#whatsapp");
await expect(page.locator("#whatsapp")).toBeInViewport();
await expect(
page.getByRole("button", { name: "Talk to us on WhatsApp", exact: true }),
).toBeDisabled();
await expect(page.locator("#whatsapp-status")).toContainText(
"number to be supplied",
);
await page.goBack();
await expect(page).toHaveURL("/fabrics");
await page.goto("/contact#whatsapp");
await page
.getByRole("link", { name: "Talk to us on WhatsApp", exact: true })
.click();
await expect(page.locator("#whatsapp")).toBeInViewport();
});
test("mobile navigation closes on selection and Escape", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await page.goto("/");
const toggle = page.locator("#menu-toggle");
await toggle.click();
await expect(toggle).toHaveAttribute("aria-expanded", "true");
await page.keyboard.press("Escape");
await expect(toggle).toHaveAttribute("aria-expanded", "false");
await expect(toggle).toBeFocused();
await toggle.click();
await page
.getByRole("navigation", { name: "Main navigation" })
.getByRole("link", { name: "About", exact: true })
.click();
await expect(page).toHaveURL("/about");
await expect(toggle).toHaveAttribute("aria-expanded", "false");
await expect(page.locator(".owner")).toHaveCount(2);
await toggle.click();
await page.getByRole("link", { name: "RRS home", exact: true }).click();
await expect(page).toHaveURL("/");
await expect(toggle).toHaveAttribute("aria-expanded", "false");
});
test("application disclosures work from the keyboard", async ({ page }) => {
await page.goto("/fabrics");
const summary = page.locator("summary").filter({ hasText: "Textured knits" });
await summary.focus();
await page.keyboard.press("Enter");
await expect(
page.getByText(
"Knitted constructions where yarn and machine configuration are used to create distinctive surface character.",
),
).toBeVisible();
await page.keyboard.press("Enter");
await expect(
page.getByText(
"Knitted constructions where yarn and machine configuration are used to create distinctive surface character.",
),
).not.toBeVisible();
await expect(page.locator(".sample")).toHaveCount(7);
});
test("conversation starter copies with accessible feedback", async ({
page,
context,
}) => {
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
await page.goto("/contact");
await page.getByRole("button", { name: "Copy conversation starter" }).click();
await expect(page.getByRole("status")).toHaveText("Message copied.");
expect(await page.evaluate(() => navigator.clipboard.readText())).toBe(
"Hello RRS, I'd like to discuss a fabric requirement.",
);
});
test("clipboard denial has a useful fallback", async ({ page }) => {
await page.goto("/contact");
await page.evaluate(() => {
Object.defineProperty(navigator, "clipboard", {
value: { writeText: () => Promise.reject(new Error("Denied")) },
configurable: true,
});
});
await page.getByRole("button", { name: "Copy conversation starter" }).click();
await expect(page.getByRole("status")).toContainText(
"Please select and copy",
);
});
test("WhatsApp requires a real international number and encodes the message", () => {
for (const value of ["", "[Number]", "123", "+12345678901", "not-a-number"])
expect(whatsappUrl(value)).toBeNull();
const url = new URL(whatsappUrl("12345678901"));
expect(url.origin).toBe("https://wa.me");
expect(url.searchParams.get("text")).toBe(
"Hello RRS, I'd like to discuss a fabric requirement.",
);
});
test("every page section unveils once when scrolled into view", async ({
page,
}) => {
for (const [path] of pages) {
await page.goto(path);
const sections = page.locator("#main-content > section");
expect(await sections.count()).toBeGreaterThan(0);
for (const section of await sections.all()) {
await section.scrollIntoViewIfNeeded();
await expect(section).toHaveClass(/reveal--visible/);
await expect(section).toHaveCSS("opacity", "1");
}
}
});
test("homepage feature headings are centered semantic h2 elements", async ({
page,
}) => {
await page.goto("/");
const fabricsHeading = page.getByRole("heading", {
level: 2,
name: "Greige is where it begins.",
});
const developmentsHeading = page.getByRole("heading", {
level: 2,
name: "Structure changes everything.",
});
await expect(fabricsHeading).toHaveCSS("text-align", "center");
await expect(developmentsHeading.locator("..").locator("..")).toHaveClass(
/section-heading--centered/,
);
});
test("capacity values roll in opposite directions and settle at 6070", async ({
page,
}) => {
for (const path of ["/", "/fabrics"]) {
await page.goto(path);
const capacity = page.getByLabel("60 to 70");
await expect(capacity).toHaveCount(1);
await capacity.scrollIntoViewIfNeeded();
await expect
.poll(() =>
capacity
.locator(".capacity-value__reel--up")
.evaluate((element) => element.getAnimations().length),
)
.toBeGreaterThan(0);
await expect(capacity.locator(".capacity-value__reel--up")).toHaveCSS(
"transform",
/matrix/,
);
await expect(capacity.locator(".capacity-value__reel--down")).toHaveCSS(
"transform",
/matrix/,
);
}
});
test("section unveiling respects reduced-motion preferences", async ({
page,
}) => {
await page.emulateMedia({ reducedMotion: "reduce" });
await page.goto("/");
const sections = page.locator("#main-content > section");
await expect(sections.first()).toHaveClass(/reveal--visible/);
for (const section of await sections.all()) {
await expect(section).toHaveCSS("opacity", "1");
await expect(section).toHaveCSS("transform", "none");
}
const capacity = page.getByLabel("60 to 70");
await capacity.scrollIntoViewIfNeeded();
expect(
await capacity
.locator(".capacity-value__reel--up")
.evaluate((element) => element.getAnimations().length),
).toBe(0);
});
test("unknown route provides a way home", async ({ page }) => {
await page.goto("/missing-page");
await expect(page.getByRole("heading", { level: 1 })).toHaveText(
"A loose thread.",
);
await page.getByRole("link", { name: "Back to home", exact: true }).click();
await expect(page).toHaveURL("/");
});