Files
2026-08-31 14:10:20 -03:00

409 lines
16 KiB
JavaScript

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('[email protected]');
// 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/);
}
});
});
});