From 45a0af62fdb59d4fd6dd4428850f5d88ade7d61d Mon Sep 17 00:00:00 2001 From: Bijin A B Date: Sat, 21 Mar 2026 20:25:48 +0530 Subject: [PATCH] chore: test fixes --- tests/cookies/corrupted-passkey.spec.ts | 5 +- .../api-setEnvVar-with-persist.spec.ts | 11 +-- .../api-setEnvVar-without-persist.spec.ts | 10 ++- .../collection-env-import.spec.ts | 15 ++-- .../global-env-import.spec.ts | 7 ++ tests/onboarding/sample-collection.spec.ts | 23 ++---- tests/onboarding/welcome-modal.spec.ts | 22 ++---- .../response-pane-update-when-focused.spec.ts | 1 + tests/utils/page/actions.ts | 8 +- .../collection-reorder-persistence.spec.ts | 8 +- .../recovery-and-backup.spec.ts | 74 +++++++------------ 11 files changed, 81 insertions(+), 103 deletions(-) diff --git a/tests/cookies/corrupted-passkey.spec.ts b/tests/cookies/corrupted-passkey.spec.ts index 959a966ce..6dc70f8fd 100644 --- a/tests/cookies/corrupted-passkey.spec.ts +++ b/tests/cookies/corrupted-passkey.spec.ts @@ -1,13 +1,14 @@ import { test, expect, closeElectronApp } from '../../playwright'; import * as path from 'path'; import * as fs from 'fs/promises'; +import { waitForReadyPage } from '../utils/page'; test('should handle corrupted passkey and still display saved cookie list', async ({ createTmpDir, launchElectronApp }) => { const userDataPath = await createTmpDir('corrupted-passkey'); const app1 = await launchElectronApp({ userDataPath }); // 1. First run – add a cookie via the UI so `cookies.json` is created. - const page1 = await app1.firstWindow(); + const page1 = await waitForReadyPage(app1); await page1.waitForSelector('[data-trigger="cookies"]'); await page1.click('[data-trigger="cookies"]'); @@ -35,7 +36,7 @@ test('should handle corrupted passkey and still display saved cookie list', asyn // 3. Second run – Bruno should recover and still list the cookie domain const app2 = await launchElectronApp({ userDataPath }); - const page2 = await app2.firstWindow(); + const page2 = await waitForReadyPage(app2); await page2.waitForSelector('[data-trigger="cookies"]'); await page2.click('[data-trigger="cookies"]'); diff --git a/tests/environments/api-setEnvVar/api-setEnvVar-with-persist.spec.ts b/tests/environments/api-setEnvVar/api-setEnvVar-with-persist.spec.ts index 7efe1eb1f..91053734a 100644 --- a/tests/environments/api-setEnvVar/api-setEnvVar-with-persist.spec.ts +++ b/tests/environments/api-setEnvVar/api-setEnvVar-with-persist.spec.ts @@ -1,5 +1,5 @@ import { test, expect, closeElectronApp } from '../../../playwright'; -import { sendRequest } from '../../utils/page'; +import { sendRequest, waitForReadyPage } from '../../utils/page'; test.describe.serial('bru.setEnvVar(name, value, { persist: true })', () => { test('set env var with persist using script', async ({ pageWithUserData: page, restartApp }) => { @@ -23,8 +23,8 @@ test.describe.serial('bru.setEnvVar(name, value, { persist: true })', () => { await page.getByTestId('environment-selector-trigger').click(); // open environment configuration - await page.locator('#configure-env').hover(); - await page.locator('#configure-env').click(); + await page.locator('#configure-env').waitFor({ state: 'visible' }); + await page.locator('#configure-env').dispatchEvent('click'); const envTab = page.locator('.request-tab').filter({ has: page.locator('.tab-label', { hasText: 'Environments' }) }); await expect(envTab).toBeVisible(); @@ -36,7 +36,7 @@ test.describe.serial('bru.setEnvVar(name, value, { persist: true })', () => { // we restart the app to confirm that the environment variable is persisted const newApp = await restartApp(); - const newPage = await newApp.firstWindow(); + const newPage = await waitForReadyPage(newApp); // select the collection and request await newPage.locator('#sidebar-collection-name').click(); @@ -44,7 +44,8 @@ test.describe.serial('bru.setEnvVar(name, value, { persist: true })', () => { // open environment dropdown await newPage.getByTestId('environment-selector-trigger').click(); - await newPage.locator('#configure-env').click(); + await newPage.locator('#configure-env').waitFor({ state: 'visible' }); + await newPage.locator('#configure-env').dispatchEvent('click'); const newEnvTab = newPage.locator('.request-tab').filter({ hasText: 'Environments' }); await expect(newEnvTab).toBeVisible(); diff --git a/tests/environments/api-setEnvVar/api-setEnvVar-without-persist.spec.ts b/tests/environments/api-setEnvVar/api-setEnvVar-without-persist.spec.ts index acefe59d7..a18fc7e37 100644 --- a/tests/environments/api-setEnvVar/api-setEnvVar-without-persist.spec.ts +++ b/tests/environments/api-setEnvVar/api-setEnvVar-without-persist.spec.ts @@ -1,5 +1,5 @@ import { test, expect, closeElectronApp } from '../../../playwright'; -import { sendRequest } from '../../utils/page'; +import { sendRequest, waitForReadyPage } from '../../utils/page'; test.describe.serial('bru.setEnvVar(name, value)', () => { test('set env var using script', async ({ pageWithUserData: page, restartApp }) => { @@ -20,7 +20,8 @@ test.describe.serial('bru.setEnvVar(name, value)', () => { // confirm that the environment variable is set await page.getByTestId('environment-selector-trigger').click(); - await page.locator('#configure-env').click(); + await page.locator('#configure-env').waitFor({ state: 'visible' }); + await page.locator('#configure-env').dispatchEvent('click'); const envTab = page.locator('.request-tab').filter({ hasText: 'Environments' }); await expect(envTab).toBeVisible(); @@ -32,7 +33,7 @@ test.describe.serial('bru.setEnvVar(name, value)', () => { // we restart the app to confirm that the environment variable is not persisted const newApp = await restartApp(); - const newPage = await newApp.firstWindow(); + const newPage = await waitForReadyPage(newApp); // select the collection and request await newPage.locator('#sidebar-collection-name').click(); @@ -40,7 +41,8 @@ test.describe.serial('bru.setEnvVar(name, value)', () => { // open environment dropdown await newPage.getByTestId('environment-selector-trigger').click(); - await newPage.locator('#configure-env').click(); + await newPage.locator('#configure-env').waitFor({ state: 'visible' }); + await newPage.locator('#configure-env').dispatchEvent('click'); const newEnvTab = newPage.locator('.request-tab').filter({ hasText: 'Environments' }); await expect(newEnvTab).toBeVisible(); diff --git a/tests/environments/import-environment/collection-env-import.spec.ts b/tests/environments/import-environment/collection-env-import.spec.ts index 88d0b0554..bb68b4c99 100644 --- a/tests/environments/import-environment/collection-env-import.spec.ts +++ b/tests/environments/import-environment/collection-env-import.spec.ts @@ -54,15 +54,20 @@ test.describe('Collection Environment Import Tests', () => { const envTab = page.locator('.request-tab').filter({ hasText: 'Environments' }); await expect(envTab).toBeVisible(); + // Environment variables table uses react-virtuoso (virtual scroll), + // so only visible rows are in the DOM. Verify first visible batch, + // then scroll to reveal the rest. const envNameInputs = page.locator('input[name$=".name"]'); - await expect.poll(async () => envNameInputs.count()).toBeGreaterThanOrEqual(6); - await expect(envNameInputs.nth(0)).toHaveValue('host'); await expect(envNameInputs.nth(1)).toHaveValue('userId'); await expect(envNameInputs.nth(2)).toHaveValue('apiKey'); - await expect(envNameInputs.nth(3)).toHaveValue('postTitle'); - await expect(envNameInputs.nth(4)).toHaveValue('postBody'); - await expect(envNameInputs.nth(5)).toHaveValue('secretApiToken'); + + // Scroll the virtualized table to reveal remaining rows + await page.locator('.table-container').evaluate((el) => el.scrollTop = el.scrollHeight); + + await expect(page.locator('input[name$=".name"][value="postTitle"]')).toBeVisible(); + await expect(page.locator('input[name$=".name"][value="postBody"]')).toBeVisible(); + await expect(page.locator('input[name$=".name"][value="secretApiToken"]')).toBeVisible(); await expect(page.locator('input[name="5.secret"]')).toBeChecked(); await envTab.hover(); await envTab.getByTestId('request-tab-close-icon').click({ force: true }); diff --git a/tests/environments/import-environment/global-env-import.spec.ts b/tests/environments/import-environment/global-env-import.spec.ts index 55ae79086..a4a70de19 100644 --- a/tests/environments/import-environment/global-env-import.spec.ts +++ b/tests/environments/import-environment/global-env-import.spec.ts @@ -48,10 +48,17 @@ test.describe('Global Environment Import Tests', () => { const envTab = page.locator('.request-tab').filter({ hasText: 'Global Environments' }); await expect(envTab).toBeVisible(); + // Environment variables table uses react-virtuoso (virtual scroll), + // so only visible rows are in the DOM. Verify first visible batch, + // then scroll to reveal the rest. const variablesTable = page.locator('.table-container'); await expect(variablesTable.locator('input[name="0.name"]')).toHaveValue('host'); await expect(variablesTable.locator('input[name="1.name"]')).toHaveValue('userId'); await expect(variablesTable.locator('input[name="2.name"]')).toHaveValue('apiKey'); + + // Scroll the virtualized table to reveal remaining rows + await variablesTable.evaluate((el) => el.scrollTop = el.scrollHeight); + await expect(variablesTable.locator('input[name="3.name"]')).toHaveValue('postTitle'); await expect(variablesTable.locator('input[name="4.name"]')).toHaveValue('postBody'); await expect(variablesTable.locator('input[name="5.name"]')).toHaveValue('secretApiToken'); diff --git a/tests/onboarding/sample-collection.spec.ts b/tests/onboarding/sample-collection.spec.ts index 4790580d5..7b671b084 100644 --- a/tests/onboarding/sample-collection.spec.ts +++ b/tests/onboarding/sample-collection.spec.ts @@ -1,5 +1,6 @@ import path from 'path'; import { test, expect, errors, closeElectronApp } from '../../playwright'; +import { waitForReadyPage } from '../utils/page'; const initUserDataPath = path.join(__dirname, 'init-user-data-fresh'); @@ -20,10 +21,7 @@ async function dismissWelcomeModalIfVisible(page: any) { test.describe('Onboarding', () => { test('should create sample collection on first launch', async ({ launchElectronApp }) => { const app = await launchElectronApp({ initUserDataPath, dotEnv: env }); - const page = await app.firstWindow(); - - // Wait for app to load and dismiss welcome modal - await page.locator('[data-app-state="loaded"]').waitFor(); + const page = await waitForReadyPage(app); await dismissWelcomeModalIfVisible(page); // Verify sample collection appears in sidebar @@ -49,10 +47,7 @@ test.describe('Onboarding', () => { // Use a fresh app instance to avoid contamination from previous tests const userDataPath = await createTmpDir('duplicate-collections'); const app = await launchElectronApp({ userDataPath, initUserDataPath, dotEnv: env }); - const page = await app.firstWindow(); - - // Wait for app to load and dismiss welcome modal - await page.locator('[data-app-state="loaded"]').waitFor(); + const page = await waitForReadyPage(app); await dismissWelcomeModalIfVisible(page); // First launch - verify sample collection is created @@ -73,7 +68,7 @@ test.describe('Onboarding', () => { // Restart app - should not create sample collection again const newApp = await launchElectronApp({ userDataPath, dotEnv: env }); - const newPage = await newApp.firstWindow(); + const newPage = await waitForReadyPage(newApp); // Verify only one sample collection exists const sampleCollections = newPage.locator('#sidebar-collection-name').getByText('Sample API Collection'); @@ -95,10 +90,7 @@ test.describe('Onboarding', () => { test('should not recreate sample collection after user deletes it', async ({ launchElectronApp, reuseOrLaunchElectronApp, createTmpDir }) => { const userDataPath = await createTmpDir('first-launch'); const app = await launchElectronApp({ userDataPath, initUserDataPath, dotEnv: env }); - const page = await app.firstWindow(); - - // Wait for app to load and dismiss welcome modal - await page.locator('[data-app-state="loaded"]').waitFor(); + const page = await waitForReadyPage(app); await dismissWelcomeModalIfVisible(page); // First launch - sample collection should be created @@ -134,10 +126,7 @@ test.describe('Onboarding', () => { // Restart app - sample collection should NOT be recreated const newApp = await reuseOrLaunchElectronApp({ userDataPath, dotEnv: env }); - const newPage = await newApp.firstWindow(); - - // Wait for the app to be loaded / onboarding to be completed - await newPage.locator('[data-app-state="loaded"]').waitFor(); + const newPage = await waitForReadyPage(newApp); // Sample collection should not appear since it's no longer first launch const sampleCollections = newPage.locator('#sidebar-collection-name').getByText('Sample API Collection'); diff --git a/tests/onboarding/welcome-modal.spec.ts b/tests/onboarding/welcome-modal.spec.ts index 111a6e24c..5905d3233 100644 --- a/tests/onboarding/welcome-modal.spec.ts +++ b/tests/onboarding/welcome-modal.spec.ts @@ -1,6 +1,7 @@ import path from 'path'; import { ElectronApplication } from '@playwright/test'; import { test, expect, closeElectronApp } from '../../playwright'; +import { waitForReadyPage } from '../utils/page'; const initUserDataPath = path.join(__dirname, 'init-user-data-fresh'); @@ -10,10 +11,7 @@ test.describe('Welcome Modal', () => { try { app = await launchElectronApp({ initUserDataPath }); - const page = await app.firstWindow(); - - // Wait for the app to fully initialize before interacting - await page.locator('[data-app-state="loaded"]').waitFor(); + const page = await waitForReadyPage(app); // Welcome modal should be visible for new users const welcomeModal = page.getByTestId('welcome-modal'); @@ -43,8 +41,7 @@ test.describe('Welcome Modal', () => { try { // Launch app for a new user - welcome modal should appear app = await launchElectronApp({ userDataPath, initUserDataPath }); - let page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor(); + let page = await waitForReadyPage(app); // Welcome modal should be visible for new users const welcomeModal = page.getByTestId('welcome-modal'); @@ -60,8 +57,7 @@ test.describe('Welcome Modal', () => { // Restart the app with the same userDataPath app = await launchElectronApp({ userDataPath }); - page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor(); + page = await waitForReadyPage(app); // Welcome modal should NOT appear after restart (hasSeenWelcomeModal persisted) await expect(page.getByTestId('welcome-modal')).not.toBeVisible(); @@ -77,10 +73,7 @@ test.describe('Welcome Modal', () => { try { app = await launchElectronApp({ initUserDataPath }); - const page = await app.firstWindow(); - - // Wait for the app to fully initialize before interacting - await page.locator('[data-app-state="loaded"]').waitFor(); + const page = await waitForReadyPage(app); const welcomeModal = page.getByTestId('welcome-modal'); @@ -110,10 +103,7 @@ test.describe('Welcome Modal', () => { try { app = await launchElectronApp({ initUserDataPath }); - const page = await app.firstWindow(); - - // Wait for the app to fully initialize before interacting - await page.locator('[data-app-state="loaded"]').waitFor(); + const page = await waitForReadyPage(app); const welcomeModal = page.getByTestId('welcome-modal'); diff --git a/tests/request/response-pane-update-when-focused.spec.ts b/tests/request/response-pane-update-when-focused.spec.ts index d71b661f7..9da1a0cb7 100644 --- a/tests/request/response-pane-update-when-focused.spec.ts +++ b/tests/request/response-pane-update-when-focused.spec.ts @@ -18,6 +18,7 @@ test.describe.serial('Response pane updates when focused and request is re-sent' const requestName = 'Echo Request'; test.beforeAll(async ({ page, createTmpDir }) => { + await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 20000 }); const collectionPath = await createTmpDir('response-pane-collection'); await createCollection(page, collectionName, collectionPath); await createRequest(page, requestName, collectionName, { url: echoUrl, method: 'POST' }); diff --git a/tests/utils/page/actions.ts b/tests/utils/page/actions.ts index 008f693dd..d962c06a5 100644 --- a/tests/utils/page/actions.ts +++ b/tests/utils/page/actions.ts @@ -12,7 +12,7 @@ type WaitForAppReadyOptions = { * Wait for the Electron app to have a ready, loaded window. * Handles cases where the first window is slow to appear. */ -const waitForAppReady = async ( +const waitForReadyPage = async ( app: ElectronApplication, options: WaitForAppReadyOptions = {} ) => { @@ -59,8 +59,8 @@ const closeAllCollections = async (page) => { const hasDiscardButton = await page.getByRole('button', { name: 'Discard All and Remove' }).isVisible().catch(() => false); if (hasDiscardButton) { - // Drafts modal - click "Discard All and Remove" - await page.getByRole('button', { name: 'Discard All and Remove' }).click(); + // Drafts modal - click "Discard All and Remove" (force to avoid element stability issues) + await page.getByRole('button', { name: 'Discard All and Remove' }).click({ force: true }); } else { // Regular modal - click the submit button await page.locator('.bruno-modal-footer .submit').click(); @@ -1313,7 +1313,7 @@ const openExampleFromSidebar = async (page: Page, requestName: string, exampleNa }; export { - waitForAppReady, + waitForReadyPage, closeAllCollections, openCollection, createCollection, diff --git a/tests/workspace/collection-reorder-persistence.spec.ts b/tests/workspace/collection-reorder-persistence.spec.ts index 00ff49003..bbecd0a9c 100644 --- a/tests/workspace/collection-reorder-persistence.spec.ts +++ b/tests/workspace/collection-reorder-persistence.spec.ts @@ -2,7 +2,7 @@ import path from 'path'; import fs from 'fs'; import yaml from 'js-yaml'; import { test, expect } from '../../playwright'; -import { createCollection, waitForAppReady } from '../utils/page'; +import { createCollection, waitForReadyPage } from '../utils/page'; type WorkspaceConfig = { collections?: { name: string }[] }; @@ -13,7 +13,7 @@ test.describe('Collection reorder persistence', () => { const colBPath = await createTmpDir('col-b'); const app = await launchElectronApp({ userDataPath }); - const page = await waitForAppReady(app); + const page = await waitForReadyPage(app); await test.step('Create two collections', async () => { await createCollection(page, 'ColA', colAPath); @@ -44,7 +44,7 @@ test.describe('Collection reorder persistence', () => { await test.step('Restart app and verify order persisted', async () => { const app2 = await launchElectronApp({ userDataPath }); - const page2 = await waitForAppReady(app2); + const page2 = await waitForReadyPage(app2); const rows2 = page2.getByTestId('sidebar-collection-row'); await expect(rows2.nth(0)).toContainText('ColB'); @@ -61,7 +61,7 @@ test.describe('Collection reorder persistence', () => { const colBPath = await createTmpDir('col-b'); const app = await launchElectronApp({ userDataPath }); - const page = await waitForAppReady(app); + const page = await waitForReadyPage(app); await test.step('Create two collections', async () => { await createCollection(page, 'ColA', colAPath); diff --git a/tests/workspace/default-workspace/recovery-and-backup.spec.ts b/tests/workspace/default-workspace/recovery-and-backup.spec.ts index 8a20966df..d32fb22c4 100644 --- a/tests/workspace/default-workspace/recovery-and-backup.spec.ts +++ b/tests/workspace/default-workspace/recovery-and-backup.spec.ts @@ -1,6 +1,7 @@ import path from 'path'; import fs from 'fs'; import { test, expect, closeElectronApp } from '../../../playwright'; +import { waitForReadyPage } from '../../utils/page'; test.describe('Default Workspace Recovery and Backup', () => { test.describe('Global Environments Backup', () => { @@ -46,8 +47,7 @@ test.describe('Default Workspace Recovery and Backup', () => { // Launch app - should trigger migration and create backup const app = await launchElectronApp({ userDataPath }); - const page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app); // Verify backup file was created const backupPath = path.join(userDataPath, 'global-environments-backup.json'); @@ -93,8 +93,8 @@ test.describe('Default Workspace Recovery and Backup', () => { // First launch const app1 = await launchElectronApp({ userDataPath }); - const page1 = await app1.firstWindow(); - await page1.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app1); + await closeElectronApp(app1); // Verify backup exists @@ -104,8 +104,7 @@ test.describe('Default Workspace Recovery and Backup', () => { // Second launch - backup should still exist const app2 = await launchElectronApp({ userDataPath }); - const page2 = await app2.firstWindow(); - await page2.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app2); // Backup should not be modified on second launch expect(fs.existsSync(backupPath)).toBe(true); @@ -136,8 +135,8 @@ test.describe('Default Workspace Recovery and Backup', () => { // Launch app - triggers migration const app = await launchElectronApp({ userDataPath }); - const page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app); + await closeElectronApp(app); // Verify lastOpenedCollections is still in preferences @@ -177,8 +176,7 @@ docs: '' // Launch app - should discover and use existing workspace const app = await launchElectronApp({ userDataPath }); - const page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + const page = await waitForReadyPage(app); // UI always shows "My Workspace" await expect(page.getByTestId('workspace-name')).toHaveText('My Workspace'); @@ -225,8 +223,7 @@ docs: '' // Launch app - should use workspace-2 (latest/highest number) const app = await launchElectronApp({ userDataPath }); - const page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + const page = await waitForReadyPage(app); await expect(page.getByTestId('workspace-name')).toHaveText('My Workspace'); @@ -288,8 +285,7 @@ docs: '' // Launch app - should skip workspace-2, use workspace-1 const app = await launchElectronApp({ userDataPath }); - const page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + const page = await waitForReadyPage(app); await expect(page.getByTestId('workspace-name')).toHaveText('My Workspace'); @@ -345,8 +341,7 @@ docs: '' // Launch app - should recover collections and create new workspace const app = await launchElectronApp({ userDataPath }); - const page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app); // New workspace should be created const newWorkspace = path.join(userDataPath, 'default-workspace-1'); @@ -416,8 +411,7 @@ docs: '' // Launch app const app = await launchElectronApp({ userDataPath }); - const page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app); // New workspace should have recovered environments const newWorkspace = path.join(userDataPath, 'default-workspace-1'); @@ -456,8 +450,7 @@ docs: '' // Launch app const app = await launchElectronApp({ userDataPath }); - const page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app); // New workspace should have the collection from lastOpenedCollections const newWorkspace = path.join(userDataPath, 'default-workspace-1'); @@ -510,8 +503,7 @@ docs: '' // Launch app - should find and use the existing valid workspace const app = await launchElectronApp({ userDataPath }); - const page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + const page = await waitForReadyPage(app); await expect(page.getByTestId('workspace-name')).toHaveText('My Workspace'); @@ -591,8 +583,7 @@ docs: '' // Launch app - should use workspace-1 (latest valid) const app = await launchElectronApp({ userDataPath }); - const page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + const page = await waitForReadyPage(app); await expect(page.getByTestId('workspace-name')).toHaveText('My Workspace'); @@ -620,8 +611,7 @@ docs: '' // First launch - creates workspace const app1 = await launchElectronApp({ userDataPath }); - const page1 = await app1.firstWindow(); - await page1.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app1); // Verify workspace was created const workspacePath = path.join(userDataPath, 'default-workspace'); @@ -666,8 +656,7 @@ variables: // Second launch - should recover const app2 = await launchElectronApp({ userDataPath }); - const page2 = await app2.firstWindow(); - await page2.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app2); // New workspace should exist const newWorkspace = path.join(userDataPath, 'default-workspace-1'); @@ -684,8 +673,7 @@ variables: // First launch - creates workspace const app1 = await launchElectronApp({ userDataPath }); - const page1 = await app1.firstWindow(); - await page1.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app1); const workspacePath = path.join(userDataPath, 'default-workspace'); expect(fs.existsSync(workspacePath)).toBe(true); @@ -698,8 +686,7 @@ variables: // Second launch - should create new workspace const app2 = await launchElectronApp({ userDataPath }); - const page2 = await app2.firstWindow(); - await page2.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app2); // New workspace should be created at default-workspace (since it was deleted) expect(fs.existsSync(workspacePath)).toBe(true); @@ -727,8 +714,8 @@ variables: // First launch const app1 = await launchElectronApp({ userDataPath }); - const page1 = await app1.firstWindow(); - await page1.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app1); + await closeElectronApp(app1); // Verify workspace-0 created @@ -750,8 +737,8 @@ variables: [] // Second launch - recovery to workspace-1 const app2 = await launchElectronApp({ userDataPath }); - const page2 = await app2.firstWindow(); - await page2.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app2); + await closeElectronApp(app2); // Verify workspace-1 created with recovered data @@ -767,8 +754,7 @@ variables: [] // Third launch - recovery to workspace-2 const app3 = await launchElectronApp({ userDataPath }); - const page3 = await app3.firstWindow(); - await page3.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app3); // Verify workspace-2 created with all data preserved const ws2 = path.join(userDataPath, 'default-workspace-2'); @@ -798,8 +784,7 @@ variables: [] ); const app = await launchElectronApp({ userDataPath }); - const page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app); // Should not crash, new workspace created const newWorkspace = path.join(userDataPath, 'default-workspace-1'); @@ -822,8 +807,7 @@ variables: [] ); const app = await launchElectronApp({ userDataPath }); - const page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app); // Should not crash expect(fs.existsSync(path.join(userDataPath, 'default-workspace-1'))).toBe(true); @@ -859,8 +843,7 @@ variables: [] ); const app = await launchElectronApp({ userDataPath }); - const page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app); // New workspace should have collection only ONCE (no duplicates) const newWorkspace = path.join(userDataPath, 'default-workspace-1'); @@ -918,8 +901,7 @@ variables: ); const app = await launchElectronApp({ userDataPath }); - const page = await app.firstWindow(); - await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 }); + await waitForReadyPage(app); // Check new workspace has the recovered environment (not overwritten by global) const newWorkspace = path.join(userDataPath, 'default-workspace-1');