// Behaviour tests for FlowEditorController — the bridge // between the YAML buffer + the graph + the layout sidecar. // The model tests cover parsing correctness; these cover // that the controller wires text -> graph reparsing, // graph -> YAML emission, selection bookkeeping, and // dirty/saved transitions correctly. import 'package:fai_studio_flow_editor/src/editor_controller.dart'; import 'package:fai_studio_flow_editor/src/model/flow_graph.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('FlowEditorController', () { test('openFlow seeds the graph + clears the dirty flag', () { final c = FlowEditorController(); addTearDown(c.dispose); c.openFlow('hello', ''' name: hello inputs: name: text steps: - id: greet use: debug.echo@^0 with: message: \$inputs.name outputs: greeting: \$greet.echoed '''); expect(c.activeName, 'hello'); expect(c.graph.steps, hasLength(1)); expect(c.graph.steps.single.id, 'greet'); expect(c.isDirty, isFalse); }); test('graph edit re-emits YAML into the text buffer', () { final c = FlowEditorController(); addTearDown(c.dispose); c.openFlow('demo', ''' name: demo steps: - id: a use: debug.echo@^0 outputs: {} '''); final initialText = c.codeController.fullText; c.applyGraphEdit( c.graph.withStepAdded(const FlowStep(id: 'b', use: 'debug.echo@^0')), ); expect(c.codeController.fullText, isNot(initialText)); expect(c.codeController.fullText, contains('id: b')); expect(c.graph.steps, hasLength(2)); }); test('graph edit marks the buffer dirty against the baseline', () { final c = FlowEditorController(); addTearDown(c.dispose); c.openFlow('demo', ''' steps: - id: a use: debug.echo@^0 outputs: {} '''); expect(c.isDirty, isFalse); c.applyGraphEdit( c.graph.withStepAdded(const FlowStep(id: 'b', use: 'debug.echo@^0')), ); expect(c.isDirty, isTrue); c.markSaved(); expect(c.isDirty, isFalse); }); test('selectStep notifies listeners only on change', () { final c = FlowEditorController(); addTearDown(c.dispose); c.openFlow('demo', ''' steps: - id: only use: debug.echo@^0 outputs: {} '''); var calls = 0; c.addListener(() => calls++); c.selectStep('only'); final after = calls; // Selecting the same thing again must NOT bump the // listener count — the controller short-circuits. c.selectStep('only'); expect(calls, after); c.selectStep(null); expect(calls, after + 1); }); }); }