feat: MVP Fase 0 — Process Cost Platform v0.1.0
Plataforma completa de análisis de costos operativos basada en BPMN 2.0. Funcionalidades incluidas: - Import de archivos BPMN 2.0 por drag & drop + 3 procesos de ejemplo - Visualización read-only del diagrama (bpmn-js) - Configuración de actividades (costo, tiempo, recursos asignados) - CRUD de recursos (rol, persona, sistema, equipo, insumo) - Configuración global (moneda, overhead %, nombre, cliente) - Motor de simulación determinístico agregado con propagación de gateways XOR/AND - Reporte visual con mapa de calor en dos modos (relativo/absoluto) - Gráficos: KPIs, top actividades, composición, costo por recurso - Export PDF (jsPDF + html2canvas) y CSV (papaparse) - Persistencia en IndexedDB (Dexie) - 268 tests unitarios/integración + 6 E2E con Playwright - Deploy: https://process-cost-platform.pages.dev Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
83
tests/lib/export/slug.test.ts
Normal file
83
tests/lib/export/slug.test.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { slugify, buildFileName } from '@/lib/export/slug'
|
||||
|
||||
describe('slugify', () => {
|
||||
it('"Proceso de Compras" → "proceso-de-compras"', () => {
|
||||
expect(slugify('Proceso de Compras')).toBe('proceso-de-compras')
|
||||
})
|
||||
|
||||
it('"Análisis Año 2025" → "analisis-ano-2025"', () => {
|
||||
expect(slugify('Análisis Año 2025')).toBe('analisis-ano-2025')
|
||||
})
|
||||
|
||||
it('elimina la ñ → n', () => {
|
||||
expect(slugify('Gestión de Compañía')).toBe('gestion-de-compania')
|
||||
})
|
||||
|
||||
it('elimina acentos (á é í ó ú ü)', () => {
|
||||
expect(slugify('Ú ltimo Análisis Crítico')).toBe('u-ltimo-analisis-critico')
|
||||
})
|
||||
|
||||
it('caracteres especiales → guiones', () => {
|
||||
expect(slugify('Proceso #1 / Sub-proceso (test)')).toBe('proceso-1-sub-proceso-test')
|
||||
})
|
||||
|
||||
it('múltiples espacios → un solo guión', () => {
|
||||
expect(slugify('Proceso de ventas')).toBe('proceso-de-ventas')
|
||||
})
|
||||
|
||||
it('string vacío → string vacío', () => {
|
||||
expect(slugify('')).toBe('')
|
||||
})
|
||||
|
||||
it('solo números → se mantienen', () => {
|
||||
expect(slugify('2025')).toBe('2025')
|
||||
})
|
||||
|
||||
it('ya en minúsculas sin acentos → sin cambios relevantes', () => {
|
||||
expect(slugify('proceso-lineal')).toBe('proceso-lineal')
|
||||
})
|
||||
|
||||
it('limita a 60 caracteres', () => {
|
||||
const long = 'a'.repeat(100)
|
||||
expect(slugify(long)).toHaveLength(60)
|
||||
})
|
||||
})
|
||||
|
||||
describe('buildFileName', () => {
|
||||
// new Date(año, mes0, día) → fecha local sin ambigüedad de timezone
|
||||
const date = new Date(2026, 4, 13) // 13 de mayo de 2026, local time
|
||||
|
||||
it('genera filename con proceso + cliente + fecha', () => {
|
||||
const name = buildFileName('Proceso de Ventas', 'Banco ABC', date, 'pdf')
|
||||
expect(name).toBe('proceso-de-ventas_banco-abc_20260513.pdf')
|
||||
})
|
||||
|
||||
it('omite cliente si está vacío', () => {
|
||||
const name = buildFileName('Proceso de Ventas', '', date, 'csv')
|
||||
expect(name).toBe('proceso-de-ventas_20260513.csv')
|
||||
})
|
||||
|
||||
it('omite cliente si es undefined', () => {
|
||||
const name = buildFileName('Proceso', undefined, date, 'pdf')
|
||||
expect(name).toBe('proceso_20260513.pdf')
|
||||
})
|
||||
|
||||
it('extensión csv correcta', () => {
|
||||
const name = buildFileName('test', 'cliente', date, 'csv')
|
||||
expect(name).toMatch(/\.csv$/)
|
||||
})
|
||||
|
||||
it('extensión pdf correcta', () => {
|
||||
const name = buildFileName('test', 'cliente', date, 'pdf')
|
||||
expect(name).toMatch(/\.pdf$/)
|
||||
})
|
||||
|
||||
it('nombre con caracteres especiales queda limpio (sin #, paréntesis)', () => {
|
||||
const name = buildFileName('Análisis #1', 'Empresa S.A.', date, 'csv')
|
||||
// Verificar solo el slug (sin la extensión que tiene un punto legítimo)
|
||||
const slug = name.replace(/\.\w+$/, '')
|
||||
expect(slug).not.toMatch(/[#()]/)
|
||||
expect(name).toMatch(/analisis/)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user