Skip to content
This repository was archived by the owner on Feb 21, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 35 additions & 10 deletions brick/hooks/post_gen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,45 @@ typedef RunProcess = Future<ProcessResult> Function(
bool runInShell,
});

Future<void> run(
HookContext context, {
@visibleForTesting RunProcess runProcess = Process.run,
}) async {
Future<void> run(HookContext context,
// We intentionally ignore the trailing comma until the following mason issue is
// fixed: https://github.com/felangel/mason/pull/1164
// ignore: require_trailing_commas
{@visibleForTesting RunProcess runProcess = Process.run}) async {
final projectName = context.vars['project_name'] as String;

final progress = context.logger.progress('Getting Dart dependencies...');

// We have to `pub get` the generated project to ensure that the analysis
// is able to fix the imports with the correct analysis options.
await runProcess(
'dart',
[
'pub',
'get',
'--directory=$projectName',
],
workingDirectory: Directory.current.path,
);

progress.update('Fixing Dart imports ordering...');

// Some imports are relative to the user specified package name, hence
// we try to fix the import directive ordering after the template has
// been generated.
//
// We only fix for the [directives_ordering](https://dart.dev/tools/linter-rules/directives_ordering)
// linter rules, as the other rule should be tackled by the template itself.
await runProcess('dart', [
'fix',
Directory.current.path,
'--apply',
'--code=directives_ordering',
]);
await runProcess(
'dart',
[
'fix',
projectName,
'--apply',
'--code=directives_ordering',
],
workingDirectory: Directory.current.path,
);

progress.complete('Completed post generation');
}
135 changes: 126 additions & 9 deletions brick/hooks/test/post_gen_test.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';

import 'package:mason/mason.dart';
Expand All @@ -8,16 +9,33 @@ import '../post_gen.dart' as post_gen;

class _MockHookContext extends Mock implements HookContext {}

class _MockLogger extends Mock implements Logger {}

class _MockProgress extends Mock implements Progress {}

class _MockProcessResult extends Mock implements ProcessResult {}

void main() {
group('post_gen', () {
late HookContext context;
late Logger logger;
late Progress progress;
late ProcessResult processResult;
late List<Invocation> invocations;

/// The value of the `project_name` context variable.
const projectName = 'project_name';

setUp(() {
context = _MockHookContext();
when(() => context.vars).thenReturn({'project_name': projectName});

logger = _MockLogger();
when(() => context.logger).thenReturn(logger);

progress = _MockProgress();
when(() => logger.progress(any())).thenReturn(progress);

processResult = _MockProcessResult();
invocations = [];
});
Expand All @@ -43,19 +61,68 @@ void main() {
return processResult;
}

test('fixes `directives_ordering` Dart linter rule', () async {
await post_gen.run(context, runProcess: runProcess);
test(
'''fixes `directives_ordering` Dart linter rule after `pub get`''',
() async {
await post_gen.run(context, runProcess: runProcess);

expect(invocations[0], isDartPubGet(directory: projectName));
expect(invocations[1], isDartDirectiveOrderingFix(path: projectName));
},
);

test('logs progress', () async {
final pubGetCompleter = Completer<void>();
final fixCompleter = Completer<void>();

// ignore: prefer_function_declarations_over_variables
final runProcess = (
String executable,
List<String> arguments, {
String? workingDirectory,
bool runInShell = false,
}) async {
switch (arguments.first) {
case 'pub':
await pubGetCompleter.future;
break;
case 'fix':
await fixCompleter.future;
break;
}
return processResult;
};

final postGen = post_gen.run(context, runProcess: runProcess);

verify(() => logger.progress('Getting Dart dependencies...')).called(1);

pubGetCompleter.complete();
await Future<void>.delayed(Duration.zero);

verify(() => progress.update('Fixing Dart imports ordering...'))
.called(1);

fixCompleter.complete();
await Future<void>.delayed(Duration.zero);

expect(invocations, contains(_IsDartDirectiveOrderingFix()));
verify(() => progress.complete('Completed post generation')).called(1);

await postGen;
});
});
}

Matcher isDartDirectiveOrderingFix() {
return _IsDartDirectiveOrderingFix();
Matcher isDartDirectiveOrderingFix({required String path}) {
return _IsDartDirectiveOrderingFix(path: path);
}

class _IsDartDirectiveOrderingFix extends Matcher {
const _IsDartDirectiveOrderingFix({required String path}) : _path = path;

/// The value of the path to apply the `dart fix` to.
final String _path;

@override
bool matches(dynamic item, Map<dynamic, dynamic> matchState) {
if (item is! Invocation) {
Expand All @@ -70,15 +137,63 @@ class _IsDartDirectiveOrderingFix extends Matcher {

return executableName == 'dart' &&
arguments.contains('fix') &&
arguments.contains(Directory.current.path) &&
arguments.contains(_path) &&
arguments.contains('--apply') &&
arguments.contains('--code=directives_ordering') &&
workingDirectory == null;
workingDirectory == Directory.current.path;
}

@override
Description describe(Description description) {
return description.add('is a `dart fix` for directives_ordering');
}

@override
Description describeMismatch(
dynamic item,
Description mismatchDescription,
Map<dynamic, dynamic> matchState,
bool verbose,
) {
return mismatchDescription
.add('is not a `dart fix` for directives_ordering');
}
}

Matcher isDartPubGet({required String directory}) {
return _IsDartPubGet(directory: directory);
}

class _IsDartPubGet extends Matcher {
const _IsDartPubGet({
required String directory,
}) : _directory = directory;

/// The value of the `--directory` argument passed to `dart pub get`.
final String _directory;

@override
bool matches(dynamic item, Map<dynamic, dynamic> matchState) {
if (item is! Invocation) {
return false;
}

final invocation = item;
final executableName = invocation.positionalArguments[0] as String;
final arguments = invocation.positionalArguments[1] as List<String>;
final workingDirectory =
invocation.namedArguments[const Symbol('workingDirectory')] as String?;

return executableName == 'dart' &&
arguments.contains('pub') &&
arguments.contains('get') &&
arguments.contains('--directory=$_directory') &&
workingDirectory == Directory.current.path;
}

@override
Description describe(Description description) {
return description.add('is a Dart fix for directives_ordering');
return description.add('is a `dart pub get --directory=$_directory`');
}

@override
Expand All @@ -88,6 +203,8 @@ class _IsDartDirectiveOrderingFix extends Matcher {
Map<dynamic, dynamic> matchState,
bool verbose,
) {
return mismatchDescription.add('is not a Dart fix for directives_ordering');
return mismatchDescription.add(
'is not a `dart pub get --directory=$_directory`',
);
}
}