Files
clide/test/kernel/file_open_test.dart
T
jpmschweitzerandClaude Fable 5 e2e305dea6 feat(canvas): open .canvas files in a workspace pane (T-322)
Makes the canvas foundation (parser/renderer/view) reachable. The
extension owns an app-scoped MultitabController (the diff/T-233
pattern) so open documents survive the pane being rebuilt; each
document is a real sub-tab per the refinement decision, kept alive
across switches. Routing goes through the existing seams instead of
the dead TabContribution.fileGlobs field: openWorkspaceFile gains a
.canvas branch mirroring .md, and ui.open gains a canvas reader for
D-6 parity (clide ui open canvas <path>). Also corrects the
json_canvas doc header that claimed SVG-lowering — the interactive
pane paints the model directly (D-103 live-widget exception).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 08:56:09 +02:00

71 lines
2.3 KiB
Dart

/// openWorkspaceFile (T-51/T-187) — the single dispatch point for file
/// activations: `.md` → markdown reader, `.canvas` → canvas pane (T-322),
/// everything else → the editor via IPC. Also records recents.
library;
import 'package:clide/clide.dart';
import 'package:clide/kernel/kernel.dart';
import 'package:flutter_test/flutter_test.dart';
import '../helpers/kernel_fixture.dart';
void main() {
late KernelFixture f;
late List<Message> published;
late List<Object?> editorOpens;
setUp(() async {
f = await KernelFixture.create();
published = [];
editorOpens = [];
f.services.messages.subscribe().listen(published.add);
f.ipc.stub('editor.open', (args) async {
editorOpens.add(args['path']);
return IpcResponse.ok(id: '1', data: const {});
});
});
tearDown(() => f.dispose());
Future<void> deliver() => Future<void>.delayed(Duration.zero);
test('.md routes to the markdown reader via the bus', () async {
openWorkspaceFile(f.services, 'docs/plan.md');
await deliver();
expect(published, hasLength(1));
expect(published.single.publisher, 'builtin.markdown');
expect(published.single.channel, 'selection');
expect(published.single.data, {'path': 'docs/plan.md'});
expect(editorOpens, isEmpty);
});
test('.canvas routes to the canvas pane via the bus (T-322)', () async {
openWorkspaceFile(f.services, 'notes/board.canvas');
await deliver();
expect(published, hasLength(1));
expect(published.single.publisher, 'builtin.canvas');
expect(published.single.channel, 'selection');
expect(published.single.data, {'path': 'notes/board.canvas'});
expect(editorOpens, isEmpty);
});
test('extension match is case-insensitive', () async {
openWorkspaceFile(f.services, 'BOARD.CANVAS');
await deliver();
expect(published.single.publisher, 'builtin.canvas');
});
test('everything else opens in the editor over IPC', () async {
openWorkspaceFile(f.services, 'lib/main.dart');
await deliver();
expect(published, isEmpty);
expect(editorOpens, ['lib/main.dart']);
});
test('records the open in recent files; empty path is a no-op', () async {
openWorkspaceFile(f.services, 'notes/board.canvas');
openWorkspaceFile(f.services, '');
await deliver();
expect(f.services.recentFiles.paths, ['notes/board.canvas']);
});
}