Skip to content

Commit 1f4a792

Browse files
authored
feat(dart_frog_cli): add cli completion (#503)
1 parent 7fce6d5 commit 1f4a792

File tree

4 files changed

+61
-6
lines changed

4 files changed

+61
-6
lines changed

packages/dart_frog_cli/lib/src/command_runner.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'dart:io' as io;
22

33
import 'package:args/args.dart';
44
import 'package:args/command_runner.dart';
5+
import 'package:cli_completion/cli_completion.dart';
56
import 'package:dart_frog_cli/src/commands/commands.dart';
67
import 'package:dart_frog_cli/src/commands/update/update.dart';
78
import 'package:dart_frog_cli/src/version.dart';
@@ -24,7 +25,7 @@ const executableDescription =
2425
/// {@template dart_frog_command_runner}
2526
/// A [CommandRunner] for the Dart Frog CLI.
2627
/// {@endtemplate}
27-
class DartFrogCommandRunner extends CommandRunner<int> {
28+
class DartFrogCommandRunner extends CompletionCommandRunner<int> {
2829
/// {@macro dart_frog_command_runner}
2930
DartFrogCommandRunner({
3031
Logger? logger,
@@ -68,7 +69,10 @@ class DartFrogCommandRunner extends CommandRunner<int> {
6869
exitCode = ExitCode.software.code;
6970
}
7071

71-
if (argResults.command?.name != 'update') await _checkForUpdates();
72+
if (argResults.command?.name != 'update' &&
73+
argResults.command?.name != 'completion') {
74+
await _checkForUpdates();
75+
}
7276

7377
return exitCode;
7478
}
@@ -121,6 +125,10 @@ Run ${lightCyan.wrap('$executableName update')} to update''',
121125

122126
@override
123127
Future<int?> runCommand(ArgResults topLevelResults) async {
128+
if (topLevelResults.command?.name == 'completion') {
129+
await super.runCommand(topLevelResults);
130+
return ExitCode.success.code;
131+
}
124132
if (topLevelResults['version'] == true) {
125133
_logger.info(packageVersion);
126134
return ExitCode.success.code;

packages/dart_frog_cli/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ environment:
1111

1212
dependencies:
1313
args: ^2.1.0
14+
cli_completion: ^0.2.0
1415
mason: ^0.1.0-dev.39
1516
meta: ^1.7.0
1617
path: ^1.8.1

packages/dart_frog_cli/test/src/command_runner_test.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,20 @@ void main() {
158158
}),
159159
);
160160

161+
test(
162+
'does not show update message when the shell calls the '
163+
'completion command',
164+
() async {
165+
when(
166+
() => pubUpdater.getLatestVersion(any()),
167+
).thenAnswer((_) async => latestVersion);
168+
169+
final result = await commandRunner.run(['completion']);
170+
expect(result, equals(ExitCode.success.code));
171+
verifyNever(() => logger.info(updateMessage));
172+
},
173+
);
174+
161175
group('--help', () {
162176
test(
163177
'outputs usage',

packages/dart_frog_cli/test/src/commands/create/create_test.dart

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,27 +69,59 @@ void main() {
6969

7070
test('throws UsageException when project directory is invalid.', () async {
7171
final directory = Directory.systemTemp.createTempSync();
72-
when(() => argResults.rest).thenReturn([directory.path]);
72+
when(() => argResults.rest).thenReturn([directory.path, directory.path]);
7373
when<dynamic>(() => argResults['project-name']).thenReturn(null);
7474
expect(command.run, throwsA(isA<UsageException>()));
7575
});
7676

77-
test('generates a project successfully.', () async {
77+
test('generates a project successfully (defaults)', () async {
78+
final generatorHooks = _MockGeneratorHooks();
79+
final directory = Directory.current.absolute.path;
80+
when(
81+
() => generatorHooks.postGen(
82+
vars: any(named: 'vars'),
83+
workingDirectory: any(named: 'workingDirectory'),
84+
),
85+
).thenAnswer((_) async {});
86+
when(() => argResults.rest).thenReturn([directory]);
87+
when(
88+
() => generator.generate(any(), vars: any(named: 'vars')),
89+
).thenAnswer((_) async => []);
90+
when(() => generator.hooks).thenReturn(generatorHooks);
91+
final exitCode = await command.run();
92+
expect(exitCode, equals(ExitCode.success.code));
93+
verify(
94+
() => generator.generate(
95+
any(),
96+
vars: {'name': 'dart_frog_cli', 'output_directory': directory},
97+
),
98+
).called(1);
99+
});
100+
101+
test('generates a project successfully (custom)', () async {
78102
final generatorHooks = _MockGeneratorHooks();
103+
final directory = Directory.current.absolute.path;
104+
const projectName = 'example';
79105
when(
80106
() => generatorHooks.postGen(
81107
vars: any(named: 'vars'),
82108
workingDirectory: any(named: 'workingDirectory'),
83109
),
84110
).thenAnswer((_) async {});
85-
when<dynamic>(() => argResults['project-name']).thenReturn('example');
86-
when(() => argResults.rest).thenReturn(['.']);
111+
when<dynamic>(() => argResults['project-name']).thenReturn(projectName);
112+
when(() => argResults.rest).thenReturn([directory]);
87113
when(
88114
() => generator.generate(any(), vars: any(named: 'vars')),
89115
).thenAnswer((_) async => []);
90116
when(() => generator.hooks).thenReturn(generatorHooks);
91117
final exitCode = await command.run();
92118
expect(exitCode, equals(ExitCode.success.code));
119+
verify(
120+
() => generator.generate(
121+
any(),
122+
vars: {'name': projectName, 'output_directory': directory},
123+
),
124+
).called(1);
93125
});
94126
});
95127
}

0 commit comments

Comments
 (0)