mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-08-01 20:12:11 +00:00
fix(e2e): make plugin tests reliable in CI environment
- Add robust plugin test helpers with retry logic and proper waits - Implement asset availability checks before running tests - Wait for plugin system initialization before test execution - Add CI-specific timeout multipliers for slower environments - Fix race conditions in plugin enabling logic - Add comprehensive debug logging for troubleshooting - Handle text matching issues with whitespace variations - Remove CI skip conditions as tests now work reliably The plugin tests were failing in CI due to: 1. Plugin assets not being fully loaded when tests started 2. Plugin system not initialized before test execution 3. Insufficient timeouts for CI environment 4. Text matching issues with unexpected whitespace All plugin tests now pass with proper synchronization and error handling.
This commit is contained in:
parent
fb0fa730a5
commit
e2722f17f9
7 changed files with 486 additions and 143 deletions
277
e2e/helpers/plugin-test.helpers.ts
Normal file
277
e2e/helpers/plugin-test.helpers.ts
Normal file
|
|
@ -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<boolean> => {
|
||||
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<boolean> => {
|
||||
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<boolean> => {
|
||||
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<boolean> => {
|
||||
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<void> => {
|
||||
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;
|
||||
};
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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 }) => {
|
||||
|
|
|
|||
|
|
@ -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' });
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue