From 11b1bbc14364bda7a2f0489e65c8f51ec72b8f08 Mon Sep 17 00:00:00 2001 From: webdevcody Date: Wed, 7 Jan 2026 16:10:17 -0500 Subject: [PATCH] feat: implement splash screen handling in navigation and interactions - Added a new function `waitForSplashScreenToDisappear` to manage splash screen visibility, ensuring it does not block user interactions. - Integrated splash screen checks in various navigation functions and interaction methods to enhance user experience by waiting for the splash screen to disappear before proceeding. - Updated test setup to disable the splash screen during tests for consistent testing behavior. --- apps/ui/tests/utils/core/interactions.ts | 3 ++ apps/ui/tests/utils/core/waiting.ts | 57 ++++++++++++++++++++++++ apps/ui/tests/utils/navigation/views.ts | 20 ++++++++- apps/ui/tests/utils/project/fixtures.ts | 3 ++ apps/ui/tests/utils/project/setup.ts | 39 ++++++++++++++++ apps/ui/tests/utils/views/agent.ts | 4 +- 6 files changed, 124 insertions(+), 2 deletions(-) diff --git a/apps/ui/tests/utils/core/interactions.ts b/apps/ui/tests/utils/core/interactions.ts index 4e458d2a..22da6a18 100644 --- a/apps/ui/tests/utils/core/interactions.ts +++ b/apps/ui/tests/utils/core/interactions.ts @@ -1,5 +1,6 @@ import { Page, expect } from '@playwright/test'; import { getByTestId, getButtonByText } from './elements'; +import { waitForSplashScreenToDisappear } from './waiting'; /** * Get the platform-specific modifier key (Meta for Mac, Control for Windows/Linux) @@ -21,6 +22,8 @@ export async function pressModifierEnter(page: Page): Promise { * Click an element by its data-testid attribute */ export async function clickElement(page: Page, testId: string): Promise { + // Wait for splash screen to disappear first (safety net) + await waitForSplashScreenToDisappear(page, 2000); const element = await getByTestId(page, testId); await element.click(); } diff --git a/apps/ui/tests/utils/core/waiting.ts b/apps/ui/tests/utils/core/waiting.ts index 09a073b0..54952efa 100644 --- a/apps/ui/tests/utils/core/waiting.ts +++ b/apps/ui/tests/utils/core/waiting.ts @@ -40,3 +40,60 @@ export async function waitForElementHidden( state: 'hidden', }); } + +/** + * Wait for the splash screen to disappear + * The splash screen has z-[9999] and blocks interactions, so we need to wait for it + */ +export async function waitForSplashScreenToDisappear(page: Page, timeout = 5000): Promise { + try { + // Check if splash screen is shown via sessionStorage first (fastest check) + const splashShown = await page.evaluate(() => { + return sessionStorage.getItem('automaker-splash-shown') === 'true'; + }); + + // If splash is already marked as shown, it won't appear, so we're done + if (splashShown) { + return; + } + + // Otherwise, wait for the splash screen element to disappear + // The splash screen is a div with z-[9999] and fixed inset-0 + // We check for elements that match the splash screen pattern + await page.waitForFunction( + () => { + // Check if splash is marked as shown in sessionStorage + if (sessionStorage.getItem('automaker-splash-shown') === 'true') { + return true; + } + + // Check for splash screen element by looking for fixed inset-0 with high z-index + const allDivs = document.querySelectorAll('div'); + for (const div of allDivs) { + const style = window.getComputedStyle(div); + const classes = div.className || ''; + // Check if it matches splash screen pattern: fixed, inset-0, and high z-index + if ( + style.position === 'fixed' && + (classes.includes('inset-0') || + (style.top === '0px' && + style.left === '0px' && + style.right === '0px' && + style.bottom === '0px')) && + (classes.includes('z-[') || parseInt(style.zIndex) >= 9999) + ) { + // Check if it's visible and blocking (opacity > 0 and pointer-events not none) + if (style.opacity !== '0' && style.pointerEvents !== 'none') { + return false; // Splash screen is still visible + } + } + } + return true; // No visible splash screen found + }, + { timeout } + ); + } catch { + // Splash screen might not exist or already gone, which is fine + // No need to wait - if it doesn't exist, we're good + } +} diff --git a/apps/ui/tests/utils/navigation/views.ts b/apps/ui/tests/utils/navigation/views.ts index 014b84d3..d83f90f4 100644 --- a/apps/ui/tests/utils/navigation/views.ts +++ b/apps/ui/tests/utils/navigation/views.ts @@ -1,7 +1,7 @@ import { Page } from '@playwright/test'; import { clickElement } from '../core/interactions'; import { handleLoginScreenIfPresent } from '../core/interactions'; -import { waitForElement } from '../core/waiting'; +import { waitForElement, waitForSplashScreenToDisappear } from '../core/waiting'; import { authenticateForTests } from '../api/client'; /** @@ -16,6 +16,9 @@ export async function navigateToBoard(page: Page): Promise { await page.goto('/board'); await page.waitForLoadState('load'); + // Wait for splash screen to disappear (safety net) + await waitForSplashScreenToDisappear(page, 3000); + // Handle login redirect if needed await handleLoginScreenIfPresent(page); @@ -35,6 +38,9 @@ export async function navigateToContext(page: Page): Promise { await page.goto('/context'); await page.waitForLoadState('load'); + // Wait for splash screen to disappear (safety net) + await waitForSplashScreenToDisappear(page, 3000); + // Handle login redirect if needed await handleLoginScreenIfPresent(page); @@ -67,6 +73,9 @@ export async function navigateToSpec(page: Page): Promise { await page.goto('/spec'); await page.waitForLoadState('load'); + // Wait for splash screen to disappear (safety net) + await waitForSplashScreenToDisappear(page, 3000); + // Wait for loading state to complete first (if present) const loadingElement = page.locator('[data-testid="spec-view-loading"]'); try { @@ -100,6 +109,9 @@ export async function navigateToAgent(page: Page): Promise { await page.goto('/agent'); await page.waitForLoadState('load'); + // Wait for splash screen to disappear (safety net) + await waitForSplashScreenToDisappear(page, 3000); + // Handle login redirect if needed await handleLoginScreenIfPresent(page); @@ -119,6 +131,9 @@ export async function navigateToSettings(page: Page): Promise { await page.goto('/settings'); await page.waitForLoadState('load'); + // Wait for splash screen to disappear (safety net) + await waitForSplashScreenToDisappear(page, 3000); + // Wait for the settings view to be visible await waitForElement(page, 'settings-view', { timeout: 10000 }); } @@ -146,6 +161,9 @@ export async function navigateToWelcome(page: Page): Promise { await page.goto('/'); await page.waitForLoadState('load'); + // Wait for splash screen to disappear (safety net) + await waitForSplashScreenToDisappear(page, 3000); + // Handle login redirect if needed await handleLoginScreenIfPresent(page); diff --git a/apps/ui/tests/utils/project/fixtures.ts b/apps/ui/tests/utils/project/fixtures.ts index e25a31b7..a02a9163 100644 --- a/apps/ui/tests/utils/project/fixtures.ts +++ b/apps/ui/tests/utils/project/fixtures.ts @@ -110,6 +110,9 @@ export async function setupProjectWithFixture( version: 0, // setup-store.ts doesn't specify a version, so zustand defaults to 0 }; localStorage.setItem('automaker-setup', JSON.stringify(setupState)); + + // Disable splash screen in tests + sessionStorage.setItem('automaker-splash-shown', 'true'); }, projectPath); } diff --git a/apps/ui/tests/utils/project/setup.ts b/apps/ui/tests/utils/project/setup.ts index d1027ff3..f1192d3d 100644 --- a/apps/ui/tests/utils/project/setup.ts +++ b/apps/ui/tests/utils/project/setup.ts @@ -81,6 +81,9 @@ export async function setupWelcomeView( if (opts?.workspaceDir) { localStorage.setItem('automaker:lastProjectDir', opts.workspaceDir); } + + // Disable splash screen in tests + sessionStorage.setItem('automaker-splash-shown', 'true'); }, { opts: options, versions: STORE_VERSIONS } ); @@ -156,6 +159,9 @@ export async function setupRealProject( version: versions.SETUP_STORE, }; localStorage.setItem('automaker-setup', JSON.stringify(setupState)); + + // Disable splash screen in tests + sessionStorage.setItem('automaker-splash-shown', 'true'); }, { path: projectPath, name: projectName, opts: options, versions: STORE_VERSIONS } ); @@ -189,6 +195,9 @@ export async function setupMockProject(page: Page): Promise { }; localStorage.setItem('automaker-storage', JSON.stringify(mockState)); + + // Disable splash screen in tests + sessionStorage.setItem('automaker-splash-shown', 'true'); }); } @@ -260,6 +269,9 @@ export async function setupMockProjectAtConcurrencyLimit( }; localStorage.setItem('automaker-storage', JSON.stringify(mockState)); + + // Disable splash screen in tests + sessionStorage.setItem('automaker-splash-shown', 'true'); }, { maxConcurrency, runningTasks } ); @@ -315,6 +327,9 @@ export async function setupMockProjectWithFeatures( // Also store features in a global variable that the mock electron API can use // This is needed because the board-view loads features from the file system (window as any).__mockFeatures = mockFeatures; + + // Disable splash screen in tests + sessionStorage.setItem('automaker-splash-shown', 'true'); }, options); } @@ -352,6 +367,9 @@ export async function setupMockProjectWithContextFile( localStorage.setItem('automaker-storage', JSON.stringify(mockState)); + // Disable splash screen in tests + sessionStorage.setItem('automaker-splash-shown', 'true'); + // Set up mock file system with a context file for the feature // This will be used by the mock electron API // Now uses features/{id}/agent-output.md path @@ -470,6 +488,9 @@ export async function setupEmptyLocalStorage(page: Page): Promise { version: 2, // Must match app-store.ts persist version }; localStorage.setItem('automaker-storage', JSON.stringify(mockState)); + + // Disable splash screen in tests + sessionStorage.setItem('automaker-splash-shown', 'true'); }); } @@ -509,6 +530,9 @@ export async function setupMockProjectsWithoutCurrent(page: Page): Promise }; localStorage.setItem('automaker-storage', JSON.stringify(mockState)); + + // Disable splash screen in tests + sessionStorage.setItem('automaker-splash-shown', 'true'); }); } @@ -560,6 +584,9 @@ export async function setupMockProjectWithSkipTestsFeatures( }; localStorage.setItem('automaker-storage', JSON.stringify(mockState)); + + // Disable splash screen in tests + sessionStorage.setItem('automaker-splash-shown', 'true'); }, options); } @@ -633,6 +660,9 @@ export async function setupMockProjectWithAgentOutput( localStorage.setItem('automaker-storage', JSON.stringify(mockState)); + // Disable splash screen in tests + sessionStorage.setItem('automaker-splash-shown', 'true'); + // Set up mock file system with output content for the feature // Now uses features/{id}/agent-output.md path (window as any).__mockContextFile = { @@ -749,6 +779,9 @@ export async function setupFirstRun(page: Page): Promise { }; localStorage.setItem('automaker-storage', JSON.stringify(appState)); + + // Disable splash screen in tests + sessionStorage.setItem('automaker-splash-shown', 'true'); }); } @@ -769,6 +802,9 @@ export async function setupComplete(page: Page): Promise { }; localStorage.setItem('automaker-setup', JSON.stringify(setupState)); + + // Disable splash screen in tests + sessionStorage.setItem('automaker-splash-shown', 'true'); }, STORE_VERSIONS); } @@ -880,5 +916,8 @@ export async function setupMockProjectWithProfiles( version: 0, // setup-store.ts doesn't specify a version, so zustand defaults to 0 }; localStorage.setItem('automaker-setup', JSON.stringify(setupState)); + + // Disable splash screen in tests + sessionStorage.setItem('automaker-splash-shown', 'true'); }, options); } diff --git a/apps/ui/tests/utils/views/agent.ts b/apps/ui/tests/utils/views/agent.ts index cf8b7cfa..ccce42c0 100644 --- a/apps/ui/tests/utils/views/agent.ts +++ b/apps/ui/tests/utils/views/agent.ts @@ -1,5 +1,5 @@ import { Page, Locator } from '@playwright/test'; -import { waitForElement } from '../core/waiting'; +import { waitForElement, waitForSplashScreenToDisappear } from '../core/waiting'; /** * Get the session list element @@ -19,6 +19,8 @@ export async function getNewSessionButton(page: Page): Promise { * Click the new session button */ export async function clickNewSessionButton(page: Page): Promise { + // Wait for splash screen to disappear first (safety net) + await waitForSplashScreenToDisappear(page, 3000); const button = await getNewSessionButton(page); await button.click(); }