All checks were successful
Auto Tag Develop / tag (push) Successful in 9s
- Nouvelles entites ConstructeurCategorie (referentiel M2M) et ConstructeurTelephone (1-N) - Constructeur : retrait colonne phone, ajout collections telephones/categories, groupes de serialisation constructeur:read/write - Migration : cree les 3 tables, migre la colonne phone existante vers constructeur_telephone, drop phone - Commande app:import-fournisseurs (dry-run par defaut, --force) : non destructive, find-or-create par nom, ne touche jamais un ID existant, ajout-seulement pour telephones/categories - MAJ MCP tools / MachineStructureController / audit subscriber / tests - Frontend : page constructeurs avec telephones multiples + categories (tableau, filtre, formulaire), composable useConstructeurCategories, composant ConstructeurCategorieSelect Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
320 lines
10 KiB
TypeScript
320 lines
10 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
|
|
import { wrapCollection } from '../fixtures/mockData'
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Import under test (AFTER all vi.mock calls)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
import { useDocuments } from '~/composables/useDocuments'
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Mocks — API layer
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const mockGet = vi.fn()
|
|
const mockPatch = vi.fn()
|
|
const mockPostFormData = vi.fn()
|
|
const mockDel = vi.fn()
|
|
|
|
vi.mock('~/composables/useApi', () => ({
|
|
useApi: () => ({
|
|
get: mockGet,
|
|
post: vi.fn(),
|
|
patch: mockPatch,
|
|
put: vi.fn(),
|
|
delete: mockDel,
|
|
postFormData: mockPostFormData,
|
|
}),
|
|
}))
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Mocks — Toast
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const mockShowSuccess = vi.fn()
|
|
const mockShowError = vi.fn()
|
|
|
|
vi.mock('~/composables/useToast', () => ({
|
|
useToast: () => ({
|
|
showSuccess: mockShowSuccess,
|
|
showError: mockShowError,
|
|
showInfo: vi.fn(),
|
|
showToast: vi.fn(),
|
|
toasts: { value: [] },
|
|
clearAll: vi.fn(),
|
|
}),
|
|
}))
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Test data
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const mockDocument = {
|
|
id: 'doc-001',
|
|
name: 'photo.jpg',
|
|
filename: 'photo.jpg',
|
|
mimeType: 'image/jpeg',
|
|
size: 12345,
|
|
fileUrl: '/files/photo.jpg',
|
|
downloadUrl: '/files/photo.jpg/download',
|
|
createdAt: '2025-01-10T08:00:00+00:00',
|
|
}
|
|
|
|
const mockDocument2 = {
|
|
id: 'doc-002',
|
|
name: 'schema.pdf',
|
|
filename: 'schema.pdf',
|
|
mimeType: 'application/pdf',
|
|
size: 54321,
|
|
fileUrl: '/files/schema.pdf',
|
|
downloadUrl: '/files/schema.pdf/download',
|
|
createdAt: '2025-01-11T09:00:00+00:00',
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
function createMockFile(name: string, type = 'image/jpeg'): File {
|
|
return new File(['content'], name, { type })
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// beforeEach
|
|
// ---------------------------------------------------------------------------
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// uploadDocuments — FormData is built correctly
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('uploadDocuments', () => {
|
|
it('builds FormData with file and context fields', async () => {
|
|
mockPostFormData.mockResolvedValue({ success: true, data: mockDocument })
|
|
|
|
const { uploadDocuments } = useDocuments()
|
|
const file = createMockFile('photo.jpg')
|
|
|
|
await uploadDocuments({
|
|
files: [file],
|
|
context: { pieceId: 'piece-001', composantId: 'comp-001' },
|
|
})
|
|
|
|
expect(mockPostFormData).toHaveBeenCalledTimes(1)
|
|
const [endpoint, formData] = mockPostFormData.mock.calls[0]!
|
|
expect(endpoint).toBe('/documents')
|
|
|
|
expect(formData).toBeInstanceOf(FormData)
|
|
expect(formData.get('file')).toBe(file)
|
|
expect(formData.get('name')).toBe('photo.jpg')
|
|
expect(formData.get('pieceId')).toBe('piece-001')
|
|
expect(formData.get('composantId')).toBe('comp-001')
|
|
})
|
|
|
|
it('uploads multiple files separately', async () => {
|
|
mockPostFormData
|
|
.mockResolvedValueOnce({ success: true, data: mockDocument })
|
|
.mockResolvedValueOnce({ success: true, data: mockDocument2 })
|
|
|
|
const { uploadDocuments } = useDocuments()
|
|
const file1 = createMockFile('photo.jpg')
|
|
const file2 = createMockFile('schema.pdf', 'application/pdf')
|
|
|
|
const result = await uploadDocuments({
|
|
files: [file1, file2],
|
|
context: { machineId: 'machine-001' },
|
|
})
|
|
|
|
expect(mockPostFormData).toHaveBeenCalledTimes(2)
|
|
|
|
// First call
|
|
const [, formData1] = mockPostFormData.mock.calls[0]!
|
|
expect(formData1.get('name')).toBe('photo.jpg')
|
|
expect(formData1.get('machineId')).toBe('machine-001')
|
|
|
|
// Second call
|
|
const [, formData2] = mockPostFormData.mock.calls[1]!
|
|
expect(formData2.get('name')).toBe('schema.pdf')
|
|
expect(formData2.get('machineId')).toBe('machine-001')
|
|
|
|
expect(result.success).toBe(true)
|
|
expect(Array.isArray(result.data) ? result.data : []).toHaveLength(2)
|
|
})
|
|
|
|
it('appends type to FormData when provided in context', async () => {
|
|
mockPostFormData.mockResolvedValue({ success: true, data: mockDocument })
|
|
|
|
const { uploadDocuments } = useDocuments()
|
|
const file = createMockFile('facture.pdf', 'application/pdf')
|
|
|
|
await uploadDocuments({
|
|
files: [file],
|
|
context: { siteId: 'site-001', type: 'facture' },
|
|
})
|
|
|
|
const [, formData] = mockPostFormData.mock.calls[0]!
|
|
expect(formData.get('type')).toBe('facture')
|
|
expect(formData.get('siteId')).toBe('site-001')
|
|
})
|
|
|
|
it('returns error when no files provided', async () => {
|
|
const { uploadDocuments } = useDocuments()
|
|
|
|
const result = await uploadDocuments({ files: [], context: {} })
|
|
|
|
expect(result.success).toBe(false)
|
|
expect(mockPostFormData).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// loadDocumentsByComponent
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('loadDocumentsByComponent', () => {
|
|
it('calls correct endpoint /documents/composant/{id}', async () => {
|
|
mockGet.mockResolvedValue({ success: true, data: wrapCollection([mockDocument]) })
|
|
|
|
const { loadDocumentsByComponent } = useDocuments()
|
|
const result = await loadDocumentsByComponent('comp-001')
|
|
|
|
expect(mockGet).toHaveBeenCalledTimes(1)
|
|
expect(mockGet).toHaveBeenCalledWith('/documents/composant/comp-001')
|
|
expect(result.success).toBe(true)
|
|
})
|
|
|
|
it('returns error for empty componentId', async () => {
|
|
const { loadDocumentsByComponent } = useDocuments()
|
|
const result = await loadDocumentsByComponent('')
|
|
|
|
expect(mockGet).not.toHaveBeenCalled()
|
|
expect(result.success).toBe(false)
|
|
})
|
|
})
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// loadDocumentsByPiece
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('loadDocumentsByPiece', () => {
|
|
it('calls correct endpoint /documents/piece/{id}', async () => {
|
|
mockGet.mockResolvedValue({ success: true, data: wrapCollection([mockDocument]) })
|
|
|
|
const { loadDocumentsByPiece } = useDocuments()
|
|
const result = await loadDocumentsByPiece('piece-001')
|
|
|
|
expect(mockGet).toHaveBeenCalledTimes(1)
|
|
expect(mockGet).toHaveBeenCalledWith('/documents/piece/piece-001')
|
|
expect(result.success).toBe(true)
|
|
})
|
|
|
|
it('returns error for empty pieceId', async () => {
|
|
const { loadDocumentsByPiece } = useDocuments()
|
|
const result = await loadDocumentsByPiece('')
|
|
|
|
expect(mockGet).not.toHaveBeenCalled()
|
|
expect(result.success).toBe(false)
|
|
})
|
|
})
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// loadDocumentsByMachine
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('loadDocumentsByMachine', () => {
|
|
it('calls correct endpoint /documents/machine/{id}', async () => {
|
|
mockGet.mockResolvedValue({ success: true, data: wrapCollection([mockDocument]) })
|
|
|
|
const { loadDocumentsByMachine } = useDocuments()
|
|
const result = await loadDocumentsByMachine('machine-001')
|
|
|
|
expect(mockGet).toHaveBeenCalledTimes(1)
|
|
expect(mockGet).toHaveBeenCalledWith('/documents/machine/machine-001')
|
|
expect(result.success).toBe(true)
|
|
})
|
|
|
|
it('returns error for empty machineId', async () => {
|
|
const { loadDocumentsByMachine } = useDocuments()
|
|
const result = await loadDocumentsByMachine('')
|
|
|
|
expect(mockGet).not.toHaveBeenCalled()
|
|
expect(result.success).toBe(false)
|
|
})
|
|
})
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// loadDocumentsByProduct
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('loadDocumentsByProduct', () => {
|
|
it('calls correct endpoint /documents/product/{id}', async () => {
|
|
mockGet.mockResolvedValue({ success: true, data: wrapCollection([mockDocument]) })
|
|
|
|
const { loadDocumentsByProduct } = useDocuments()
|
|
const result = await loadDocumentsByProduct('prod-001')
|
|
|
|
expect(mockGet).toHaveBeenCalledTimes(1)
|
|
expect(mockGet).toHaveBeenCalledWith('/documents/product/prod-001')
|
|
expect(result.success).toBe(true)
|
|
})
|
|
|
|
it('returns error for empty productId', async () => {
|
|
const { loadDocumentsByProduct } = useDocuments()
|
|
const result = await loadDocumentsByProduct('')
|
|
|
|
expect(mockGet).not.toHaveBeenCalled()
|
|
expect(result.success).toBe(false)
|
|
})
|
|
})
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// deleteDocument
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('deleteDocument', () => {
|
|
it('calls DELETE on correct endpoint', async () => {
|
|
mockDel.mockResolvedValue({ success: true })
|
|
|
|
const { deleteDocument } = useDocuments()
|
|
const result = await deleteDocument('doc-001')
|
|
|
|
expect(mockDel).toHaveBeenCalledTimes(1)
|
|
expect(mockDel).toHaveBeenCalledWith('/documents/doc-001')
|
|
expect(result.success).toBe(true)
|
|
})
|
|
|
|
it('removes from store when updateStore is true', async () => {
|
|
mockGet.mockResolvedValue({
|
|
success: true,
|
|
data: wrapCollection([mockDocument, mockDocument2]),
|
|
})
|
|
mockDel.mockResolvedValue({ success: true })
|
|
|
|
const { loadDocuments, deleteDocument, documents } = useDocuments()
|
|
|
|
// Load documents into store first
|
|
await loadDocuments({ force: true })
|
|
|
|
expect(documents.value).toHaveLength(2)
|
|
|
|
// Delete with updateStore: true
|
|
await deleteDocument('doc-001', { updateStore: true })
|
|
|
|
expect(documents.value).toHaveLength(1)
|
|
expect(documents.value[0]!.id).toBe('doc-002')
|
|
})
|
|
|
|
it('shows success toast on successful delete', async () => {
|
|
mockDel.mockResolvedValue({ success: true })
|
|
|
|
const { deleteDocument } = useDocuments()
|
|
await deleteDocument('doc-001')
|
|
|
|
expect(mockShowSuccess).toHaveBeenCalledWith('Document supprimé')
|
|
})
|
|
})
|