test sweep: cover ExtensionManager contribution branches + ctx (T-91)
test / unit + widget + golden + a11y (push) Failing after 31s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m2s

Five new tests in test/kernel/src/extensions_manager_test.dart
covering the contribution-type case branches and the _ExtensionContext
passthrough surface that the existing 8-test suite didn't reach:

- TrayItemContribution lands in TrayRegistry on activate; deactivate
  removes it.
- StatusItem + ToolbarButton contributions activate/deactivate
  cleanly through PanelRegistry.
- LayoutPresetContribution exercises the no-kernel-side-wiring case
  branch (consumed by default-layout's own activate()).
- Every _ExtensionContext getter (log / events / messages / settings
  / theme / i18n / panels / arrangement / commands / palette /
  clipboard / files / notify / dialog / tray / secrets / os / net /
  focus / project / ipc) returns the same instance the kernel
  exposes — locks in the passthrough contract.

Coverage: kernel/src/extensions_manager.dart 85/130 -> 122/130
(94%). Remaining 8 lines are the disabled-by-default + persisted-
disabled paths plus the deactivate-during-failed-activate cleanup —
edge cases reachable only when extensions throw.

Total coverage 83.39% -> 83.85%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-13 08:22:33 +02:00
co-authored by Claude Opus 4.7
parent 2a7875e1a9
commit e2d98445f5
@@ -184,5 +184,99 @@ void main() {
await s1.cancel();
await s2.cancel();
});
test('TrayItemContribution lands in TrayRegistry; deactivate removes it', () async {
f.services.extensions.register(_Ext(
id: 'tray-ext',
contributions: [
const TrayItemContribution(
id: 'tray-ext.item',
label: 'Item',
onSelected: _noop,
),
],
));
await f.services.extensions.activateAll();
expect(f.services.tray.items.map((i) => i.id), contains('tray-ext.item'));
await f.services.extensions.deactivate('tray-ext');
expect(f.services.tray.items, isEmpty);
});
test('StatusItem + ToolbarButton contributions activate and deactivate cleanly', () async {
f.services.extensions.register(_Ext(
id: 'status-and-toolbar',
contributions: [
StatusItemContribution(
id: 'status-and-toolbar.status',
priority: 1,
build: (_) => const SizedBox.shrink(),
),
ToolbarButtonContribution(
id: 'status-and-toolbar.btn',
label: 'B',
onPressed: () {},
),
],
));
await f.services.extensions.activateAll();
// Both contributions register through PanelRegistry.contributionsFor.
expect(f.services.panels.contributionsFor(Slots.statusbar).whereType<StatusItemContribution>(), hasLength(1));
await f.services.extensions.deactivate('status-and-toolbar');
expect(f.services.panels.contributionsFor(Slots.statusbar).whereType<StatusItemContribution>(), isEmpty);
});
test('LayoutPresetContribution is accepted (no kernel-side wiring) and survives deactivate', () async {
// LayoutPresetContribution is consumed by the default-layout
// extension's own activate(); the manager's add/remove just hit
// the no-op case branch.
f.services.extensions.register(_Ext(
id: 'preset-only',
contributions: [
const LayoutPresetContribution(
id: 'preset-only.default',
displayName: 'Preset only',
slots: [],
),
],
));
await f.services.extensions.activateAll();
await f.services.extensions.deactivate('preset-only');
});
test('extension context exposes every kernel service via passthrough getters', () async {
ClideExtensionContext? captured;
f.services.extensions.register(_Ext(
id: 'ctx-capture',
onActivate: (ctx) async {
captured = ctx;
},
));
await f.services.extensions.activateAll();
final ctx = captured!;
expect(ctx.id, 'ctx-capture');
expect(ctx.log, same(f.services.log));
expect(ctx.events, same(f.services.events));
expect(ctx.messages, same(f.services.messages));
expect(ctx.settings, same(f.services.settings));
expect(ctx.theme, same(f.services.theme));
expect(ctx.i18n, same(f.services.i18n));
expect(ctx.panels, same(f.services.panels));
expect(ctx.arrangement, same(f.services.arrangement));
expect(ctx.commands, same(f.services.commands));
expect(ctx.palette, same(f.services.palette));
expect(ctx.clipboard, same(f.services.clipboard));
expect(ctx.files, same(f.services.files));
expect(ctx.notify, same(f.services.notify));
expect(ctx.dialog, same(f.services.dialog));
expect(ctx.tray, same(f.services.tray));
expect(ctx.secrets, same(f.services.secrets));
expect(ctx.os, same(f.services.os));
expect(ctx.net, same(f.services.net));
expect(ctx.focus, same(f.services.focus));
expect(ctx.project, same(f.services.project));
expect(ctx.ipc, same(f.services.ipc));
});
});
}
void _noop() {}