From 57978c54c5e2c42015403bf8b726f4eb804b9b22 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 12 Jun 2026 14:34:07 +0200 Subject: [PATCH] cover the T-410 epic's pane handlers and Activity tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The epic's new lib surface dropped coverage to 94.87% (floor 95). Add the missing tests: the command-bus → _send path end-to-end in the pane (effort respawn with --effort, invalid-level notice, both pickers, set_permission_mode write, sidebar navigation messages, /memory editor.open, /help summary, TUI-only notice without a session write) and ActivityTabView's USAGE block + placeholder branch. 95.14% after. Co-Authored-By: Claude Fable 5 --- test/builtin/claude/activity_tab_test.dart | 37 +++++++++ test/builtin/claude/claude_pane_test.dart | 89 ++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 test/builtin/claude/activity_tab_test.dart diff --git a/test/builtin/claude/activity_tab_test.dart b/test/builtin/claude/activity_tab_test.dart new file mode 100644 index 00000000..63571f38 --- /dev/null +++ b/test/builtin/claude/activity_tab_test.dart @@ -0,0 +1,37 @@ +/// Direct tests for ActivityTabView (T-415): the USAGE block renders parsed +/// /usage values; the empty state still shows under the control strip. +library; + +import 'package:clide/builtin/claude/src/claude_stats.dart'; +import 'package:clide/builtin/claude/src/claude_status.dart' show ClaudeUsage; +import 'package:clide/builtin/claude/src/meta_sidebar/activity_tab.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../helpers/kernel_fixture.dart'; +import '../../helpers/widget_harness.dart'; + +void main() { + late KernelFixture f; + setUp(() async => f = await KernelFixture.create()); + tearDown(() => f.dispose()); + + testWidgets('renders the USAGE block from parsed /usage values', (tester) async { + const usage = ClaudeUsage(session: '15% used · resets Jun 12, 3:39pm', week: '53% used · resets Jun 15, 6:59pm', weekSonnet: '0% used'); + await tester.pumpWidget(harness(f, const ActivityTabView(stats: ClaudeStats(), primaryStatus: null, config: null, usage: usage))); + await tester.pump(); + + expect(find.text('USAGE'), findsOneWidget); + expect(find.text('15% used · resets Jun 12, 3:39pm'), findsOneWidget); + expect(find.text('53% used · resets Jun 15, 6:59pm'), findsOneWidget); + expect(find.text('0% used'), findsOneWidget); + }); + + testWidgets('no stats and no usage → the control strip plus the placeholder', (tester) async { + await tester.pumpWidget(harness(f, const ActivityTabView(stats: ClaudeStats(), primaryStatus: null, config: null))); + await tester.pump(); + + expect(find.text('SESSION'), findsOneWidget); // controls always present + expect(find.text('No activity recorded yet.'), findsOneWidget); + expect(find.text('USAGE'), findsNothing); + }); +} diff --git a/test/builtin/claude/claude_pane_test.dart b/test/builtin/claude/claude_pane_test.dart index 48b20b8b..f86d7186 100644 --- a/test/builtin/claude/claude_pane_test.dart +++ b/test/builtin/claude/claude_pane_test.dart @@ -339,4 +339,93 @@ void main() { expect(created, hasLength(1)); expect(proc.killed, isFalse); }); + + // ---- T-410 epic: bus-driven owned commands (T-412/T-413/T-414) ---------- + // The sidebar controls publish slash-command text on builtin.claude/command; + // the primary pane executes it through _send — the same path as typing. + group('command bus → owned slash commands', () { + Future command(WidgetTester tester, String text) => act(tester, () => f.services.messages.publish('builtin.claude', 'command', {'text': text})); + + testWidgets('/effort respawns the session carrying --effort (T-412)', (tester) async { + await mount(tester, const ClaudePane(showChrome: false)); + expect(created, hasLength(1)); + + await command(tester, '/effort xhigh'); + + expect(created, hasLength(2), reason: 'effort change = respawn'); + final args = spawnArgs.last; + final i = args.indexOf('--effort'); + expect(i, isNonNegative, reason: 'args: $args'); + expect(args[i + 1], 'xhigh'); + // The pane records the level on the session status (the wire never + // reports effort). + expect(orch.byId('primary')!.session.status.effort, 'xhigh'); + }); + + testWidgets('/effort with an unknown level notices and does NOT respawn (T-412)', (tester) async { + await mount(tester, const ClaudePane(showChrome: false)); + await command(tester, '/effort warp9'); + expect(created, hasLength(1)); + expect(find.textContaining('unknown effort "warp9"'), findsOneWidget); + }); + + testWidgets('bare /effort opens the effort picker in the interaction zone (T-412)', (tester) async { + await mount(tester, const ClaudePane(showChrome: false)); + await command(tester, '/effort'); + expect(find.byType(ModelPickerCard), findsOneWidget); + expect(find.text('effort'), findsOneWidget); // the picker title + }); + + testWidgets('/permissions sends set_permission_mode (T-413)', (tester) async { + await mount(tester, const ClaudePane(showChrome: false)); + await command(tester, '/permissions plan'); + final proc = created.single; + expect(proc.writes.any((w) => w.contains('set_permission_mode') && w.contains('"plan"')), isTrue, reason: proc.writes.join('\n')); + }); + + testWidgets('bare /permissions opens the mode picker (T-413)', (tester) async { + await mount(tester, const ClaudePane(showChrome: false)); + await command(tester, '/permissions'); + expect(find.byType(ModelPickerCard), findsOneWidget); + expect(find.text('permissions'), findsOneWidget); + }); + + testWidgets('/status and /config navigate the Claude sidebar (T-413)', (tester) async { + final tabs = []; + final sub = f.services.messages.subscribe(publisher: 'builtin.claude', channel: 'meta.tab').listen((m) => tabs.add(m.data['tab'] as String?)); + addTearDown(sub.cancel); + + await mount(tester, const ClaudePane(showChrome: false)); + await command(tester, '/status'); + await command(tester, '/config'); + await command(tester, '/mcp'); + expect(tabs, ['activity', 'config', 'config']); + }); + + testWidgets('/memory opens CLAUDE.md in the editor (T-413)', (tester) async { + final opened = []; + f.ipc.stub('editor.open', (args) async { + opened.add(args['path'] as String?); + return IpcResponse.ok(id: '', data: const {}); + }); + await mount(tester, const ClaudePane(showChrome: false)); + await command(tester, '/memory'); + expect(opened, ['/repo-a/CLAUDE.md']); + }); + + testWidgets('/help renders a local clide summary card (T-413)', (tester) async { + await mount(tester, const ClaudePane(showChrome: false)); + await command(tester, '/help'); + expect(find.textContaining('clide commands:'), findsOneWidget); + expect(find.text('clide'), findsOneWidget); // synthetic card attribution + }); + + testWidgets('a TUI-only command becomes a notice card, never reaching the session (T-411)', (tester) async { + await mount(tester, const ClaudePane(showChrome: false)); + final before = created.single.writes.length; + await command(tester, '/doctor'); + expect(find.textContaining('/doctor is a Claude Code TUI command'), findsOneWidget); + expect(created.single.writes.length, before, reason: 'nothing forwarded'); + }); + }); }