P4-02 P6-02: add metadata-driven property editor

This commit is contained in:
2026-08-02 07:53:35 -04:00
parent 8d8d1b7c26
commit 23b7f58df5
10 changed files with 250 additions and 30 deletions

View File

@@ -100,7 +100,7 @@ test('persistence writes are serialized and continue after a failed write', asyn
test('autosave scheduler coalesces idle document changes and flushes the latest version', async () => {
const savedVersions: number[] = []
const scheduler = new ProjectAutosaveScheduler(async (document) => { savedVersions.push(document.version); return { documentId: document.id, documentVersion: document.version, persistedAt: Date.now(), mode: 'sqlite-memory' } }, 5)
const document = (version: number) => ({ id: 'doc', label: 'Autosave', version, dirty: true, readOnly: false, units: 'mm', tree: [] })
const document = (version: number) => ({ id: 'doc', label: 'Autosave', version, dirty: true, readOnly: false, units: 'mm', tree: [], objects: [] })
scheduler.schedule(document(1))
scheduler.schedule(document(2))
await new Promise((resolve) => setTimeout(resolve, 15))
@@ -172,12 +172,37 @@ test('document mutations use the facade history boundary', () => {
assert.equal(facade.app.document.getActive().label, 'Untitled document')
})
test('typed properties are validated, versioned and undoable through the document boundary', () => {
const facade = createMockFacade()
const before = facade.app.document.getActive().version
assert.equal(facade.app.document.getObject('pad')?.properties.find((property) => property.name === 'Length')?.value, 42)
facade.app.document.setProperty({ objectId: 'pad', propertyName: 'Length', value: 50 })
assert.equal(facade.app.document.getActive().version, before + 1)
assert.equal(facade.app.document.getActive().tree.find((item) => item.id === 'pad')?.state, 'dirty')
assert.equal(facade.app.document.getObject('pad')?.properties.find((property) => property.name === 'Status')?.value, 'Touched')
assert.throws(() => facade.app.document.setProperty({ objectId: 'pad', propertyName: 'Length', value: -1 }), /negative/)
assert.equal(facade.app.document.getActive().version, before + 1)
facade.history.undo()
assert.equal(facade.app.document.getObject('pad')?.properties.find((property) => property.name === 'Length')?.value, 42)
facade.history.redo()
assert.equal(facade.app.document.getObject('pad')?.properties.find((property) => property.name === 'Length')?.value, 50)
})
test('view properties do not mark the geometry object touched', () => {
const facade = createMockFacade()
facade.app.document.setProperty({ objectId: 'fillet', propertyName: 'Visibility', value: false })
assert.equal(facade.app.document.getActive().tree.find((item) => item.id === 'fillet')?.state, 'valid')
assert.equal(facade.app.document.getObject('fillet')?.properties.find((property) => property.name === 'Visibility')?.value, false)
})
test('project persistence remains behind the facade contract', async () => {
const facade = createMockFacade()
facade.app.document.setProperty({ objectId: 'pad', propertyName: 'Length', value: 55 })
const saved = await facade.project.save()
assert.equal(saved.documentId, 'doc-pump-housing')
const loaded = await facade.project.load('doc-pump-housing')
assert.equal(loaded?.label, 'Pump Housing')
assert.equal(loaded?.objects.find((object) => object.id === 'pad')?.properties.find((property) => property.name === 'Length')?.value, 55)
assert.equal(facade.project.capabilities().mode, 'sqlite-memory')
})