import { describe, expect, it } from 'vitest'; import { RenameVariableCommand } from '../../src/core/commands/variableCommands'; import { GraphInterpreter, resolveDef } from '../../src/core/graph'; import type { BehaviorAsset } from '../../src/core/model/Asset'; import { behaviorLookup } from '../../src/core/model/behaviorPrograms'; import { fakeContext, graphEditor, makeBehavior, makeProject } from '../helpers'; function setup() { const fixture = makeProject(); const behavior = makeBehavior(fixture, 'B', ['count', 'copy']); return { fixture, behavior, edit: graphEditor(fixture, behavior) }; } function run(behavior: BehaviorAsset, fixture: ReturnType, count = 5) { const ctx = fakeContext({ variables: { count, copy: 0 } }); new GraphInterpreter(behavior.graph, behaviorLookup(fixture.project)).fire('OnStart', ctx); return ctx.variables; } const titleOf = (behavior: BehaviorAsset, index: number) => resolveDef(behavior.graph.nodes[index]!, behavior.graph, () => null)!.label; describe('the Variable node', () => { it('can be read without a flow ever reaching it', () => { const { fixture, behavior, edit } = setup(); const start = edit.addNode('OnStart'); const variable = edit.addNode('Variable', { variable: 'count' }); const copy = edit.addNode('SetVariable', { variable: 'copy' }); edit.wire([ [start, 'exec', copy, 'exec'], [variable, 'value', copy, 'value'], ]); expect(run(behavior, fixture)).toEqual({ count: 5, copy: 5 }); }); it('writes its input when the flow arrives, unconnected as well as through an edge', () => { const { fixture, behavior, edit } = setup(); const start = edit.addNode('OnStart'); const inline = edit.addNode('Variable', { variable: 'count', value: 7 }); const number = edit.addNode('Number', { value: 3 }); const wired = edit.addNode('Variable', { variable: 'copy' }); edit.wire([ [start, 'exec', inline, 'exec'], [inline, 'exec', wired, 'exec'], [number, 'value', wired, 'value'], ]); expect(run(behavior, fixture)).toEqual({ count: 7, copy: 3 }); }); it('counts up through its own output: count = count + 1', () => { const { fixture, behavior, edit } = setup(); const start = edit.addNode('OnStart'); const variable = edit.addNode('Variable', { variable: 'count' }); const add = edit.addNode('Add', { b: 1 }); edit.wire([ [start, 'exec', variable, 'exec'], [variable, 'value', add, 'a'], [add, 'result', variable, 'value'], ]); expect(run(behavior, fixture).count).toBe(6); }); it('is read at the moment it is asked, so it sees a later write by another node', () => { const { fixture, behavior, edit } = setup(); const start = edit.addNode('OnStart'); const variable = edit.addNode('Variable', { variable: 'count', value: 7 }); const overwrite = edit.addNode('SetVariable', { variable: 'count', value: 9 }); const copy = edit.addNode('SetVariable', { variable: 'copy' }); edit.wire([ [start, 'exec', variable, 'exec'], [variable, 'exec', overwrite, 'exec'], [overwrite, 'exec', copy, 'exec'], [variable, 'value', copy, 'value'], ]); expect(run(behavior, fixture)).toEqual({ count: 9, copy: 9 }); }); }); describe('variable node titles', () => { it('name the picked variable, and keep the plain title until one is picked', () => { const { behavior, edit } = setup(); edit.addNode('GetVariable', { variable: 'count' }); edit.addNode('SetVariable', { variable: 'count' }); edit.addNode('Variable', { variable: 'count' }); edit.addNode('GetVariable'); edit.addNode('Variable'); expect([0, 1, 2, 3, 4].map((i) => titleOf(behavior, i))).toEqual([ 'Get count', 'Set count', 'count', 'Get variable', 'Variable', ]); }); it('follow a renamed variable', () => { const { fixture, behavior, edit } = setup(); edit.addNode('Variable', { variable: 'count' }); fixture.stack.execute(new RenameVariableCommand(behavior.id, 'count', 'score')); expect(titleOf(behavior, 0)).toBe('score'); }); });