179 lines
6.1 KiB
Dart
179 lines
6.1 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:args/command_runner.dart';
|
|
import 'package:cli_completion/cli_completion.dart';
|
|
import 'package:mason_logger/mason_logger.dart';
|
|
import 'package:mocktail/mocktail.dart';
|
|
import 'package:pub_updater/pub_updater.dart';
|
|
import 'package:shorebird_cli/src/command_runner.dart';
|
|
import 'package:shorebird_cli/src/version.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
class _MockLogger extends Mock implements Logger {}
|
|
|
|
class _MockProcessResult extends Mock implements ProcessResult {}
|
|
|
|
class _MockProgress extends Mock implements Progress {}
|
|
|
|
class _MockPubUpdater extends Mock implements PubUpdater {}
|
|
|
|
const latestVersion = '0.0.0';
|
|
|
|
final updatePrompt = '''
|
|
${lightYellow.wrap('Update available!')} ${lightCyan.wrap(packageVersion)} \u2192 ${lightCyan.wrap(latestVersion)}
|
|
Run ${lightCyan.wrap('$executableName update')} to update''';
|
|
|
|
void main() {
|
|
group('ShorebirdCliCommandRunner', () {
|
|
late PubUpdater pubUpdater;
|
|
late Logger logger;
|
|
late ProcessResult processResult;
|
|
late ShorebirdCliCommandRunner commandRunner;
|
|
|
|
setUp(() {
|
|
pubUpdater = _MockPubUpdater();
|
|
|
|
when(
|
|
() => pubUpdater.getLatestVersion(any()),
|
|
).thenAnswer((_) async => packageVersion);
|
|
|
|
logger = _MockLogger();
|
|
|
|
processResult = _MockProcessResult();
|
|
when(() => processResult.exitCode).thenReturn(ExitCode.success.code);
|
|
|
|
commandRunner = ShorebirdCliCommandRunner(
|
|
logger: logger,
|
|
pubUpdater: pubUpdater,
|
|
);
|
|
});
|
|
|
|
test('shows update message when newer version exists', () async {
|
|
when(
|
|
() => pubUpdater.getLatestVersion(any()),
|
|
).thenAnswer((_) async => latestVersion);
|
|
|
|
final result = await commandRunner.run(['--version']);
|
|
expect(result, equals(ExitCode.success.code));
|
|
verify(() => logger.info(updatePrompt)).called(1);
|
|
});
|
|
|
|
test(
|
|
'Does not show update message when the shell calls the '
|
|
'completion command',
|
|
() async {
|
|
when(
|
|
() => pubUpdater.getLatestVersion(any()),
|
|
).thenAnswer((_) async => latestVersion);
|
|
|
|
final result = await commandRunner.run(['completion']);
|
|
expect(result, equals(ExitCode.success.code));
|
|
verifyNever(() => logger.info(updatePrompt));
|
|
},
|
|
);
|
|
|
|
test('does not show update message when using update command', () async {
|
|
when(
|
|
() => pubUpdater.getLatestVersion(any()),
|
|
).thenAnswer((_) async => latestVersion);
|
|
when(
|
|
() => pubUpdater.update(
|
|
packageName: packageName,
|
|
versionConstraint: any(named: 'versionConstraint'),
|
|
),
|
|
).thenAnswer((_) async => processResult);
|
|
when(
|
|
() => pubUpdater.isUpToDate(
|
|
packageName: any(named: 'packageName'),
|
|
currentVersion: any(named: 'currentVersion'),
|
|
),
|
|
).thenAnswer((_) async => true);
|
|
|
|
final progress = _MockProgress();
|
|
final progressLogs = <String>[];
|
|
when(() => progress.complete(any())).thenAnswer((_) {
|
|
final message = _.positionalArguments.elementAt(0) as String?;
|
|
if (message != null) progressLogs.add(message);
|
|
});
|
|
when(() => logger.progress(any())).thenReturn(progress);
|
|
|
|
final result = await commandRunner.run(['update']);
|
|
expect(result, equals(ExitCode.success.code));
|
|
verifyNever(() => logger.info(updatePrompt));
|
|
});
|
|
|
|
test('can be instantiated without an explicit analytics/logger instance',
|
|
() {
|
|
final commandRunner = ShorebirdCliCommandRunner();
|
|
expect(commandRunner, isNotNull);
|
|
expect(commandRunner, isA<CompletionCommandRunner<int>>());
|
|
});
|
|
|
|
test('handles FormatException', () async {
|
|
const exception = FormatException('oops!');
|
|
var isFirstInvocation = true;
|
|
when(() => logger.info(any())).thenAnswer((_) {
|
|
if (isFirstInvocation) {
|
|
isFirstInvocation = false;
|
|
throw exception;
|
|
}
|
|
});
|
|
final result = await commandRunner.run(['--version']);
|
|
expect(result, equals(ExitCode.usage.code));
|
|
verify(() => logger.err(exception.message)).called(1);
|
|
verify(() => logger.info(commandRunner.usage)).called(1);
|
|
});
|
|
|
|
test('handles UsageException', () async {
|
|
final exception = UsageException('oops!', 'exception usage');
|
|
var isFirstInvocation = true;
|
|
when(() => logger.info(any())).thenAnswer((_) {
|
|
if (isFirstInvocation) {
|
|
isFirstInvocation = false;
|
|
throw exception;
|
|
}
|
|
});
|
|
final result = await commandRunner.run(['--version']);
|
|
expect(result, equals(ExitCode.usage.code));
|
|
verify(() => logger.err(exception.message)).called(1);
|
|
verify(() => logger.info('exception usage')).called(1);
|
|
});
|
|
|
|
group('--version', () {
|
|
test('outputs current version', () async {
|
|
final result = await commandRunner.run(['--version']);
|
|
expect(result, equals(ExitCode.success.code));
|
|
verify(() => logger.info(packageVersion)).called(1);
|
|
});
|
|
});
|
|
|
|
group('--verbose', () {
|
|
test('enables verbose logging', () async {
|
|
final result = await commandRunner.run(['--verbose']);
|
|
expect(result, equals(ExitCode.success.code));
|
|
|
|
verify(() => logger.detail('Argument information:')).called(1);
|
|
verify(() => logger.detail(' Top level options:')).called(1);
|
|
verify(() => logger.detail(' - verbose: true')).called(1);
|
|
verifyNever(() => logger.detail(' Command options:'));
|
|
});
|
|
|
|
test('enables verbose logging for sub commands', () async {
|
|
final result = await commandRunner.run([
|
|
'--verbose',
|
|
'sample',
|
|
'--cyan',
|
|
]);
|
|
expect(result, equals(ExitCode.success.code));
|
|
|
|
verify(() => logger.detail('Argument information:')).called(1);
|
|
verify(() => logger.detail(' Top level options:')).called(1);
|
|
verify(() => logger.detail(' - verbose: true')).called(1);
|
|
verify(() => logger.detail(' Command: sample')).called(1);
|
|
verify(() => logger.detail(' Command options:')).called(1);
|
|
verify(() => logger.detail(' - cyan: true')).called(1);
|
|
});
|
|
});
|
|
});
|
|
}
|