Skip to content

Commit

Permalink
feat: add wear os template to the create command (#694)
Browse files Browse the repository at this point in the history
* feat: add wear os template to the create command

* wear template

* default teamplte name to first template
  • Loading branch information
renancaraujo committed Mar 28, 2023
1 parent d22e66b commit 40f71c1
Show file tree
Hide file tree
Showing 10 changed files with 866 additions and 81 deletions.
9 changes: 5 additions & 4 deletions lib/src/commands/create/commands/create_subcommand.dart
Original file line number Diff line number Diff line change
Expand Up @@ -287,15 +287,16 @@ mixin OrgName on CreateSubCommand {

/// Mixin for [CreateSubCommand] subclasses that receives multiple templates.
///
/// Subcommands that mix with this mixin should override [templates] and
/// [defaultTemplateName];
/// Subcommands that mix with this mixin should override [templates].
///
/// Takes care of parsing the desired template from [argResults] and
/// validating the org name.
mixin MultiTemplates on CreateSubCommand {
/// Gets the desired template to be created during a command run when the
/// template argument is not provided.`
String get defaultTemplateName;
/// template argument is not provided.
///
/// Defaults to the first template in [templates].
String get defaultTemplateName => templates.first.name;

/// Gets all the templates to be created during a command run.
List<Template> get templates;
Expand Down
11 changes: 7 additions & 4 deletions lib/src/commands/create/commands/flutter_app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:very_good_cli/src/commands/create/templates/templates.dart';
/// {@template very_good_create_flutter_app_command}
/// A [CreateSubCommand] for creating Flutter apps.
/// {@endtemplate}
class CreateFlutterApp extends CreateSubCommand with OrgName {
class CreateFlutterApp extends CreateSubCommand with OrgName, MultiTemplates {
/// {@macro very_good_create_flutter_app_command}
CreateFlutterApp({
required super.analytics,
Expand All @@ -25,9 +25,6 @@ class CreateFlutterApp extends CreateSubCommand with OrgName {
@override
String get description => 'Generate a Very Good Flutter application.';

@override
Template get template => VeryGoodCoreTemplate();

@override
Map<String, dynamic> getTemplateVars() {
final vars = super.getTemplateVars();
Expand All @@ -39,4 +36,10 @@ class CreateFlutterApp extends CreateSubCommand with OrgName {

return vars;
}

@override
final List<Template> templates = [
VeryGoodCoreTemplate(),
VeryGoodWearAppTemplate(),
];
}
1 change: 1 addition & 0 deletions lib/src/commands/create/templates/templates.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export 'very_good_docs_site/very_good_docs_site.dart';
export 'very_good_flame_game/very_good_flame_game.dart';
export 'very_good_flutter_package/very_good_flutter_package.dart';
export 'very_good_flutter_plugin/very_good_flutter_plugin.dart';
export 'very_good_wear_app/very_good_wear_app.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'very_good_wear_app_bundle.dart';
export 'very_good_wear_app_template.dart';

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'dart:io';

import 'package:mason/mason.dart';
import 'package:very_good_cli/src/commands/create/templates/templates.dart';
import 'package:very_good_cli/src/logger_extension.dart';

/// {@template wear_app_template}
/// A template for Wear OS apps.
/// {@endtemplate}
class VeryGoodWearAppTemplate extends Template {
/// {@macro wear_app_template}
VeryGoodWearAppTemplate()
: super(
name: 'wear',
bundle: veryGoodWearAppBundle,
help: 'Generate a Very Good Flutter Wear OS application.',
);

@override
Future<void> onGenerateComplete(Logger logger, Directory outputDir) async {
await installFlutterPackages(logger, outputDir);
await applyDartFixes(logger, outputDir);
_logSummary(logger);
}

void _logSummary(Logger logger) {
logger
..info('\n')
..created('Created a Very Good Wear OS app! ⌚️🦄')
..info('\n');
}
}
59 changes: 59 additions & 0 deletions test/helpers/test_multi_template_commands.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'dart:io';

import 'package:args/args.dart';
import 'package:mason/mason.dart';
import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';
import 'package:very_good_cli/src/commands/commands.dart';
import 'package:very_good_cli/src/logger_extension.dart';

class _MockArgResults extends Mock implements ArgResults {}

Future<void> testMultiTemplateCommand({
required MultiTemplates multiTemplatesCommand,
required String templateName,
required Map<String, dynamic> mockArgs,
required MasonGenerator generator,
required Logger logger,
required GeneratorHooks hooks,

// expected
required Map<String, dynamic> expectedVars,
required String expectedLogSummary,
}) async {
final tempDir = Directory.systemTemp.createTempSync();
addTearDown(() => tempDir.deleteSync(recursive: true));
final argResults = _MockArgResults();
final command = multiTemplatesCommand..argResultOverrides = argResults;

when(() => argResults['template'] as String?).thenReturn(templateName);
when(() => argResults['output-directory'] as String?)
.thenReturn(tempDir.path);

for (final entry in mockArgs.entries) {
when(() => argResults[entry.key]).thenReturn(entry.value);
}

when(() => argResults.rest).thenReturn(['my_app']);

final result = await command.run();

expect(command.template.name, templateName);
expect(result, equals(ExitCode.success.code));

verify(() => logger.progress('Bootstrapping')).called(1);
verify(
() => hooks.preGen(
vars: expectedVars,
onVarsChanged: any(named: 'onVarsChanged'),
),
);
verify(
() => generator.generate(
any(),
vars: expectedVars,
logger: logger,
),
).called(1);
verify(() => logger.created(expectedLogSummary)).called(1);
}
122 changes: 55 additions & 67 deletions test/src/commands/create/commands/flutter_app_test.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import 'dart:io';

import 'package:args/args.dart';
import 'package:mason/mason.dart';
import 'package:mocktail/mocktail.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'package:usage/usage.dart';
import 'package:very_good_cli/src/commands/commands.dart';
import 'package:very_good_cli/src/commands/create/commands/flutter_app.dart';

import '../../../../helpers/helpers.dart';
import '../../../../helpers/test_multi_template_commands.dart';

class MockAnalytics extends Mock implements Analytics {}

Expand All @@ -18,8 +19,6 @@ class MockMasonGenerator extends Mock implements MasonGenerator {}

class MockGeneratorHooks extends Mock implements GeneratorHooks {}

class MockArgResults extends Mock implements ArgResults {}

class FakeLogger extends Fake implements Logger {}

class FakeDirectoryGeneratorTarget extends Fake
Expand All @@ -30,13 +29,18 @@ final expectedUsage = [
Generate a Very Good Flutter application.
Usage: very_good create flutter_app <project-name> [arguments]
-h, --help Print this usage information.
-o, --output-directory The desired output directory when creating a new project.
--description The description for this new project.
(defaults to "A Very Good Project created by Very Good CLI.")
--org-name The organization for this new project.
(defaults to "com.example.verygoodcore")
--application-id The bundle identifier on iOS or application id on Android. (defaults to <org-name>.<project-name>)
-h, --help Print this usage information.
-o, --output-directory The desired output directory when creating a new project.
--description The description for this new project.
(defaults to "A Very Good Project created by Very Good CLI.")
-t, --template The template used to generate this new project.
[core] (default) Generate a Very Good Flutter application.
[wear] Generate a Very Good Flutter Wear OS application.
--org-name The organization for this new project.
(defaults to "com.example.verygoodcore")
--application-id The bundle identifier on iOS or application id on Android. (defaults to <org-name>.<project-name>)
Run "very_good help" to see global options.''',
];
Expand All @@ -52,6 +56,8 @@ void main() {
late Analytics analytics;
late Logger logger;

final generatedFiles = List.filled(10, const GeneratedFile.created(path: ''));

setUpAll(() {
registerFallbackValue(FakeDirectoryGeneratorTarget());
registerFallbackValue(FakeLogger());
Expand Down Expand Up @@ -116,9 +122,6 @@ void main() {
);

group('running the command', () {
final generatedFiles =
List.filled(10, const GeneratedFile.created(path: ''));

late GeneratorHooks hooks;
late MasonGenerator generator;

Expand All @@ -134,16 +137,6 @@ void main() {
),
).thenAnswer((_) async {});

when(
() => generator.generate(
any(),
vars: any(named: 'vars'),
logger: any(named: 'logger'),
),
).thenAnswer((_) async {
return generatedFiles;
});

when(() => generator.id).thenReturn('generator_id');
when(() => generator.description).thenReturn('generator description');
when(() => generator.hooks).thenReturn(hooks);
Expand All @@ -170,59 +163,54 @@ void main() {
});
});

test('create core app', () async {
final tempDir = Directory.systemTemp.createTempSync();
addTearDown(() => tempDir.deleteSync(recursive: true));
final argResults = MockArgResults();
final command = CreateFlutterApp(
analytics: analytics,
logger: logger,
generatorFromBundle: (_) async => throw Exception('oops'),
generatorFromBrick: (_) async => generator,
)..argResultOverrides = argResults;
when(() => argResults['output-directory'] as String?)
.thenReturn(tempDir.path);
when(() => argResults.rest).thenReturn(['my_app']);
when(() => argResults['application-id'] as String?).thenReturn(
'xyz.app.my_app',
);

final result = await command.run();

expect(command.template.name, 'core');
expect(result, equals(ExitCode.success.code));

verify(() => logger.progress('Bootstrapping')).called(1);
verify(
() => hooks.preGen(
vars: <String, dynamic>{
group('templates', () {
test('core', () async {
await testMultiTemplateCommand(
multiTemplatesCommand: CreateFlutterApp(
analytics: analytics,
logger: logger,
generatorFromBundle: (_) async => throw Exception('oops'),
generatorFromBrick: (_) async => generator,
),
logger: logger,
hooks: hooks,
generator: generator,
templateName: 'core',
mockArgs: {'application-id': 'xyz.app.my_app'},
expectedVars: {
'project_name': 'my_app',
'description': '',
'org_name': 'com.example.verygoodcore',
'application_id': 'xyz.app.my_app',
},
onVarsChanged: any(named: 'onVarsChanged'),
),
);
verify(
() => generator.generate(
any(),
vars: <String, dynamic>{
expectedLogSummary: 'Created a Very Good App! 🦄',
);
});

test('wear', () async {
await testMultiTemplateCommand(
multiTemplatesCommand: CreateFlutterApp(
analytics: analytics,
logger: logger,
generatorFromBundle: (_) async => throw Exception('oops'),
generatorFromBrick: (_) async => generator,
),
logger: logger,
hooks: hooks,
generator: generator,
templateName: 'wear',
mockArgs: {
'application-id': 'xyz.app.my_wear_app',
},
expectedVars: {
'project_name': 'my_app',
'description': '',
'org_name': 'com.example.verygoodcore',
'application_id': 'xyz.app.my_app',
'application_id': 'xyz.app.my_wear_app',
},
logger: logger,
),
).called(1);
expect(
progressLogs,
equals(['Generated ${generatedFiles.length} file(s)']),
);
verify(
() => logger.info('Created a Very Good App! 🦄'),
).called(1);
expectedLogSummary: 'Created a Very Good Wear OS app! ⌚️🦄',
);
});
});
});
});
Expand Down
6 changes: 0 additions & 6 deletions test/src/commands/create/create_subcommand_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ class _TestCreateSubCommandWithPublishable extends _TestCreateSubCommand
class _TestCreateSubCommandMultiTemplate extends CreateSubCommand
with MultiTemplates {
_TestCreateSubCommandMultiTemplate({
required this.defaultTemplateName,
required this.templates,
required super.analytics,
required super.logger,
Expand All @@ -88,9 +87,6 @@ class _TestCreateSubCommandMultiTemplate extends CreateSubCommand
@override
final String description = 'Create command';

@override
final String defaultTemplateName;

@override
final List<Template> templates;
}
Expand Down Expand Up @@ -932,7 +928,6 @@ Run "runner help" to see global options.''';
group('can be instantiated', () {
test('with default options', () {
final command = _TestCreateSubCommandMultiTemplate(
defaultTemplateName: 'template1',
templates: templates,
analytics: analytics,
logger: logger,
Expand Down Expand Up @@ -1014,7 +1009,6 @@ Run "runner help" to see global options.''';
});

final command = _TestCreateSubCommandMultiTemplate(
defaultTemplateName: 'template1',
templates: templates,
analytics: analytics,
logger: logger,
Expand Down
1 change: 1 addition & 0 deletions tool/generate_bundles.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ bricks=(
very_good_flutter_plugin
very_good_flame_game
very_good_docs_site
very_good_wear_app
)

for brick in "${bricks[@]}"
Expand Down

0 comments on commit 40f71c1

Please sign in to comment.