Files
FRASA-CAS/apps/landing/src/components/TablaCaptura.test.tsx

323 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// @vitest-environment jsdom
import { afterEach, describe, expect, it, vi } from 'vitest'
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
import { TablaCaptura } from './TablaCaptura'
import type { CampoDef, DatosCaptura } from '../services/captura'
const CAMPOS_OBRA: CampoDef[] = [
{ campo: 'certificacion_gas', tipo: 'boolean', label: 'Certificación Gas' },
{ campo: 'comentarios_obra', tipo: 'text', label: 'Comentarios' },
]
const CAMPOS_VENTAS: CampoDef[] = [
{ campo: 'estatus', tipo: 'select', label: 'Estatus', options: ['DISPONIBLE', 'APARTADO', 'VENDIDO'] },
{ campo: 'precio_venta', tipo: 'number', label: 'Precio Venta' },
]
const DATOS: DatosCaptura = {
'409-1': { certificacion_gas: 'true', comentarios_obra: 'Listo' },
'409-2': { certificacion_gas: null, comentarios_obra: null },
}
const UNIDADES = ['409-1', '409-2']
afterEach(() => { cleanup() })
describe('TablaCaptura', () => {
it('renderiza la tabla con unidades y campos', () => {
const onGuardar = vi.fn()
render(
<TablaCaptura
unidades={UNIDADES}
campos={CAMPOS_OBRA}
datos={DATOS}
onGuardar={onGuardar}
guardando={false}
/>,
)
expect(screen.getByText('409-1')).toBeTruthy()
expect(screen.getByText('409-2')).toBeTruthy()
expect(screen.getByText('Certificación Gas')).toBeTruthy()
expect(screen.getByText('Comentarios')).toBeTruthy()
})
it('muestra checkboxes para campos boolean', () => {
const onGuardar = vi.fn()
render(
<TablaCaptura
unidades={UNIDADES}
campos={CAMPOS_OBRA}
datos={DATOS}
onGuardar={onGuardar}
guardando={false}
/>,
)
const checkboxes = screen.getAllByRole('checkbox')
expect(checkboxes.length).toBe(2) // 2 unidades * 1 boolean campo
// Primera unidad tiene certificacion_gas = 'true'
expect((checkboxes[0] as HTMLInputElement).checked).toBe(true)
// Segunda unidad tiene null
expect((checkboxes[1] as HTMLInputElement).checked).toBe(false)
})
it('muestra select para campos select', () => {
const onGuardar = vi.fn()
render(
<TablaCaptura
unidades={UNIDADES}
campos={CAMPOS_VENTAS}
datos={{}}
onGuardar={onGuardar}
guardando={false}
/>,
)
const selects = screen.getAllByRole('combobox')
expect(selects.length).toBe(2) // 2 unidades * 1 select campo
})
it('muestra "sin unidades" cuando la lista está vacía', () => {
const onGuardar = vi.fn()
render(
<TablaCaptura
unidades={[]}
campos={CAMPOS_OBRA}
datos={{}}
onGuardar={onGuardar}
guardando={false}
/>,
)
expect(screen.getByText(/No hay unidades/)).toBeTruthy()
})
it('edita una celda y muestra el botón de guardar', () => {
const onGuardar = vi.fn()
render(
<TablaCaptura
unidades={UNIDADES}
campos={CAMPOS_OBRA}
datos={DATOS}
onGuardar={onGuardar}
guardando={false}
/>,
)
// Click en el checkbox de la segunda unidad (cambiar de unchecked a checked)
const checkboxes = screen.getAllByRole('checkbox')
fireEvent.click(checkboxes[1])
// Debería aparecer "Hay cambios sin guardar"
expect(screen.getByText('Hay cambios sin guardar')).toBeTruthy()
expect(screen.getByText('Guardar cambios')).toBeTruthy()
expect(screen.getByText('Descartar')).toBeTruthy()
})
it('click en Guardar llama a onGuardar con los cambios dirty', () => {
const onGuardar = vi.fn()
render(
<TablaCaptura
unidades={UNIDADES}
campos={CAMPOS_OBRA}
datos={DATOS}
onGuardar={onGuardar}
guardando={false}
/>,
)
// Cambiar checkbox de la segunda unidad
const checkboxes = screen.getAllByRole('checkbox')
fireEvent.click(checkboxes[1])
// Click en Guardar
fireEvent.click(screen.getByText('Guardar cambios'))
expect(onGuardar).toHaveBeenCalledTimes(1)
const dirty = onGuardar.mock.calls[0][0]
expect(dirty['409-2']).toEqual({ certificacion_gas: 'true' })
})
it('click en Descartar elimina los cambios', () => {
const onGuardar = vi.fn()
render(
<TablaCaptura
unidades={UNIDADES}
campos={CAMPOS_OBRA}
datos={DATOS}
onGuardar={onGuardar}
guardando={false}
/>,
)
// Cambiar checkbox
const checkboxes = screen.getAllByRole('checkbox')
fireEvent.click(checkboxes[1])
// Descartar
fireEvent.click(screen.getByText('Descartar'))
// Ya no debería mostrar "Hay cambios sin guardar"
expect(screen.queryByText('Hay cambios sin guardar')).toBeNull()
})
it('filtra por estatus', () => {
const onGuardar = vi.fn()
const datosConEstatus: DatosCaptura = {
'409-1': { estatus: 'VENDIDO' },
'409-2': { estatus: 'DISPONIBLE' },
}
render(
<TablaCaptura
unidades={UNIDADES}
campos={CAMPOS_VENTAS}
datos={datosConEstatus}
onGuardar={onGuardar}
guardando={false}
filtroEstatus="VENDIDO"
/>,
)
expect(screen.getByText('409-1')).toBeTruthy()
expect(screen.queryByText('409-2')).toBeNull()
})
it('paginación: muestra paginación cuando hay >50 unidades', () => {
const onGuardar = vi.fn()
const unidadesGrandes = Array.from({ length: 60 }, (_, i) => `U-${String(i + 1).padStart(3, '0')}`)
render(
<TablaCaptura
unidades={unidadesGrandes}
campos={CAMPOS_OBRA}
datos={{}}
onGuardar={onGuardar}
guardando={false}
/>,
)
expect(screen.getByText(/Mostrando 1–50 de 60/)).toBeTruthy()
expect(screen.getByText('Anterior')).toBeTruthy()
expect(screen.getByText('Siguiente')).toBeTruthy()
// Navigate to page 2
fireEvent.click(screen.getByText('Siguiente'))
expect(screen.getByText(/Mostrando 51–60 de 60/)).toBeTruthy()
})
it('boolean con conFecha: fecha + checkbox en una celda, semáforo y dirty del campo fecha', () => {
const onGuardar = vi.fn()
const camposEmparejados: CampoDef[] = [
{ campo: 'certificacion_gas', tipo: 'boolean', label: 'Certificación Gas', conFecha: 'certificacion_gas_fecha' },
{ campo: 'certificacion_gas_fecha', tipo: 'date', label: 'Fecha Certificación Gas' },
]
const datosEmparejados: DatosCaptura = {
// Check activo → fecha en verde
'409-1': { certificacion_gas: 'true', certificacion_gas_fecha: '2026-01-01' },
// Fecha pasada sin check → fecha en rojo
'409-2': { certificacion_gas: null, certificacion_gas_fecha: '2026-01-01' },
}
const { container } = render(
<TablaCaptura
unidades={UNIDADES}
campos={camposEmparejados}
datos={datosEmparejados}
onGuardar={onGuardar}
guardando={false}
/>,
)
// La columna standalone de fecha se oculta: solo Unidad + Certificación Gas
expect(screen.getAllByRole('columnheader')).toHaveLength(2)
expect(screen.queryByText('Fecha Certificación Gas')).toBeNull()
// Un date input y un checkbox por unidad
const fechas = container.querySelectorAll('input[type="date"]')
expect(fechas).toHaveLength(2)
expect((fechas[0] as HTMLInputElement).value).toBe('2026-01-01')
// Semáforo: check activo → verde; fecha pasada sin check → rojo
expect(fechas[0].className).toContain('text-green-600')
expect(fechas[1].className).toContain('text-red-600')
// Cambiar la fecha marca dirty en el campo _fecha y se guarda en el bulk
fireEvent.change(fechas[1], { target: { value: '2026-12-01' } })
// El input muestra el valor recién elegido (no se resetea al guardado)
expect((fechas[1] as HTMLInputElement).value).toBe('2026-12-01')
fireEvent.click(screen.getByText('Guardar cambios'))
expect(onGuardar).toHaveBeenCalledTimes(1)
const dirty = onGuardar.mock.calls[0][0]
expect(dirty['409-2']).toEqual({ certificacion_gas_fecha: '2026-12-01' })
})
it('regresión: escribir en un campo text no se borra (valor viene de dirty)', () => {
const onGuardar = vi.fn()
const { container } = render(
<TablaCaptura
unidades={UNIDADES}
campos={CAMPOS_OBRA}
datos={DATOS}
onGuardar={onGuardar}
guardando={false}
/>,
)
// Teclear en comentarios de la primera unidad (original: 'Listo')
const textos = container.querySelectorAll('input[type="text"]')
expect(textos.length).toBe(2) // 1 campo text * 2 unidades
fireEvent.change(textos[0], { target: { value: 'Nota X' } })
expect((textos[0] as HTMLInputElement).value).toBe('Nota X')
// Guardar persiste el texto tecleado
fireEvent.click(screen.getByText('Guardar cambios'))
expect(onGuardar).toHaveBeenCalledTimes(1)
expect(onGuardar.mock.calls[0][0]['409-1']).toEqual({ comentarios_obra: 'Nota X' })
})
it('fechas de Obra guardadas: disabled para no-admin, editables para admin', () => {
const camposEmparejados: CampoDef[] = [
{ campo: 'certificacion_gas', tipo: 'boolean', label: 'Certificación Gas', conFecha: 'certificacion_gas_fecha' },
{ campo: 'certificacion_gas_fecha', tipo: 'date', label: 'Fecha Certificación Gas' },
]
const datosConFecha: DatosCaptura = {
'409-1': { certificacion_gas: null, certificacion_gas_fecha: '2026-01-01' },
'409-2': { certificacion_gas: null, certificacion_gas_fecha: null },
}
// No-admin: la fecha guardada se deshabilita, la vacía sigue editable
const r1 = render(
<TablaCaptura
unidades={UNIDADES}
campos={camposEmparejados}
datos={datosConFecha}
onGuardar={vi.fn()}
guardando={false}
puedeEditarFechas={false}
/>,
)
const fechas1 = r1.container.querySelectorAll('input[type="date"]')
expect((fechas1[0] as HTMLInputElement).disabled).toBe(true)
expect((fechas1[1] as HTMLInputElement).disabled).toBe(false)
r1.unmount()
// Admin: todas editables
const r2 = render(
<TablaCaptura
unidades={UNIDADES}
campos={camposEmparejados}
datos={datosConFecha}
onGuardar={vi.fn()}
guardando={false}
puedeEditarFechas
/>,
)
const fechas2 = r2.container.querySelectorAll('input[type="date"]')
expect((fechas2[0] as HTMLInputElement).disabled).toBe(false)
expect((fechas2[1] as HTMLInputElement).disabled).toBe(false)
})
})