Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 26 additions & 19 deletions e2e/tests/helpers/hub.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
// Shared E2E helpers for the Hub UI (PRs #800/#801; f023f89 "no-rail layout").
// Shared E2E helpers for the Hub UI.
//
// Since the hub landed, bare '/' no longer boots the editor: it renders the
// HomeView library page (data-testid="home-view"), and the editor is reached by
// clicking "New diagram" (home-empty-new — empty-library CTA) or the header New
// button (home-new). The Sidebar icon rail was removed app-wide (f023f89), so
// nothing navigates via sidebar-* anymore. Editor URLs (/?id= or ?code=/?embed)
// still render their surface directly without passing through the hub.
// Editor-as-landing (2026-06-13): bare '/' boots the EDITOR again (resume
// last-code, else seed a sample diagram), so '/' renders data-testid="dsl-editor"
// directly. The HomeView library page (data-testid="home-view") is now OPT-IN via
// '/?view=diagrams'. The Sidebar icon rail was removed app-wide (f023f89), so
// nothing navigates via sidebar-* anymore. Editor deep links (/?id= or ?code=/
// ?embed) still render their surface directly.
//
// Every spec that needs the editor goes through openEditor(); specs that need
// the library grid go through gotoHome(). Keeping the click-through in ONE place
// means the next hub-routing change is a one-file fix.
// the library grid go through gotoHome() — which navigates to '/?view=diagrams'.
// Keeping the routing in ONE place means the next routing change is a one-file fix.

import { expect } from '@playwright/test';

const HOME_VIEW = '[data-testid="home-view"]';
const EDITOR_SURFACE = '[data-testid="dsl-editor"] .cm-content';

// The hub/library is opt-in via this query param under editor-as-landing.
export const HUB_URL = '/?view=diagrams';

/**
* Navigate to `url` (default '/') and end up in the editor.
*
* - If the URL renders the HomeView hub, click through its New CTA
* (home-empty-new when the library is empty, else home-new).
* - If the URL renders the editor directly (e.g. '/?id=…' or '/?code=…'),
* just wait for the editor surface.
* - If the URL renders the editor directly (the default for bare '/', and for
* '/?id=…' or '/?code=…'), just wait for the editor surface.
* - If the URL renders the HomeView hub ('/?view=diagrams'), click through its
* New CTA (home-empty-new when the library is empty, else home-new).
*
* React renders after the 'load' event page.goto resolves on, so we first wait
* for WHICHEVER surface this URL produces (home-view or the CM6 editor) before
Expand All @@ -32,7 +35,9 @@ export async function openEditor(page, { url = '/' } = {}) {
await page.goto(url);
const homeView = page.locator(HOME_VIEW);
const editorSurface = page.locator(EDITOR_SURFACE);
await expect(homeView.or(editorSurface).first()).toBeVisible({ timeout: 15_000 });
await expect(homeView.or(editorSurface).first()).toBeVisible({
timeout: 15_000,
});
if (await homeView.isVisible()) {
const emptyCta = page.locator('[data-testid="home-empty-new"]');
if (await emptyCta.isVisible()) {
Expand All @@ -45,13 +50,15 @@ export async function openEditor(page, { url = '/' } = {}) {
}

/**
* Navigate to the HomeView library page ('/').
* Navigate to the HomeView library page ('/?view=diagrams').
*
* A FULL navigation (not client-side breadcrumb) so the signed-out useItems
* mount re-reads the localItems index from a clean slate — robust regardless of
* whether the in-page subscription has caught up.
* Editor-as-landing: the hub is opt-in via ?view=diagrams; bare '/' now boots
* the editor, so the library is reached only by this param. A FULL navigation
* (not a client-side breadcrumb) so the signed-out useItems mount re-reads the
* localItems index from a clean slate — robust regardless of whether the in-page
* subscription has caught up.
*/
export async function gotoHome(page) {
await page.goto('/');
await page.goto(HUB_URL);
await expect(page.locator(HOME_VIEW)).toBeVisible({ timeout: 15_000 });
}
52 changes: 33 additions & 19 deletions e2e/tests/library.spec.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// M03 Task 15 — Library E2E (signed-out / local flows only).
//
// Hub (PRs #800/#801; f023f89 "no-rail layout"): the in-editor Library PANEL was
// retired — the Sidebar icon rail (sidebar-library) and LibraryPanel no longer
// render anywhere. The web library IS the HomeView page at '/': rows are
// f023f89 "no-rail layout": the in-editor Library PANEL was retired — the
// Sidebar icon rail (sidebar-library) and LibraryPanel no longer render anywhere.
// The web library IS the HomeView page, reached at '/?view=diagrams' under
// editor-as-landing (2026-06-13; bare '/' boots the editor again). Rows are
// home-card-<id> cards in home-grid, search is home-search (same SearchInput
// component, so the clear affordance is still search-clear), and the bulk
// Export-all / Import controls (lib-export-all / lib-import-input) were re-homed
Expand All @@ -21,10 +22,10 @@
// /create-share reads the cloud item doc + needs a fresh ID token). Same auth
// note as persistence.spec.js / M02.
//
// Navigation note: seeding happens in the EDITOR (reached through the hub's New
// CTA — openEditor); reading the library back happens by a FULL navigation to
// '/' (gotoHome), which freshly mounts useItems and re-reads the localItems
// index regardless of in-page subscription timing.
// Navigation note: seeding happens in the EDITOR (bare '/' boots it directly
// under editor-as-landing — openEditor); reading the library back happens by a
// FULL navigation to '/?view=diagrams' (gotoHome), which freshly mounts useItems
// and re-reads the localItems index regardless of in-page subscription timing.

