326 lines
13 KiB
JavaScript
326 lines
13 KiB
JavaScript
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('Professor - Correção de Provas', () => {
|
|
// TODO: Fazer login como professor
|
|
// 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\/teacher/);
|
|
// });
|
|
|
|
test.describe('Lista de Provas da Turma', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('/dashboard/teacher/class/class-id');
|
|
});
|
|
|
|
test('deve mostrar provas atribuídas à turma', async ({ page }) => {
|
|
const assignmentList = page.locator('[data-testid="assignment-list"]');
|
|
if (await assignmentList.isVisible()) {
|
|
await expect(assignmentList.getByText(/prova/i)).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('deve mostrar status da prova', async ({ page }) => {
|
|
const assignmentList = page.locator('[data-testid="assignment-list"]');
|
|
if (await assignmentList.isVisible()) {
|
|
// Status: pendente, em andamento, concluída
|
|
const statusBadge = assignmentList.locator('[data-testid="status-label"]').first();
|
|
if (await statusBadge.isVisible()) {
|
|
await expect(statusBadge).toBeVisible();
|
|
}
|
|
}
|
|
});
|
|
|
|
test('deve mostrar número de alunos que fizeram a prova', async ({ page }) => {
|
|
const assignmentList = page.locator('[data-testid="assignment-list"]');
|
|
if (await assignmentList.isVisible()) {
|
|
const progressText = assignmentList.getByText(/\d+\/\d+/i).first();
|
|
if (await progressText.isVisible()) {
|
|
await expect(progressText).toBeVisible();
|
|
}
|
|
}
|
|
});
|
|
|
|
test('deve ter botão de ver resultados', async ({ page }) => {
|
|
const assignmentCard = page.locator('[data-testid="assignment-card"]').first();
|
|
if (await assignmentCard.isVisible()) {
|
|
const resultsButton = assignmentCard.getByRole('link', { name: /resultados|correções/i });
|
|
if (await resultsButton.isVisible()) {
|
|
await expect(resultsButton).toBeVisible();
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe('Página de Resultados da Turma', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// TODO: Navegar para resultados de uma turma específica
|
|
await page.goto('/dashboard/teacher/class/class-id/results');
|
|
});
|
|
|
|
test('deve mostrar lista de tentativas', async ({ page }) => {
|
|
await expect(page.getByText(/tentativas|resultados/i)).toBeVisible();
|
|
await expect(page.locator('table')).toBeVisible();
|
|
});
|
|
|
|
test('deve mostrar aluno, prova, nota e status', async ({ page }) => {
|
|
const table = page.locator('table').first();
|
|
if (await table.isVisible()) {
|
|
await expect(table.getByText(/aluno|student/i)).toBeVisible();
|
|
await expect(table.getByText(/prova|template/i)).toBeVisible();
|
|
await expect(table.getByText(/nota|score/i)).toBeVisible();
|
|
await expect(table.getByText(/status/i)).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('deve mostrar badge de status de correçã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, partially_graded, fully_graded
|
|
const label = statusCell.locator('[data-testid="label"], .label').first();
|
|
if (await label.isVisible()) {
|
|
const classes = await label.getAttribute('class') || '';
|
|
expect(classes).toMatch(/label-(gray|amber|emerald)/);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe('Corrigir Prova - Modal', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('/dashboard/teacher/class/class-id/results');
|
|
|
|
// Abrir modal de correção
|
|
const gradeButton = page.getByRole('button', { name: /corrigir|grade/i }).first();
|
|
if (await gradeButton.isVisible()) {
|
|
await gradeButton.click();
|
|
}
|
|
});
|
|
|
|
test('deve abrir modal com detalhes da tentativa', async ({ page }) => {
|
|
await expect(page.locator('.fixed.inset-0')).toBeVisible();
|
|
await expect(page.getByText(/correção|grading/i)).toBeVisible();
|
|
});
|
|
|
|
test('deve mostrar informações do aluno', async ({ page }) => {
|
|
if (await page.locator('.fixed.inset-0').isVisible()) {
|
|
await expect(page.getByText(/aluno|student/i)).toBeVisible();
|
|
|
|
// Deve mostrar data de envio
|
|
await expect(page.getByText(/\d{2}\/\d{2}\/\d{4}/)).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('deve mostrar questões da prova', async ({ page }) => {
|
|
if (await page.locator('.fixed.inset-0').isVisible()) {
|
|
const questions = page.locator('[data-testid="question-card"]');
|
|
const count = await questions.count();
|
|
|
|
if (count > 0) {
|
|
// Primeira questão deve ter enunciado
|
|
await expect(questions.nth(0).getByPlaceholder(/enunciado|questão/i)).toBeVisible();
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe('Corrigir Questão Textual', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Abrir modal de correção com questão textual
|
|
// await page.goto('/dashboard/teacher/class/class-id/results');
|
|
// await page.getByRole('button', { name: /corrigir/i }).click();
|
|
});
|
|
|
|
test('deve mostrar resposta do aluno', async ({ page }) => {
|
|
if (await page.locator('.fixed.inset-0').isVisible()) {
|
|
const textQuestion = page.locator('[data-testid="text-question"]').first();
|
|
if (await textQuestion.isVisible()) {
|
|
await expect(textQuestion.getByPlaceholder(/resposta/i)).not.toBeEmpty();
|
|
}
|
|
}
|
|
});
|
|
|
|
test('deve permitir dar pontos parciais', async ({ page }) => {
|
|
if (await page.locator('.fixed.inset-0').isVisible()) {
|
|
const textQuestion = page.locator('[data-testid="text-question"]').first();
|
|
if (await textQuestion.isVisible()) {
|
|
const pointsInput = textQuestion.getByRole('spinbutton', { name: /pontos/i });
|
|
if (await pointsInput.isVisible()) {
|
|
await pointsInput.fill('3.5');
|
|
|
|
const value = await pointsInput.inputValue();
|
|
expect(value).toBe('3.5');
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
test('deve ter campo de feedback', async ({ page }) => {
|
|
if (await page.locator('.fixed.inset-0').isVisible()) {
|
|
const textQuestion = page.locator('[data-testid="text-question"]').first();
|
|
if (await textQuestion.isVisible()) {
|
|
const feedbackInput = textQuestion.getByPlaceholder(/feedback|comentário/i);
|
|
if (await feedbackInput.isVisible()) {
|
|
await feedbackInput.fill('Bom trabalho, mas pode melhorar a gramática.');
|
|
|
|
await expect(feedbackInput).toHaveValue(/gramática/i);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe('Questões de Múltipla Escolha', () => {
|
|
test('deve mostrar resposta selecionada pelo aluno', async ({ page }) => {
|
|
if (await page.locator('.fixed.inset-0').isVisible()) {
|
|
const mcQuestion = page.locator('[data-testid="mc-question"]').first();
|
|
if (await mcQuestion.isVisible()) {
|
|
// Opção correta deve estar marcada
|
|
const correctOption = mcQuestion.locator('[data-testid="correct-option"]').first();
|
|
if (await correctOption.isVisible()) {
|
|
await expect(correctOption).toBeVisible();
|
|
}
|
|
|
|
// Opção errada deve estar marcada se o aluno errou
|
|
const wrongOption = mcQuestion.locator('[data-testid="wrong-option"]').first();
|
|
if (await wrongOption.isVisible()) {
|
|
await expect(wrongOption).toBeVisible();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
test('deve mostrar opção correta', async ({ page }) => {
|
|
if (await page.locator('.fixed.inset-0').isVisible()) {
|
|
const mcQuestion = page.locator('[data-testid="mc-question"]').first();
|
|
if (await mcQuestion.isVisible()) {
|
|
const correctOption = mcQuestion.locator('[data-testid="correct-option"]').first();
|
|
if (await correctOption.isVisible()) {
|
|
await expect(correctOption.getByText(/correta/i)).toBeVisible();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
test('não deve permitir modificar correção automática', async ({ page }) => {
|
|
if (await page.locator('.fixed.inset-0').isVisible()) {
|
|
const mcQuestion = page.locator('[data-testid="mc-question"]').first();
|
|
if (await mcQuestion.isVisible()) {
|
|
// Opções devem ser readonly
|
|
const options = mcQuestion.locator('input[type="radio"]');
|
|
const count = await options.count();
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
await expect(options.nth(i)).toBeDisabled();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe('Salvar Correção', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Abrir modal de correção
|
|
// await page.goto('/dashboard/teacher/class/class-id/results');
|
|
// await page.getByRole('button', { name: /corrigir/i }).first().click();
|
|
});
|
|
|
|
test('deve calcular nota total automaticamente', async ({ page }) => {
|
|
if (await page.locator('.fixed.inset-0').isVisible()) {
|
|
// Deve mostrar nota total calculada
|
|
const totalScore = page.getByText(/nota total|total score/i);
|
|
if (await totalScore.isVisible()) {
|
|
await expect(totalScore).toBeVisible();
|
|
}
|
|
}
|
|
});
|
|
|
|
test('deve salvar correção com sucesso', async ({ page }) => {
|
|
if (await page.locator('.fixed.inset-0').isVisible()) {
|
|
await page.getByRole('button', { name: /salvar|concluir/i }).click();
|
|
|
|
// Deve fechar modal
|
|
await expect(page.locator('.fixed.inset-0')).not.toBeVisible({ timeout: 5000 });
|
|
|
|
// Deve mostrar mensagem de sucesso ou atualizar status
|
|
// await expect(page.getByText(/correção salva|sucesso/i)).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('deve atualizar status para "corrigido"', async ({ page }) => {
|
|
// TODO: Implementar fluxo completo
|
|
// 1. Abrir correção
|
|
// 2. Salvar
|
|
// 3. Verificar se status mudou na lista
|
|
});
|
|
});
|
|
|
|
test.describe('Correção Parcial', () => {
|
|
test('deve indicar provas parcialmente corrigidas', async ({ page }) => {
|
|
await page.goto('/dashboard/teacher/class/class-id/results');
|
|
|
|
// Deve ter badges para: pending, partially_graded, fully_graded
|
|
const partialBadge = page.locator('[data-testid="status-label"]')
|
|
.filter({ hasText: /parcial/i });
|
|
|
|
const count = await partialBadge.count();
|
|
expect(count).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
test('deve mostrar ícone de pendência para questões não corrigidas', async ({ page }) => {
|
|
if (await page.locator('.fixed.inset-0').isVisible()) {
|
|
// Questões textuais sem correção devem ter indicador
|
|
const ungradedQuestions = page.locator('[data-testid="needs-grading"]');
|
|
const count = await ungradedQuestions.count();
|
|
|
|
if (count > 0) {
|
|
await expect(ungradedQuestions.nth(0).getByText(/pendente|corrigir/i)).toBeVisible();
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe('Cancelar Correção', () => {
|
|
test('deve ter botão de cancelar', async ({ page }) => {
|
|
if (await page.locator('.fixed.inset-0').isVisible()) {
|
|
await expect(page.getByRole('button', { name: /cancelar/i })).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('deve fechar modal sem salvar ao cancelar', async ({ page }) => {
|
|
if (await page.locator('.fixed.inset-0').isVisible()) {
|
|
await page.getByRole('button', { name: /cancelar/i }).click();
|
|
|
|
await expect(page.locator('.fixed.inset-0').not.toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('deve perder alterações não salvas', async ({ page }) => {
|
|
if (await page.locator('.fixed.inset-0').isVisible()) {
|
|
// Fazer alterações
|
|
const textQuestion = page.locator('[data-testid="text-question"]').first();
|
|
if (await textQuestion.isVisible()) {
|
|
const pointsInput = textQuestion.getByRole('spinbutton', { name: /pontos/i });
|
|
if (await pointsInput.isVisible()) {
|
|
await pointsInput.fill('5');
|
|
}
|
|
}
|
|
|
|
// Cancelar
|
|
await page.getByRole('button', { name: /cancelar/i }).click();
|
|
|
|
// Abrir novamente - não deve ter os 5 pontos
|
|
// await page.getByRole('button', { name: /corrigir/i }).first().click();
|
|
// const newPoints = page.getByRole('spinbutton', { name: /pontos/i }).first();
|
|
// const value = await newPoints.inputValue();
|
|
// expect(value).not.toBe('5');
|
|
}
|
|
});
|
|
});
|
|
});
|