a6b48b1dd1
Auto Tag Develop / tag (push) Successful in 11s
## ERP-196 — Refonte des blocs de formulaire Refonte visuelle des blocs répétables des formulaires (clients, fournisseurs, prestataires, transporteurs), alignée sur les blocs « ticket de pesée » : à plat (sans box-shadow), titre de bloc en noir, séparation par filet noir 1px. ### ✅ Blocs Contact - Box-shadow / fond blanc / padding latéral retirés - En-tête `flex justify-between` : titre noir (« Contact 1 »…) à gauche, poubelle `button-class="p-0"` à droite - 4 colonnes, filet `border-b border-black` entre blocs (pas sous le dernier, prop `last`) - i18n `contact.title` ajouté pour transporteurs / prestataires - 9 pages câblées (new / edit / consultation des 4 répertoires) ### ✅ Blocs Adresse - Même traitement (à plat, titre noir, filet sauf dernier) - i18n `address.title` pour transporteurs / prestataires - Transporteur : adresse unique → titre « Adresse » (sans numéro) - 12 pages câblées ### ✅ Bloc Comptabilité - Bloc **infos** : titre « Informations » + filet bas (uniquement si des RIB suivent) - Blocs **RIB** : titre « RIB 1 / RIB 2… » + poubelle `p-0`, filet sauf le dernier - i18n `accounting.infoTitle` (3 modules) + `accounting.ribTitle` (fournisseurs / prestataires) - 9 pages câblées (clients / fournisseurs / prestataires) ### Vérifications - Vitest : 44/44 (specs contact + adresse) - Eslint : clean sur l'ensemble des composants et pages modifiés ### Commits - `feat : refonte des blocs contact (ERP-196)` - `feat : refonte des blocs adresse (ERP-196)` - `feat : refonte du bloc comptabilité (ERP-196)` Reviewed-on: #145 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
152 lines
6.4 KiB
TypeScript
152 lines
6.4 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
import { mount, flushPromises } from '@vue/test-utils'
|
|
import { defineComponent, h, ref, computed } from 'vue'
|
|
import { emptyCarrierAddress } from '~/modules/transport/types/carrierForm'
|
|
import CarrierAddressBlock from '../CarrierAddressBlock.vue'
|
|
|
|
/**
|
|
* Tests de l'autocomplétion BAN du bloc Adresse transporteur (ERP-167) — réutilise
|
|
* `useAddressAutocomplete` (M1/M2/M3). On vérifie le NOMINAL (CP → ville) et le
|
|
* DÉGRADÉ (BAN indisponible → saisie libre + event `degraded`).
|
|
*/
|
|
|
|
const { searchCityMock, searchAddressMock } = vi.hoisted(() => ({
|
|
searchCityMock: vi.fn(),
|
|
searchAddressMock: vi.fn(),
|
|
}))
|
|
vi.mock('~/shared/composables/useAddressAutocomplete', () => ({
|
|
useAddressAutocomplete: () => ({
|
|
searchCity: searchCityMock,
|
|
searchAddress: searchAddressMock,
|
|
}),
|
|
}))
|
|
|
|
vi.stubGlobal('useI18n', () => ({ t: (key: string) => key }))
|
|
vi.stubGlobal('ref', ref)
|
|
vi.stubGlobal('computed', computed)
|
|
|
|
const MalioInputTextStub = defineComponent({
|
|
name: 'MalioInputText',
|
|
props: { modelValue: { default: null }, label: { type: String, default: '' }, error: { type: String, default: '' } },
|
|
emits: ['update:modelValue'],
|
|
setup(props) {
|
|
return () => h('div', { 'data-testid': 'input-text', 'data-label': props.label, 'data-error': props.error })
|
|
},
|
|
})
|
|
|
|
const MalioSelectStub = defineComponent({
|
|
name: 'MalioSelect',
|
|
props: { modelValue: { default: null }, options: { type: Array as () => { value: string }[], default: () => [] }, label: { type: String, default: '' }, error: { type: String, default: '' } },
|
|
emits: ['update:modelValue'],
|
|
setup(props) {
|
|
return () => h('div', { 'data-testid': 'select', 'data-label': props.label, 'data-options': JSON.stringify(props.options.map(o => o.value)) })
|
|
},
|
|
})
|
|
|
|
const MalioInputAutocompleteStub = defineComponent({
|
|
name: 'MalioInputAutocomplete',
|
|
props: { modelValue: { default: null }, options: { type: Array as () => { value: string }[], default: () => [] }, loading: { type: Boolean, default: false }, allowCreate: { type: Boolean, default: false } },
|
|
emits: ['update:modelValue', 'search', 'select'],
|
|
setup(props) {
|
|
return () => h('div', { 'data-testid': 'addr-autocomplete', 'data-options': JSON.stringify(props.options.map(o => o.value)) })
|
|
},
|
|
})
|
|
|
|
function mountBlock(overrides: Record<string, unknown> = {}) {
|
|
return mount(CarrierAddressBlock, {
|
|
props: {
|
|
modelValue: { ...emptyCarrierAddress(), ...overrides },
|
|
title: 'Adresse 1',
|
|
countryOptions: [{ value: 'France', label: 'France' }],
|
|
},
|
|
global: {
|
|
stubs: {
|
|
MalioButtonIcon: true,
|
|
MalioInputText: MalioInputTextStub,
|
|
MalioSelect: MalioSelectStub,
|
|
MalioInputAutocomplete: MalioInputAutocompleteStub,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
/** Récupère le composant MalioInputText d'un label donné. */
|
|
function inputTextByLabel(wrapper: ReturnType<typeof mountBlock>, label: string) {
|
|
return wrapper.findAllComponents(MalioInputTextStub).find(c => c.props('label') === label)
|
|
}
|
|
|
|
describe('CarrierAddressBlock — autocomplétion ville (BAN) NOMINAL', () => {
|
|
beforeEach(() => {
|
|
searchCityMock.mockReset()
|
|
searchAddressMock.mockReset()
|
|
})
|
|
|
|
it('saisie d\'un CP à 5 chiffres → searchCity + peuple le select Ville', async () => {
|
|
searchCityMock.mockResolvedValueOnce([{ city: 'Poitiers', postalCode: '86000' }])
|
|
const wrapper = mountBlock()
|
|
|
|
const cp = inputTextByLabel(wrapper, 'transport.carriers.form.address.postalCode')
|
|
cp?.vm.$emit('update:modelValue', '86000')
|
|
await flushPromises()
|
|
|
|
expect(searchCityMock).toHaveBeenCalledWith('86000')
|
|
const citySelect = wrapper.findAllComponents(MalioSelectStub).find(c => c.props('label') === 'transport.carriers.form.address.city')
|
|
const options = JSON.parse(citySelect?.attributes('data-options') ?? '[]')
|
|
expect(options).toContain('Poitiers')
|
|
expect(wrapper.emitted('degraded')).toBeUndefined()
|
|
})
|
|
|
|
it('n\'interroge pas la BAN sous 5 chiffres', async () => {
|
|
const wrapper = mountBlock()
|
|
inputTextByLabel(wrapper, 'transport.carriers.form.address.postalCode')?.vm.$emit('update:modelValue', '860')
|
|
await flushPromises()
|
|
expect(searchCityMock).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
describe('CarrierAddressBlock — autocomplétion DÉGRADÉE', () => {
|
|
beforeEach(() => {
|
|
searchCityMock.mockReset()
|
|
searchAddressMock.mockReset()
|
|
})
|
|
|
|
it('BAN ville indisponible → bascule en saisie libre + émet « degraded »', async () => {
|
|
searchCityMock.mockRejectedValueOnce(new Error('BAN indisponible'))
|
|
const wrapper = mountBlock()
|
|
|
|
inputTextByLabel(wrapper, 'transport.carriers.form.address.postalCode')?.vm.$emit('update:modelValue', '86000')
|
|
await flushPromises()
|
|
|
|
expect(wrapper.emitted('degraded')).toHaveLength(1)
|
|
// En dégradé, la Ville devient un MalioInputText (plus de MalioSelect ville).
|
|
const citySelect = wrapper.findAllComponents(MalioSelectStub).find(c => c.props('label') === 'transport.carriers.form.address.city')
|
|
expect(citySelect).toBeUndefined()
|
|
expect(inputTextByLabel(wrapper, 'transport.carriers.form.address.city')).toBeDefined()
|
|
})
|
|
|
|
it('autocomplétion adresse : pas d\'appel BAN sous 3 caractères', async () => {
|
|
const wrapper = mountBlock()
|
|
wrapper.findComponent(MalioInputAutocompleteStub).vm.$emit('search', 'ab')
|
|
await flushPromises()
|
|
expect(searchAddressMock).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('autocomplétion adresse : émet « degraded » une seule fois malgré plusieurs erreurs', async () => {
|
|
searchAddressMock.mockRejectedValue(new Error('BAN indisponible'))
|
|
const wrapper = mountBlock()
|
|
const auto = wrapper.findComponent(MalioInputAutocompleteStub)
|
|
|
|
auto.vm.$emit('search', 'rue de la paix')
|
|
await flushPromises()
|
|
auto.vm.$emit('search', 'rue de la paixx')
|
|
await flushPromises()
|
|
|
|
expect(wrapper.emitted('degraded')).toHaveLength(1)
|
|
})
|
|
|
|
it('active allow-create sur le champ Adresse (saisie manuelle libre)', () => {
|
|
const wrapper = mountBlock()
|
|
expect(wrapper.findComponent(MalioInputAutocompleteStub).props('allowCreate')).toBe(true)
|
|
})
|
|
})
|