RRS First version
This commit is contained in:
@@ -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", "Let’s 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 60–70", 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("/");
|
||||
});
|
||||
Reference in New Issue
Block a user