chain-studio-flow-editor/test/editor_controller_test.dart
flemming-it 1e9968496e feat(editor): properties-panel toggle + LabVIEW-style type colours
Two visible UX fixes:

1. Properties-panel toggle. Tap a step → opens. Tap the
   same step again → closes (deselect via toggle). Tap the
   canvas background → also closes. selectStep now returns
   to null when called with the already-selected id; the
   editor-controller test updated for the new semantics.

2. LabVIEW-style type colours on every port + wire.
   _typeAccent now picks vibrant brightness-aware hues per
   datatype:
     text   → magenta/pink (LabVIEW string)
     json   → amber/orange (cluster feel)
     bytes  → cyan/teal (raw binary)
     file   → green (file reference)
     number → yellow/amber
   Step-input dots, step-output dots, and outputs-endpoint
   dots all switch from the generic theme.primary to the
   field's declared type accent. Operators read the
   payload from the dot alone.

Editor tests still pass (20).

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-02 11:13:01 +02:00

99 lines
2.9 KiB
Dart

// 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 on change + toggles off on same-id click', () {
final c = FlowEditorController();
addTearDown(c.dispose);
c.openFlow('demo', '''
steps:
- id: only
use: debug.echo@^0
outputs: {}
''');
var calls = 0;
c.addListener(() => calls++);
// First select — selectedStepId goes null → 'only',
// listeners fire once.
c.selectStep('only');
expect(c.selectedStepId, 'only');
expect(calls, 1);
// Clicking the same step again deselects (toggle
// semantics): selectedStepId goes 'only' → null,
// listeners fire again. That IS a state change.
c.selectStep('only');
expect(c.selectedStepId, null);
expect(calls, 2);
// Explicit null while already null is a no-op.
c.selectStep(null);
expect(calls, 2);
});
});
}