From fb48a3133f0a186b5b22c4e65d1288c7f468a28b Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Wed, 13 May 2026 19:13:39 +0200 Subject: [PATCH] test sweep: icons, log.trace, DialogHost, FilesDropped getters (T-91) Three small additions to push coverage toward the 90% target: - test/widgets/icons_test.dart: one sweep test calling .paint() on every custom ClideIconPainter (Check, ChevronRight, ChevronDown, Dot, Folder, Gear, GitBranch, Plug, Search, Terminal, Warning). - log_test: Logger.trace covered at minLevel.trace + filtered out at minLevel.info. - services_bigger_test: DialogRouter.current getter; DialogHost widget rendered with backdrop + inner builder, then dismissed through the router. Plus FilesDropped subsystem/kind getters exercised through the existing notifyDropped test. Coverage 89.08% -> 89.93%. Co-Authored-By: Claude Opus 4.7 (1M context) --- test/kernel/src/log_test.dart | 13 +++++++ test/kernel/src/services_bigger_test.dart | 42 ++++++++++++++++++++++ test/widgets/icons_test.dart | 43 +++++++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 test/widgets/icons_test.dart diff --git a/test/kernel/src/log_test.dart b/test/kernel/src/log_test.dart index 30d0d36e..cbd9ea9a 100644 --- a/test/kernel/src/log_test.dart +++ b/test/kernel/src/log_test.dart @@ -67,5 +67,18 @@ void main() { expect(a, hasLength(1)); expect(b, hasLength(1)); }); + + test('trace records emit at minLevel.trace + are filtered above it', () { + final got = []; + final log = Logger(minLevel: LogLevel.trace, sinks: [got.add]); + log.trace('s', 'low-level detail'); + expect(got, hasLength(1)); + expect(got.first.level, LogLevel.trace); + // Same call at info-level is filtered. + got.clear(); + final filtered = Logger(minLevel: LogLevel.info, sinks: [got.add]); + filtered.trace('s', 'still detail'); + expect(got, isEmpty); + }); }); } diff --git a/test/kernel/src/services_bigger_test.dart b/test/kernel/src/services_bigger_test.dart index bace5103..8295eedc 100644 --- a/test/kernel/src/services_bigger_test.dart +++ b/test/kernel/src/services_bigger_test.dart @@ -52,6 +52,46 @@ void main() { r.dismiss(1); // close expect(calls, 2); }); + + test('current getter exposes the active builder while open', () { + final r = DialogRouter(); + expect(r.current, isNull); + r.show((ctx, _) => const SizedBox()); + expect(r.current, isNotNull); + r.dismiss(); + expect(r.current, isNull); + }); + + testWidgets('DialogHost renders the child + the dialog over a backdrop', (tester) async { + final r = DialogRouter(); + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: DialogHost( + router: r, + child: const ColoredBox(color: Color(0xFF111111), child: SizedBox.expand()), + ), + )); + await tester.pump(); + // No dialog open yet — backdrop + modal not present. + expect(find.byType(GestureDetector), findsNothing); + // Open one. + final future = r.show((ctx, dismiss) { + return GestureDetector( + onTap: () => dismiss('inner'), + child: const SizedBox(width: 100, height: 100), + ); + }); + await tester.pump(); + // Backdrop + inner-wrapper + my own each add a GestureDetector. + expect(find.byType(GestureDetector), findsAtLeast(2)); + // Dismiss programmatically — the future completes with null. + r.dismiss(); + final result = await future; + expect(result, isNull); + // After dismiss, no GestureDetector remains. + await tester.pump(); + expect(find.byType(GestureDetector), findsNothing); + }); }); group('FileServices stub', () { @@ -70,6 +110,8 @@ void main() { final e = await got.timeout(const Duration(seconds: 1)); expect(e.paths, ['/a.txt', '/b.txt']); expect(e.slot, Slots.workspace); + expect(e.subsystem, 'files'); + expect(e.kind, 'dropped'); expect(e.payload()['slot'], Slots.workspace.value); }); }); diff --git a/test/widgets/icons_test.dart b/test/widgets/icons_test.dart new file mode 100644 index 00000000..b44e5e0b --- /dev/null +++ b/test/widgets/icons_test.dart @@ -0,0 +1,43 @@ +/// Smoke tests for the custom paint icons under `lib/widgets/src/icons/`. +/// Each paint() call runs against a real PictureRecorder canvas to verify +/// it doesn't throw and to fill in lcov. +library; + +import 'dart:ui'; + +import 'package:clide/widgets/src/icons/check.dart'; +import 'package:clide/widgets/src/icons/chevron.dart'; +import 'package:clide/widgets/src/icons/dot.dart'; +import 'package:clide/widgets/src/icons/folder.dart'; +import 'package:clide/widgets/src/icons/gear.dart'; +import 'package:clide/widgets/src/icons/git_branch.dart'; +import 'package:clide/widgets/src/icons/plug.dart'; +import 'package:clide/widgets/src/icons/search.dart'; +import 'package:clide/widgets/src/icons/terminal_icon.dart'; +import 'package:clide/widgets/src/icons/warning.dart'; +import 'package:test/test.dart'; + +Canvas _canvas() => Canvas(PictureRecorder()); + +void main() { + const color = Color(0xFF000000); + + test('every custom icon painter renders without throwing', () { + const painters = [ + CheckIcon(), + ChevronRightIcon(), + ChevronDownIcon(), + DotIcon(), + FolderIcon(), + GearIcon(), + GitBranchIcon(), + PlugIcon(), + SearchIcon(), + TerminalIcon(), + WarningIcon(), + ]; + for (final p in painters) { + p.paint(_canvas(), color); + } + }); +}