Skip to content

Commit d8b1f82

Browse files
author
Mohammod Al Amin Ashik
committed
Add gateway and settings E2E tests with test IDs (55 tests passing)
1 parent e0fd2b5 commit d8b1f82

3 files changed

Lines changed: 319 additions & 14 deletions

File tree

apps/desktop/src/App.tsx

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -282,20 +282,21 @@ function DashboardView() {
282282
</div>
283283

284284
{/* Gateway Status Banner */}
285-
<Card className={gatewayStatus.running ? 'border-green-500' : 'border-orange-500'}>
285+
<Card className={gatewayStatus.running ? 'border-green-500' : 'border-orange-500'} data-testid="gateway-status-card">
286286
<CardContent className="flex items-center justify-between py-3">
287287
<div className="flex items-center gap-3">
288288
<span
289289
className={`h-3 w-3 rounded-full ${
290290
gatewayStatus.running ? 'bg-green-500' : 'bg-orange-500'
291291
}`}
292+
data-testid="gateway-status-indicator"
292293
/>
293294
<div>
294-
<span className="font-medium">
295+
<span className="font-medium" data-testid="gateway-status-text">
295296
Gateway: {gatewayStatus.running ? 'Running' : 'Stopped'}
296297
</span>
297298
{gatewayStatus.url && (
298-
<span className="text-sm text-[rgb(var(--muted))] ml-2">
299+
<span className="text-sm text-[rgb(var(--muted))] ml-2" data-testid="gateway-url">
299300
{gatewayStatus.url}
300301
</span>
301302
)}
@@ -305,62 +306,63 @@ function DashboardView() {
305306
variant={gatewayStatus.running ? 'ghost' : 'primary'}
306307
size="sm"
307308
onClick={handleToggleGateway}
309+
data-testid="gateway-toggle-btn"
308310
>
309311
{gatewayStatus.running ? 'Stop' : 'Start'}
310312
</Button>
311313
</CardContent>
312314
</Card>
313315

314316
{/* Stats Grid */}
315-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
316-
<Card>
317+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4" data-testid="dashboard-stats-grid">
318+
<Card data-testid="stat-servers">
317319
<CardHeader>
318320
<CardTitle className="flex items-center gap-2 text-base">
319321
<Server className="h-5 w-5 text-primary-500" />
320322
Servers
321323
</CardTitle>
322324
</CardHeader>
323325
<CardContent>
324-
<div className="text-3xl font-bold">{stats.connectedServers}/{stats.installedServers}</div>
326+
<div className="text-3xl font-bold" data-testid="stat-servers-value">{stats.connectedServers}/{stats.installedServers}</div>
325327
<div className="text-sm text-[rgb(var(--muted))]">Connected / Installed</div>
326328
</CardContent>
327329
</Card>
328330

329-
<Card>
331+
<Card data-testid="stat-featuresets">
330332
<CardHeader>
331333
<CardTitle className="flex items-center gap-2 text-base">
332334
<Wrench className="h-5 w-5 text-primary-500" />
333335
FeatureSets
334336
</CardTitle>
335337
</CardHeader>
336338
<CardContent>
337-
<div className="text-3xl font-bold">{stats.featureSets}</div>
339+
<div className="text-3xl font-bold" data-testid="stat-featuresets-value">{stats.featureSets}</div>
338340
<div className="text-sm text-[rgb(var(--muted))]">Permission bundles</div>
339341
</CardContent>
340342
</Card>
341343

342-
<Card>
344+
<Card data-testid="stat-clients">
343345
<CardHeader>
344346
<CardTitle className="flex items-center gap-2 text-base">
345347
<Monitor className="h-5 w-5 text-primary-500" />
346348
Clients
347349
</CardTitle>
348350
</CardHeader>
349351
<CardContent>
350-
<div className="text-3xl font-bold">{stats.clients}</div>
352+
<div className="text-3xl font-bold" data-testid="stat-clients-value">{stats.clients}</div>
351353
<div className="text-sm text-[rgb(var(--muted))]">Registered AI clients</div>
352354
</CardContent>
353355
</Card>
354356

355-
<Card>
357+
<Card data-testid="stat-active-space">
356358
<CardHeader>
357359
<CardTitle className="flex items-center gap-2 text-base">
358360
<Globe className="h-5 w-5 text-primary-500" />
359361
Active Space
360362
</CardTitle>
361363
</CardHeader>
362364
<CardContent>
363-
<div className="text-xl font-bold truncate">
365+
<div className="text-xl font-bold truncate" data-testid="stat-active-space-value">
364366
{viewSpace?.icon} {viewSpace?.name || 'None'}
365367
</div>
366368
<div className="text-sm text-[rgb(var(--muted))]">Current context</div>
@@ -407,6 +409,7 @@ function DashboardView() {
407409
setExportSuccess('Config copied to clipboard!');
408410
setTimeout(() => setExportSuccess(null), 2000);
409411
}}
412+
data-testid="copy-config-btn"
410413
>
411414
📋 Copy
412415
</Button>
@@ -473,11 +476,12 @@ function SettingsView() {
473476
<div className="space-y-4">
474477
<div>
475478
<label className="text-sm font-medium">Theme</label>
476-
<div className="flex gap-2 mt-2">
479+
<div className="flex gap-2 mt-2" data-testid="theme-buttons">
477480
<Button
478481
variant={theme === 'light' ? 'primary' : 'secondary'}
479482
size="sm"
480483
onClick={() => setTheme('light')}
484+
data-testid="theme-light-btn"
481485
>
482486
<Sun className="h-4 w-4 mr-2" />
483487
Light
@@ -486,6 +490,7 @@ function SettingsView() {
486490
variant={theme === 'dark' ? 'primary' : 'secondary'}
487491
size="sm"
488492
onClick={() => setTheme('dark')}
493+
data-testid="theme-dark-btn"
489494
>
490495
<Moon className="h-4 w-4 mr-2" />
491496
Dark
@@ -494,6 +499,7 @@ function SettingsView() {
494499
variant={theme === 'system' ? 'primary' : 'secondary'}
495500
size="sm"
496501
onClick={() => setTheme('system')}
502+
data-testid="theme-system-btn"
497503
>
498504
<Monitor className="h-4 w-4 mr-2" />
499505
System
@@ -516,7 +522,7 @@ function SettingsView() {
516522
<div className="space-y-4">
517523
<div>
518524
<label className="text-sm font-medium">Log Files Location</label>
519-
<p className="text-sm text-[rgb(var(--muted))] mt-1 font-mono bg-surface-secondary rounded px-2 py-1">
525+
<p className="text-sm text-[rgb(var(--muted))] mt-1 font-mono bg-surface-secondary rounded px-2 py-1" data-testid="logs-path">
520526
{logsPath || 'Loading...'}
521527
</p>
522528
</div>
@@ -526,6 +532,7 @@ function SettingsView() {
526532
size="sm"
527533
onClick={handleOpenLogs}
528534
disabled={openingLogs}
535+
data-testid="open-logs-btn"
529536
>
530537
{openingLogs ? (
531538
<Loader2 className="h-4 w-4 mr-2 animate-spin" />

tests/e2e/specs/gateway.wdio.ts

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/**
2+
* E2E Tests: Gateway Control
3+
*
4+
* Test Cases Covered:
5+
* - TC-GW-001: Gateway Status Display
6+
* - TC-GW-002: Gateway Running on Startup
7+
* - TC-GW-003: Dashboard Shows Gateway Status
8+
* - TC-GW-006: Gateway URL Display
9+
* - TC-GW-007: Copy Client Config
10+
* - TC-GW-008: Dashboard Statistics
11+
*/
12+
13+
// Helper to find element by test ID or fallback
14+
async function findElement(testId: string, fallbackSelector: string) {
15+
const byTestId = await $(`[data-testid="${testId}"]`);
16+
const testIdExists = await byTestId.isExisting().catch(() => false);
17+
if (testIdExists) {
18+
return byTestId;
19+
}
20+
return $(fallbackSelector);
21+
}
22+
23+
describe('Gateway Status - Dashboard', () => {
24+
before(async () => {
25+
// Navigate to Dashboard
26+
const dashboardBtn = await findElement('nav-dashboard', 'button*=Dashboard');
27+
await dashboardBtn.click();
28+
await browser.pause(2000);
29+
});
30+
31+
it('TC-GW-001: Gateway status is displayed in status bar', async () => {
32+
await browser.saveScreenshot('./tests/e2e/screenshots/gw-01-status-bar.png');
33+
34+
const pageSource = await browser.getPageSource();
35+
36+
// Status bar should show gateway status
37+
const hasGatewayStatus =
38+
pageSource.includes('Gateway Active') ||
39+
pageSource.includes('Gateway Stopped') ||
40+
pageSource.includes('Gateway');
41+
42+
expect(hasGatewayStatus).toBe(true);
43+
});
44+
45+
it('TC-GW-002: Gateway is running on startup', async () => {
46+
// Check gateway status card
47+
const statusCard = await findElement('gateway-status-card', '.border-green-500');
48+
const isDisplayed = await statusCard.isDisplayed().catch(() => false);
49+
50+
await browser.saveScreenshot('./tests/e2e/screenshots/gw-02-gateway-running.png');
51+
52+
const pageSource = await browser.getPageSource();
53+
54+
// Gateway should be running by default
55+
const isRunning =
56+
pageSource.includes('Gateway: Running') ||
57+
pageSource.includes('border-green-500');
58+
59+
console.log('[DEBUG] Gateway running:', isRunning);
60+
expect(isRunning).toBe(true);
61+
});
62+
63+
it('TC-GW-003: Dashboard shows gateway status banner', async () => {
64+
// Verify gateway status content via page source
65+
const pageSource = await browser.getPageSource();
66+
67+
// Check for gateway status card
68+
const hasGatewayCard =
69+
pageSource.includes('Gateway: Running') ||
70+
pageSource.includes('Gateway: Stopped') ||
71+
pageSource.includes('gateway-status-card');
72+
73+
expect(hasGatewayCard).toBe(true);
74+
75+
// Verify toggle button exists
76+
const hasToggleBtn =
77+
pageSource.includes('Stop') ||
78+
pageSource.includes('Start');
79+
80+
expect(hasToggleBtn).toBe(true);
81+
});
82+
83+
it('TC-GW-006: Gateway URL is displayed', async () => {
84+
const pageSource = await browser.getPageSource();
85+
86+
// Gateway URL should be visible (localhost:3100 or similar)
87+
const hasGatewayUrl =
88+
pageSource.includes('localhost:') ||
89+
pageSource.includes('http://127.0.0.1');
90+
91+
console.log('[DEBUG] Has gateway URL:', hasGatewayUrl);
92+
expect(hasGatewayUrl).toBe(true);
93+
});
94+
95+
it('TC-GW-007: Copy client config button exists', async () => {
96+
const copyBtn = await findElement('copy-config-btn', 'button*=Copy');
97+
const isDisplayed = await copyBtn.isDisplayed().catch(() => false);
98+
99+
await browser.saveScreenshot('./tests/e2e/screenshots/gw-07-copy-config.png');
100+
101+
expect(isDisplayed).toBe(true);
102+
});
103+
104+
it('TC-GW-008: Dashboard statistics are displayed', async () => {
105+
// Check stats grid exists
106+
const statsGrid = await findElement('dashboard-stats-grid', '.grid');
107+
const gridDisplayed = await statsGrid.isDisplayed().catch(() => false);
108+
109+
await browser.saveScreenshot('./tests/e2e/screenshots/gw-08-dashboard-stats.png');
110+
111+
// Verify individual stat cards
112+
const pageSource = await browser.getPageSource();
113+
114+
const hasServersCard = pageSource.includes('Servers');
115+
const hasFeatureSetsCard = pageSource.includes('FeatureSets');
116+
const hasClientsCard = pageSource.includes('Clients');
117+
const hasActiveSpaceCard = pageSource.includes('Active Space');
118+
119+
console.log('[DEBUG] Stats - Servers:', hasServersCard);
120+
console.log('[DEBUG] Stats - FeatureSets:', hasFeatureSetsCard);
121+
console.log('[DEBUG] Stats - Clients:', hasClientsCard);
122+
console.log('[DEBUG] Stats - Active Space:', hasActiveSpaceCard);
123+
124+
expect(hasServersCard).toBe(true);
125+
expect(hasFeatureSetsCard).toBe(true);
126+
expect(hasClientsCard).toBe(true);
127+
expect(hasActiveSpaceCard).toBe(true);
128+
});
129+
});
130+
131+
describe('Gateway Toggle', () => {
132+
before(async () => {
133+
// Navigate to Dashboard
134+
const dashboardBtn = await findElement('nav-dashboard', 'button*=Dashboard');
135+
await dashboardBtn.click();
136+
await browser.pause(2000);
137+
});
138+
139+
it('TC-GW-004/005: Toggle gateway button exists and is clickable', async () => {
140+
const toggleBtn = await findElement('gateway-toggle-btn', 'button*=Stop');
141+
const isDisplayed = await toggleBtn.isDisplayed().catch(() => false);
142+
143+
if (!isDisplayed) {
144+
// Try finding Start button instead
145+
const startBtn = await findElement('gateway-toggle-btn', 'button*=Start');
146+
const startDisplayed = await startBtn.isDisplayed().catch(() => false);
147+
expect(startDisplayed).toBe(true);
148+
} else {
149+
expect(isDisplayed).toBe(true);
150+
}
151+
152+
await browser.saveScreenshot('./tests/e2e/screenshots/gw-toggle-button.png');
153+
});
154+
});

0 commit comments

Comments
 (0)