feat: add frontend logging system with router and visibility tracking
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user