Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions lib/src/command_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:args/command_runner.dart';
import 'package:io/ansi.dart';
import 'package:io/io.dart';
import 'package:mason/mason.dart';
import 'package:pub_updater/pub_updater.dart';
import 'package:usage/usage_io.dart';
import 'package:very_good_cli/src/commands/commands.dart';
import 'package:very_good_cli/src/version.dart';
Expand All @@ -13,15 +14,20 @@ const _gaTrackingId = 'UA-117465969-4';
// The Google Analytics Application Name.
const _gaAppName = 'very-good-cli';

/// The package name.
const packageName = 'very_good_cli';

/// {@template very_good_command_runner}
/// A [CommandRunner] for the Very Good CLI.
/// {@endtemplate}
class VeryGoodCommandRunner extends CommandRunner<int> {
/// {@macro very_good_command_runner}
VeryGoodCommandRunner({Analytics? analytics, Logger? logger})
VeryGoodCommandRunner(
{Analytics? analytics, Logger? logger, PubUpdater? pubUpdater})
: _logger = logger ?? Logger(),
_analytics =
analytics ?? AnalyticsIO(_gaTrackingId, _gaAppName, packageVersion),
_pubUpdater = pubUpdater ?? PubUpdater(),
super('very_good', '🦄 A Very Good Command Line Interface') {
argParser
..addFlag(
Expand All @@ -46,6 +52,10 @@ class VeryGoodCommandRunner extends CommandRunner<int> {

final Logger _logger;
final Analytics _analytics;
final PubUpdater _pubUpdater;

/// The current package version
String get version => packageVersion;

@override
Future<int> run(Iterable<String> args) async {
Expand Down Expand Up @@ -85,8 +95,31 @@ class VeryGoodCommandRunner extends CommandRunner<int> {

@override
Future<int?> runCommand(ArgResults topLevelResults) async {
final isUpToDate = await _pubUpdater.isUpToDate(
packageName: packageName,
currentVersion: packageVersion,
);

if (!isUpToDate) {
final response = _logger.prompt(lightGray.wrap('''
A newer version of VeryGoodCLI is available.
Would you like to update?
[y/n]'''));

final normalizedResponse = response.toLowerCase().trim();
final shouldUpdate =
normalizedResponse == 'y' || normalizedResponse == 'yes';

if (shouldUpdate) {
final doneUpdating =
_logger.progress('Updating to the latest version...');
await _pubUpdater.update(packageName: packageName);
doneUpdating('Updating to the latest version!');
}
}

if (topLevelResults['version'] == true) {
_logger.info('very_good version: $packageVersion');
_logger.info('very_good version: $version');
return ExitCode.success.code;
}
if (topLevelResults['analytics'] != null) {
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies:
mason: ^0.0.1-dev.46
meta: ^1.3.0
path: ^1.8.0
pub_updater: ^0.1.0
universal_io: ^2.0.4
usage: ^4.0.2
very_good_analysis: ^2.3.0
Expand Down
58 changes: 58 additions & 0 deletions test/src/command_runner_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
import 'dart:async';

import 'package:args/command_runner.dart';
import 'package:io/ansi.dart';
import 'package:io/io.dart';
import 'package:mason/mason.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pub_updater/pub_updater.dart';
import 'package:test/test.dart';
import 'package:universal_io/io.dart';
import 'package:usage/usage_io.dart';
import 'package:very_good_cli/src/command_runner.dart';
import 'package:very_good_cli/src/version.dart';
Expand All @@ -14,6 +17,10 @@ class MockAnalytics extends Mock implements Analytics {}

class MockLogger extends Mock implements Logger {}

class MockPubUpdater extends Mock implements PubUpdater {}

class FakeProcessResult extends Fake implements ProcessResult {}

const expectedUsage = [
'🦄 A Very Good Command Line Interface\n'
'\n'
Expand All @@ -34,10 +41,14 @@ const expectedUsage = [
'Run "very_good help <command>" for more information about a command.'
];

const responseBody =
'{"name": "very_good_cli", "versions": ["0.4.0", "0.3.3"]}';

void main() {
group('VeryGoodCommandRunner', () {
late List<String> printLogs;
late Analytics analytics;
late PubUpdater pubUpdater;
late Logger logger;
late VeryGoodCommandRunner commandRunner;

Expand All @@ -54,13 +65,30 @@ void main() {
printLogs = [];

analytics = MockAnalytics();
pubUpdater = MockPubUpdater();

when(() => analytics.firstRun).thenReturn(false);
when(() => analytics.enabled).thenReturn(false);

when(
() => pubUpdater.isUpToDate(
packageName: any(named: 'packageName'),
currentVersion: any(named: 'currentVersion'),
),
).thenAnswer((_) => Future.value(true));

when(
() => pubUpdater.update(
packageName: any(named: 'packageName'),
),
).thenAnswer((_) => Future.value(FakeProcessResult()));

logger = MockLogger();

commandRunner = VeryGoodCommandRunner(
analytics: analytics,
logger: logger,
pubUpdater: pubUpdater,
);
});

Expand All @@ -71,6 +99,36 @@ void main() {
});

group('run', () {
test('prompts for update when newer version exists', () async {
when(() => pubUpdater.isUpToDate(
packageName: any(named: 'packageName'),
currentVersion: any(named: 'currentVersion'),
)).thenAnswer((_) => Future.value(false));

when(() => logger.prompt(any())).thenReturn('n');

final result = await commandRunner.run(['--version']);
expect(result, equals(ExitCode.success.code));
verify(() => logger.prompt(lightGray.wrap('''
A newer version of VeryGoodCLI is available.
Would you like to update?
[y/n]'''))).called(1);
});

test('updates on "y" response when newer version exists', () async {
when(() => pubUpdater.isUpToDate(
packageName: any(named: 'packageName'),
currentVersion: any(named: 'currentVersion'),
)).thenAnswer((_) => Future.value(false));

when(() => logger.prompt(any())).thenReturn('y');

final result = await commandRunner.run(['--version']);
expect(result, equals(ExitCode.success.code));
verify(() => logger.info('Updating to the latest version...'))
.called(1);
});

test('prompts for analytics collection on first run (y)', () async {
when(() => analytics.firstRun).thenReturn(true);
when(() => logger.prompt(any())).thenReturn('y');
Expand Down
15 changes: 15 additions & 0 deletions test/src/commands/create_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:io/io.dart';
import 'package:mason/mason.dart';
import 'package:mocktail/mocktail.dart';
import 'package:path/path.dart' as p;
import 'package:pub_updater/pub_updater.dart';
import 'package:test/test.dart';
import 'package:universal_io/io.dart';
import 'package:usage/usage_io.dart';
Expand Down Expand Up @@ -45,6 +46,8 @@ class MockAnalytics extends Mock implements Analytics {}

class MockLogger extends Mock implements Logger {}

class MockPubUpdater extends Mock implements PubUpdater {}

class MockMasonGenerator extends Mock implements MasonGenerator {}

class FakeDirectoryGeneratorTarget extends Fake
Expand All @@ -56,6 +59,7 @@ void main() {
late List<String> printLogs;
late Analytics analytics;
late Logger logger;
late PubUpdater pubUpdater;
late VeryGoodCommandRunner commandRunner;

void Function() overridePrint(void Function() fn) {
Expand Down Expand Up @@ -90,9 +94,20 @@ void main() {
if (_ != null) progressLogs.add(_);
},
);

pubUpdater = MockPubUpdater();

when(
() => pubUpdater.isUpToDate(
packageName: any(named: 'packageName'),
currentVersion: any(named: 'currentVersion'),
),
).thenAnswer((_) => Future.value(true));

commandRunner = VeryGoodCommandRunner(
analytics: analytics,
logger: logger,
pubUpdater: pubUpdater,
);
});

Expand Down