Skip to content

Commit 286b3ff

Browse files
committed
new frontend e2e tests
1 parent 86a01a4 commit 286b3ff

File tree

9 files changed

+2335
-0
lines changed

9 files changed

+2335
-0
lines changed

frontend/e2e/admin-events.spec.ts

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
import { test, expect, type Page } from '@playwright/test';
2+
3+
async function loginAsAdmin(page: Page) {
4+
await page.context().clearCookies();
5+
await page.goto('/login');
6+
await page.evaluate(() => {
7+
localStorage.clear();
8+
sessionStorage.clear();
9+
});
10+
await page.waitForSelector('#username');
11+
await page.fill('#username', 'admin');
12+
await page.fill('#password', 'admin123');
13+
await page.click('button[type="submit"]');
14+
await expect(page.getByRole('heading', { name: 'Code Editor' })).toBeVisible({ timeout: 10000 });
15+
}
16+
17+
async function navigateToAdminEvents(page: Page) {
18+
await page.goto('/admin/events');
19+
await expect(page.getByRole('heading', { name: 'Event Browser' })).toBeVisible({ timeout: 10000 });
20+
}
21+
22+
test.describe('Admin Events Page', () => {
23+
test.beforeEach(async ({ page }) => {
24+
await loginAsAdmin(page);
25+
await navigateToAdminEvents(page);
26+
});
27+
28+
test('displays event browser page with header', async ({ page }) => {
29+
await expect(page.getByRole('heading', { name: 'Event Browser' })).toBeVisible();
30+
await expect(page.getByText('Monitor and replay system events')).toBeVisible();
31+
});
32+
33+
test('shows admin sidebar navigation', async ({ page }) => {
34+
await expect(page.getByText('Admin Panel')).toBeVisible();
35+
await expect(page.getByRole('link', { name: 'Event Browser' })).toBeVisible();
36+
await expect(page.getByRole('link', { name: 'Sagas' })).toBeVisible();
37+
await expect(page.getByRole('link', { name: 'Users' })).toBeVisible();
38+
await expect(page.getByRole('link', { name: 'Settings' })).toBeVisible();
39+
});
40+
41+
test('event browser link is active in sidebar', async ({ page }) => {
42+
const eventBrowserLink = page.getByRole('link', { name: 'Event Browser' });
43+
await expect(eventBrowserLink).toHaveClass(/bg-primary/);
44+
});
45+
46+
test('shows action buttons', async ({ page }) => {
47+
await expect(page.getByRole('button', { name: /Filters/i })).toBeVisible();
48+
await expect(page.getByRole('button', { name: /Export/i })).toBeVisible();
49+
await expect(page.getByRole('button', { name: /Refresh/i })).toBeVisible();
50+
});
51+
52+
test('shows auto-refresh control', async ({ page }) => {
53+
await expect(page.getByText(/Auto-refresh/i)).toBeVisible();
54+
});
55+
});
56+
57+
test.describe('Admin Events Stats Cards', () => {
58+
test.beforeEach(async ({ page }) => {
59+
await loginAsAdmin(page);
60+
await navigateToAdminEvents(page);
61+
});
62+
63+
test('shows event statistics cards', async ({ page }) => {
64+
const statsSection = page.locator('[class*="grid"]').filter({ hasText: /Total|Events/i }).first();
65+
const isVisible = await statsSection.isVisible({ timeout: 5000 }).catch(() => false);
66+
67+
if (isVisible) {
68+
await expect(page.getByText(/Total/i).first()).toBeVisible();
69+
}
70+
});
71+
});
72+
73+
test.describe('Admin Events Filtering', () => {
74+
test.beforeEach(async ({ page }) => {
75+
await loginAsAdmin(page);
76+
await navigateToAdminEvents(page);
77+
});
78+
79+
test('can toggle filter panel', async ({ page }) => {
80+
const filterButton = page.getByRole('button', { name: /Filters/i });
81+
await filterButton.click();
82+
83+
await page.waitForTimeout(500);
84+
85+
const filterPanel = page.locator('[class*="filter"], [class*="panel"]').filter({ hasText: /Event Type|From|To/i });
86+
const isExpanded = await filterPanel.first().isVisible({ timeout: 2000 }).catch(() => false);
87+
88+
if (isExpanded) {
89+
await filterButton.click();
90+
await page.waitForTimeout(300);
91+
}
92+
});
93+
94+
test('filter panel shows date range inputs', async ({ page }) => {
95+
await page.getByRole('button', { name: /Filters/i }).click();
96+
await page.waitForTimeout(500);
97+
98+
const fromInput = page.locator('input[type="datetime-local"], input[type="date"]').first();
99+
const isVisible = await fromInput.isVisible({ timeout: 2000 }).catch(() => false);
100+
101+
if (isVisible) {
102+
await expect(fromInput).toBeVisible();
103+
}
104+
});
105+
106+
test('filter panel shows event type selector', async ({ page }) => {
107+
await page.getByRole('button', { name: /Filters/i }).click();
108+
await page.waitForTimeout(500);
109+
110+
const eventTypeSelector = page.locator('select, [class*="select"]').filter({ hasText: /event_type|All Types/i }).first();
111+
const isVisible = await eventTypeSelector.isVisible({ timeout: 2000 }).catch(() => false);
112+
113+
if (isVisible) {
114+
await expect(eventTypeSelector).toBeVisible();
115+
}
116+
});
117+
118+
test('shows active filter count badge', async ({ page }) => {
119+
await page.getByRole('button', { name: /Filters/i }).click();
120+
await page.waitForTimeout(500);
121+
122+
const eventTypeSelect = page.locator('select').first();
123+
if (await eventTypeSelect.isVisible({ timeout: 2000 }).catch(() => false)) {
124+
const options = await eventTypeSelect.locator('option').all();
125+
if (options.length > 1) {
126+
await eventTypeSelect.selectOption({ index: 1 });
127+
}
128+
}
129+
130+
const applyButton = page.getByRole('button', { name: /Apply/i });
131+
if (await applyButton.isVisible({ timeout: 2000 }).catch(() => false)) {
132+
await applyButton.click();
133+
}
134+
});
135+
});
136+
137+
test.describe('Admin Events Export', () => {
138+
test.beforeEach(async ({ page }) => {
139+
await loginAsAdmin(page);
140+
await navigateToAdminEvents(page);
141+
});
142+
143+
test('can open export dropdown', async ({ page }) => {
144+
await page.getByRole('button', { name: /Export/i }).click();
145+
146+
await expect(page.getByText('CSV')).toBeVisible();
147+
await expect(page.getByText('JSON')).toBeVisible();
148+
});
149+
150+
test('export dropdown has CSV option', async ({ page }) => {
151+
await page.getByRole('button', { name: /Export/i }).click();
152+
153+
const csvOption = page.getByText('CSV');
154+
await expect(csvOption).toBeVisible();
155+
});
156+
157+
test('export dropdown has JSON option', async ({ page }) => {
158+
await page.getByRole('button', { name: /Export/i }).click();
159+
160+
const jsonOption = page.getByText('JSON');
161+
await expect(jsonOption).toBeVisible();
162+
});
163+
});
164+
165+
test.describe('Admin Events Table', () => {
166+
test.beforeEach(async ({ page }) => {
167+
await loginAsAdmin(page);
168+
await navigateToAdminEvents(page);
169+
});
170+
171+
test('shows events table or empty state', async ({ page }) => {
172+
await page.waitForTimeout(2000);
173+
174+
const table = page.locator('table').first();
175+
const emptyState = page.getByText(/No events found/i);
176+
const loadingState = page.getByText(/Loading/i);
177+
178+
const hasTable = await table.isVisible({ timeout: 3000 }).catch(() => false);
179+
const hasEmpty = await emptyState.isVisible({ timeout: 3000 }).catch(() => false);
180+
const isLoading = await loadingState.isVisible({ timeout: 1000 }).catch(() => false);
181+
182+
expect(hasTable || hasEmpty || isLoading).toBe(true);
183+
});
184+
185+
test('events table shows time column', async ({ page }) => {
186+
await page.waitForTimeout(2000);
187+
188+
const timeHeader = page.getByText('Time');
189+
const isVisible = await timeHeader.isVisible({ timeout: 3000 }).catch(() => false);
190+
191+
if (isVisible) {
192+
await expect(timeHeader).toBeVisible();
193+
}
194+
});
195+
196+
test('events table shows type column', async ({ page }) => {
197+
await page.waitForTimeout(2000);
198+
199+
const typeHeader = page.getByText('Type').first();
200+
const isVisible = await typeHeader.isVisible({ timeout: 3000 }).catch(() => false);
201+
202+
if (isVisible) {
203+
await expect(typeHeader).toBeVisible();
204+
}
205+
});
206+
207+
test('events table shows actions column', async ({ page }) => {
208+
await page.waitForTimeout(2000);
209+
210+
const actionsHeader = page.getByText('Actions');
211+
const isVisible = await actionsHeader.isVisible({ timeout: 3000 }).catch(() => false);
212+
213+
if (isVisible) {
214+
await expect(actionsHeader).toBeVisible();
215+
}
216+
});
217+
218+
test('event rows are clickable', async ({ page }) => {
219+
await page.waitForTimeout(2000);
220+
221+
const eventRow = page.locator('tr[role="button"], [role="button"][aria-label*="event"]').first();
222+
const isVisible = await eventRow.isVisible({ timeout: 3000 }).catch(() => false);
223+
224+
if (isVisible) {
225+
await expect(eventRow).toHaveAttribute('tabindex', '0');
226+
}
227+
});
228+
});
229+
230+
test.describe('Admin Events Detail Modal', () => {
231+
test.beforeEach(async ({ page }) => {
232+
await loginAsAdmin(page);
233+
await navigateToAdminEvents(page);
234+
});
235+
236+
test('can view event details by clicking row', async ({ page }) => {
237+
await page.waitForTimeout(2000);
238+
239+
const eventRow = page.locator('tr[role="button"], [role="button"][aria-label*="event"]').first();
240+
const isVisible = await eventRow.isVisible({ timeout: 3000 }).catch(() => false);
241+
242+
if (isVisible) {
243+
await eventRow.click();
244+
await page.waitForTimeout(1000);
245+
}
246+
});
247+
});
248+
249+
test.describe('Admin Events Replay', () => {
250+
test.beforeEach(async ({ page }) => {
251+
await loginAsAdmin(page);
252+
await navigateToAdminEvents(page);
253+
});
254+
255+
test('preview replay button exists in event actions', async ({ page }) => {
256+
await page.waitForTimeout(2000);
257+
258+
const previewButton = page.locator('button[title="Preview replay"]').first();
259+
const isVisible = await previewButton.isVisible({ timeout: 3000 }).catch(() => false);
260+
261+
if (isVisible) {
262+
await expect(previewButton).toBeVisible();
263+
}
264+
});
265+
266+
test('replay button exists in event actions', async ({ page }) => {
267+
await page.waitForTimeout(2000);
268+
269+
const replayButton = page.locator('button[title="Replay"]').first();
270+
const isVisible = await replayButton.isVisible({ timeout: 3000 }).catch(() => false);
271+
272+
if (isVisible) {
273+
await expect(replayButton).toBeVisible();
274+
}
275+
});
276+
});
277+
278+
test.describe('Admin Events Auto-Refresh', () => {
279+
test.beforeEach(async ({ page }) => {
280+
await loginAsAdmin(page);
281+
await navigateToAdminEvents(page);
282+
});
283+
284+
test('auto-refresh control is visible', async ({ page }) => {
285+
await expect(page.getByText(/Auto-refresh/i)).toBeVisible();
286+
});
287+
288+
test('can toggle auto-refresh', async ({ page }) => {
289+
const autoRefreshToggle = page.locator('input[type="checkbox"]').first();
290+
const isVisible = await autoRefreshToggle.isVisible({ timeout: 3000 }).catch(() => false);
291+
292+
if (isVisible) {
293+
const initialState = await autoRefreshToggle.isChecked();
294+
await autoRefreshToggle.click();
295+
const newState = await autoRefreshToggle.isChecked();
296+
expect(newState).toBe(!initialState);
297+
}
298+
});
299+
300+
test('can manually refresh events', async ({ page }) => {
301+
const refreshButton = page.getByRole('button', { name: /Refresh/i });
302+
await expect(refreshButton).toBeVisible();
303+
await refreshButton.click();
304+
305+
await page.waitForTimeout(500);
306+
});
307+
});
308+
309+
test.describe('Admin Events Pagination', () => {
310+
test.beforeEach(async ({ page }) => {
311+
await loginAsAdmin(page);
312+
await navigateToAdminEvents(page);
313+
});
314+
315+
test('shows pagination when events exist', async ({ page }) => {
316+
await page.waitForTimeout(2000);
317+
318+
const pagination = page.locator('text=/of|Page|Showing/').first();
319+
const isVisible = await pagination.isVisible({ timeout: 3000 }).catch(() => false);
320+
321+
if (isVisible) {
322+
await expect(pagination).toBeVisible();
323+
}
324+
});
325+
});
326+
327+
test.describe('Admin Events Access Control', () => {
328+
test('redirects non-admin users', async ({ page }) => {
329+
await page.context().clearCookies();
330+
await page.goto('/login');
331+
await page.evaluate(() => {
332+
localStorage.clear();
333+
sessionStorage.clear();
334+
});
335+
await page.waitForSelector('#username');
336+
await page.fill('#username', 'user');
337+
await page.fill('#password', 'user123');
338+
await page.click('button[type="submit"]');
339+
await expect(page.getByRole('heading', { name: 'Code Editor' })).toBeVisible({ timeout: 10000 });
340+
341+
await page.goto('/admin/events');
342+
343+
await expect(page).toHaveURL(/^\/$|\/login/);
344+
});
345+
346+
test('redirects unauthenticated users to login', async ({ page }) => {
347+
await page.context().clearCookies();
348+
await page.goto('/login');
349+
await page.evaluate(() => {
350+
localStorage.clear();
351+
sessionStorage.clear();
352+
});
353+
354+
await page.goto('/admin/events');
355+
356+
await expect(page).toHaveURL(/\/login/);
357+
});
358+
});

0 commit comments

Comments
 (0)