featrue/testing
This commit is contained in:
259
frontend/src/lib/Reporte_Pruebas.MD
Normal file
259
frontend/src/lib/Reporte_Pruebas.MD
Normal file
@@ -0,0 +1,259 @@
|
||||
# Reporte de Pruebas — Anexo 76
|
||||
|
||||
## Resumen ejecutivo
|
||||
|
||||
| Herramienta | Archivos | Pruebas | Estado |
|
||||
|-------------|----------|---------|--------|
|
||||
| Backend — pytest | 4 | 11 | Pasando |
|
||||
| Frontend — Vitest (server) | 8 | 68 | Pasando |
|
||||
| Frontend — Playwright (E2E) | 5 | 28 | Pasando |
|
||||
| **Total** | **17** | **107** | **Pasando** |
|
||||
|
||||
---
|
||||
|
||||
## Backend — pytest (11 pruebas)
|
||||
|
||||
Ubicacion: `backend/tests/`
|
||||
|
||||
### e2e/test_inventory_flow_anexo24.py — 1 prueba
|
||||
|
||||
**test_e2e_inventory_flow_import_then_export** — la prueba mas importante del repositorio. Simula el flujo completo del negocio:
|
||||
|
||||
1. Crea una factura de importacion TEM con 10 piezas
|
||||
2. La procesa — genera movimiento ENTRY en el inventario
|
||||
3. Crea una factura de exportacion consumiendo 4 piezas
|
||||
4. La procesa — genera CONSUMPTION y DISCHARGE
|
||||
5. Verifica que el saldo neto es positivo y menor a 10
|
||||
|
||||
### integration/ — 5 pruebas
|
||||
|
||||
- **test_process_export_endpoint_consumes_existing_balances** — exportacion consume saldos existentes correctamente
|
||||
- **test_process_export_prevents_negative_balance** — exportacion no puede consumir mas de lo que hay
|
||||
- **test_process_endpoint_prevents_double_processing_import** — una factura no se puede procesar dos veces
|
||||
- **test_process_import_endpoint_creates_balance_entries** — importacion TEM genera entradas de balance
|
||||
- **test_process_import_def_does_not_create_balance_entries** — importacion DEF no genera entradas de balance
|
||||
|
||||
### unit/ — 5 pruebas
|
||||
|
||||
- **test_net_balance_accounts_for_returns_and_entry_void** — balance neto calcula correctamente entradas, consumos, devoluciones y anulaciones
|
||||
- **test_fifo_consumption_algorithm_uses_oldest_lots_first** — algoritmo PEPS consume primero los lotes mas antiguos
|
||||
- **test_assign_values_iva_lines_currency_me** — calculo de IVA en moneda extranjera
|
||||
- **test_assign_values_iva_lines_currency_mn** — calculo de IVA en moneda local
|
||||
- **test_assign_values_iva_lines_currency_mc** — calculo de IVA en moneda manual
|
||||
|
||||
### Comando
|
||||
|
||||
```bash
|
||||
docker exec -it anexo76-backend pytest /app/tests/ -v
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Frontend Vitest — 68 pruebas
|
||||
|
||||
Vitest corre en Node sin navegador. Prueba funciones puras que reciben datos y devuelven un resultado.
|
||||
|
||||
### backend.test.ts — 2 pruebas
|
||||
|
||||
Verifica que el backend esta disponible desde el contenedor del frontend usando la red interna de Docker.
|
||||
|
||||
- **el backend esta corriendo y responde** — GET /api/health devuelve 200
|
||||
- **el endpoint de facturas responde** — el endpoint de invoices responde (200, 401, 403 o 422 son validos)
|
||||
|
||||
Nota: no se usa `docker exec` porque el contenedor del frontend no tiene acceso a Docker. Se usa la URL interna `http://backend:8000` de la red Docker Compose.
|
||||
|
||||
### utils.getInvoiceTypeColor.test.ts — 19 pruebas
|
||||
|
||||
Prueba que cada tipo de factura devuelve el color Tailwind correcto para mostrarse en la UI.
|
||||
|
||||
- Entradas invalidas (null, undefined, '') devuelven gris por defecto
|
||||
- TEM / IMPO TEM -> rojo (bg-red-100)
|
||||
- DEF / IMPO DEF -> verde (bg-green-100)
|
||||
- MEX / COMP MEX -> morado (bg-purple-100)
|
||||
- CAM REG -> azul (bg-blue-100)
|
||||
- EXPO / EXDEF / PTERM -> azul (bg-blue-100)
|
||||
- IMP REP -> azul claro (bg-sky-100)
|
||||
- Tipo desconocido -> gris por defecto
|
||||
|
||||
### utils.getFileHelpers.test.ts — 9 pruebas
|
||||
|
||||
Prueba extraccion de nombres de archivo desde rutas y formateo para mostrar al usuario.
|
||||
|
||||
- null / undefined -> string vacio
|
||||
- Ruta normal '/uploads/file.png' -> 'file.png'
|
||||
- Ruta con query string '/uploads/file.png?token=123' -> 'file.png' sin el token
|
||||
- Con fileType -> 'file.png (PDF)'
|
||||
|
||||
### utils.getBackendAssetUrl.test.ts — 5 pruebas
|
||||
|
||||
Prueba construccion de URLs del backend evitando duplicar /api.
|
||||
|
||||
Deuda tecnica identificada: la funcion depende de import.meta.env.VITE_API_URL. Se pasa baseUrl explicitamente en cada test.
|
||||
|
||||
- null / undefined -> string vacio
|
||||
- URL completa -> se devuelve sin modificar
|
||||
- /api/... con base /api -> evita /api/api/
|
||||
- Ruta normal -> URL completa correcta
|
||||
|
||||
### date-utils.test.ts — 17 pruebas
|
||||
|
||||
Prueba conversion de fechas entre zona local y UTC ISO.
|
||||
|
||||
- prepareDateForBackend: vacio -> null, fecha + hora -> ISO string valido
|
||||
- loadServerDate: null/undefined -> '', ISO UTC -> YYYY-MM-DD, fecha plana sin modificar
|
||||
- addDaysLocal: vacio -> '', suma normal, cambio de mes, ano bisiesto (2024-02-29)
|
||||
- getCurrentLocal*: verifica formato con regex — no valor exacto porque cambia cada dia
|
||||
|
||||
### csv-import-commit-metrics.test.ts — 11 pruebas
|
||||
|
||||
Prueba metricas del resultado de importacion CSV.
|
||||
|
||||
- totalSkippedFromCommit: null -> 0, objeto vacio -> 0, suma correcta de campos skipped
|
||||
- criticalReferenceGaps: null -> 0, objeto vacio -> 0, campo presente -> su valor
|
||||
- referenceStateReady: null -> true, campo booleano respeta el valor
|
||||
|
||||
### csv-import-status-api.test.ts — 4 pruebas
|
||||
|
||||
Prueba isWaitingConfirmationPayload. fetchCsvImportStatus no se prueba porque depende del backend.
|
||||
|
||||
- null / {} -> false
|
||||
- { status: 'waiting_confirmation' } -> true
|
||||
- { job_id: 'abc', total_rows: 10 } -> true
|
||||
|
||||
### Comando
|
||||
|
||||
```bash
|
||||
docker exec -it anexo76-frontend pnpm test:unit --project server
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Frontend Playwright E2E — 28 pruebas
|
||||
|
||||
Playwright abre un navegador real y prueba flujos completos con el backend y Keycloak levantados.
|
||||
|
||||
### Configuracion
|
||||
|
||||
- **workers: 1** — las pruebas con login no pueden correr en paralelo
|
||||
- **headless: false** — necesario para estabilidad en Windows
|
||||
- **timeout: 60000** — Keycloak puede tardar hasta 60 segundos
|
||||
- **storageState** — sesion guardada en e2e/.auth/user.json y reutilizada
|
||||
|
||||
### auth.setup.ts — 1 prueba
|
||||
|
||||
Login inicial con credenciales demo/demo123. Guarda la sesion para reutilizar.
|
||||
|
||||
### full-flow.spec.ts — 4 pruebas
|
||||
|
||||
Conecta backend y frontend — corre pytest primero y si pasa, verifica el frontend.
|
||||
|
||||
- **pruebas del backend pasan antes de continuar** — ejecuta pytest /app/tests/ y verifica 11 passed
|
||||
- **Import Invoices TEM carga despues de que backend pasa** — URL /invoices + main visible
|
||||
- **Export Invoices carga despues de que backend pasa** — URL /invoices + main visible
|
||||
- **Reportes de facturas carga despues de que backend pasa** — URL /reports + main visible
|
||||
|
||||
### login.spec.ts — 3 pruebas
|
||||
|
||||
- Login exitoso redirige al dashboard
|
||||
- Dashboard muestra h1 visible
|
||||
- Credenciales incorrectas se quedan en /login
|
||||
|
||||
### navigation.spec.ts — 10 pruebas
|
||||
|
||||
- Dashboard carga con h1 visible
|
||||
- Header muestra Aduanasoft S.A. de C.V.
|
||||
- Menu lateral tiene Audit Logs, Customs Brokers, Fractions, Pedimentos
|
||||
- Audit Logs, Customs Brokers, Fraction Sitar y Pedimentos cargan sin error
|
||||
|
||||
### modules.spec.ts — 10 pruebas
|
||||
|
||||
- Import Invoices TEM y DEF cargan sin error
|
||||
- Export Invoices carga sin error
|
||||
- Fixed Catalogs, General Catalogs, Transportes, Goods, Settings, Reportes cargan sin error
|
||||
- Logout redirige a /login
|
||||
|
||||
### Comando
|
||||
|
||||
```bash
|
||||
# Desde la carpeta frontend en Windows
|
||||
pnpm test:e2e
|
||||
|
||||
# Suite especifica
|
||||
pnpm test:e2e --grep "Flujo completo"
|
||||
pnpm test:e2e --grep "Login"
|
||||
pnpm test:e2e --grep "Modulos"
|
||||
pnpm test:e2e --grep "Navegacion"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Archivos que NO se prueban con Vitest
|
||||
|
||||
| Archivo | Razon |
|
||||
|---------|-------|
|
||||
| api.ts | Depende de fetch, cookies, window, document |
|
||||
| auth.ts | Depende de Keycloak JS, cookies, window |
|
||||
| session-manager.ts | Depende de window, setInterval, eventos DOM |
|
||||
| sso.ts | Depende de Keycloak JS y browser |
|
||||
| csv-import-pending.ts | Depende de localStorage |
|
||||
| csv-import-session.ts | Depende de sessionStorage |
|
||||
| csv-upload-row-count.ts | Depende de File.text(), API del browser |
|
||||
| fetchCsvImportStatus | Depende de api que necesita el backend |
|
||||
|
||||
---
|
||||
|
||||
## Pruebas pendientes para el futuro
|
||||
|
||||
### Necesitan datos en la BD
|
||||
- Buscar una factura especifica en Import Invoices
|
||||
- Filtrar por fecha en Reportes
|
||||
- Verificar que tablas muestran registros tras procesar una factura
|
||||
|
||||
### Necesitan mas usuarios
|
||||
- RBAC: usuario sin permisos intenta acceder a ruta protegida
|
||||
- Admin vs usuario normal ven opciones distintas
|
||||
|
||||
### Necesitan flujo completo
|
||||
- Subir un CSV y verificar que el proceso funciona
|
||||
- Crear una factura y verificar que aparece en la tabla
|
||||
|
||||
---
|
||||
|
||||
## Estructura de archivos final
|
||||
|
||||
```
|
||||
backend/tests/
|
||||
├── conftest.py
|
||||
├── fixtures/
|
||||
│ └── builders.py
|
||||
├── e2e/
|
||||
│ └── test_inventory_flow_anexo24.py 1 test
|
||||
├── integration/
|
||||
│ ├── test_export_process_api.py 3 tests
|
||||
│ └── test_import_process_api.py 2 tests
|
||||
└── unit/
|
||||
└── invoices/
|
||||
├── test_balance_algorithm.py 2 tests
|
||||
└── test_currency_conversion.py 3 tests
|
||||
|
||||
frontend/src/lib/
|
||||
├── backend.test.ts 2 tests
|
||||
├── utils.getInvoiceTypeColor.test.ts 19 tests
|
||||
├── utils.getFileHelpers.test.ts 9 tests
|
||||
├── utils.getBackendAssetUrl.test.ts 5 tests
|
||||
├── date-utils.test.ts 17 tests
|
||||
├── csv-import-commit-metrics.test.ts 11 tests
|
||||
└── csv-import-status-api.test.ts 4 tests
|
||||
|
||||
frontend/e2e/
|
||||
├── .auth/user.json
|
||||
├── auth.setup.ts 1 test
|
||||
├── full-flow.spec.ts 4 tests
|
||||
├── login.spec.ts 3 tests
|
||||
├── navigation.spec.ts 10 tests
|
||||
└── modules.spec.ts 10 tests
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Anexo 76 — Reporte de Pruebas v3.0 — 107 pruebas totales*
|
||||
18
frontend/src/lib/backend.test.ts
Normal file
18
frontend/src/lib/backend.test.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
|
||||
describe('backend — health check', () => {
|
||||
|
||||
it('el backend esta corriendo y responde', async () => {
|
||||
const response = await fetch('http://backend:8000/api/health')
|
||||
expect(response.status).toBe(200)
|
||||
})
|
||||
|
||||
it('el endpoint de facturas responde', async () => {
|
||||
const response = await fetch('http://backend:8000/api/v1/a76/invoices/?company_id=1', {
|
||||
headers: { 'Authorization': 'Bearer test' }
|
||||
})
|
||||
// 200 con datos o 401/403 sin token valido — ambos significan que el backend esta vivo
|
||||
expect([200, 401, 403, 422]).toContain(response.status)
|
||||
})
|
||||
|
||||
})
|
||||
46
frontend/src/lib/csv-import-commit-metrics.test.ts
Normal file
46
frontend/src/lib/csv-import-commit-metrics.test.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { totalSkippedFromCommit, criticalReferenceGaps, referenceStateReady } from './csv-import-commit-metrics'
|
||||
|
||||
describe('csv-import-commit-metrics', () => {
|
||||
|
||||
describe('totalSkippedFromCommit', () => {
|
||||
it('devuelve 0 si recibe null', () => {
|
||||
expect(totalSkippedFromCommit(null)).toBe(0)
|
||||
})
|
||||
it('devuelve 0 si recibe objeto vacío', () => {
|
||||
expect(totalSkippedFromCommit({})).toBe(0)
|
||||
})
|
||||
it('suma los campos skipped correctamente', () => {
|
||||
expect(totalSkippedFromCommit({skipped_invalid: 2, skipped_duplicate: 3})).toBe(5)
|
||||
})
|
||||
})
|
||||
|
||||
describe('criticalReferenceGaps', () => {
|
||||
it('devuelve 0 si recibe null', () => {
|
||||
expect(criticalReferenceGaps(null)).toBe(0)
|
||||
})
|
||||
it('devuelve 0 si recibe objeto vacío', () => {
|
||||
expect(criticalReferenceGaps({})).toBe(0)
|
||||
})
|
||||
it('devuelve el valor del campo', () => {
|
||||
expect(criticalReferenceGaps({ critical_reference_gaps: 4 })).toBe(4)
|
||||
})
|
||||
})
|
||||
describe('referenceStateReady', () => {
|
||||
it('devuelve true si recibe null', () => {
|
||||
expect(referenceStateReady(null)).toBe(true)
|
||||
})
|
||||
it('devuelve true si reference_state_ready es true', () => {
|
||||
expect(referenceStateReady({ reference_state_ready: true })).toBe(true)
|
||||
})
|
||||
it('devuelve false si reference_state_ready es false', () => {
|
||||
expect(referenceStateReady({ reference_state_ready: false })).toBe(false)
|
||||
})
|
||||
it('devuelve true si critical_reference_gaps es 0', () => {
|
||||
expect(referenceStateReady({ critical_reference_gaps: 0 })).toBe(true)
|
||||
})
|
||||
it('devuelve false si critical_reference_gaps es mayor a 0', () => {
|
||||
expect(referenceStateReady({ critical_reference_gaps: 2 })).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
18
frontend/src/lib/csv-import-status-api.test.ts
Normal file
18
frontend/src/lib/csv-import-status-api.test.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { isWaitingConfirmationPayload} from './csv-import-status-api'
|
||||
describe('csv-import-status-api', () => {
|
||||
describe('isWaitingConfirmationPayload', () => {
|
||||
it('devuelve false si recibe null', () => {
|
||||
expect(isWaitingConfirmationPayload(null)).toBe(false)
|
||||
})
|
||||
it('devuelve false si recibe objeto vacío', () => {
|
||||
expect(isWaitingConfirmationPayload({})).toBe(false)
|
||||
})
|
||||
it('devuelve true si status es waiting_confirmation', () => {
|
||||
expect(isWaitingConfirmationPayload({ status: 'waiting_confirmation' })).toBe(true)
|
||||
})
|
||||
it('devuelve true si tiene job_id y total_rows', () => {
|
||||
expect(isWaitingConfirmationPayload({ job_id: 'abc', total_rows: 10 })).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
106
frontend/src/lib/date-utils.test.ts
Normal file
106
frontend/src/lib/date-utils.test.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
prepareDateForBackend,
|
||||
loadServerDate,
|
||||
addDaysLocal,
|
||||
getCurrentLocalYear,
|
||||
getCurrentLocalDate,
|
||||
getCurrentLocalTime
|
||||
} from './date-utils'
|
||||
|
||||
const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/
|
||||
const ISO_DATETIME_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/
|
||||
const TIME_REGEX = /^\d{2}:\d{2}$/
|
||||
|
||||
describe('date-utils', () => {
|
||||
|
||||
describe('prepareDateForBackend', () => {
|
||||
|
||||
it('devuelve null si dateStr está vacío', () => {
|
||||
expect(prepareDateForBackend('')).toBeNull()
|
||||
})
|
||||
|
||||
it('devuelve ISO string con fecha y hora explícita', () => {
|
||||
const result = prepareDateForBackend('2024-06-15', '09:30')
|
||||
expect(result).toMatch(ISO_DATETIME_REGEX)
|
||||
})
|
||||
|
||||
it('el resultado es una fecha JavaScript válida', () => {
|
||||
const result = prepareDateForBackend('2024-06-15', '09:30')
|
||||
expect(new Date(result!).toString()).not.toBe('Invalid Date')
|
||||
})
|
||||
|
||||
it('usa 00:00 como hora por defecto', () => {
|
||||
const result = prepareDateForBackend('2024-06-15')
|
||||
expect(result).toMatch(ISO_DATETIME_REGEX)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('loadServerDate', () => {
|
||||
|
||||
it.each([
|
||||
[null, ''],
|
||||
[undefined, ''],
|
||||
['', ''],
|
||||
])('devuelve string vacío para %s', (input, expected) => {
|
||||
expect(loadServerDate(input)).toBe(expected)
|
||||
})
|
||||
|
||||
it('convierte ISO UTC a formato YYYY-MM-DD', () => {
|
||||
const result = loadServerDate('2024-06-15T12:00:00.000Z')
|
||||
expect(result).toMatch(ISO_DATE_REGEX)
|
||||
})
|
||||
|
||||
it('retorna fecha plana sin modificarla', () => {
|
||||
expect(loadServerDate('2024-06-15')).toBe('2024-06-15')
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('addDaysLocal', () => {
|
||||
|
||||
it('devuelve string vacío si dateStr está vacío', () => {
|
||||
expect(addDaysLocal('', 5)).toBe('')
|
||||
})
|
||||
|
||||
it.each([
|
||||
['2024-01-01', 1, '2024-01-02'],
|
||||
['2024-01-31', 1, '2024-02-01'],
|
||||
['2024-03-01', -1, '2024-02-29'],
|
||||
])('suma %s + %s días = %s', (date, days, expected) => {
|
||||
expect(addDaysLocal(date, days)).toBe(expected)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('getCurrentLocalYear', () => {
|
||||
|
||||
it('devuelve 2 dígitos', () => {
|
||||
expect(getCurrentLocalYear()).toMatch(/^\d{2}$/)
|
||||
})
|
||||
|
||||
it('corresponde al año actual del sistema', () => {
|
||||
const expected = String(new Date().getFullYear()).slice(-2)
|
||||
expect(getCurrentLocalYear()).toBe(expected)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('getCurrentLocalDate', () => {
|
||||
|
||||
it('devuelve formato YYYY-MM-DD', () => {
|
||||
expect(getCurrentLocalDate()).toMatch(ISO_DATE_REGEX)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('getCurrentLocalTime', () => {
|
||||
|
||||
it('devuelve formato HH:MM', () => {
|
||||
expect(getCurrentLocalTime()).toMatch(TIME_REGEX)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
26
frontend/src/lib/utils.getBackendAssetUrl.test.ts
Normal file
26
frontend/src/lib/utils.getBackendAssetUrl.test.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { getBackendAssetUrl } from './utils'
|
||||
|
||||
describe('getBackendAssetUrl', () => {
|
||||
|
||||
it('devuelve string vacío si path es null', () => {
|
||||
expect(getBackendAssetUrl(null)).toBe('')
|
||||
})
|
||||
|
||||
it('devuelve string vacío si path es undefined', () => {
|
||||
expect(getBackendAssetUrl(undefined)).toBe('')
|
||||
})
|
||||
|
||||
it('devuelve la URL tal cual si ya es completa', () => {
|
||||
expect(getBackendAssetUrl('http://ejemplo.com/archivo.png')).toBe('http://ejemplo.com/archivo.png')
|
||||
})
|
||||
|
||||
it('evita duplicar /api en la URL', () => {
|
||||
expect(getBackendAssetUrl('/api/v1/items', 'http://localhost:8000/api')).toBe('http://localhost:8000/api/v1/items')
|
||||
})
|
||||
|
||||
it('construye URL completa para ruta normal', () => {
|
||||
expect(getBackendAssetUrl('/uploads/file.png', 'http://localhost:8000/api')).toBe('http://localhost:8000/api/uploads/file.png')
|
||||
})
|
||||
|
||||
})
|
||||
46
frontend/src/lib/utils.getFileHelpers.test.ts
Normal file
46
frontend/src/lib/utils.getFileHelpers.test.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { getFileNameFromPath, getFileDisplayName } from './utils'
|
||||
|
||||
describe('getFileNameFromPath', () => {
|
||||
|
||||
it('devuelve string vacío si recibe null', () => {
|
||||
expect(getFileNameFromPath(null)).toBe('')
|
||||
})
|
||||
|
||||
it('devuelve string vacío si recibe undefined', () => {
|
||||
expect(getFileNameFromPath(undefined)).toBe('')
|
||||
})
|
||||
|
||||
it('devuelve solo el nombre del archivo', () => {
|
||||
expect(getFileNameFromPath('/uploads/avatars/file.png')).toBe('file.png')
|
||||
})
|
||||
|
||||
it('devuelve nombre sin query string', () => {
|
||||
expect(getFileNameFromPath('/uploads/file.png?token=123')).toBe('file.png')
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('getFileDisplayName', () => {
|
||||
|
||||
it('devuelve label por defecto si filePath es null', () => {
|
||||
expect(getFileDisplayName(null)).toBe('Seleccionar archivo')
|
||||
})
|
||||
|
||||
it('devuelve label por defecto si filePath es undefined', () => {
|
||||
expect(getFileDisplayName(undefined)).toBe('Seleccionar archivo')
|
||||
})
|
||||
|
||||
it('devuelve label personalizado si se pasa defaultLabel', () => {
|
||||
expect(getFileDisplayName(null, undefined, 'Subir archivo')).toBe('Subir archivo')
|
||||
})
|
||||
|
||||
it('devuelve solo el nombre del archivo sin fileType', () => {
|
||||
expect(getFileDisplayName('/uploads/file.png')).toBe('file.png')
|
||||
})
|
||||
|
||||
it('devuelve nombre con tipo en mayúsculas', () => {
|
||||
expect(getFileDisplayName('/uploads/file.png', 'pdf')).toBe('file.png (PDF)')
|
||||
})
|
||||
|
||||
})
|
||||
46
frontend/src/lib/utils.getInvoiceTypeColor.test.ts
Normal file
46
frontend/src/lib/utils.getInvoiceTypeColor.test.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { getInvoiceTypeColor } from './utils'
|
||||
|
||||
const DEFAULT_COLOR = 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200'
|
||||
|
||||
describe('getInvoiceTypeColor', () => {
|
||||
|
||||
describe('cuando no recibe tipo', () => {
|
||||
it.each([
|
||||
[undefined, 'undefined'],
|
||||
[null, 'null'],
|
||||
['', 'string vacío'],
|
||||
] as const)('devuelve color gris por defecto para %s', (input) => {
|
||||
expect(getInvoiceTypeColor(input)).toBe(DEFAULT_COLOR)
|
||||
})
|
||||
})
|
||||
|
||||
describe('tipos de factura conocidos', () => {
|
||||
it.each([
|
||||
// input color esperado tipo
|
||||
['tem', 'bg-red-100', 'TEM exacto'],
|
||||
['impo tem', 'bg-red-100', 'TEM con prefijo'],
|
||||
['def', 'bg-green-100', 'DEF exacto'],
|
||||
['impo def', 'bg-green-100', 'DEF con prefijo'],
|
||||
['mex', 'bg-purple-100', 'MEX exacto'],
|
||||
['comp mex', 'bg-purple-100', 'MEX con prefijo'],
|
||||
['cr', 'bg-blue-100', 'CAM REG exacto'],
|
||||
['cam. reg.', 'bg-blue-100', 'CAM REG con puntos'],
|
||||
['cam reg', 'bg-blue-100', 'CAM REG sin puntos'],
|
||||
['expo', 'bg-blue-100', 'EXPO'],
|
||||
['exdef', 'bg-blue-100', 'EXDEF'],
|
||||
['pterm', 'bg-blue-100', 'PTERM'],
|
||||
['repar', 'bg-sky-100', 'REPAR exacto'],
|
||||
['imp. rep.', 'bg-sky-100', 'IMP REP con puntos'],
|
||||
['imp rep', 'bg-sky-100', 'IMP REP sin puntos'],
|
||||
] as const)('retorna el color correcto para %s — %s', (input, expectedColor) => {
|
||||
expect(getInvoiceTypeColor(input)).toContain(expectedColor)
|
||||
})
|
||||
})
|
||||
|
||||
describe('tipo desconocido', () => {
|
||||
it('devuelve color gris para tipo no reconocido', () => {
|
||||
expect(getInvoiceTypeColor('TIPO_INEXISTENTE')).toBe(DEFAULT_COLOR)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user