T46 E2E: the suite follows the new site (hero, features, city, search, rail, keys, captures, live frames, focus, skip, full screen), in Chrome, Firefox and a phone
This commit is contained in:
1 parent
1bf9c632ec
commit
4587d1f251
3 files changed
+78
-61
No files matched your search
+24
-18
@@ -5,10 +5,10 @@ const STARTS = 45_000;
|
||||
|
||||
async function live(page: Page, path: string) {
|
||||
await page.goto(path);
|
||||
const frame = page.frameLocator(".stage iframe");
|
||||
// The host hides its state once the gallery says it is ready.
|
||||
await expect(page.locator(".stage .state")).toBeHidden({ timeout: STARTS });
|
||||
return frame;
|
||||
await expect(page.locator(".live iframe")).toHaveCount(1);
|
||||
// The host drops its state once the gallery says it is ready.
|
||||
await expect(page.locator(".live .live-state")).toHaveCount(0, { timeout: STARTS });
|
||||
return page.frameLocator(".live iframe");
|
||||
}
|
||||
|
||||
test("a story runs live and follows the site's mode", async ({ page }) => {
|
||||
@@ -16,33 +16,33 @@ test("a story runs live and follows the site's mode", async ({ page }) => {
|
||||
const frame = await live(page, "/components/buttons/button/");
|
||||
await expect(frame.locator("canvas")).toBeVisible();
|
||||
await expect(frame.locator("html")).not.toHaveClass(/dark/);
|
||||
await page.locator("[data-mode]").click();
|
||||
await page.getByRole("button", { name: "Dark mode" }).click();
|
||||
await expect(frame.locator("html")).toHaveClass(/dark/);
|
||||
});
|
||||
|
||||
test("the live story leaves focus with the host until it is pressed", async ({ page, isMobile }) => {
|
||||
test("a starting story leaves focus with the host", async ({ page, isMobile }) => {
|
||||
test.skip(isMobile, "no hardware keyboard");
|
||||
await live(page, "/components/forms/input-textfield-clearableinput/");
|
||||
await page.waitForTimeout(1000);
|
||||
expect(await page.evaluate(() => document.activeElement?.tagName)).not.toBe("IFRAME");
|
||||
await page.keyboard.press("/");
|
||||
await expect(page.getByRole("dialog", { name: "Search components" })).toBeVisible();
|
||||
await page.goto("/");
|
||||
await page.getByRole("link", { name: "Browse" }).first().focus();
|
||||
await page.locator(".feature").first().scrollIntoViewIfNeeded();
|
||||
await expect(page.locator(".feature .live .live-state")).toHaveCount(0, { timeout: STARTS });
|
||||
await expect(page.getByRole("link", { name: "Browse" }).first()).toBeFocused();
|
||||
});
|
||||
|
||||
test("a skip link passes the live example", async ({ page, isMobile }) => {
|
||||
test("the skip control passes the live example", async ({ page, isMobile }) => {
|
||||
test.skip(isMobile, "no hardware keyboard");
|
||||
await page.goto("/components/buttons/button/");
|
||||
const skip = page.getByRole("link", { name: "Skip the live example" });
|
||||
const skip = page.getByRole("button", { name: "Skip the live example" });
|
||||
await skip.focus();
|
||||
await expect(skip).toBeVisible();
|
||||
await page.keyboard.press("Enter");
|
||||
await expect(page.locator("#after-stage")).toBeFocused();
|
||||
await expect(page.locator(".live + span")).toBeFocused();
|
||||
});
|
||||
|
||||
test("typing reaches a live field after a press", async ({ page, isMobile }) => {
|
||||
test.skip(isMobile, "no hardware keyboard");
|
||||
const frame = await live(page, "/components/forms/input-textfield-clearableinput/");
|
||||
const box = (await page.locator(".stage iframe").boundingBox())!;
|
||||
const box = (await page.locator(".live iframe").boundingBox())!;
|
||||
// The first field sits under the section's note, as the gallery lays it.
|
||||
await page.mouse.click(box.x + 120, box.y + 72);
|
||||
const before = await frame.locator("canvas").screenshot();
|
||||
@@ -53,11 +53,17 @@ test("typing reaches a live field after a press", async ({ page, isMobile }) =>
|
||||
|
||||
test("full screen covers the page and Escape leaves it", async ({ page }) => {
|
||||
await live(page, "/components/buttons/button/");
|
||||
await page.locator(".stage").hover();
|
||||
await page.locator(".live").hover();
|
||||
await page.getByRole("button", { name: "Full screen" }).click();
|
||||
await expect(page.locator(".stage")).toHaveClass(/full/);
|
||||
await expect(page.locator(".live")).toHaveClass(/full/);
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(page.locator(".stage")).not.toHaveClass(/full/);
|
||||
await expect(page.locator(".live")).not.toHaveClass(/full/);
|
||||
});
|
||||
|
||||
test("home frames run only near the view", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.locator(".close").scrollIntoViewIfNeeded();
|
||||
await expect(page.locator(".feature iframe")).toHaveCount(0);
|
||||
});
|
||||
|
||||
// One story from every fifth chapter, each drawn in its frame.
|
||||
|
||||
+53
-43
@@ -1,7 +1,7 @@
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import manifest from "../../examples/gallery/stories.json" with { type: "json" };
|
||||
|
||||
/** Fails the test on any console error or uncaught exception. */
|
||||
/** Collects console errors and uncaught exceptions. */
|
||||
function watch(page: Page): string[] {
|
||||
const errors: string[] = [];
|
||||
page.on("console", (m) => m.type() === "error" && errors.push(m.text()));
|
||||
@@ -9,13 +9,14 @@ function watch(page: Page): string[] {
|
||||
return errors;
|
||||
}
|
||||
|
||||
test("the home page shows the pitch, the scene and the credits", async ({ page }) => {
|
||||
test("the home page shows the hero, five features, the city and the credits", async ({ page }) => {
|
||||
const errors = watch(page);
|
||||
await page.goto("/");
|
||||
await expect(page.getByRole("heading", { level: 1 })).toContainText("component library for GPUI");
|
||||
await expect(page.locator("canvas.iso")).toBeAttached();
|
||||
await expect(page.getByRole("heading", { level: 1 })).toHaveText("Components for GPUI.");
|
||||
await expect(page.locator(".hero-iso")).toBeVisible();
|
||||
await expect(page.locator("#foot")).toContainText("Solar Icons by 480 Design");
|
||||
await expect(page.locator(".feature")).toHaveCount(5);
|
||||
await expect(page.locator(".city-names li")).toHaveCount(manifest.pages.length);
|
||||
await expect(page.locator(".foot")).toContainText("Solar Icons by 480 Design");
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -30,52 +31,61 @@ test("the mode toggles, and a reload keeps it", async ({ page }) => {
|
||||
await expect(html).toHaveClass(/dark/);
|
||||
});
|
||||
|
||||
test("the index lists every chapter with its story count", async ({ page }) => {
|
||||
test("the index lists every chapter with its stories", async ({ page }) => {
|
||||
await page.goto("/components/");
|
||||
const rows = page.locator(".grid a");
|
||||
const rows = page.locator(".chapters li");
|
||||
await expect(rows).toHaveCount(manifest.pages.length);
|
||||
const first = manifest.pages[0];
|
||||
await expect(rows.first()).toContainText(`${first.stories.length} stories`);
|
||||
const [first] = manifest.pages;
|
||||
await expect(rows.first().locator(".titles a")).toHaveCount(first.stories.length);
|
||||
});
|
||||
|
||||
test("a chapter opens its first story, and paging walks on", async ({ page }) => {
|
||||
const [chapter] = manifest.pages;
|
||||
await page.goto(`/components/${chapter.slug}/`);
|
||||
await expect(page.getByRole("heading", { level: 1 })).toHaveText(chapter.stories[0].title);
|
||||
await page.locator(".pager a.next").click();
|
||||
await expect(page).toHaveURL(`/components/${chapter.slug}/${chapter.stories[1].slug}/`);
|
||||
await expect(page.getByRole("heading", { level: 1 })).toHaveText(chapter.stories[1].title);
|
||||
});
|
||||
|
||||
test("the sidebar filters by story and component name", async ({ page }) => {
|
||||
test("search filters by story and component name, and Enter opens the first match", async ({ page }) => {
|
||||
await page.goto("/components/");
|
||||
await page.getByPlaceholder("Filter components").fill("Toast");
|
||||
await expect(page.locator(".side li a").first()).toContainText("Toast");
|
||||
await page.getByPlaceholder("Filter components").fill("zzzz");
|
||||
await expect(page.locator(".side .none")).toContainText("Nothing matches");
|
||||
const find = page.getByRole("searchbox", { name: "Search components" });
|
||||
await find.fill("zzzz");
|
||||
await expect(page.locator(".none")).toContainText("Nothing matches");
|
||||
await find.fill("breadcrumb");
|
||||
await expect(page.locator(".chapters li").first()).toContainText(/Breadcrumb/);
|
||||
await find.press("Enter");
|
||||
await expect(page).toHaveURL(/\/components\/[a-z_]+\/.*breadcrumb/);
|
||||
});
|
||||
|
||||
test("search opens with the keyboard and goes where Enter points", async ({ page, isMobile }) => {
|
||||
test("Slash focuses search from the page", async ({ page, isMobile }) => {
|
||||
test.skip(isMobile, "no hardware keyboard");
|
||||
await page.goto("/components/");
|
||||
await page.keyboard.press("/");
|
||||
const dialog = page.getByRole("dialog", { name: "Search components" });
|
||||
await expect(dialog).toBeVisible();
|
||||
await page.keyboard.type("breadcrumb");
|
||||
await page.keyboard.press("Enter");
|
||||
await expect(dialog).toBeHidden();
|
||||
await expect(page).toHaveURL(/\/components\/navigation\/.*breadcrumb/);
|
||||
await expect(page.getByRole("searchbox", { name: "Search components" })).toBeFocused();
|
||||
});
|
||||
|
||||
test("the Browse menu opens a chapter's stories beside it", async ({ page, isMobile }) => {
|
||||
test.skip(isMobile, "hover menu");
|
||||
await page.goto("/components/");
|
||||
await page.getByRole("button", { name: "Browse" }).click();
|
||||
await page.getByRole("menuitem", { name: "Charts" }).hover();
|
||||
const story = page.getByRole("menuitem", { name: /^RadarChart/ });
|
||||
await expect(story).toBeVisible();
|
||||
await story.click();
|
||||
await expect(page).toHaveURL(/\/components\/charts\/radarchart/);
|
||||
test("a chapter opens its first story, and the pager and arrows walk on", async ({ page, isMobile }) => {
|
||||
const [chapter] = manifest.pages;
|
||||
await page.goto(`/components/${chapter.slug}/`);
|
||||
await expect(page).toHaveURL(`/components/${chapter.slug}/${chapter.stories[0].slug}/`);
|
||||
await expect(page.getByRole("heading", { level: 1 })).toHaveText(chapter.stories[0].title);
|
||||
await page.locator(".pager a.next").click();
|
||||
await expect(page.getByRole("heading", { level: 1 })).toHaveText(chapter.stories[1].title);
|
||||
if (isMobile) return;
|
||||
await page.keyboard.press("ArrowLeft");
|
||||
await expect(page.getByRole("heading", { level: 1 })).toHaveText(chapter.stories[0].title);
|
||||
});
|
||||
|
||||
test("the rail marks the current story", async ({ page, isMobile }) => {
|
||||
test.skip(isMobile, "the rail hides on phones");
|
||||
const chapter = manifest.pages[manifest.pages.length - 1];
|
||||
const story = chapter.stories[chapter.stories.length - 1];
|
||||
await page.goto(`/components/${chapter.slug}/${story.slug}/`);
|
||||
await expect(page.locator('.side li [aria-current="page"]')).toHaveText(story.title);
|
||||
await expect(page.locator('.side li [aria-current="page"]')).toBeInViewport();
|
||||
});
|
||||
|
||||
test("a chapter's name in the city names it and opens it", async ({ page, isMobile }) => {
|
||||
test.skip(isMobile, "hover");
|
||||
await page.goto("/");
|
||||
const chapter = manifest.pages[15];
|
||||
await page.locator(".city-names a", { hasText: chapter.title }).first().hover();
|
||||
await expect(page.locator(".city h2")).toHaveText(`${chapter.title}.`);
|
||||
await page.locator(".city-names a", { hasText: chapter.title }).first().click();
|
||||
await expect(page).toHaveURL(`/components/${chapter.slug}/${chapter.stories[0].slug}/`);
|
||||
});
|
||||
|
||||
test("an unknown chapter says so", async ({ page }) => {
|
||||
@@ -90,14 +100,14 @@ test("a story the web cannot run shows its native capture, in the site's mode",
|
||||
await page.goto("/components/forms/pathinput/");
|
||||
const shot = page.locator("img.shot");
|
||||
await expect(shot).toHaveAttribute("srcset", /pathinput-light\.jpg/);
|
||||
await expect(page.locator(".stage iframe")).toHaveCount(0);
|
||||
await page.locator("[data-mode]").click();
|
||||
await expect(page.locator(".live iframe")).toHaveCount(0);
|
||||
await page.getByRole("button", { name: "Dark mode" }).click();
|
||||
await expect(shot).toHaveAttribute("srcset", /pathinput-dark\.jpg/);
|
||||
await expect.poll(() => shot.evaluate((img: HTMLImageElement) => img.naturalWidth)).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test("a live story that opens a window shows that window natively", async ({ page }) => {
|
||||
await page.goto("/components/shell/aboutdialog/");
|
||||
await expect(page.locator(".stage iframe")).toHaveCount(1);
|
||||
await expect(page.locator(".native img.shot")).toHaveAttribute("srcset", /shell-about-window/);
|
||||
await expect(page.locator(".live iframe")).toHaveCount(1);
|
||||
await expect(page.locator(".windows img.shot")).toHaveAttribute("srcset", /shell-about-window/);
|
||||
});
|
||||
@@ -10,6 +10,7 @@ export default defineConfig({
|
||||
webServer: { command: "pnpm preview --port 4173 --strictPort", url: "http://localhost:4173", reuseExistingServer: true },
|
||||
projects: [
|
||||
{ name: "desktop", use: { ...devices["Desktop Chrome"] } },
|
||||
{ name: "firefox", use: { ...devices["Desktop Firefox"] } },
|
||||
{ name: "phone", use: { ...devices["Pixel 7"] } },
|
||||
],
|
||||
});
|
||||
Reference in new issue
Block a user