-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
@ F r integrated Richard's solution (force)
- Loading branch information
1 parent
aaead54
commit 30c1e1a
Showing
12 changed files
with
1,343 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,5 +17,6 @@ generated_* | |
|
||
# Other files | ||
.old.dart | ||
.build | ||
|
||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import 'dart:io'; | ||
|
||
const topBar = '▶▶▶▶'; | ||
const bottomBar = '◀◀◀◀'; | ||
|
||
/// [String] extension | ||
extension StringApprovedExtension on String { | ||
/// git diff complains when file doesn't end in newline. This getter ensures a string does. | ||
String get endWithNewline => endsWith('\n') ? this : '$this\n'; | ||
} | ||
|
||
extension DirectoryApprovedExtension on Directory { | ||
Set<File> filesWithExtension(String extension) { | ||
final fileSystemEntities = listSync().where((entity) => entity is File && entity.path.endsWith(extension)); | ||
return fileSystemEntities.whereType<File>().toSet(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import 'dart:async'; | ||
import 'dart:convert'; | ||
import 'dart:io'; | ||
|
||
import 'package:analyzer/dart/analysis/context_builder.dart'; | ||
import 'package:analyzer/dart/analysis/context_locator.dart'; | ||
import 'package:analyzer/dart/analysis/results.dart'; | ||
import 'package:analyzer/dart/ast/ast.dart'; | ||
import 'package:approval_tests/src/widget/src/common.dart'; | ||
|
||
final _widgetNamesDir = Directory('./test/approved'); | ||
final _widgetNamesPath = '${_widgetNamesDir.path}/class_names.txt'; | ||
|
||
Future<Set<String>> getWidgetNames() async { | ||
final resultCompleter = Completer<Set<String>>(); | ||
|
||
await isWidgetNamesFileFresh().then((isFileFresh) { | ||
if (isFileFresh) { | ||
readWidgetsFile(_widgetNamesPath).then((widgetNames) { | ||
resultCompleter.complete(widgetNames); | ||
}); | ||
} else { | ||
final libPath = '${Directory.current.absolute.path}/lib'; | ||
stdout.write("package:approved: searching for class names in $libPath..."); | ||
extractWidgetNames(libPath).then((widgetsList) { | ||
if (!_widgetNamesDir.existsSync()) { | ||
_widgetNamesDir.createSync(); | ||
} | ||
_widgetNamesDir.createSync(); | ||
final widgetsFile = File(_widgetNamesPath); | ||
widgetsFile.createSync(); | ||
const header = ''' | ||
# This file was autogenerated by package:approved. Please do not edit. | ||
# Below is a list of class found in the project /lib folder.'''; | ||
final widgetNamesString = widgetsList.join('\n').endWithNewline; | ||
widgetsFile.writeAsStringSync('$header\n$widgetNamesString'); | ||
stdout.write('done\n'); | ||
resultCompleter.complete(widgetsList); | ||
}); | ||
} | ||
}); | ||
|
||
return resultCompleter.future; | ||
} | ||
|
||
String loadWidgetNames() { | ||
late String result; | ||
final widgetNamesFile = File(_widgetNamesPath); | ||
if (widgetNamesFile.existsSync()) { | ||
result = widgetNamesFile.readAsStringSync(); | ||
} | ||
return result; | ||
} | ||
|
||
/// Crawls the project and extracts widget names. | ||
Future<Set<String>> extractWidgetNames(String libPath) async { | ||
final completer = Completer<Set<String>>(); | ||
|
||
final contextLocator = ContextLocator(); | ||
final contextRoots = contextLocator.locateRoots(includedPaths: [libPath]); | ||
|
||
final contextBuilder = ContextBuilder(); | ||
await getFlutterSdkPath().then((path) { | ||
final dartStr = '$path/bin/cache/dart-sdk'; | ||
final analysisContext = contextBuilder.createContext( | ||
contextRoot: contextRoots.first, | ||
sdkPath: dartStr, | ||
); | ||
|
||
final classNames = <String>{}; | ||
|
||
// Traverse all files in the lib folder | ||
final libDirectory = Directory(libPath); | ||
final dartFiles = libDirectory.listSync(recursive: true).where( | ||
(file) => | ||
file.path.endsWith('.dart') && !file.path.contains('.g.dart') && !file.path.contains('.freezed.dart'), | ||
); | ||
|
||
for (final file in dartFiles) { | ||
final analysisSession = analysisContext.currentSession; | ||
final parsedResult = analysisSession.getParsedUnit(file.path) as ParsedUnitResult; | ||
|
||
for (final compilationUnitMember in parsedResult.unit.declarations) { | ||
if (compilationUnitMember is ClassDeclaration) { | ||
final String name = compilationUnitMember.name.value().toString(); | ||
if (!name.startsWith('_')) { | ||
classNames.add(name); | ||
} | ||
} | ||
} | ||
} | ||
|
||
completer.complete(classNames); | ||
}); | ||
|
||
return completer.future; | ||
} | ||
|
||
/// Get the path to the Flutter SDK | ||
Future<String> getFlutterSdkPath() async { | ||
final completer = Completer<String>(); | ||
|
||
await Process.run('flutter', ['--version', '--machine']).then((result) { | ||
if (result.exitCode != 0) { | ||
throw Exception('Failed to run flutter command: ${result.stderr}'); | ||
} | ||
|
||
final jsonData = jsonDecode(result.stdout.toString()) as Map<String, dynamic>; | ||
|
||
completer.complete(jsonData['flutterRoot'].toString()); | ||
}); | ||
|
||
return completer.future; | ||
} | ||
|
||
Future<Set<String>> readWidgetsFile(String filePath) async { | ||
final completer = Completer<Set<String>>(); | ||
final File file = File(filePath); | ||
|
||
await file.readAsString().then((text) { | ||
// Split by lines | ||
final linesList = text.split('\n'); | ||
// Remove empty lines and comments | ||
final linesSet = linesList.where((line) => line.isNotEmpty && !line.startsWith('#')).toSet(); | ||
completer.complete(linesSet); | ||
}); | ||
|
||
return completer.future; | ||
} | ||
|
||
Future<bool> isWidgetNamesFileFresh() async { | ||
final resultCompleter = Completer<bool>(); | ||
|
||
final libDirectory = Directory('lib'); | ||
|
||
await findNewestDartFileTimestamp(libDirectory).then((dateTime) { | ||
final widgetNamesFile = File(_widgetNamesPath); | ||
if (dateTime != null && widgetNamesFile.existsSync() && widgetNamesFile.lastModifiedSync().isAfter(dateTime)) { | ||
resultCompleter.complete(true); | ||
} else { | ||
resultCompleter.complete(false); | ||
} | ||
}); | ||
|
||
return resultCompleter.future; | ||
} | ||
|
||
Future<DateTime?> findNewestDartFileTimestamp(Directory dir) async { | ||
DateTime? newestTimestamp; | ||
|
||
if (!await dir.exists()) { | ||
return null; | ||
} | ||
|
||
await for (final FileSystemEntity entity in dir.list(recursive: true)) { | ||
if (entity is File && entity.path.endsWith('.dart')) { | ||
final DateTime lastModified = await entity.lastModified(); | ||
|
||
if (newestTimestamp == null || lastModified.isAfter(newestTimestamp)) { | ||
newestTimestamp = lastModified; | ||
} | ||
} | ||
} | ||
|
||
return newestTimestamp; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// ignore_for_file: avoid_print | ||
|
||
import 'dart:io'; | ||
|
||
import 'package:approval_tests/src/widget/src/common.dart'; | ||
|
||
void printGitDiffs(String testDescription, String differences) { | ||
print(topBar); | ||
print("Results of git diff during approvalTest('$testDescription'):"); | ||
print(differences.trim()); | ||
print(bottomBar); | ||
} | ||
|
||
/// return the diff of two files | ||
String gitDiffFiles(File path0, FileSystemEntity path1) { | ||
final processResult = Process.runSync('git', ['diff', '--no-index', path0.path, path1.path]); | ||
|
||
final stdoutString = processResult.stdout as String; | ||
final stderrString = processResult.stderr as String; | ||
|
||
final processString = stdoutString.isNotEmpty || stderrString.isNotEmpty ? stdoutString : ''; | ||
|
||
return _stripGitDiff(processString); | ||
} | ||
|
||
/// Format the git --diff if superfluous text | ||
String _stripGitDiff(String multiLineString) { | ||
bool startsWithAny(String line, List<String> prefixes) => prefixes.any((prefix) => line.startsWith(prefix)); | ||
|
||
final List<String> lines = multiLineString.split('\n'); | ||
final List<String> filteredLines = lines.where((line) => !startsWithAny(line, ['diff', 'index', '@@'])).toList(); | ||
|
||
final String result = filteredLines.join('\n'); | ||
|
||
return result; | ||
} |
Oops, something went wrong.