Files
FRASA-CAS/apps/landing/src/data/modulos.test.ts
Consult AS Technology f72354f775 feat: roles con modulos por rol + mejoras Mi dia y descarte
- Matriz de roles (vendedor, administracion_venta, director_ventas,
  escrituracion, direccion_general, admin) con modulos visibles por rol;
  auth/me y login incluyen 'modulos'; PATCH /usuarios/:id/rol (admin).
- Landing: Cobranza -> Clientes, Chat IA -> Chat Hermes, nuevo modulo
  Contactos (vista interna leyendo el tablero Contactos), grid filtrado
  por rol, rol visible en Configuracion de cuenta.
- Dashboards: tab 'Tablero comercial' y tabs filtrados por rol.
- Boton '<- Menu' en el header de todas las apps.
- Mi dia: cada lead muestra el ultimo cambio (que, quien, cuando) desde
  el activity log de Monday (normalizacion de created_at 100ns/us/ms).
- Descarte: el motivo se crea en el grupo de su categoria en el tablero
  Motivos No Compra.
2026-09-04 09:12:01 +00:00

83 lines
3.0 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { MODULOS, esModuloVisible, type Modulo } from './modulos'
const porId = (id: string): Modulo => {
const modulo = MODULOS.find((m) => m.id === id)
if (!modulo) throw new Error(`Módulo ${id} no existe`)
return modulo
}
describe('MODULOS', () => {
it('expone los 7 módulos del ecosistema', () => {
expect(MODULOS).toHaveLength(7)
expect(MODULOS.map((m) => m.id)).toEqual([
'cotizador',
'alta-clientes',
'clientes',
'chat-hermes',
'tableros',
'mi-dia',
'contactos',
])
})
it('cada módulo tiene nombre, descripción y claves ligadas a la matriz de roles', () => {
for (const modulo of MODULOS) {
expect(modulo.nombre.length).toBeGreaterThan(0)
expect(modulo.descripcion.length).toBeGreaterThan(0)
expect(modulo.claves.length).toBeGreaterThan(0)
if (modulo.url !== undefined) expect(modulo.url).toMatch(/^\/[a-z]+\/$/)
}
})
it('apunta a los prefijos correctos (same-origin detrás de NGINX)', () => {
expect(porId('cotizador').url).toBe('/quote/')
expect(porId('alta-clientes').url).toBe('/client/')
expect(porId('clientes').url).toBe('/pay/')
expect(porId('chat-hermes').url).toBe('/ai/')
expect(porId('tableros').url).toBe('/dash/')
expect(porId('mi-dia').url).toBe('/hoy/')
})
it('Contactos es vista interna (sin url externa)', () => {
expect(porId('contactos').url).toBeUndefined()
})
it('liga cada módulo a la matriz de roles del API', () => {
expect(porId('cotizador').claves).toEqual(['cotizador'])
expect(porId('alta-clientes').claves).toEqual(['alta-cliente', 'alta-afluencia'])
expect(porId('clientes').claves).toEqual(['cobranza'])
expect(porId('chat-hermes').claves).toEqual(['chat'])
expect(porId('tableros').claves).toEqual(['mi-tablero', 'tablero-comercial'])
expect(porId('mi-dia').claves).toEqual(['mi-dia'])
expect(porId('contactos').claves).toEqual(['contactos'])
})
it('no hay ids ni URLs duplicadas', () => {
const ids = MODULOS.map((m) => m.id)
const urls = MODULOS.map((m) => m.url).filter((u) => u !== undefined)
expect(new Set(ids).size).toBe(MODULOS.length)
expect(new Set(urls).size).toBe(urls.length)
})
})
describe('esModuloVisible', () => {
it('sin sesión (null) muestra todos los módulos', () => {
for (const modulo of MODULOS) expect(esModuloVisible(modulo, null)).toBe(true)
})
it('con sesión, un módulo es visible si ALGUNA de sus claves está permitida', () => {
const vendedor = ['mi-dia', 'alta-cliente', 'alta-afluencia', 'cotizador', 'mi-tablero', 'contactos', 'chat']
expect(MODULOS.filter((m) => esModuloVisible(m, vendedor)).map((m) => m.id)).toEqual([
'cotizador',
'alta-clientes',
'chat-hermes',
'tableros', // visible por 'mi-tablero'
'mi-dia',
'contactos',
])
// 'cobranza' no está permitida → Clientes no se muestra
expect(esModuloVisible(porId('clientes'), vendedor)).toBe(false)
})
})