diff --git a/CHANGELOG.md b/CHANGELOG.md index c6a804327..d3449dfdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- **E2E sidebar tests failing due to missing auth** (`e2e`): The `sidebar-text-visibility.spec.ts` tests failed because the MSW mock environment returns `auth.enabled: true` in its discovery response, causing `AuthGuard` to redirect unauthenticated users to the login page where no sidebar exists. Added `e2e/helpers/auth.ts` with a `registerAndLogin()` helper that signs up a test user via the register form, and updated both sidebar tests to authenticate before asserting sidebar visibility. + - **Changesets release bump escalation** (`release`): Added `___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH.onlyUpdatePeerDependentsWhenOutOfRange` in `.changeset/config.json` and moved internal `@object-ui/*` entries in `@object-ui/plugin-designer` from `peerDependencies` to `dependencies` to prevent minor release PRs from incorrectly escalating fixed-group packages to a major version. - **Home page star/favorite not reactive** (`@object-ui/console`): Migrated `useFavorites` from standalone hook to React Context (`FavoritesProvider`) so all consumers (HomePage, AppCard, AppSidebar, UnifiedSidebar) share a single state instance. Previously, each component calling `useFavorites()` created independent state, so toggling a favorite in AppCard did not trigger re-render in HomePage. localStorage persistence is retained as the storage layer. diff --git a/e2e/helpers/auth.ts b/e2e/helpers/auth.ts new file mode 100644 index 000000000..9f379befe --- /dev/null +++ b/e2e/helpers/auth.ts @@ -0,0 +1,36 @@ +import type { Page } from '@playwright/test'; +import { CONSOLE_BASE } from './index'; + +/** Shared timeout for waiting on React rendering and MSW boot. */ +const REACT_READY_TIMEOUT = 30_000; + +/** + * Register a new test user and wait for redirect to the home page. + * + * MSW auth handlers run in-memory, so each browser context starts + * without a session. This helper creates a fresh user via the + * register form so that subsequent navigation hits authenticated + * routes (HomeLayout with sidebar, etc.). + */ +export async function registerAndLogin(page: Page) { + await page.goto(`${CONSOLE_BASE}/register`); + + // Wait for React to mount and the register form to render + await page.waitForFunction( + () => (document.getElementById('root')?.children.length ?? 0) > 0, + { timeout: REACT_READY_TIMEOUT }, + ); + // Wait for the form inputs to be present (MSW boot + React render) + await page.locator('input[type="email"]').waitFor({ state: 'visible', timeout: 15_000 }); + + // Fill in the registration form + const ts = Date.now(); + await page.locator('input[type="text"]').fill('E2E Test User'); + await page.locator('input[type="email"]').fill(`e2e-${ts}@test.local`); + await page.locator('input[type="password"]').first().fill('Test1234!'); + await page.locator('input[type="password"]').nth(1).fill('Test1234!'); + + // Submit and wait for navigation away from the register page + await page.locator('button[type="submit"]').click(); + await page.waitForURL(/\/console\/(home|apps\/)/, { timeout: REACT_READY_TIMEOUT }); +} diff --git a/e2e/sidebar-text-visibility.spec.ts b/e2e/sidebar-text-visibility.spec.ts index dfe091f44..89800fe0d 100644 --- a/e2e/sidebar-text-visibility.spec.ts +++ b/e2e/sidebar-text-visibility.spec.ts @@ -1,25 +1,33 @@ import { test, expect } from '@playwright/test'; -import { waitForReactMount, CONSOLE_BASE } from './helpers'; +import { CONSOLE_BASE } from './helpers'; +import { registerAndLogin } from './helpers/auth'; + +/** Timeout for sidebar elements to become visible after auth + page render. */ +const SIDEBAR_VISIBLE_TIMEOUT = 15_000; /** * Sidebar text visibility tests * * These tests validate that the sidebar displays text correctly * when toggled between collapsed (icon mode) and expanded states. + * + * The MSW mock environment requires authentication, so each test + * registers a fresh user before navigating to the home page. */ test.describe('Sidebar Text Visibility', () => { test('should show all text labels when sidebar is expanded in icon mode', async ({ page }) => { - await page.goto(`${CONSOLE_BASE}/`); - await waitForReactMount(page); + // registerAndLogin navigates to /register, signs up, and waits for the + // redirect to /home — the page is already authenticated & on the home route. + await registerAndLogin(page); - // Wait for sidebar to be visible + // Wait for sidebar to be visible (page needs time to render after auth redirect) const sidebar = page.locator('[data-sidebar="sidebar"]').first(); - await expect(sidebar).toBeVisible(); + await expect(sidebar).toBeVisible({ timeout: SIDEBAR_VISIBLE_TIMEOUT }); - // Find the sidebar toggle button + // Find the sidebar toggle button (desktop trigger rendered by AppShell) const toggleButton = page.locator('[data-sidebar="trigger"]').first(); - await expect(toggleButton).toBeVisible(); + await expect(toggleButton).toBeVisible({ timeout: SIDEBAR_VISIBLE_TIMEOUT }); // Get the parent sidebar element that has data-state attribute const sidebarGroup = page.locator('.group[data-collapsible="icon"]').first(); @@ -92,14 +100,13 @@ test.describe('Sidebar Text Visibility', () => { }); test('should hide text labels when sidebar is collapsed in icon mode', async ({ page }) => { - await page.goto(`${CONSOLE_BASE}/`); - await waitForReactMount(page); + await registerAndLogin(page); const sidebar = page.locator('[data-sidebar="sidebar"]').first(); - await expect(sidebar).toBeVisible(); + await expect(sidebar).toBeVisible({ timeout: SIDEBAR_VISIBLE_TIMEOUT }); const toggleButton = page.locator('[data-sidebar="trigger"]').first(); - await expect(toggleButton).toBeVisible(); + await expect(toggleButton).toBeVisible({ timeout: SIDEBAR_VISIBLE_TIMEOUT }); const sidebarGroup = page.locator('.group[data-collapsible="icon"]').first();