Preserve stable snapshots and stabilize Electron e2e (#734)
This commit is contained in:
committed by
GitHub
Unverified
parent
34bbb039d3
commit
5a3da41562
@@ -5,12 +5,16 @@ import { createServer } from 'node:net';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join, resolve } from 'node:path';
|
||||
|
||||
type LaunchElectronOptions = {
|
||||
skipSetup?: boolean;
|
||||
};
|
||||
|
||||
type ElectronFixtures = {
|
||||
electronApp: ElectronApplication;
|
||||
page: Page;
|
||||
homeDir: string;
|
||||
userDataDir: string;
|
||||
launchElectronApp: () => Promise<ElectronApplication>;
|
||||
launchElectronApp: (options?: LaunchElectronOptions) => Promise<ElectronApplication>;
|
||||
};
|
||||
|
||||
const repoRoot = resolve(process.cwd());
|
||||
@@ -38,7 +42,77 @@ async function allocatePort(): Promise<number> {
|
||||
});
|
||||
}
|
||||
|
||||
async function launchClawXElectron(homeDir: string, userDataDir: string): Promise<ElectronApplication> {
|
||||
async function getStableWindow(app: ElectronApplication): Promise<Page> {
|
||||
const deadline = Date.now() + 30_000;
|
||||
let page = await app.firstWindow();
|
||||
|
||||
while (Date.now() < deadline) {
|
||||
const openWindows = app.windows().filter((candidate) => !candidate.isClosed());
|
||||
const currentWindow = openWindows.at(-1) ?? page;
|
||||
|
||||
if (currentWindow && !currentWindow.isClosed()) {
|
||||
try {
|
||||
await currentWindow.waitForLoadState('domcontentloaded', { timeout: 2_000 });
|
||||
return currentWindow;
|
||||
} catch (error) {
|
||||
if (!String(error).includes('has been closed')) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
page = await app.waitForEvent('window', { timeout: 2_000 });
|
||||
} catch {
|
||||
// Keep polling until a stable window is available or the deadline expires.
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('No stable Electron window became available');
|
||||
}
|
||||
|
||||
async function closeElectronApp(app: ElectronApplication, timeoutMs = 5_000): Promise<void> {
|
||||
let closed = false;
|
||||
|
||||
await Promise.race([
|
||||
(async () => {
|
||||
const [closeResult] = await Promise.allSettled([
|
||||
app.waitForEvent('close', { timeout: timeoutMs }),
|
||||
app.evaluate(({ app: electronApp }) => {
|
||||
electronApp.quit();
|
||||
}),
|
||||
]);
|
||||
|
||||
if (closeResult.status === 'fulfilled') {
|
||||
closed = true;
|
||||
}
|
||||
})(),
|
||||
new Promise((resolve) => setTimeout(resolve, timeoutMs)),
|
||||
]);
|
||||
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await app.close();
|
||||
return;
|
||||
} catch {
|
||||
// Fall through to process kill if Playwright cannot close the app cleanly.
|
||||
}
|
||||
|
||||
try {
|
||||
app.process().kill('SIGKILL');
|
||||
} catch {
|
||||
// Ignore process kill failures during e2e teardown.
|
||||
}
|
||||
}
|
||||
|
||||
async function launchClawXElectron(
|
||||
homeDir: string,
|
||||
userDataDir: string,
|
||||
options: LaunchElectronOptions = {},
|
||||
): Promise<ElectronApplication> {
|
||||
const hostApiPort = await allocatePort();
|
||||
const electronEnv = process.platform === 'linux'
|
||||
? { ELECTRON_DISABLE_SANDBOX: '1' }
|
||||
@@ -56,6 +130,7 @@ async function launchClawXElectron(homeDir: string, userDataDir: string): Promis
|
||||
XDG_CONFIG_HOME: join(homeDir, '.config'),
|
||||
CLAWX_E2E: '1',
|
||||
CLAWX_USER_DATA_DIR: userDataDir,
|
||||
...(options.skipSetup ? { CLAWX_E2E_SKIP_SETUP: '1' } : {}),
|
||||
CLAWX_PORT_CLAWX_HOST_API: String(hostApiPort),
|
||||
},
|
||||
timeout: 90_000,
|
||||
@@ -85,7 +160,7 @@ export const test = base.extend<ElectronFixtures>({
|
||||
},
|
||||
|
||||
launchElectronApp: async ({ homeDir, userDataDir }, provideLauncher) => {
|
||||
await provideLauncher(async () => await launchClawXElectron(homeDir, userDataDir));
|
||||
await provideLauncher(async (options?: LaunchElectronOptions) => await launchClawXElectron(homeDir, userDataDir, options));
|
||||
},
|
||||
|
||||
electronApp: async ({ launchElectronApp }, provideElectronApp) => {
|
||||
@@ -99,14 +174,13 @@ export const test = base.extend<ElectronFixtures>({
|
||||
await provideElectronApp(app);
|
||||
} finally {
|
||||
if (!appClosed) {
|
||||
await app.close().catch(() => {});
|
||||
await closeElectronApp(app);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
page: async ({ electronApp }, providePage) => {
|
||||
const page = await electronApp.firstWindow();
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
const page = await getStableWindow(electronApp);
|
||||
await providePage(page);
|
||||
},
|
||||
});
|
||||
@@ -117,4 +191,6 @@ export async function completeSetup(page: Page): Promise<void> {
|
||||
await expect(page.getByTestId('main-layout')).toBeVisible();
|
||||
}
|
||||
|
||||
export { closeElectronApp };
|
||||
export { getStableWindow };
|
||||
export { expect };
|
||||
|
||||
Reference in New Issue
Block a user