* refactor(tools): group the oracle captures and mirror the bindings tests
Two names broke the rule that a multi-file domain gets a subfolder
rather than repeated sibling prefixes. `document-capture.ts` repeated
its own folder and sat beside `capture-scene.ts`, so both become
`document/capture/{document,scene}.ts` behind `#visual/capture/*`.
The document bindings tests were split between `document/binding/` and
a `text-bindings.test.ts` one level above, while their source is
`document/bindings/`. They move into `document/bindings/` and take the
source file names, so the test tree mirrors it.
* refactor(fig): group the node-change prefix families into folders
`node-change/` held five pairs of files sharing a leading segment —
derived-text, style, text, variable, vector — plus `instance-geometry`
beside an `instance-linkage/` folder. The guide asks for a subfolder
rather than a repeated prefix, so each becomes one: `style/{refs,runs}`,
`variable/{bindings,expression}`, and so on, with `instance-linkage/`
folded in beside `instance/geometry.ts`.
Imports across seventeen files follow. Two of the moved files reached
out of the package tree with `../../` once they were a level deeper;
those use `#fig/*` instead.
73 lines
2.3 KiB
TypeScript
73 lines
2.3 KiB
TypeScript
import { expect, test } from 'bun:test'
|
|
|
|
import { linearVariableExpression } from '#fig/node-change/variable/expression'
|
|
|
|
import { nodeChangeToProps } from '@open-pencil/fig/node-change'
|
|
import type { VariableDataEntry } from '@open-pencil/kiwi/fig/codec'
|
|
|
|
const alias: VariableDataEntry = {
|
|
dataType: 'ALIAS',
|
|
value: { alias: { guid: { sessionID: 1, localID: 2 } } }
|
|
}
|
|
const literal = (floatValue: number): VariableDataEntry => ({
|
|
dataType: 'FLOAT',
|
|
value: { floatValue }
|
|
})
|
|
const multiply = (...expressionArguments: VariableDataEntry[]): VariableDataEntry => ({
|
|
dataType: 'EXPRESSION',
|
|
value: { expressionValue: { expressionFunction: 'MULTIPLY', expressionArguments } }
|
|
})
|
|
|
|
test('linear products retain their symbolic alias and exact coefficient', () => {
|
|
expect(linearVariableExpression(multiply(literal(2), multiply(alias, literal(0.25))))).toEqual({
|
|
reference: alias.value?.alias,
|
|
multiplier: 0.5
|
|
})
|
|
const props = nodeChangeToProps(
|
|
{
|
|
type: 'FRAME',
|
|
parameterConsumptionMap: {
|
|
entries: [
|
|
{
|
|
variableField: 'OPACITY',
|
|
variableData: multiply(literal(0.5), alias)
|
|
}
|
|
]
|
|
}
|
|
},
|
|
[]
|
|
)
|
|
expect(props.boundVariables).toEqual({ opacity: '1:2' })
|
|
expect(props.variableBindingScales).toEqual({ opacity: 0.005 })
|
|
})
|
|
|
|
test('nonlinear, unsupported, overflowing, and unbounded expressions fail rather than lose bindings', () => {
|
|
expect(() => linearVariableExpression(multiply(alias, alias))).toThrow('one alias')
|
|
expect(() => linearVariableExpression({ dataType: 'EXPRESSION' })).toThrow('Missing')
|
|
expect(() =>
|
|
nodeChangeToProps(
|
|
{
|
|
type: 'FRAME',
|
|
parameterConsumptionMap: {
|
|
entries: [
|
|
{
|
|
variableField: 'WIDTH',
|
|
variableData: multiply(literal(2), literal(3))
|
|
}
|
|
]
|
|
}
|
|
},
|
|
[]
|
|
)
|
|
).toThrow('one alias')
|
|
expect(() =>
|
|
linearVariableExpression({ value: { expressionValue: { expressionFunction: 'ADD' } } })
|
|
).toThrow('Unsupported')
|
|
expect(() =>
|
|
linearVariableExpression(multiply(literal(Number.MAX_VALUE), literal(2), alias))
|
|
).toThrow('non-finite')
|
|
let deep = alias
|
|
for (let index = 0; index < 66; index++) deep = multiply(literal(1), deep)
|
|
expect(() => linearVariableExpression(deep)).toThrow('maximum depth')
|
|
})
|