chore: create new cli
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
@Tags(['version-verify'])
|
||||
library;
|
||||
|
||||
import 'package:build_verify/build_verify.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('ensure_build', expectBuildClean);
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:shorebird_cli/src/command_runner.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class _MockLogger extends Mock implements Logger {}
|
||||
|
||||
void main() {
|
||||
group('publish', () {
|
||||
late Logger logger;
|
||||
late ShorebirdCliCommandRunner commandRunner;
|
||||
|
||||
setUp(() {
|
||||
logger = _MockLogger();
|
||||
commandRunner = ShorebirdCliCommandRunner(logger: logger);
|
||||
});
|
||||
|
||||
test('outputs coming soon...', () async {
|
||||
final exitCode = await commandRunner.run(['publish']);
|
||||
|
||||
expect(exitCode, ExitCode.success.code);
|
||||
|
||||
verify(() => logger.info('Coming soon...')).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
import 'dart:io';
|
||||
|
||||
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/commands/commands.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 {}
|
||||
|
||||
void main() {
|
||||
const latestVersion = '0.0.0';
|
||||
|
||||
group('update', () {
|
||||
late PubUpdater pubUpdater;
|
||||
late Logger logger;
|
||||
late ProcessResult processResult;
|
||||
late ShorebirdCliCommandRunner commandRunner;
|
||||
|
||||
setUp(() {
|
||||
final progress = _MockProgress();
|
||||
final progressLogs = <String>[];
|
||||
pubUpdater = _MockPubUpdater();
|
||||
logger = _MockLogger();
|
||||
processResult = _MockProcessResult();
|
||||
commandRunner = ShorebirdCliCommandRunner(
|
||||
logger: logger,
|
||||
pubUpdater: pubUpdater,
|
||||
);
|
||||
|
||||
when(
|
||||
() => pubUpdater.getLatestVersion(any()),
|
||||
).thenAnswer((_) async => packageVersion);
|
||||
when(
|
||||
() => pubUpdater.update(
|
||||
packageName: packageName,
|
||||
versionConstraint: latestVersion,
|
||||
),
|
||||
).thenAnswer((_) async => processResult);
|
||||
when(
|
||||
() => pubUpdater.isUpToDate(
|
||||
packageName: any(named: 'packageName'),
|
||||
currentVersion: any(named: 'currentVersion'),
|
||||
),
|
||||
).thenAnswer((_) async => true);
|
||||
when(() => progress.complete(any())).thenAnswer((_) {
|
||||
final message = _.positionalArguments.elementAt(0) as String?;
|
||||
if (message != null) progressLogs.add(message);
|
||||
});
|
||||
when(() => logger.progress(any())).thenReturn(progress);
|
||||
when(() => processResult.exitCode).thenReturn(ExitCode.success.code);
|
||||
});
|
||||
|
||||
test('can be instantiated without a pub updater', () {
|
||||
final command = UpdateCommand(logger: logger);
|
||||
expect(command, isNotNull);
|
||||
});
|
||||
|
||||
test(
|
||||
'handles pub latest version query errors',
|
||||
() async {
|
||||
when(
|
||||
() => pubUpdater.getLatestVersion(any()),
|
||||
).thenThrow(Exception('oops'));
|
||||
final result = await commandRunner.run(['update']);
|
||||
expect(result, equals(ExitCode.software.code));
|
||||
verify(() => logger.progress('Checking for updates')).called(1);
|
||||
verify(() => logger.err('Exception: oops'));
|
||||
verifyNever(
|
||||
() => pubUpdater.update(
|
||||
packageName: any(named: 'packageName'),
|
||||
versionConstraint: any(named: 'versionConstraint'),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'handles pub update errors',
|
||||
() async {
|
||||
when(
|
||||
() => pubUpdater.getLatestVersion(any()),
|
||||
).thenAnswer((_) async => latestVersion);
|
||||
when(
|
||||
() => pubUpdater.update(
|
||||
packageName: any(named: 'packageName'),
|
||||
versionConstraint: any(named: 'versionConstraint'),
|
||||
),
|
||||
).thenThrow(Exception('oops'));
|
||||
final result = await commandRunner.run(['update']);
|
||||
expect(result, equals(ExitCode.software.code));
|
||||
verify(() => logger.progress('Checking for updates')).called(1);
|
||||
verify(() => logger.err('Exception: oops'));
|
||||
verify(
|
||||
() => pubUpdater.update(
|
||||
packageName: any(named: 'packageName'),
|
||||
versionConstraint: any(named: 'versionConstraint'),
|
||||
),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test('handles pub update process errors', () async {
|
||||
const error = 'Oh no! Installing this is not possible right now!';
|
||||
|
||||
when(() => processResult.exitCode).thenReturn(1);
|
||||
when<dynamic>(() => processResult.stderr).thenReturn(error);
|
||||
when(
|
||||
() => pubUpdater.getLatestVersion(any()),
|
||||
).thenAnswer((_) async => latestVersion);
|
||||
|
||||
when(
|
||||
() => pubUpdater.update(
|
||||
packageName: any(named: 'packageName'),
|
||||
versionConstraint: any(named: 'versionConstraint'),
|
||||
),
|
||||
).thenAnswer((_) async => processResult);
|
||||
|
||||
final result = await commandRunner.run(['update']);
|
||||
|
||||
expect(result, equals(ExitCode.software.code));
|
||||
verify(() => logger.progress('Checking for updates')).called(1);
|
||||
verify(() => logger.err('Error updating CLI: $error'));
|
||||
verify(
|
||||
() => pubUpdater.update(
|
||||
packageName: any(named: 'packageName'),
|
||||
versionConstraint: any(named: 'versionConstraint'),
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test(
|
||||
'updates when newer version exists',
|
||||
() async {
|
||||
when(
|
||||
() => pubUpdater.getLatestVersion(any()),
|
||||
).thenAnswer((_) async => latestVersion);
|
||||
when(
|
||||
() => pubUpdater.update(
|
||||
packageName: any(named: 'packageName'),
|
||||
versionConstraint: any(named: 'versionConstraint'),
|
||||
),
|
||||
).thenAnswer((_) async => processResult);
|
||||
when(() => logger.progress(any())).thenReturn(_MockProgress());
|
||||
final result = await commandRunner.run(['update']);
|
||||
expect(result, equals(ExitCode.success.code));
|
||||
verify(() => logger.progress('Checking for updates')).called(1);
|
||||
verify(() => logger.progress('Updating to $latestVersion')).called(1);
|
||||
verify(
|
||||
() => pubUpdater.update(
|
||||
packageName: packageName,
|
||||
versionConstraint: latestVersion,
|
||||
),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'does not update when already on latest version',
|
||||
() async {
|
||||
when(
|
||||
() => pubUpdater.getLatestVersion(any()),
|
||||
).thenAnswer((_) async => packageVersion);
|
||||
when(() => logger.progress(any())).thenReturn(_MockProgress());
|
||||
final result = await commandRunner.run(['update']);
|
||||
expect(result, equals(ExitCode.success.code));
|
||||
verify(
|
||||
() => logger.info('CLI is already at the latest version.'),
|
||||
).called(1);
|
||||
verifyNever(() => logger.progress('Updating to $latestVersion'));
|
||||
verifyNever(
|
||||
() => pubUpdater.update(
|
||||
packageName: any(named: 'packageName'),
|
||||
versionConstraint: any(named: 'versionConstraint'),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user