/** * E2E Test: Scenario CRUD Operations * * Tests for: * - Create new scenario * - Edit scenario * - Delete scenario * - Verify scenario appears in list */ import { test, expect } from '@playwright/test'; import { navigateTo, waitForLoading, waitForTableData, generateTestScenarioName, createScenarioViaAPI, deleteScenarioViaAPI, } from './utils/test-helpers'; import { newScenarioData, updatedScenarioData } from './fixtures/test-scenarios'; // Test data with unique names to avoid conflicts const testScenarioName = generateTestScenarioName('CRUD Test'); const updatedName = generateTestScenarioName('CRUD Updated'); // Store created scenario ID for cleanup let createdScenarioId: string | null = null; test.describe('Scenario CRUD Operations', () => { test.beforeEach(async ({ page }) => { // Navigate to scenarios page before each test await navigateTo(page, '/scenarios'); await waitForLoading(page); }); test.afterEach(async ({ request }) => { // Cleanup: Delete test scenario if it was created if (createdScenarioId) { await deleteScenarioViaAPI(request, createdScenarioId); createdScenarioId = null; } }); test('should display scenarios list', async ({ page }) => { // Verify page header await expect(page.getByRole('heading', { name: 'Scenarios' })).toBeVisible(); await expect(page.getByText('Manage your AWS cost simulation scenarios')).toBeVisible(); // Verify table headers await expect(page.getByRole('columnheader', { name: 'Name' })).toBeVisible(); await expect(page.getByRole('columnheader', { name: 'Status' })).toBeVisible(); await expect(page.getByRole('columnheader', { name: 'Region' })).toBeVisible(); await expect(page.getByRole('columnheader', { name: 'Requests' })).toBeVisible(); await expect(page.getByRole('columnheader', { name: 'Cost' })).toBeVisible(); }); test('should navigate to scenario detail when clicking a row', async ({ page, request }) => { // Create a test scenario via API const scenario = await createScenarioViaAPI(request, { ...newScenarioData, name: testScenarioName, }); createdScenarioId = scenario.id; // Refresh the page to show new scenario await page.reload(); await waitForLoading(page); // Find and click on the scenario row const scenarioRow = page.locator('table tbody tr').filter({ hasText: testScenarioName }); await expect(scenarioRow).toBeVisible(); await scenarioRow.click(); // Verify navigation to detail page await expect(page).toHaveURL(new RegExp(`/scenarios/${scenario.id}`)); await expect(page.getByRole('heading', { name: testScenarioName })).toBeVisible(); }); test('should show scenario status badges correctly', async ({ page, request }) => { // Create scenarios with different statuses const draftScenario = await createScenarioViaAPI(request, { ...newScenarioData, name: `${testScenarioName} - Draft`, }); createdScenarioId = draftScenario.id; await page.reload(); await waitForLoading(page); // Verify status badge is visible const draftRow = page.locator('table tbody tr').filter({ hasText: `${testScenarioName} - Draft` }); await expect(draftRow.locator('span', { hasText: 'draft' })).toBeVisible(); }); test('should show scenario actions dropdown', async ({ page, request }) => { // Create a test scenario const scenario = await createScenarioViaAPI(request, { ...newScenarioData, name: `${testScenarioName} - Actions`, }); createdScenarioId = scenario.id; await page.reload(); await waitForLoading(page); // Find the scenario row const scenarioRow = page.locator('table tbody tr').filter({ hasText: `${testScenarioName} - Actions` }); // Click on actions dropdown const actionsButton = scenarioRow.locator('button').first(); await actionsButton.click(); // Verify dropdown menu appears with expected actions const dropdown = page.locator('[role="menu"]'); await expect(dropdown).toBeVisible(); // For draft scenarios, should show Start action await expect(dropdown.getByRole('menuitem', { name: /start/i })).toBeVisible(); await expect(dropdown.getByRole('menuitem', { name: /delete/i })).toBeVisible(); }); test('should display correct scenario metrics in table', async ({ page, request }) => { // Create a scenario with specific region const scenario = await createScenarioViaAPI(request, { ...newScenarioData, name: `${testScenarioName} - Metrics`, region: 'eu-west-1', }); createdScenarioId = scenario.id; await page.reload(); await waitForLoading(page); // Verify row displays correct data const scenarioRow = page.locator('table tbody tr').filter({ hasText: `${testScenarioName} - Metrics` }); await expect(scenarioRow).toContainText('eu-west-1'); await expect(scenarioRow).toContainText('0'); // initial requests await expect(scenarioRow).toContainText('$0.000000'); // initial cost }); test('should handle empty scenarios list gracefully', async ({ page }) => { // The test scenarios list might be empty or have items // This test verifies the table structure is always present const table = page.locator('table'); await expect(table).toBeVisible(); // Verify header row is always present const headerRow = table.locator('thead tr'); await expect(headerRow).toBeVisible(); }); test('should navigate from sidebar to scenarios page', async ({ page }) => { // Start from dashboard await navigateTo(page, '/'); await waitForLoading(page); // Click Scenarios in sidebar const scenariosLink = page.locator('nav').getByRole('link', { name: 'Scenarios' }); await scenariosLink.click(); // Verify navigation await expect(page).toHaveURL('/scenarios'); await expect(page.getByRole('heading', { name: 'Scenarios' })).toBeVisible(); }); }); test.describe('Scenario CRUD - Detail Page', () => { test('should display scenario detail with metrics', async ({ page, request }) => { // Create a test scenario const scenario = await createScenarioViaAPI(request, { ...newScenarioData, name: `${testScenarioName} - Detail`, }); createdScenarioId = scenario.id; // Navigate to detail page await navigateTo(page, `/scenarios/${scenario.id}`); await waitForLoading(page); // Verify page structure await expect(page.getByRole('heading', { name: `${testScenarioName} - Detail` })).toBeVisible(); await expect(page.getByText(newScenarioData.description)).toBeVisible(); // Verify metrics cards are displayed await expect(page.getByText('Total Requests')).toBeVisible(); await expect(page.getByText('Total Cost')).toBeVisible(); await expect(page.getByText('SQS Blocks')).toBeVisible(); await expect(page.getByText('LLM Tokens')).toBeVisible(); // Verify status badge await expect(page.locator('span').filter({ hasText: 'draft' }).first()).toBeVisible(); }); test('should show 404 for non-existent scenario', async ({ page }) => { // Navigate to a non-existent scenario await navigateTo(page, '/scenarios/non-existent-id-12345'); await waitForLoading(page); // Should show not found message await expect(page.getByText(/not found/i)).toBeVisible(); }); test('should refresh metrics automatically', async ({ page, request }) => { // Create a test scenario const scenario = await createScenarioViaAPI(request, { ...newScenarioData, name: `${testScenarioName} - Auto Refresh`, }); createdScenarioId = scenario.id; // Navigate to detail page await navigateTo(page, `/scenarios/${scenario.id}`); await waitForLoading(page); // Verify metrics are loaded const totalRequests = page.locator('text=Total Requests').locator('..').locator('text=0'); await expect(totalRequests).toBeVisible(); // Metrics should refresh every 5 seconds (as per useMetrics hook) // We verify the page remains stable await page.waitForTimeout(6000); await expect(page.getByRole('heading', { name: `${testScenarioName} - Auto Refresh` })).toBeVisible(); }); });