323 lines
10 KiB
TypeScript
323 lines
10 KiB
TypeScript
// @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)
|
||
})
|
||
})
|