diff --git a/e2e/helpers/plugin-test.helpers.ts b/e2e/helpers/plugin-test.helpers.ts new file mode 100644 index 0000000000..94ab75d3d0 --- /dev/null +++ b/e2e/helpers/plugin-test.helpers.ts @@ -0,0 +1,277 @@ +import { Page } from '@playwright/test'; + +/** + * Helper functions for plugin testing with robust loading verification + */ + +/** + * Wait for plugin assets to be available via HTTP + */ +export const waitForPluginAssets = async ( + page: Page, + maxRetries: number = 10, + retryDelay: number = 1000, +): Promise => { + const baseUrl = page.url().split('#')[0]; + const testUrl = `${baseUrl}assets/bundled-plugins/api-test-plugin/manifest.json`; + + console.log(`[Plugin Test] Checking plugin assets availability at: ${testUrl}`); + + for (let i = 0; i < maxRetries; i++) { + try { + const response = await page.request.get(testUrl); + if (response.ok()) { + const manifest = await response.json(); + console.log(`[Plugin Test] Plugin manifest loaded successfully:`, manifest.id); + return true; + } else { + console.log( + `[Plugin Test] Attempt ${i + 1}/${maxRetries}: HTTP ${response.status()}`, + ); + } + } catch (error) { + console.log( + `[Plugin Test] Attempt ${i + 1}/${maxRetries}: Error - ${error.message}`, + ); + } + + if (i < maxRetries - 1) { + await page.waitForTimeout(retryDelay); + } + } + + console.error('[Plugin Test] Failed to load plugin assets after all retries'); + return false; +}; + +/** + * Wait for plugin system to be initialized + */ +export const waitForPluginSystemInit = async ( + page: Page, + timeout: number = 30000, +): Promise => { + console.log('[Plugin Test] Waiting for plugin system initialization...'); + + try { + // Check if plugin system is initialized by looking for plugin management in settings + const result = await page.waitForFunction( + () => { + // Check if we can access the Angular app + const appRoot = document.querySelector('app-root'); + if (!appRoot) { + console.log('[Plugin Test] App root not found'); + return false; + } + + // Check if plugin service exists in Angular's injector (if accessible) + // This is a more indirect check since we can't directly access Angular internals + const hasPluginElements = + document.querySelector('plugin-management') !== null || + document.querySelector('plugin-menu') !== null || + document.querySelector('[class*="plugin"]') !== null; + + if (hasPluginElements) { + console.log('[Plugin Test] Plugin elements detected'); + } + + return hasPluginElements; + }, + { timeout }, + ); + + console.log('[Plugin Test] Plugin system initialized'); + return !!result; + } catch (error) { + console.error('[Plugin Test] Plugin system initialization timeout:', error.message); + return false; + } +}; + +/** + * Enable a plugin with robust verification + */ +export const enablePluginWithVerification = async ( + page: Page, + pluginName: string, + timeout: number = 15000, +): Promise => { + console.log(`[Plugin Test] Attempting to enable plugin: ${pluginName}`); + + const startTime = Date.now(); + + // First, verify the plugin card exists + const pluginCardResult = 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); + }); + + if (targetCard) { + console.log(`[Plugin Test] Found plugin card for: ${name}`); + return true; + } + return false; + }, + pluginName, + { timeout: timeout / 2 }, + ) + .catch(() => null); + + if (!pluginCardResult) { + console.error(`[Plugin Test] Plugin card not found for: ${pluginName}`); + return false; + } + + // Enable the plugin + const enableResult = await page.evaluate((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); + }); + + if (!targetCard) { + return { success: false, error: 'Card not found' }; + } + + const toggle = targetCard.querySelector( + 'mat-slide-toggle button[role="switch"]', + ) as HTMLButtonElement; + + if (!toggle) { + return { success: false, error: 'Toggle not found' }; + } + + const wasEnabled = toggle.getAttribute('aria-checked') === 'true'; + if (!wasEnabled) { + toggle.click(); + console.log(`[Plugin Test] Clicked toggle to enable ${name}`); + } else { + console.log(`[Plugin Test] ${name} was already enabled`); + } + + return { success: true, wasEnabled }; + }, pluginName); + + if (!enableResult.success) { + console.error(`[Plugin Test] Failed to enable plugin: ${enableResult.error}`); + return false; + } + + // Wait for the toggle state to update + 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'; + }, + pluginName, + { timeout: timeout - (Date.now() - startTime) }, + ); + + console.log(`[Plugin Test] Plugin ${pluginName} enabled successfully`); + return true; +}; + +/** + * Wait for plugin to be fully loaded and visible in menu + */ +export const waitForPluginInMenu = async ( + page: Page, + pluginName: string, + timeout: number = 20000, +): Promise => { + console.log(`[Plugin Test] Waiting for ${pluginName} to appear in menu...`); + + try { + // Navigate to main view to see the menu + await page.goto('/#/tag/TODAY'); + + // Wait for plugin menu to exist + await page.waitForSelector('plugin-menu', { + state: 'attached', + timeout: timeout / 2, + }); + + // Wait for the specific plugin button in the menu + const result = await page.waitForFunction( + (name) => { + const pluginMenu = document.querySelector('plugin-menu'); + if (!pluginMenu) { + console.log('[Plugin Test] Plugin menu not found'); + return false; + } + + const buttons = Array.from(pluginMenu.querySelectorAll('button')); + const found = buttons.some((btn) => { + const text = btn.textContent?.trim() || ''; + return text.includes(name); + }); + + if (found) { + console.log(`[Plugin Test] Found ${name} in plugin menu`); + } + + return found; + }, + pluginName, + { timeout }, + ); + + console.log(`[Plugin Test] Plugin ${pluginName} is available in menu`); + return !!result; + } catch (error) { + console.error(`[Plugin Test] Plugin ${pluginName} not found in menu:`, error.message); + return false; + } +}; + +/** + * Debug helper to log current plugin state + */ +export const logPluginState = async (page: Page): Promise => { + const state = await page.evaluate(() => { + const cards = Array.from(document.querySelectorAll('plugin-management mat-card')); + const plugins = cards.map((card) => { + const title = + card.querySelector('mat-card-title')?.textContent?.trim() || 'Unknown'; + const toggle = card.querySelector( + 'mat-slide-toggle button[role="switch"]', + ) as HTMLButtonElement; + const enabled = toggle?.getAttribute('aria-checked') === 'true'; + return { title, enabled }; + }); + + const menuButtons = Array.from(document.querySelectorAll('plugin-menu button')).map( + (btn) => btn.textContent?.trim() || '', + ); + + return { + pluginCards: plugins, + menuEntries: menuButtons, + hasPluginManagement: !!document.querySelector('plugin-management'), + hasPluginMenu: !!document.querySelector('plugin-menu'), + }; + }); + + console.log('[Plugin Test] Current plugin state:', JSON.stringify(state, null, 2)); +}; + +/** + * Get timeout multiplier for CI environment + */ +export const getCITimeoutMultiplier = (): number => { + return process.env.CI ? 2 : 1; +}; diff --git a/e2e/tests/plugins/enable-plugin-test.spec.ts b/e2e/tests/plugins/enable-plugin-test.spec.ts index bbbb54534e..c644de2407 100644 --- a/e2e/tests/plugins/enable-plugin-test.spec.ts +++ b/e2e/tests/plugins/enable-plugin-test.spec.ts @@ -1,21 +1,38 @@ import { test, expect } from '../../fixtures/test.fixture'; import { cssSelectors } from '../../constants/selectors'; +import { + waitForPluginAssets, + waitForPluginSystemInit, + getCITimeoutMultiplier, +} from '../../helpers/plugin-test.helpers'; const { SIDENAV } = cssSelectors; const SETTINGS_BTN = `${SIDENAV} .tour-settingsMenuBtn`; test.describe('Enable Plugin Test', () => { - // Skip in CI - bundled plugins not loading reliably - test.skip( - !!process.env.CI, - 'Skipping plugin tests in CI - bundled plugins not loading reliably', - ); test('navigate to plugin settings and enable API Test Plugin', async ({ page, workViewPage, }) => { + const timeoutMultiplier = getCITimeoutMultiplier(); + test.setTimeout(60000 * timeoutMultiplier); + + console.log('[Plugin Test] Starting enable plugin test...'); + + // First, ensure plugin assets are available + const assetsAvailable = await waitForPluginAssets(page); + if (!assetsAvailable) { + throw new Error('Plugin assets not available - cannot proceed with test'); + } + await workViewPage.waitForTaskList(); + // Wait for plugin system to initialize + const pluginSystemReady = await waitForPluginSystemInit(page); + if (!pluginSystemReady) { + console.warn('[Plugin Test] Plugin system may not be fully initialized'); + } + // Navigate to plugin settings await page.click(SETTINGS_BTN); await page.waitForTimeout(1000); diff --git a/e2e/tests/plugins/plugin-enable-verify.spec.ts b/e2e/tests/plugins/plugin-enable-verify.spec.ts index 5a712ac2d1..dfd0f2220d 100644 --- a/e2e/tests/plugins/plugin-enable-verify.spec.ts +++ b/e2e/tests/plugins/plugin-enable-verify.spec.ts @@ -1,104 +1,91 @@ import { test, expect } from '../../fixtures/test.fixture'; import { cssSelectors } from '../../constants/selectors'; +import { + waitForPluginAssets, + waitForPluginSystemInit, + enablePluginWithVerification, + waitForPluginInMenu, + logPluginState, + getCITimeoutMultiplier, +} from '../../helpers/plugin-test.helpers'; const { SIDENAV } = cssSelectors; const SETTINGS_BTN = `${SIDENAV} .tour-settingsMenuBtn`; test.describe.serial('Plugin Enable Verify', () => { - // Skip in CI - bundled plugins not loading reliably - test.skip( - !!process.env.CI, - 'Skipping plugin tests in CI - bundled plugins not loading reliably', - ); test('enable API Test Plugin and verify menu entry', async ({ page, workViewPage }) => { + const timeoutMultiplier = getCITimeoutMultiplier(); + test.setTimeout(60000 * timeoutMultiplier); + + console.log('[Plugin Test] Starting plugin enable verification test...'); + + // First, ensure plugin assets are available + const assetsAvailable = await waitForPluginAssets(page); + if (!assetsAvailable) { + throw new Error('Plugin assets not available - cannot proceed with test'); + } + await workViewPage.waitForTaskList(); + // Wait for plugin system to initialize + const pluginSystemReady = await waitForPluginSystemInit(page); + if (!pluginSystemReady) { + console.warn('[Plugin Test] Plugin system may not be fully initialized'); + } + // Navigate to plugin settings await page.click(SETTINGS_BTN); - await page.waitForTimeout(1000); + await page.waitForSelector('.page-settings', { + state: 'visible', + timeout: 10000 * timeoutMultiplier, + }); + // Expand plugin section 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'); + if (collapsible && !collapsible.classList.contains('isExpanded')) { + const header = collapsible.querySelector('.collapsible-header'); + if (header) { + (header as HTMLElement).click(); } - } else { - console.error('Plugin collapsible not found'); } }); - await page.waitForTimeout(1000); - await expect(page.locator('plugin-management')).toBeVisible({ timeout: 5000 }); - - // Enable API Test Plugin - const result = await page.evaluate(() => { - const cards = Array.from(document.querySelectorAll('plugin-management mat-card')); - const apiTestCard = cards.find((card) => { - const title = card.querySelector('mat-card-title')?.textContent || ''; - return title.includes('API Test Plugin'); - }); - - if (!apiTestCard) { - return { found: false }; - } - - const toggle = apiTestCard.querySelector( - 'mat-slide-toggle button[role="switch"]', - ) as HTMLButtonElement; - if (!toggle) { - return { found: true, hasToggle: false }; - } - - const wasEnabled = toggle.getAttribute('aria-checked') === 'true'; - if (!wasEnabled) { - toggle.click(); - } - - return { - found: true, - hasToggle: true, - wasEnabled, - clicked: !wasEnabled, - }; + // Wait for plugin management to be visible + await page.waitForSelector('plugin-management', { + state: 'visible', + timeout: 10000 * timeoutMultiplier, }); - console.log('Enable plugin result:', result); - expect(result.found).toBe(true); - expect(result.clicked || result.wasEnabled).toBe(true); + // Log current plugin state for debugging + if (process.env.CI) { + await logPluginState(page); + } - await page.waitForTimeout(3000); // Wait for plugin to initialize + // Enable API Test Plugin with verification + const pluginEnabled = await enablePluginWithVerification( + page, + 'API Test Plugin', + 15000 * timeoutMultiplier, + ); - // Navigate back to main view - await page.click(SIDENAV); - await page.waitForTimeout(500); - await page.goto('/#/tag/TODAY'); - await page.waitForTimeout(1000); + expect(pluginEnabled).toBe(true); - // Check plugin menu exists + // Wait for plugin to appear in menu + const pluginInMenu = await waitForPluginInMenu( + page, + 'API Test Plugin', + 20000 * timeoutMultiplier, + ); + + expect(pluginInMenu).toBe(true); + + // Additional verification - check menu structure const menuResult = await page.evaluate(() => { const pluginMenu = document.querySelector('side-nav plugin-menu'); const buttons = pluginMenu ? Array.from(pluginMenu.querySelectorAll('button')) : []; @@ -107,18 +94,18 @@ test.describe.serial('Plugin Enable Verify', () => { hasPluginMenu: !!pluginMenu, buttonCount: buttons.length, buttonTexts: buttons.map((btn) => btn.textContent?.trim() || ''), - menuHTML: pluginMenu?.outerHTML?.substring(0, 200), }; }); - console.log('Plugin menu state:', menuResult); + console.log('[Plugin Test] Plugin menu state:', menuResult); expect(menuResult.hasPluginMenu).toBe(true); expect(menuResult.buttonCount).toBeGreaterThan(0); - - // Verify API Test Plugin menu entry - await expect(page.locator(`${SIDENAV} plugin-menu button`)).toBeVisible(); - await expect(page.locator(`${SIDENAV} plugin-menu button`)).toContainText( - 'API Test Plugin', + // Check if any button text contains "API Test Plugin" (handle whitespace) + const hasApiTestPlugin = menuResult.buttonTexts.some((text: string) => + text.includes('API Test Plugin'), ); + expect(hasApiTestPlugin).toBe(true); + + console.log('[Plugin Test] Plugin enable verification test completed successfully'); }); }); diff --git a/e2e/tests/plugins/plugin-iframe.spec.ts b/e2e/tests/plugins/plugin-iframe.spec.ts index e497c45b86..3555e9e42f 100644 --- a/e2e/tests/plugins/plugin-iframe.spec.ts +++ b/e2e/tests/plugins/plugin-iframe.spec.ts @@ -1,5 +1,13 @@ import { test, expect } from '../../fixtures/test.fixture'; import { cssSelectors } from '../../constants/selectors'; +import { + waitForPluginAssets, + waitForPluginSystemInit, + enablePluginWithVerification, + waitForPluginInMenu, + logPluginState, + getCITimeoutMultiplier, +} from '../../helpers/plugin-test.helpers'; const { SIDENAV } = cssSelectors; @@ -13,16 +21,27 @@ const SETTINGS_PAGE = '.page-settings'; const COLLAPSIBLE_EXPANDED = '.plugin-section collapsible.isExpanded'; test.describe.serial('Plugin Iframe', () => { - // Skip in CI - bundled plugins not loading reliably - test.skip( - !!process.env.CI, - 'Skipping plugin tests in CI - bundled plugins not loading reliably', - ); test.beforeEach(async ({ page, workViewPage }) => { - test.setTimeout(60000); // Overall test timeout for CI + // Increase timeout for CI environment + const timeoutMultiplier = getCITimeoutMultiplier(); + test.setTimeout(60000 * timeoutMultiplier); + + console.log('[Plugin Test] Starting test setup...'); + + // First, ensure plugin assets are available + const assetsAvailable = await waitForPluginAssets(page); + if (!assetsAvailable) { + throw new Error('Plugin assets not available - cannot proceed with test'); + } await workViewPage.waitForTaskList(); + // Wait for plugin system to initialize + const pluginSystemReady = await waitForPluginSystemInit(page); + if (!pluginSystemReady) { + console.warn('[Plugin Test] Plugin system may not be fully initialized'); + } + // Navigate to settings const settingsBtn = page.locator(SETTINGS_BTN); await settingsBtn.waitFor({ state: 'visible' }); @@ -48,46 +67,39 @@ test.describe.serial('Plugin Iframe', () => { } // Wait for plugin management to be visible - await page.waitForSelector(PLUGIN_MANAGEMENT, { state: 'visible' }); + await page.waitForSelector(PLUGIN_MANAGEMENT, { + state: 'visible', + timeout: 10000 * timeoutMultiplier, + }); - // Find API Test Plugin card using filter - const pluginCards = page.locator('plugin-management mat-card'); - const apiPluginCard = pluginCards.filter({ hasText: 'API Test Plugin' }); - - // Find the toggle within the API Test Plugin card - const toggle = apiPluginCard.locator('mat-slide-toggle button[role="switch"]'); - await toggle.waitFor({ state: 'visible' }); - - // Check if already enabled by checking aria-checked attribute - const isEnabled = (await toggle.getAttribute('aria-checked')) === 'true'; - if (!isEnabled) { - // Not enabled, click to enable - await toggle.click(); - // Wait for the aria-checked attribute to become true - await page.waitForFunction( - () => { - const cards = Array.from( - document.querySelectorAll('plugin-management mat-card'), - ); - const apiCard = cards.find((card) => - card - .querySelector('mat-card-title') - ?.textContent?.includes('API Test Plugin'), - ); - const toggleBtn = apiCard?.querySelector( - 'mat-slide-toggle button[role="switch"]', - ) as HTMLButtonElement; - return toggleBtn?.getAttribute('aria-checked') === 'true'; - }, - { timeout: 10000 }, - ); + // Log current plugin state for debugging + if (process.env.CI) { + await logPluginState(page); } - // Navigate back to work view - await page.goto('/#/tag/TODAY'); + // Enable API Test Plugin with verification + const pluginEnabled = await enablePluginWithVerification( + page, + 'API Test Plugin', + 15000 * timeoutMultiplier, + ); - // Wait for the task list to confirm we're on the work view - await page.waitForSelector('task-list', { state: 'visible' }); + if (!pluginEnabled) { + throw new Error('Failed to enable API Test Plugin'); + } + + // Wait for plugin to appear in menu (navigates to work view internally) + const pluginInMenu = await waitForPluginInMenu( + page, + 'API Test Plugin', + 20000 * timeoutMultiplier, + ); + + if (!pluginInMenu) { + // Log state for debugging + await logPluginState(page); + throw new Error('API Test Plugin not found in menu after enabling'); + } // Dismiss tour dialog if present (non-blocking) const tourDialog = page.locator('[data-shepherd-step-id="Welcome"]'); @@ -102,8 +114,7 @@ test.describe.serial('Plugin Iframe', () => { } } - // Wait for plugin menu to appear (indicates plugin is loaded) - await page.waitForSelector(PLUGIN_MENU_ITEM, { state: 'visible' }); + console.log('[Plugin Test] Setup completed successfully'); }); test('open plugin iframe view', async ({ page }) => { diff --git a/e2e/tests/plugins/plugin-lifecycle.spec.ts b/e2e/tests/plugins/plugin-lifecycle.spec.ts index 002e6ee426..4a734afca7 100644 --- a/e2e/tests/plugins/plugin-lifecycle.spec.ts +++ b/e2e/tests/plugins/plugin-lifecycle.spec.ts @@ -1,5 +1,10 @@ import { test, expect } from '../../fixtures/test.fixture'; import { cssSelectors } from '../../constants/selectors'; +import { + waitForPluginAssets, + waitForPluginSystemInit, + getCITimeoutMultiplier, +} from '../../helpers/plugin-test.helpers'; const { SIDENAV } = cssSelectors; @@ -9,14 +14,26 @@ const PLUGIN_MENU = `${SIDENAV} plugin-menu`; const PLUGIN_MENU_ITEM = `${PLUGIN_MENU} button`; test.describe.serial('Plugin Lifecycle', () => { - // Skip in CI - bundled plugins not loading reliably - test.skip( - !!process.env.CI, - 'Skipping plugin tests in CI - bundled plugins not loading reliably', - ); test.beforeEach(async ({ page, workViewPage }) => { + const timeoutMultiplier = getCITimeoutMultiplier(); + test.setTimeout(60000 * timeoutMultiplier); + + console.log('[Plugin Test] Starting plugin lifecycle test setup...'); + + // First, ensure plugin assets are available + const assetsAvailable = await waitForPluginAssets(page); + if (!assetsAvailable) { + throw new Error('Plugin assets not available - cannot proceed with test'); + } + await workViewPage.waitForTaskList(); + // Wait for plugin system to initialize + const pluginSystemReady = await waitForPluginSystemInit(page); + if (!pluginSystemReady) { + console.warn('[Plugin Test] Plugin system may not be fully initialized'); + } + // Enable API Test Plugin const settingsBtn = page.locator(SETTINGS_BTN); await settingsBtn.waitFor({ state: 'visible' }); diff --git a/e2e/tests/plugins/plugin-loading.spec.ts b/e2e/tests/plugins/plugin-loading.spec.ts index e51e585fa7..b9a289228c 100644 --- a/e2e/tests/plugins/plugin-loading.spec.ts +++ b/e2e/tests/plugins/plugin-loading.spec.ts @@ -1,5 +1,10 @@ import { test, expect } from '../../fixtures/test.fixture'; import { cssSelectors } from '../../constants/selectors'; +import { + waitForPluginAssets, + waitForPluginSystemInit, + getCITimeoutMultiplier, +} from '../../helpers/plugin-test.helpers'; const { SIDENAV } = cssSelectors; @@ -11,14 +16,26 @@ const PLUGIN_MENU_ENTRY = `${SIDENAV} plugin-menu button`; const PLUGIN_IFRAME = 'plugin-index iframe'; test.describe.serial('Plugin Loading', () => { - // Skip in CI - bundled plugins not loading reliably - test.skip( - !!process.env.CI, - 'Skipping plugin tests in CI - bundled plugins not loading reliably', - ); test('full plugin loading lifecycle', async ({ page, workViewPage }) => { + const timeoutMultiplier = getCITimeoutMultiplier(); + test.setTimeout(60000 * timeoutMultiplier); + + console.log('[Plugin Test] Starting full plugin loading lifecycle test...'); + + // First, ensure plugin assets are available + const assetsAvailable = await waitForPluginAssets(page); + if (!assetsAvailable) { + throw new Error('Plugin assets not available - cannot proceed with test'); + } + await workViewPage.waitForTaskList(); + // Wait for plugin system to initialize + const pluginSystemReady = await waitForPluginSystemInit(page); + if (!pluginSystemReady) { + console.warn('[Plugin Test] Plugin system may not be fully initialized'); + } + // Enable API Test Plugin first (implementing enableTestPlugin inline) await page.click(SETTINGS_BTN); await page.waitForTimeout(1000); diff --git a/e2e/tests/plugins/plugin-structure-test.spec.ts b/e2e/tests/plugins/plugin-structure-test.spec.ts index b67689cfc8..2086f2d6c3 100644 --- a/e2e/tests/plugins/plugin-structure-test.spec.ts +++ b/e2e/tests/plugins/plugin-structure-test.spec.ts @@ -1,18 +1,35 @@ import { test, expect } from '../../fixtures/test.fixture'; import { cssSelectors } from '../../constants/selectors'; +import { + waitForPluginAssets, + waitForPluginSystemInit, + getCITimeoutMultiplier, +} from '../../helpers/plugin-test.helpers'; const { SIDENAV } = cssSelectors; const SETTINGS_BTN = `${SIDENAV} .tour-settingsMenuBtn`; test.describe.serial('Plugin Structure Test', () => { - // Skip in CI - bundled plugins not loading reliably - test.skip( - !!process.env.CI, - 'Skipping plugin tests in CI - bundled plugins not loading reliably', - ); test('check plugin card structure', async ({ page, workViewPage }) => { + const timeoutMultiplier = getCITimeoutMultiplier(); + test.setTimeout(60000 * timeoutMultiplier); + + console.log('[Plugin Test] Starting plugin structure test...'); + + // First, ensure plugin assets are available + const assetsAvailable = await waitForPluginAssets(page); + if (!assetsAvailable) { + throw new Error('Plugin assets not available - cannot proceed with test'); + } + await workViewPage.waitForTaskList(); + // Wait for plugin system to initialize + const pluginSystemReady = await waitForPluginSystemInit(page); + if (!pluginSystemReady) { + console.warn('[Plugin Test] Plugin system may not be fully initialized'); + } + // Navigate to plugin settings (implementing navigateToPluginSettings inline) await page.click(SETTINGS_BTN); await page.waitForTimeout(1000);