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.
141 lines
4.4 KiB
TypeScript
141 lines
4.4 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
// ---------- Prisma mockeado ----------
|
|
const prismaMock = vi.hoisted(() => ({
|
|
pushSubscription: {
|
|
upsert: vi.fn(),
|
|
deleteMany: vi.fn(),
|
|
findUnique: vi.fn(),
|
|
},
|
|
usuario: {
|
|
findFirst: vi.fn(),
|
|
},
|
|
}))
|
|
|
|
vi.mock('./prisma.js', () => ({ prisma: prismaMock }))
|
|
|
|
// ---------- web-push mockeado ----------
|
|
const webPushMock = vi.hoisted(() => ({
|
|
setVapidDetails: vi.fn(),
|
|
sendNotification: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('web-push', () => ({ default: webPushMock }))
|
|
|
|
// ---------- config mockeado ----------
|
|
vi.mock('../config/index.js', () => ({
|
|
config: {
|
|
push: {
|
|
vapidPublicKey: 'test-public-key',
|
|
vapidPrivateKey: 'test-private-key',
|
|
vapidEmail: 'mailto:test@frasa.com',
|
|
},
|
|
},
|
|
}))
|
|
|
|
import {
|
|
registrarSuscripcion,
|
|
eliminarSuscripcion,
|
|
enviarPush,
|
|
usuarioIdPorMondayUserId,
|
|
obtenerVapidPublicKey,
|
|
} from './push.service.js'
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
describe('push.service', () => {
|
|
describe('registrarSuscripcion', () => {
|
|
it('hace upsert con los datos de la suscripción', async () => {
|
|
prismaMock.pushSubscription.upsert.mockResolvedValue({})
|
|
await registrarSuscripcion('user-1', {
|
|
endpoint: 'https://fcm.googleapis.com/fcm/send/abc',
|
|
p256dh: 'key123',
|
|
auth: 'auth456',
|
|
})
|
|
expect(prismaMock.pushSubscription.upsert).toHaveBeenCalledWith({
|
|
where: { usuarioId: 'user-1' },
|
|
update: {
|
|
endpoint: 'https://fcm.googleapis.com/fcm/send/abc',
|
|
p256dh: 'key123',
|
|
auth: 'auth456',
|
|
},
|
|
create: {
|
|
usuarioId: 'user-1',
|
|
endpoint: 'https://fcm.googleapis.com/fcm/send/abc',
|
|
p256dh: 'key123',
|
|
auth: 'auth456',
|
|
},
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('eliminarSuscripcion', () => {
|
|
it('elimina todas las suscripciones del usuario', async () => {
|
|
prismaMock.pushSubscription.deleteMany.mockResolvedValue({ count: 1 })
|
|
await eliminarSuscripcion('user-1')
|
|
expect(prismaMock.pushSubscription.deleteMany).toHaveBeenCalledWith({ where: { usuarioId: 'user-1' } })
|
|
})
|
|
})
|
|
|
|
describe('enviarPush', () => {
|
|
it('envía push si el usuario tiene suscripción', async () => {
|
|
prismaMock.pushSubscription.findUnique.mockResolvedValue({
|
|
endpoint: 'https://fcm.googleapis.com/fcm/send/abc',
|
|
p256dh: 'key123',
|
|
auth: 'auth456',
|
|
})
|
|
webPushMock.sendNotification.mockResolvedValue({ statusCode: 201 })
|
|
|
|
const result = await enviarPush('user-1', 'Test', 'Cuerpo del mensaje')
|
|
expect(result).toBe(true)
|
|
expect(webPushMock.sendNotification).toHaveBeenCalledWith(
|
|
{ endpoint: 'https://fcm.googleapis.com/fcm/send/abc', keys: { p256dh: 'key123', auth: 'auth456' } },
|
|
expect.stringContaining('"titulo":"Test"'),
|
|
)
|
|
})
|
|
|
|
it('devuelve false si el usuario no tiene suscripción', async () => {
|
|
prismaMock.pushSubscription.findUnique.mockResolvedValue(null)
|
|
const result = await enviarPush('user-1', 'Test', 'Cuerpo')
|
|
expect(result).toBe(false)
|
|
expect(webPushMock.sendNotification).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('elimina suscripción inválida (404) y devuelve false', async () => {
|
|
prismaMock.pushSubscription.findUnique.mockResolvedValue({
|
|
endpoint: 'https://expired',
|
|
p256dh: 'key',
|
|
auth: 'auth',
|
|
})
|
|
webPushMock.sendNotification.mockRejectedValue({ statusCode: 404 })
|
|
prismaMock.pushSubscription.deleteMany.mockResolvedValue({ count: 1 })
|
|
|
|
const result = await enviarPush('user-1', 'Test', 'Cuerpo')
|
|
expect(result).toBe(false)
|
|
expect(prismaMock.pushSubscription.deleteMany).toHaveBeenCalledWith({ where: { usuarioId: 'user-1' } })
|
|
})
|
|
})
|
|
|
|
describe('usuarioIdPorMondayUserId', () => {
|
|
it('devuelve el usuarioId si encuentra el usuario', async () => {
|
|
prismaMock.usuario.findFirst.mockResolvedValue({ id: 'local-user-1' })
|
|
const result = await usuarioIdPorMondayUserId(12345)
|
|
expect(result).toBe('local-user-1')
|
|
})
|
|
|
|
it('devuelve null si no encuentra el usuario', async () => {
|
|
prismaMock.usuario.findFirst.mockResolvedValue(null)
|
|
const result = await usuarioIdPorMondayUserId(99999)
|
|
expect(result).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('obtenerVapidPublicKey', () => {
|
|
it('devuelve la VAPID public key del config', () => {
|
|
expect(obtenerVapidPublicKey()).toBe('test-public-key')
|
|
})
|
|
})
|
|
})
|