import { test, expect } from '@playwright/test';
import { suppressOneTimeModals } from './helpers/onetime';
Expand Down Expand Up @@ -62,8 +63,8 @@ async function gotoFresh(page) {
await suppressOneTimeModals(page);
await page.goto('/');
await page.evaluate(() => localStorage.clear());
// Hub: '/' is the HomeView library — seeding drives the editor's header, so
// click through the hub's New CTA (empty library → home-empty-new).
// Editor-as-landing: bare '/' boots the editor (cleared storage → seeds a fresh
// sample diagram), so openEditor lands straight on the CM6 surface here.
await openEditor(page);
}

Expand Down Expand Up @@ -134,10 +135,11 @@ async function seedItem(page, { title, dsl, firstSave = false }) {
}

/**
* Navigate to the library. Hub (PRs #800/#801): the library is no longer an
* in-editor side panel (sidebar-library + library-panel are gone — f023f89
* removed the rail and LibraryPanel was retired); it is the HomeView page at
* '/'. gotoHome is a FULL navigation, so useItems freshly re-reads the
* Navigate to the library. The library is no longer an in-editor side panel
* (sidebar-library + library-panel are gone — f023f89 removed the rail and
* LibraryPanel was retired); it is the HomeView page. Editor-as-landing
* (2026-06-13): the hub moved off bare '/' and is now opt-in at '/?view=diagrams'.
* gotoHome navigates there (a FULL navigation), so useItems freshly re-reads the
* localItems index — same guarantee the old reload-then-open-panel shape gave.
*/
async function reloadIntoLibrary(page) {
Expand All @@ -147,7 +149,9 @@ async function reloadIntoLibrary(page) {
// ──────────────────────────────────────────────────────────────────────────────
// Test 1: Library lists locally-saved diagrams; search filters them.
// ──────────────────────────────────────────────────────────────────────────────
test('library lists local items and SearchInput filters them', async ({ page }) => {
test('library lists local items and SearchInput filters them', async ({
page,
}) => {
page.on('pageerror', (err) => {
if (isThirdPartyError(err)) return;
throw err;
Expand Down Expand Up @@ -177,7 +181,9 @@ test('library lists local items and SearchInput filters them', async ({ page })

// Both saved items appear as cards in the home grid (hub PRs: library-list /
// lib-row-* became home-grid / home-card-* — see HomeView.tsx + DiagramCard.tsx).
await expect(page.locator('[data-testid="home-grid"]')).toBeVisible({ timeout: 10_000 });
await expect(page.locator('[data-testid="home-grid"]')).toBeVisible({
timeout: 10_000,
});
await expect(page.getByText('AlphaDiagram', { exact: true })).toBeVisible();
await expect(page.getByText('GammaDiagram', { exact: true })).toBeVisible();

Expand Down Expand Up @@ -219,7 +225,9 @@ test('export-all triggers a JSON file download', async ({ page }) => {
});

await reloadIntoLibrary(page);
await expect(page.getByText('ExportableDiagram', { exact: true })).toBeVisible();
await expect(
page.getByText('ExportableDiagram', { exact: true }),
).toBeVisible();

// Clicking export-all builds a Blob and triggers a download (handleExportAll →
// downloadText('zenuml-diagrams.json', …)). Assert the download event fires.
Expand Down Expand Up @@ -251,7 +259,9 @@ test('importing a JSON file adds a new library row', async ({ page }) => {
});

await reloadIntoLibrary(page);
await expect(page.getByText('PreexistingDiagram', { exact: true })).toBeVisible();
await expect(
page.getByText('PreexistingDiagram', { exact: true }),
).toBeVisible();

// Build a minimal import payload matching the { items: Item[] } shape that
// exportAllItemsJson produces and parseImportJson accepts. The migrate step
Expand Down Expand Up @@ -288,6 +298,10 @@ test('importing a JSON file adds a new library row', async ({ page }) => {
// regardless of in-page subscription timing.
await reloadIntoLibrary(page);

await expect(page.getByText('ImportedDiagram', { exact: true })).toBeVisible();
await expect(page.getByText('PreexistingDiagram', { exact: true })).toBeVisible();
await expect(
page.getByText('ImportedDiagram', { exact: true }),
).toBeVisible();
await expect(
page.getByText('PreexistingDiagram', { exact: true }),
).toBeVisible();
});
Loading
Loading