Skip to content

Commit

Permalink
feat(create): generate project using very_good_core template (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
felangel authored Feb 1, 2021
1 parent a6daf01 commit 11b8181
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 12 deletions.
1 change: 1 addition & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ include: package:very_good_analysis/analysis_options.yaml
analyzer:
exclude:
- "**/version.dart"
- "bricks/**"
46 changes: 38 additions & 8 deletions lib/src/commands/create.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import 'dart:io';

import 'package:args/args.dart';
import 'package:args/command_runner.dart';
import 'package:io/ansi.dart';
import 'package:io/io.dart';
import 'package:mason/mason.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;

const _veryGoodCoreGitPath = GitPath(
'git@github.com:VeryGoodOpenSource/very_good_cli.git',
path: 'bricks/very_good_core',
);

// A valid Dart identifier that can be used for a package, i.e. no
// capital letters.
// https://dart.dev/guides/language/language-tour#important-concepts
Expand All @@ -15,7 +23,11 @@ final RegExp _identifierRegExp = RegExp('[a-z_][a-z0-9_]*');
/// {@endtemplate}
class CreateCommand extends Command<int> {
/// {@macro create_command}
CreateCommand({Logger logger}) : _logger = logger ?? Logger() {
CreateCommand({
Logger logger,
Future<MasonGenerator> Function(GitPath) generate,
}) : _logger = logger ?? Logger(),
_generate = generate ?? MasonGenerator.fromGitPath {
argParser.addOption(
'project-name',
help: 'The project name for this new Flutter project. '
Expand All @@ -25,6 +37,7 @@ class CreateCommand extends Command<int> {
}

final Logger _logger;
final Future<MasonGenerator> Function(GitPath) _generate;

@override
final String description =
Expand All @@ -33,15 +46,32 @@ class CreateCommand extends Command<int> {
@override
final String name = 'create';

/// [ArgResults] which can be overridden for testing.
@visibleForTesting
ArgResults argResultOverrides;

ArgResults get _argResults => argResultOverrides ?? argResults;

@override
Future<int> run() async {
// ignore: unused_local_variable
final outputDirectory = _outputDirectory;

// ignore: unused_local_variable
final projectName = _projectName;
final generateDone = _logger.progress('Bootstrapping');
final generator = await _generate(_veryGoodCoreGitPath);

_logger.alert('Created a Very Good App! 🦄');
final target = DirectoryGeneratorTarget(outputDirectory, _logger);
final fileCount = await generator.generate(
target,
vars: {'project_name': projectName},
);
generateDone('Bootstrapping complete');
_logger
..info(
'${lightGreen.wrap('✓')} '
'Generated $fileCount file(s):',
)
..flush(_logger.success)
..alert('Created a Very Good App! 🦄');
return ExitCode.success.code;
}

Expand All @@ -50,8 +80,8 @@ class CreateCommand extends Command<int> {
/// Uses the current directory path name
/// if the `--project-name` option is not explicitly specified.
String get _projectName {
final projectName =
argResults['project-name'] ?? path.basename(_outputDirectory.path);
final projectName = _argResults['project-name'] ??
path.basename(path.normalize(_outputDirectory.absolute.path));
_validateProjectName(projectName);
return projectName;
}
Expand All @@ -73,7 +103,7 @@ class CreateCommand extends Command<int> {
}

Directory get _outputDirectory {
final rest = argResults.rest;
final rest = _argResults.rest;
_validateOutputDirectoryArg(rest);
return Directory(rest.first);
}
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ dependencies:
args: ^1.6.0
io: ^0.3.4
mason: ^0.0.1-dev.22
meta: ^1.2.4
path: ^1.7.0

dev_dependencies:
Expand Down
40 changes: 36 additions & 4 deletions test/src/commands/create_test.dart
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
import 'package:args/args.dart';
import 'package:io/ansi.dart';
import 'package:io/io.dart';
import 'package:mason/mason.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
import 'package:very_good_cli/src/command_runner.dart';
import 'package:very_good_cli/src/commands/create.dart';

class MockArgResults extends Mock implements ArgResults {}

class MockLogger extends Mock implements Logger {}

class MockMasonGenerator extends Mock implements MasonGenerator {}

void main() {
group('Create', () {
Logger logger;
VeryGoodCommandRunner commandRunner;

setUp(() {
logger = MockLogger();
when(logger.progress(any)).thenReturn((_) {});
commandRunner = VeryGoodCommandRunner(logger: logger);
});

test('can be instantiated without an explicit Logger instance', () {
test('can be instantiated without any explicit dependencies', () {
final command = CreateCommand();
expect(command, isNotNull);
});
Expand Down Expand Up @@ -59,11 +66,36 @@ void main() {
});

test('completes successfully with correct output', () async {
final result = await commandRunner.run(
['create', '.', '--project-name', 'my_app'],
);
final argResults = MockArgResults();
final generator = MockMasonGenerator();
final command = CreateCommand(
logger: logger,
generate: (_) async => generator,
)..argResultOverrides = argResults;
when(argResults['project-name']).thenReturn('my_app');
when(argResults.rest).thenReturn(['.tmp']);
when(generator.generate(any, vars: anyNamed('vars')))
.thenAnswer((_) async => 62);
final result = await command.run();
expect(result, equals(ExitCode.success.code));
verify(logger.progress('Bootstrapping')).called(1);
verify(logger.info(
'${lightGreen.wrap('✓')} '
'Generated 62 file(s):',
));
verify(logger.alert('Created a Very Good App! 🦄')).called(1);
verify(
generator.generate(
argThat(
isA<DirectoryGeneratorTarget>().having(
(g) => g.dir.path,
'dir',
'.tmp',
),
),
vars: {'project_name': 'my_app'},
),
).called(1);
});
});
}

0 comments on commit 11b8181

Please sign in to comment.