diff --git a/e2e/helpers/plugin-test.helpers.ts b/e2e/helpers/plugin-test.helpers.ts index 6d59acfd1c..a44fe6cf8d 100644 --- a/e2e/helpers/plugin-test.helpers.ts +++ b/e2e/helpers/plugin-test.helpers.ts @@ -81,24 +81,19 @@ export const waitForPluginAssets = async ( */ export const waitForPluginManagementInit = async ( page: Page, - timeout: number = 15000, // Reduced from 20s to 15s // Reduced from 30s to 20s + timeout: number = 30000, ): Promise => { try { - // First ensure we're on the settings page and plugin section is expanded + // Navigate to settings page if not already there const currentUrl = page.url(); if (!currentUrl.includes('#/config')) { - await page.click('text=Settings'); - await page.waitForURL(/.*#\/config.*/, { timeout: 8000 }); // Reduced from 10s to 8s + await page.goto('/#/config'); } // Wait for settings page to load - await page.waitForSelector('.page-settings', { state: 'visible', timeout: 8000 }); // Reduced from 10s to 8s + await page.waitForSelector('.page-settings', { state: 'visible', timeout: 10000 }); - // Wait a bit for the page to stabilize - await page.waitForTimeout(500); - - // Navigate to the Plugins tab (4th tab, index 3) - // The plugin section is now inside the Plugins tab, not directly on the page + // Navigate to the Plugins tab await page.evaluate(() => { const pluginsTab = Array.from( document.querySelectorAll('mat-tab-header .mat-mdc-tab'), @@ -112,10 +107,8 @@ export const waitForPluginManagementInit = async ( } }); - // Wait for tab content to load - await page.waitForTimeout(500); - - // Now expand plugin section if collapsed and scroll it into view + // Expand plugin section if collapsed and scroll it into view + await page.waitForSelector('.plugin-section', { state: 'visible', timeout: 5000 }); await page.evaluate(() => { const pluginSection = document.querySelector('.plugin-section'); if (pluginSection) { @@ -131,10 +124,21 @@ export const waitForPluginManagementInit = async ( } }); - // Wait for expansion animation and scroll to complete - await page.waitForTimeout(300); + // Wait for plugin management component with plugin cards + await page.waitForSelector('plugin-management', { + state: 'attached', + timeout: 10000, + }); - // Scroll plugin-management into view explicitly + const result = await page.waitForFunction( + () => { + const cards = document.querySelectorAll('plugin-management mat-card'); + return cards.length > 0; + }, + { timeout: 10000 }, + ); + + // Scroll plugin-management into view await page.evaluate(() => { const pluginMgmt = document.querySelector('plugin-management'); if (pluginMgmt) { @@ -142,24 +146,6 @@ export const waitForPluginManagementInit = async ( } }); - // Wait for plugin management component to be attached (not necessarily visible) - await page.waitForSelector('plugin-management', { - state: 'attached', - timeout: Math.max(5000, timeout - 20000), - }); - - // Additional check for plugin cards to be loaded - const result = await page.waitForFunction( - () => { - const pluginMgmt = document.querySelector('plugin-management'); - if (!pluginMgmt) return false; - - const cards = document.querySelectorAll('plugin-management mat-card'); - return cards.length > 0; - }, - { timeout: Math.max(5000, timeout - 25000) }, - ); - return !!result; } catch (error) { const message = error instanceof Error ? error.message : String(error); diff --git a/e2e/pages/planner.page.ts b/e2e/pages/planner.page.ts index fbd68a0f56..bded94f8e0 100644 --- a/e2e/pages/planner.page.ts +++ b/e2e/pages/planner.page.ts @@ -25,18 +25,17 @@ export class PlannerPage extends BasePage { // Try to click the planner nav item first, fallback to direct navigation try { await this.page.locator('magic-side-nav a[href="#/planner"]').click(); - await this.page.waitForLoadState('networkidle'); } catch (error) { // Fallback to direct navigation await this.page.goto('/#/tag/TODAY/planner'); - await this.page.waitForLoadState('networkidle'); } + await this.page.waitForURL(/\/(planner|tasks)/); await this.routerWrapper.waitFor({ state: 'visible' }); } async navigateToPlannerForProject(projectId: string): Promise { await this.page.goto(`/#/project/${projectId}/planner`); - await this.page.waitForLoadState('networkidle'); + await this.page.waitForURL(/\/(planner|tasks)/); await this.routerWrapper.waitFor({ state: 'visible' }); } diff --git a/e2e/pages/tag.page.ts b/e2e/pages/tag.page.ts index 8a4def9069..92ecbee1d6 100644 --- a/e2e/pages/tag.page.ts +++ b/e2e/pages/tag.page.ts @@ -120,7 +120,12 @@ export class TagPage extends BasePage { await tagNameInput.waitFor({ state: 'hidden', timeout: 3000 }); } - // Wait for all overlays to close to ensure clean state for next operation + // Wait for all menus and overlays to fully close before returning + await this.page + .locator('.mat-mdc-menu-panel') + .first() + .waitFor({ state: 'detached', timeout: 3000 }) + .catch(() => {}); await this.ensureOverlaysClosed(); // Wait for the tag to actually appear on the task diff --git a/e2e/tests/migration/pre-migration-dialog.spec.ts b/e2e/tests/migration/pre-migration-dialog.spec.ts index 196dd45d89..bc464f65a8 100644 --- a/e2e/tests/migration/pre-migration-dialog.spec.ts +++ b/e2e/tests/migration/pre-migration-dialog.spec.ts @@ -19,27 +19,28 @@ test.describe('@migration Pre-migration Dialog', () => { page, workViewPage, }) => { + test.setTimeout(45000); + // Wait for app to be ready - this implicitly verifies migration completed await workViewPage.waitForTaskList(); // Create a task to verify app is fully functional await workViewPage.addTask('Post-migration test task'); - await page.waitForSelector('task', { state: 'visible' }); - // Verify the task was created + // Verify the task was created (addTask already waits for visibility) await expect(page.locator('task-title').first()).toContainText( 'Post-migration test task', ); // Create another task to ensure state management works await workViewPage.addTask('Second post-migration task'); - await expect(page.locator('task')).toHaveCount(2); + await expect(page.locator('task')).toHaveCount(2, { timeout: 10000 }); }); test('dialog-confirm should show both buttons by default', async ({ page }) => { // Navigate to settings and trigger a dialog that shows both buttons await page.goto('/#/settings'); - await page.waitForLoadState('networkidle'); + await page.waitForLoadState('domcontentloaded'); // Find and click a section that might trigger a confirm dialog // We'll use the "Clear Storage" option if available diff --git a/e2e/tests/planner/planner-time-estimates.spec.ts b/e2e/tests/planner/planner-time-estimates.spec.ts index f1be90b845..980cb20e13 100644 --- a/e2e/tests/planner/planner-time-estimates.spec.ts +++ b/e2e/tests/planner/planner-time-estimates.spec.ts @@ -15,9 +15,8 @@ test.describe('Planner Time Estimates', () => { }) => { // Add task with time estimate using short syntax await workViewPage.addTask('Important task /2h/'); - await page.waitForLoadState('networkidle'); - // Verify task was created + // Verify task was created (auto-retries until count matches) await expect(page.locator('task')).toHaveCount(1); // Task should contain the time estimate in title @@ -32,9 +31,8 @@ test.describe('Planner Time Estimates', () => { await workViewPage.addTask('First task /1h/'); await workViewPage.addTask('Second task /30m/'); await workViewPage.addTask('Third task /2h/'); - await page.waitForLoadState('networkidle'); - // Verify all tasks created + // Verify all tasks created (auto-retries until count matches) await expect(page.locator('task')).toHaveCount(3); // Navigate to planner @@ -51,7 +49,6 @@ test.describe('Planner Time Estimates', () => { }) => { // Add task with time estimate syntax await workViewPage.addTask('Development work /4h/'); - await page.waitForLoadState('networkidle'); // Navigate to planner await plannerPage.navigateToPlanner(); @@ -69,7 +66,6 @@ test.describe('Planner Time Estimates', () => { // Add tasks with various time formats await workViewPage.addTask('Quick fix /15m/'); await workViewPage.addTask('Feature development /3h30m/'); - await page.waitForLoadState('networkidle'); // Navigate to planner await plannerPage.navigateToPlanner(); diff --git a/e2e/tests/plugins/enable-plugin-test.spec.ts b/e2e/tests/plugins/enable-plugin-test.spec.ts index 0c22fc9d71..736a4f18fd 100644 --- a/e2e/tests/plugins/enable-plugin-test.spec.ts +++ b/e2e/tests/plugins/enable-plugin-test.spec.ts @@ -1,22 +1,17 @@ import { expect, test } from '../../fixtures/test.fixture'; -import { cssSelectors } from '../../constants/selectors'; import { getCITimeoutMultiplier, waitForPluginAssets, waitForPluginManagementInit, } from '../../helpers/plugin-test.helpers'; -const { SETTINGS_BTN } = cssSelectors; - test.describe('Enable Plugin Test', () => { test('navigate to plugin settings and enable API Test Plugin', async ({ page, workViewPage, }) => { const timeoutMultiplier = getCITimeoutMultiplier(); - test.setTimeout(30000 * timeoutMultiplier); // Reduced from 60s to 30s base - - // console.log('[Plugin Test] Starting enable plugin test...'); + test.setTimeout(30000 * timeoutMultiplier); // First, ensure plugin assets are available const assetsAvailable = await waitForPluginAssets(page); @@ -30,77 +25,19 @@ test.describe('Enable Plugin Test', () => { await workViewPage.waitForTaskList(); + // Navigate to settings and initialize plugin management + // This navigates to settings, selects plugin tab, and expands plugin section await waitForPluginManagementInit(page); - // Navigate to plugin settings - await page.click(SETTINGS_BTN); - await page - .locator('.page-settings') - .first() - .waitFor({ state: 'visible', timeout: 10000 }); - - await page.evaluate(() => { - const configPage = document.querySelector('.page-settings'); - if (!configPage) { - console.error('Not on config page'); - return; - } - - const pluginSection = document.querySelector('.plugin-section'); - if (pluginSection) { - pluginSection.scrollIntoView({ behavior: 'smooth', block: 'center' }); - } else { - console.error('Plugin section not found'); - return; - } - - const collapsible = document.querySelector('.plugin-section collapsible'); - if (collapsible) { - const isExpanded = collapsible.classList.contains('isExpanded'); - if (!isExpanded) { - const header = collapsible.querySelector('.collapsible-header'); - if (header) { - (header as HTMLElement).click(); - // console.log('Clicked to expand plugin collapsible'); - } else { - console.error('Could not find collapsible header'); - } - } else { - // console.log('Plugin collapsible already expanded'); - } - } else { - console.error('Plugin collapsible not found'); - } - }); - await expect(page.locator('plugin-management')).toBeVisible({ timeout: 10000 }); + // Wait for plugin cards to be loaded await page .locator('plugin-management mat-card') .first() .waitFor({ state: 'attached', timeout: 10000 }); - // Check if plugin-management has any content - await page.evaluate(() => { - const pluginMgmt = document.querySelector('plugin-management'); - const matCards = pluginMgmt ? pluginMgmt.querySelectorAll('mat-card') : []; - - // Filter out warning card - const pluginCards = Array.from(matCards).filter((card) => { - return card.querySelector('mat-slide-toggle') !== null; - }); - - return { - pluginMgmtExists: !!pluginMgmt, - totalCardCount: matCards.length, - pluginCardCount: pluginCards.length, - pluginCardTexts: pluginCards.map( - (card) => card.querySelector('mat-card-title')?.textContent?.trim() || '', - ), - }; - }); - - // Try to find and enable the API Test Plugin (which exists by default) + // Try to find and enable the API Test Plugin const enableResult = await page.evaluate(() => { const pluginCards = document.querySelectorAll('plugin-management mat-card'); let foundApiTestPlugin = false; @@ -128,7 +65,6 @@ test.describe('Enable Plugin Test', () => { }; }); - // console.log('Plugin enablement result:', enableResult); expect(enableResult.foundApiTestPlugin).toBe(true); // Wait for toggle state to change to enabled @@ -150,16 +86,5 @@ test.describe('Enable Plugin Test', () => { { timeout: 10000 }, ); } - - // Now check if plugin menu has buttons - await page.evaluate(() => { - const sideNav = document.querySelector('magic-side-nav'); - const buttons = sideNav ? sideNav.querySelectorAll('nav-item button') : []; - return { - sideNavExists: !!sideNav, - buttonCount: buttons.length, - buttonTexts: Array.from(buttons).map((btn) => btn.textContent?.trim() || ''), - }; - }); }); }); diff --git a/e2e/tests/plugins/plugin-enable-verify.spec.ts b/e2e/tests/plugins/plugin-enable-verify.spec.ts index c730eacdcc..a2c1c29437 100644 --- a/e2e/tests/plugins/plugin-enable-verify.spec.ts +++ b/e2e/tests/plugins/plugin-enable-verify.spec.ts @@ -1,20 +1,16 @@ import { expect, test } from '../../fixtures/test.fixture'; -import { cssSelectors } from '../../constants/selectors'; import { enablePluginWithVerification, getCITimeoutMultiplier, - logPluginState, waitForPluginAssets, waitForPluginInMenu, waitForPluginManagementInit, } from '../../helpers/plugin-test.helpers'; -const { SETTINGS_BTN } = cssSelectors; - test.describe.serial('Plugin Enable Verify', () => { test('enable API Test Plugin and verify menu entry', async ({ page, workViewPage }) => { const timeoutMultiplier = getCITimeoutMultiplier(); - test.setTimeout(30000 * timeoutMultiplier); // Reduced from 60s to 30s base + test.setTimeout(30000 * timeoutMultiplier); // First, ensure plugin assets are available const assetsAvailable = await waitForPluginAssets(page); @@ -28,6 +24,7 @@ test.describe.serial('Plugin Enable Verify', () => { await workViewPage.waitForTaskList(); + // Navigate to settings and initialize plugin management const initSuccess = await waitForPluginManagementInit(page); if (!initSuccess) { throw new Error( @@ -35,45 +32,11 @@ test.describe.serial('Plugin Enable Verify', () => { ); } - // Navigate to plugin settings - await page.click(SETTINGS_BTN); - await page.waitForSelector('.page-settings', { - state: 'visible', - timeout: 10000 * timeoutMultiplier, - }); - - // Expand plugin section - await page.evaluate(() => { - const pluginSection = document.querySelector('.plugin-section'); - if (pluginSection) { - pluginSection.scrollIntoView({ behavior: 'smooth', block: 'center' }); - } - - const collapsible = document.querySelector('.plugin-section collapsible'); - if (collapsible && !collapsible.classList.contains('isExpanded')) { - const header = collapsible.querySelector('.collapsible-header'); - if (header) { - (header as HTMLElement).click(); - } - } - }); - - // Wait for plugin management to be visible - await page.waitForSelector('plugin-management', { - state: 'visible', - timeout: 10000 * timeoutMultiplier, - }); - - // Log current plugin state for debugging - if (process.env.CI) { - await logPluginState(page); - } - - // Enable API Test Plugin with verification + // Plugin management is now visible - enable API Test Plugin const pluginEnabled = await enablePluginWithVerification( page, 'API Test Plugin', - 10000 * timeoutMultiplier, // Reduced from 15s to 10s + 10000 * timeoutMultiplier, ); expect(pluginEnabled).toBe(true); @@ -82,14 +45,13 @@ test.describe.serial('Plugin Enable Verify', () => { const pluginInMenu = await waitForPluginInMenu( page, 'API Test Plugin', - 15000 * timeoutMultiplier, // Reduced from 20s to 15s + 15000 * timeoutMultiplier, ); expect(pluginInMenu).toBe(true); // Additional verification - check menu structure in magic-side-nav const menuResult = await page.evaluate(() => { - // Look for plugin items in magic-side-nav structure const sideNav = document.querySelector('magic-side-nav'); const navButtons = sideNav ? Array.from(sideNav.querySelectorAll('nav-item button')) @@ -104,7 +66,6 @@ test.describe.serial('Plugin Enable Verify', () => { expect(menuResult.hasSideNav).toBe(true); expect(menuResult.buttonCount).toBeGreaterThan(0); - // Check if any button text contains "API Test Plugin" (handle whitespace) const hasApiTestPlugin = menuResult.buttonTexts.some((text: string) => text.includes('API Test Plugin'), ); diff --git a/e2e/tests/plugins/plugin-iframe.spec.ts b/e2e/tests/plugins/plugin-iframe.spec.ts index ec29b81c31..06a8e94735 100644 --- a/e2e/tests/plugins/plugin-iframe.spec.ts +++ b/e2e/tests/plugins/plugin-iframe.spec.ts @@ -3,29 +3,23 @@ import { cssSelectors } from '../../constants/selectors'; import { enablePluginWithVerification, getCITimeoutMultiplier, - logPluginState, waitForPluginAssets, waitForPluginInMenu, waitForPluginManagementInit, } from '../../helpers/plugin-test.helpers'; -const { SIDENAV, SETTINGS_BTN } = cssSelectors; +const { SIDENAV } = cssSelectors; // Plugin-related selectors const PLUGIN_NAV_ITEMS = `${SIDENAV} nav-item button`; const PLUGIN_IFRAME = 'plugin-index iframe'; -const PLUGIN_MANAGEMENT = 'plugin-management'; -const PLUGIN_SECTION = '.plugin-section'; -const SETTINGS_PAGE = '.page-settings'; -const COLLAPSIBLE_EXPANDED = '.plugin-section collapsible.isExpanded'; test.describe.serial('Plugin Iframe', () => { test.beforeEach(async ({ page, workViewPage }) => { - // Increase timeout for CI environment const timeoutMultiplier = getCITimeoutMultiplier(); - test.setTimeout(30000 * timeoutMultiplier); // Reduced from 60s to 30s base + test.setTimeout(30000 * timeoutMultiplier); - // First, ensure plugin assets are available + // Ensure plugin assets are available const assetsAvailable = await waitForPluginAssets(page); if (!assetsAvailable) { if (process.env.CI) { @@ -37,6 +31,7 @@ test.describe.serial('Plugin Iframe', () => { await workViewPage.waitForTaskList(); + // Navigate to settings and initialize plugin management const initSuccess = await waitForPluginManagementInit(page); if (!initSuccess) { throw new Error( @@ -44,46 +39,11 @@ test.describe.serial('Plugin Iframe', () => { ); } - // Navigate to settings - const settingsBtn = page.locator(SETTINGS_BTN); - await settingsBtn.waitFor({ state: 'visible' }); - await settingsBtn.click(); - - // Wait for settings page to be visible - await page.waitForSelector(SETTINGS_PAGE, { state: 'visible' }); - - // Scroll to plugin section - const pluginSection = page.locator(PLUGIN_SECTION); - await pluginSection.scrollIntoViewIfNeeded(); - - // Expand collapsible if needed - const collapsible = page.locator('.plugin-section collapsible'); - const collapsibleHeader = collapsible.locator('.collapsible-header'); - - // Check if already expanded, if not click to expand - const expandedCollapsible = page.locator(COLLAPSIBLE_EXPANDED); - if ((await expandedCollapsible.count()) === 0) { - await collapsibleHeader.click(); - // Wait for the expanded class to appear - await page.waitForSelector(COLLAPSIBLE_EXPANDED, { state: 'visible' }); - } - - // Wait for plugin management to be visible - await page.waitForSelector(PLUGIN_MANAGEMENT, { - state: 'visible', - timeout: 10000 * timeoutMultiplier, - }); - - // Log current plugin state for debugging - if (process.env.CI) { - await logPluginState(page); - } - - // Enable API Test Plugin with verification + // Enable API Test Plugin const pluginEnabled = await enablePluginWithVerification( page, 'API Test Plugin', - 10000 * timeoutMultiplier, // Reduced from 15s to 10s + 10000 * timeoutMultiplier, ); if (!pluginEnabled) { @@ -94,12 +54,10 @@ test.describe.serial('Plugin Iframe', () => { const pluginInMenu = await waitForPluginInMenu( page, 'API Test Plugin', - 15000 * timeoutMultiplier, // Reduced from 20s to 15s + 15000 * timeoutMultiplier, ); if (!pluginInMenu) { - // Log state for debugging - await logPluginState(page); throw new Error('API Test Plugin not found in menu after enabling'); } @@ -111,7 +69,6 @@ test.describe.serial('Plugin Iframe', () => { ); if (await cancelBtn.isVisible().catch(() => false)) { await cancelBtn.click(); - // Wait for dialog to disappear await tourDialog.waitFor({ state: 'hidden' }); } } @@ -149,13 +106,10 @@ test.describe.serial('Plugin Iframe', () => { await expect(iframe).toBeVisible(); // Try to verify iframe content if possible - // Note: Cross-origin iframes may not allow content access const frameLocator = page.frameLocator(PLUGIN_IFRAME); try { - // Wait for iframe body to be loaded await frameLocator.locator('body').waitFor({ state: 'visible' }); - // Check for expected content if accessible const h1 = frameLocator.locator('h1'); const hasH1 = (await h1.count()) > 0; if (hasH1) { @@ -164,7 +118,6 @@ test.describe.serial('Plugin Iframe', () => { } catch (error) { // If iframe content is not accessible due to cross-origin restrictions, // at least verify the iframe element itself is present - // console.log('Note: Iframe content verification skipped (possibly cross-origin)'); await expect(iframe).toBeVisible(); } }); diff --git a/e2e/tests/plugins/plugin-lifecycle.spec.ts b/e2e/tests/plugins/plugin-lifecycle.spec.ts index db76cd4cc4..ca833c2d25 100644 --- a/e2e/tests/plugins/plugin-lifecycle.spec.ts +++ b/e2e/tests/plugins/plugin-lifecycle.spec.ts @@ -9,7 +9,7 @@ import { getCITimeoutMultiplier, } from '../../helpers/plugin-test.helpers'; -const { SIDENAV, SETTINGS_BTN } = cssSelectors; +const { SIDENAV } = cssSelectors; // Plugin-related selectors const API_TEST_PLUGIN_NAV_ITEM = `${SIDENAV} nav-item button:has-text("API Test Plugin")`; @@ -18,7 +18,7 @@ const TEST_TIMEOUT_MS = 30000 * TIMEOUT_MULTIPLIER; test.describe('Plugin Lifecycle', () => { test.beforeEach(async ({ page, workViewPage }) => { - test.setTimeout(TEST_TIMEOUT_MS); // Reduced from 60s to 30s base + test.setTimeout(TEST_TIMEOUT_MS); // First, ensure plugin assets are available const assetsAvailable = await waitForPluginAssets(page); @@ -32,6 +32,7 @@ test.describe('Plugin Lifecycle', () => { await workViewPage.waitForTaskList(); + // Navigate to settings and initialize plugin management const initSuccess = await waitForPluginManagementInit(page); if (!initSuccess) { throw new Error( @@ -55,7 +56,7 @@ test.describe('Plugin Lifecycle', () => { }); test('verify plugin is initially loaded', async ({ page }) => { - test.setTimeout(TEST_TIMEOUT_MS); // Increase timeout + test.setTimeout(TEST_TIMEOUT_MS); // Wait for magic-side-nav to be ready await page.locator(SIDENAV).waitFor({ state: 'visible' }); @@ -65,7 +66,7 @@ test.describe('Plugin Lifecycle', () => { }); test('test plugin navigation', async ({ page }) => { - test.setTimeout(TEST_TIMEOUT_MS); // Increase timeout + test.setTimeout(TEST_TIMEOUT_MS); // Click on the plugin nav item to navigate to plugin const pluginNavItem = page.locator(API_TEST_PLUGIN_NAV_ITEM); @@ -89,110 +90,13 @@ test.describe('Plugin Lifecycle', () => { await page.goto('/#/tag/TODAY'); }); - test('disable plugin and verify cleanup', async ({ page, workViewPage }) => { - test.setTimeout(30000); // Increase timeout + test('disable plugin and verify cleanup', async ({ page }) => { + test.setTimeout(TEST_TIMEOUT_MS); - // Navigate to settings - await page.click(SETTINGS_BTN); - // Wait for settings page to be visible - use first() to avoid multiple matches - await page - .locator('.page-settings') - .first() - .waitFor({ state: 'visible', timeout: 10000 }); - - // Navigate to Plugins tab first - await page.evaluate(() => { - const pluginsTab = Array.from( - document.querySelectorAll('mat-tab-header .mat-mdc-tab'), - ).find((tab) => { - const icon = tab.querySelector('mat-icon'); - return icon?.textContent?.trim() === 'extension'; - }); - - if (pluginsTab) { - (pluginsTab as HTMLElement).click(); - } - }); - - await page.waitForTimeout(500); - - // Expand plugin section - await page.evaluate(() => { - const pluginSection = document.querySelector('.plugin-section'); - if (pluginSection) { - pluginSection.scrollIntoView({ behavior: 'instant', block: 'center' }); - } - - const collapsible = document.querySelector('.plugin-section collapsible'); - if (collapsible && !collapsible.classList.contains('isExpanded')) { - const header = collapsible.querySelector('.collapsible-header'); - if (header) { - (header as HTMLElement).click(); - } - } - }); - - // Scroll plugin-management into view - await page.evaluate(() => { - const pluginMgmt = document.querySelector('plugin-management'); - if (pluginMgmt) { - pluginMgmt.scrollIntoView({ behavior: 'instant', block: 'center' }); - } - }); - - // Wait for plugin management to be ready (attached, not necessarily visible) - await page - .locator('plugin-management') - .waitFor({ state: 'attached', timeout: 10000 }); - // Wait for plugin cards to be available - await page - .locator('plugin-management mat-card') - .first() - .waitFor({ state: 'attached', timeout: 10000 }); - - // Check current state of the plugin and enable if needed - const currentState = await page.evaluate((pluginName: string) => { - const cards = Array.from(document.querySelectorAll('plugin-management mat-card')); - const targetCard = cards.find((card) => { - const title = card.querySelector('mat-card-title')?.textContent || ''; - return title.includes(pluginName); - }); - - if (targetCard) { - const toggleButton = targetCard.querySelector( - 'mat-slide-toggle button[role="switch"]', - ) as HTMLButtonElement; - if (toggleButton) { - const isEnabled = toggleButton.getAttribute('aria-checked') === 'true'; - if (!isEnabled) { - toggleButton.click(); - return { found: true, wasEnabled: false, clicked: true }; - } - return { found: true, wasEnabled: true, clicked: false }; - } - } - return { found: false }; - }, 'API Test Plugin'); - - // If we just enabled it, wait for it to be enabled - if (currentState.clicked) { - await page.waitForFunction( - (name) => { - const cards = Array.from( - document.querySelectorAll('plugin-management mat-card'), - ); - const targetCard = cards.find((card) => { - const title = card.querySelector('mat-card-title')?.textContent || ''; - return title.includes(name); - }); - const toggle = targetCard?.querySelector( - 'mat-slide-toggle button[role="switch"]', - ) as HTMLButtonElement; - return toggle?.getAttribute('aria-checked') === 'true'; - }, - 'API Test Plugin', - { timeout: 5000 }, - ); + // Navigate to settings and set up plugin management view + const initSuccess = await waitForPluginManagementInit(page); + if (!initSuccess) { + throw new Error('Plugin management failed to re-initialize for disable test'); } const disabled = await disablePluginWithVerification( @@ -211,7 +115,6 @@ test.describe('Plugin Lifecycle', () => { const sideNavExists = (await page.locator(SIDENAV).count()) > 0; if (sideNavExists) { - // Check all plugin nav items to ensure API Test Plugin is not present const hasApiTestPlugin = await page.evaluate(() => { const menuItems = Array.from( document.querySelectorAll('magic-side-nav nav-item button'), @@ -221,7 +124,6 @@ test.describe('Plugin Lifecycle', () => { expect(hasApiTestPlugin).toBe(false); } else { - // Magic-side-nav doesn't exist at all, which is unexpected expect(sideNavExists).toBe(true); } }); diff --git a/e2e/tests/work-view/work-view-features.spec.ts b/e2e/tests/work-view/work-view-features.spec.ts index a14c82c946..cecf2b0d90 100644 --- a/e2e/tests/work-view/work-view-features.spec.ts +++ b/e2e/tests/work-view/work-view-features.spec.ts @@ -14,7 +14,7 @@ test.describe('Work View Features', () => { workViewPage, testPrefix, }) => { - test.setTimeout(30000); + test.setTimeout(45000); // Wait for work view to be ready await workViewPage.waitForTaskList(); @@ -116,11 +116,11 @@ test.describe('Work View Features', () => { // Navigate to settings await page.goto('/#/config'); - await expect(page.locator('.page-settings')).toBeVisible(); + await expect(page.locator('.page-settings')).toBeVisible({ timeout: 10000 }); // Navigate back await page.goto('/#/tag/TODAY'); - await expect(page.locator('task-list').first()).toBeVisible(); + await workViewPage.waitForTaskList(); // Verify task is still there await expect(page.locator(TASK_TITLE).first()).toContainText(/Persistent task/);