Skip to content

Commit 35b9bd1

Browse files
authored
feat: create docs site new usage (#614)
* feat: create docs site new usage * tests
1 parent 1a94dc6 commit 35b9bd1

File tree

8 files changed

+320
-0
lines changed

8 files changed

+320
-0
lines changed

.github/workflows/very_good_cli.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ jobs:
5757
- test/src/commands/create/e2e/flutter_app/core_test.dart
5858
- test/src/commands/create/e2e/dart_package/dart_pkg_test.dart
5959
- test/src/commands/create/e2e/dart_cli/dart_cli_test.dart
60+
- test/src/commands/create/e2e/docs_site/docs_site_test.dart
6061

6162
# E2E tests for the legacy create command syntax
6263
- test/src/commands/create/e2e/legacy/core_test.dart
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export 'create_subcommand.dart';
22
export 'dart_cli.dart';
33
export 'dart_package.dart';
4+
export 'docs_site.dart';
45
export 'flutter_app.dart';
56
export 'legacy.dart';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import 'package:mason_logger/mason_logger.dart';
2+
import 'package:usage/usage.dart';
3+
import 'package:very_good_cli/src/commands/commands.dart';
4+
import 'package:very_good_cli/src/commands/create/templates/templates.dart';
5+
6+
/// {@template very_good_create_docs_site}
7+
/// A [CreateSubCommand] for creating Dart command line interfaces.
8+
/// {@endtemplate}
9+
class CreateDocsSite extends CreateSubCommand with OrgName {
10+
/// {@macro very_good_create_docs_site}
11+
CreateDocsSite({
12+
required Analytics analytics,
13+
required Logger logger,
14+
required MasonGeneratorFromBundle? generatorFromBundle,
15+
required MasonGeneratorFromBrick? generatorFromBrick,
16+
}) : super(
17+
analytics: analytics,
18+
logger: logger,
19+
generatorFromBundle: generatorFromBundle,
20+
generatorFromBrick: generatorFromBrick,
21+
);
22+
23+
@override
24+
String get name => 'docs_site';
25+
26+
@override
27+
String get description =>
28+
'Creates a new very good docs site in the specified directory.';
29+
30+
@override
31+
Template get template => VeryGoodDocsSiteTemplate();
32+
}

lib/src/commands/create/create.dart

+10
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ class CreateCommand extends Command<int> {
5858
generatorFromBrick: generatorFromBrick,
5959
),
6060
);
61+
62+
// very_good create docs_site <args>
63+
addSubcommand(
64+
CreateDocsSite(
65+
analytics: analytics,
66+
logger: logger,
67+
generatorFromBundle: generatorFromBundle,
68+
generatorFromBrick: generatorFromBrick,
69+
),
70+
);
6171
}
6272

