45cb5c834c
Auto Tag Develop / tag (push) Successful in 8s
## Contexte (ERP-172) Sur les ecrans de **modification**, supprimer un bloc Contact / Adresse / RIB ne supprimait pas la sous-ressource cote serveur : - **M1 / M2** : DELETE differe au clic « Enregistrer » de l'onglet -> ne partait jamais si l'utilisateur ne re-validait pas. - **M3** : aucun DELETE (`splice` local uniquement). ## Correctifs ### 1. DELETE immediat des sous-ressources - Nouveau helper partage `frontend/shared/utils/collectionRow.ts` (`removeCollectionRow`) + tests Vitest. - A la confirmation de la modale : bloc existant (`id` en base) -> `DELETE` immediat ; bloc jamais persiste -> retrait local ; echec serveur (ex. 409 dernier RIB d'une LCR) -> bloc conserve + message back. - Branche sur M1 / M2 / M3 (contacts / adresses / RIB). Suppression du mecanisme differe (`removed*Ids` + boucles dans `submit*`) devenu mort. ### 2. Affichage de la poubelle unifie (`isRowRemovable`) Regle identique sur les 3 modules : poubelle visible sur un bloc **seulement s'il reste un autre bloc deja enregistre** (`id` en base). - Tant que rien n'est enregistre -> aucune poubelle (plus de suppression d'un simple brouillon non valide). - On peut jeter un brouillon non enregistre s'il reste un bloc enregistre. - On ne peut jamais supprimer son dernier bloc enregistre. - Applique aux ecrans **new + edit** des 3 modules (contacts / adresses / RIB). ## Tests - Helper couvert par Vitest (`removeCollectionRow` + `isRowRemovable`). - `make nuxt-test` : 480 tests OK. `make nuxt-lint` : OK. ## A verifier (golden path) Sur les 3 modules : supprimer un bloc existant -> `DELETE` part immediatement -> reload -> le bloc a disparu ; la poubelle n'apparait qu'avec un 2e bloc deja enregistre. Reviewed-on: #109 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
122 lines
4.9 KiB
TypeScript
122 lines
4.9 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest'
|
|
import { removeCollectionRow, isRowRemovable, type DeletableRow } from '../collectionRow'
|
|
|
|
/**
|
|
* Tests de `removeCollectionRow` — suppression d'une ligne de collection
|
|
* (contact / adresse / RIB) avec DELETE immediat de la sous-ressource existante
|
|
* (ERP-172). Coeur de logique mutualise par les 3 modules (Client / Fournisseur /
|
|
* Prestataire) : un seul comportement teste ici couvre les 9 cas (3 modules x 3
|
|
* blocs).
|
|
*/
|
|
interface Row extends DeletableRow {
|
|
label?: string
|
|
}
|
|
|
|
function makeEmpty(): Row {
|
|
return { id: null, label: '' }
|
|
}
|
|
|
|
describe('removeCollectionRow', () => {
|
|
it('emet un DELETE sur la sous-ressource quand le bloc est existant (id non null)', async () => {
|
|
const rows: Row[] = [{ id: 10, label: 'A' }, { id: 11, label: 'B' }]
|
|
const errors: Record<string, string>[] = [{}, {}]
|
|
const deleteRow = vi.fn().mockResolvedValue(undefined)
|
|
const onError = vi.fn()
|
|
|
|
const removed = await removeCollectionRow({
|
|
rows, errors, index: 0,
|
|
endpoint: '/client_contacts',
|
|
deleteRow, makeEmpty, onError,
|
|
})
|
|
|
|
expect(deleteRow).toHaveBeenCalledOnce()
|
|
expect(deleteRow).toHaveBeenCalledWith('/client_contacts/10')
|
|
expect(removed).toBe(true)
|
|
expect(rows).toEqual([{ id: 11, label: 'B' }])
|
|
expect(errors).toHaveLength(1)
|
|
expect(onError).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('ne fait AUCUN appel reseau pour un bloc jamais persiste (id null) — retrait local', async () => {
|
|
const rows: Row[] = [{ id: 10, label: 'A' }, { id: null, label: 'brouillon' }]
|
|
const errors: Record<string, string>[] = [{}, {}]
|
|
const deleteRow = vi.fn().mockResolvedValue(undefined)
|
|
const onError = vi.fn()
|
|
|
|
const removed = await removeCollectionRow({
|
|
rows, errors, index: 1,
|
|
endpoint: '/client_contacts',
|
|
deleteRow, makeEmpty, onError,
|
|
})
|
|
|
|
expect(deleteRow).not.toHaveBeenCalled()
|
|
expect(removed).toBe(true)
|
|
expect(rows).toEqual([{ id: 10, label: 'A' }])
|
|
})
|
|
|
|
it('conserve le bloc et remonte l\'erreur si le DELETE serveur echoue (ex. 409 dernier RIB LCR)', async () => {
|
|
const rows: Row[] = [{ id: 10, label: 'A' }, { id: 11, label: 'B' }]
|
|
const errors: Record<string, string>[] = [{}, {}]
|
|
const error = { response: { status: 409 } }
|
|
const deleteRow = vi.fn().mockRejectedValue(error)
|
|
const onError = vi.fn()
|
|
|
|
const removed = await removeCollectionRow({
|
|
rows, errors, index: 0,
|
|
endpoint: '/client_ribs',
|
|
deleteRow, makeEmpty, onError,
|
|
})
|
|
|
|
expect(removed).toBe(false)
|
|
expect(onError).toHaveBeenCalledWith(error)
|
|
// Bloc NON retire : la suppression n'a pas ete confirmee par le serveur.
|
|
expect(rows).toEqual([{ id: 10, label: 'A' }, { id: 11, label: 'B' }])
|
|
expect(errors).toHaveLength(2)
|
|
})
|
|
|
|
it('garde au moins un bloc visible apres retrait du dernier (amorce vide)', async () => {
|
|
const rows: Row[] = [{ id: 10, label: 'A' }]
|
|
const errors: Record<string, string>[] = [{}]
|
|
const deleteRow = vi.fn().mockResolvedValue(undefined)
|
|
|
|
await removeCollectionRow({
|
|
rows, errors, index: 0,
|
|
endpoint: '/client_contacts',
|
|
deleteRow, makeEmpty, onError: vi.fn(),
|
|
})
|
|
|
|
expect(rows).toEqual([{ id: null, label: '' }])
|
|
})
|
|
})
|
|
|
|
/**
|
|
* Tests de `isRowRemovable` — la poubelle d'un bloc n'apparait que s'il reste un
|
|
* AUTRE bloc deja enregistre (id en base). Empeche de supprimer un bloc tant que
|
|
* rien n'est sauvegarde, et de supprimer son dernier bloc enregistre (ERP-172).
|
|
*/
|
|
describe('isRowRemovable', () => {
|
|
it('faux quand aucun autre bloc n\'est enregistre (que des brouillons)', () => {
|
|
const rows: Row[] = [{ id: null, label: 'brouillon 1' }, { id: null, label: 'brouillon 2' }]
|
|
expect(isRowRemovable(rows, 0)).toBe(false)
|
|
expect(isRowRemovable(rows, 1)).toBe(false)
|
|
})
|
|
|
|
it('faux pour le seul bloc enregistre (un brouillon a cote ne compte pas)', () => {
|
|
const rows: Row[] = [{ id: 10, label: 'enregistre' }, { id: null, label: 'brouillon' }]
|
|
// Le bloc enregistre ne peut pas etre supprime : aucun AUTRE bloc enregistre.
|
|
expect(isRowRemovable(rows, 0)).toBe(false)
|
|
// Le brouillon peut etre jete : il reste le bloc enregistre id=10.
|
|
expect(isRowRemovable(rows, 1)).toBe(true)
|
|
})
|
|
|
|
it('vrai pour chaque bloc des qu\'au moins deux sont enregistres', () => {
|
|
const rows: Row[] = [{ id: 10, label: 'A' }, { id: 11, label: 'B' }]
|
|
expect(isRowRemovable(rows, 0)).toBe(true)
|
|
expect(isRowRemovable(rows, 1)).toBe(true)
|
|
})
|
|
|
|
it('faux pour un unique bloc', () => {
|
|
expect(isRowRemovable([{ id: 10, label: 'A' }], 0)).toBe(false)
|
|
})
|
|
})
|