/** * E2E tests for CSV upload and session processing workflow * * Tests the complete data processing pipeline: * 1. CSV import configuration * 2. Data import and validation * 3. Session processing and AI analysis * 4. Dashboard visualization * 5. Data filtering and search */ import { test, expect, type Page } from "@playwright/test"; // Test data const testAdmin = { email: "admin@csvtest.com", password: "AdminTestPassword123!", }; const mockCsvData = `sessionId,userId,language,country,ipAddress,sentiment,messagesSent,startTime,endTime,escalated,forwardedHr,summary session1,user1,en,US,192.168.1.1,positive,5,2024-01-15T10:00:00Z,2024-01-15T10:30:00Z,false,false,User requested vacation time session2,user2,nl,NL,192.168.1.2,neutral,3,2024-01-15T11:00:00Z,2024-01-15T11:20:00Z,true,false,User had login issues session3,user3,de,DE,192.168.1.3,negative,8,2024-01-15T12:00:00Z,2024-01-15T12:45:00Z,false,true,User complained about salary`; // Helper functions async function loginAsAdmin(page: Page) { await page.goto("http://localhost:3000/login"); await page.fill('[data-testid="email"]', testAdmin.email); await page.fill('[data-testid="password"]', testAdmin.password); await page.click('[data-testid="login-button"]'); await expect(page).toHaveURL(/\/dashboard/); } async function waitForDataProcessing(page: Page, timeout = 30000) { // Wait for processing indicators to disappear await page.waitForSelector('[data-testid="processing-indicator"]', { state: "hidden", timeout, }); } async function setupMockCsvEndpoint(page: Page) { // Mock the CSV endpoint to return test data await page.route("**/test-csv-data", (route) => { route.fulfill({ status: 200, contentType: "text/csv", body: mockCsvData, }); }); } test.describe("CSV Processing Workflow", () => { test.beforeEach(async ({ page }) => { // Setup mock CSV endpoint await setupMockCsvEndpoint(page); // Login as admin await loginAsAdmin(page); }); test.describe("CSV Import Configuration", () => { test("should configure CSV import settings", async ({ page }) => { // Navigate to company settings await page.click('[data-testid="nav-company"]'); await expect(page).toHaveURL(/\/dashboard\/company/); // Update CSV configuration await page.fill( '[data-testid="csv-url"]', "http://localhost:3000/api/test-csv-data" ); await page.fill('[data-testid="csv-username"]', "testuser"); await page.fill('[data-testid="csv-password"]', "testpass"); // Save settings await page.click('[data-testid="save-settings"]'); // Should show success message await expect( page.locator('[data-testid="success-message"]') ).toContainText("Settings saved successfully"); }); test("should validate CSV URL format", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/company"); // Enter invalid URL await page.fill('[data-testid="csv-url"]', "invalid-url"); await page.click('[data-testid="save-settings"]'); // Should show validation error await expect(page.locator('[data-testid="csv-url-error"]')).toContainText( "Invalid URL format" ); }); }); test.describe("Manual CSV Import", () => { test.beforeEach(async ({ page }) => { // Configure CSV settings first await page.goto("http://localhost:3000/dashboard/company"); await page.fill( '[data-testid="csv-url"]', "http://localhost:3000/api/test-csv-data" ); await page.click('[data-testid="save-settings"]'); await expect( page.locator('[data-testid="success-message"]') ).toBeVisible(); }); test("should trigger manual CSV import", async ({ page }) => { // Navigate to overview await page.goto("http://localhost:3000/dashboard/overview"); // Trigger manual refresh await page.click('[data-testid="refresh-data-button"]'); // Should show processing indicator await expect( page.locator('[data-testid="processing-indicator"]') ).toBeVisible(); // Wait for processing to complete await waitForDataProcessing(page); // Should show success message await expect( page.locator('[data-testid="import-success"]') ).toContainText("Data imported successfully"); }); test("should display import progress", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/overview"); // Start import await page.click('[data-testid="refresh-data-button"]'); // Check progress indicators await expect( page.locator('[data-testid="import-progress"]') ).toBeVisible(); // Progress should show stages await expect( page.locator('[data-testid="stage-csv-import"]') ).toContainText("CSV Import"); await expect( page.locator('[data-testid="stage-processing"]') ).toContainText("Processing"); await expect( page.locator('[data-testid="stage-ai-analysis"]') ).toContainText("AI Analysis"); }); test("should handle import errors gracefully", async ({ page }) => { // Configure invalid CSV URL await page.goto("http://localhost:3000/dashboard/company"); await page.fill( '[data-testid="csv-url"]', "http://localhost:3000/api/nonexistent-csv" ); await page.click('[data-testid="save-settings"]'); // Try to import await page.goto("http://localhost:3000/dashboard/overview"); await page.click('[data-testid="refresh-data-button"]'); // Should show error message await expect(page.locator('[data-testid="import-error"]')).toContainText( "Failed to fetch CSV data" ); }); }); test.describe("Data Visualization", () => { test.beforeEach(async ({ page }) => { // Import test data first await page.goto("http://localhost:3000/dashboard/company"); await page.fill( '[data-testid="csv-url"]', "http://localhost:3000/api/test-csv-data" ); await page.click('[data-testid="save-settings"]'); await page.goto("http://localhost:3000/dashboard/overview"); await page.click('[data-testid="refresh-data-button"]'); await waitForDataProcessing(page); }); test("should display session metrics correctly", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/overview"); // Check metric cards show correct data await expect( page.locator('[data-testid="total-sessions"]') ).toContainText("3"); // Check sentiment distribution const sentimentChart = page.locator('[data-testid="sentiment-chart"]'); await expect(sentimentChart).toBeVisible(); // Verify sentiment data await expect( page.locator('[data-testid="positive-sentiment"]') ).toContainText("1"); await expect( page.locator('[data-testid="neutral-sentiment"]') ).toContainText("1"); await expect( page.locator('[data-testid="negative-sentiment"]') ).toContainText("1"); }); test("should display geographic distribution", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/overview"); // Check geographic map const geoMap = page.locator('[data-testid="geographic-map"]'); await expect(geoMap).toBeVisible(); // Check country data await expect(page.locator('[data-testid="country-us"]')).toContainText( "US: 1" ); await expect(page.locator('[data-testid="country-nl"]')).toContainText( "NL: 1" ); await expect(page.locator('[data-testid="country-de"]')).toContainText( "DE: 1" ); }); test("should display escalation metrics", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/overview"); // Check escalation rate await expect( page.locator('[data-testid="escalation-rate"]') ).toContainText("33%"); // Check HR forwarding rate await expect( page.locator('[data-testid="hr-forwarding-rate"]') ).toContainText("33%"); }); }); test.describe("Session Management", () => { test.beforeEach(async ({ page }) => { // Import test data await page.goto("http://localhost:3000/dashboard/company"); await page.fill( '[data-testid="csv-url"]', "http://localhost:3000/api/test-csv-data" ); await page.click('[data-testid="save-settings"]'); await page.goto("http://localhost:3000/dashboard/overview"); await page.click('[data-testid="refresh-data-button"]'); await waitForDataProcessing(page); }); test("should display sessions list", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/sessions"); // Should show all sessions await expect(page.locator('[data-testid="session-list"]')).toBeVisible(); await expect(page.locator('[data-testid="session-item"]')).toHaveCount(3); // Check session details const firstSession = page.locator('[data-testid="session-item"]').first(); await expect(firstSession).toContainText("session1"); await expect(firstSession).toContainText("positive"); await expect(firstSession).toContainText("US"); }); test("should filter sessions by sentiment", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/sessions"); // Filter by positive sentiment await page.selectOption('[data-testid="sentiment-filter"]', "POSITIVE"); // Should show only positive sessions await expect(page.locator('[data-testid="session-item"]')).toHaveCount(1); await expect(page.locator('[data-testid="session-item"]')).toContainText( "session1" ); }); test("should filter sessions by country", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/sessions"); // Filter by Germany await page.selectOption('[data-testid="country-filter"]', "DE"); // Should show only German sessions await expect(page.locator('[data-testid="session-item"]')).toHaveCount(1); await expect(page.locator('[data-testid="session-item"]')).toContainText( "session3" ); }); test("should search sessions by content", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/sessions"); // Search for "vacation" await page.fill('[data-testid="search-input"]', "vacation"); // Should show matching sessions await expect(page.locator('[data-testid="session-item"]')).toHaveCount(1); await expect(page.locator('[data-testid="session-item"]')).toContainText( "vacation time" ); }); test("should paginate sessions", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/sessions"); // Set small page size await page.selectOption('[data-testid="page-size"]', "2"); // Should show pagination await expect(page.locator('[data-testid="pagination"]')).toBeVisible(); await expect(page.locator('[data-testid="session-item"]')).toHaveCount(2); // Go to next page await page.click('[data-testid="next-page"]'); await expect(page.locator('[data-testid="session-item"]')).toHaveCount(1); }); }); test.describe("Session Details", () => { test.beforeEach(async ({ page }) => { // Import test data await page.goto("http://localhost:3000/dashboard/company"); await page.fill( '[data-testid="csv-url"]', "http://localhost:3000/api/test-csv-data" ); await page.click('[data-testid="save-settings"]'); await page.goto("http://localhost:3000/dashboard/overview"); await page.click('[data-testid="refresh-data-button"]'); await waitForDataProcessing(page); }); test("should view individual session details", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/sessions"); // Click on first session await page.click('[data-testid="session-item"]'); // Should navigate to session detail page await expect(page).toHaveURL(/\/dashboard\/sessions\/[^/]+/); // Check session details await expect(page.locator('[data-testid="session-id"]')).toContainText( "session1" ); await expect( page.locator('[data-testid="sentiment-badge"]') ).toContainText("positive"); await expect(page.locator('[data-testid="country-badge"]')).toContainText( "US" ); await expect( page.locator('[data-testid="session-summary"]') ).toContainText("vacation time"); }); test("should display session timeline", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/sessions"); await page.click('[data-testid="session-item"]'); // Check timeline const timeline = page.locator('[data-testid="session-timeline"]'); await expect(timeline).toBeVisible(); // Should show start and end times await expect(page.locator('[data-testid="start-time"]')).toContainText( "10:00" ); await expect(page.locator('[data-testid="end-time"]')).toContainText( "10:30" ); await expect(page.locator('[data-testid="duration"]')).toContainText( "30 minutes" ); }); test("should display extracted questions", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/sessions"); await page.click('[data-testid="session-item"]'); // Check questions section const questionsSection = page.locator( '[data-testid="extracted-questions"]' ); await expect(questionsSection).toBeVisible(); // Should show AI-extracted questions (if any) const questionsList = page.locator('[data-testid="questions-list"]'); if (await questionsList.isVisible()) { await expect( questionsList.locator('[data-testid="question-item"]') ).toHaveCount.greaterThan(0); } }); }); test.describe("Real-time Updates", () => { test("should show real-time processing status", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/overview"); // Configure CSV await page.goto("http://localhost:3000/dashboard/company"); await page.fill( '[data-testid="csv-url"]', "http://localhost:3000/api/test-csv-data" ); await page.click('[data-testid="save-settings"]'); // Start import and monitor status await page.goto("http://localhost:3000/dashboard/overview"); await page.click('[data-testid="refresh-data-button"]'); // Should show real-time status updates await expect( page.locator('[data-testid="status-importing"]') ).toBeVisible(); // Status should progress through stages await page.waitForSelector('[data-testid="status-processing"]', { timeout: 10000, }); await page.waitForSelector('[data-testid="status-analyzing"]', { timeout: 10000, }); await page.waitForSelector('[data-testid="status-complete"]', { timeout: 30000, }); }); test("should update metrics in real-time", async ({ page }) => { await page.goto("http://localhost:3000/dashboard/overview"); // Get initial metrics const initialSessions = await page .locator('[data-testid="total-sessions"]') .textContent(); // Import data await page.goto("http://localhost:3000/dashboard/company"); await page.fill( '[data-testid="csv-url"]', "http://localhost:3000/api/test-csv-data" ); await page.click('[data-testid="save-settings"]'); await page.goto("http://localhost:3000/dashboard/overview"); await page.click('[data-testid="refresh-data-button"]'); await waitForDataProcessing(page); // Metrics should be updated const updatedSessions = await page .locator('[data-testid="total-sessions"]') .textContent(); expect(updatedSessions).not.toBe(initialSessions); }); }); test.describe("Error Handling", () => { test("should handle CSV parsing errors", async ({ page }) => { // Mock invalid CSV data await page.route("**/invalid-csv", (route) => { route.fulfill({ status: 200, contentType: "text/csv", body: "invalid,csv,format\nwithout,proper,headers", }); }); await page.goto("http://localhost:3000/dashboard/company"); await page.fill( '[data-testid="csv-url"]', "http://localhost:3000/api/invalid-csv" ); await page.click('[data-testid="save-settings"]'); await page.goto("http://localhost:3000/dashboard/overview"); await page.click('[data-testid="refresh-data-button"]'); // Should show parsing error await expect(page.locator('[data-testid="parsing-error"]')).toContainText( "Invalid CSV format" ); }); test("should handle AI processing failures", async ({ page }) => { // Mock AI service failure await page.route("**/api/openai/**", (route) => { route.fulfill({ status: 500, body: JSON.stringify({ error: "AI service unavailable" }), }); }); await page.goto("http://localhost:3000/dashboard/company"); await page.fill( '[data-testid="csv-url"]', "http://localhost:3000/api/test-csv-data" ); await page.click('[data-testid="save-settings"]'); await page.goto("http://localhost:3000/dashboard/overview"); await page.click('[data-testid="refresh-data-button"]'); // Should show AI processing error await expect(page.locator('[data-testid="ai-error"]')).toContainText( "AI analysis failed" ); }); test("should retry failed operations", async ({ page }) => { let attemptCount = 0; // Mock failing then succeeding API await page.route("**/api/process-batch", (route) => { attemptCount++; if (attemptCount === 1) { route.fulfill({ status: 500, body: "Server error" }); } else { route.fulfill({ status: 200, body: JSON.stringify({ success: true }), }); } }); await page.goto("http://localhost:3000/dashboard/overview"); await page.click('[data-testid="refresh-data-button"]'); // Should show retry attempt await expect( page.locator('[data-testid="retry-indicator"]') ).toBeVisible(); // Should eventually succeed await waitForDataProcessing(page); await expect( page.locator('[data-testid="import-success"]') ).toBeVisible(); }); }); });