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); + }); }); }