Files
frontend/test/e2e/gallery.spec.ts
T
Aidan Timson 7f27c9a948 E2E performance improvements and consolidation (#53057)
* Run e2e suites in parallel

* Make suites run as parallel jobs in CI. Merge reports after

* Consolidate app route e2e helpers

* Move app e2e smoke data out of spec

* Tighten app e2e smoke timeouts

* Split e2e helpers by suite

* Consolidate app e2e smoke data

* Move app e2e helpers into src

* Enable workers and full paralelism to tests. Use 60% for local (4 in my case) and 1 for CI

* Shard e2e tests

* Fold e2e-local into report job

* Fix name

* Format

* Double sharding count

* Shard demo (2) and gallery (3)

* rm

* 6,4,6

* Add local sharded e2e runner

* Queue e2e benchmark runs

* Use test mode for e2e builds

* Fix e2e benchmark concurrency

* Build e2e app artifact modern only

* Build demo e2e artifact modern only

* 4/4/2 sharding

* Remove local sharded e2e runner

* Use Playwright image for e2e tests

* Remove redundant e2e test cases

* Run e2e sidebar checks concurrently

* Consolidate config link e2e checks

* Avoid duplicate gallery page waits

* Avoid slow gallery alert count wait

* Run app e2e readiness waits concurrently

* Avoid duplicate gallery readiness wait

* Restore concurrency

* Restore concurrency

* Share parallel e2e smoke registration

* Restore demo page error tracking

* Remove ineffective gallery test mode

* Remove unused route project filtering

* Remove duplicate smoke test parallel mode

* Keep page error filtering internal

* Require helper reuse in test changes

* Clarify local E2E server workflow

* Stabilize demo sidebar navigation test

* Avoid concurrent cold E2E builds

* Validate E2E worker overrides

* Restore real demo sidebar clicks

* Keep app shell error checks strict

* Limit gallery shell error filtering

* Address E2E review findings
2026-07-21 22:27:37 +02:00

124 lines
4.0 KiB
TypeScript

/**
* E2E tests for the HA gallery (port 8100).
*
* Each component page is tested by navigating to its hash and asserting that
* the demo content renders without JS errors and the page element is visible.
*
* Run with:
* yarn test:e2e:gallery
*/
import { test, expect } from "@playwright/test";
import {
expectNoPageErrors,
QUICK_TIMEOUT,
SHELL_TIMEOUT,
trackPageErrors,
} from "./helpers";
import {
defineGallerySmokeTests,
expectGalleryDemoElement,
galleryLocator,
getGalleryDemo,
goToGalleryHome,
GALLERY_SHELL_IGNORED_PAGE_ERRORS,
} from "./gallery/helpers";
import { componentPages, lovelacePages, moreInfoPages } from "./gallery/pages";
test.describe("Gallery shell", () => {
test("page loads and ha-gallery mounts", async ({ page }) => {
const errors = trackPageErrors(page);
await goToGalleryHome(page);
expectNoPageErrors(errors, undefined, GALLERY_SHELL_IGNORED_PAGE_ERRORS);
});
test("sidebar renders navigation links", async ({ page }) => {
await goToGalleryHome(page);
await expect(galleryLocator(page, "ha-drawer")).toBeAttached({
timeout: QUICK_TIMEOUT,
});
});
});
defineGallerySmokeTests("Components", "components", componentPages);
defineGallerySmokeTests("More-info dialogs", "more-info", moreInfoPages);
defineGallerySmokeTests("Lovelace cards", "lovelace", lovelacePages);
test.describe("Component interactions", () => {
test("ha-alert renders all four types", async ({ page }) => {
const demo = await getGalleryDemo(page, "components/ha-alert");
// The demo uses property binding (.alertType) not attribute binding, so we
// verify that multiple ha-alert elements are present.
const alerts = demo.locator("ha-alert");
await expect(alerts.nth(3)).toBeAttached({ timeout: QUICK_TIMEOUT });
});
test("ha-button renders primary action button", async ({ page }) => {
const demo = await getGalleryDemo(page, "components/ha-button");
await expectGalleryDemoElement(demo, "ha-button, mwc-button");
});
test("ha-control-slider can be found in DOM", async ({ page }) => {
const demo = await getGalleryDemo(page, "components/ha-control-slider");
await expectGalleryDemoElement(demo, "ha-control-slider");
});
test("ha-form renders schema-driven fields", async ({ page }) => {
const demo = await getGalleryDemo(page, "components/ha-form");
await expectGalleryDemoElement(demo, "ha-form");
});
test("tile-card renders entity state", async ({ page }) => {
const demo = await getGalleryDemo(page, "lovelace/tile-card");
await expectGalleryDemoElement(demo, "hui-tile-card");
});
test("more-info light renders controls", async ({ page }) => {
const demo = await getGalleryDemo(page, "more-info/light");
await expectGalleryDemoElement(
demo,
"ha-control-slider, ha-more-info-light, more-info-content",
SHELL_TIMEOUT
);
});
test("ha-gauge renders a gauge element", async ({ page }) => {
await getGalleryDemo(page, "components/ha-gauge");
// ha-gauge page is markdown-based; gauge elements render in the description area.
await expect(galleryLocator(page, "ha-gauge").first()).toBeAttached({
timeout: QUICK_TIMEOUT,
});
});
test("ha-switch toggles state on click", async ({ page }) => {
const demo = await getGalleryDemo(page, "components/ha-switch");
// Find the first interactive (non-disabled) switch. Pull its checked state
// from the property because ha-switch toggles via property, not attribute.
const switchEl = demo.locator("ha-switch:not([disabled])").first();
await expect(switchEl).toBeAttached({ timeout: QUICK_TIMEOUT });
const before = await switchEl.evaluate(
(el: HTMLElement & { checked?: boolean }) => el.checked === true
);
await switchEl.click();
await expect
.poll(
() =>
switchEl.evaluate(
(el: HTMLElement & { checked?: boolean }) => el.checked === true
),
{ timeout: QUICK_TIMEOUT }
)
.toBe(!before);
});
});