6373
@override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
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:usage/usage.dart';
9+
import 'package:very_good_cli/src/commands/commands.dart';
10+
11+
import '../../../../helpers/helpers.dart';
12+
13+
class MockAnalytics extends Mock implements Analytics {}
14+
15+
class MockLogger extends Mock implements Logger {}
16+
17+
class MockMasonGenerator extends Mock implements MasonGenerator {}
18+
19+
class MockGeneratorHooks extends Mock implements GeneratorHooks {}
20+
21+
class MockArgResults extends Mock implements ArgResults {}
22+
23+
class FakeLogger extends Fake implements Logger {}
24+
25+
class FakeDirectoryGeneratorTarget extends Fake
26+
implements DirectoryGeneratorTarget {}
27+
28+
final expectedUsage = [
29+
'''
30+
Creates a new very good docs site in the specified directory.
31+
32+
Usage: very_good create docs_site <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+
40+
Run "very_good help" to see global options.''',
41+
];
42+
43+
const pubspec = '''
44+
name: example
45+
environment:
46+
sdk: ">=2.13.0 <3.0.0"
47+
''';
48+
49+
void main() {
50+
late Analytics analytics;
51+
late Logger logger;
52+
53+
setUpAll(() {
54+
registerFallbackValue(FakeDirectoryGeneratorTarget());
55+
registerFallbackValue(FakeLogger());
56+
});
57+
58+
setUp(() {
59+
analytics = MockAnalytics();
60+
when(
61+
() => analytics.sendEvent(any(), any(), label: any(named: 'label')),
62+
).thenAnswer((_) async {});
63+
when(
64+
() => analytics.waitForLastPing(timeout: any(named: 'timeout')),
65+
).thenAnswer((_) async {});
66+
67+
logger = MockLogger();
68+
69+
final progress = MockProgress();
70+
71+
when(() => logger.progress(any())).thenReturn(progress);
72+
});
73+
74+
group('can be instantiated', () {
75+
test('with default options', () {
76+
final logger = Logger();
77+
final command = CreateDocsSite(
78+
analytics: analytics,
79+
logger: logger,
80+
generatorFromBundle: null,
81+
generatorFromBrick: null,
82+
);
83+
expect(command.name, equals('docs_site'));
84+
expect(
85+
command.description,
86+
equals(
87+
'Creates a new very good docs site in the specified directory.',
88+
),
89+
);
90+
expect(command.logger, equals(logger));
91+
expect(command, isA<OrgName>());
92+
});
93+
});
94+
95+
group('create docs_site', () {
96+
test(
97+
'help',
98+
withRunner((commandRunner, logger, pubUpdater, printLogs) async {
99+
final result =
100+
await commandRunner.run(['create', 'docs_site', '--help']);
101+
expect(printLogs, equals(expectedUsage));
102+
expect(result, equals(ExitCode.success.code));
103+
104+
printLogs.clear();
105+
106+
final resultAbbr =
107+
await commandRunner.run(['create', 'docs_site', '-h']);
108+
expect(printLogs, equals(expectedUsage));
109+
expect(resultAbbr, equals(ExitCode.success.code));
110+
}),
111+
);
112+
113+
group('running the command', () {
114+
final generatedFiles =
115+
List.filled(10, const GeneratedFile.created(path: ''));
116+
117+
late GeneratorHooks hooks;
118+
late MasonGenerator generator;
119+
120+
setUp(() {
121+
hooks = MockGeneratorHooks();
122+
generator = MockMasonGenerator();
123+
124+
when(() => generator.hooks).thenReturn(hooks);
125+
when(
126+
() => hooks.preGen(
127+
vars: any(named: 'vars'),
128+
onVarsChanged: any(named: 'onVarsChanged'),
129+
),
130+
).thenAnswer((_) async {});
131+
132+
when(
133+
() => generator.generate(
134+
any(),
135+
vars: any(named: 'vars'),
136+
logger: any(named: 'logger'),
137+
),
138+
).thenAnswer((_) async {
139+
return generatedFiles;
140+
});
141+
142+
when(() => generator.id).thenReturn('generator_id');
143+
when(() => generator.description).thenReturn('generator description');
144+
when(() => generator.hooks).thenReturn(hooks);
145+
146+
when(
147+
() => hooks.preGen(
148+
vars: any(named: 'vars'),
149+
onVarsChanged: any(named: 'onVarsChanged'),
150+
),
151+
).thenAnswer((_) async {});
152+
when(
153+
() => generator.generate(
154+
any(),
155+
vars: any(named: 'vars'),
156+
logger: any(named: 'logger'),
157+
),
158+
).thenAnswer((_) async {
159+
final target =
160+
_.positionalArguments.first as DirectoryGeneratorTarget;
161+
File(path.join(target.dir.path, 'my_docs_site', 'pubspec.yaml'))
162+
..createSync(recursive: true)
163+
..writeAsStringSync(pubspec);
164+
return generatedFiles;
165+
});
166+
});
167+
168+
test('creates docs site', () async {
169+
final tempDir = Directory.systemTemp.createTempSync();
170+
addTearDown(() => tempDir.deleteSync(recursive: true));
171+
final argResults = MockArgResults();
172+
final command = CreateDocsSite(
173+
analytics: analytics,
174+
logger: logger,
175+
generatorFromBundle: (_) async => throw Exception('oops'),
176+
generatorFromBrick: (_) async => generator,
177+
)..argResultOverrides = argResults;
178+
when(() => argResults['output-directory'] as String?)
179+
.thenReturn(tempDir.path);
180+
when(() => argResults.rest).thenReturn(['my_docs_site']);
181+
when(() => argResults['org-name'] as String?).thenReturn(
182+
'xyz.app.my_app',
183+
);
184+
185+
final result = await command.run();
186+
187+
expect(command.template.name, 'docs_site');
188+
expect(result, equals(ExitCode.success.code));
189+
190+
verify(() => logger.progress('Bootstrapping')).called(1);
191+
verify(
192+
() => hooks.preGen(
193+
vars: <String, dynamic>{
194+
'project_name': 'my_docs_site',
195+
'description': '',
196+
'org_name': 'xyz.app.my_app',
197+
},
198+
onVarsChanged: any(named: 'onVarsChanged'),
199+
),
200+
);
201+
verify(
202+
() => generator.generate(
203+
any(),
204+
vars: <String, dynamic>{
205+
'project_name': 'my_docs_site',
206+
'description': '',
207+
'org_name': 'xyz.app.my_app',
208+
},
209+
logger: logger,
210+
),
211+
).called(1);
212+
verify(
213+
() => logger.info('Created a Very Good documentation site! 🦄'),
214+
).called(1);
215+
});
216+
});
217+
});
218+
}

