From 1c8219ddd30b903f98bdef301af26a22d4c66acd Mon Sep 17 00:00:00 2001 From: Yelaman Yelmuratov Date: Sat, 29 Jun 2024 22:18:36 +0500 Subject: [PATCH] @ F widget tests, formatted code, some minor changes --- .gitignore | 2 +- CHANGELOG.md | 8 ++ README.md | 1 + lib/approval_tests.dart | 1 + lib/src/reporters/git.dart | 48 ++++++++ lib/src/widget/src/common.dart | 3 +- lib/src/widget/src/get_widget_names.dart | 21 +++- lib/src/widget/src/git_diffs.dart | 13 ++- lib/src/widget/src/src.dart | 104 ++++++------------ .../collect_widgets_meta_data.dart | 86 ++++++++++----- .../src/widget_meta/load_string_en.dart | 5 +- .../widget/src/widget_meta/widget_meta.dart | 22 +++- .../widget_meta/widget_tester_extension.dart | 9 +- test/approval_test.dart | 5 + test/approved/class_names.txt | 38 +++++++ test/groups/approval_tests.dart | 6 + test/groups/diff_tools_tests.dart | 29 +++++ test/groups/exception_tests.dart | 6 + test/groups/minor_tests.dart | 4 + test/groups/widget_tests.dart | 55 +++++++++ .../widget_tests.smoke_test.approved.txt | 1 + 21 files changed, 350 insertions(+), 117 deletions(-) create mode 100644 lib/src/reporters/git.dart create mode 100644 test/approved/class_names.txt create mode 100644 test/groups/widget_tests.dart create mode 100644 test/groups/widget_tests.smoke_test.approved.txt diff --git a/.gitignore b/.gitignore index f340fa0..e0aa6e1 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,6 @@ generated_* # Other files .old.dart -.build +build/ .DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index cd91674..b14012d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 1.1.0 + +- Breaking release: + - Added new reporter: `GitReporter`. It allows you to use `git` to view the differences between the received and approved files. + - Added support to approve files using CLI. Now you can approve files using the command line: `dart run approved:review` + - Added support to use ApprovalTests during widget tests. +Thanks to [Richard Coutts](https://github.com/buttonsrtoys) + ## 1.0.0 - Initial major release. diff --git a/README.md b/README.md index 4963ae5..80b8c43 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ Reporters are the part of Approval Tests that launch diff tools when things do n There are several reporters available in the package: - `CommandLineReporter` - This is the default reporter, which will output the diff in the terminal. +- `GitReporter` - This reporter will open the diff in the Git GUI. - `DiffReporter` - This reporter will open the Diff Tool in your IDE. - For Diff Reporter I using the default paths to the IDE, if something didn't work then you in the console see the expected correct path to the IDE and specify customDiffInfo. You can also contact me for help. diff --git a/lib/approval_tests.dart b/lib/approval_tests.dart index 3e872d7..c8cac6b 100644 --- a/lib/approval_tests.dart +++ b/lib/approval_tests.dart @@ -39,3 +39,4 @@ part 'src/scrubbers/date_scrubber.dart'; part 'src/scrubbers/nothing_scrubber.dart'; part 'src/scrubbers/reg_exp_scrubber.dart'; part 'src/writers/approval_text_writer.dart'; +part 'src/reporters/git.dart'; diff --git a/lib/src/reporters/git.dart b/lib/src/reporters/git.dart new file mode 100644 index 0000000..e9c63f7 --- /dev/null +++ b/lib/src/reporters/git.dart @@ -0,0 +1,48 @@ +part of '../../approval_tests.dart'; + +/// `GitReporter` is a class for reporting the comparison results using the git. +class GitReporter implements Reporter { + const GitReporter(); + + @override + Future report(String approvedPath, String receivedPath) async { + const DiffInfo diffInfo = + DiffInfo(name: "Git", command: 'git', arg: 'diff --no-index'); + + try { + await Future.wait([ + _checkFileExists(approvedPath), + _checkFileExists(receivedPath), + ]); + + await Process.run( + diffInfo.command, + [diffInfo.arg, approvedPath, receivedPath], + ); + } catch (e, st) { + if (e is PathNotFoundException) { + ApprovalLogger.exception(e, stackTrace: st); + rethrow; + } + if (e is ProcessException) { + final ProcessResult result = + await Process.run(ApprovalUtils.commandWhere, [diffInfo.command]); + ApprovalLogger.exception( + 'Error during comparison via Git. Please make sure that Git is installed and available in the system path. Error: ${e.message}. Git path: ${result.stdout}', + stackTrace: st, + ); + } + rethrow; + } + } + + Future _checkFileExists(String path) async { + if (!ApprovalUtils.isFileExists(path)) { + throw PathNotFoundException( + path, + const OSError('File not found'), + 'From GitReporter: File not found at path: [$path]. Please check the path and try again.', + ); + } + } +} diff --git a/lib/src/widget/src/common.dart b/lib/src/widget/src/common.dart index 9986f13..7bdbfdb 100644 --- a/lib/src/widget/src/common.dart +++ b/lib/src/widget/src/common.dart @@ -11,7 +11,8 @@ extension StringApprovedExtension on String { extension DirectoryApprovedExtension on Directory { Set filesWithExtension(String extension) { - final fileSystemEntities = listSync().where((entity) => entity is File && entity.path.endsWith(extension)); + final fileSystemEntities = listSync() + .where((entity) => entity is File && entity.path.endsWith(extension)); return fileSystemEntities.whereType().toSet(); } } diff --git a/lib/src/widget/src/get_widget_names.dart b/lib/src/widget/src/get_widget_names.dart index 40801c6..ece275f 100644 --- a/lib/src/widget/src/get_widget_names.dart +++ b/lib/src/widget/src/get_widget_names.dart @@ -21,7 +21,8 @@ Future> getWidgetNames() async { }); } else { final libPath = '${Directory.current.absolute.path}/lib'; - stdout.write("package:approved: searching for class names in $libPath..."); + stdout + .write("package:approved: searching for class names in $libPath..."); extractWidgetNames(libPath).then((widgetsList) { if (!_widgetNamesDir.existsSync()) { _widgetNamesDir.createSync(); @@ -73,12 +74,15 @@ Future> extractWidgetNames(String libPath) async { 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'), + 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; + final parsedResult = + analysisSession.getParsedUnit(file.path) as ParsedUnitResult; for (final compilationUnitMember in parsedResult.unit.declarations) { if (compilationUnitMember is ClassDeclaration) { @@ -105,7 +109,8 @@ Future getFlutterSdkPath() async { throw Exception('Failed to run flutter command: ${result.stderr}'); } - final jsonData = jsonDecode(result.stdout.toString()) as Map; + final jsonData = + jsonDecode(result.stdout.toString()) as Map; completer.complete(jsonData['flutterRoot'].toString()); }); @@ -121,7 +126,9 @@ Future> readWidgetsFile(String filePath) async { // Split by lines final linesList = text.split('\n'); // Remove empty lines and comments - final linesSet = linesList.where((line) => line.isNotEmpty && !line.startsWith('#')).toSet(); + final linesSet = linesList + .where((line) => line.isNotEmpty && !line.startsWith('#')) + .toSet(); completer.complete(linesSet); }); @@ -135,7 +142,9 @@ Future isWidgetNamesFileFresh() async { await findNewestDartFileTimestamp(libDirectory).then((dateTime) { final widgetNamesFile = File(_widgetNamesPath); - if (dateTime != null && widgetNamesFile.existsSync() && widgetNamesFile.lastModifiedSync().isAfter(dateTime)) { + if (dateTime != null && + widgetNamesFile.existsSync() && + widgetNamesFile.lastModifiedSync().isAfter(dateTime)) { resultCompleter.complete(true); } else { resultCompleter.complete(false); diff --git a/lib/src/widget/src/git_diffs.dart b/lib/src/widget/src/git_diffs.dart index 2b281c4..fd413ec 100644 --- a/lib/src/widget/src/git_diffs.dart +++ b/lib/src/widget/src/git_diffs.dart @@ -13,22 +13,27 @@ void printGitDiffs(String testDescription, String differences) { /// 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 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 : ''; + 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 prefixes) => prefixes.any((prefix) => line.startsWith(prefix)); + bool startsWithAny(String line, List prefixes) => + prefixes.any((prefix) => line.startsWith(prefix)); final List lines = multiLineString.split('\n'); - final List filteredLines = lines.where((line) => !startsWithAny(line, ['diff', 'index', '@@'])).toList(); + final List filteredLines = lines + .where((line) => !startsWithAny(line, ['diff', 'index', '@@'])) + .toList(); final String result = filteredLines.join('\n'); diff --git a/lib/src/widget/src/src.dart b/lib/src/widget/src/src.dart index b5f3c48..dd9cf7a 100644 --- a/lib/src/widget/src/src.dart +++ b/lib/src/widget/src/src.dart @@ -3,19 +3,18 @@ import 'dart:async'; import 'dart:io'; +import 'package:approval_tests/approval_tests.dart'; import 'package:approval_tests/src/widget/src/common.dart'; import 'package:approval_tests/src/widget/src/get_widget_names.dart'; -import 'package:approval_tests/src/widget/src/git_diffs.dart'; -import 'package:approval_tests/src/widget/src/widget_meta/collect_widgets_meta_data.dart' as wmd; +import 'package:approval_tests/src/widget/src/widget_meta/collect_widgets_meta_data.dart' + as wmd; import 'package:flutter_test/flutter_test.dart'; Set? _widgetNames; -const _approvedExtension = 'approved.txt'; -const _unapprovedExtension = 'unapproved.txt'; final _executedApprovedFullPaths = {}; bool _allTestsPassed = true; -class Approved { +class ApprovalWidgets { /// Initializes the approval test by building a database of project classes. /// /// Typically called from within flutter_tests function 'setUpAll' @@ -37,15 +36,23 @@ class Approved { final testPath = _testFilePath(); final testDirectory = Directory(testPath); - final approvedFullPaths = testDirectory.filesWithExtension('.$_approvedExtension').map((file) => file.path).toSet(); - final unapprovedFullPaths = - testDirectory.filesWithExtension('.$_unapprovedExtension').map((file) => file.path).toSet(); + final approvedFullPaths = testDirectory + .filesWithExtension('.${Namer.approvedExtension}') + .map((file) => file.path) + .toSet(); + final unapprovedFullPaths = testDirectory + .filesWithExtension('.${Namer.receivedExtension}') + .map((file) => file.path) + .toSet(); for (final approvedFullPath in _executedApprovedFullPaths) { if (approvedFullPaths.contains(approvedFullPath)) { approvedFullPaths.remove(approvedFullPath); } - final unapprovedFullPath = approvedFullPath.replaceAll(_approvedExtension, _unapprovedExtension); + final unapprovedFullPath = approvedFullPath.replaceAll( + Namer.approvedExtension, + Namer.receivedExtension, + ); if (unapprovedFullPaths.contains(unapprovedFullPath)) { unapprovedFullPaths.remove(unapprovedFullPath); } @@ -75,62 +82,11 @@ topBar static Set? get widgetNames => _widgetNames; } -Future approvalTest( - String testDescription, - String textForReview, -) async { - try { - final String outputPath = _testFilePath(); - - final approvedFullPath = '$outputPath/$testDescription.$_approvedExtension'; - final currentFullPath = '$outputPath/$testDescription.$_unapprovedExtension'; - String approvedText = ''; - - if (_executedApprovedFullPaths.contains(approvedFullPath)) { - _allTestsPassed = false; - throw Exception(''' -$topBar - An approvalTest with description '$testDescription' was already created in path '$outputPath'. - Try adding a unique description to approvedTest. E.g., - - await tester.approvalTest('my unique description'); -$bottomBar'''); - } - - _executedApprovedFullPaths.add(approvedFullPath); - - final approvedFile = File(approvedFullPath); - final currentFile = File(currentFullPath); - - if (approvedFile.existsSync()) { - approvedText = approvedFile.readAsStringSync(); - } else { - approvedFile.writeAsStringSync(approvedText.endWithNewline); - } - - if (approvedText == textForReview.endWithNewline) { - if (currentFile.existsSync()) { - currentFile.deleteSync(); - } - } else { - currentFile.writeAsStringSync(textForReview.endWithNewline); - - final differences = gitDiffFiles(approvedFile, currentFile); - - if (differences.isNotEmpty) { - _allTestsPassed = false; - printGitDiffs(testDescription, differences); - throw Exception("Approval test '$testDescription' failed. The file diff is listed above."); - } - } - } catch (e) { - print(e.toString()); - rethrow; - } -} - /// [_globalApprovalTest] resolves the name conflict with [WidgetTester.approvalTest] -Future Function(String, String) _globalApprovalTest = approvalTest; +Future Function(String, String) _globalApprovalTest = + (description, value) async { + Approvals.verify(value); +}; extension WidgetTesterApprovedExtension on WidgetTester { /// Returns the meta data for the widgets for comparison during the approval test @@ -151,7 +107,7 @@ $bottomBar'''); this, outputMeta: true, verbose: false, - widgetNames: Approved.widgetNames, + widgetNames: ApprovalWidgets.widgetNames, ) .then((stringList) { completer.complete(stringList.join('\n')); @@ -164,10 +120,14 @@ $bottomBar'''); /// /// [description] is the name of the test. It is appended to the description in [Tester]. /// [textForReview] is the meta data text used in the approval test. - Future approvalTest([String? description, String? textForReview]) async { + Future approvalTest([ + String? description, + String? textForReview, + ]) async { final resultCompleter = Completer(); final widgetsMetaCompleter = Completer(); - final String updatedTestDescription = description == null ? testDescription : '$testDescription $description'; + final String updatedTestDescription = + description == null ? testDescription : '$testDescription $description'; // Get the test path before the stack gets too deep. _testFilePath(); @@ -181,7 +141,8 @@ $bottomBar'''); widgetsMetaCompleter.complete(textForReview); } await widgetsMetaCompleter.future.then((value) { - resultCompleter.complete(_globalApprovalTest(updatedTestDescription, value)); + resultCompleter + .complete(_globalApprovalTest(updatedTestDescription, value)); }); return resultCompleter.future; } @@ -204,13 +165,16 @@ String _testFilePath() { final stackTrace = StackTrace.current; final lines = stackTrace.toString().split('\n'); - final pathLine = lines.firstWhere((line) => line.contains('_test.dart'), orElse: () => ''); + final pathLine = + lines.firstWhere((line) => line.contains('_test.dart'), orElse: () => ''); if (pathLine.isNotEmpty) { final match = RegExp(r'\(file:\/\/(.*\/)').firstMatch(pathLine); if (match != null && match.groupCount > 0) { result = Uri.parse(match.group(1)!).toFilePath(); - result = result.endsWith('/') ? result.substring(0, result.length - 1) : result; + result = result.endsWith('/') + ? result.substring(0, result.length - 1) + : result; } } diff --git a/lib/src/widget/src/widget_meta/collect_widgets_meta_data.dart b/lib/src/widget/src/widget_meta/collect_widgets_meta_data.dart index c2f1993..59059f8 100644 --- a/lib/src/widget/src/widget_meta/collect_widgets_meta_data.dart +++ b/lib/src/widget/src/widget_meta/collect_widgets_meta_data.dart @@ -8,7 +8,8 @@ import 'package:flutter_test/flutter_test.dart'; Set registeredNames = {}; -const String instructions = '/// Replace your call to generateExpects with the code below.'; +const String instructions = + '/// Replace your call to generateExpects with the code below.'; List _previousWidgetMetas = []; List _previousExpectStrings = []; bool _isEnStringReverseLookupLoaded = false; @@ -127,7 +128,8 @@ Future> collectWidgetsMetaData( tester: tester, verbose: verbose, silent: silent, - outputType: (outputMeta ?? false) ? OutputType.widgetMeta : OutputType.expects, + outputType: + (outputMeta ?? false) ? OutputType.widgetMeta : OutputType.expects, ), ); } @@ -145,17 +147,22 @@ Future> _generateExpectsForWidgets( final text = []; final currentWidgetMetas = _widgetMetasFromWidgets(widgets); - final deltaWidgetMetas = _getDeltaWidgetMetas(currentWidgetMetas, _previousWidgetMetas); + final deltaWidgetMetas = + _getDeltaWidgetMetas(currentWidgetMetas, _previousWidgetMetas); currentWidgetMetas.addAll(deltaWidgetMetas); - final currentExpectStrings = _outputStringsFromWidgetMetas(currentWidgetMetas, outputType, verbose); - final deltaExpectStrings = _getDeltaExpectStrings(currentExpectStrings, _previousExpectStrings); + final currentExpectStrings = + _outputStringsFromWidgetMetas(currentWidgetMetas, outputType, verbose); + final deltaExpectStrings = + _getDeltaExpectStrings(currentExpectStrings, _previousExpectStrings); if (!silent) { if (deltaExpectStrings.isEmpty) { if (_previousWidgetMetas.isEmpty) { text.add('/// No widget with keys or custom types found to test'); } else { - text.add("/// No changes to widget with keys or custom types since the prior call to 'generateExpects'"); + text.add( + "/// No changes to widget with keys or custom types since the prior call to 'generateExpects'", + ); } } else { if (verbose && outputType == OutputType.expects) { @@ -171,8 +178,13 @@ Future> _generateExpectsForWidgets( return text; } -List _getDeltaExpectStrings(List currentExpectStrings, List previousExpectStrings) { - final deltaExpectStrings = currentExpectStrings.where((item) => !previousExpectStrings.contains(item)).toList(); +List _getDeltaExpectStrings( + List currentExpectStrings, + List previousExpectStrings, +) { + final deltaExpectStrings = currentExpectStrings + .where((item) => !previousExpectStrings.contains(item)) + .toList(); return deltaExpectStrings; } @@ -196,7 +208,8 @@ List _widgetMetasFromWidgets(List widgets) { return widgetMetas; } -bool _isProperlyFormattedKey(Widget widget) => widget.key.toString().indexOf('[<') == 0; +bool _isProperlyFormattedKey(Widget widget) => + widget.key.toString().indexOf('[<') == 0; /// The order to output expects enum _ExpectTypeOrder { @@ -243,7 +256,9 @@ List _outputStringsFromWidgetMetas( if (!generatedNonIntlTextComment && sortOrder(expectMeta) == 2) { generatedNonIntlTextComment = true; if (verbose) { - result.add('\t// No reverse lookup found for the text in the expect statements below'); + result.add( + '\t// No reverse lookup found for the text in the expect statements below', + ); } } @@ -266,12 +281,18 @@ void _outputText(List strings) { } } -List _getDeltaWidgetMetas(List currentWidgetMetas, List previousWidgetMetas) { - final deltaPreviousWidgetMetas = previousWidgetMetas.where((item) => !currentWidgetMetas.contains(item)).toList(); +List _getDeltaWidgetMetas( + List currentWidgetMetas, + List previousWidgetMetas, +) { + final deltaPreviousWidgetMetas = previousWidgetMetas + .where((item) => !currentWidgetMetas.contains(item)) + .toList(); // Matchers may have changed for the previous tests (e.g., findsOneWidget may now be findNothing), so update - final updatedDeltaPreviousWidgetMetas = - deltaPreviousWidgetMetas.map((widgetMeta) => WidgetMeta(widget: widgetMeta.widget)).toList(); + final updatedDeltaPreviousWidgetMetas = deltaPreviousWidgetMetas + .map((widgetMeta) => WidgetMeta(widget: widgetMeta.widget)) + .toList(); return updatedDeltaPreviousWidgetMetas; } @@ -302,7 +323,9 @@ List _getWidgetsForExpects( if (isEmptyTextWidget(widget)) { result = false; } else { - result = (widget.key != null && (widget.key.toString().isCustomString || widget.key.toString().isEnumString)) || + result = (widget.key != null && + (widget.key.toString().isCustomString || + widget.key.toString().isEnumString)) || registeredTypes.contains(widget.runtimeType) || widgetNames.contains(widget.runtimeType.toString()) || WidgetMeta.isTextEnabled(widget); @@ -323,13 +346,20 @@ List _expectStringsFromExpectMeta(ExpectMeta expectMeta) { final expects = []; // Number of attributes (e.g., Type, key, text) to match in expect - final int attributesToMatchCount = (expectMeta.widgetMeta.keyString.isNotEmpty ? 1 : 0) + - (expectMeta.widgetMeta.widgetText.isNotEmpty ? 1 : 0) + - (expectMeta.widgetMeta.isWidgetTypeRegistered ? 1 : 0); + final int attributesToMatchCount = + (expectMeta.widgetMeta.keyString.isNotEmpty ? 1 : 0) + + (expectMeta.widgetMeta.widgetText.isNotEmpty ? 1 : 0) + + (expectMeta.widgetMeta.isWidgetTypeRegistered ? 1 : 0); if (attributesToMatchCount >= 1) { - if (_haveEnString(expectMeta.widgetMeta.widgetText) || attributesToMatchCount >= 2) { - expects.addAll(_generateExpectWidgets(expectMeta.widgetMeta, attributesToMatchCount)); + if (_haveEnString(expectMeta.widgetMeta.widgetText) || + attributesToMatchCount >= 2) { + expects.addAll( + _generateExpectWidgets( + expectMeta.widgetMeta, + attributesToMatchCount, + ), + ); } else { expects.add(_generateExpect(expectMeta.widgetMeta)); } @@ -354,14 +384,17 @@ String _generateExpect(WidgetMeta widgetMeta) { generatedExpect = '\texpect(find.byKey(const ValueKey(${widgetMeta.keyString})), ${widgetMeta.matcherType.matcherName});'; } else if (widgetMeta.keyType == KeyType.stringValueKey) { - generatedExpect = '\texpect(find.byKey(${widgetMeta.keyString}), ${widgetMeta.matcherType.matcherName});'; + generatedExpect = + '\texpect(find.byKey(${widgetMeta.keyString}), ${widgetMeta.matcherType.matcherName});'; } else { throw Exception('Unexpected keyType'); } } else if (widgetMeta.widgetText.isNotEmpty) { - generatedExpect = "\texpect(find.text('${widgetMeta.widgetText}'), ${widgetMeta.matcherType.matcherName});"; + generatedExpect = + "\texpect(find.text('${widgetMeta.widgetText}'), ${widgetMeta.matcherType.matcherName});"; } else if (widgetMeta.isWidgetTypeRegistered) { - generatedExpect = '\texpect(find.byType(${widgetMeta.widgetType}), ${widgetMeta.matcherType.matcherName});'; + generatedExpect = + '\texpect(find.byType(${widgetMeta.widgetType}), ${widgetMeta.matcherType.matcherName});'; } else { generatedExpect = '(Internal error. Expect not generated.)'; } @@ -413,7 +446,8 @@ List _generateExpectWidgets( buffer.write(', matcher: ${widgetMeta.matcherType.matcherName},'); } - bool haveMoreAttributesToProcess() => ++attributesWrittenToBuffer < attributesToMatch; + bool haveMoreAttributesToProcess() => + ++attributesWrittenToBuffer < attributesToMatch; buffer.write('\ttester.expectWidget('); @@ -448,7 +482,9 @@ List _generateExpectWidgets( } else { final bufferString = buffer.toString(); if (intlKeys!.length > 1) { - result.add('\t// Multiple matches for "${widgetMeta.widgetText}" in string_en.json. Pick one.'); + result.add( + '\t// Multiple matches for "${widgetMeta.widgetText}" in string_en.json. Pick one.', + ); } for (final intlKey in intlKeys!) { result.add(bufferString.replaceAll(intlPlaceHolder, intlKey)); diff --git a/lib/src/widget/src/widget_meta/load_string_en.dart b/lib/src/widget/src/widget_meta/load_string_en.dart index 24956a2..03e476b 100644 --- a/lib/src/widget/src/widget_meta/load_string_en.dart +++ b/lib/src/widget/src/widget_meta/load_string_en.dart @@ -1,4 +1,7 @@ -Future>> loadEnStringReverseLookup(String path) async => >{}; +Future>> loadEnStringReverseLookup( + String path, +) async => + >{}; void addToReverseLookup({ required Map> reverseLookupMap, diff --git a/lib/src/widget/src/widget_meta/widget_meta.dart b/lib/src/widget/src/widget_meta/widget_meta.dart index 17c486b..f7c1ab5 100644 --- a/lib/src/widget/src/widget_meta/widget_meta.dart +++ b/lib/src/widget/src/widget_meta/widget_meta.dart @@ -11,17 +11,23 @@ class WidgetMeta { }) { _updateWidgetKey(); widgetType = widget.runtimeType; - isWidgetTypeRegistered = registeredTypes.contains(widgetType) || registeredNames.contains(widgetType.toString()); + isWidgetTypeRegistered = registeredTypes.contains(widgetType) || + registeredNames.contains(widgetType.toString()); _updateWidgetText(); _updateMatcher(); - assert(keyString.isNotEmpty || isWidgetTypeRegistered || widgetText.isNotEmpty, 'WidgetMeta widget is invalid'); + assert( + keyString.isNotEmpty || isWidgetTypeRegistered || widgetText.isNotEmpty, + 'WidgetMeta widget is invalid', + ); } @override bool operator ==(Object other) { if (other is WidgetMeta) { - return keyString == other.keyString && widgetText == other.widgetText && widgetType == other.widgetType; + return keyString == other.keyString && + widgetText == other.widgetText && + widgetType == other.widgetType; } else { return false; } @@ -111,7 +117,8 @@ class WidgetMeta { final strippedWidgetKey = originalWidgetKey.replaceAll("'", ''); final startIndex = strippedWidgetKey.indexOf('[<'); final endIndex = strippedWidgetKey.indexOf('>]'); - final trimmedWidgetKey = strippedWidgetKey.substring(startIndex + 2, endIndex); + final trimmedWidgetKey = + strippedWidgetKey.substring(startIndex + 2, endIndex); final words = trimmedWidgetKey.split(RegExp("__|_")); words.removeWhere((word) => word == ''); @@ -142,14 +149,17 @@ class WidgetMeta { } bool _isWidgetKeyProperlyFormatted(String originalWidgetKey) => - (originalWidgetKey.isCustomString || originalWidgetKey.isEnumString || originalWidgetKey.isValueKeyString) && + (originalWidgetKey.isCustomString || + originalWidgetKey.isEnumString || + originalWidgetKey.isValueKeyString) && originalWidgetKey.contains('[<') && originalWidgetKey.contains('>]'); } extension KeyString on String { bool get isCustomString => this.contains('__'); - bool get isEnumString => this.contains('_') == false && this.contains('.') == true; + bool get isEnumString => + this.contains('_') == false && this.contains('.') == true; bool get isValueKeyString => this.startsWith('[<') && this.endsWith('>]'); } diff --git a/lib/src/widget/src/widget_meta/widget_tester_extension.dart b/lib/src/widget/src/widget_meta/widget_tester_extension.dart index ab936b3..5375d7f 100644 --- a/lib/src/widget/src/widget_meta/widget_tester_extension.dart +++ b/lib/src/widget/src/widget_meta/widget_tester_extension.dart @@ -41,7 +41,8 @@ extension WidgetTesterExtension on WidgetTester { }) { assert(intl == null || text == null); - final Type? soughtType = key == null && widgetType == null ? Text : widgetType; + final Type? soughtType = + key == null && widgetType == null ? Text : widgetType; late Finder finder; @@ -77,7 +78,8 @@ extension WidgetTesterExtension on WidgetTester { Key? key, Matcher matcher = findsOneWidget, }) { - final Finder finder = findBy(intl: intl, text: text, widgetType: widgetType, key: key); + final Finder finder = + findBy(intl: intl, text: text, widgetType: widgetType, key: key); expect(finder, matcher); } @@ -89,7 +91,8 @@ extension WidgetTesterExtension on WidgetTester { Key? key, bool shouldPumpAndSettle = true, }) async { - final Finder finder = findBy(intl: intl, text: text, widgetType: widgetType, key: key); + final Finder finder = + findBy(intl: intl, text: text, widgetType: widgetType, key: key); expect(finder, findsOneWidget); await tap(finder); if (shouldPumpAndSettle) { diff --git a/test/approval_test.dart b/test/approval_test.dart index 57958a1..cc760a0 100644 --- a/test/approval_test.dart +++ b/test/approval_test.dart @@ -7,6 +7,7 @@ import 'groups/approval_tests.dart' as verify_tests; import 'groups/diff_tools_tests.dart' as diff_tools_tests; import 'groups/exception_tests.dart' as exception_tests; import 'groups/minor_tests.dart' as minor_tests; +import 'groups/widget_tests.dart' as widget_tests; import 'models/item.dart'; import 'queries/db_request_query.dart'; @@ -45,6 +46,10 @@ void main() { diff_tools_tests.main(); + /// ================== Approvals: widget tests ================== + + widget_tests.main(); + /// ================== Tear down ================== tearDownAll(() { diff --git a/test/approved/class_names.txt b/test/approved/class_names.txt new file mode 100644 index 0000000..e0eef0e --- /dev/null +++ b/test/approved/class_names.txt @@ -0,0 +1,38 @@ +# This file was autogenerated by package:approved. Please do not edit. +# Below is a list of class found in the project /lib folder. +FileComparator +Approvals +ApprovalWriter +ApprovalLogger +ApprovalScrubber +ApprovalUtils +ExecutableQuery +FilePathExtractor +ApprovalConverter +Reporter +IStackTraceFetcher +StackTraceFetcher +IPlatformWrapper +PlatformWrapper +ApprovalNamer +Options +Comparator +ExpectMeta +WidgetMeta +Approved +ScrubDates +ScrubNothing +ScrubWithRegEx +FileNamerOptions +Namer +ApprovalTextWriter +DoesntMatchException +NoDiffToolException +FileNotFoundException +DiffReporter +MacDiffTools +WindowsDiffTools +LinuxDiffTools +DiffInfo +GitReporter +CommandLineReporter diff --git a/test/groups/approval_tests.dart b/test/groups/approval_tests.dart index 230c5b5..9cf70cd 100644 --- a/test/groups/approval_tests.dart +++ b/test/groups/approval_tests.dart @@ -48,5 +48,11 @@ void main() { test("Query result", () async { await helper.verifyQuery(dbQuery, 'verify_query'); }); + + tearDownAll(() { + ApprovalLogger.log( + "$lines25 Group: Main verify tests methods are done $lines25", + ); + }); }); } diff --git a/test/groups/diff_tools_tests.dart b/test/groups/diff_tools_tests.dart index 65a126c..7250be1 100644 --- a/test/groups/diff_tools_tests.dart +++ b/test/groups/diff_tools_tests.dart @@ -93,5 +93,34 @@ void main() { "Test Passed: Successfully handled NoDiffToolException.", ); }); + + test('verify string with Git reporter', () { + const reporter = GitReporter(); + + // Setup: paths to existent files + const existentApprovedPath = + 'test/approved_files/approval_test.verify.approved.txt'; + const existentReceivedPath = + 'test/approved_files/approval_test.verify.received.txt'; + + // Expect an exception to be thrown + expect( + () => reporter.report( + existentApprovedPath, + existentReceivedPath, + ), + returnsNormally, + ); + + ApprovalLogger.success( + "Test Passed: Successfully handled 'normal' for Git reporter.", + ); + }); + + tearDownAll(() { + ApprovalLogger.log( + "$lines25 Group: Diff Tool tests are done $lines25", + ); + }); }); } diff --git a/test/groups/exception_tests.dart b/test/groups/exception_tests.dart index 4eb8954..06a60a4 100644 --- a/test/groups/exception_tests.dart +++ b/test/groups/exception_tests.dart @@ -39,5 +39,11 @@ void main() { "Test Passed: Successfully handled a log mismatch. Method «verify» correctly throws DoesntMatchException as expected.", ); }); + + tearDownAll(() { + ApprovalLogger.log( + "$lines25 Group: Exception tests are done $lines25", + ); + }); }); } diff --git a/test/groups/minor_tests.dart b/test/groups/minor_tests.dart index a9739e3..95440cb 100644 --- a/test/groups/minor_tests.dart +++ b/test/groups/minor_tests.dart @@ -187,5 +187,9 @@ void main() { ), ); }); + + tearDownAll(() { + ApprovalLogger.log("$lines25 Group: Minor tests are done $lines25"); + }); }); } diff --git a/test/groups/widget_tests.dart b/test/groups/widget_tests.dart new file mode 100644 index 0000000..337f2a5 --- /dev/null +++ b/test/groups/widget_tests.dart @@ -0,0 +1,55 @@ +import 'package:approval_tests/approval_tests.dart'; +import 'package:approval_tests/src/widget/src/src.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../approval_test.dart'; + +MaterialApp _buildApp(Widget widget) => MaterialApp( + home: MyCustomClass( + widget: widget, + ), + ); + +class MyCustomClass extends StatelessWidget { + const MyCustomClass({ + required this.widget, + super.key, + }); + + final Widget widget; + + @override + Widget build(BuildContext context) => Container( + child: widget, + ); +} + +enum MyEnumKeys { + myKeyName, +} + +void main() { + group('Widget tests', () { + setUpAll(() async { + await ApprovalWidgets.setUpAll(); + ApprovalLogger.log( + "$lines25 Group: Widget tests tests are starting $lines25", + ); + }); + + testWidgets('smoke test', (WidgetTester tester) async { + await tester.pumpWidget(_buildApp(const Text('Testing 1, 2, 3'))); + await tester.pumpAndSettle(); + + await tester.approvalTest(); + }); + + tearDownAll(() async { + await ApprovalWidgets.tearDownAll(); + ApprovalLogger.log( + "$lines25 Group: Widget tests tests are done $lines25", + ); + }); + }); +} diff --git a/test/groups/widget_tests.smoke_test.approved.txt b/test/groups/widget_tests.smoke_test.approved.txt new file mode 100644 index 0000000..9e1664e --- /dev/null +++ b/test/groups/widget_tests.smoke_test.approved.txt @@ -0,0 +1 @@ +Text: {text: 'Testing 1, 2, 3, 4', count: 1} \ No newline at end of file