Skip to content

. r F highlight error text in command line reporter #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 17, 2024
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
2 changes: 1 addition & 1 deletion example/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ void main() {
Approvals.verifyAll(
[3, 5, 15],
options: const Options(
comparator: IDEComparator(),
reporter: DiffReporter(),
deleteReceivedFile: true,
),
processor: (items) => fizzBuzz(items).toString(),
Expand Down
3 changes: 2 additions & 1 deletion example/verify_methods/verify/verify_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import 'package:test/test.dart';

void main() {
test('verify method', () {
const String response = '{"result": "success", "data": {"id": 1, "name": "Item"}}';
const String response =
'{"result": "success", "data": {"id": 1, "name": "Item"}}';

Approvals.verify(
response,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ 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.
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
14 changes: 9 additions & 5 deletions lib/approval_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ part 'src/writers/approval_text_writer.dart';

part 'src/core/logger/logger.dart';
part 'src/core/utils/utils.dart';
part 'src/core/reporter.dart';
part 'src/core/options.dart';
part 'src/core/approval_writer.dart';
part 'src/core/enums/comporator_ide.dart';
Expand All @@ -27,12 +28,15 @@ part 'src/namer/namer.dart';
part 'src/core/approval_number.dart';
part 'src/namer/file_namer_options.dart';

part 'src/reporters/comparator.dart';
part 'src/reporters/command_line_comparator.dart';
part 'src/reporters/ide_comparator.dart';
part 'src/reporters/diff_info.dart';
part 'src/reporters/diff_tools.dart';
part 'src/core/comparator.dart';
part 'src/comparator/file_comparator.dart';
part 'src/reporters/diff_tool/diff_info.dart';
part 'src/reporters/diff_tool/diff_tools.dart';

part 'src/exceptions/doesnt_match_exception.dart';
part 'src/exceptions/command_line_comparator_exception.dart';
part 'src/exceptions/ide_comparator_exception.dart';

part 'src/reporters/command_line/command_line_reporter.dart';
part 'src/reporters/diff_tool/diff_tool_reporter.dart';
part 'src/reporters/command_line/colorize.dart';
15 changes: 7 additions & 8 deletions lib/src/approvals.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,19 @@ class Approvals {
}

// Check if received file matches the approved file
final bool isFilesMatch =
ApprovalUtils.filesMatch(namer.approved, namer.received);
final bool isFilesMatch = options.comparator.compare(
approvedPath: namer.approved,
receivedPath: namer.received,
isLogError: options.logErrors,
);

// Log results and throw exception if files do not match
if (!isFilesMatch) {
options.comparator.compare(
approvedPath: namer.approved,
receivedPath: namer.received,
isLogError: options.logErrors,
);
options.reporter.report(namer.approved, namer.received);
throw DoesntMatchException(
'Test failed: ${namer.approved} does not match ${namer.received}.\n - Approved file path: ${namer.approved}\n - Received file path: ${namer.received}',
);
} else if (isFilesMatch) {
} else {
if (options.logResults) {
ApprovalLogger.success(
'Test passed: [${namer.approvedFileName}] matches [${namer.receivedFileName}]\n- Approved file path: ${namer.approved}\n- Received file path: ${namer.received}',
Expand Down
31 changes: 31 additions & 0 deletions lib/src/comparator/file_comparator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
part of '../../approval_tests.dart';

/// A class `FileComparator` that implements the `Comparator` interface.
/// This class is used to compare the content of two files.
final class FileComparator implements Comparator {
const FileComparator();

@override
bool compare({
required String approvedPath,
required String receivedPath,
bool isLogError = true,
}) {
try {
final approved = ApprovalUtils.readFile(path: approvedPath)
.replaceAll('\r\n', '\n')
.trim();
final received = ApprovalUtils.readFile(path: receivedPath)
.replaceAll('\r\n', '\n')
.trim();

// Return true if contents of both files match exactly
return approved.compareTo(received) == 0;
} catch (e) {
if (isLogError) {
ApprovalLogger.exception(e.toString());
}
rethrow;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ abstract interface class Comparator {
const Comparator();

/// A method named `compare` for comparing two files.
Future<void> compare({
bool compare({
required String approvedPath,
required String receivedPath,
bool isLogError = true,
Expand Down
4 changes: 3 additions & 1 deletion lib/src/core/extensions/approval_string_extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ extension StringExtensions on String {
}) {
final regExp = RegExp(matchingPattern);
return replaceAllMapped(
regExp, (match) => replacementProvider(match.group(0)!),);
regExp,
(match) => replacementProvider(match.group(0)!),
);
}
}
6 changes: 5 additions & 1 deletion lib/src/core/options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ class Options {
/// A final variable `comparator` of type `Comparator` used to compare the approved and received files.
final Comparator comparator;

/// A final variable `reporter` of type `Reporter` used to report the comparison results.
final Reporter reporter;

/// A final bool variable `approveResult` used to determine if the result should be approved after the test.
final bool approveResult;

Expand All @@ -27,7 +30,8 @@ class Options {
const Options({
this.scrubber = const ScrubNothing(),
this.approveResult = false,
this.comparator = const CommandLineComparator(),
this.comparator = const FileComparator(),
this.reporter = const CommandLineReporter(),
this.deleteReceivedFile = false,
this.namer,
this.logErrors = true,
Expand Down
6 changes: 6 additions & 0 deletions lib/src/core/reporter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
part of '../../approval_tests.dart';

/// `Reporter` is an abstract class for reporting the comparison results.
abstract interface class Reporter {
void report(String approvedPath, String receivedPath);
}
18 changes: 0 additions & 18 deletions lib/src/core/utils/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,24 +54,6 @@ final class ApprovalUtils {

static String lines(int count) => List.filled(count, '=').join();

// Helper private method to check if contents of two files match
static bool filesMatch(String approvedPath, String receivedPath) {
try {
// Read contents of the approved and received files
final approved = ApprovalUtils.readFile(path: approvedPath)
.replaceAll('\r\n', '\n')
.trim();
final received = ApprovalUtils.readFile(path: receivedPath)
.replaceAll('\r\n', '\n')
.trim();

// Return true if contents of both files match exactly
return approved.compareTo(received) == 0;
} catch (_) {
rethrow;
}
}

static void deleteFile(String path) {
try {
final File file = File(path);
Expand Down
19 changes: 19 additions & 0 deletions lib/src/reporters/command_line/colorize.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
part of '../../../approval_tests.dart';

enum _LogColorStyles { red, bgRed }

class _Colorize {
static const String esc = "\u{1B}[";
final String text;

const _Colorize(this.text);

_Colorize apply(_LogColorStyles style) {
final appliedText =
"$esc${style == _LogColorStyles.red ? '38;2;222;121;121' : '41'}m$text${esc}0m";
return _Colorize(appliedText);
}

@override
String toString() => text;
}
60 changes: 60 additions & 0 deletions lib/src/reporters/command_line/command_line_reporter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
part of '../../../approval_tests.dart';

/// `CommandLineReporter` is a class for reporting the comparison results using the command line.
class CommandLineReporter implements Reporter {
const CommandLineReporter();

@override
void report(String approvedPath, String receivedPath, {String? message}) {
try {
final String approvedContent = ApprovalUtils.readFile(path: approvedPath);
final String receivedContent = ApprovalUtils.readFile(path: receivedPath);

final StringBuffer buffer = StringBuffer(message ?? "Differences:\n");
final List<String> approvedLines = approvedContent.split('\n');
final List<String> receivedLines = receivedContent.split('\n');

final int maxLines = max(approvedLines.length, receivedLines.length);
for (int i = 0; i < maxLines; i++) {
final String approvedLine =
i < approvedLines.length ? approvedLines[i] : "";
final String receivedLine =
i < receivedLines.length ? receivedLines[i] : "";

if (approvedLine != receivedLine) {
buffer.writeln(
'${ApprovalUtils.lines(20)} Difference at line ${i + 1} ${ApprovalUtils.lines(20)}',
);
buffer.writeln(
'Approved file: ${_highlightDifference(approvedLine, receivedLine)}',
);
buffer.writeln(
'Received file: ${_highlightDifference(receivedLine, approvedLine)}',
);
}
}

if (buffer.isNotEmpty) {
final String reportMessage = buffer.toString();
ApprovalLogger.exception(reportMessage);
}
} catch (e) {
rethrow;
}
}

String _highlightDifference(String line1, String line2) {
final int minLength = min(line1.length, line2.length);
final StringBuffer highlighted = StringBuffer();

for (int i = 0; i < minLength; i++) {
if (line1[i] != line2[i]) {
highlighted.write(_Colorize(line1[i]).apply(_LogColorStyles.bgRed));
} else {
highlighted.write(_Colorize(line1[i]).apply(_LogColorStyles.red));
}
}

return highlighted.toString();
}
}
50 changes: 0 additions & 50 deletions lib/src/reporters/command_line_comparator.dart

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
part of '../../../approval_tests.dart';
part of '../../../../approval_tests.dart';

/// `DiffInfo` is a class for storing information about a diff tool.
class DiffInfo {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,27 @@
part of '../../approval_tests.dart';
part of '../../../approval_tests.dart';

/// `IDEComparator` is a class for comparing files using an `IDE`.
///
/// Available IDEs:
/// - `Visual Studio Code`
/// - `Android Studio`
final class IDEComparator extends Comparator {
/// `DiffReporter` is a class for reporting the comparison results using a `Diff Tool`.
class DiffReporter implements Reporter {
final ComparatorIDE ide;
final DiffInfo? customDiffInfo;

const IDEComparator({
const DiffReporter({
this.ide = ComparatorIDE.vsCode,
this.customDiffInfo,
});

@override
Future<void> compare({
required String approvedPath,
required String receivedPath,
bool isLogError = true,
}) async {
Future<void> report(String approvedPath, String receivedPath) async {
final DiffInfo diffInfo = _diffInfo;
try {
final File approvedFile = File(approvedPath);
final File receivedFile = File(receivedPath);

if (!_fileExists(approvedFile) || !_fileExists(receivedFile)) {
_throwFileException(
'Files not found for comparison. Please check the paths: \n\n Approved file path: $approvedPath, \n\n Received file path: $receivedPath.',
);
}
final DiffInfo diffInfo = _diffInfo;

await Process.run(
diffInfo.command,
[diffInfo.arg, approvedPath, receivedPath],
);
} catch (e, st) {
throw IDEComparatorException(
message:
'Error during comparison via ${ide.name}. Please try restart your IDE.',
'Error during comparison via ${ide.name}. Please try check path to IDE. \n Current path: ${diffInfo.command}.',
exception: e,
stackTrace: st,
);
Expand Down Expand Up @@ -67,14 +50,4 @@ final class IDEComparator extends Comparator {
}
}
}

bool _fileExists(File file) => file.existsSync();

void _throwFileException(String message) {
throw IDEComparatorException(
message: message,
exception: null,
stackTrace: StackTrace.current,
);
}
}
Loading
Loading