import { test, expect } from '@playwright/test'; test.describe('Responsável - Pagamentos', () => { // TODO: Fazer login como responsável // test.beforeEach(async ({ page }) => { // await page.goto('/auth/login'); // await page.getByLabel(/email/i).fill('guardian@teste.com'); // await page.getByLabel(/senha/i).fill('123456'); // await page.getByRole('button', { name: /entrar/i }).click(); // await page.waitForURL(/\/dispatcher|\/dashboard\/guardian/); // }); test.describe('Dashboard do Responsável', () => { test.beforeEach(async ({ page }) => { await page.goto('/dashboard/guardian'); }); test('deve mostrar lista de wards vinculados', async ({ page }) => { await expect(page.getByText(/alunos|wards|meus alunos/i)).toBeVisible(); }); test('deve mostrar resumo financeiro', async ({ page }) => { await expect(page.getByText(/pagamentos|mensalidades|financeiro/i)).toBeVisible(); }); test('deve mostrar total de pendências', async ({ page }) => { const pendingValue = page.getByText(/\d+,\d+|pendência/i); if (await pendingValue.isVisible()) { await expect(pendingValue).toBeVisible(); } }); }); test.describe('Pagamentos por Aluno', () => { test.beforeEach(async ({ page }) => { // TODO: Navegar para página de pagamentos de um ward específico // await page.goto('/dashboard/guardian/ward-id/payments'); await page.goto('/dashboard/guardian'); }); test('deve mostrar lista de obrigações do aluno', async ({ page }) => { await expect(page.getByText(/obrigações|mensalidades/i)).toBeVisible(); await expect(page.locator('table, [data-testid="payment-list"]').first()).toBeVisible(); }); test('deve mostrar valor e vencimento de cada obrigação', async ({ page }) => { const table = page.locator('table').first(); if (await table.isVisible()) { await expect(table.getByText(/valor|amount|r\$/i)).toBeVisible(); await expect(table.getByText(/vencimento|due date/i)).toBeVisible(); } }); test('deve mostrar status da obrigação', async ({ page }) => { const table = page.locator('table').first(); if (await table.isVisible()) { const statusCell = table.locator('tbody tr td').last(); if (await statusCell.isVisible()) { // Status: pending, paid, rejected const label = statusCell.locator('[data-testid="status-label"], .label').first(); if (await label.isVisible()) { const classes = await label.getAttribute('class') || ''; expect(classes).toMatch(/label-(amber|emerald|red|gray)/); } } } }); }); test.describe('Registrar Pagamento', () => { test.beforeEach(async ({ page }) => { // TODO: Navegar para página de pagamentos de um ward await page.goto('/dashboard/guardian/payments/register'); }); test('deve mostrar formulário de registro', async ({ page }) => { await expect(page.getByRole('combobox', { name: /aluno|ward/i })).toBeVisible(); await expect(page.getByLabel(/valor|amount/i)).toBeVisible(); await expect(page.getByRole('combobox', { name: /método|payment method/i })).toBeVisible(); }); test('deve listar wards disponíveis', async ({ page }) => { const wardSelect = page.getByRole('combobox', { name: /aluno|ward/i }); const options = await wardSelect.locator('option').count(); expect(options).toBeGreaterThan(0); }); test('deve permitir selecionar forma de pagamento', async ({ page }) => { const methodSelect = page.getByRole('combobox', { name: /método/i }); const options = ['pix', 'transferência', 'dinheiro', 'cartão']; const selectOptions = await methodSelect.locator('option').all(); for (const option of selectOptions) { const text = await option.textContent(); expect(options.some(o => o.toLowerCase().includes(text.toLowerCase()))).toBe(true); } }); }); test.describe('Upload de Comprovante', () => { test.beforeEach(async ({ page }) => { await page.goto('/dashboard/guardian/payments/register'); }); test('deve ter campo de upload de comprovante', async ({ page }) => { await expect(page.getByLabelText(/comprovante|recibo/i)).toBeVisible(); }); test('deve aceitar arquivo de imagem', async ({ page }) => { const fileInput = page.getByLabelText(/comprovante/i); // await fileInput.setInputFiles('tests/fixtures/recibo.jpg'); await expect(fileInput).toBeVisible(); }); test('deve aceitar arquivo PDF', async ({ page }) => { const fileInput = page.getByLabelText(/comprovante/i); // await fileInput.setInputFiles('tests/fixtures/recibo.pdf'); await expect(fileInput).toBeVisible(); }); test('deve mostrar preview do comprovante após upload', async ({ page }) => { const fileInput = page.getByLabelText(/comprovante/i); // await fileInput.setInputFiles('tests/fixtures/recibo.jpg'); // await page.waitForTimeout(1000); // const preview = page.locator('img[src*="blob:"], .file-upload-preview').first(); // if (await preview.isVisible()) { // await expect(preview).toBeVisible(); // } }); test('deve remover comprovante ao clicar em remover', async ({ page }) => { const fileInput = page.getByLabelText(/comprovante/i); // await fileInput.setInputFiles('tests/fixtures/recibo.jpg'); // await page.waitForTimeout(1000); const removeButton = page.getByRole('button', { name: /remover|x/i }); if (await removeButton.isVisible()) { await removeButton.click(); // Campo deve estar vazio // expect(await fileInput.inputValue()).toBe(''); } }); }); test.describe('Validações de Pagamento', () => { test.beforeEach(async ({ page }) => { await page.goto('/dashboard/guardian/payments/register'); }); test('deve validar valor obrigatório', async ({ page }) => { await page.getByRole('button', { name: /registrar|salvar/i }).click(); await expect(page.getByText(/valor.*obrigatório|amount.*required/i)).toBeVisible(); }); test('deve validar formato de valor (com vírgula)', async ({ page }) => { await page.getByLabel(/valor|amount/i).fill('50,00'); await page.getByRole('button', { name: /registrar|salvar/i }).click(); // Não deve mostrar erro de formato // Ou pode converter para formato correto }); test('deve aceitar valor com ponto', async ({ page }) => { await page.getByLabel(/valor|amount/i).fill('50.00'); await page.getByRole('button', { name: /registrar|salvar/i }).click(); }); test('deve validar aluno obrigatório', async ({ page }) => { await page.getByRole('button', { name: /registrar|salvar/i }).click(); await expect(page.getByText(/aluno.*obrigatório|ward.*required/i)).toBeVisible(); }); }); test.describe('Confirmar Pagamento', () => { test('deve mostrar resumo antes de confirmar', async ({ page }) => { await page.goto('/dashboard/guardian/payments/register'); // Preencher formulário await page.getByRole('combobox', { name: /aluno/i }).selectOption({ index: 0 }); await page.getByLabel(/valor/i).fill('150,00'); await page.getByRole('combobox', { name: /método/i }).selectOption('pix'); await page.getByRole('button', { name: /registrar|salvar/i }).click(); // Pode mostrar modal de confirmação ou resumo const confirmDialog = page.locator('[data-testid="confirm-dialog"]'); if (await confirmDialog.isVisible()) { await expect(confirmDialog.getByText(/confirma|registro|pagamento/i)).toBeVisible(); await expect(page.getByRole('button', { name: /confirmar|sim/i })).toBeVisible(); await expect(page.getByRole('button', { name: /cancelar|não/i })).toBeVisible(); } }); test('deve registrar pagamento com sucesso', async ({ page }) => { await page.goto('/dashboard/guardian/payments/register'); // TODO: Preencher com dados válidos // await page.getByRole('combobox', { name: /aluno/i }).selectOption({ index: 0 }); // await page.getByLabel(/valor/i).fill('150,00'); // await page.getByRole('combobox', { name: /método/i }).selectOption('pix'); // await page.getByLabelText(/comprovante/i).setInputFiles('tests/fixtures/recibo.pdf'); // await page.getByRole('button', { name: /confirmar/i }).click(); // await expect(page.getByText(/pagamento registrado|sucesso/i)).toBeVisible(); // await expect(page).toHaveURL(/\/dashboard\/guardian/, { timeout: 10000 }); }); }); test.describe('Histórico de Pagamentos', () => { test.beforeEach(async ({ page }) => { await page.goto('/dashboard/guardian/payments/history'); }); test('deve mostrar histórico de pagamentos', async ({ page }) => { await expect(page.getByText(/histórico|pagamentos/i)).toBeVisible(); }); test('deve mostrar filtros de status', async ({ page }) => { const statusFilter = page.getByRole('combobox', { name: /status/i }); if (await statusFilter.isVisible()) { await expect(statusFilter).toBeVisible(); } }); test('deve filtrar por status', async ({ page }) => { const statusFilter = page.getByRole('combobox', { name: /status/i }); if (await statusFilter.isVisible()) { await statusFilter.selectOption('pending'); await page.waitForTimeout(500); // Deve mostrar apenas pendentes } }); }); test.describe('Status de Pagamento', () => { test('deve mostrar status "Pendente" em amarelo', async ({ page }) => { const pendingLabel = page.locator('[data-testid="status-label"]').filter({ hasText: /pendente/i }); if (await pendingLabel.isVisible()) { const classes = await pendingLabel.getAttribute('class') || ''; expect(classes).toMatch(/label-amber|text-amber|bg-amber/i); } }); test('deve mostrar status "Aguardando Verificação" em azul', async ({ page }) => { const verificationLabel = page.locator('[data-testid="status-label"]').filter({ hasText: /aguardando/i }); if (await verificationLabel.isVisible()) { const classes = await verificationLabel.getAttribute('class') || ''; expect(classes).toMatch(/label-blue|text-blue|bg-blue/i); } }); test('deve mostrar status "Pago" em verde', async ({ page }) => { const paidLabel = page.locator('[data-testid="status-label"]').filter({ hasText: /pago|verificado/i }); if (await paidLabel.isVisible()) { const classes = await paidLabel.getAttribute('class') || ''; expect(classes).toMatch(/label-emerald|text-emerald|bg-emerald/i); } }); test('deve mostrar status "Rejeitado" em vermelho', async ({ page }) => { const rejectedLabel = page.locator('[data-testid="status-label"]').filter({ hasText: /rejeitado/i }); if (await rejectedLabel.isVisible()) { const classes = await rejectedLabel.getAttribute('class') || ''; expect(classes).toMatch(/label-red|text-red|bg-red/i); } }); }); test.describe('Notificações', () => { test('deve mostrar notificação de pagamento recebido', async ({ page }) => { // TODO: Testar notificação (pode precisar de WebSocket ou polling) // await page.goto('/dashboard/guardian'); // Pode ter ícone de notificação const notificationIcon = page.locator('[data-testid="notification-icon"], [data-testid="notification-badge"]'); if (await notificationIcon.isVisible()) { const badge = notificationIcon.locator('[data-testid="badge"], .badge'); const count = await badge.count(); if (count > 0) { await expect(badge.first()).toContainText(/\d+/); } } }); test('deve mostrar alerta de vencimento próximo', async ({ page }) => { // TODO: Testar alerta de vencimento // await page.goto('/dashboard/guardian'); // Pode ter banner de alerta const alertBanner = page.locator('[data-testid="due-date-alert"]'); if (await alertBanner.isVisible()) { await expect(alertBanner.getByText(/vence em breve|próximo ao vencimento/i)).toBeVisible(); } }); }); test.describe('Recibo de Pagamento', () => { test('deve permitir visualizar comprovante', async ({ page }) => { await page.goto('/dashboard/guardian/payments/history'); const viewReceiptButton = page.getByRole('button', { name: /comprovante|recibo/i }).first(); if (await viewReceiptButton.isVisible()) { await viewReceiptButton.click(); // Deve abrir modal com comprovante await expect(page.locator('.fixed.inset-0')).toBeVisible(); } }); test('deve mostrar comprovante em modal', async ({ page }) => { // TODO: Implementar // await page.getByRole('button', { name: /comprovante/i }).first().click(); // // Deve mostrar imagem ou PDF // const content = page.locator('img[src*="/uploads/"], iframe').first(); // await expect(content).toBeVisible(); }); }); test.describe('Múltiplos Wards', () => { test('deve ter filtro para selecionar ward específico', async ({ page }) => { await page.goto('/dashboard/guardian/payments'); const wardFilter = page.getByRole('combobox', { name: /aluno|ward/i }); if (await wardFilter.isVisible()) { await expect(wardFilter).toBeVisible(); // Deve ter opção "Todos" const allOption = wardFilter.getByRole('option', { name: /todos|all/i }); await expect(allOption).toBeVisible(); } }); test('deve mostrar apenas pagamentos do ward selecionado', async ({ page }) => { await page.goto('/dashboard/guardian/payments'); const wardFilter = page.getByRole('combobox', { name: /aluno|ward/i }); if (await wardFilter.isVisible()) { const options = await wardFilter.locator('option').count(); if (options > 1) { const beforeCount = await page.locator('[data-testid="payment-row"]').count(); await wardFilter.selectOption({ index: 1 }); await page.waitForTimeout(500); const afterCount = await page.locator('[data-testid="payment-row"]').count(); // Pode mostrar menos resultados } } }); }); test.describe('Responsividade - Mobile', () => { test('formulário de registro deve ser usável em mobile', async ({ page }) => { await page.setViewportSize({ width: 375, height: 667 }); await page.goto('/dashboard/guardian/payments/register'); // Sem scroll horizontal const hasHorizontalScroll = await page.evaluate(() => { return document.body.scrollWidth > document.body.clientWidth; }); expect(hasHorizontalScroll).toBe(false); // Campos devem estar visíveis await expect(page.getByRole('combobox', { name: /aluno/i })).toBeVisible(); await expect(page.getByLabel(/valor/i)).toBeVisible(); }); test('tabela de histórico deve ter scroll horizontal em mobile', async ({ page }) => { await page.setViewportSize({ width: 375, height: 667 }); await page.goto('/dashboard/guardian/payments/history'); const table = page.locator('table').first(); if (await table.isVisible()) { const container = table.locator('..'); const overflowX = await container.evaluate(el => { return window.getComputedStyle(el).overflowX; }); // Deve ter scroll ou estar ajustado expect(overflowX || 'visible').toMatch(/auto|scroll|visible/); } }); }); });