import { test } from 'node:test' import assert from 'node:assert/strict' import { unzipSync } from 'fflate' import { createWebCadFacade } from '../src/facade/mockFacade' import { decodeFcstdPropertyValue } from '../src/facade/fcstd' import { encodeFreecadPropertyStatus } from '../src/facade/propertyStatus' import type { DocumentSnapshot, LinkSubValue } from '../src/facade/types' const hiddenStatus = encodeFreecadPropertyStatus(['PropHidden']) const initialValue: LinkSubValue = { schemaVersion: 1, objectId: 'ColorSourceA', subElements: ['Face1', 'Face3'] } const editedValue: LinkSubValue = { schemaVersion: 1, objectId: 'ColorSourceB', subElements: ['Face5'] } const fixture = (): DocumentSnapshot => ({ id: 'property-linksubhidden-facade', label: 'PropertyLinkSubHidden Facade', version: 1, dirty: false, readOnly: false, units: 'mm', tree: [ { id: 'LinkProbe', label: 'Link probe', type: 'feature', state: 'valid' }, { id: 'ColorSourceA', label: 'Color source A', type: 'feature', state: 'valid' }, { id: 'ColorSourceB', label: 'Color source B', type: 'feature', state: 'valid' }, ], objects: [ { id: 'LinkProbe', typeId: 'App::Link', properties: [{ name: 'ColoredElements', label: 'Colored elements', group: ' Link', scope: 'data', type: 'App::PropertyLinkSubHidden', value: initialValue, nativeStatus: hiddenStatus, recompute: true }] }, { id: 'ColorSourceA', typeId: 'Part::Feature', properties: [] }, { id: 'ColorSourceB', typeId: 'Part::Feature', properties: [] }, ], dependencies: [], recompute: { generation: 0, status: 'idle', objectStates: { LinkProbe: 'up-to-date', ColorSourceA: 'up-to-date', ColorSourceB: 'up-to-date' }, dirtyObjects: [], order: [], errors: [] }, }) const valueOf = (facade: ReturnType) => facade.app.document.getObject('LinkProbe')?.properties.find((property) => property.name === 'ColoredElements')?.value test('App::PropertyLinkSubHidden preserves structured references without dependency edges', async () => { const facade = createWebCadFacade({ initialDocument: fixture(), initialSelectedObjectIds: ['LinkProbe'], runtimeMode: 'mock' }) facade.app.document.setProperty({ objectId: 'LinkProbe', propertyName: 'ColoredElements', value: editedValue }) assert.deepEqual(valueOf(facade), editedValue) assert.deepEqual(facade.app.document.getDependencies(), []) assert.equal(facade.app.document.getActive().recompute?.objectStates.LinkProbe, 'touched') assert.throws(() => facade.app.document.setProperty({ objectId: 'LinkProbe', propertyName: 'ColoredElements', value: { schemaVersion: 1, objectId: 'Missing', subElements: ['Face1'] } as LinkSubValue }), /target does not exist/) assert.deepEqual(valueOf(facade), editedValue) const archive = facade.project.fcstd.serializeMetadata(facade.app.document.getActive()) const xml = new TextDecoder().decode(unzipSync(archive)['Document.xml']) assert.match(xml, new RegExp(`<\\/LinkSub><\\/Property>`)) assert.doesNotMatch(xml, /]+(?:group|doc|attr|ro|hide)=/) assert.doesNotMatch(xml, /