modified: .env.example

new file:   .env.local
modified:   .gitignore
new file:   docs/mock-audit.md
modified:   eslint.config.js
modified:   package-lock.json
modified:   package.json
deleted:    src/App.css
modified:   src/App.jsx
deleted:    src/assets/react.svg
deleted:    src/assets/vite.svg
new file:   src/components/RichTextEditor.jsx
modified:   src/components/calendar/AgendaMonthlyView.jsx
modified:   src/components/calendar/AgendaWeeklyView.jsx
modified:   src/components/ui.jsx
modified:   src/config/api.js
modified:   src/data/mockData.js
new file:   src/data/reportTemplates.js
modified:   src/hooks/useAgenda.js
modified:   src/mappers/appointmentMapper.js
modified:   src/pages/AgendaPage.jsx
modified:   src/pages/MedicalRecordsPage.jsx
modified:   src/pages/MessagesPage.jsx
modified:   src/pages/PatientsPage.jsx
modified:   src/pages/ProfilePage.jsx
modified:   src/pages/ReportsPage.jsx
modified:   src/pages/UsersPage.jsx
modified:   src/pages/VisitsPage.jsx
modified:   src/repositories/patientRepository.js
modified:   src/repositories/profileRepository.js
modified:   src/repositories/userRepository.js
deleted:    test.mjs
deleted:    test2.mjs
deleted:    test3.mjs
deleted:    test4.mjs
deleted:    test5.mjs
new file:   tests/mappers.test.mjs
new file:   tests/patientRepository.test.mjs
new file:   tests/permissions.test.mjs
new file:   tests/repositoryUtils.test.mjs
This commit is contained in:
2026-05-12 04:48:25 -03:00
parent 8f0e616d2b
commit bd337349e1
40 changed files with 3244 additions and 925 deletions

60
tests/mappers.test.mjs Normal file
View File

@@ -0,0 +1,60 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { appointmentMapper } from '../src/mappers/appointmentMapper.js'
import { reportMapper } from '../src/mappers/reportMapper.js'
test('appointmentMapper envia valores aceitos pela API Supabase', () => {
const payload = appointmentMapper.toApi(
{
patientId: 'patient-1',
professionalId: 'doctor-1',
date: '2026-05-11',
time: '10:30',
mode: 'Teleconsulta',
status: 'Em triagem',
notes: '',
},
'supabase',
)
assert.equal(payload.patient_id, 'patient-1')
assert.equal(payload.doctor_id, 'doctor-1')
assert.equal(payload.appointment_type, 'telemedicina')
assert.equal(payload.status, 'checked_in')
assert.equal(payload.duration_minutes, 30)
assert.equal('notes' in payload, true)
})
test('appointmentMapper converte resposta da API para labels da agenda', () => {
const appointment = appointmentMapper.toUi({
id: 'appt-1',
status: 'confirmed',
appointment_type: 'telemedicina',
scheduled_at: '2026-05-11T13:30:00.000Z',
patients: { id: 'patient-1', full_name: 'Ana Souza' },
doctors: { id: 'doctor-1', full_name: 'Dra. Leticia' },
})
assert.equal(appointment.id, 'appt-1')
assert.equal(appointment.status, 'Confirmada')
assert.equal(appointment.mode, 'Teleconsulta')
assert.equal(appointment.patient, 'Ana Souza')
assert.equal(appointment.professional, 'Dra. Leticia')
})
test('reportMapper remove campos vazios e normaliza status', () => {
const payload = reportMapper.toApi({
patientId: 'patient-1',
status: 'finalized',
exam: '',
requestedBy: 'Dra. Leticia',
contentHtml: '<p>Conclusao clinica</p>',
})
assert.equal(payload.patient_id, 'patient-1')
assert.equal(payload.status, 'finalized')
assert.equal(payload.requested_by, 'Dra. Leticia')
assert.equal(payload.content_html, '<p>Conclusao clinica</p>')
assert.equal('exam' in payload, false)
})

View File

