Skip to content

Commit d720ef0

Browse files
author
Mohammod Al Amin Ashik
committed
test: add e2e toast notification tests for all pages
Added Playwright e2e tests to verify toast notification presence and behavior across all pages: - BasePage: shared toast helper methods (waitForToast, getToastText, dismissToast) - SpacesPage: toast container, create/delete/set-active space toasts - RegistryPage: toast container, install/uninstall server toasts - ClientsPage: toast container, save config/remove/toggle grant toasts - ServersPage: server enable, clear logs, copy log path toasts - FeatureSetsPage: toast container, panel save/error toasts
1 parent a273a52 commit d720ef0

6 files changed

Lines changed: 429 additions & 1 deletion

File tree

tests/e2e/pages/BasePage.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,33 @@ export abstract class BasePage {
6161
async screenshot(name: string) {
6262
await this.page.screenshot({ path: `./reports/screenshots/${name}.png` });
6363
}
64+
65+
/**
66+
* Wait for a toast notification of the given type to appear
67+
*/
68+
async waitForToast(type: 'success' | 'error' | 'warning' | 'info', timeout = 5000) {
69+
await this.page.getByTestId(`toast-${type}`).first().waitFor({ timeout });
70+
}
71+
72+
/**
73+
* Get text content of the first visible toast
74+
*/
75+
async getToastText(): Promise<string | null> {
76+
const toast = this.page.getByTestId('toast-container').locator('[role="alert"]').first();
77+
return toast.textContent();
78+
}
79+
80+
/**
81+
* Dismiss the first visible toast
82+
*/
83+
async dismissToast() {
84+
await this.page.getByTestId('toast-close').first().click();
85+
}
86+
87+
/**
88+
* Assert that a toast container is present in the DOM
89+
*/
90+
get toastContainer(): Locator {
91+
return this.page.getByTestId('toast-container');
92+
}
6493
}

tests/e2e/specs/clients.spec.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,103 @@ test.describe('Client Management', () => {
111111
}
112112
});
113113
});
114+
115+
test.describe('Client Toast Notifications', () => {
116+
test('should have toast container on clients page', async ({ page }) => {
117+
const dashboard = new DashboardPage(page);
118+
const clients = new ClientsPage(page);
119+
await dashboard.navigate();
120+
121+
await page.locator('nav button:has-text("Clients")').click();
122+
await expect(clients.heading).toBeVisible();
123+
124+
await expect(clients.toastContainer).toBeAttached();
125+
});
126+
127+
// Skip in web mode - requires Tauri API for client operations
128+
test.skip('should show success toast when saving client config', async ({ page }) => {
129+
const dashboard = new DashboardPage(page);
130+
const clients = new ClientsPage(page);
131+
await dashboard.navigate();
132+
133+
await page.locator('nav button:has-text("Clients")').click();
134+
135+
// Click first client card to open panel
136+
const clientCards = page.locator('[data-testid^="client-card-"]');
137+
const count = await clientCards.count();
138+
139+
if (count > 0) {
140+
await clientCards.first().click();
141+
142+
// Wait for panel to open
143+
await expect(page.locator('text=Quick Settings')).toBeVisible();
144+
145+
// Click Save Changes
146+
const saveButton = page.getByRole('button', { name: /Save Changes/i });
147+
if (await saveButton.isVisible()) {
148+
await saveButton.click();
149+
150+
await clients.waitForToast('success');
151+
const toastText = await clients.getToastText();
152+
expect(toastText).toContain('Client settings saved');
153+
}
154+
}
155+
});
156+
157+
// Skip in web mode - requires Tauri API for client deletion
158+
test.skip('should show success toast when removing a client', async ({ page }) => {
159+
const dashboard = new DashboardPage(page);
160+
const clients = new ClientsPage(page);
161+
await dashboard.navigate();
162+
163+
await page.locator('nav button:has-text("Clients")').click();
164+
165+
const clientCards = page.locator('[data-testid^="client-card-"]');
166+
const count = await clientCards.count();
167+
168+
if (count > 0) {
169+
await clientCards.first().click();
170+
171+
// Click Remove Client in panel footer
172+
page.on('dialog', dialog => dialog.accept());
173+
const removeButton = page.getByRole('button', { name: /Remove Client/i });
174+
if (await removeButton.isVisible()) {
175+
await removeButton.click();
176+
177+
await clients.waitForToast('success');
178+
const toastText = await clients.getToastText();
179+
expect(toastText).toContain('Client removed');
180+
}
181+
}
182+
});
183+
184+
// Skip in web mode - requires Tauri API for permission toggle
185+
test.skip('should show success toast when toggling feature set grant', async ({ page }) => {
186+
const dashboard = new DashboardPage(page);
187+
const clients = new ClientsPage(page);
188+
await dashboard.navigate();
189+
190+
await page.locator('nav button:has-text("Clients")').click();
191+
192+
const clientCards = page.locator('[data-testid^="client-card-"]');
193+
const count = await clientCards.count();
194+
195+
if (count > 0) {
196+
await clientCards.first().click();
197+
198+
// Expand Permissions section
199+
await page.locator('text=Permissions').click();
200+
await page.waitForTimeout(300);
201+
202+
// Find a non-default feature set checkbox
203+
const featureSetToggle = page.locator('button:has([class*="rounded border"])').first();
204+
if (await featureSetToggle.isVisible()) {
205+
await featureSetToggle.click();
206+
207+
await clients.waitForToast('success');
208+
const toastText = await clients.getToastText();
209+
expect(toastText).toMatch(/Permission (granted|revoked)/);
210+
}
211+
}
212+
});
213+
});

