Skip to content

Commit 506f5c1

Browse files
author
Mohammod Al Amin Ashik
committed
fix(e2e): improve test reliability for CI environments
- Add TIMEOUT constants (5s/15s/30s/60s) for CI-friendly waits - Add waitForModalClose() to wait for modal overlays to close - Add safeClick() to ensure clicks aren't intercepted by overlays - Update all E2E test files to use new helpers - Increase waitForClickable timeouts from 5s to 15s - Add waitForModalClose() after install/uninstall/save operations Fixes flaky tests on Windows CI: - Element click intercepted by modal backdrop - Element not displayed after short timeout - MCP handshake timing issues
1 parent 4d206a3 commit 506f5c1

7 files changed

Lines changed: 102 additions & 67 deletions

File tree

tests/e2e/helpers/selectors.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,28 @@
33
* Use $('[data-testid="x"]') for all element selection.
44
*/
55

6+
// CI-friendly timeouts (Windows CI is slower)
7+
export const TIMEOUT = {
8+
short: 5000,
9+
medium: 15000, // Default for waitForDisplayed/Clickable
10+
long: 30000, // For slow operations like MCP connections
11+
veryLong: 60000,
12+
};
13+
614
/** Get element by data-testid */
715
export const byTestId = (testId: string) => $(`[data-testid="${testId}"]`);
16+
17+
/** Wait for any modal overlay to close (backdrop with blur) */
18+
export async function waitForModalClose(timeout = TIMEOUT.medium): Promise<void> {
19+
const overlay = await $('.fixed.inset-0.bg-black\\/20');
20+
if (await overlay.isExisting()) {
21+
await overlay.waitForDisplayed({ timeout, reverse: true });
22+
}
23+
}
24+
25+
/** Click element after ensuring no modal overlay is blocking */
26+
export async function safeClick(element: WebdriverIO.Element, timeout = TIMEOUT.medium): Promise<void> {
27+
await waitForModalClose(timeout);
28+
await element.waitForClickable({ timeout });
29+
await element.click();
30+
}

tests/e2e/specs/app.wdio.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Uses data-testid only (ADR-003).
44
*/
55

6-
import { byTestId } from '../helpers/selectors';
6+
import { byTestId, TIMEOUT, safeClick } from '../helpers/selectors';
77