@@ -0,0 +1,59 @@
import assert from 'node:assert/strict'
import test from 'node:test'
process.env.VITE_SUPABASE_URL = 'https://example.supabase.co'
process.env.VITE_SUPABASE_ANON_KEY = 'anon-key'
globalThis.Event = class Event {
constructor(type) {
this.type = type
}
}
globalThis.window = {
dispatchEvent() {},
sessionStorage: {
getItem() {
return JSON.stringify({
access_token: 'access-token',
expires_at: Math.floor(Date.now() / 1000) + 3600,
})
},
removeItem() {},
setItem() {},
},
}
test('patientRepository.getById busca o paciente direto por id', async () => {
const calls = []
globalThis.fetch = async (url) => {
const requestUrl = String(url)
calls.push(requestUrl)
if (requestUrl.includes('/patients?')) {
return Response.json([
{
id: 'patient-1',
full_name: 'Ana Souza',
cpf: '12345678900',
birth_date: '1990-01-01',
},
])
}
if (requestUrl.includes('/appointments?')) {
return Response.json([])
}
throw new Error(`URL inesperada: ${requestUrl}`)
}
const { patientRepository } = await import('../src/repositories/patientRepository.js')
const patient = await patientRepository.getById('patient-1')
assert.equal(patient.id, 'patient-1')
assert.equal(patient.name, 'Ana Souza')
assert.ok(calls.some((url) => url.includes('/patients?') && url.includes('id=eq.patient-1')))
assert.ok(calls.every((url) => !url.includes('/patients?select=*') || url.includes('id=eq.patient-1')))
})

View File

@@ -0,0 +1,30 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { canAccess, hasCapability, normalizeRole } from '../src/config/permissions.js'
test('normaliza aliases de perfis conhecidos', () => {
assert.equal(normalizeRole('doctor'), 'medico')
assert.equal(normalizeRole('Gestao / Coordenacao'), 'gestor')
assert.equal(normalizeRole('administrator'), 'admin')
assert.equal(normalizeRole('secretary'), 'secretaria')
})
test('medico acessa pacientes e prontuario, mas nao painel ou analytics', () => {
assert.equal(canAccess('medico', '/pacientes'), true)
assert.equal(canAccess('medico', '/prontuario/123'), true)
assert.equal(canAccess('medico', '/inicio'), false)
assert.equal(canAccess('medico', '/relatorios'), false)
})
test('secretaria acessa agenda e pacientes, mas nao painel', () => {
assert.equal(canAccess('secretaria', '/agenda'), true)
assert.equal(canAccess('secretaria', '/pacientes'), true)
assert.equal(canAccess('secretaria', '/inicio'), false)
})
test('roles administrativos mantem capacidades criticas', () => {
assert.equal(hasCapability('admin', 'manageUsers'), true)
assert.equal(hasCapability('gestor', 'hardDeletePatients'), true)
assert.equal(hasCapability('medico', 'hardDeletePatients'), false)
})

View File

@@ -0,0 +1,40 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { getResponseError, translateErrorMessage } from '../src/repositories/repositoryUtils.js'
test('traduz erros crus comuns do Supabase para pt-BR', () => {
assert.equal(translateErrorMessage('Invalid login credentials'), 'E-mail ou senha inválidos.')
assert.equal(
translateErrorMessage('new row violates row-level security policy for table "patients"'),
'Você não tem permissão para realizar esta ação.',
)
assert.equal(
translateErrorMessage('invalid input value for enum appointment_type: "teleconsulta"'),
'Valor inválido para uma opção do sistema.',
)
})
test('getResponseError preserva erros estruturados em portugues da API', async () => {
const response = new Response(
JSON.stringify({
title: 'Erro de Validacao',
errors: {
cpf: ['Campo obrigatorio'],
},
}),
{ status: 400 },
)
const message = await getResponseError(response, 'Erro ao criar usuario.')
assert.match(message, /Erro ao criar usuario\. \(400\):/)
assert.match(message, /cpf: Campo obrigatorio/)
})
test('getResponseError usa fallback em ingles desconhecido', async () => {
const response = new Response('Something went wrong in backend', { status: 500 })
const message = await getResponseError(response, 'Falha ao salvar registro.')
assert.equal(message, 'Falha ao salvar registro. (500): Falha ao salvar registro.')
})