test(e2e): fix 12 flaky tests by removing networkidle waits and fixing plugin setup

Replace unreliable waitForLoadState('networkidle') with element-based waits
in planner page and tests. Fix plugin helper timeout math bugs and eliminate
duplicate settings navigation in plugin tests. Improve menu overlay cleanup
and increase tight timeouts in migration and work-view tests.
This commit is contained in:
Johannes Millan 2026-02-09 14:00:21 +01:00
parent a416661bf6
commit b834bbedb1
10 changed files with 67 additions and 339 deletions

View file

@ -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<boolean> => {
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);

View file

@ -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<void> {
await this.page.goto(`/#/project/${projectId}/planner`);
await this.page.waitForLoadState('networkidle');
await this.page.waitForURL(/\/(planner|tasks)/);
await this.routerWrapper.waitFor({ state: 'visible' });
}

View file

@ -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

View file

@ -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

View file

@ -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();

View file

@ -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() || ''),
};
});
});
});

View file

@ -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'),
);

View file

@ -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();
}
});

View file

@ -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);
}
});

View file

@ -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/);