RRS First version

This commit is contained in:
2026-09-12 03:08:23 -04:00
commit c050d25a84
41 changed files with 5575 additions and 0 deletions
+265
View File
@@ -0,0 +1,265 @@
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("/");
});