openpencil/tests/engine/library/instance-updates.test.ts

127 lines
4.7 KiB
TypeScript
Raw Permalink Normal View History

import { describe, expect, test } from 'bun:test'
import {
createLibraryRevision,
materializeLibraryAsset,
planOutdatedLibraryInstances
} from '@open-pencil/core/library'
import { SceneGraph } from '@open-pencil/scene-graph'
function source(label: string) {
const graph = new SceneGraph()
const page = graph.getPages()[0]
const component = graph.createNode('COMPONENT', page.id, {
name: 'Button',
componentKey: 'button',
width: label === 'New' ? 120 : 80
})
graph.createNode('TEXT', component.id, { name: 'Label', text: label })
return graph
}
describe('instance-authoritative library updates', () => {
test('respects explicit instance scope and leaves other assets untouched', async () => {
const oldSource = source('Old')
const oldPage = oldSource.getPages()[0]
oldSource.createNode('COMPONENT', oldPage.id, {
name: 'Card',
componentKey: 'card',
width: 200
})
const newSource = source('New')
const newPage = newSource.getPages()[0]
newSource.createNode('COMPONENT', newPage.id, {
name: 'Card',
componentKey: 'card',
width: 200
})
const first = await createLibraryRevision({
libraryId: 'design-system',
name: 'Design system',
graph: oldSource
})
const second = await createLibraryRevision({
libraryId: 'design-system',
name: 'Design system',
graph: newSource,
previousRevisionId: first.manifest.revisionId
})
const consumer = new SceneGraph()
const button = materializeLibraryAsset(consumer, first, 'button')
const card = materializeLibraryAsset(consumer, first, 'card')
const firstPage = consumer.getPages()[0]
const secondPage = consumer.addPage('Second')
const reviewed = consumer.createNode('INSTANCE', firstPage.id, {
componentId: button.componentId
})
const samePage = consumer.createNode('INSTANCE', firstPage.id, {
componentId: button.componentId
})
const otherPage = consumer.createNode('INSTANCE', secondPage.id, {
componentId: button.componentId
})
const otherAsset = consumer.createNode('INSTANCE', firstPage.id, {
componentId: card.componentId
})
materializeLibraryAsset(consumer, second, 'button')
const plans = planOutdatedLibraryInstances(
consumer,
second,
new Set(['button']),
new Set([reviewed.id])
)
expect(plans.map((plan) => plan.instanceId)).toEqual([reviewed.id])
const plan = plans[0]
if (!plan) throw new Error('Expected scoped plan')
consumer.swapInstanceComponent(plan.instanceId, plan.componentId)
expect(consumer.getNode(reviewed.id)?.componentId).toBe(plan.componentId)
expect(consumer.getNode(samePage.id)?.componentId).toBe(button.componentId)
expect(consumer.getNode(otherPage.id)?.componentId).toBe(button.componentId)
expect(consumer.getNode(otherAsset.id)?.componentId).toBe(card.componentId)
consumer.swapInstanceComponent(plan.instanceId, plan.previousComponentId)
expect(consumer.getNode(reviewed.id)?.componentId).toBe(button.componentId)
})
test('plans only outdated instances and permits mixed revisions', async () => {
const first = await createLibraryRevision({
libraryId: 'design-system',
name: 'Design system',
graph: source('Old'),
publishedAt: '2026-01-01T00:00:00.000Z'
})
const second = await createLibraryRevision({
libraryId: 'design-system',
name: 'Design system',
graph: source('New'),
previousRevisionId: first.manifest.revisionId,
publishedAt: '2026-01-02T00:00:00.000Z'
})
const consumer = new SceneGraph()
const oldDefinition = materializeLibraryAsset(consumer, first, 'button')
const firstInstance = consumer.createNode('INSTANCE', consumer.getPages()[0].id, {
componentId: oldDefinition.componentId
})
const secondInstance = consumer.createNode('INSTANCE', consumer.getPages()[0].id, {
componentId: oldDefinition.componentId
})
materializeLibraryAsset(consumer, second, 'button')
const [firstPlan, secondPlan] = planOutdatedLibraryInstances(consumer, second)
expect([firstPlan?.instanceId, secondPlan?.instanceId]).toEqual([
firstInstance.id,
secondInstance.id
])
expect(firstPlan?.previousComponentId).toBe(oldDefinition.componentId)
expect(firstPlan?.componentId).not.toBe(oldDefinition.componentId)
consumer.swapInstanceComponent(firstPlan.instanceId, firstPlan.componentId)
expect(planOutdatedLibraryInstances(consumer, second).map((plan) => plan.instanceId)).toEqual([
secondInstance.id
])
expect(consumer.getNode(firstInstance.id)?.componentId).not.toBe(
consumer.getNode(secondInstance.id)?.componentId
)
})
})