Files
clide/test/builtin/search/search_panel_view_test.dart
T
jpmschweitzerandClaude Opus 4.8 f96c565acd add find-in-files sidebar panel + Ctrl/Cmd+Shift+F
The find-in-files UI on top of the search.grep engine. A
FindInFilesController drives search.grep, accumulates streamed
search.match events (scoped to the active searchId, stale ids
ignored) grouped by file, and opens a match in the editor at its line.
The SearchPanelView contributes a sidebar tab: a debounced query box,
regex + case toggles, include/exclude glob fields, and a grouped
results list with the matched span highlighted.

findInFiles.open (Ctrl/Cmd+Shift+F) reveals and activates the search
tab.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:53:50 +02:00

73 lines
2.4 KiB
Dart

/// Widget test for [SearchPanelView] — searching renders streamed
/// matches grouped by file and clicking a match opens it (T-52).
library;
import 'package:clide/builtin/search/src/search_panel_view.dart';
import 'package:clide/clide.dart';
import 'package:clide/kernel/kernel.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import '../../helpers/kernel_fixture.dart';
import '../../helpers/widget_harness.dart';
IpcResponse _ok(Map<String, Object?> data) => IpcResponse.ok(id: '', data: data);
void main() {
late KernelFixture f;
setUp(() async {
f = await KernelFixture.create();
f.ipc.stub('search.grep', (_) async => _ok({'searchId': 's1'}));
f.ipc.stub('search.cancel', (_) async => _ok(const {}));
});
tearDown(() => f.dispose());
void emitMatches() {
f.services.events.emit(DaemonEvent(
subsystem: 'search',
kind: 'search.match',
data: const {
'searchId': 's1',
'matches': [
{'path': 'lib/a.dart', 'line': 12, 'matchStart': 6, 'matchEnd': 9, 'preview': 'final foo = 1;'},
],
},
ts: DateTime.now().toUtc(),
));
}
testWidgets('search renders matches grouped by file', (tester) async {
await tester.pumpWidget(harness(f, const SearchPanelView()));
await tester.enterText(find.byType(EditableText).first, 'foo');
await tester.pump(const Duration(milliseconds: 250)); // debounce → run()
await pumpAsync(tester); // search.grep resolves; activeSearchId set
emitMatches();
await pumpAsync(tester);
expect(find.text('lib/a.dart'), findsOneWidget); // file group header
expect(find.text('12'), findsOneWidget); // line number
});
testWidgets('tapping a match opens the editor at its line', (tester) async {
Map<String, Object?>? opened;
f.ipc.stub('editor.open', (args) async {
opened = args;
return _ok(const {});
});
await tester.pumpWidget(harness(f, const SearchPanelView()));
await tester.enterText(find.byType(EditableText).first, 'foo');
await tester.pump(const Duration(milliseconds: 250));
await pumpAsync(tester);
emitMatches();
await pumpAsync(tester);
await tester.tap(find.text('12')); // the match row's line number
await pumpAsync(tester);
expect(opened, isNotNull);
expect(opened!['path'], 'lib/a.dart');
expect(opened!['line'], 12);
});
}