#49 feat: CRUD admin de organizaciones, proveedores y geografía
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import GeographyManagement from '@/components/provenance/admin/GeographyManagement.vue'
|
||||
import vuetify from '@/plugins/vuetify'
|
||||
import { clickBody, setBodyInput } from './helpers'
|
||||
|
||||
const countries = [
|
||||
{ id: 1, name: 'Colombia', code: 'CO' },
|
||||
{ id: 2, name: 'Venezuela', code: 'VE' },
|
||||
]
|
||||
|
||||
const departments = [
|
||||
{ id: 2, name: 'Cundinamarca', country: 1, country_detail: { id: 1, name: 'Colombia', code: 'CO' } },
|
||||
{ id: 3, name: 'Antioquia', country: 1, country_detail: { id: 1, name: 'Colombia', code: 'CO' } },
|
||||
]
|
||||
|
||||
const municipalities = [
|
||||
{ id: 7, name: 'La Mesa', department: 2, country: 1, department_detail: { id: 2, name: 'Cundinamarca' } },
|
||||
{ id: 8, name: 'San Antonio', department: 3, country: 1, department_detail: { id: 3, name: 'Antioquia' } },
|
||||
]
|
||||
|
||||
function mockApi () {
|
||||
return {
|
||||
getCountries: vi.fn().mockResolvedValue(countries),
|
||||
createCountry: vi.fn().mockResolvedValue({}),
|
||||
updateCountry: vi.fn().mockResolvedValue({}),
|
||||
deleteCountry: vi.fn().mockResolvedValue({}),
|
||||
getDepartments: vi.fn().mockResolvedValue(departments),
|
||||
createDepartment: vi.fn().mockResolvedValue({}),
|
||||
updateDepartment: vi.fn().mockResolvedValue({}),
|
||||
deleteDepartment: vi.fn().mockResolvedValue({}),
|
||||
getMunicipalities: vi.fn().mockResolvedValue(municipalities),
|
||||
createMunicipality: vi.fn().mockResolvedValue({}),
|
||||
updateMunicipality: vi.fn().mockResolvedValue({}),
|
||||
deleteMunicipality: vi.fn().mockResolvedValue({}),
|
||||
}
|
||||
}
|
||||
|
||||
function mountComponent (api) {
|
||||
return mount(GeographyManagement, {
|
||||
global: { plugins: [vuetify], provide: { api } },
|
||||
})
|
||||
}
|
||||
|
||||
describe('GeographyManagement', () => {
|
||||
it('muestra los países por defecto y permite crear uno', async () => {
|
||||
const api = mockApi()
|
||||
const wrapper = mountComponent(api)
|
||||
await flushPromises()
|
||||
|
||||
expect(api.getCountries).toHaveBeenCalled()
|
||||
expect(wrapper.text()).toContain('Colombia')
|
||||
|
||||
await wrapper.find('[data-testid="geo-country-create"]').trigger('click')
|
||||
setBodyInput('[data-testid="geo-country-form-name"] input', 'Ecuador')
|
||||
setBodyInput('[data-testid="geo-country-form-code"] input', 'EC')
|
||||
clickBody('[data-testid="geo-country-save"]')
|
||||
await flushPromises()
|
||||
|
||||
expect(api.createCountry).toHaveBeenCalledWith({ name: 'Ecuador', code: 'EC' })
|
||||
expect(api.getCountries).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('cambia a la pestaña de departamentos y crea uno', async () => {
|
||||
const api = mockApi()
|
||||
const wrapper = mountComponent(api)
|
||||
await flushPromises()
|
||||
|
||||
await wrapper.find('[data-testid="geo-tab-departments"]').trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(api.getDepartments).toHaveBeenCalled()
|
||||
expect(wrapper.text()).toContain('Cundinamarca')
|
||||
|
||||
await wrapper.find('[data-testid="geo-department-create"]').trigger('click')
|
||||
setBodyInput('[data-testid="geo-department-form-name"] input', 'Risaralda')
|
||||
const countrySelect = wrapper.findAllComponents({ name: 'VSelect' })
|
||||
.find(select => select.props('items').some(item => item.id === 2))
|
||||
await countrySelect.vm.$emit('update:modelValue', 2)
|
||||
clickBody('[data-testid="geo-department-save"]')
|
||||
await flushPromises()
|
||||
|
||||
expect(api.createDepartment).toHaveBeenCalledWith(expect.objectContaining({
|
||||
name: 'Risaralda',
|
||||
country: 2,
|
||||
}))
|
||||
expect(api.getDepartments).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('cambia a la pestaña de municipios y crea uno con departamento y país', async () => {
|
||||
const api = mockApi()
|
||||
const wrapper = mountComponent(api)
|
||||
await flushPromises()
|
||||
|
||||
await wrapper.find('[data-testid="geo-tab-municipalities"]').trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(api.getMunicipalities).toHaveBeenCalled()
|
||||
expect(wrapper.text()).toContain('San Antonio')
|
||||
|
||||
await wrapper.find('[data-testid="geo-municipality-create"]').trigger('click')
|
||||
setBodyInput('[data-testid="geo-municipality-form-name"] input', 'Pereira')
|
||||
await wrapper.findAllComponents({ name: 'VAutocomplete' })[0].vm.$emit('update:modelValue', 3)
|
||||
clickBody('[data-testid="geo-municipality-save"]')
|
||||
await flushPromises()
|
||||
|
||||
expect(api.createMunicipality).toHaveBeenCalledWith(expect.objectContaining({
|
||||
name: 'Pereira',
|
||||
department: 3,
|
||||
country: 1,
|
||||
}))
|
||||
expect(api.getMunicipalities).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,117 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import OrganizationsManagement from '@/components/provenance/admin/OrganizationsManagement.vue'
|
||||
import vuetify from '@/plugins/vuetify'
|
||||
import { clickBody, setBodyInput } from './helpers'
|
||||
|
||||
const organizations = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Red de Economía Solidaria',
|
||||
description: 'Red de organizaciones',
|
||||
website: 'https://red.example.org',
|
||||
contact_email: 'contacto@red.example.org',
|
||||
contact_phone: '3001112233',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Cooperativa El Campo',
|
||||
description: null,
|
||||
website: null,
|
||||
contact_email: null,
|
||||
contact_phone: null,
|
||||
},
|
||||
]
|
||||
|
||||
function mockApi () {
|
||||
return {
|
||||
getOrganizations: vi.fn().mockResolvedValue(organizations),
|
||||
createOrganization: vi.fn().mockResolvedValue({}),
|
||||
updateOrganization: vi.fn().mockResolvedValue({}),
|
||||
deleteOrganization: vi.fn().mockResolvedValue({}),
|
||||
}
|
||||
}
|
||||
|
||||
function mountComponent (api) {
|
||||
return mount(OrganizationsManagement, {
|
||||
global: { plugins: [vuetify], provide: { api } },
|
||||
})
|
||||
}
|
||||
|
||||
function fillForm () {
|
||||
setBodyInput('[data-testid="org-form-name"] input', 'Fundación Montaña')
|
||||
setBodyInput('[data-testid="org-form-description"] textarea', 'Fundación')
|
||||
setBodyInput('[data-testid="org-form-website"] input', 'https://montana.example.org')
|
||||
setBodyInput('[data-testid="org-form-email"] input', 'info@montana.example.org')
|
||||
setBodyInput('[data-testid="org-form-phone"] input', '3109876543')
|
||||
}
|
||||
|
||||
describe('OrganizationsManagement', () => {
|
||||
it('carga y muestra las organizaciones', async () => {
|
||||
const api = mockApi()
|
||||
const wrapper = mountComponent(api)
|
||||
await flushPromises()
|
||||
|
||||
expect(api.getOrganizations).toHaveBeenCalled()
|
||||
expect(wrapper.text()).toContain('Red de Economía Solidaria')
|
||||
expect(wrapper.text()).toContain('Cooperativa El Campo')
|
||||
})
|
||||
|
||||
it('filtra las organizaciones por búsqueda', async () => {
|
||||
const wrapper = mountComponent(mockApi())
|
||||
await flushPromises()
|
||||
|
||||
await wrapper.find('[data-testid="org-search"] input').setValue('Cooperativa')
|
||||
|
||||
expect(wrapper.text()).toContain('Cooperativa El Campo')
|
||||
expect(wrapper.text()).not.toContain('Red de Economía Solidaria')
|
||||
})
|
||||
|
||||
it('crea una organización', async () => {
|
||||
const api = mockApi()
|
||||
const wrapper = mountComponent(api)
|
||||
await flushPromises()
|
||||
|
||||
await wrapper.find('[data-testid="org-create"]').trigger('click')
|
||||
fillForm()
|
||||
clickBody('[data-testid="org-save"]')
|
||||
await flushPromises()
|
||||
|
||||
expect(api.createOrganization).toHaveBeenCalledWith({
|
||||
name: 'Fundación Montaña',
|
||||
description: 'Fundación',
|
||||
website: 'https://montana.example.org',
|
||||
contact_email: 'info@montana.example.org',
|
||||
contact_phone: '3109876543',
|
||||
})
|
||||
expect(api.getOrganizations).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('edita una organización', async () => {
|
||||
const api = mockApi()
|
||||
const wrapper = mountComponent(api)
|
||||
await flushPromises()
|
||||
|
||||
await wrapper.find('[data-testid="org-edit-1"]').trigger('click')
|
||||
setBodyInput('[data-testid="org-form-name"] input', 'Red de Economía Solidaria Colombia')
|
||||
clickBody('[data-testid="org-save"]')
|
||||
await flushPromises()
|
||||
|
||||
expect(api.updateOrganization).toHaveBeenCalledWith(1, expect.objectContaining({
|
||||
name: 'Red de Economía Solidaria Colombia',
|
||||
}))
|
||||
})
|
||||
|
||||
it('elimina una organización', async () => {
|
||||
const api = mockApi()
|
||||
const wrapper = mountComponent(api)
|
||||
await flushPromises()
|
||||
|
||||
await wrapper.find('[data-testid="org-delete-2"]').trigger('click')
|
||||
clickBody('[data-testid="org-confirm-delete"]')
|
||||
await flushPromises()
|
||||
|
||||
expect(api.deleteOrganization).toHaveBeenCalledWith(2)
|
||||
expect(api.getOrganizations).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,125 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import SuppliersManagement from '@/components/provenance/admin/SuppliersManagement.vue'
|
||||
import vuetify from '@/plugins/vuetify'
|
||||
import { clickBody, setBodyInput } from './helpers'
|
||||
|
||||
const organizations = [
|
||||
{ id: 3, name: 'Red de Economía Solidaria' },
|
||||
{ id: 4, name: 'Cooperativa El Campo' },
|
||||
]
|
||||
|
||||
const municipalities = [
|
||||
{ id: 7, name: 'La Mesa' },
|
||||
{ id: 8, name: 'San Antonio' },
|
||||
]
|
||||
|
||||
const suppliers = [
|
||||
{
|
||||
id: 5,
|
||||
name: 'Asociación Agropecuaria La Mesa',
|
||||
description: 'Cooperativa de campesinos',
|
||||
organization: 3,
|
||||
organization_detail: { id: 3, name: 'Red de Economía Solidaria' },
|
||||
municipality: 7,
|
||||
municipality_detail: { id: 7, name: 'La Mesa' },
|
||||
contact_email: 'contacto@agro.example.org',
|
||||
contact_phone: '300 123 4567',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Finca El Paraíso',
|
||||
description: null,
|
||||
organization: null,
|
||||
organization_detail: null,
|
||||
municipality: null,
|
||||
municipality_detail: null,
|
||||
contact_email: null,
|
||||
contact_phone: null,
|
||||
},
|
||||
]
|
||||
|
||||
function mockApi () {
|
||||
return {
|
||||
getSuppliers: vi.fn().mockResolvedValue(suppliers),
|
||||
getOrganizations: vi.fn().mockResolvedValue(organizations),
|
||||
getMunicipalities: vi.fn().mockResolvedValue(municipalities),
|
||||
createSupplier: vi.fn().mockResolvedValue({}),
|
||||
updateSupplier: vi.fn().mockResolvedValue({}),
|
||||
deleteSupplier: vi.fn().mockResolvedValue({}),
|
||||
}
|
||||
}
|
||||
|
||||
function mountComponent (api) {
|
||||
return mount(SuppliersManagement, {
|
||||
global: { plugins: [vuetify], provide: { api } },
|
||||
})
|
||||
}
|
||||
|
||||
describe('SuppliersManagement', () => {
|
||||
it('carga y muestra los proveedores con su organización y municipio', async () => {
|
||||
const api = mockApi()
|
||||
const wrapper = mountComponent(api)
|
||||
await flushPromises()
|
||||
|
||||
expect(api.getSuppliers).toHaveBeenCalled()
|
||||
expect(wrapper.text()).toContain('Asociación Agropecuaria La Mesa')
|
||||
expect(wrapper.text()).toContain('Red de Economía Solidaria')
|
||||
expect(wrapper.text()).toContain('La Mesa')
|
||||
})
|
||||
|
||||
it('crea un proveedor con organización y municipio', async () => {
|
||||
const api = mockApi()
|
||||
const wrapper = mountComponent(api)
|
||||
await flushPromises()
|
||||
|
||||
await wrapper.find('[data-testid="supplier-create"]').trigger('click')
|
||||
|
||||
setBodyInput('[data-testid="supplier-form-name"] input', 'Asociación El Cedro')
|
||||
setBodyInput('[data-testid="supplier-form-description"] textarea', 'Caficultores')
|
||||
const orgSelect = wrapper.findAllComponents({ name: 'VSelect' })
|
||||
.find(select => select.props('items').some(item => item.id === 4))
|
||||
await orgSelect.vm.$emit('update:modelValue', 4)
|
||||
await wrapper.findAllComponents({ name: 'VAutocomplete' })[0].vm.$emit('update:modelValue', 8)
|
||||
clickBody('[data-testid="supplier-save"]')
|
||||
await flushPromises()
|
||||
|
||||
expect(api.createSupplier).toHaveBeenCalledWith(expect.objectContaining({
|
||||
name: 'Asociación El Cedro',
|
||||
description: 'Caficultores',
|
||||
organization: 4,
|
||||
municipality: 8,
|
||||
}))
|
||||
expect(api.getSuppliers).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('edita un proveedor conservando la organización seleccionada', async () => {
|
||||
const api = mockApi()
|
||||
const wrapper = mountComponent(api)
|
||||
await flushPromises()
|
||||
|
||||
await wrapper.find('[data-testid="supplier-edit-5"]').trigger('click')
|
||||
setBodyInput('[data-testid="supplier-form-name"] input', 'Asociación Agropecuaria La Mesa Renovada')
|
||||
clickBody('[data-testid="supplier-save"]')
|
||||
await flushPromises()
|
||||
|
||||
expect(api.updateSupplier).toHaveBeenCalledWith(5, expect.objectContaining({
|
||||
name: 'Asociación Agropecuaria La Mesa Renovada',
|
||||
organization: 3,
|
||||
municipality: 7,
|
||||
}))
|
||||
})
|
||||
|
||||
it('elimina un proveedor', async () => {
|
||||
const api = mockApi()
|
||||
const wrapper = mountComponent(api)
|
||||
await flushPromises()
|
||||
|
||||
await wrapper.find('[data-testid="supplier-delete-6"]').trigger('click')
|
||||
clickBody('[data-testid="supplier-confirm-delete"]')
|
||||
await flushPromises()
|
||||
|
||||
expect(api.deleteSupplier).toHaveBeenCalledWith(6)
|
||||
expect(api.getSuppliers).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
23
tests/unit/components/provenance/admin/helpers.js
Normal file
23
tests/unit/components/provenance/admin/helpers.js
Normal file
@@ -0,0 +1,23 @@
|
||||
// Helpers para interactuar con el contenido de diálogos, que Vuetify
|
||||
// teleporta a document.body fuera del DOM del wrapper.
|
||||
|
||||
function setNativeValue (el, value) {
|
||||
const proto = el.tagName === 'TEXTAREA'
|
||||
? window.HTMLTextAreaElement.prototype
|
||||
: window.HTMLInputElement.prototype
|
||||
const setter = Object.getOwnPropertyDescriptor(proto, 'value').set
|
||||
setter.call(el, value)
|
||||
el.dispatchEvent(new Event('input', { bubbles: true }))
|
||||
}
|
||||
|
||||
export function setBodyInput (selector, value) {
|
||||
const el = document.body.querySelector(selector)
|
||||
if (!el) throw new Error(`Input no encontrado en body: ${selector}`)
|
||||
setNativeValue(el, value)
|
||||
}
|
||||
|
||||
export function clickBody (selector) {
|
||||
const el = document.body.querySelector(selector)
|
||||
if (!el) throw new Error(`Elemento no encontrado en body: ${selector}`)
|
||||
el.click()
|
||||
}
|
||||
Reference in New Issue
Block a user