You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

314 lines
9.8 KiB
JavaScript

import { describe, it, expect, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { h } from 'vue'
import ConvocatoriasSection from '../../src/components/WebPageSections/ConvocatoriasSection.vue'
// Mock Ant Design Vue components
const mockAntComponents = {
'a-badge': {
template: '<span class="a-badge"><slot /></span>',
props: ['count']
},
'a-card': {
template: '<div class="a-card"><slot /></div>'
},
'a-tag': {
template: '<span class="a-tag" :class="color"><slot /></span>',
props: ['color']
},
'a-divider': {
template: '<hr class="a-divider" />'
},
'a-button': {
template: '<button class="a-button" @click="$emit(\'click\')"><slot /><slot name="icon" /></button>',
props: ['type', 'size', 'ghost'],
emits: ['click']
},
'a-image': {
template: '<img class="a-image" :src="src" :alt="alt" />',
props: ['src', 'alt', 'preview']
},
'a-empty': {
template: '<div class="a-empty"><slot /></div>',
props: ['description']
}
}
// Mock icons
const mockIcons = {
FileTextOutlined: { template: '<span class="icon file-text" />' },
DollarOutlined: { template: '<span class="icon dollar" />' },
TeamOutlined: { template: '<span class="icon team" />' },
CalendarOutlined: { template: '<span class="icon calendar" />' },
FormOutlined: { template: '<span class="icon form" />' }
}
const createWrapper = (props = {}) => {
return mount(ConvocatoriasSection, {
props,
global: {
stubs: {
...mockAntComponents,
...mockIcons
}
}
})
}
describe('ConvocatoriasSection', () => {
describe('when no procesos are provided', () => {
it('renders empty state message', () => {
const wrapper = createWrapper({ procesos: [] })
expect(wrapper.find('.empty-state').exists()).toBe(true)
expect(wrapper.text()).toContain('No hay convocatorias vigentes en este momento')
})
it('does not render main or secondary cards', () => {
const wrapper = createWrapper({ procesos: [] })
expect(wrapper.find('.main-convocatoria-card').exists()).toBe(false)
expect(wrapper.find('.secondary-list').exists()).toBe(false)
})
})
describe('when one proceso is provided', () => {
const singleProceso = [
{
id: 1,
titulo: 'Admisión Ordinaria 2026',
subtitulo: 'Proceso regular de admisión',
descripcion: 'Descripción del proceso de admisión',
estado: 'publicado',
fecha_inicio_inscripcion: '2026-02-01',
fecha_fin_inscripcion: '2026-02-28',
link_preinscripcion: 'https://example.com/preinscripcion'
}
]
it('renders main convocatoria card with correct title', () => {
const wrapper = createWrapper({ procesos: singleProceso })
expect(wrapper.find('.main-convocatoria-card').exists()).toBe(true)
expect(wrapper.find('h3').text()).toBe('Admisión Ordinaria 2026')
})
it('renders the "Principal" badge on main card', () => {
const wrapper = createWrapper({ procesos: singleProceso })
expect(wrapper.find('.card-badge').exists()).toBe(true)
expect(wrapper.find('.card-badge').text()).toBe('Principal')
})
it('renders action buttons for requisitos, pagos, vacantes, and cronograma', () => {
const wrapper = createWrapper({ procesos: singleProceso })
const actionButtons = wrapper.findAll('.action-btn')
expect(actionButtons.length).toBe(4)
const buttonTexts = actionButtons.map(btn => btn.text())
expect(buttonTexts).toContain('Requisitos')
expect(buttonTexts).toContain('Pagos')
expect(buttonTexts).toContain('Vacantes')
expect(buttonTexts).toContain('Cronograma')
})
it('does not render secondary cards list when only one proceso', () => {
const wrapper = createWrapper({ procesos: singleProceso })
expect(wrapper.find('.secondary-list').exists()).toBe(false)
})
it('displays the estado tag with correct label', () => {
const wrapper = createWrapper({ procesos: singleProceso })
const statusTag = wrapper.find('.status-tag')
expect(statusTag.exists()).toBe(true)
expect(statusTag.text()).toBe('Abierto') // 'publicado' maps to 'Abierto'
})
it('displays inscription dates when provided', () => {
const wrapper = createWrapper({ procesos: singleProceso })
expect(wrapper.text()).toContain('Inscripciones:')
})
})
describe('when multiple procesos are provided', () => {
const multipleProcesos = [
{
id: 1,
titulo: 'Proceso Principal',
descripcion: 'Descripción del proceso principal',
estado: 'publicado'
},
{
id: 2,
titulo: 'Proceso Secundario 1',
descripcion: 'Descripción secundario 1',
estado: 'en_proceso'
},
{
id: 3,
titulo: 'Proceso Secundario 2',
descripcion: 'Descripción secundario 2',
estado: 'nuevo'
}
]
it('renders main card for first proceso', () => {
const wrapper = createWrapper({ procesos: multipleProcesos })
expect(wrapper.find('.main-convocatoria-card').exists()).toBe(true)
expect(wrapper.find('h3').text()).toBe('Proceso Principal')
})
it('renders secondary cards for remaining procesos', () => {
const wrapper = createWrapper({ procesos: multipleProcesos })
expect(wrapper.find('.secondary-list').exists()).toBe(true)
const secondaryCards = wrapper.findAll('.secondary-convocatoria-card')
expect(secondaryCards.length).toBe(2)
})
it('renders correct titles in secondary cards', () => {
const wrapper = createWrapper({ procesos: multipleProcesos })
const secondaryTitles = wrapper.findAll('.secondary-title')
expect(secondaryTitles[0].text()).toBe('Proceso Secundario 1')
expect(secondaryTitles[1].text()).toBe('Proceso Secundario 2')
})
it('renders estado tags with correct colors for different estados', () => {
const wrapper = createWrapper({ procesos: multipleProcesos })
const statusTags = wrapper.findAll('.status-tag')
// First is main card (publicado), then secondary cards
expect(statusTags[0].text()).toBe('Abierto') // publicado
expect(statusTags[1].text()).toBe('En Proceso') // en_proceso
expect(statusTags[2].text()).toBe('PRÓXIMAMENTE') // nuevo
})
})
describe('emits events correctly', () => {
const procesos = [
{
id: 1,
titulo: 'Proceso Test',
estado: 'publicado'
},
{
id: 2,
titulo: 'Proceso Secundario',
estado: 'publicado'
}
]
it('emits show-modal event with correct payload when action button is clicked', async () => {
const wrapper = createWrapper({ procesos })
const actionButtons = wrapper.findAll('.action-btn')
await actionButtons[0].trigger('click') // Requisitos button
expect(wrapper.emitted('show-modal')).toBeTruthy()
expect(wrapper.emitted('show-modal')[0]).toEqual([
{ procesoId: 1, tipo: 'requisitos' }
])
})
it('emits show-modal for pagos button', async () => {
const wrapper = createWrapper({ procesos })
const actionButtons = wrapper.findAll('.action-btn')
await actionButtons[1].trigger('click') // Pagos button
expect(wrapper.emitted('show-modal')[0]).toEqual([
{ procesoId: 1, tipo: 'pagos' }
])
})
it('emits show-modal for vacantes button', async () => {
const wrapper = createWrapper({ procesos })
const actionButtons = wrapper.findAll('.action-btn')
await actionButtons[2].trigger('click') // Vacantes button
expect(wrapper.emitted('show-modal')[0]).toEqual([
{ procesoId: 1, tipo: 'vacantes' }
])
})
it('emits show-modal for cronograma button', async () => {
const wrapper = createWrapper({ procesos })
const actionButtons = wrapper.findAll('.action-btn')
await actionButtons[3].trigger('click') // Cronograma button
expect(wrapper.emitted('show-modal')[0]).toEqual([
{ procesoId: 1, tipo: 'cronograma' }
])
})
})
describe('estado mapping', () => {
const testEstadoCases = [
{ estado: 'publicado', expectedLabel: 'Abierto' },
{ estado: 'en_proceso', expectedLabel: 'En Proceso' },
{ estado: 'nuevo', expectedLabel: 'PRÓXIMAMENTE' },
{ estado: 'finalizado', expectedLabel: 'FINALIZADO' },
{ estado: 'cancelado', expectedLabel: 'CANCELADO' }
]
testEstadoCases.forEach(({ estado, expectedLabel }) => {
it(`maps estado "${estado}" to label "${expectedLabel}"`, () => {
const wrapper = createWrapper({
procesos: [{ id: 1, titulo: 'Test', estado }]
})
expect(wrapper.find('.status-tag').text()).toBe(expectedLabel)
})
})
})
describe('description display', () => {
it('displays descripcion when provided', () => {
const wrapper = createWrapper({
procesos: [{
id: 1,
titulo: 'Test',
descripcion: 'Esta es la descripción del proceso',
estado: 'publicado'
}]
})
expect(wrapper.find('.convocatoria-desc').text()).toBe('Esta es la descripción del proceso')
})
it('falls back to subtitulo when descripcion is not provided', () => {
const wrapper = createWrapper({
procesos: [{
id: 1,
titulo: 'Test',
subtitulo: 'Este es el subtítulo',
estado: 'publicado'
}]
})
expect(wrapper.find('.convocatoria-desc').text()).toBe('Este es el subtítulo')
})
it('shows default text when neither descripcion nor subtitulo is provided', () => {
const wrapper = createWrapper({
procesos: [{
id: 1,
titulo: 'Test',
estado: 'publicado'
}]
})
expect(wrapper.find('.convocatoria-desc').text()).toBe('Proceso de admisión')
})
})
})