Skip to content

Commit 40f71c1

Browse files
authored
feat: add wear os template to the create command (#694)
* feat: add wear os template to the create command * wear template * default teamplte name to first template
1 parent d22e66b commit 40f71c1

File tree

10 files changed

+866
-81
lines changed

10 files changed

+866
-81
lines changed

lib/src/commands/create/commands/create_subcommand.dart

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,15 +287,16 @@ mixin OrgName on CreateSubCommand {
287287

288288
/// Mixin for [CreateSubCommand] subclasses that receives multiple templates.
289289
///
290-
/// Subcommands that mix with this mixin should override [templates] and
291-
/// [defaultTemplateName];
290+
/// Subcommands that mix with this mixin should override [templates].
292291
///
293292
/// Takes care of parsing the desired template from [argResults] and
294293
/// validating the org name.
295294
mixin MultiTemplates on CreateSubCommand {
296295
/// Gets the desired template to be created during a command run when the
297-
/// template argument is not provided.`
298-
String get defaultTemplateName;
296+
/// template argument is not provided.
297+
///
298+
/// Defaults to the first template in [templates].
299+
String get defaultTemplateName => templates.first.name;
299300

300301
/// Gets all the templates to be created during a command run.
301302
List<Template> get templates;

lib/src/commands/create/commands/flutter_app.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import 'package:very_good_cli/src/commands/create/templates/templates.dart';
44
/// {@template very_good_create_flutter_app_command}
55
/// A [CreateSubCommand] for creating Flutter apps.
66
/// {@endtemplate}
7-
class CreateFlutterApp extends CreateSubCommand with OrgName {
7+
class CreateFlutterApp extends CreateSubCommand with OrgName, MultiTemplates {
88
/// {@macro very_good_create_flutter_app_command}
99
CreateFlutterApp({
1010
required super.analytics,
@@ -25,9 +25,6 @@ class CreateFlutterApp extends CreateSubCommand with OrgName {
2525
@override
2626
String get description => 'Generate a Very Good Flutter application.';
2727

28-
@override
29-
Template get template => VeryGoodCoreTemplate();
30-
3128
@override
3229
Map<String, dynamic> getTemplateVars() {
3330
final vars = super.getTemplateVars();
@@ -39,4 +36,10 @@ class CreateFlutterApp extends CreateSubCommand with OrgName {
3936

4037
return vars;
4138
}
39+
40+
@override
41+
final List<Template> templates = [
42+
VeryGoodCoreTemplate(),
43+
VeryGoodWearAppTemplate(),
44+
];
4245
}

lib/src/commands/create/templates/templates.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export 'very_good_docs_site/very_good_docs_site.dart';
77
export 'very_good_flame_game/very_good_flame_game.dart';
88
export 'very_good_flutter_package/very_good_flutter_package.dart';
99
export 'very_good_flutter_plugin/very_good_flutter_plugin.dart';
10+
export 'very_good_wear_app/very_good_wear_app.dart';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export 'very_good_wear_app_bundle.dart';
2+
export 'very_good_wear_app_template.dart';

lib/src/commands/create/templates/very_good_wear_app/very_good_wear_app_bundle.dart

Lines changed: 704 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import 'dart:io';
2+
3+
import 'package:mason/mason.dart';
4+
import 'package:very_good_cli/src/commands/create/templates/templates.dart';
5+
import 'package:very_good_cli/src/logger_extension.dart';
6+
7+
/// {@template wear_app_template}
8+
/// A template for Wear OS apps.
9+
/// {@endtemplate}
10+
class VeryGoodWearAppTemplate extends Template {
11+
/// {@macro wear_app_template}
12+
VeryGoodWearAppTemplate()
13+
: super(
14+
name: 'wear',
15+
bundle: veryGoodWearAppBundle,
16+
help: 'Generate a Very Good Flutter Wear OS application.',
17+
);
18+
19+
@override
20+
Future<void> onGenerateComplete(Logger logger, Directory outputDir) async {
21+
await installFlutterPackages(logger, outputDir);
22+
await applyDartFixes(logger, outputDir);
23+
_logSummary(logger);
24+
}
25+
26+
void _logSummary(Logger logger) {
27+
logger
28+
..info('\n')
29+
..created('Created a Very Good Wear OS app! ⌚️🦄')
30+
..info('\n');
31+
}
32+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import 'dart:io';
2+
3+
import 'package:args/args.dart';
4+
import 'package:mason/mason.dart';
5+
import 'package:mocktail/mocktail.dart';
6+
import 'package:test/test.dart';
7+
import 'package:very_good_cli/src/commands/commands.dart';
8+
import 'package:very_good_cli/src/logger_extension.dart';
9+
10+
class _MockArgResults extends Mock implements ArgResults {}
11+
12+
Future<void> testMultiTemplateCommand({
13+
required MultiTemplates multiTemplatesCommand,
14+
required String templateName,
15+
required Map<String, dynamic> mockArgs,
16+
required MasonGenerator generator,
17+
required Logger logger,
18+
required GeneratorHooks hooks,
19+
20+
// expected
21+
required Map<String, dynamic> expectedVars,
22+
required String expectedLogSummary,
23+
}) async {
24+
final tempDir = Directory.systemTemp.createTempSync();
25+
addTearDown(() => tempDir.deleteSync(recursive: true));
26+
final argResults = _MockArgResults();
27+
final command = multiTemplatesCommand..argResultOverrides = argResults;
28+
29+
when(() => argResults['template'] as String?).thenReturn(templateName);
30+
when(() => argResults['output-directory'] as String?)
31+
.thenReturn(tempDir.path);
32+
33+
for (final entry in mockArgs.entries) {
34+
when(() => argResults[entry.key]).thenReturn(entry.value);
35+
}
36+
37+
when(() => argResults.rest).thenReturn(['my_app']);
38+
39+
final result = await command.run();
40+
41+
expect(command.template.name, templateName);
42+
expect(result, equals(ExitCode.success.code));
43+
44+
verify(() => logger.progress('Bootstrapping')).called(1);
45+
verify(
46+
() => hooks.preGen(
47+
vars: expectedVars,
48+
onVarsChanged: any(named: 'onVarsChanged'),
49+
),
50+
);
51+
verify(
52+
() => generator.generate(
53+
any(),
54+
vars: expectedVars,
55+
logger: logger,
56+
),
57+
).called(1);
58+
verify(() => logger.created(expectedLogSummary)).called(1);
59+
}

test/src/commands/create/commands/flutter_app_test.dart

Lines changed: 55 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import 'dart:io';
22

3-
import 'package:args/args.dart';
43
import 'package:mason/mason.dart';
54
import 'package:mocktail/mocktail.dart';
65
import 'package:path/path.dart' as path;
76
import 'package:test/test.dart';
87
import 'package:usage/usage.dart';
8+
import 'package:very_good_cli/src/commands/commands.dart';
99
import 'package:very_good_cli/src/commands/create/commands/flutter_app.dart';
1010

1111
import '../../../../helpers/helpers.dart';
12+
import '../../../../helpers/test_multi_template_commands.dart';
1213

1314
class MockAnalytics extends Mock implements Analytics {}
1415

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

1920
class MockGeneratorHooks extends Mock implements GeneratorHooks {}
2021

21-
class MockArgResults extends Mock implements ArgResults {}
22-
2322
class FakeLogger extends Fake implements Logger {}
2423

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

59+
final generatedFiles = List.filled(10, const GeneratedFile.created(path: ''));
60+
5561
setUpAll(() {
5662
registerFallbackValue(FakeDirectoryGeneratorTarget());
5763
registerFallbackValue(FakeLogger());
@@ -116,9 +122,6 @@ void main() {
116122
);
117123

118124
group('running the command', () {
119-
final generatedFiles =
120-
List.filled(10, const GeneratedFile.created(path: ''));
121-
122125
late GeneratorHooks hooks;
123126
late MasonGenerator generator;
124127

@@ -134,16 +137,6 @@ void main() {
134137
),
135138
).thenAnswer((_) async {});
136139

137-
when(
138-
() => generator.generate(
139-
any(),
140-
vars: any(named: 'vars'),
141-
logger: any(named: 'logger'),
142-
),
143-
).thenAnswer((_) async {
144-
return generatedFiles;
145-
});
146-
147140
when(() => generator.id).thenReturn('generator_id');
148141
when(() => generator.description).thenReturn('generator description');
149142
when(() => generator.hooks).thenReturn(hooks);
@@ -170,59 +163,54 @@ void main() {
170163
});
171164
});
172165

173-
test('create core app', () async {
174-
final tempDir = Directory.systemTemp.createTempSync();
175-
addTearDown(() => tempDir.deleteSync(recursive: true));
176-
final argResults = MockArgResults();
177-
final command = CreateFlutterApp(
178-
analytics: analytics,
179-
logger: logger,
180-
generatorFromBundle: (_) async => throw Exception('oops'),
181-
generatorFromBrick: (_) async => generator,
182-
)..argResultOverrides = argResults;
183-
when(() => argResults['output-directory'] as String?)
184-
.thenReturn(tempDir.path);
185-
when(() => argResults.rest).thenReturn(['my_app']);
186-
when(() => argResults['application-id'] as String?).thenReturn(
187-
'xyz.app.my_app',
188-
);
189-
190-
final result = await command.run();
191-
192-
expect(command.template.name, 'core');
193-
expect(result, equals(ExitCode.success.code));
194-
195-
verify(() => logger.progress('Bootstrapping')).called(1);
196-
verify(
197-
() => hooks.preGen(
198-
vars: <String, dynamic>{
166+
group('templates', () {
167+
test('core', () async {
168+
await testMultiTemplateCommand(
169+
multiTemplatesCommand: CreateFlutterApp(
170+
analytics: analytics,
171+
logger: logger,
172+
generatorFromBundle: (_) async => throw Exception('oops'),
173+
generatorFromBrick: (_) async => generator,
174+
),
175+
logger: logger,
176+
hooks: hooks,
177+
generator: generator,
178+
templateName: 'core',
179+
mockArgs: {'application-id': 'xyz.app.my_app'},
180+
expectedVars: {
199181
'project_name': 'my_app',
200182
'description': '',
201183
'org_name': 'com.example.verygoodcore',
202184
'application_id': 'xyz.app.my_app',
203185
},
204-
onVarsChanged: any(named: 'onVarsChanged'),
205-
),
206-
);
207-
verify(
208-
() => generator.generate(
209-
any(),
210-
vars: <String, dynamic>{
186+
expectedLogSummary: 'Created a Very Good App! 🦄',
187+
);
188+
});
189+
190+
test('wear', () async {
191+
await testMultiTemplateCommand(
192+
multiTemplatesCommand: CreateFlutterApp(
193+
analytics: analytics,
194+
logger: logger,
195+
generatorFromBundle: (_) async => throw Exception('oops'),
196+
generatorFromBrick: (_) async => generator,
197+
),
198+
logger: logger,
199+
hooks: hooks,
200+
generator: generator,
201+
templateName: 'wear',
202+
mockArgs: {
203+
'application-id': 'xyz.app.my_wear_app',
204+
},
205+
expectedVars: {
211206
'project_name': 'my_app',
212207
'description': '',
213208
'org_name': 'com.example.verygoodcore',
214-
'application_id': 'xyz.app.my_app',
209+
'application_id': 'xyz.app.my_wear_app',
215210
},
216-
logger: logger,
217-
),
218-
).called(1);
219-
expect(
220-
progressLogs,
221-
equals(['Generated ${generatedFiles.length} file(s)']),
222-
);
223-
verify(
224-
() => logger.info('Created a Very Good App! 🦄'),
225-
).called(1);
211+
expectedLogSummary: 'Created a Very Good Wear OS app! ⌚️🦄',
212+
);
213+
});
226214
});
227215
});
228216
});

test/src/commands/create/create_subcommand_test.dart

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ class _TestCreateSubCommandWithPublishable extends _TestCreateSubCommand
7474
class _TestCreateSubCommandMultiTemplate extends CreateSubCommand
7575
with MultiTemplates {
7676
_TestCreateSubCommandMultiTemplate({
77-
required this.defaultTemplateName,
7877
required this.templates,
7978
required super.analytics,
8079
required super.logger,
@@ -88,9 +87,6 @@ class _TestCreateSubCommandMultiTemplate extends CreateSubCommand
8887
@override
8988
final String description = 'Create command';
9089

91-
@override
92-
final String defaultTemplateName;
93-
9490
@override
9591
final List<Template> templates;
9692
}
@@ -932,7 +928,6 @@ Run "runner help" to see global options.''';
932928
group('can be instantiated', () {
933929
test('with default options', () {
934930
final command = _TestCreateSubCommandMultiTemplate(
935-
defaultTemplateName: 'template1',
936931
templates: templates,
937932
analytics: analytics,
938933
logger: logger,
@@ -1014,7 +1009,6 @@ Run "runner help" to see global options.''';
10141009
});
10151010

10161011
final command = _TestCreateSubCommandMultiTemplate(
1017-
defaultTemplateName: 'template1',
10181012
templates: templates,
10191013
analytics: analytics,
10201014
logger: logger,

tool/generate_bundles.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ bricks=(
99
very_good_flutter_plugin
1010
very_good_flame_game
1111
very_good_docs_site
12+
very_good_wear_app
1213
)
1314

1415
for brick in "${bricks[@]}"

0 commit comments

Comments
 (0)