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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
36 changes: 36 additions & 0 deletions e2e/helpers/auth.ts
Original file line number Diff line number Diff line change
@@ -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 });
}
29 changes: 18 additions & 11 deletions e2e/sidebar-text-visibility.spec.ts
Original file line number Diff line number Diff line change
@@ -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();
Expand Down Expand Up @@ -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();

Expand Down