test sweep: cover shortcut/ + base/event.dart (T-91)
test / unit + widget + golden + a11y (push) Failing after 30s
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 1m0s
test / unit + widget + golden + a11y (push) Failing after 30s
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 1m0s
Adds 9 tests in test/terminal/shortcut_event_test.dart covering Event / EventEmitter / EventSubscription, the platform branch in defaultTerminalShortcuts, and the three TerminalActions intent handlers (copy / paste / select-all, plus the empty-selection no-op path). Coverage: base/event.dart 2/13 -> 13/13; shortcut/actions.dart 8/24 -> 24/24; shortcut/shortcuts.dart 8/18 -> 18/18. Total 69.87% -> 70.30%; floor bumped to 70. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
/// Pure + widget tests for the small terminal helpers:
|
||||
/// `lib/src/terminal/src/base/event.dart`,
|
||||
/// `lib/src/terminal/src/ui/shortcut/shortcuts.dart`, and
|
||||
/// `lib/src/terminal/src/ui/shortcut/actions.dart`.
|
||||
library;
|
||||
|
||||
import 'package:clide/src/terminal/src/base/event.dart';
|
||||
import 'package:clide/src/terminal/src/terminal.dart';
|
||||
import 'package:clide/src/terminal/src/ui/controller.dart';
|
||||
import 'package:clide/src/terminal/src/ui/shortcut/actions.dart';
|
||||
import 'package:clide/src/terminal/src/ui/shortcut/shortcuts.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
group('Event / EventEmitter / EventSubscription', () {
|
||||
test('emit fans out to all listeners', () {
|
||||
final emitter = EventEmitter<int>();
|
||||
final received = <int>[];
|
||||
emitter((e) => received.add(e * 10));
|
||||
emitter((e) => received.add(e * 100));
|
||||
emitter.emit(2);
|
||||
expect(received, [20, 200]);
|
||||
});
|
||||
|
||||
test('subscription.dispose removes the listener', () {
|
||||
final emitter = EventEmitter<String>();
|
||||
final received = <String>[];
|
||||
final sub = emitter(received.add);
|
||||
emitter.emit('a');
|
||||
sub.dispose();
|
||||
emitter.emit('b');
|
||||
expect(received, ['a']);
|
||||
});
|
||||
|
||||
test('Event wrapper invokes the underlying emitter on call', () {
|
||||
final emitter = EventEmitter<int>();
|
||||
final event = emitter.event;
|
||||
final received = <int>[];
|
||||
event(received.add);
|
||||
emitter.emit(7);
|
||||
expect(received, [7]);
|
||||
});
|
||||
});
|
||||
|
||||
group('defaultTerminalShortcuts', () {
|
||||
tearDown(() => debugDefaultTargetPlatformOverride = null);
|
||||
|
||||
test('Linux/Windows/Android/Fuchsia use Ctrl-modified bindings', () {
|
||||
for (final p in [
|
||||
TargetPlatform.linux,
|
||||
TargetPlatform.windows,
|
||||
TargetPlatform.android,
|
||||
TargetPlatform.fuchsia,
|
||||
]) {
|
||||
debugDefaultTargetPlatformOverride = p;
|
||||
final map = defaultTerminalShortcuts;
|
||||
// Three entries: copy / paste / select-all.
|
||||
expect(map.length, 3);
|
||||
// Spot-check the paste binding — Ctrl-V on these platforms.
|
||||
final pasteEntry = map.entries.firstWhere((e) => e.value is PasteTextIntent);
|
||||
final activator = pasteEntry.key as SingleActivator;
|
||||
expect(activator.trigger, LogicalKeyboardKey.keyV);
|
||||
expect(activator.control, isTrue);
|
||||
expect(activator.meta, isFalse);
|
||||
}
|
||||
});
|
||||
|
||||
test('macOS/iOS use Meta-modified bindings', () {
|
||||
for (final p in [TargetPlatform.macOS, TargetPlatform.iOS]) {
|
||||
debugDefaultTargetPlatformOverride = p;
|
||||
final map = defaultTerminalShortcuts;
|
||||
expect(map.length, 3);
|
||||
final pasteEntry = map.entries.firstWhere((e) => e.value is PasteTextIntent);
|
||||
final activator = pasteEntry.key as SingleActivator;
|
||||
expect(activator.trigger, LogicalKeyboardKey.keyV);
|
||||
expect(activator.meta, isTrue);
|
||||
expect(activator.control, isFalse);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
group('TerminalActions', () {
|
||||
Widget host(Widget child) => Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: child,
|
||||
);
|
||||
|
||||
testWidgets('CopySelectionTextIntent writes selection text to the clipboard', (tester) async {
|
||||
final outputs = <String>[];
|
||||
final terminal = Terminal(maxLines: 100, onOutput: outputs.add);
|
||||
terminal.write('hello');
|
||||
final controller = TerminalController();
|
||||
addTearDown(controller.dispose);
|
||||
// Set a selection covering 'hello'.
|
||||
controller.setSelection(
|
||||
terminal.buffer.createAnchor(0, 0),
|
||||
terminal.buffer.createAnchor(5, 0),
|
||||
);
|
||||
|
||||
String? clipboardText;
|
||||
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
(call) async {
|
||||
if (call.method == 'Clipboard.setData') {
|
||||
final args = call.arguments as Map;
|
||||
clipboardText = args['text'] as String?;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
late BuildContext capturedContext;
|
||||
await tester.pumpWidget(host(TerminalActions(
|
||||
terminal: terminal,
|
||||
controller: controller,
|
||||
child: Builder(builder: (context) {
|
||||
capturedContext = context;
|
||||
return const SizedBox();
|
||||
}),
|
||||
)));
|
||||
|
||||
Actions.invoke(capturedContext, CopySelectionTextIntent.copy);
|
||||
await tester.pump();
|
||||
expect(clipboardText, contains('hello'));
|
||||
});
|
||||
|
||||
testWidgets('CopySelectionTextIntent is a no-op when nothing is selected', (tester) async {
|
||||
final terminal = Terminal(maxLines: 100, onOutput: (_) {});
|
||||
final controller = TerminalController();
|
||||
addTearDown(controller.dispose);
|
||||
var setDataCalls = 0;
|
||||
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
(call) async {
|
||||
if (call.method == 'Clipboard.setData') setDataCalls++;
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
late BuildContext ctx;
|
||||
await tester.pumpWidget(host(TerminalActions(
|
||||
terminal: terminal,
|
||||
controller: controller,
|
||||
child: Builder(builder: (c) {
|
||||
ctx = c;
|
||||
return const SizedBox();
|
||||
}),
|
||||
)));
|
||||
|
||||
Actions.invoke(ctx, CopySelectionTextIntent.copy);
|
||||
await tester.pump();
|
||||
expect(setDataCalls, 0);
|
||||
});
|
||||
|
||||
testWidgets('PasteTextIntent pastes clipboard text into terminal and clears selection', (tester) async {
|
||||
final outputs = <String>[];
|
||||
final terminal = Terminal(maxLines: 100, onOutput: outputs.add);
|
||||
final controller = TerminalController();
|
||||
addTearDown(controller.dispose);
|
||||
controller.setSelection(
|
||||
terminal.buffer.createAnchor(0, 0),
|
||||
terminal.buffer.createAnchor(2, 0),
|
||||
);
|
||||
expect(controller.selection, isNotNull);
|
||||
|
||||
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
(call) async {
|
||||
if (call.method == 'Clipboard.getData') {
|
||||
return <String, dynamic>{'text': 'pasted-payload'};
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
late BuildContext ctx;
|
||||
await tester.pumpWidget(host(TerminalActions(
|
||||
terminal: terminal,
|
||||
controller: controller,
|
||||
child: Builder(builder: (c) {
|
||||
ctx = c;
|
||||
return const SizedBox();
|
||||
}),
|
||||
)));
|
||||
|
||||
Actions.invoke(ctx, const PasteTextIntent(SelectionChangedCause.keyboard));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// terminal.paste emits via onOutput; selection is cleared.
|
||||
expect(outputs.any((o) => o.contains('pasted-payload')), isTrue);
|
||||
expect(controller.selection, isNull);
|
||||
});
|
||||
|
||||
testWidgets('SelectAllTextIntent sets a selection across the visible buffer', (tester) async {
|
||||
final terminal = Terminal(maxLines: 100, onOutput: (_) {});
|
||||
final controller = TerminalController();
|
||||
addTearDown(controller.dispose);
|
||||
expect(controller.selection, isNull);
|
||||
|
||||
late BuildContext ctx;
|
||||
await tester.pumpWidget(host(TerminalActions(
|
||||
terminal: terminal,
|
||||
controller: controller,
|
||||
child: Builder(builder: (c) {
|
||||
ctx = c;
|
||||
return const SizedBox();
|
||||
}),
|
||||
)));
|
||||
|
||||
Actions.invoke(ctx, const SelectAllTextIntent(SelectionChangedCause.keyboard));
|
||||
await tester.pump();
|
||||
expect(controller.selection, isNotNull);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user