import { test, expect } from '@playwright/test'; import { login } from './helpers/auth'; const API = 'https://fiberops-api.juankibin.space/api/v1'; test.describe('Plans', () => { test.beforeEach(async ({ page }) => { await login(page); await page.goto('/plans'); await page.waitForLoadState('networkidle'); }); test('plans page renders with header and table', async ({ page }) => { await expect(page.locator('h1:has-text("Plans")')).toBeVisible(); await expect(page.locator('table')).toBeVisible(); await expect(page.locator('[data-testid="btn-add-plan"]')).toBeVisible(); }); test('plans list loads rows from API', async ({ page }) => { // Wait for either rows or empty state await page.waitForTimeout(3000); const rows = page.locator('[data-testid="plan-row"]'); const empty = page.locator('text=No plans found'); const count = await rows.count(); const hasEmpty = await empty.isVisible(); // Either rows exist OR empty state is shown — both are valid expect(count > 0 || hasEmpty).toBeTruthy(); }); test('add plan modal opens and submits', async ({ page }) => { // Open create modal await page.click('[data-testid="btn-add-plan"]'); // Modal should appear with form fields await expect(page.locator('[data-testid="input-plan-name"]')).toBeVisible(); await expect(page.locator('[data-testid="select-plan-type"]')).toBeVisible(); await expect(page.locator('[data-testid="input-plan-speed"]')).toBeVisible(); await expect(page.locator('[data-testid="input-plan-price"]')).toBeVisible(); // Fill form const planName = `E2E Test Plan ${Date.now()}`; await page.fill('[data-testid="input-plan-name"]', planName); await page.selectOption('[data-testid="select-plan-type"]', 'PREPAID'); await page.fill('[data-testid="input-plan-speed"]', '25Mbps'); await page.fill('[data-testid="input-plan-price"]', '999'); await page.fill('[data-testid="input-plan-description"]', 'E2E test plan - safe to delete'); // Submit await page.click('[data-testid="btn-submit-create"]'); // Modal should close and success toast or new row appears await page.waitForTimeout(2000); // Modal should be closed const modalInput = page.locator('[data-testid="input-plan-name"]'); await expect(modalInput).not.toBeVisible({ timeout: 5000 }); }); test('edit plan modal opens and submits', async ({ page }) => { // Wait for rows to load await page.waitForTimeout(3000); const editBtns = page.locator('[data-testid="btn-edit-plan"]'); const count = await editBtns.count(); if (count === 0) { // No plans exist yet — create one first via API, then test edit test.skip(); return; } // Click first edit button await editBtns.first().click(); // Edit modal should open await expect(page.locator('[data-testid="input-edit-name"]')).toBeVisible({ timeout: 5000 }); await expect(page.locator('[data-testid="input-edit-speed"]')).toBeVisible(); await expect(page.locator('[data-testid="input-edit-price"]')).toBeVisible(); // Change the speed await page.fill('[data-testid="input-edit-speed"]', '50Mbps'); // Submit await page.click('[data-testid="btn-submit-edit"]'); // Modal should close await page.waitForTimeout(2000); const editModal = page.locator('[data-testid="input-edit-name"]'); await expect(editModal).not.toBeVisible({ timeout: 5000 }); }); test('delete plan shows confirmation dialog', async ({ page }) => { // Wait for rows await page.waitForTimeout(3000); const deleteBtns = page.locator('[data-testid="btn-delete-plan"]'); const count = await deleteBtns.count(); if (count === 0) { test.skip(); return; } // Click first delete button await deleteBtns.first().click(); // Confirmation modal should appear await expect(page.locator('[data-testid="modal-delete-plan"]')).toBeVisible({ timeout: 5000 }); await expect(page.locator('[data-testid="btn-confirm-delete"]')).toBeVisible(); await expect(page.locator('[data-testid="btn-cancel-delete"]')).toBeVisible(); // Cancel the delete (don't actually delete in tests) await page.click('[data-testid="btn-cancel-delete"]'); // Modal should close await page.waitForTimeout(500); const deleteModal = page.locator('[data-testid="modal-delete-plan"]'); await expect(deleteModal).not.toBeVisible({ timeout: 5000 }); }); });