test(piece-edit,documents) : add productIds sync, error paths, and document CRUD tests

This commit is contained in:
2026-04-06 15:56:07 +02:00
parent 972f30e772
commit 150aceac24
2 changed files with 494 additions and 0 deletions

View File

@@ -466,4 +466,179 @@ describe('submitEdition — no data loss', () => {
expect(formLinks[1].constructeurId).toBe(mockConstructeurFAG.id)
expect(formLinks[2].constructeurId).toBe('cstr-new-003')
})
it('sends both productId and productIds in payload', async () => {
mockUpdatePiece.mockResolvedValue({ success: true, data: { id: PIECE_ID } })
const composable = await createAndHydrate()
composable.setProductSelection(0, 'prod-001')
await tick()
await composable.submitEdition()
expect(mockUpdatePiece).toHaveBeenCalledTimes(1)
const payload = mockUpdatePiece.mock.calls[0]![1]
expect(payload.productId).toBe('prod-001')
expect(payload.productIds).toEqual(['prod-001'])
})
it('productId is the first product selection when multiple exist', async () => {
// Override the piece type to have 2 product requirements
const multiProductType = {
...mockPieceType,
structure: {
...mockPieceType.structure,
products: [
{
typeProductId: 'tprod-grease-001',
typeProductLabel: 'Graisse SKF',
familyCode: 'LUB',
role: 'lubrification',
},
{
typeProductId: 'tprod-oil-002',
typeProductLabel: 'Huile',
familyCode: 'LUB',
role: 'lubrification secondaire',
},
],
customFields: [],
},
}
mockPieceTypes.value = [multiProductType]
mockUpdatePiece.mockResolvedValue({ success: true, data: { id: PIECE_ID } })
const composable = await createAndHydrate({
productIds: ['prod-001', 'prod-002'],
})
composable.setProductSelection(0, 'prod-001')
composable.setProductSelection(1, 'prod-002')
await tick()
await composable.submitEdition()
expect(mockUpdatePiece).toHaveBeenCalledTimes(1)
const payload = mockUpdatePiece.mock.calls[0]![1]
expect(payload.productId).toBe('prod-001')
expect(payload.productIds).toEqual(['prod-001', 'prod-002'])
})
})
// ---------------------------------------------------------------------------
// submitEdition — null field handling
// ---------------------------------------------------------------------------
describe('submitEdition — null field handling', () => {
it('empty prix sends null', async () => {
mockUpdatePiece.mockResolvedValue({ success: true, data: { id: PIECE_ID } })
const composable = await createAndHydrate()
composable.editionForm.prix = ''
composable.setProductSelection(0, 'prod-001')
await tick()
await composable.submitEdition()
const payload = mockUpdatePiece.mock.calls[0]![1]
expect(payload.prix).toBeNull()
})
it('whitespace-only prix sends null', async () => {
mockUpdatePiece.mockResolvedValue({ success: true, data: { id: PIECE_ID } })
const composable = await createAndHydrate()
composable.editionForm.prix = ' '
composable.setProductSelection(0, 'prod-001')
await tick()
await composable.submitEdition()
const payload = mockUpdatePiece.mock.calls[0]![1]
expect(payload.prix).toBeNull()
})
it('empty reference sends null', async () => {
mockUpdatePiece.mockResolvedValue({ success: true, data: { id: PIECE_ID } })
const composable = await createAndHydrate()
composable.editionForm.reference = ''
composable.setProductSelection(0, 'prod-001')
await tick()
await composable.submitEdition()
const payload = mockUpdatePiece.mock.calls[0]![1]
expect(payload.reference).toBeNull()
})
it('valid prix is sent as string number', async () => {
mockUpdatePiece.mockResolvedValue({ success: true, data: { id: PIECE_ID } })
const composable = await createAndHydrate()
composable.editionForm.prix = '99.50'
composable.setProductSelection(0, 'prod-001')
await tick()
await composable.submitEdition()
const payload = mockUpdatePiece.mock.calls[0]![1]
expect(payload.prix).toBe('99.5')
})
})
// ---------------------------------------------------------------------------
// submitEdition — error paths
// ---------------------------------------------------------------------------
describe('submitEdition — error paths', () => {
it('does not save custom fields when updatePiece fails', async () => {
mockUpdatePiece.mockResolvedValue({ success: false, error: 'Server error' })
const composable = await createAndHydrate()
composable.setProductSelection(0, 'prod-001')
await tick()
await composable.submitEdition()
expect(mockUpdatePiece).toHaveBeenCalledTimes(1)
expect(mockSaveAll).not.toHaveBeenCalled()
expect(mockSyncLinks).not.toHaveBeenCalled()
})
it('does not save custom fields when updatePiece throws', async () => {
mockUpdatePiece.mockRejectedValue(new Error('Network failure'))
const composable = await createAndHydrate()
composable.setProductSelection(0, 'prod-001')
await tick()
await composable.submitEdition()
expect(mockUpdatePiece).toHaveBeenCalledTimes(1)
expect(mockSaveAll).not.toHaveBeenCalled()
expect(mockSyncLinks).not.toHaveBeenCalled()
expect(mockShowError).toHaveBeenCalledWith('Network failure')
})
it('shows error toast when product selection is not filled', async () => {
const composable = await createAndHydrate()
// Clear product selection
composable.setProductSelection(0, null)
await tick()
await composable.submitEdition()
expect(mockUpdatePiece).not.toHaveBeenCalled()
expect(mockShowError).toHaveBeenCalledWith('Sélectionnez un produit conforme au squelette.')
})
})