tests/e2e/specs/featuresets.spec.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ test.describe('FeatureSet Details', () => {
7272
});
7373
});
7474

75+
test.describe('Feature Set Toast Container', () => {
76+
test('should have toast container on feature sets page', async ({ page }) => {
77+
const dashboard = new DashboardPage(page);
78+
await dashboard.navigate();
79+
80+
await page.locator('nav button:has-text("FeatureSets")').click({ force: true });
81+
await expect(page.getByRole('heading', { name: 'Feature Sets' }).first()).toBeVisible();
82+
83+
await expect(page.getByTestId('toast-container')).toBeAttached();
84+
});
85+
});
86+
7587
test.describe('Feature Set Operations with Toast', () => {
7688
// Skip in web mode - requires Tauri API
7789
test.skip('should show toast when creating feature set', async ({ page }) => {
@@ -144,6 +156,59 @@ test.describe('Feature Set Operations with Toast', () => {
144156
});
145157
});
146158

159+
test.describe('Feature Set Panel Save Toast', () => {
160+
// Skip in web mode - requires Tauri API
161+
test.skip('should show success toast when saving feature set members', async ({ page }) => {
162+
const dashboard = new DashboardPage(page);
163+
await dashboard.navigate();
164+
165+
await page.locator('nav button:has-text("FeatureSets")').click({ force: true });
166+
167+
// Click on a configurable feature set (Default or Custom)
168+
const configurableSet = page.locator('[data-testid^="featureset-card-"]').first();
169+
if (await configurableSet.isVisible()) {
170+
await configurableSet.click();
171+
172+
// Wait for panel to open
173+
await expect(page.locator('text=Save Changes')).toBeVisible({ timeout: 5000 });
174+
175+
// Click Save Changes
176+
await page.getByRole('button', { name: /Save Changes/i }).click();
177+
178+
// Wait for success toast
179+
await expect(page.getByTestId('toast-success')).toBeVisible({ timeout: 5000 });
180+
const toastText = await page.getByTestId('toast-container').locator('[role="alert"]').first().textContent();
181+
expect(toastText).toContain('Changes saved');
182+
}
183+
});
184+
185+
// Skip in web mode - requires Tauri API
186+
test.skip('should show error toast on failed save', async ({ page }) => {
187+
const dashboard = new DashboardPage(page);
188+
await dashboard.navigate();
189+
190+
await page.locator('nav button:has-text("FeatureSets")').click({ force: true });
191+
192+
// Click on a feature set
193+
const featureSetCard = page.locator('[data-testid^="featureset-card-"]').first();
194+
if (await featureSetCard.isVisible()) {
195+
await featureSetCard.click();
196+
197+
// Simulate network error scenario - panel save should show error toast
198+
await page.route('**/feature-sets/*/members', route => route.abort());
199+
200+
const saveButton = page.getByRole('button', { name: /Save Changes/i });
201+
if (await saveButton.isVisible()) {
202+
await saveButton.click();
203+
204+
await expect(page.getByTestId('toast-error')).toBeVisible({ timeout: 5000 });
205+
const toastText = await page.getByTestId('toast-container').locator('[role="alert"]').first().textContent();
206+
expect(toastText).toContain('Failed to save');
207+
}
208+
}
209+
});
210+
});
211+
147212
test.describe('Config Editor Toast', () => {
148213
// Skip in web mode - requires Tauri API
149214
test.skip('should show toast when saving space configuration', async ({ page }) => {

tests/e2e/specs/registry.spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,56 @@ test.describe('Registry Pagination', () => {
143143
}
144144
});
145145
});
146+
147+
test.describe('Registry Toast Notifications', () => {
148+
test('should have toast container on registry page', async ({ page }) => {
149+
const dashboard = new DashboardPage(page);
150+
const registry = new RegistryPage(page);
151+
await dashboard.navigate();
152+
153+
await page.locator('nav button:has-text("Discover")').click();
154+
await expect(registry.heading).toBeVisible();
155+
156+
await expect(registry.toastContainer).toBeAttached();
157+
});
158+
159+
// Skip in web mode - requires Tauri API for install
160+
test.skip('should show success toast when installing a server', async ({ page }) => {
161+
const dashboard = new DashboardPage(page);
162+
const registry = new RegistryPage(page);
163+
await dashboard.navigate();
164+
165+
await page.locator('nav button:has-text("Discover")').click();
166+
await expect(registry.heading).toBeVisible();
167+
168+
// Find an uninstalled server's install button
169+
const installBtn = page.getByRole('button', { name: /Install/i }).first();
170+
if (await installBtn.isVisible()) {
171+
await installBtn.click();
172+
173+
await registry.waitForToast('success');
174+
const toastText = await registry.getToastText();
175+
expect(toastText).toContain('Server installed');
176+
}
177+
});
178+
179+
// Skip in web mode - requires Tauri API for uninstall
180+
test.skip('should show success toast when uninstalling a server', async ({ page }) => {
181+
const dashboard = new DashboardPage(page);
182+
const registry = new RegistryPage(page);
183+
await dashboard.navigate();
184+
185+
await page.locator('nav button:has-text("Discover")').click();
186+
await expect(registry.heading).toBeVisible();
187+
188+
// Find an installed server's uninstall button
189+
const uninstallBtn = page.getByRole('button', { name: /Uninstall/i }).first();
190+
if (await uninstallBtn.isVisible()) {
191+
await uninstallBtn.click();
192+
193+
await registry.waitForToast('success');
194+
const toastText = await registry.getToastText();
195+
expect(toastText).toContain('Server uninstalled');
196+
}
197+
});
198+
});

tests/e2e/specs/servers.spec.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,70 @@ test.describe('Server Actions', () => {
8989
// If no server cards found, test passes (no servers installed)
9090
});
9191
});
92+
93+
test.describe('Server Toast Notifications', () => {
94+
// Skip in web mode - requires Tauri API for server enable/disable
95+
test.skip('should show success toast on server enable', async ({ page }) => {
96+
const dashboard = new DashboardPage(page);
97+
await dashboard.navigate();
98+
99+
await page.locator('nav button:has-text("My Servers")').click();
100+
101+
const enableBtn = page.getByRole('button', { name: /Enable/i }).first();
102+
if (await enableBtn.isVisible()) {
103+
await enableBtn.click();
104+
105+
await expect(page.getByTestId('toast-success')).toBeVisible({ timeout: 5000 });
106+
}
107+
});
108+
109+
// Skip in web mode - requires Tauri API for log viewer
110+
test.skip('should show toast when clearing server logs', async ({ page }) => {
111+
const dashboard = new DashboardPage(page);
112+
await dashboard.navigate();
113+
114+
await page.locator('nav button:has-text("My Servers")').click();
115+
116+
// Open log viewer for first server (if available)
117+
const logButton = page.getByRole('button', { name: /Logs/i }).first();
118+
if (await logButton.isVisible()) {
119+
await logButton.click();
120+
121+
// Accept confirmation dialog
122+
page.on('dialog', dialog => dialog.accept());
123+
124+
// Click clear logs button
125+
const clearBtn = page.locator('button[title="Clear all logs"]');
126+
if (await clearBtn.isVisible()) {
127+
await clearBtn.click();
128+
129+
await expect(page.getByTestId('toast-success')).toBeVisible({ timeout: 5000 });
130+
const toastText = await page.getByTestId('toast-container').locator('[role="alert"]').first().textContent();
131+
expect(toastText).toContain('Logs cleared');
132+
}
133+
}
134+
});
135+
136+
// Skip in web mode - requires Tauri API for log file path
137+
test.skip('should show toast when copying log file path', async ({ page }) => {
138+
const dashboard = new DashboardPage(page);
139+
await dashboard.navigate();
140+
141+
await page.locator('nav button:has-text("My Servers")').click();
142+
143+
const logButton = page.getByRole('button', { name: /Logs/i }).first();
144+
if (await logButton.isVisible()) {
145+
await logButton.click();
146+
147+
// Click copy path button
148+
const copyBtn = page.locator('button[title="Open log file in external editor"]');
149+
if (await copyBtn.isVisible()) {
150+
await copyBtn.click();
151+
152+
await expect(page.getByTestId('toast-success')).toBeVisible({ timeout: 5000 });
153+
const toastText = await page.getByTestId('toast-container').locator('[role="alert"]').first().textContent();
154+
expect(toastText).toContain('Path copied');
155+
}
156+
}
157+
});
158+
});

0 commit comments

Comments
 (0)