#49 feat: grafico unificado de provenance con filtro por niveles y re-estabilizacion
This commit is contained in:
@@ -10,6 +10,8 @@ const vis = vi.hoisted(() => {
|
||||
setOptions: vi.fn(),
|
||||
fit: vi.fn(),
|
||||
destroy: vi.fn(),
|
||||
getPositions: vi.fn(() => ({})),
|
||||
moveNode: vi.fn(),
|
||||
}
|
||||
const dataSetInstance = {
|
||||
get: vi.fn(),
|
||||
@@ -96,12 +98,46 @@ describe('VisChart', () => {
|
||||
expect(vis.networkInstance.fit).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('actualiza la red cuando cambian los nodos o aristas', async () => {
|
||||
it('separa verticalmente los nodos de una misma columna tras estabilizar', () => {
|
||||
vis.networkInstance.getPositions.mockReturnValue({
|
||||
'supplier:5': { x: 0, y: 100 },
|
||||
'supplier:6': { x: 0, y: 112 },
|
||||
'municipality:7': { x: 240, y: 100 },
|
||||
'product:1': { x: -240, y: 60 },
|
||||
})
|
||||
mount(VisChart, { props: { nodes: [], edges: [], minVerticalSpacing: 70 } })
|
||||
|
||||
const onStabilized = handlerFor(vis.networkInstance.once, 'stabilizationIterationsDone')
|
||||
onStabilized()
|
||||
|
||||
expect(vis.networkInstance.moveNode).toHaveBeenCalledWith('supplier:6', 0, 170)
|
||||
expect(vis.networkInstance.moveNode).not.toHaveBeenCalledWith('supplier:5', 0, expect.anything())
|
||||
expect(vis.networkInstance.moveNode).not.toHaveBeenCalledWith('municipality:7', 240, expect.anything())
|
||||
expect(vis.networkInstance.moveNode).not.toHaveBeenCalledWith('product:1', -240, expect.anything())
|
||||
})
|
||||
|
||||
it('no separa nodos cuando no se indica espaciado vertical mínimo', () => {
|
||||
vis.networkInstance.getPositions.mockReturnValue({
|
||||
'supplier:5': { x: 0, y: 100 },
|
||||
'supplier:6': { x: 0, y: 112 },
|
||||
})
|
||||
mount(VisChart, { props: { nodes: [], edges: [] } })
|
||||
|
||||
const onStabilized = handlerFor(vis.networkInstance.once, 'stabilizationIterationsDone')
|
||||
onStabilized()
|
||||
|
||||
expect(vis.networkInstance.moveNode).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('vuelve a estabilizar el grafo cuando cambian los nodos o aristas', async () => {
|
||||
const wrapper = mount(VisChart, { props: { nodes: [{ id: 'product:1' }], edges: [] } })
|
||||
expect(vis.networkInstance.setData).toHaveBeenCalledTimes(1)
|
||||
expect(vis.Network).toHaveBeenCalledTimes(1)
|
||||
expect(vis.networkInstance.destroy).not.toHaveBeenCalled()
|
||||
|
||||
await wrapper.setProps({ nodes: [{ id: 'product:2' }], edges: [{ from: 'product:2', to: 'supplier:5' }] })
|
||||
|
||||
expect(vis.networkInstance.destroy).toHaveBeenCalled()
|
||||
expect(vis.Network).toHaveBeenCalledTimes(2)
|
||||
expect(vis.networkInstance.setData).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import ProvenanceDetailModal from '@/components/provenance/ProvenanceDetailModal.vue'
|
||||
import ProductSupplierOrganizationChart from '@/components/provenance/ProductSupplierOrganizationChart.vue'
|
||||
import vuetify from '@/plugins/vuetify'
|
||||
|
||||
vi.mock('@/components/graph/VisChart.vue', () => ({
|
||||
default: {
|
||||
name: 'VisChart',
|
||||
template: '<div class="vis-chart-stub" />',
|
||||
props: ['nodes', 'edges', 'height', 'options'],
|
||||
},
|
||||
}))
|
||||
|
||||
const provenance = [
|
||||
{
|
||||
product: {
|
||||
id: 1,
|
||||
name: 'Panela regional por Kg',
|
||||
catalogue_images: ['http://localhost/media/panela.jpg'],
|
||||
},
|
||||
suppliers: [
|
||||
{
|
||||
supplier: { id: 5, name: 'Asociación Agropecuaria La Mesa', description: 'Cooperativa' },
|
||||
organization: { id: 3, name: 'Red de Economía Solidaria', description: 'Red' },
|
||||
municipality: null,
|
||||
department: null,
|
||||
country: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const provenanceWithTwoSuppliers = [
|
||||
{
|
||||
product: { id: 1, name: 'Panela regional por Kg' },
|
||||
suppliers: [
|
||||
{
|
||||
supplier: { id: 5, name: 'Asociación Agropecuaria La Mesa' },
|
||||
organization: { id: 3, name: 'Red de Economía Solidaria' },
|
||||
},
|
||||
{
|
||||
supplier: { id: 6, name: 'Asociación San Antonio' },
|
||||
organization: { id: 3, name: 'Red de Economía Solidaria' },
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
function mountChart (props = {}) {
|
||||
return mount(ProductSupplierOrganizationChart, {
|
||||
props: { provenance, ...props },
|
||||
global: { plugins: [vuetify] },
|
||||
})
|
||||
}
|
||||
|
||||
function visChart (wrapper) {
|
||||
return wrapper.findComponent({ name: 'VisChart' })
|
||||
}
|
||||
|
||||
describe('ProductSupplierOrganizationChart', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('renderiza el gráfico con nodos de productos, proveedores y organizaciones', () => {
|
||||
const wrapper = mountChart()
|
||||
|
||||
const chart = visChart(wrapper)
|
||||
expect(chart.exists()).toBe(true)
|
||||
const nodeIds = chart.props('nodes').map(node => node.id)
|
||||
expect(nodeIds).toContain('product:1')
|
||||
expect(nodeIds).toContain('supplier:5')
|
||||
expect(nodeIds).toContain('organization:3')
|
||||
})
|
||||
|
||||
it('incluye la etiqueta del nodo y la imagen circular para quien tiene foto', () => {
|
||||
const wrapper = mountChart()
|
||||
|
||||
const nodes = visChart(wrapper).props('nodes')
|
||||
const product = nodes.find(node => node.id === 'product:1')
|
||||
expect(product.label).toBe('Panela regional por Kg')
|
||||
expect(product.image).toBe('http://localhost/media/panela.jpg')
|
||||
expect(product.shape).toBe('circularImage')
|
||||
})
|
||||
|
||||
it('con varios proveedores crea el punto de disyunción: sólida hasta él y discontinua hacia cada proveedor', () => {
|
||||
const wrapper = mountChart({ provenance: provenanceWithTwoSuppliers })
|
||||
|
||||
const chart = visChart(wrapper)
|
||||
const nodes = chart.props('nodes')
|
||||
const junction = nodes.find(node => node.id === 'junction:1')
|
||||
expect(junction).toBeDefined()
|
||||
expect(junction.shape).toBe('dot')
|
||||
expect(junction.label).toBe('')
|
||||
|
||||
const edges = chart.props('edges')
|
||||
expect(edges.find(edge => edge.from === 'product:1' && edge.to === 'junction:1').dashes).toBe(false)
|
||||
expect(edges.find(edge => edge.from === 'junction:1' && edge.to === 'supplier:5').dashes).toBe(true)
|
||||
expect(edges.find(edge => edge.from === 'junction:1' && edge.to === 'supplier:6').dashes).toBe(true)
|
||||
})
|
||||
|
||||
it('una organización compartida viaja en línea continua', () => {
|
||||
const wrapper = mountChart({ provenance: provenanceWithTwoSuppliers })
|
||||
|
||||
const edges = visChart(wrapper).props('edges')
|
||||
expect(edges.find(edge => edge.from === 'supplier:5' && edge.to === 'organization:3').dashes).toBe(false)
|
||||
expect(edges.find(edge => edge.from === 'supplier:6' && edge.to === 'organization:3').dashes).toBe(false)
|
||||
})
|
||||
|
||||
it('no muestra mensaje de próximamente cuando existen relaciones', () => {
|
||||
const wrapper = mountChart()
|
||||
|
||||
expect(wrapper.text()).not.toContain('próximamente')
|
||||
})
|
||||
|
||||
it('muestra mensaje de próximamente cuando ningún producto tiene proveedores', () => {
|
||||
const wrapper = mountChart({
|
||||
provenance: [{ product: { id: 1, name: 'Panela' }, suppliers: [] }],
|
||||
})
|
||||
|
||||
expect(visChart(wrapper).exists()).toBe(false)
|
||||
expect(wrapper.text()).toContain('próximamente')
|
||||
})
|
||||
|
||||
it('abre el modal de detalle al seleccionar un nodo', async () => {
|
||||
const wrapper = mountChart()
|
||||
|
||||
await visChart(wrapper).vm.$emit('select', { id: 'supplier:5', kind: 'supplier', label: 'Asociación Agropecuaria La Mesa', entity: { kind: 'supplier' } })
|
||||
|
||||
expect(wrapper.findComponent(ProvenanceDetailModal).props('visible')).toBe(true)
|
||||
expect(document.body.textContent).toContain('Asociación Agropecuaria La Mesa')
|
||||
})
|
||||
|
||||
it('ignora la selección del punto de disyunción', async () => {
|
||||
const wrapper = mountChart({ provenance: provenanceWithTwoSuppliers })
|
||||
|
||||
await visChart(wrapper).vm.$emit('select', { id: 'junction:1', kind: 'junction', label: '' })
|
||||
|
||||
expect(wrapper.findComponent(ProvenanceDetailModal).props('visible')).toBe(false)
|
||||
})
|
||||
|
||||
it('alinea los productos en una columna vertical fija y centrada', () => {
|
||||
const provenance = [
|
||||
{ product: { id: 1, name: 'Panela' }, suppliers: [{ supplier: { id: 5, name: 'A' } }] },
|
||||
{ product: { id: 2, name: 'Arroz' }, suppliers: [{ supplier: { id: 6, name: 'B' } }] },
|
||||
{ product: { id: 3, name: 'Queso' }, suppliers: [{ supplier: { id: 7, name: 'C' } }] },
|
||||
]
|
||||
const wrapper = mountChart({ provenance })
|
||||
|
||||
const products = visChart(wrapper).props('nodes').filter(node => node.kind === 'product')
|
||||
expect(products).toHaveLength(3)
|
||||
expect(new Set(products.map(node => node.x)).size).toBe(1)
|
||||
expect(products.map(node => node.y)).toEqual([-110, 0, 110])
|
||||
products.forEach(node => {
|
||||
expect(node.fixed).toEqual({ x: true, y: true })
|
||||
})
|
||||
})
|
||||
|
||||
it('alinea proveedores y organizaciones en columnas por nivel', () => {
|
||||
const wrapper = mountChart()
|
||||
|
||||
const supplier = visChart(wrapper).props('nodes').find(node => node.id === 'supplier:5')
|
||||
expect(supplier.x).toBe(0)
|
||||
expect(supplier.fixed).toEqual({ x: true, y: false })
|
||||
expect(supplier.y).toBeUndefined()
|
||||
|
||||
const organization = visChart(wrapper).props('nodes').find(node => node.id === 'organization:3')
|
||||
expect(organization.x).toBe(240)
|
||||
expect(organization.fixed).toEqual({ x: true, y: false })
|
||||
})
|
||||
|
||||
it('el punto de disyunción queda a la misma altura que su producto', () => {
|
||||
const wrapper = mountChart({ provenance: provenanceWithTwoSuppliers })
|
||||
|
||||
const junction = visChart(wrapper).props('nodes').find(node => node.id === 'junction:1')
|
||||
const product = visChart(wrapper).props('nodes').find(node => node.id === 'product:1')
|
||||
expect(junction.x).toBe(-120)
|
||||
expect(junction.y).toBe(product.y)
|
||||
expect(junction.fixed).toEqual({ x: true, y: true })
|
||||
})
|
||||
})
|
||||
@@ -1,146 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import ProvenanceDetailModal from '@/components/provenance/ProvenanceDetailModal.vue'
|
||||
import ProductTerritoryChart from '@/components/provenance/ProductTerritoryChart.vue'
|
||||
import vuetify from '@/plugins/vuetify'
|
||||
|
||||
vi.mock('@/components/graph/VisChart.vue', () => ({
|
||||
default: {
|
||||
name: 'VisChart',
|
||||
template: '<div class="vis-chart-stub" />',
|
||||
props: ['nodes', 'edges', 'height', 'options'],
|
||||
},
|
||||
}))
|
||||
|
||||
const provenance = [
|
||||
{
|
||||
product: { id: 1, name: 'Panela regional por Kg' },
|
||||
suppliers: [
|
||||
{
|
||||
supplier: { id: 5, name: 'Asociación Agropecuaria La Mesa' },
|
||||
organization: null,
|
||||
municipality: { id: 7, name: 'La Mesa' },
|
||||
department: { id: 2, name: 'Cundinamarca' },
|
||||
country: { id: 1, name: 'Colombia', code: 'CO' },
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const provenanceWithTwoSuppliers = [
|
||||
{
|
||||
product: { id: 1, name: 'Panela regional por Kg' },
|
||||
suppliers: [
|
||||
{
|
||||
supplier: { id: 5, name: 'Asociación Agropecuaria La Mesa' },
|
||||
municipality: { id: 7, name: 'La Mesa' },
|
||||
department: { id: 2, name: 'Cundinamarca' },
|
||||
country: { id: 1, name: 'Colombia' },
|
||||
},
|
||||
{
|
||||
supplier: { id: 6, name: 'Asociación San Antonio' },
|
||||
municipality: { id: 8, name: 'San Antonio' },
|
||||
department: { id: 3, name: 'Antioquia' },
|
||||
country: { id: 1, name: 'Colombia' },
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
function mountChart (props = {}) {
|
||||
return mount(ProductTerritoryChart, {
|
||||
props: { provenance, ...props },
|
||||
global: { plugins: [vuetify] },
|
||||
})
|
||||
}
|
||||
|
||||
function visChart (wrapper) {
|
||||
return wrapper.findComponent({ name: 'VisChart' })
|
||||
}
|
||||
|
||||
describe('ProductTerritoryChart', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('renderiza el gráfico con municipio y departamento, sin país', () => {
|
||||
const wrapper = mountChart()
|
||||
|
||||
const chart = visChart(wrapper)
|
||||
expect(chart.exists()).toBe(true)
|
||||
const nodeIds = chart.props('nodes').map(node => node.id)
|
||||
expect(nodeIds).toContain('product:1')
|
||||
expect(nodeIds).toContain('supplier:5')
|
||||
expect(nodeIds).toContain('municipality:7')
|
||||
expect(nodeIds).toContain('department:2')
|
||||
expect(nodeIds).not.toContain('country:1')
|
||||
})
|
||||
|
||||
it('incluye la etiqueta del nodo y el color según el tipo', () => {
|
||||
const wrapper = mountChart()
|
||||
|
||||
const nodes = visChart(wrapper).props('nodes')
|
||||
const municipality = nodes.find(node => node.id === 'municipality:7')
|
||||
expect(municipality.label).toBe('La Mesa')
|
||||
expect(municipality.color).toBe('#66bb6a')
|
||||
})
|
||||
|
||||
it('con varios proveedores la duda se corta cuando comparten territorio', () => {
|
||||
const wrapper = mountChart({ provenance: provenanceWithTwoSuppliers })
|
||||
|
||||
const chart = visChart(wrapper)
|
||||
expect(chart.props('nodes').map(node => node.id)).toContain('junction:1')
|
||||
|
||||
const edges = chart.props('edges')
|
||||
expect(edges.find(edge => edge.from === 'product:1' && edge.to === 'junction:1').dashes).toBe(false)
|
||||
expect(edges.find(edge => edge.from === 'junction:1' && edge.to === 'supplier:5').dashes).toBe(true)
|
||||
expect(edges.find(edge => edge.from === 'supplier:5' && edge.to === 'municipality:7').dashes).toBe(true)
|
||||
expect(edges.find(edge => edge.from === 'municipality:7' && edge.to === 'department:2').dashes).toBe(true)
|
||||
})
|
||||
|
||||
it('con varios productos del mismo proveedor solo traza una línea al municipio', () => {
|
||||
const provenanceWithRepeatedSupplier = [
|
||||
{ product: { id: 1, name: 'Panela' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: { id: 7, name: 'Santa Bárbara' }, department: { id: 2, name: 'Antioquia' } }] },
|
||||
{ product: { id: 2, name: 'Arroz' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: { id: 7, name: 'Santa Bárbara' }, department: { id: 2, name: 'Antioquia' } }] },
|
||||
{ product: { id: 3, name: 'Queso' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: { id: 7, name: 'Santa Bárbara' }, department: { id: 2, name: 'Antioquia' } }] },
|
||||
]
|
||||
const wrapper = mountChart({ provenance: provenanceWithRepeatedSupplier })
|
||||
|
||||
const chart = visChart(wrapper)
|
||||
const edges = chart.props('edges')
|
||||
expect(edges.filter(edge => edge.from === 'supplier:5' && edge.to === 'municipality:7')).toHaveLength(1)
|
||||
expect(chart.props('nodes').filter(node => node.id === 'municipality:7')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('muestra mensaje de próximamente cuando ningún proveedor tiene territorio', () => {
|
||||
const wrapper = mountChart({
|
||||
provenance: [
|
||||
{
|
||||
product: { id: 1, name: 'Panela' },
|
||||
suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: null, department: null, country: null }],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(visChart(wrapper).exists()).toBe(false)
|
||||
expect(wrapper.text()).toContain('próximamente')
|
||||
})
|
||||
|
||||
it('abre el modal de detalle al seleccionar un municipio', async () => {
|
||||
const wrapper = mountChart()
|
||||
|
||||
await visChart(wrapper).vm.$emit('select', { id: 'municipality:7', kind: 'municipality', label: 'La Mesa', entity: { kind: 'municipality', name: 'La Mesa' } })
|
||||
|
||||
expect(wrapper.findComponent(ProvenanceDetailModal).props('visible')).toBe(true)
|
||||
expect(document.body.textContent).toContain('La Mesa')
|
||||
})
|
||||
|
||||
it('alinea municipios y departamentos en columnas por nivel', () => {
|
||||
const wrapper = mountChart()
|
||||
|
||||
const nodes = visChart(wrapper).props('nodes')
|
||||
expect(nodes.find(node => node.id === 'supplier:5').x).toBe(0)
|
||||
expect(nodes.find(node => node.id === 'municipality:7').x).toBe(240)
|
||||
expect(nodes.find(node => node.id === 'department:2').x).toBe(480)
|
||||
})
|
||||
})
|
||||
154
tests/unit/components/provenance/ProvenanceGraph.spec.js
Normal file
154
tests/unit/components/provenance/ProvenanceGraph.spec.js
Normal file
@@ -0,0 +1,154 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { nextTick } from 'vue'
|
||||
import ProvenanceDetailModal from '@/components/provenance/ProvenanceDetailModal.vue'
|
||||
import ProvenanceGraph from '@/components/provenance/ProvenanceGraph.vue'
|
||||
import vuetify from '@/plugins/vuetify'
|
||||
|
||||
vi.mock('@/components/graph/VisChart.vue', () => ({
|
||||
default: {
|
||||
name: 'VisChart',
|
||||
template: '<div class="vis-chart-stub" />',
|
||||
props: ['nodes', 'edges', 'height', 'options', 'minVerticalSpacing'],
|
||||
},
|
||||
}))
|
||||
|
||||
const provenance = [
|
||||
{
|
||||
product: {
|
||||
id: 1,
|
||||
name: 'Panela regional por Kg',
|
||||
catalogue_images: ['http://localhost/media/panela.jpg'],
|
||||
},
|
||||
suppliers: [
|
||||
{
|
||||
supplier: { id: 5, name: 'Asociación Agropecuaria La Mesa' },
|
||||
organization: { id: 3, name: 'Red de Economía Solidaria' },
|
||||
municipality: { id: 7, name: 'La Mesa' },
|
||||
department: { id: 2, name: 'Cundinamarca' },
|
||||
country: { id: 1, name: 'Colombia' },
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
function mountGraph (props = {}) {
|
||||
return mount(ProvenanceGraph, {
|
||||
props: { provenance, ...props },
|
||||
global: { plugins: [vuetify] },
|
||||
})
|
||||
}
|
||||
|
||||
function visChart (wrapper) {
|
||||
return wrapper.findComponent({ name: 'VisChart' })
|
||||
}
|
||||
|
||||
function checkbox (wrapper, label) {
|
||||
return wrapper.findAllComponents({ name: 'VCheckbox' }).find(cb => cb.props('label') === label)
|
||||
}
|
||||
|
||||
async function setKinds (wrapper, kinds) {
|
||||
checkbox(wrapper, 'Productos').vm.$emit('update:modelValue', kinds)
|
||||
await nextTick()
|
||||
}
|
||||
|
||||
describe('ProvenanceGraph', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('por defecto grafica solo productos y proveedores', () => {
|
||||
const wrapper = mountGraph()
|
||||
|
||||
const nodeKinds = visChart(wrapper).props('nodes').map(node => node.kind)
|
||||
expect(nodeKinds).toEqual(['product', 'supplier'])
|
||||
const edges = visChart(wrapper).props('edges')
|
||||
expect(edges.find(edge => edge.from === 'product:1' && edge.to === 'supplier:5').dashes).toBe(false)
|
||||
})
|
||||
|
||||
it('pide separación vertical mínima equivalente al círculo más una línea de label', () => {
|
||||
const wrapper = mountGraph()
|
||||
|
||||
expect(visChart(wrapper).props('minVerticalSpacing')).toBe(70)
|
||||
})
|
||||
|
||||
it('muestra un checkbox por cada nivel con productos y proveedores activos', () => {
|
||||
const wrapper = mountGraph()
|
||||
|
||||
const labels = wrapper.findAllComponents({ name: 'VCheckbox' }).map(cb => cb.props('label'))
|
||||
expect(labels).toEqual(['Productos', 'Proveedores', 'Organizaciones', 'Municipios', 'Departamentos', 'País'])
|
||||
})
|
||||
|
||||
it('al activar municipios agrega nodos de municipio y la arista proveedor→municipio', async () => {
|
||||
const wrapper = mountGraph()
|
||||
|
||||
await setKinds(wrapper, ['product', 'supplier', 'municipality'])
|
||||
|
||||
const nodeIds = visChart(wrapper).props('nodes').map(node => node.id)
|
||||
expect(nodeIds).toContain('municipality:7')
|
||||
const edges = visChart(wrapper).props('edges')
|
||||
expect(edges.find(edge => edge.from === 'supplier:5' && edge.to === 'municipality:7').dashes).toBe(false)
|
||||
})
|
||||
|
||||
it('con país activo crea el nodo país', async () => {
|
||||
const wrapper = mountGraph()
|
||||
|
||||
await setKinds(wrapper, ['product', 'supplier', 'country'])
|
||||
|
||||
expect(visChart(wrapper).props('nodes').map(node => node.id)).toContain('country:1')
|
||||
})
|
||||
|
||||
it('al desactivar productos no se crean nodos de producto', async () => {
|
||||
const wrapper = mountGraph()
|
||||
|
||||
await setKinds(wrapper, ['supplier', 'municipality', 'department'])
|
||||
|
||||
const nodeKinds = visChart(wrapper).props('nodes').map(node => node.kind)
|
||||
expect(nodeKinds).not.toContain('product')
|
||||
expect(nodeKinds).not.toContain('junction')
|
||||
})
|
||||
|
||||
it('avisa cuando no queda ningún nivel seleccionado', async () => {
|
||||
const wrapper = mountGraph()
|
||||
|
||||
await setKinds(wrapper, [])
|
||||
|
||||
expect(visChart(wrapper).exists()).toBe(false)
|
||||
expect(wrapper.text()).toContain('Selecciona al menos un elemento')
|
||||
})
|
||||
|
||||
it('muestra mensaje de próximamente cuando ningún producto tiene proveedores', () => {
|
||||
const wrapper = mountGraph({
|
||||
provenance: [{ product: { id: 1, name: 'Panela' }, suppliers: [] }],
|
||||
})
|
||||
|
||||
expect(visChart(wrapper).exists()).toBe(false)
|
||||
expect(wrapper.text()).toContain('próximamente')
|
||||
})
|
||||
|
||||
it('abre el modal de detalle al seleccionar un proveedor', async () => {
|
||||
const wrapper = mountGraph()
|
||||
|
||||
await visChart(wrapper).vm.$emit('select', { id: 'supplier:5', kind: 'supplier', label: 'Asociación Agropecuaria La Mesa', entity: { kind: 'supplier' } })
|
||||
|
||||
expect(wrapper.findComponent(ProvenanceDetailModal).props('visible')).toBe(true)
|
||||
expect(document.body.textContent).toContain('Asociación Agropecuaria La Mesa')
|
||||
})
|
||||
|
||||
it('ignora la selección del punto de disyunción', async () => {
|
||||
const provenanceWithTwoSuppliers = [
|
||||
{
|
||||
product: { id: 1, name: 'Panela' },
|
||||
suppliers: [
|
||||
{ supplier: { id: 5, name: 'A' }, municipality: { id: 7, name: 'La Mesa' } },
|
||||
{ supplier: { id: 6, name: 'B' }, municipality: { id: 8, name: 'San Antonio' } },
|
||||
],
|
||||
},
|
||||
]
|
||||
const wrapper = mountGraph({ provenance: provenanceWithTwoSuppliers })
|
||||
|
||||
await visChart(wrapper).vm.$emit('select', { id: 'junction:1', kind: 'junction', label: '' })
|
||||
|
||||
expect(wrapper.findComponent(ProvenanceDetailModal).props('visible')).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,7 +1,6 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import ProductSupplierOrganizationChart from '@/components/provenance/ProductSupplierOrganizationChart.vue'
|
||||
import ProductTerritoryChart from '@/components/provenance/ProductTerritoryChart.vue'
|
||||
import ProvenanceGraph from '@/components/provenance/ProvenanceGraph.vue'
|
||||
import ProvenanceSection from '@/components/provenance/ProvenanceSection.vue'
|
||||
import vuetify from '@/plugins/vuetify'
|
||||
|
||||
@@ -39,30 +38,25 @@ describe('ProvenanceSection', () => {
|
||||
it('no renderiza nada cuando no hay provenance', () => {
|
||||
const wrapper = mountSection({ provenance: null })
|
||||
|
||||
expect(wrapper.findComponent(ProductSupplierOrganizationChart).exists()).toBe(false)
|
||||
expect(wrapper.findComponent(ProductTerritoryChart).exists()).toBe(false)
|
||||
expect(wrapper.findComponent(ProvenanceGraph).exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('no renderiza nada cuando provenance es una lista vacía', () => {
|
||||
const wrapper = mountSection({ provenance: [] })
|
||||
|
||||
expect(wrapper.findComponent(ProductSupplierOrganizationChart).exists()).toBe(false)
|
||||
expect(wrapper.findComponent(ProductTerritoryChart).exists()).toBe(false)
|
||||
expect(wrapper.findComponent(ProvenanceGraph).exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('muestra los dos gráficos con sus títulos cuando hay provenance', () => {
|
||||
it('muestra el gráfico unificado con su título cuando hay provenance', () => {
|
||||
const wrapper = mountSection()
|
||||
|
||||
expect(wrapper.findComponent(ProductSupplierOrganizationChart).exists()).toBe(true)
|
||||
expect(wrapper.findComponent(ProductTerritoryChart).exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('Proveedores y organizaciones')
|
||||
expect(wrapper.text()).toContain('Departamento y municipio')
|
||||
expect(wrapper.findComponent(ProvenanceGraph).exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('Origen e historia de los productos')
|
||||
})
|
||||
|
||||
it('pasa el provenance a ambos gráficos', () => {
|
||||
it('pasa el provenance al gráfico', () => {
|
||||
const wrapper = mountSection()
|
||||
|
||||
expect(wrapper.findComponent(ProductSupplierOrganizationChart).props('provenance')).toStrictEqual(provenance)
|
||||
expect(wrapper.findComponent(ProductTerritoryChart).props('provenance')).toStrictEqual(provenance)
|
||||
expect(wrapper.findComponent(ProvenanceGraph).props('provenance')).toStrictEqual(provenance)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
buildSupplierOrganizationGraph,
|
||||
buildTerritoryGraph,
|
||||
buildProvenanceGraph,
|
||||
hasAnySupplier,
|
||||
hasAnyTerritory,
|
||||
} from '@/components/provenance/provenance-graph'
|
||||
|
||||
const singleSupplier = [
|
||||
const ALL_KINDS = ['product', 'supplier', 'organization', 'municipality', 'department', 'country']
|
||||
|
||||
const singleRelation = [
|
||||
{
|
||||
product: {
|
||||
id: 1,
|
||||
@@ -15,39 +15,41 @@ const singleSupplier = [
|
||||
},
|
||||
suppliers: [
|
||||
{
|
||||
supplier: { id: 5, name: 'Asociación Agropecuaria La Mesa', description: 'Cooperativa' },
|
||||
organization: { id: 3, name: 'Red de Economía Solidaria', description: 'Red' },
|
||||
supplier: { id: 5, name: 'Asociación Agropecuaria La Mesa' },
|
||||
organization: { id: 3, name: 'Red de Economía Solidaria' },
|
||||
municipality: { id: 7, name: 'La Mesa' },
|
||||
department: { id: 2, name: 'Cundinamarca' },
|
||||
country: { id: 1, name: 'Colombia', code: 'CO' },
|
||||
country: { id: 1, name: 'Colombia' },
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const twoSuppliers = (overrides = {}) => [
|
||||
{
|
||||
product: { id: 1, name: 'Panela' },
|
||||
suppliers: [
|
||||
{
|
||||
supplier: { id: 5, name: 'Asociación A' },
|
||||
organization: { id: 3, name: 'Red' },
|
||||
municipality: { id: 7, name: 'La Mesa' },
|
||||
department: { id: 2, name: 'Cundinamarca' },
|
||||
country: { id: 1, name: 'Colombia' },
|
||||
...(overrides.first || {}),
|
||||
},
|
||||
{
|
||||
supplier: { id: 6, name: 'Asociación B' },
|
||||
organization: { id: 3, name: 'Red' },
|
||||
municipality: { id: 8, name: 'San Antonio' },
|
||||
department: { id: 3, name: 'Antioquia' },
|
||||
country: { id: 1, name: 'Colombia' },
|
||||
...(overrides.second || {}),
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
function twoSuppliers (overrides = {}) {
|
||||
return [
|
||||
{
|
||||
product: { id: 1, name: 'Panela' },
|
||||
suppliers: [
|
||||
{
|
||||
supplier: { id: 5, name: 'Asociación A' },
|
||||
organization: { id: 3, name: 'Red' },
|
||||
municipality: { id: 7, name: 'La Mesa' },
|
||||
department: { id: 2, name: 'Cundinamarca' },
|
||||
country: { id: 1, name: 'Colombia' },
|
||||
...(overrides.first || {}),
|
||||
},
|
||||
{
|
||||
supplier: { id: 6, name: 'Asociación B' },
|
||||
organization: { id: 3, name: 'Red' },
|
||||
municipality: { id: 8, name: 'San Antonio' },
|
||||
department: { id: 3, name: 'Antioquia' },
|
||||
country: { id: 1, name: 'Colombia' },
|
||||
...(overrides.second || {}),
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
function edgeOf (graph, from, to) {
|
||||
return graph.edges.find(edge => edge.from === from && edge.to === to)
|
||||
@@ -55,7 +57,7 @@ function edgeOf (graph, from, to) {
|
||||
|
||||
describe('hasAnySupplier', () => {
|
||||
it('es true cuando algún producto tiene proveedores', () => {
|
||||
expect(hasAnySupplier(singleSupplier)).toBe(true)
|
||||
expect(hasAnySupplier(singleRelation)).toBe(true)
|
||||
})
|
||||
|
||||
it('es false cuando todos los productos están sin proveedores', () => {
|
||||
@@ -68,115 +70,82 @@ describe('hasAnySupplier', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('hasAnyTerritory', () => {
|
||||
it('es true cuando algún proveedor tiene territorio', () => {
|
||||
expect(hasAnyTerritory(singleSupplier)).toBe(true)
|
||||
})
|
||||
describe('buildProvenanceGraph', () => {
|
||||
it('por defecto solo grafica productos y proveedores', () => {
|
||||
const graph = buildProvenanceGraph(singleRelation)
|
||||
|
||||
it('es false cuando todos los territorios vienen en null', () => {
|
||||
const noTerritory = [
|
||||
{
|
||||
product: { id: 1, name: 'Panela' },
|
||||
suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, organization: null, municipality: null, department: null, country: null }],
|
||||
},
|
||||
]
|
||||
expect(hasAnyTerritory(noTerritory)).toBe(false)
|
||||
})
|
||||
|
||||
it('es false cuando no hay suppliers', () => {
|
||||
const noSuppliers = [{ product: { id: 1, name: 'Panela' }, suppliers: [] }]
|
||||
expect(hasAnyTerritory(noSuppliers)).toBe(false)
|
||||
})
|
||||
|
||||
it('es true cuando hay municipio aunque el país venga null', () => {
|
||||
const data = [
|
||||
{
|
||||
product: { id: 1, name: 'Panela' },
|
||||
suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: { id: 7, name: 'Santa Bárbara' }, department: null, country: null }],
|
||||
},
|
||||
]
|
||||
expect(hasAnyTerritory(data)).toBe(true)
|
||||
})
|
||||
|
||||
it('es false cuando solo hay país', () => {
|
||||
const data = [
|
||||
{
|
||||
product: { id: 1, name: 'Panela' },
|
||||
suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: null, department: null, country: { id: 1, name: 'Colombia' } }],
|
||||
},
|
||||
]
|
||||
expect(hasAnyTerritory(data)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('buildSupplierOrganizationGraph', () => {
|
||||
it('con un único proveedor conecta con arista cierta y sin junction', () => {
|
||||
const graph = buildSupplierOrganizationGraph(singleSupplier)
|
||||
|
||||
expect(graph.nodes.map(n => n.id)).not.toContain('junction:1')
|
||||
expect(edgeOf(graph, 'product:1', 'supplier:5')).toEqual({ from: 'product:1', to: 'supplier:5', certain: true })
|
||||
})
|
||||
|
||||
it('con varios proveedores crea un punto de disyunción: sólida hasta él y dudosa hacia cada proveedor', () => {
|
||||
const graph = buildSupplierOrganizationGraph(twoSuppliers())
|
||||
|
||||
expect(graph.nodes.map(n => n.id)).toContain('junction:1')
|
||||
expect(edgeOf(graph, 'product:1', 'junction:1')).toEqual({ from: 'product:1', to: 'junction:1', certain: true })
|
||||
expect(edgeOf(graph, 'junction:1', 'supplier:5')).toEqual({ from: 'junction:1', to: 'supplier:5', certain: false })
|
||||
expect(edgeOf(graph, 'junction:1', 'supplier:6')).toEqual({ from: 'junction:1', to: 'supplier:6', certain: false })
|
||||
})
|
||||
|
||||
it('una organización compartida por todos los proveedores es cierta', () => {
|
||||
const graph = buildSupplierOrganizationGraph(twoSuppliers())
|
||||
|
||||
expect(edgeOf(graph, 'supplier:5', 'organization:3')).toEqual({ from: 'supplier:5', to: 'organization:3', certain: true })
|
||||
expect(edgeOf(graph, 'supplier:6', 'organization:3')).toEqual({ from: 'supplier:6', to: 'organization:3', certain: true })
|
||||
})
|
||||
|
||||
it('organizaciones distintas entre proveedores son dudosas', () => {
|
||||
const graph = buildSupplierOrganizationGraph(twoSuppliers({
|
||||
first: { organization: { id: 3, name: 'Red' } },
|
||||
second: { organization: { id: 4, name: 'Cooperativa' } },
|
||||
}))
|
||||
|
||||
expect(edgeOf(graph, 'supplier:5', 'organization:3').certain).toBe(false)
|
||||
expect(edgeOf(graph, 'supplier:6', 'organization:4').certain).toBe(false)
|
||||
})
|
||||
|
||||
it('incluye la imagen del producto y el detalle de la entidad', () => {
|
||||
const graph = buildSupplierOrganizationGraph(singleSupplier)
|
||||
const product = graph.nodes.find(n => n.id === 'product:1')
|
||||
|
||||
expect(product.image).toBe('http://localhost/media/panela.jpg')
|
||||
expect(product.label).toBe('Panela regional por Kg')
|
||||
expect(product.entity.kind).toBe('product')
|
||||
expect(product.entity.name).toBe('Panela regional por Kg')
|
||||
})
|
||||
|
||||
it('omite el nodo de organización cuando la relación es null', () => {
|
||||
const data = [
|
||||
{
|
||||
product: { id: 1, name: 'Panela' },
|
||||
suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, organization: null }],
|
||||
},
|
||||
]
|
||||
const graph = buildSupplierOrganizationGraph(data)
|
||||
|
||||
expect(graph.nodes.map(n => n.id)).not.toContain('organization:')
|
||||
expect(graph.nodes.map(n => n.kind)).toEqual(['product', 'supplier'])
|
||||
expect(graph.edges).toEqual([{ from: 'product:1', to: 'supplier:5', certain: true }])
|
||||
})
|
||||
|
||||
it('no duplica nodos ni aristas cuando se repiten entre productos', () => {
|
||||
const data = [
|
||||
{ product: { id: 1, name: 'Panela' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, organization: { id: 3, name: 'Red' } }] },
|
||||
{ product: { id: 2, name: 'Arroz' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, organization: { id: 3, name: 'Red' } }] },
|
||||
]
|
||||
const graph = buildSupplierOrganizationGraph(data)
|
||||
it('con todos los niveles grafica la cadena completa y la organización', () => {
|
||||
const graph = buildProvenanceGraph(singleRelation, ALL_KINDS)
|
||||
|
||||
expect(graph.nodes.filter(n => n.id === 'supplier:5')).toHaveLength(1)
|
||||
expect(graph.nodes.filter(n => n.id === 'organization:3')).toHaveLength(1)
|
||||
expect(graph.edges.filter(e => e.from === 'supplier:5' && e.to === 'organization:3')).toHaveLength(1)
|
||||
expect(graph.edges).toHaveLength(3)
|
||||
expect(edgeOf(graph, 'product:1', 'supplier:5')).toEqual({ from: 'product:1', to: 'supplier:5', certain: true })
|
||||
expect(edgeOf(graph, 'supplier:5', 'organization:3').certain).toBe(true)
|
||||
expect(edgeOf(graph, 'supplier:5', 'municipality:7')).toEqual({ from: 'supplier:5', to: 'municipality:7', certain: true })
|
||||
expect(edgeOf(graph, 'municipality:7', 'department:2').certain).toBe(true)
|
||||
expect(edgeOf(graph, 'department:2', 'country:1').certain).toBe(true)
|
||||
})
|
||||
|
||||
it('con varios proveedores crea el punto de disyunción y la duda se propaga', () => {
|
||||
const graph = buildProvenanceGraph(twoSuppliers(), ALL_KINDS)
|
||||
|
||||
expect(graph.nodes.map(n => n.id)).toContain('junction:1')
|
||||
expect(edgeOf(graph, 'product:1', 'junction:1')).toEqual({ from: 'product:1', to: 'junction:1', certain: true })
|
||||
expect(edgeOf(graph, 'junction:1', 'supplier:5').certain).toBe(false)
|
||||
expect(edgeOf(graph, 'junction:1', 'supplier:6').certain).toBe(false)
|
||||
expect(edgeOf(graph, 'supplier:5', 'municipality:7').certain).toBe(false)
|
||||
expect(edgeOf(graph, 'municipality:7', 'department:2').certain).toBe(false)
|
||||
expect(edgeOf(graph, 'department:2', 'country:1').certain).toBe(true)
|
||||
expect(edgeOf(graph, 'supplier:5', 'organization:3').certain).toBe(true)
|
||||
})
|
||||
|
||||
it('omite los niveles no solicitados conectando el nivel previo con el siguiente', () => {
|
||||
const graph = buildProvenanceGraph(singleRelation, ['product', 'supplier', 'department'])
|
||||
|
||||
expect(graph.nodes.map(n => n.kind)).toEqual(['product', 'supplier', 'department'])
|
||||
expect(edgeOf(graph, 'supplier:5', 'department:2')).toEqual({ from: 'supplier:5', to: 'department:2', certain: true })
|
||||
expect(edgeOf(graph, 'supplier:5', 'municipality:7')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('sin proveedores activos conecta el producto con el siguiente nivel', () => {
|
||||
const graph = buildProvenanceGraph(singleRelation, ['product', 'municipality', 'department'])
|
||||
|
||||
expect(edgeOf(graph, 'product:1', 'municipality:7')).toEqual({ from: 'product:1', to: 'municipality:7', certain: true })
|
||||
expect(edgeOf(graph, 'municipality:7', 'department:2').certain).toBe(true)
|
||||
})
|
||||
|
||||
it('sin productos activos los proveedores quedan como raíces sin junction', () => {
|
||||
const graph = buildProvenanceGraph(twoSuppliers(), ['supplier', 'municipality'])
|
||||
|
||||
expect(graph.nodes.some(n => n.kind === 'product')).toBe(false)
|
||||
expect(graph.nodes.some(n => n.kind === 'junction')).toBe(false)
|
||||
expect(edgeOf(graph, 'supplier:5', 'municipality:7').certain).toBe(false)
|
||||
})
|
||||
|
||||
it('con país activo crea el nodo país aunque se omitan niveles intermedios', () => {
|
||||
const graph = buildProvenanceGraph(singleRelation, ['product', 'supplier', 'country'])
|
||||
|
||||
expect(graph.nodes.map(n => n.id)).toContain('country:1')
|
||||
expect(edgeOf(graph, 'supplier:5', 'country:1').certain).toBe(true)
|
||||
})
|
||||
|
||||
it('no crea nodos de país si no se solicita', () => {
|
||||
const graph = buildProvenanceGraph(singleRelation, ['product', 'supplier', 'municipality'])
|
||||
|
||||
expect(graph.nodes.some(n => n.kind === 'country')).toBe(false)
|
||||
})
|
||||
|
||||
it('no duplica aristas cuando varios productos comparten proveedor y municipio', () => {
|
||||
const data = [
|
||||
{ product: { id: 1, name: 'Panela' }, suppliers: [{ supplier: { id: 5, name: 'A' }, municipality: { id: 7, name: 'La Mesa' }, department: { id: 2, name: 'Cundinamarca' } }] },
|
||||
{ product: { id: 2, name: 'Arroz' }, suppliers: [{ supplier: { id: 5, name: 'A' }, municipality: { id: 7, name: 'La Mesa' }, department: { id: 2, name: 'Cundinamarca' } }] },
|
||||
]
|
||||
const graph = buildProvenanceGraph(data, ['product', 'supplier', 'municipality'])
|
||||
|
||||
expect(graph.nodes.filter(n => n.id === 'municipality:7')).toHaveLength(1)
|
||||
expect(graph.edges.filter(e => e.from === 'supplier:5' && e.to === 'municipality:7')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('si el mismo par de nodos repite con distinta certeza, gana la duda', () => {
|
||||
@@ -184,98 +153,24 @@ describe('buildSupplierOrganizationGraph', () => {
|
||||
{ product: { id: 1, name: 'Panela' }, suppliers: [{ supplier: { id: 5, name: 'A' }, organization: { id: 3, name: 'Red' } }] },
|
||||
{ product: { id: 2, name: 'Arroz' }, suppliers: [{ supplier: { id: 5, name: 'A' }, organization: { id: 3, name: 'Red' } }, { supplier: { id: 6, name: 'B' }, organization: { id: 4, name: 'Coop' } }] },
|
||||
]
|
||||
const graph = buildSupplierOrganizationGraph(data)
|
||||
const graph = buildProvenanceGraph(data, ['product', 'supplier', 'organization'])
|
||||
|
||||
expect(graph.edges.filter(e => e.from === 'supplier:5' && e.to === 'organization:3')).toHaveLength(1)
|
||||
expect(edgeOf(graph, 'supplier:5', 'organization:3').certain).toBe(false)
|
||||
})
|
||||
|
||||
it('no incluye posiciones de layout', () => {
|
||||
const graph = buildSupplierOrganizationGraph(singleSupplier)
|
||||
it('incluye la imagen y el detalle de la entidad del producto', () => {
|
||||
const graph = buildProvenanceGraph(singleRelation)
|
||||
const product = graph.nodes.find(n => n.id === 'product:1')
|
||||
|
||||
expect(product.x).toBeUndefined()
|
||||
expect(product.y).toBeUndefined()
|
||||
expect(product.image).toBe('http://localhost/media/panela.jpg')
|
||||
expect(product.entity.kind).toBe('product')
|
||||
})
|
||||
|
||||
it('devuelve un grafo vacío cuando no hay provenance', () => {
|
||||
const graph = buildSupplierOrganizationGraph([])
|
||||
const graph = buildProvenanceGraph([])
|
||||
|
||||
expect(graph.nodes).toEqual([])
|
||||
expect(graph.edges).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('buildTerritoryGraph', () => {
|
||||
it('con un único proveedor toda la cadena es cierta', () => {
|
||||
const graph = buildTerritoryGraph(singleSupplier)
|
||||
|
||||
expect(edgeOf(graph, 'product:1', 'supplier:5')).toEqual({ from: 'product:1', to: 'supplier:5', certain: true })
|
||||
expect(edgeOf(graph, 'supplier:5', 'municipality:7')).toEqual({ from: 'supplier:5', to: 'municipality:7', certain: true })
|
||||
expect(edgeOf(graph, 'municipality:7', 'department:2')).toEqual({ from: 'municipality:7', to: 'department:2', certain: true })
|
||||
})
|
||||
|
||||
it('no incluye nodos ni aristas de país', () => {
|
||||
const graph = buildTerritoryGraph(singleSupplier)
|
||||
|
||||
expect(graph.nodes.map(n => n.id)).not.toContain('country:1')
|
||||
expect(graph.edges.some(edge => edge.from.startsWith('country:') || edge.to.startsWith('country:'))).toBe(false)
|
||||
})
|
||||
|
||||
it('con varios productos del mismo proveedor solo traza una línea hacia el municipio', () => {
|
||||
const data = [
|
||||
{ product: { id: 1, name: 'Panela' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: { id: 7, name: 'Santa Bárbara' }, department: { id: 2, name: 'Antioquia' } }] },
|
||||
{ product: { id: 2, name: 'Arroz' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: { id: 7, name: 'Santa Bárbara' }, department: { id: 2, name: 'Antioquia' } }] },
|
||||
{ product: { id: 3, name: 'Queso' }, suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: { id: 7, name: 'Santa Bárbara' }, department: { id: 2, name: 'Antioquia' } }] },
|
||||
]
|
||||
const graph = buildTerritoryGraph(data)
|
||||
|
||||
expect(graph.edges.filter(e => e.from === 'supplier:5' && e.to === 'municipality:7')).toHaveLength(1)
|
||||
expect(graph.nodes.filter(n => n.id === 'municipality:7')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('cuando los proveedores comparten municipio, el territorio es cierto desde allí', () => {
|
||||
const graph = buildTerritoryGraph(twoSuppliers({
|
||||
second: { municipality: { id: 7, name: 'La Mesa' }, department: { id: 2, name: 'Cundinamarca' } },
|
||||
}))
|
||||
|
||||
expect(edgeOf(graph, 'product:1', 'junction:1').certain).toBe(true)
|
||||
expect(edgeOf(graph, 'junction:1', 'supplier:5').certain).toBe(false)
|
||||
expect(edgeOf(graph, 'supplier:5', 'municipality:7')).toEqual({ from: 'supplier:5', to: 'municipality:7', certain: true })
|
||||
expect(edgeOf(graph, 'supplier:6', 'municipality:7')).toEqual({ from: 'supplier:6', to: 'municipality:7', certain: true })
|
||||
expect(edgeOf(graph, 'municipality:7', 'department:2').certain).toBe(true)
|
||||
})
|
||||
|
||||
it('municipios distintos pero mismo departamento: dudoso hasta el municipio y cierto desde el departamento', () => {
|
||||
const graph = buildTerritoryGraph(twoSuppliers({
|
||||
second: { department: { id: 2, name: 'Cundinamarca' } },
|
||||
}))
|
||||
|
||||
expect(edgeOf(graph, 'supplier:5', 'municipality:7').certain).toBe(false)
|
||||
expect(edgeOf(graph, 'supplier:6', 'municipality:8').certain).toBe(false)
|
||||
expect(edgeOf(graph, 'municipality:7', 'department:2')).toEqual({ from: 'municipality:7', to: 'department:2', certain: true })
|
||||
expect(edgeOf(graph, 'municipality:8', 'department:2')).toEqual({ from: 'municipality:8', to: 'department:2', certain: true })
|
||||
})
|
||||
|
||||
it('municipios y departamentos distintos mantienen la duda', () => {
|
||||
const graph = buildTerritoryGraph(twoSuppliers())
|
||||
|
||||
expect(edgeOf(graph, 'supplier:5', 'municipality:7').certain).toBe(false)
|
||||
expect(edgeOf(graph, 'municipality:7', 'department:2').certain).toBe(false)
|
||||
expect(edgeOf(graph, 'supplier:6', 'municipality:8').certain).toBe(false)
|
||||
expect(edgeOf(graph, 'municipality:8', 'department:3').certain).toBe(false)
|
||||
})
|
||||
|
||||
it('no crea nodos de territorio cuando el proveedor no tiene municipio', () => {
|
||||
const data = [
|
||||
{
|
||||
product: { id: 1, name: 'Panela' },
|
||||
suppliers: [{ supplier: { id: 5, name: 'La Mesa' }, municipality: null, department: null, country: null }],
|
||||
},
|
||||
]
|
||||
const graph = buildTerritoryGraph(data)
|
||||
|
||||
expect(graph.nodes.map(n => n.id)).toEqual(['product:1', 'supplier:5'])
|
||||
expect(graph.edges).toEqual([{ from: 'product:1', to: 'supplier:5', certain: true }])
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user