201 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { test, expect } from '@playwright/test';
 | |
| 
 | |
| test.describe('Frontend Logging System', () => {
 | |
|   let logRequests: any[] = [];
 | |
| 
 | |
|   test.beforeEach(async ({ page }) => {
 | |
|     logRequests = [];
 | |
| 
 | |
|     // Intercept log requests
 | |
|     await page.route('**/api/log', async (route) => {
 | |
|       const request = route.request();
 | |
|       const postData = request.postDataJSON();
 | |
|       
 | |
|       // Store the log data
 | |
|       if (Array.isArray(postData)) {
 | |
|         logRequests.push(...postData);
 | |
|       } else {
 | |
|         logRequests.push(postData);
 | |
|       }
 | |
| 
 | |
|       // Respond with success
 | |
|       await route.fulfill({
 | |
|         status: 200,
 | |
|         contentType: 'application/json',
 | |
|         body: JSON.stringify({ ok: true }),
 | |
|       });
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   test('should log APP_START on page load', async ({ page }) => {
 | |
|     await page.goto('/');
 | |
|     
 | |
|     // Wait for logs to be sent
 | |
|     await page.waitForTimeout(3000);
 | |
| 
 | |
|     const appStartLogs = logRequests.filter(log => log.event === 'APP_START');
 | |
|     expect(appStartLogs.length).toBeGreaterThan(0);
 | |
| 
 | |
|     const log = appStartLogs[0];
 | |
|     expect(log.app).toBe('ObsiViewer');
 | |
|     expect(log.sessionId).toBeTruthy();
 | |
|     expect(log.context).toBeDefined();
 | |
|   });
 | |
| 
 | |
|   test('should log SEARCH_EXECUTED on search', async ({ page }) => {
 | |
|     await page.goto('/');
 | |
|     
 | |
|     // Find search input and perform search
 | |
|     const searchInput = page.locator('input[type="search"], input[placeholder*="search" i]').first();
 | |
|     await searchInput.fill('test query');
 | |
|     await searchInput.press('Enter');
 | |
|     
 | |
|     // Wait for logs
 | |
|     await page.waitForTimeout(3000);
 | |
| 
 | |
|     const searchLogs = logRequests.filter(log => log.event === 'SEARCH_EXECUTED');
 | |
|     expect(searchLogs.length).toBeGreaterThan(0);
 | |
| 
 | |
|     const log = searchLogs[0];
 | |
|     expect(log.data.query).toBe('test query');
 | |
|   });
 | |
| 
 | |
|   test('should log BOOKMARKS_OPEN when opening bookmarks', async ({ page }) => {
 | |
|     await page.goto('/');
 | |
|     
 | |
|     // Click bookmarks button/tab
 | |
|     const bookmarksButton = page.locator('button:has-text("Bookmarks"), [data-testid="bookmarks-tab"]').first();
 | |
|     if (await bookmarksButton.isVisible()) {
 | |
|       await bookmarksButton.click();
 | |
|       
 | |
|       // Wait for logs
 | |
|       await page.waitForTimeout(3000);
 | |
| 
 | |
|       const bookmarksLogs = logRequests.filter(log => log.event === 'BOOKMARKS_OPEN');
 | |
|       expect(bookmarksLogs.length).toBeGreaterThan(0);
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   test('should log GRAPH_VIEW_OPEN when opening graph view', async ({ page }) => {
 | |
|     await page.goto('/');
 | |
|     
 | |
|     // Click graph view button/tab
 | |
|     const graphButton = page.locator('button:has-text("Graph"), [data-testid="graph-tab"]').first();
 | |
|     if (await graphButton.isVisible()) {
 | |
|       await graphButton.click();
 | |
|       
 | |
|       // Wait for logs
 | |
|       await page.waitForTimeout(3000);
 | |
| 
 | |
|       const graphLogs = logRequests.filter(log => log.event === 'GRAPH_VIEW_OPEN');
 | |
|       expect(graphLogs.length).toBeGreaterThan(0);
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   test('should log THEME_CHANGE when toggling theme', async ({ page }) => {
 | |
|     await page.goto('/');
 | |
|     
 | |
|     // Find and click theme toggle button
 | |
|     const themeButton = page.locator('button[aria-label*="theme" i], button:has-text("Theme")').first();
 | |
|     if (await themeButton.isVisible()) {
 | |
|       await themeButton.click();
 | |
|       
 | |
|       // Wait for logs
 | |
|       await page.waitForTimeout(3000);
 | |
| 
 | |
|       const themeLogs = logRequests.filter(log => log.event === 'THEME_CHANGE');
 | |
|       expect(themeLogs.length).toBeGreaterThan(0);
 | |
| 
 | |
|       const log = themeLogs[0];
 | |
|       expect(log.data.from).toBeDefined();
 | |
|       expect(log.data.to).toBeDefined();
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   test('should include session ID in all logs', async ({ page }) => {
 | |
|     await page.goto('/');
 | |
|     
 | |
|     // Perform multiple actions
 | |
|     const searchInput = page.locator('input[type="search"]').first();
 | |
|     if (await searchInput.isVisible()) {
 | |
|       await searchInput.fill('test');
 | |
|       await searchInput.press('Enter');
 | |
|     }
 | |
|     
 | |
|     // Wait for logs
 | |
|     await page.waitForTimeout(3000);
 | |
| 
 | |
|     expect(logRequests.length).toBeGreaterThan(0);
 | |
| 
 | |
|     const sessionIds = new Set(logRequests.map(log => log.sessionId));
 | |
|     expect(sessionIds.size).toBe(1); // All logs should have same session ID
 | |
|   });
 | |
| 
 | |
|   test('should include context in all logs', async ({ page }) => {
 | |
|     await page.goto('/');
 | |
|     
 | |
|     // Wait for logs
 | |
|     await page.waitForTimeout(3000);
 | |
| 
 | |
|     expect(logRequests.length).toBeGreaterThan(0);
 | |
| 
 | |
|     logRequests.forEach(log => {
 | |
|       expect(log.context).toBeDefined();
 | |
|       expect(log.context.version).toBeDefined();
 | |
|       expect(log.context.route).toBeDefined();
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   test('should batch logs when multiple events occur', async ({ page }) => {
 | |
|     await page.goto('/');
 | |
|     
 | |
|     // Perform multiple quick actions
 | |
|     const searchInput = page.locator('input[type="search"]').first();
 | |
|     if (await searchInput.isVisible()) {
 | |
|       await searchInput.fill('test1');
 | |
|       await searchInput.press('Enter');
 | |
|       await page.waitForTimeout(100);
 | |
|       
 | |
|       await searchInput.fill('test2');
 | |
|       await searchInput.press('Enter');
 | |
|       await page.waitForTimeout(100);
 | |
|       
 | |
|       await searchInput.fill('test3');
 | |
|       await searchInput.press('Enter');
 | |
|     }
 | |
|     
 | |
|     // Wait for batched logs
 | |
|     await page.waitForTimeout(3000);
 | |
| 
 | |
|     // Should have received logs (possibly batched)
 | |
|     expect(logRequests.length).toBeGreaterThan(0);
 | |
|   });
 | |
| 
 | |
|   test('should handle offline scenario', async ({ page, context }) => {
 | |
|     await page.goto('/');
 | |
|     
 | |
|     // Go offline
 | |
|     await context.setOffline(true);
 | |
|     
 | |
|     // Perform actions while offline
 | |
|     const searchInput = page.locator('input[type="search"]').first();
 | |
|     if (await searchInput.isVisible()) {
 | |
|       await searchInput.fill('offline test');
 | |
|       await searchInput.press('Enter');
 | |
|     }
 | |
|     
 | |
|     await page.waitForTimeout(1000);
 | |
|     
 | |
|     const offlineLogCount = logRequests.length;
 | |
|     
 | |
|     // Go back online
 | |
|     await context.setOffline(false);
 | |
|     
 | |
|     // Wait for queued logs to be sent
 | |
|     await page.waitForTimeout(5000);
 | |
|     
 | |
|     // Should have received more logs after going online
 | |
|     expect(logRequests.length).toBeGreaterThanOrEqual(offlineLogCount);
 | |
|   });
 | |
| });
 |