test/src/commands/create/commands/legacy_test.dart

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Usage: very_good create <subcommand> <project-name> [arguments]
2525
Available subcommands:
2626
dart_cli Creates a new very good Dart CLI in the specified directory.
2727
dart_package Creates a new very good Dart package in the specified directory.
28+
docs_site Creates a new very good docs site in the specified directory.
2829
flutter_app Creates a new very good Flutter app in the specified directory.
2930
3031
Run "very_good help" to see global options.'''

test/src/commands/create/create_test.dart

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Usage: very_good create <subcommand> <project-name> [arguments]
3131
Available subcommands:
3232
dart_cli Creates a new very good Dart CLI in the specified directory.
3333
dart_package Creates a new very good Dart package in the specified directory.
34+
docs_site Creates a new very good docs site in the specified directory.
3435
flutter_app Creates a new very good Flutter app in the specified directory.
3536
3637
Run "very_good help" to see global options.'''
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
@Tags(['e2e'])
2+
import 'package:mason/mason.dart';
3+
import 'package:path/path.dart' as path;
4+
import 'package:test/test.dart';
5+
import 'package:universal_io/io.dart';
6+
7+
import '../../../../../helpers/helpers.dart';
8+
9+
void main() {
10+
test(
11+
'create docs_site',
12+
withRunner((commandRunner, logger, updater, logs) async {
13+
final directory = Directory.systemTemp.createTempSync();
14+
final result = await commandRunner.run(
15+
['create', 'docs_site', 'very_good_docs_site', '-o', directory.path],
16+
);
17+
expect(result, equals(ExitCode.success.code));
18+
19+
final installResult = await Process.run(
20+
'npm',
21+
['install'],
22+
workingDirectory: path.join(directory.path, 'very_good_docs_site'),
23+
runInShell: true,
24+
);
25+
expect(installResult.exitCode, equals(ExitCode.success.code));
26+
27+
final formatResult = await Process.run(
28+
'npm',
29+
['run', 'format'],
30+
workingDirectory: path.join(directory.path, 'very_good_docs_site'),
31+
runInShell: true,
32+
);
33+
expect(formatResult.exitCode, equals(ExitCode.success.code));
34+
expect(formatResult.stderr, isEmpty);
35+
36+
final lintResult = await Process.run(
37+
'npm',
38+
['run', 'lint'],
39+
workingDirectory: path.join(directory.path, 'very_good_docs_site'),
40+
runInShell: true,
41+
);
42+
expect(lintResult.exitCode, equals(ExitCode.success.code));
43+
expect(lintResult.stderr, isEmpty);
44+
45+
final buildResult = await Process.run(
46+
'npm',
47+
['run', 'build'],
48+
workingDirectory: path.join(directory.path, 'very_good_docs_site'),
49+
runInShell: true,
50+
);
51+
expect(buildResult.exitCode, equals(ExitCode.success.code));
52+
expect(buildResult.stderr, isEmpty);
53+
}),
54+
timeout: const Timeout(Duration(minutes: 2)),
55+
);
56+
}

0 commit comments

Comments
 (0)