From 3339d3fcb53cfeffc2a092c622a871f81c7879d5 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Wed, 13 May 2026 07:30:52 +0200 Subject: [PATCH] test sweep: cover default_layout commands (T-91) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eight new tests in test/builtin/default_layout/widget_test.dart covering the DefaultLayoutExtension command handlers the existing four-test suite didn't reach: - All commands return the _notActivated() error pre-activate (sweeps every command's defensive null-context branch in one loop). - palette.toggle flips the palette's open state. - sidebar.collapse + context.collapse toggle their slots. - panel.focus.left / .middle / .right — including the auto-expand branches when a focused side is collapsed. - panel.focusMode toggles focus mode on the active slot. - panel.focusMode.exit unwinds in order: focus mode → editor → palette, with the empty-data no-op fallback. - editor.open + editor.close including the close-already-closed no-op. - sidebar.section.N: auto-expand sidebar + no-op when no tabs are contributed. Coverage: builtin/default_layout/src/extension.dart 60/167 -> 139/167 (83%). Remaining 28 lines are the _restoreLayout / _persistLayout persistence paths and the section-activation happy path, which need a populated tab list + a projectDir on settings to exercise — out of scope for this batch. Total coverage 79.80% -> 80.81%; floor bumped to 80. Co-Authored-By: Claude Opus 4.7 (1M context) --- pubspec.yaml | 2 +- test/builtin/default_layout/widget_test.dart | 109 +++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 5d005f1a..25835bf0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,7 +18,7 @@ repository: https://github.com/postmeridiem/clide # Pre-push line-coverage floor. Ratchets up only — see D-66. # Reading: `awk -F: '/^coverage_floor:/ {gsub(/ /,"",$2); print $2}' pubspec.yaml`. -coverage_floor: 79 +coverage_floor: 80 # Project metadata (was project.yaml, folded in per D-056). # version: above is the single source of truth. The Makefile reads diff --git a/test/builtin/default_layout/widget_test.dart b/test/builtin/default_layout/widget_test.dart index 6eb7c343..1b110ece 100644 --- a/test/builtin/default_layout/widget_test.dart +++ b/test/builtin/default_layout/widget_test.dart @@ -45,5 +45,114 @@ void main() { hasLength(1), ); }); + + test('all commands return not-activated errors before activate', () async { + final ext = DefaultLayoutExtension(); + final cmds = ext.contributions.whereType().toList(); + // 12+ commands including the 5 sidebar.section.N variants. + expect(cmds.length, greaterThan(10)); + for (final cmd in cmds) { + final r = await cmd.run(const []); + expect(r.ok, isFalse, reason: 'expected ${cmd.command} to fail pre-activate'); + expect(r.error!.message, contains('not activated')); + } + }); + + test('palette.toggle flips palette open state', () async { + f.services.extensions.register(DefaultLayoutExtension()); + await f.services.extensions.activateAll(); + expect(f.services.palette.isOpen, isFalse); + final r1 = await f.services.commands.execute('palette.toggle'); + expect(r1.ok, isTrue); + expect(r1.data['open'], isTrue); + expect(f.services.palette.isOpen, isTrue); + final r2 = await f.services.commands.execute('palette.toggle'); + expect(r2.data['open'], isFalse); + }); + + test('sidebar.collapse + context.collapse toggle their respective slots', () async { + f.services.extensions.register(DefaultLayoutExtension()); + await f.services.extensions.activateAll(); + expect(f.services.arrangement.isCollapsed(Slots.sidebar), isFalse); + await f.services.commands.execute('sidebar.collapse'); + expect(f.services.arrangement.isCollapsed(Slots.sidebar), isTrue); + await f.services.commands.execute('sidebar.collapse'); + expect(f.services.arrangement.isCollapsed(Slots.sidebar), isFalse); + await f.services.commands.execute('context.collapse'); + expect(f.services.arrangement.isCollapsed(Slots.contextPanel), isTrue); + }); + + test('panel.focus.{left,middle,right} expand collapsed sides + set focus', () async { + f.services.extensions.register(DefaultLayoutExtension()); + await f.services.extensions.activateAll(); + // Collapse both side panels first. + f.services.arrangement.setCollapsed(Slots.sidebar, true); + f.services.arrangement.setCollapsed(Slots.contextPanel, true); + // Focus left: re-expands sidebar. + await f.services.commands.execute('panel.focus.left'); + expect(f.services.arrangement.isCollapsed(Slots.sidebar), isFalse); + // Focus right: re-expands context. + await f.services.commands.execute('panel.focus.right'); + expect(f.services.arrangement.isCollapsed(Slots.contextPanel), isFalse); + // Focus middle: no expansion needed, just sets focus. + final r = await f.services.commands.execute('panel.focus.middle'); + expect(r.ok, isTrue); + }); + + test('panel.focusMode toggles focus mode on the active slot', () async { + f.services.extensions.register(DefaultLayoutExtension()); + await f.services.extensions.activateAll(); + expect(f.services.arrangement.isInFocusMode, isFalse); + await f.services.commands.execute('panel.focusMode'); + expect(f.services.arrangement.isInFocusMode, isTrue); + }); + + test('panel.focusMode.exit unwinds focus-mode → editor → palette', () async { + f.services.extensions.register(DefaultLayoutExtension()); + await f.services.extensions.activateAll(); + // Focus mode: exit clears it. + f.services.arrangement.toggleFocusMode(Slots.workspace); + expect(f.services.arrangement.isInFocusMode, isTrue); + await f.services.commands.execute('panel.focusMode.exit'); + expect(f.services.arrangement.isInFocusMode, isFalse); + // Editor open: exit closes it. + f.services.arrangement.openEditor(); + expect(f.services.arrangement.editorOpen, isTrue); + await f.services.commands.execute('panel.focusMode.exit'); + expect(f.services.arrangement.editorOpen, isFalse); + // Palette open: exit closes it. + f.services.palette.toggle(); + expect(f.services.palette.isOpen, isTrue); + await f.services.commands.execute('panel.focusMode.exit'); + expect(f.services.palette.isOpen, isFalse); + // Nothing to exit: returns ok with empty data. + final noop = await f.services.commands.execute('panel.focusMode.exit'); + expect(noop.ok, isTrue); + }); + + test('editor.open and editor.close toggle the editor split', () async { + f.services.extensions.register(DefaultLayoutExtension()); + await f.services.extensions.activateAll(); + expect(f.services.arrangement.editorOpen, isFalse); + await f.services.commands.execute('editor.open'); + expect(f.services.arrangement.editorOpen, isTrue); + await f.services.commands.execute('editor.close'); + expect(f.services.arrangement.editorOpen, isFalse); + // editor.close when already closed → ok no-op. + final noop = await f.services.commands.execute('editor.close'); + expect(noop.ok, isTrue); + }); + + test('sidebar.section.N activates the Nth sidebar tab', () async { + f.services.extensions.register(DefaultLayoutExtension()); + await f.services.extensions.activateAll(); + // Collapse sidebar so we exercise the re-expand branch. + f.services.arrangement.setCollapsed(Slots.sidebar, true); + // No-op when no tabs are contributed. + final r = await f.services.commands.execute('sidebar.section.1'); + expect(r.ok, isTrue); + // Sidebar auto-expanded. + expect(f.services.arrangement.isCollapsed(Slots.sidebar), isFalse); + }); }); }