From e7cd9ca13ce46f0cf580c5153f9420599ed84a52 Mon Sep 17 00:00:00 2001 From: flemming-it Date: Mon, 1 Jun 2026 00:53:35 +0200 Subject: [PATCH] test(editor): controller tests for graph<->text round-trip Four behaviour tests guarding the controller's contract with the rest of the editor: - openFlow seeds the graph + clears the dirty flag against the freshly-captured baseline. - graph edits re-emit YAML into the shared CodeController so the text tab stays in lockstep. - graph edits mark the buffer dirty against the baseline; markSaved snaps it back to clean. - selectStep is idempotent: re-selecting the same step doesn't notify listeners (cheap rebuild guard for the properties panel). 11/11 tests pass. Signed-off-by: flemming-it --- test/editor_controller_test.dart | 93 ++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 test/editor_controller_test.dart diff --git a/test/editor_controller_test.dart b/test/editor_controller_test.dart new file mode 100644 index 0000000..3a6db19 --- /dev/null +++ b/test/editor_controller_test.dart @@ -0,0 +1,93 @@ +// 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); + }); + }); +}