Skip to content

Commit

Permalink
@ t d updated docs, tests
Browse files Browse the repository at this point in the history
  • Loading branch information
yelmuratoff committed Jun 30, 2024
1 parent 4cea675 commit 14535a3
Show file tree
Hide file tree
Showing 29 changed files with 299 additions and 56 deletions.
25 changes: 19 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ ApprovalTests is designed for two level: Dart and Flutter. <br>

## 📋 How it works

- If the changed results match the approved file perfectly, the test passes.
- If there's a difference, a reporter tool will highlight the mismatch and the test fails.
- The first run of the test automatically creates an `approved` file if there is no such file.
- If the changed results match the `approved` file perfectly, the test passes.
- If there's a difference, a `reporter` tool will highlight the mismatch and the test fails.
- If the test is passed, the `received` file is deleted automatically. You can change this by changing the `deleteReceivedFile` value in `options`. If the test fails, the received file remains for analysis.

## 📦 Installation

Expand All @@ -57,7 +59,7 @@ Add the following to your `pubspec.yaml` file:
```yaml
dependencies:
approval_tests: ^1.1.0
approval_tests_flutter: ^1.1.0 # if you need
approval_tests_flutter: ^1.1.0 # If you need. This package is needed for widget and integration tests.
```
## 👀 Getting Started
Expand All @@ -75,13 +77,13 @@ It comes ready with:

## 📚 How to use

In order to use Approval Tests, the user needs to:
In order to use `Approval Tests`, the user needs to:

1. Set up a test: This involves importing the Approval Tests library into your own code.

2. Optionally, set up a reporter: Reporters are tools that highlight differences between approved and received files when a test fails. Although not necessary, they make it significantly easier to see what changes have caused a test to fail. The default reporter is the `CommandLineReporter`. You can also use the `DiffReporter` to compare the files in your IDE.
2. Optionally, set up a reporter: Reporters are tools that highlight differences between approved and received files when a test fails. Although not necessary, they make it significantly easier to see what changes have caused a test to fail. The default reporter is the `CommandLineReporter`. You can also use the `DiffReporter` to compare the files in your IDE, and the `GitReporter` to see the differences in the `Git GUI`.

3. Manage the "approved" file: When the test is run for the first time, an approved file is created automatically. This file will represent the expected outcome. Once the test results in a favorable outcome, the approved file should be updated to reflect these changes. A little bit below I wrote how to do it.
3. Manage the `approved` file: When the test is run for the first time, an approved file is created automatically. This file will represent the expected outcome. Once the test results in a favorable outcome, the approved file should be updated to reflect these changes. A little bit below I wrote how to do it.

This setup is useful because it shortens feedback loops, saving developers time by only highlighting what has been altered rather than requiring them to parse through their entire output to see what effect their changes had.

Expand All @@ -95,6 +97,16 @@ We’ll provide more explanation in due course, but, briefly, here are the most
Most diff tools have the ability to move text from left to right, and save the result.
How to use diff tools is just below, there is a `Comparator` class for that.

#### • Via CLI command
You can run the command in a terminal to review your files:
```bash
dart run approval_tests:review
```
After running the command, the files will be analyzed and you will be asked to choose one of the options:
- `y` - Approve the received file.
- `n` - Reject the received file.
- `v`iew - View the differences between the received and approved files. After selecting `v` you will be asked which IDE you want to use to view the differences.

#### • Via approveResult property
If you want the result to be automatically saved after running the test, you need to use the `approveResult` property in `Options`:

Expand Down Expand Up @@ -177,6 +189,7 @@ You can study it to understand how to use the package to test complex code.
And the `verify_methods` folder has small examples of using different `ApprovalTests` methods for different cases.

### JSON example
With `verifyAsJson`, if you pass data models as `JsonItem`, with nested other models as `AnotherItem` and `SubItem`, then you need to add an `toJson` method to each model for the serialization to succeed.

<!-- snippet: same_verify_as_json_test_with_model -->
<a id='snippet-same_verify_as_json_test_with_model'></a>
Expand Down
2 changes: 1 addition & 1 deletion examples/dart_example/test/dart_example_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ void main() {
[3, 5, 15],
options: const Options(
reporter: DiffReporter(),
deleteReceivedFile: false,
deleteApprovedFile: true,
),
processor: (items) => fizzBuzz(items).toString(),
);
Expand Down

This file was deleted.

