mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 08:53:36 +00:00
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.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { Page, expect } from '@playwright/test';
|
import { Page, expect } from '@playwright/test';
|
||||||
import { getByTestId, getButtonByText } from './elements';
|
import { getByTestId, getButtonByText } from './elements';
|
||||||
|
import { waitForSplashScreenToDisappear } from './waiting';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the platform-specific modifier key (Meta for Mac, Control for Windows/Linux)
|
* Get the platform-specific modifier key (Meta for Mac, Control for Windows/Linux)
|
||||||
@@ -21,6 +22,8 @@ export async function pressModifierEnter(page: Page): Promise<void> {
|
|||||||
* Click an element by its data-testid attribute
|
* Click an element by its data-testid attribute
|
||||||
*/
|
*/
|
||||||
export async function clickElement(page: Page, testId: string): Promise<void> {
|
export async function clickElement(page: Page, testId: string): Promise<void> {
|
||||||
|
// Wait for splash screen to disappear first (safety net)
|
||||||
|
await waitForSplashScreenToDisappear(page, 2000);
|
||||||
const element = await getByTestId(page, testId);
|
const element = await getByTestId(page, testId);
|
||||||
await element.click();
|
await element.click();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,3 +40,60 @@ export async function waitForElementHidden(
|
|||||||
state: 'hidden',
|
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<void> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Page } from '@playwright/test';
|
import { Page } from '@playwright/test';
|
||||||
import { clickElement } from '../core/interactions';
|
import { clickElement } from '../core/interactions';
|
||||||
import { handleLoginScreenIfPresent } from '../core/interactions';
|
import { handleLoginScreenIfPresent } from '../core/interactions';
|
||||||
import { waitForElement } from '../core/waiting';
|
import { waitForElement, waitForSplashScreenToDisappear } from '../core/waiting';
|
||||||
import { authenticateForTests } from '../api/client';
|
import { authenticateForTests } from '../api/client';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,6 +16,9 @@ export async function navigateToBoard(page: Page): Promise<void> {
|
|||||||
await page.goto('/board');
|
await page.goto('/board');
|
||||||
await page.waitForLoadState('load');
|
await page.waitForLoadState('load');
|
||||||
|
|
||||||
|
// Wait for splash screen to disappear (safety net)
|
||||||
|
await waitForSplashScreenToDisappear(page, 3000);
|
||||||
|
|
||||||
// Handle login redirect if needed
|
// Handle login redirect if needed
|
||||||
await handleLoginScreenIfPresent(page);
|
await handleLoginScreenIfPresent(page);
|
||||||
|
|
||||||
@@ -35,6 +38,9 @@ export async function navigateToContext(page: Page): Promise<void> {
|
|||||||
await page.goto('/context');
|
await page.goto('/context');
|
||||||
await page.waitForLoadState('load');
|
await page.waitForLoadState('load');
|
||||||
|
|
||||||
|
// Wait for splash screen to disappear (safety net)
|
||||||
|
await waitForSplashScreenToDisappear(page, 3000);
|
||||||
|
|
||||||
// Handle login redirect if needed
|
// Handle login redirect if needed
|
||||||
await handleLoginScreenIfPresent(page);
|
await handleLoginScreenIfPresent(page);
|
||||||
|
|
||||||
@@ -67,6 +73,9 @@ export async function navigateToSpec(page: Page): Promise<void> {
|
|||||||
await page.goto('/spec');
|
await page.goto('/spec');
|
||||||
await page.waitForLoadState('load');
|
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)
|
// Wait for loading state to complete first (if present)
|
||||||
const loadingElement = page.locator('[data-testid="spec-view-loading"]');
|
const loadingElement = page.locator('[data-testid="spec-view-loading"]');
|
||||||
try {
|
try {
|
||||||
@@ -100,6 +109,9 @@ export async function navigateToAgent(page: Page): Promise<void> {
|
|||||||
await page.goto('/agent');
|
await page.goto('/agent');
|
||||||
await page.waitForLoadState('load');
|
await page.waitForLoadState('load');
|
||||||
|
|
||||||
|
// Wait for splash screen to disappear (safety net)
|
||||||
|
await waitForSplashScreenToDisappear(page, 3000);
|
||||||
|
|
||||||
// Handle login redirect if needed
|
// Handle login redirect if needed
|
||||||
await handleLoginScreenIfPresent(page);
|
await handleLoginScreenIfPresent(page);
|
||||||
|
|
||||||
@@ -119,6 +131,9 @@ export async function navigateToSettings(page: Page): Promise<void> {
|
|||||||
await page.goto('/settings');
|
await page.goto('/settings');
|
||||||
await page.waitForLoadState('load');
|
await page.waitForLoadState('load');
|
||||||
|
|
||||||
|
// Wait for splash screen to disappear (safety net)
|
||||||
|
await waitForSplashScreenToDisappear(page, 3000);
|
||||||
|
|
||||||
// Wait for the settings view to be visible
|
// Wait for the settings view to be visible
|
||||||
await waitForElement(page, 'settings-view', { timeout: 10000 });
|
await waitForElement(page, 'settings-view', { timeout: 10000 });
|
||||||
}
|
}
|
||||||
@@ -146,6 +161,9 @@ export async function navigateToWelcome(page: Page): Promise<void> {
|
|||||||
await page.goto('/');
|
await page.goto('/');
|
||||||
await page.waitForLoadState('load');
|
await page.waitForLoadState('load');
|
||||||
|
|
||||||
|
// Wait for splash screen to disappear (safety net)
|
||||||
|
await waitForSplashScreenToDisappear(page, 3000);
|
||||||
|
|
||||||
// Handle login redirect if needed
|
// Handle login redirect if needed
|
||||||
await handleLoginScreenIfPresent(page);
|
await handleLoginScreenIfPresent(page);
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,9 @@ export async function setupProjectWithFixture(
|
|||||||
version: 0, // setup-store.ts doesn't specify a version, so zustand defaults to 0
|
version: 0, // setup-store.ts doesn't specify a version, so zustand defaults to 0
|
||||||
};
|
};
|
||||||
localStorage.setItem('automaker-setup', JSON.stringify(setupState));
|
localStorage.setItem('automaker-setup', JSON.stringify(setupState));
|
||||||
|
|
||||||
|
// Disable splash screen in tests
|
||||||
|
sessionStorage.setItem('automaker-splash-shown', 'true');
|
||||||
}, projectPath);
|
}, projectPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,9 @@ export async function setupWelcomeView(
|
|||||||
if (opts?.workspaceDir) {
|
if (opts?.workspaceDir) {
|
||||||
localStorage.setItem('automaker:lastProjectDir', opts.workspaceDir);
|
localStorage.setItem('automaker:lastProjectDir', opts.workspaceDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable splash screen in tests
|
||||||
|
sessionStorage.setItem('automaker-splash-shown', 'true');
|
||||||
},
|
},
|
||||||
{ opts: options, versions: STORE_VERSIONS }
|
{ opts: options, versions: STORE_VERSIONS }
|
||||||
);
|
);
|
||||||
@@ -156,6 +159,9 @@ export async function setupRealProject(
|
|||||||
version: versions.SETUP_STORE,
|
version: versions.SETUP_STORE,
|
||||||
};
|
};
|
||||||
localStorage.setItem('automaker-setup', JSON.stringify(setupState));
|
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 }
|
{ path: projectPath, name: projectName, opts: options, versions: STORE_VERSIONS }
|
||||||
);
|
);
|
||||||
@@ -189,6 +195,9 @@ export async function setupMockProject(page: Page): Promise<void> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
localStorage.setItem('automaker-storage', JSON.stringify(mockState));
|
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));
|
localStorage.setItem('automaker-storage', JSON.stringify(mockState));
|
||||||
|
|
||||||
|
// Disable splash screen in tests
|
||||||
|
sessionStorage.setItem('automaker-splash-shown', 'true');
|
||||||
},
|
},
|
||||||
{ maxConcurrency, runningTasks }
|
{ maxConcurrency, runningTasks }
|
||||||
);
|
);
|
||||||
@@ -315,6 +327,9 @@ export async function setupMockProjectWithFeatures(
|
|||||||
// Also store features in a global variable that the mock electron API can use
|
// 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
|
// This is needed because the board-view loads features from the file system
|
||||||
(window as any).__mockFeatures = mockFeatures;
|
(window as any).__mockFeatures = mockFeatures;
|
||||||
|
|
||||||
|
// Disable splash screen in tests
|
||||||
|
sessionStorage.setItem('automaker-splash-shown', 'true');
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,6 +367,9 @@ export async function setupMockProjectWithContextFile(
|
|||||||
|
|
||||||
localStorage.setItem('automaker-storage', JSON.stringify(mockState));
|
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
|
// Set up mock file system with a context file for the feature
|
||||||
// This will be used by the mock electron API
|
// This will be used by the mock electron API
|
||||||
// Now uses features/{id}/agent-output.md path
|
// Now uses features/{id}/agent-output.md path
|
||||||
@@ -470,6 +488,9 @@ export async function setupEmptyLocalStorage(page: Page): Promise<void> {
|
|||||||
version: 2, // Must match app-store.ts persist version
|
version: 2, // Must match app-store.ts persist version
|
||||||
};
|
};
|
||||||
localStorage.setItem('automaker-storage', JSON.stringify(mockState));
|
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<void>
|
|||||||
};
|
};
|
||||||
|
|
||||||
localStorage.setItem('automaker-storage', JSON.stringify(mockState));
|
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));
|
localStorage.setItem('automaker-storage', JSON.stringify(mockState));
|
||||||
|
|
||||||
|
// Disable splash screen in tests
|
||||||
|
sessionStorage.setItem('automaker-splash-shown', 'true');
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -633,6 +660,9 @@ export async function setupMockProjectWithAgentOutput(
|
|||||||
|
|
||||||
localStorage.setItem('automaker-storage', JSON.stringify(mockState));
|
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
|
// Set up mock file system with output content for the feature
|
||||||
// Now uses features/{id}/agent-output.md path
|
// Now uses features/{id}/agent-output.md path
|
||||||
(window as any).__mockContextFile = {
|
(window as any).__mockContextFile = {
|
||||||
@@ -749,6 +779,9 @@ export async function setupFirstRun(page: Page): Promise<void> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
localStorage.setItem('automaker-storage', JSON.stringify(appState));
|
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<void> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
localStorage.setItem('automaker-setup', JSON.stringify(setupState));
|
localStorage.setItem('automaker-setup', JSON.stringify(setupState));
|
||||||
|
|
||||||
|
// Disable splash screen in tests
|
||||||
|
sessionStorage.setItem('automaker-splash-shown', 'true');
|
||||||
}, STORE_VERSIONS);
|
}, 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
|
version: 0, // setup-store.ts doesn't specify a version, so zustand defaults to 0
|
||||||
};
|
};
|
||||||
localStorage.setItem('automaker-setup', JSON.stringify(setupState));
|
localStorage.setItem('automaker-setup', JSON.stringify(setupState));
|
||||||
|
|
||||||
|
// Disable splash screen in tests
|
||||||
|
sessionStorage.setItem('automaker-splash-shown', 'true');
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Page, Locator } from '@playwright/test';
|
import { Page, Locator } from '@playwright/test';
|
||||||
import { waitForElement } from '../core/waiting';
|
import { waitForElement, waitForSplashScreenToDisappear } from '../core/waiting';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the session list element
|
* Get the session list element
|
||||||
@@ -19,6 +19,8 @@ export async function getNewSessionButton(page: Page): Promise<Locator> {
|
|||||||
* Click the new session button
|
* Click the new session button
|
||||||
*/
|
*/
|
||||||
export async function clickNewSessionButton(page: Page): Promise<void> {
|
export async function clickNewSessionButton(page: Page): Promise<void> {
|
||||||
|
// Wait for splash screen to disappear first (safety net)
|
||||||
|
await waitForSplashScreenToDisappear(page, 3000);
|
||||||
const button = await getNewSessionButton(page);
|
const button = await getNewSessionButton(page);
|
||||||
await button.click();
|
await button.click();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user