test(machine-detail) : add hierarchy loading and override data integrity tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
700
frontend/tests/composables/useMachineDetailData.test.ts
Normal file
700
frontend/tests/composables/useMachineDetailData.test.ts
Normal file
@@ -0,0 +1,700 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { ref } from 'vue'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mock data — realistic /machines/{id}/structure response
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const MACHINE_ID = 'cl-machine-abc123'
|
||||
const SITE_ID = 'cl-site-nord-001'
|
||||
const COMPONENT_LINK_ID = 'cl-mcl-001'
|
||||
const PIECE_LINK_ID = 'cl-mpl-001'
|
||||
const PRODUCT_LINK_ID = 'cl-mprl-001'
|
||||
const COMPOSANT_ID = 'cl-comp-moteur-001'
|
||||
const PIECE_ID = 'cl-piece-roul-001'
|
||||
const PRODUCT_ID = 'cl-prod-graisse-001'
|
||||
const CONSTRUCTEUR_ID = 'cstr-skf-001'
|
||||
|
||||
const mockConstructeurSKF = {
|
||||
id: CONSTRUCTEUR_ID,
|
||||
name: 'SKF',
|
||||
email: 'contact@skf.com',
|
||||
phone: '+33 1 23 45 67 89',
|
||||
}
|
||||
|
||||
const mockStructureResponse = {
|
||||
success: true,
|
||||
data: {
|
||||
machine: {
|
||||
id: MACHINE_ID,
|
||||
name: 'Presse hydraulique PH-200',
|
||||
reference: 'MACH-PH-200',
|
||||
prix: 150000,
|
||||
siteId: SITE_ID,
|
||||
site: { id: SITE_ID, name: 'Usine Nord' },
|
||||
documents: [{ id: 'doc-001', name: 'Manuel PH-200.pdf', type: 'manual' }],
|
||||
customFieldValues: [
|
||||
{
|
||||
id: 'mcfv-001',
|
||||
value: 'SN-2025-PH200',
|
||||
customField: {
|
||||
id: 'mcf-001',
|
||||
name: 'Serial Number',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: [],
|
||||
defaultValue: null,
|
||||
orderIndex: 0,
|
||||
machineContextOnly: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
customFields: [
|
||||
{
|
||||
id: 'mcf-001',
|
||||
name: 'Serial Number',
|
||||
type: 'text',
|
||||
required: true,
|
||||
options: [],
|
||||
defaultValue: null,
|
||||
orderIndex: 0,
|
||||
machineContextOnly: false,
|
||||
},
|
||||
],
|
||||
constructeurs: [
|
||||
{
|
||||
id: 'cl-mconst-001',
|
||||
constructeur: mockConstructeurSKF,
|
||||
supplierReference: 'SKF-PH200',
|
||||
},
|
||||
],
|
||||
},
|
||||
componentLinks: [
|
||||
{
|
||||
id: COMPONENT_LINK_ID,
|
||||
composant: {
|
||||
id: COMPOSANT_ID,
|
||||
name: 'Moteur principal',
|
||||
reference: 'COMP-MOT-001',
|
||||
prix: 12500,
|
||||
typeComposant: { id: 'tc-moteur', name: 'Moteur electrique' },
|
||||
constructeurs: [mockConstructeurSKF],
|
||||
constructeurIds: [CONSTRUCTEUR_ID],
|
||||
documents: [],
|
||||
customFields: [
|
||||
{
|
||||
definitionId: 'cf-comp-001',
|
||||
name: 'Tension nominale',
|
||||
type: 'number',
|
||||
value: '380',
|
||||
},
|
||||
],
|
||||
customFieldValues: [],
|
||||
},
|
||||
overrides: {
|
||||
name: 'Moteur principal PH-200',
|
||||
reference: 'COMP-MOT-PH200',
|
||||
prix: 13000,
|
||||
},
|
||||
contextCustomFields: [
|
||||
{
|
||||
id: 'ctx-cf-001',
|
||||
name: 'Position sur machine',
|
||||
type: 'text',
|
||||
machineContextOnly: true,
|
||||
},
|
||||
],
|
||||
contextCustomFieldValues: [
|
||||
{
|
||||
id: 'ctx-cfv-001',
|
||||
value: 'Bloc moteur gauche',
|
||||
customField: {
|
||||
id: 'ctx-cf-001',
|
||||
name: 'Position sur machine',
|
||||
type: 'text',
|
||||
machineContextOnly: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
pieceLinks: [
|
||||
{
|
||||
id: PIECE_LINK_ID,
|
||||
piece: {
|
||||
id: PIECE_ID,
|
||||
name: 'Roulement 6205',
|
||||
reference: 'ROUL-6205',
|
||||
prix: 45.90,
|
||||
typePiece: { id: 'tp-bearing', name: 'Roulement' },
|
||||
constructeurs: [mockConstructeurSKF],
|
||||
documents: [],
|
||||
customFields: [],
|
||||
},
|
||||
overrides: {
|
||||
name: 'Roulement 6205-RS',
|
||||
},
|
||||
quantity: 2,
|
||||
parentComponentLinkId: COMPONENT_LINK_ID,
|
||||
contextCustomFields: [],
|
||||
contextCustomFieldValues: [
|
||||
{
|
||||
id: 'ctx-cfv-piece-001',
|
||||
value: 'Cote entrainement',
|
||||
customField: {
|
||||
id: 'ctx-cf-piece-001',
|
||||
name: 'Emplacement',
|
||||
type: 'text',
|
||||
machineContextOnly: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
childLinks: [],
|
||||
},
|
||||
],
|
||||
pieceLinks: [],
|
||||
productLinks: [
|
||||
{
|
||||
id: PRODUCT_LINK_ID,
|
||||
product: {
|
||||
id: PRODUCT_ID,
|
||||
name: 'Graisse LGMT2',
|
||||
reference: 'LUB-LGMT2',
|
||||
prix: 45.90,
|
||||
},
|
||||
overrides: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
// Response with NO overrides — for fallback testing
|
||||
const mockStructureNoOverrides = {
|
||||
success: true,
|
||||
data: {
|
||||
machine: {
|
||||
...mockStructureResponse.data.machine,
|
||||
},
|
||||
componentLinks: [
|
||||
{
|
||||
id: COMPONENT_LINK_ID,
|
||||
composant: {
|
||||
id: COMPOSANT_ID,
|
||||
name: 'Moteur principal',
|
||||
reference: 'COMP-MOT-001',
|
||||
prix: 12500,
|
||||
typeComposant: { id: 'tc-moteur', name: 'Moteur electrique' },
|
||||
constructeurs: [],
|
||||
documents: [],
|
||||
customFields: [],
|
||||
customFieldValues: [],
|
||||
},
|
||||
overrides: null,
|
||||
contextCustomFields: [],
|
||||
contextCustomFieldValues: [],
|
||||
pieceLinks: [
|
||||
{
|
||||
id: PIECE_LINK_ID,
|
||||
piece: {
|
||||
id: PIECE_ID,
|
||||
name: 'Roulement 6205',
|
||||
reference: 'ROUL-6205',
|
||||
prix: 45.90,
|
||||
typePiece: { id: 'tp-bearing', name: 'Roulement' },
|
||||
constructeurs: [],
|
||||
documents: [],
|
||||
customFields: [],
|
||||
},
|
||||
overrides: null,
|
||||
quantity: 1,
|
||||
parentComponentLinkId: COMPONENT_LINK_ID,
|
||||
contextCustomFields: [],
|
||||
contextCustomFieldValues: [],
|
||||
},
|
||||
],
|
||||
childLinks: [],
|
||||
},
|
||||
],
|
||||
pieceLinks: [],
|
||||
productLinks: [],
|
||||
},
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mocks — all composables used by useMachineDetailData
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPatch = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockDel = vi.fn()
|
||||
|
||||
vi.mock('~/composables/useApi', () => ({
|
||||
useApi: () => ({
|
||||
get: mockGet,
|
||||
patch: mockPatch,
|
||||
post: mockPost,
|
||||
delete: mockDel,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('~/composables/useToast', () => ({
|
||||
useToast: () => ({
|
||||
showSuccess: vi.fn(),
|
||||
showError: vi.fn(),
|
||||
showInfo: vi.fn(),
|
||||
showToast: vi.fn(),
|
||||
toasts: { value: [] },
|
||||
clearAll: vi.fn(),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('~/composables/useMachines', () => ({
|
||||
useMachines: () => ({
|
||||
updateMachine: vi.fn().mockResolvedValue({ success: true }),
|
||||
updateStructure: vi.fn().mockResolvedValue({ success: true }),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('~/composables/useComposants', () => ({
|
||||
useComposants: () => ({
|
||||
updateComposant: vi.fn().mockResolvedValue({ success: true }),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('~/composables/usePieces', () => ({
|
||||
usePieces: () => ({
|
||||
updatePiece: vi.fn().mockResolvedValue({ success: true }),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('~/composables/useComponentTypes', () => ({
|
||||
useComponentTypes: () => ({
|
||||
componentTypes: ref([
|
||||
{ id: 'tc-moteur', name: 'Moteur electrique' },
|
||||
]),
|
||||
loadComponentTypes: vi.fn().mockResolvedValue(undefined),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('~/composables/usePieceTypes', () => ({
|
||||
usePieceTypes: () => ({
|
||||
pieceTypes: ref([
|
||||
{ id: 'tp-bearing', name: 'Roulement' },
|
||||
]),
|
||||
loadPieceTypes: vi.fn().mockResolvedValue(undefined),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('~/composables/useCustomFields', () => ({
|
||||
useCustomFields: () => ({
|
||||
upsertCustomFieldValue: vi.fn().mockResolvedValue({ success: true }),
|
||||
updateCustomFieldValue: vi.fn().mockResolvedValue({ success: true }),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('~/composables/useConstructeurs', () => ({
|
||||
useConstructeurs: () => ({
|
||||
constructeurs: ref([mockConstructeurSKF]),
|
||||
loadConstructeurs: vi.fn().mockResolvedValue(undefined),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('~/composables/useSites', () => ({
|
||||
useSites: () => ({
|
||||
sites: ref([{ id: SITE_ID, name: 'Usine Nord' }]),
|
||||
loadSites: vi.fn().mockResolvedValue(undefined),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('~/composables/useProducts', () => ({
|
||||
useProducts: () => ({
|
||||
products: ref([
|
||||
{ id: PRODUCT_ID, name: 'Graisse LGMT2', reference: 'LUB-LGMT2', prix: 45.90 },
|
||||
]),
|
||||
loadProducts: vi.fn().mockResolvedValue(undefined),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('~/composables/useDocuments', () => ({
|
||||
useDocuments: () => ({
|
||||
uploadDocuments: vi.fn().mockResolvedValue({ success: true }),
|
||||
deleteDocument: vi.fn().mockResolvedValue({ success: true }),
|
||||
loadDocumentsByMachine: vi.fn().mockResolvedValue({ success: true, data: [] }),
|
||||
loadDocumentsByProduct: vi.fn().mockResolvedValue({ success: true, data: [] }),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('~/composables/useConstructeurLinks', () => ({
|
||||
useConstructeurLinks: () => ({
|
||||
fetchLinks: vi.fn().mockResolvedValue([]),
|
||||
syncLinks: vi.fn().mockResolvedValue({ success: true }),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('~/utils/printTemplates/machineReport', () => ({
|
||||
buildMachinePrintContext: vi.fn(),
|
||||
buildMachinePrintHtml: vi.fn().mockReturnValue('<html></html>'),
|
||||
}))
|
||||
|
||||
vi.mock('~/utils/documentPreview', () => ({
|
||||
canPreviewDocument: vi.fn().mockReturnValue(false),
|
||||
}))
|
||||
|
||||
vi.mock('~/shared/utils/documentDisplayUtils', () => ({
|
||||
downloadDocument: vi.fn(),
|
||||
}))
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Import under test (after mocks)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
import { useMachineDetailData } from '~/composables/useMachineDetailData'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Setup
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockGet.mockResolvedValue(mockStructureResponse)
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function loadAndReturn(responseOverride?: unknown) {
|
||||
if (responseOverride) {
|
||||
mockGet.mockResolvedValue(responseOverride)
|
||||
}
|
||||
const result = useMachineDetailData(MACHINE_ID)
|
||||
await result.loadMachineData()
|
||||
return result
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// 1. Hierarchy loading
|
||||
// ===========================================================================
|
||||
|
||||
describe('hierarchy loading', () => {
|
||||
it('loads machine with all core fields', async () => {
|
||||
const { machine, machineName, machineReference, machineSiteId } = await loadAndReturn()
|
||||
|
||||
expect(machine.value).not.toBeNull()
|
||||
expect(machine.value!.id).toBe(MACHINE_ID)
|
||||
expect(machine.value!.name).toBe('Presse hydraulique PH-200')
|
||||
expect(machine.value!.reference).toBe('MACH-PH-200')
|
||||
expect(machine.value!.prix).toBe(150000)
|
||||
expect(machineName.value).toBe('Presse hydraulique PH-200')
|
||||
expect(machineReference.value).toBe('MACH-PH-200')
|
||||
expect(machineSiteId.value).toBe(SITE_ID)
|
||||
})
|
||||
|
||||
it('calls GET /machines/{id}/structure', async () => {
|
||||
await loadAndReturn()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith(`/machines/${MACHINE_ID}/structure`)
|
||||
})
|
||||
|
||||
it('loads componentLinks from structure response', async () => {
|
||||
const { machineComponentLinks } = await loadAndReturn()
|
||||
|
||||
expect(machineComponentLinks.value).toHaveLength(1)
|
||||
expect(machineComponentLinks.value[0]!.id).toBe(COMPONENT_LINK_ID)
|
||||
})
|
||||
|
||||
it('builds component hierarchy with composant data', async () => {
|
||||
const { components } = await loadAndReturn()
|
||||
|
||||
expect(components.value.length).toBeGreaterThanOrEqual(1)
|
||||
const comp = components.value[0]!
|
||||
expect(comp.composantId).toBe(COMPOSANT_ID)
|
||||
})
|
||||
|
||||
it('loads piece links nested under their parent componentLink', async () => {
|
||||
const { components } = await loadAndReturn()
|
||||
|
||||
const comp = components.value[0]!
|
||||
const pieces = comp.pieces as Record<string, unknown>[]
|
||||
expect(pieces).toBeDefined()
|
||||
expect(pieces.length).toBeGreaterThanOrEqual(1)
|
||||
|
||||
const piece = pieces[0]!
|
||||
expect(piece.pieceId).toBe(PIECE_ID)
|
||||
expect(piece.parentComponentLinkId).toBe(COMPONENT_LINK_ID)
|
||||
})
|
||||
|
||||
it('preserves piece quantity', async () => {
|
||||
const { components } = await loadAndReturn()
|
||||
|
||||
const comp = components.value[0]!
|
||||
const pieces = comp.pieces as Record<string, unknown>[]
|
||||
const piece = pieces[0]!
|
||||
expect(piece.quantity).toBe(2)
|
||||
})
|
||||
|
||||
it('loads product links at machine level', async () => {
|
||||
const { machineProductLinks } = await loadAndReturn()
|
||||
|
||||
expect(machineProductLinks.value).toHaveLength(1)
|
||||
expect(machineProductLinks.value[0]!.id).toBe(PRODUCT_LINK_ID)
|
||||
})
|
||||
|
||||
it('preserves machine documents', async () => {
|
||||
const { machine } = await loadAndReturn()
|
||||
|
||||
const docs = machine.value!.documents as unknown[]
|
||||
expect(docs).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('preserves machine customFieldValues', async () => {
|
||||
const { machine } = await loadAndReturn()
|
||||
|
||||
const cfv = machine.value!.customFieldValues as Record<string, unknown>[]
|
||||
expect(cfv).toHaveLength(1)
|
||||
expect((cfv[0] as any).value).toBe('SN-2025-PH200')
|
||||
})
|
||||
|
||||
it('sets loading to false after data load', async () => {
|
||||
const { loading } = await loadAndReturn()
|
||||
|
||||
expect(loading.value).toBe(false)
|
||||
})
|
||||
|
||||
it('handles failed API response gracefully', async () => {
|
||||
const { machine, components, pieces } = await loadAndReturn({
|
||||
success: false,
|
||||
error: 'Not found',
|
||||
})
|
||||
|
||||
expect(machine.value).toBeNull()
|
||||
expect(components.value).toEqual([])
|
||||
expect(pieces.value).toEqual([])
|
||||
})
|
||||
|
||||
it('handles invalid machine payload gracefully', async () => {
|
||||
const { machine } = await loadAndReturn({
|
||||
success: true,
|
||||
data: null,
|
||||
})
|
||||
|
||||
expect(machine.value).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
// ===========================================================================
|
||||
// 2. Overrides
|
||||
// ===========================================================================
|
||||
|
||||
describe('overrides on component links', () => {
|
||||
it('uses nameOverride when present', async () => {
|
||||
const { components } = await loadAndReturn()
|
||||
|
||||
const comp = components.value[0]!
|
||||
expect(comp.name).toBe('Moteur principal PH-200')
|
||||
})
|
||||
|
||||
it('falls back to composant.name when nameOverride is null', async () => {
|
||||
const { components } = await loadAndReturn(mockStructureNoOverrides)
|
||||
|
||||
const comp = components.value[0]!
|
||||
expect(comp.name).toBe('Moteur principal')
|
||||
})
|
||||
|
||||
it('uses referenceOverride when present', async () => {
|
||||
const { components } = await loadAndReturn()
|
||||
|
||||
const comp = components.value[0]!
|
||||
expect(comp.reference).toBe('COMP-MOT-PH200')
|
||||
})
|
||||
|
||||
it('falls back to composant.reference when referenceOverride is null', async () => {
|
||||
const { components } = await loadAndReturn(mockStructureNoOverrides)
|
||||
|
||||
const comp = components.value[0]!
|
||||
expect(comp.reference).toBe('COMP-MOT-001')
|
||||
})
|
||||
|
||||
it('uses prixOverride when present', async () => {
|
||||
const { components } = await loadAndReturn()
|
||||
|
||||
const comp = components.value[0]!
|
||||
expect(comp.prix).toBe(13000)
|
||||
})
|
||||
|
||||
it('falls back to composant.prix when prixOverride is null', async () => {
|
||||
const { components } = await loadAndReturn(mockStructureNoOverrides)
|
||||
|
||||
const comp = components.value[0]!
|
||||
expect(comp.prix).toBe(12500)
|
||||
})
|
||||
})
|
||||
|
||||
describe('overrides on piece links', () => {
|
||||
it('uses piece nameOverride when present', async () => {
|
||||
const { components } = await loadAndReturn()
|
||||
|
||||
const piece = (components.value[0]!.pieces as Record<string, unknown>[])[0]!
|
||||
expect(piece.name).toBe('Roulement 6205-RS')
|
||||
})
|
||||
|
||||
it('falls back to piece.name when nameOverride is null', async () => {
|
||||
const { components } = await loadAndReturn(mockStructureNoOverrides)
|
||||
|
||||
const piece = (components.value[0]!.pieces as Record<string, unknown>[])[0]!
|
||||
expect(piece.name).toBe('Roulement 6205')
|
||||
})
|
||||
|
||||
it('preserves piece reference from underlying entity when no override', async () => {
|
||||
const { components } = await loadAndReturn()
|
||||
|
||||
const piece = (components.value[0]!.pieces as Record<string, unknown>[])[0]!
|
||||
// The override only has name, so reference comes from the piece entity
|
||||
expect(piece.reference).toBe('ROUL-6205')
|
||||
})
|
||||
|
||||
it('preserves piece prix from underlying entity when no override', async () => {
|
||||
const { components } = await loadAndReturn()
|
||||
|
||||
const piece = (components.value[0]!.pieces as Record<string, unknown>[])[0]!
|
||||
expect(piece.prix).toBe(45.90)
|
||||
})
|
||||
})
|
||||
|
||||
// ===========================================================================
|
||||
// 3. Custom field values on links (context fields)
|
||||
// ===========================================================================
|
||||
|
||||
describe('contextCustomFieldValues on component links', () => {
|
||||
it('loads contextCustomFieldValues on component hierarchy nodes', async () => {
|
||||
const { components } = await loadAndReturn()
|
||||
|
||||
const comp = components.value[0]!
|
||||
const ctxValues = comp.contextCustomFieldValues as Record<string, unknown>[]
|
||||
expect(ctxValues).toBeDefined()
|
||||
expect(ctxValues).toHaveLength(1)
|
||||
expect((ctxValues[0] as any).value).toBe('Bloc moteur gauche')
|
||||
expect((ctxValues[0] as any).customField.name).toBe('Position sur machine')
|
||||
})
|
||||
|
||||
it('loads contextCustomFields definitions on component hierarchy nodes', async () => {
|
||||
const { components } = await loadAndReturn()
|
||||
|
||||
const comp = components.value[0]!
|
||||
const ctxFields = comp.contextCustomFields as Record<string, unknown>[]
|
||||
expect(ctxFields).toBeDefined()
|
||||
expect(ctxFields).toHaveLength(1)
|
||||
expect((ctxFields[0] as any).name).toBe('Position sur machine')
|
||||
expect((ctxFields[0] as any).machineContextOnly).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('contextCustomFieldValues on piece links', () => {
|
||||
it('loads contextCustomFieldValues on piece hierarchy nodes', async () => {
|
||||
const { components } = await loadAndReturn()
|
||||
|
||||
const piece = (components.value[0]!.pieces as Record<string, unknown>[])[0]!
|
||||
const ctxValues = piece.contextCustomFieldValues as Record<string, unknown>[]
|
||||
expect(ctxValues).toBeDefined()
|
||||
expect(ctxValues).toHaveLength(1)
|
||||
expect((ctxValues[0] as any).value).toBe('Cote entrainement')
|
||||
})
|
||||
|
||||
it('has empty contextCustomFieldValues when none provided', async () => {
|
||||
const { components } = await loadAndReturn(mockStructureNoOverrides)
|
||||
|
||||
const piece = (components.value[0]!.pieces as Record<string, unknown>[])[0]!
|
||||
const ctxValues = piece.contextCustomFieldValues as Record<string, unknown>[]
|
||||
expect(ctxValues).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
// ===========================================================================
|
||||
// 4. Constructeur links on machine
|
||||
// ===========================================================================
|
||||
|
||||
describe('constructeur links on machine', () => {
|
||||
it('parses constructeur links from machine data', async () => {
|
||||
const { constructeurLinks } = await loadAndReturn()
|
||||
|
||||
expect(constructeurLinks.value).toHaveLength(1)
|
||||
expect(constructeurLinks.value[0]!.constructeurId).toBe(CONSTRUCTEUR_ID)
|
||||
expect(constructeurLinks.value[0]!.supplierReference).toBe('SKF-PH200')
|
||||
})
|
||||
|
||||
it('populates machineConstructeurIds from links', async () => {
|
||||
const { machineConstructeurIds } = await loadAndReturn()
|
||||
|
||||
expect(machineConstructeurIds.value).toContain(CONSTRUCTEUR_ID)
|
||||
})
|
||||
|
||||
it('stores original constructeur links for cancel rollback', async () => {
|
||||
const { originalConstructeurLinks } = await loadAndReturn()
|
||||
|
||||
expect(originalConstructeurLinks.value).toHaveLength(1)
|
||||
expect(originalConstructeurLinks.value[0]!.constructeurId).toBe(CONSTRUCTEUR_ID)
|
||||
})
|
||||
|
||||
it('hasMachineConstructeur is true when constructeur present', async () => {
|
||||
const { hasMachineConstructeur } = await loadAndReturn()
|
||||
|
||||
expect(hasMachineConstructeur.value).toBe(true)
|
||||
})
|
||||
|
||||
it('resolves constructeur display objects', async () => {
|
||||
const { machineConstructeursDisplay } = await loadAndReturn()
|
||||
|
||||
expect(machineConstructeursDisplay.value.length).toBeGreaterThanOrEqual(1)
|
||||
const display = machineConstructeursDisplay.value[0] as any
|
||||
expect(display.name).toBe('SKF')
|
||||
})
|
||||
})
|
||||
|
||||
// ===========================================================================
|
||||
// 5. Site (required)
|
||||
// ===========================================================================
|
||||
|
||||
describe('site loaded with machine data', () => {
|
||||
it('machineSiteId is populated from machine payload', async () => {
|
||||
const { machineSiteId } = await loadAndReturn()
|
||||
|
||||
expect(machineSiteId.value).toBe(SITE_ID)
|
||||
})
|
||||
|
||||
it('sites ref is available for dropdowns', async () => {
|
||||
const { sites } = await loadAndReturn()
|
||||
|
||||
expect(sites.value).toHaveLength(1)
|
||||
expect((sites.value[0] as any).name).toBe('Usine Nord')
|
||||
})
|
||||
|
||||
it('machine.site object is preserved in machine ref', async () => {
|
||||
const { machine } = await loadAndReturn()
|
||||
|
||||
const site = machine.value!.site as Record<string, unknown>
|
||||
expect(site.id).toBe(SITE_ID)
|
||||
expect(site.name).toBe('Usine Nord')
|
||||
})
|
||||
})
|
||||
|
||||
// ===========================================================================
|
||||
// 6. UI state defaults
|
||||
// ===========================================================================
|
||||
|
||||
describe('UI state defaults', () => {
|
||||
it('isEditMode starts as false', () => {
|
||||
const { isEditMode } = useMachineDetailData(MACHINE_ID)
|
||||
expect(isEditMode.value).toBe(false)
|
||||
})
|
||||
|
||||
it('saving starts as false', () => {
|
||||
const { saving } = useMachineDetailData(MACHINE_ID)
|
||||
expect(saving.value).toBe(false)
|
||||
})
|
||||
|
||||
it('loading starts as true', () => {
|
||||
const { loading } = useMachineDetailData(MACHINE_ID)
|
||||
expect(loading.value).toBe(true)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user