7 changes: 4 additions & 3 deletions packages/approval_tests/bin/review.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ Future<void> processFile(File approvedFile, FileSystemEntity reviewFile) async {
ComparatorIDE comparatorIDE = ComparatorIDE.vsCode;

if (resultString.isNotEmpty || resultString.isNotEmpty) {
final String fileNameWithoutExtension =
approvedFile.path.split('/').last.split('.').first;
GitReporter.printGitDiffs(fileNameWithoutExtension, resultString);
// final String fileNameWithoutExtension =
// approvedFile.path.split('/').last.split('.').first;
// GitReporter.printGitDiffs(fileNameWithoutExtension, resultString);
const CommandLineReporter().report(approvedFile.path, reviewFile.path);

String? firstCharacter;

Expand Down
1 change: 0 additions & 1 deletion packages/approval_tests/example/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ void main() {
[3, 5, 15],
options: const Options(
reporter: DiffReporter(),
deleteReceivedFile: true,
),
processor: (items) => fizzBuzz(items).toString(),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ void main() {

Approvals.verify(
response,
options: const Options(deleteReceivedFile: true),
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ void main() {
Approvals.verifyAll(
items,
processor: (item) => 'Item: $item',
options: const Options(deleteReceivedFile: true),
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ void main() {
Approvals.verifyAllCombinations(
inputs,
processor: (combination) => 'Combination: ${combination.join(", ")}',
options: const Options(deleteReceivedFile: true),
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ void main() {
};
Approvals.verifyAsJson(
complexObject,
options: const Options(deleteReceivedFile: true),
);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ void main() {
Approvals.verifyAsJson(
jsonItem,
options: const Options(
deleteReceivedFile:
true, // Automatically delete the received file after the test.
approveResult:
true, // Approve the result automatically. You can remove this property after the approved file is created.
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ void main() async {
test('verify db query', () async {
await Approvals.verifyQuery(
dbQuery,
options: const Options(deleteReceivedFile: true),
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ void main() async {
test('verify network query', () async {
await Approvals.verifyQuery(
query,
options: const Options(deleteReceivedFile: true),
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ void main() {

Approvals.verifySequence(
sequence,
options: const Options(deleteReceivedFile: true),
);
});
}
1 change: 1 addition & 0 deletions packages/approval_tests/lib/approval_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:convert';
import 'dart:io';
import 'dart:math';

import 'package:approval_tests/src/core/enums/file_type.dart';
import 'package:diff_match_patch2/diff_match_patch.dart';
import 'package:talker/talker.dart';
import 'package:test_api/src/backend/invoker.dart' show Invoker;
Expand Down
38 changes: 21 additions & 17 deletions packages/approval_tests/lib/src/approvals.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,33 +70,37 @@ class Approvals {
ApprovalLogger.success(
'Test passed: [${namer.approvedFileName}] matches [${namer.receivedFileName}]\n\n- Approved file path: ${namer.approved}\n\n- Received file path: ${namer.received}',
);
if (options.deleteReceivedFile) {
_deleteFileAfterTest(namer: namer, fileType: FileType.received);
}
if (options.deleteApprovedFile) {
_deleteFileAfterTest(namer: namer, fileType: FileType.approved);
}
}
}
} catch (e, st) {
if (options.logErrors) {
ApprovalLogger.exception(e, stackTrace: st);
}
if (options.deleteReceivedFile) {
_deleteFileAfterTest(namer);
}
rethrow;
} finally {
if (options.deleteReceivedFile) {
_deleteFileAfterTest(namer);
}
}
}

/// `_deleteFileAfterTest` method to delete the received file after the test.
static void _deleteFileAfterTest(ApprovalNamer? namer) {
if (namer != null) {
ApprovalUtils.deleteFile(namer.received);
} else {
ApprovalUtils.deleteFile(
Namer(filePath: filePathExtractor.filePath.split('.dart').first)
.received,
);
}
static void _deleteFileAfterTest({
required ApprovalNamer? namer,
required FileType fileType,
}) {
final fileToNamerMap = {
FileType.approved: (ApprovalNamer n) => n.approved,
FileType.received: (ApprovalNamer n) => n.received,
};

final filePath = (namer == null)
? Namer(filePath: filePathExtractor.filePath.split('.dart').first)
: namer;

ApprovalUtils.deleteFile(fileToNamerMap[fileType]!(filePath));
}

/// Verifies all combinations of inputs for a provided function.
Expand Down Expand Up @@ -127,7 +131,7 @@ class Approvals {
// Encode the object into JSON format
final jsonContent = ApprovalConverter.encodeReflectively(
encodable,
includeClassName: true,
includeClassName: options.includeClassNameDuringSerialization,
);
final prettyJson = ApprovalConverter.convert(jsonContent);

Expand Down
4 changes: 4 additions & 0 deletions packages/approval_tests/lib/src/core/enums/file_type.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
enum FileType {
approved,
received,
}
10 changes: 9 additions & 1 deletion packages/approval_tests/lib/src/core/options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,21 @@ class Options {
/// A final bool variable `logResults` used to determine if the results should be logged.
final bool logResults;

/// A final bool variable `includeClassNameDuringSerialization` used to determine if the class name should be included during serialization.
final bool includeClassNameDuringSerialization;

// A constructor for the class Options which initializes `isScrub` and `fileExtensionWithoutDot`.
const Options({
this.scrubber = const ScrubNothing(),
this.approveResult = false,
this.comparator = const FileComparator(),
this.reporter = const CommandLineReporter(),
this.deleteReceivedFile = false,
this.deleteReceivedFile = true,
this.deleteApprovedFile = false,
this.namer,
this.logErrors = true,
this.logResults = true,
this.includeClassNameDuringSerialization = true,
});

Options copyWith({
Expand All @@ -52,6 +56,7 @@ class Options {
Namer? namer,
bool? logErrors,
bool? logResults,
bool? includeClassNameDuringSerialization,
}) =>
Options(
scrubber: scrubber ?? this.scrubber,
Expand All @@ -63,5 +68,8 @@ class Options {
namer: namer ?? this.namer,
logErrors: logErrors ?? this.logErrors,
logResults: logResults ?? this.logResults,
includeClassNameDuringSerialization:
includeClassNameDuringSerialization ??
this.includeClassNameDuringSerialization,
);
}
19 changes: 17 additions & 2 deletions packages/approval_tests/lib/src/namer/file_namer_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,33 @@ final class FileNamerOptions {
final String folderPath;
final String fileName;
final String testName;
final String? description;

const FileNamerOptions({
required this.folderPath,
required this.fileName,
required this.testName,
required this.description,
});

String get approved => '$folderPath/$approvedFileName';

String get received => '$folderPath/$receivedFileName';

String get approvedFileName => '$fileName.$testName.approved.txt';
String get approvedFileName {
if (description != null) {
return '$fileName.$testName.$_updatedDescription.approved.txt';
}
return '$fileName.$testName.approved.txt';
}

String get receivedFileName => '$fileName.$testName.received.txt';
String get receivedFileName {
if (description != null) {
return '$fileName.$testName.$_updatedDescription.received.txt';
}
return '$fileName.$testName.received.txt';
}

String get _updatedDescription =>
description == null ? '' : description!.replaceAll(' ', '_');
}
7 changes: 4 additions & 3 deletions packages/approval_tests/lib/src/namer/namer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,12 @@ final class Namer implements ApprovalNamer {
@override
String get currentTestName {
final testName = Invoker.current?.liveTest.individualName;
return testName == null ? '' : testName.replaceAll(' ', '_');
return testName == null ? '' : testName.replaceAll(' ', '_').toLowerCase();
}

String get _updatedDescription =>
description == null ? '' : description!.replaceAll(' ', '_');
String get _updatedDescription => description == null
? ''
: description!.replaceAll(' ', '_').toLowerCase();

String get _fileName {
final path = filePath!;
Expand Down
21 changes: 12 additions & 9 deletions packages/approval_tests/lib/src/reporters/git.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ part of '../../approval_tests.dart';

/// `GitReporter` is a class for reporting the comparison results using the git.
class GitReporter implements Reporter {
const GitReporter();
final DiffInfo? customDiffInfo;
const GitReporter({
this.customDiffInfo,
});

@override
Future<void> report(String approvedPath, String receivedPath) async {
const DiffInfo diffInfo =
DiffInfo(name: "Git", command: 'git', arg: 'diff --no-index');
final DiffInfo diffInfo = customDiffInfo ??
const DiffInfo(name: "Git", command: 'git', arg: 'diff --no-index');

try {
await Future.wait([
Expand Down Expand Up @@ -74,10 +77,10 @@ class GitReporter implements Reporter {
return result;
}

static void printGitDiffs(String testDescription, String differences) {
ApprovalLogger.log(
"Results of git diff during approvalTest('$testDescription'):",
);
ApprovalLogger.log(differences.trim());
}
// static void printGitDiffs(String testDescription, String differences) {
// ApprovalLogger.log(
// "Results of git diff during approvalTest('$testDescription'):",
// );
// ApprovalLogger.log(differences.trim());
// }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"id": 1,
"name": "JsonItem",
"subItem": {
"id": 1,
"name": "SubItem",
"anotherItems": [
{
"id": 1,
"name": "AnotherItem 1"
},
{
"id": 2,
"name": "AnotherItem 2"
}
]
},
"anotherItem": {
"id": 1,
"name": "AnotherItem"
}
}
Loading

0 comments on commit 14535a3

Please sign in to comment.