Skip to content

Commit f63571a

Browse files
authored
feat: Adding very_good create app_ui command (#1520)
1 parent 4feb354 commit f63571a

10 files changed

Lines changed: 610 additions & 0 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import 'package:very_good_cli/src/commands/commands.dart';
2+
import 'package:very_good_cli/src/commands/create/templates/templates.dart';
3+
4+
/// {@template very_good_create_app_ui_package_command}
5+
/// A [CreateSubCommand] for creating App UI packages.
6+
/// {@endtemplate}
7+
class CreateAppUiPackage extends CreateSubCommand with Publishable {
8+
/// {@macro very_good_create_app_ui_package_command}
9+
CreateAppUiPackage({
10+
required super.logger,
11+
required super.generatorFromBundle,
12+
required super.generatorFromBrick,
13+
});
14+
15+
@override
16+
String get name => 'app_ui_package';
17+
18+
@override
19+
List<String> get aliases => ['app_ui_pkg'];
20+
21+
@override
22+
String get description => 'Generate a Very Good App UI package.';
23+
24+
@override
25+
Template get template => AppUiTemplate();
26+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export 'app_ui_package.dart';
12
export 'create_subcommand.dart';
23
export 'dart_cli.dart';
34
export 'dart_package.dart';

lib/src/commands/create/create.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ class CreateCommand extends Command<int> {
1616
@visibleForTesting MasonGeneratorFromBundle? generatorFromBundle,
1717
@visibleForTesting MasonGeneratorFromBrick? generatorFromBrick,
1818
}) {
19+
// very_good create app_ui_package <args>
20+
addSubcommand(
21+
CreateAppUiPackage(
22+
logger: logger,
23+
generatorFromBundle: generatorFromBundle,
24+
generatorFromBrick: generatorFromBrick,
25+
),
26+
);
27+
1928
// very_good create flutter_app <args>
2029
addSubcommand(
2130
CreateFlutterApp(

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export 'post_generate_actions.dart';
22
export 'template.dart';
3+
export 'very_good_app_ui/very_good_app_ui.dart';
34
export 'very_good_core/very_good_core.dart';
45
export 'very_good_dart_cli/very_good_dart_cli.dart';
56
export 'very_good_dart_package/very_good_dart_package.dart';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export 'very_good_app_ui_bundle.dart';
2+
export 'very_good_app_ui_template.dart';

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

Lines changed: 327 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 'package:mason/mason.dart';
2+
import 'package:universal_io/io.dart';
3+
import 'package:very_good_cli/src/commands/create/templates/templates.dart';
4+
import 'package:very_good_cli/src/logger_extension.dart';
5+
6+
/// {@template app_ui_template}
7+
/// A Flutter app UI package template.
8+
/// {@endtemplate}
9+
class AppUiTemplate extends Template {
10+
/// {@macro app_ui_template}
11+
AppUiTemplate()
12+
: super(
13+
name: 'app_ui',
14+
bundle: veryGoodAppUiBundle,
15+
help: 'Generate a Very Good App UI package.',
16+
);
17+
18+
@override
19+
Future<void> onGenerateComplete(Logger logger, Directory outputDir) async {
20+
if (await installFlutterPackages(logger, outputDir)) {
21+
await applyDartFixes(logger, outputDir);
22+
}
23+
_logSummary(logger);
24+
}
25+
26+
void _logSummary(Logger logger) {
27+
logger
28+
..info('\n')
29+
..created('Created a Very Good App UI Package! 🦄')
30+
..info('\n');
31+
}
32+
}
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
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:path/path.dart' as path;
7+
import 'package:test/test.dart';
8+
import 'package:very_good_cli/src/commands/commands.dart';
9+
10+
import '../../../../helpers/helpers.dart';
11+
12+
class _MockLogger extends Mock implements Logger {}
13+
14+
class _MockProgress extends Mock implements Progress {}
15+
16+
class _MockMasonGenerator extends Mock implements MasonGenerator {}
17+
18+
class _MockGeneratorHooks extends Mock implements GeneratorHooks {}
19+
20+
class _MockArgResults extends Mock implements ArgResults {}
21+
22+
class _FakeLogger extends Fake implements Logger {}
23+
24+
class _FakeDirectoryGeneratorTarget extends Fake
25+
implements DirectoryGeneratorTarget {}
26+
27+
final expectedUsage = [
28+
'''
29+
Generate a Very Good App UI package.
30+
31+
Usage: very_good create app_ui_package <project-name> [arguments]
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+
--publishable Whether the generated project is intended to be published.
37+
38+
Run "very_good help" to see global options.''',
39+
];
40+
41+
const pubspec = '''
42+
name: example
43+
environment:
44+
sdk: ^3.11.0
45+
''';
46+
47+
void main() {
48+
late Logger logger;
49+
50+
setUpAll(() {
51+
registerFallbackValue(_FakeDirectoryGeneratorTarget());
52+
registerFallbackValue(_FakeLogger());
53+
});
54+
55+
setUp(() {
56+
logger = _MockLogger();
57+
58+
final progress = _MockProgress();
59+
60+
when(() => logger.progress(any())).thenReturn(progress);
61+
});
62+
63+
group('can be instantiated', () {
64+
test('with default options', () {
65+
final logger = Logger();
66+
final command = CreateAppUiPackage(
67+
logger: logger,
68+
generatorFromBundle: null,
69+
generatorFromBrick: null,
70+
);
71+
expect(command.name, equals('app_ui_package'));
72+
expect(
73+
command.description,
74+
equals('Generate a Very Good App UI package.'),
75+
);
76+
expect(command.logger, equals(logger));
77+
expect(command, isA<Publishable>());
78+
});
79+
});
80+
81+
group('create app_ui_package', () {
82+
test(
83+
'help',
84+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
85+
final result = await commandRunner.run([
86+
'create',
87+
'app_ui_package',
88+
'--help',
89+
]);
90+
expect(printLogs, equals(expectedUsage));
91+
expect(result, equals(ExitCode.success.code));
92+
93+
printLogs.clear();
94+
95+
final resultAbbr = await commandRunner.run([
96+
'create',
97+
'app_ui_pkg',
98+
'-h',
99+
]);
100+
expect(printLogs, equals(expectedUsage));
101+
expect(resultAbbr, equals(ExitCode.success.code));
102+
}),
103+
);
104+
105+
group('running the command', () {
106+
final generatedFiles = List.filled(
107+
10,
108+
const GeneratedFile.created(path: ''),
109+
);
110+
111+
late GeneratorHooks hooks;
112+
late MasonGenerator generator;
113+
114+
setUp(() {
115+
hooks = _MockGeneratorHooks();
116+
generator = _MockMasonGenerator();
117+
118+
when(() => generator.hooks).thenReturn(hooks);
119+
when(
120+
() => hooks.preGen(
121+
vars: any(named: 'vars'),
122+
onVarsChanged: any(named: 'onVarsChanged'),
123+
),
124+
).thenAnswer((_) async {});
125+
126+
when(
127+
() => generator.generate(
128+
any(),
129+
vars: any(named: 'vars'),
130+
logger: any(named: 'logger'),
131+
),
132+
).thenAnswer((_) async {
133+
return generatedFiles;
134+
});
135+
136+
when(() => generator.id).thenReturn('generator_id');
137+
when(() => generator.description).thenReturn('generator description');
138+
when(() => generator.hooks).thenReturn(hooks);
139+
140+
when(
141+
() => hooks.preGen(
142+
vars: any(named: 'vars'),
143+
onVarsChanged: any(named: 'onVarsChanged'),
144+
),
145+
).thenAnswer((_) async {});
146+
when(
147+
() => generator.generate(
148+
any(),
149+
vars: any(named: 'vars'),
150+
logger: any(named: 'logger'),
151+
),
152+
).thenAnswer((invocation) async {
153+
final target =
154+
invocation.positionalArguments.first as DirectoryGeneratorTarget;
155+
File(path.join(target.dir.path, 'pubspec.yaml'))
156+
..createSync(recursive: true)
157+
..writeAsStringSync(pubspec);
158+
return generatedFiles;
159+
});
160+
});
161+
162+
test('creates app ui package', () async {
163+
final tempDirectory = Directory.systemTemp.createTempSync();
164+
addTearDown(() => tempDirectory.deleteSync(recursive: true));
165+
166+
final argResults = _MockArgResults();
167+
final command = CreateAppUiPackage(
168+
logger: logger,
169+
generatorFromBundle: (_) async => throw Exception('oops'),
170+
generatorFromBrick: (_) async => generator,
171+
)..argResultOverrides = argResults;
172+
when(
173+
() => argResults['output-directory'] as String?,
174+
).thenReturn(tempDirectory.path);
175+
when(() => argResults.rest).thenReturn(['my_app_ui']);
176+
177+
final result = await command.run();
178+
179+
expect(command.template.name, 'app_ui');
180+
expect(result, equals(ExitCode.success.code));
181+
182+
verify(() => logger.progress('Bootstrapping')).called(1);
183+
verify(
184+
() => hooks.preGen(
185+
vars: <String, dynamic>{
186+
'project_name': 'my_app_ui',
187+
'description': '',
188+
'publishable': false,
189+
},
190+
onVarsChanged: any(named: 'onVarsChanged'),
191+
),
192+
);
193+
verify(
194+
() => generator.generate(
195+
any(),
196+
vars: <String, dynamic>{
197+
'project_name': 'my_app_ui',
198+
'description': '',
199+
'publishable': false,
200+
},
201+
logger: logger,
202+
),
203+
).called(1);
204+
verify(
205+
() => logger.info('Created a Very Good App UI Package! 🦄'),
206+
).called(1);
207+
});
208+
});
209+
});
210+
}

test/src/commands/create/create_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Usage: very_good create <subcommand> <project-name> [arguments]
1111
-h, --help Print this usage information.
1212
1313
Available subcommands:
14+
app_ui_package Generate a Very Good App UI package.
1415
dart_cli Generate a Very Good Dart CLI application.
1516
dart_package Generate a Very Good Dart package.
1617
docs_site Generate a Very Good documentation site.

tool/generate_bundles.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Runs `mason bundle` to generate bundles for all bricks within the respective templates directories.
33

44
bricks=(
5+
very_good_app_ui
56
very_good_core
67
very_good_dart_package
78
very_good_dart_cli

0 commit comments

Comments
 (0)