88
describe('McpMux Application', () => {
99
it('should launch and show main window', async () => {
@@ -14,13 +14,13 @@ describe('McpMux Application', () => {
1414

1515
it('should display sidebar navigation', async () => {
1616
const navItem = await byTestId('nav-dashboard');
17-
await navItem.waitForDisplayed({ timeout: 10000 });
17+
await navItem.waitForDisplayed({ timeout: TIMEOUT.medium });
1818
await expect(navItem).toBeDisplayed();
1919
});
2020

2121
it('should show My Servers tab', async () => {
2222
const serversButton = await byTestId('nav-my-servers');
23-
await serversButton.waitForDisplayed({ timeout: 10000 });
23+
await serversButton.waitForDisplayed({ timeout: TIMEOUT.medium });
2424
await expect(serversButton).toBeDisplayed();
2525
});
2626

@@ -31,7 +31,7 @@ describe('McpMux Application', () => {
3131

3232
it('should navigate to Discover page', async () => {
3333
const discoverButton = await byTestId('nav-discover');
34-
await discoverButton.click();
34+
await safeClick(discoverButton);
3535
await browser.pause(1000);
3636
const heading = await byTestId('registry-title');
3737
await expect(heading).toBeDisplayed();
@@ -44,41 +44,41 @@ describe('McpMux Application', () => {
4444

4545
it('should navigate to My Servers page', async () => {
4646
const serversButton = await byTestId('nav-my-servers');
47-
await serversButton.click();
47+
await safeClick(serversButton);
4848
await browser.pause(1000);
4949
const heading = await byTestId('servers-title');
5050
await expect(heading).toBeDisplayed();
5151
});
5252

5353
it('should navigate to Clients page', async () => {
5454
const clientsButton = await byTestId('nav-clients');
55-
await clientsButton.waitForClickable({ timeout: 5000 });
56-
await clientsButton.click();
55+
await clientsButton.waitForClickable({ timeout: TIMEOUT.medium });
56+
await safeClick(clientsButton);
5757
await browser.pause(1500);
5858
const pageSource = await browser.getPageSource();
5959
expect(pageSource.includes('Connected Clients') || pageSource.includes('Clients')).toBe(true);
6060
});
6161

6262
it('should navigate to FeatureSets page', async () => {
6363
const featuresButton = await byTestId('nav-featuresets');
64-
await featuresButton.waitForClickable({ timeout: 5000 });
65-
await featuresButton.click();
64+
await featuresButton.waitForClickable({ timeout: TIMEOUT.medium });
65+
await safeClick(featuresButton);
6666
await browser.pause(1500);
6767
const pageSource = await browser.getPageSource();
6868
expect(pageSource.includes('Feature Sets') || pageSource.includes('FeatureSets')).toBe(true);
6969
});
7070

7171
it('should show space switcher in sidebar', async () => {
7272
const navItem = await byTestId('nav-dashboard');
73-
await navItem.waitForDisplayed({ timeout: 5000 });
73+
await navItem.waitForDisplayed({ timeout: TIMEOUT.medium });
7474
await expect(navItem).toBeDisplayed();
7575
});
7676
});
7777

7878
describe('Registry/Discover Functionality', () => {
7979
before(async () => {
8080
const discoverButton = await byTestId('nav-discover');
81-
await discoverButton.click();
81+
await safeClick(discoverButton);
8282
await browser.pause(2000);
8383
});
8484

tests/e2e/specs/comprehensive.wdio.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Uses data-testid only (ADR-003).
44
*/
55

6-
import { byTestId } from '../helpers/selectors';
6+
import { byTestId, safeClick } from '../helpers/selectors';
77
import {
88
createSpace,
99
deleteSpace,
@@ -94,7 +94,7 @@ describe('Comprehensive: Space Isolation', () => {
9494
await browser.pause(2000);
9595

9696
const serversBtn = await byTestId('nav-my-servers');
97-
await serversBtn.click();
97+
await safeClick(serversBtn);
9898
await browser.pause(2000);
9999

100100
await browser.saveScreenshot('./tests/e2e/screenshots/comp-01-work-servers.png');
@@ -188,7 +188,7 @@ describe('Comprehensive: Client Grants', () => {
188188

189189
it('TC-COMP-CL-002: Verify Clients page loads', async () => {
190190
const clientsBtn = await byTestId('nav-clients');
191-
await clientsBtn.click();
191+
await safeClick(clientsBtn);
192192
await browser.pause(2000);
193193

194194
await browser.saveScreenshot('./tests/e2e/screenshots/comp-03-clients.png');
@@ -238,7 +238,7 @@ describe('Comprehensive: Server Lifecycle with API', () => {
238238

239239
it('TC-COMP-SV-002: Verify server in UI after API install', async () => {
240240
const serversBtn = await byTestId('nav-my-servers');
241-
await serversBtn.click();
241+
await safeClick(serversBtn);
242242
await browser.pause(2000);
243243

244244
await browser.saveScreenshot('./tests/e2e/screenshots/comp-04-server-installed.png');
@@ -339,7 +339,7 @@ describe('Comprehensive: Custom FeatureSet', () => {
339339

340340
it('TC-COMP-FS-002: Verify FeatureSet in UI', async () => {
341341
const featureSetsBtn = await byTestId('nav-featuresets');
342-
await featureSetsBtn.click();
342+
await safeClick(featureSetsBtn);
343343
await browser.pause(2000);
344344

345345
await browser.saveScreenshot('./tests/e2e/screenshots/comp-07-featureset.png');
@@ -412,7 +412,7 @@ describe('Comprehensive: Multi-Space Server Management', () => {
412412

413413
it('TC-COMP-MS-003: Verify space switcher shows all spaces', async () => {
414414
const spacesBtn = await byTestId('nav-spaces');
415-
await spacesBtn.click();
415+
await safeClick(spacesBtn);
416416
await browser.pause(2000);
417417

418418
await browser.saveScreenshot('./tests/e2e/screenshots/comp-08-all-spaces.png');

tests/e2e/specs/featureset.wdio.ts

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
* Uses data-testid only (ADR-003).
44
*/
55

6-
import { byTestId } from '../helpers/selectors';
6+
import { byTestId, TIMEOUT, waitForModalClose, safeClick } from '../helpers/selectors';
77

88
describe('FeatureSet - Builtin Sets', () => {
99
it('TC-FS-001: Navigate to FeatureSets page and verify builtin sets exist', async () => {
1010
const featureSetsButton = await byTestId('nav-featuresets');
11-
await featureSetsButton.click();
11+
await safeClick(featureSetsButton);
1212
await browser.pause(2000);
1313

1414
await browser.saveScreenshot('./tests/e2e/screenshots/fs-01-page.png');
@@ -36,7 +36,7 @@ describe('FeatureSet - Builtin Sets', () => {
3636
describe('FeatureSet - Server-All Auto Creation', () => {
3737
it('Setup: Install and Enable Echo Server', async () => {
3838
const discoverButton = await byTestId('nav-discover');
39-
await discoverButton.click();
39+
await safeClick(discoverButton);
4040
await browser.pause(2000);
4141

4242
const searchInput = await byTestId('search-input');
@@ -49,21 +49,22 @@ describe('FeatureSet - Server-All Auto Creation', () => {
4949
const isInstallDisplayed = await installButton.isDisplayed().catch(() => false);
5050

5151
if (isInstallDisplayed) {
52-
await installButton.waitForClickable({ timeout: 5000 });
53-
await installButton.click();
52+
await installButton.waitForClickable({ timeout: TIMEOUT.medium });
53+
await safeClick(installButton);
5454
await browser.pause(3000);
55+
await waitForModalClose();
5556
}
5657

5758
const myServersButton = await byTestId('nav-my-servers');
58-
await myServersButton.click();
59+
await safeClick(myServersButton);
5960
await browser.pause(2000);
6061

6162
const enableButton = await byTestId('enable-server-echo-server');
6263
const isEnableDisplayed = await enableButton.isDisplayed().catch(() => false);
6364

6465
if (isEnableDisplayed) {
65-
await enableButton.click();
66-
await browser.pause(5000); // Wait for connection
66+
await safeClick(enableButton);
67+
await browser.pause(TIMEOUT.medium); // Wait for MCP connection on slow CI
6768
}
6869

6970
await browser.saveScreenshot('./tests/e2e/screenshots/fs-02-server-enabled.png');
@@ -80,7 +81,7 @@ describe('FeatureSet - Server-All Auto Creation', () => {
8081

8182
it('TC-FS-002: Verify server-all FeatureSet is created for Echo Server', async () => {
8283
const featureSetsButton = await byTestId('nav-featuresets');
83-
await featureSetsButton.click();
84+
await safeClick(featureSetsButton);
8485
await browser.pause(2000);
8586

8687
await browser.saveScreenshot('./tests/e2e/screenshots/fs-03-featuresets-with-server.png');
@@ -136,19 +137,19 @@ describe('FeatureSet - Server-All Auto Creation', () => {
136137

137138
it('TC-FS-004: Disable server and verify FeatureSet is hidden', async () => {
138139
const myServersButton = await byTestId('nav-my-servers');
139-
await myServersButton.click();
140+
await safeClick(myServersButton);
140141
await browser.pause(2000);
141142

142143
const disableButton = await byTestId('disable-server-echo-server');
143144
const isDisableDisplayed = await disableButton.isDisplayed().catch(() => false);
144145

145146
if (isDisableDisplayed) {
146-
await disableButton.click();
147+
await safeClick(disableButton);
147148
await browser.pause(2000);
148149
}
149150

150151
const featureSetsButton = await byTestId('nav-featuresets');
151-
await featureSetsButton.click();
152+
await safeClick(featureSetsButton);
152153
await browser.pause(2000);
153154

154155
await browser.saveScreenshot('./tests/e2e/screenshots/fs-05-after-disable.png');
@@ -162,7 +163,7 @@ describe('FeatureSet - Server-All Auto Creation', () => {
162163

163164
it('Cleanup: Uninstall Echo Server', async () => {
164165
const discoverButton = await byTestId('nav-discover');
165-
await discoverButton.click();
166+
await safeClick(discoverButton);
166167
await browser.pause(2000);
167168

168169
const searchInput = await byTestId('search-input');
@@ -175,9 +176,10 @@ describe('FeatureSet - Server-All Auto Creation', () => {
175176
const isDisplayed = await uninstallButton.isDisplayed().catch(() => false);
176177

177178
if (isDisplayed) {
178-
await uninstallButton.waitForClickable({ timeout: 5000 });
179-
await uninstallButton.click();
179+
await uninstallButton.waitForClickable({ timeout: TIMEOUT.medium });
180+
await safeClick(uninstallButton);
180181
await browser.pause(2000);
182+
await waitForModalClose();
181183
}
182184

183185
await browser.saveScreenshot('./tests/e2e/screenshots/fs-06-cleanup.png');

0 commit comments

Comments
 (0)