feat(mejoras): 14 mejoras al CRM Frasa

Fase 1 - UI: dark mode, skeletons, toasts
Fase 2 - Filtros: avanzados Mi día, búsqueda global Ctrl+K
Fase 3 - Datos: export Excel, notas apartados, auditoría
Fase 4 - Dashboard: gráficas vendedor + métricas uso
Fase 5 - Responsive: tablets optimizadas
Fase 6 - PWA: IndexedDB offline, push notifications

Backend: 3 tablas nuevas, 6 services, 6 endpoints nuevos
721 API tests + fronts verdes.
This commit is contained in:
2026-09-07 06:04:49 +00:00
parent 388e1da338
commit c77b4c0628
170 changed files with 5802 additions and 637 deletions

View File

@@ -0,0 +1,67 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { Request, Response } from 'express'
// ---------- Service mockeado ----------
const buscarMock = vi.hoisted(() => vi.fn())
vi.mock('../services/buscar.service.js', () => ({ buscar: buscarMock }))
import { buscarEndpoint } from './buscar.controller.js'
function req(query: Record<string, string> = {}): Request {
return { query } as unknown as Request
}
function res(): Response {
const json = vi.fn()
return { json } as unknown as Response
}
beforeEach(() => {
vi.clearAllMocks()
})
describe('buscarEndpoint (GET /api/buscar)', () => {
it('extrae q del query string y llama buscar', async () => {
buscarMock.mockResolvedValue([])
const r = res()
await buscarEndpoint(req({ q: 'María' }), r)
expect(buscarMock).toHaveBeenCalledWith('María', 20)
expect(r.json).toHaveBeenCalledWith([])
})
it('q vacío (no string) → termino vacío', async () => {
buscarMock.mockResolvedValue([])
const r = res()
await buscarEndpoint(req({}), r)
expect(buscarMock).toHaveBeenCalledWith('', 20)
})
it('limite válido del query string', async () => {
buscarMock.mockResolvedValue([])
const r = res()
await buscarEndpoint(req({ q: 'x', limite: '10' }), r)
expect(buscarMock).toHaveBeenCalledWith('x', 10)
})
it('limite inválido o > 50 se clampa a 20', async () => {
buscarMock.mockResolvedValue([])
const r = res()
await buscarEndpoint(req({ q: 'x', limite: 'abc' }), r)
expect(buscarMock).toHaveBeenCalledWith('x', 20)
const r2 = res()
await buscarEndpoint(req({ q: 'x', limite: '100' }), r2)
expect(buscarMock).toHaveBeenCalledWith('x', 50)
})
it('devuelve los resultados del service como JSON', async () => {
const fakeResults = [
{ tipo: 'lead', id: 'l1', nombre: 'Test', detalle: '', url: '/app/#leads' },
]
buscarMock.mockResolvedValue(fakeResults)
const r = res()
await buscarEndpoint(req({ q: 'Test' }), r)
expect(r.json).toHaveBeenCalledWith(fakeResults)
})
})