Skip to content

Commit

Permalink
test: added golden tests
Browse files Browse the repository at this point in the history
  • Loading branch information
JideGuru committed Aug 7, 2024
1 parent 8476cb9 commit ab58269
Show file tree
Hide file tree
Showing 16 changed files with 326 additions and 68 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ jobs:
- name: Restore packages
run: flutter pub get
- name: Analyze
run: flutter analyze
run: flutter analyze
- name: Run Tests without coverage
run: flutter test
18 changes: 6 additions & 12 deletions lib/view/drawing_canvas/drawing_canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,11 @@ import 'package:flutter_drawing_board/view/drawing_canvas/models/sketch.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class DrawingCanvas extends HookWidget {
final double height;
final double width;
final ValueNotifier<Color> selectedColor;
final ValueNotifier<double> strokeSize;
final ValueNotifier<Image?> backgroundImage;
final ValueNotifier<double> eraserSize;
final ValueNotifier<DrawingMode> drawingMode;
final AnimationController sideBarController;
final ValueNotifier<Sketch?> currentSketch;
final ValueNotifier<List<Sketch>> allSketches;
final GlobalKey canvasGlobalKey;
Expand All @@ -24,13 +21,10 @@ class DrawingCanvas extends HookWidget {

const DrawingCanvas({
Key? key,
required this.height,
required this.width,
required this.selectedColor,
required this.strokeSize,
required this.eraserSize,
required this.drawingMode,
required this.sideBarController,
required this.currentSketch,
required this.allSketches,
required this.canvasGlobalKey,
Expand Down Expand Up @@ -114,16 +108,16 @@ class DrawingCanvas extends HookWidget {

Widget buildAllSketches(BuildContext context) {
return SizedBox(
height: height,
width: width,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: ValueListenableBuilder<List<Sketch>>(
valueListenable: allSketches,
builder: (context, sketches, _) {
return RepaintBoundary(
key: canvasGlobalKey,
child: Container(
height: height,
width: width,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: kCanvasColor,
child: CustomPaint(
painter: SketchPainter(
Expand All @@ -148,8 +142,8 @@ class DrawingCanvas extends HookWidget {
builder: (context, sketch, child) {
return RepaintBoundary(
child: SizedBox(
height: height,
width: width,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: CustomPaint(
painter: SketchPainter(
sketches: sketch == null ? [] : [sketch],
Expand Down
3 changes: 0 additions & 3 deletions lib/view/drawing_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,10 @@ class DrawingPage extends HookWidget {
width: double.maxFinite,
height: double.maxFinite,
child: DrawingCanvas(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
drawingMode: drawingMode,
selectedColor: selectedColor,
strokeSize: strokeSize,
eraserSize: eraserSize,
sideBarController: animationController,
currentSketch: currentSketch,
allSketches: allSketches,
canvasGlobalKey: canvasGlobalKey,
Expand Down
64 changes: 44 additions & 20 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -162,18 +162,18 @@ packages:
dependency: "direct main"
description:
name: flutter_colorpicker
sha256: "458a6ed8ea480eb16ff892aedb4b7092b2804affd7e046591fb03127e8d8ef8b"
sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
version: "1.1.0"
flutter_hooks:
dependency: "direct main"
description:
name: flutter_hooks
sha256: "2b202559a4ed3656bbb7aae9d8b335fb0037b23acc7ae3f377d1ba0b95c21aec"
sha256: cde36b12f7188c85286fba9b38cc5a902e7279f36dd676967106c041dc9dde70
url: "https://pub.dev"
source: hosted
version: "0.18.5+1"
version: "0.20.5"
flutter_lints:
dependency: "direct dev"
description:
Expand Down Expand Up @@ -312,6 +312,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.6.4"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.dev"
source: hosted
version: "10.0.4"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
lints:
dependency: transitive
description:
Expand All @@ -324,26 +348,26 @@ packages:
dependency: transitive
description:
name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
version: "0.12.16"
version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
url: "https://pub.dev"
source: hosted
version: "0.5.0"
version: "0.8.0"
meta:
dependency: transitive
description:
name: meta
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev"
source: hosted
version: "1.10.0"
version: "1.12.0"
mime:
dependency: transitive
description:
Expand All @@ -356,10 +380,10 @@ packages:
dependency: transitive
description:
name: path
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.8.3"
version: "1.9.0"
path_parsing:
dependency: transitive
description:
Expand Down Expand Up @@ -505,10 +529,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.dev"
source: hosted
version: "0.6.1"
version: "0.7.0"
typed_data:
dependency: transitive
description:
Expand Down Expand Up @@ -629,14 +653,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
web:
vm_service:
dependency: transitive
description:
name: web
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
name: vm_service
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev"
source: hosted
version: "0.3.0"
version: "14.2.1"
win32:
dependency: transitive
description:
Expand All @@ -662,5 +686,5 @@ packages:
source: hosted
version: "6.4.2"
sdks:
dart: ">=3.2.0-194.0.dev <4.0.0"
flutter: ">=3.13.0"
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
4 changes: 2 additions & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
flutter_colorpicker: ^1.0.3
flutter_hooks: ^0.18.5+1
flutter_colorpicker: ^1.1.0
flutter_hooks: ^0.20.5
flutter_svg: ^2.0.9
file_saver: ^0.2.9
font_awesome_flutter: ^10.6.0
Expand Down
34 changes: 34 additions & 0 deletions test/flutter_test_config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'dart:async';

import 'package:flutter_test/flutter_test.dart';

import 'util/local_file_comparator_with_threshold.dart';

/// Customise your threshold here
/// For example, the error threshold here is 0.5%
/// Golden tests will pass if the pixel difference is equal to or below 0.5%
const _kGoldenTestsThreshold = 0.5 / 100;

Future<void> testExecutable(FutureOr<void> Function() testMain) async {
if (goldenFileComparator is LocalFileComparator) {
final testUrl = (goldenFileComparator as LocalFileComparator).basedir;

goldenFileComparator = LocalFileComparatorWithThreshold(
// flutter_test's LocalFileComparator expects the test's URI to be passed
// as an argument, but it only uses it to parse the baseDir in order to
// obtain the directory where the golden tests will be placed.
// As such, we use the default `testUrl`, which is only the `baseDir` and
// append a generically named `test.dart` so that the `baseDir` is
// properly extracted.
Uri.parse('$testUrl/test.dart'),
_kGoldenTestsThreshold,
);
} else {
throw Exception(
'Expected `goldenFileComparator` to be of type `LocalFileComparator`, '
'but it is of type `${goldenFileComparator.runtimeType}`',
);
}

await testMain();
}
42 changes: 42 additions & 0 deletions test/util/local_file_comparator_with_threshold.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';

// source: https://stackoverflow.com/a/73048760/14052058

/// Works just like [LocalFileComparator] but includes a [threshold] that, when
/// exceeded, marks the test as a failure.
class LocalFileComparatorWithThreshold extends LocalFileComparator {
/// Threshold above which tests will be marked as failing.
/// Ranges from 0 to 1, both inclusive.
final double threshold;

LocalFileComparatorWithThreshold(super.testFile, this.threshold)
: assert(threshold >= 0 && threshold <= 1);

/// Copy of [LocalFileComparator]'s [compare] method, except for the fact that
/// it checks if the [ComparisonResult.diffPercent] is not greater than
/// [threshold] to decide whether this test is successful or a failure.
@override
Future<bool> compare(Uint8List imageBytes, Uri golden) async {
final result = await GoldenFileComparator.compareLists(
imageBytes,
await getGoldenBytes(golden),
);

if (!result.passed && result.diffPercent <= threshold) {
debugPrint(
'A difference of ${result.diffPercent * 100}% was found, but it is '
'acceptable since it is not greater than the threshold of '
'${threshold * 100}%',
);

return true;
}

if (!result.passed) {
final error = await generateFailureOutput(result, golden, basedir);
throw FlutterError(error);
}
return result.passed;
}
}
Loading

0 comments on commit ab58269

Please sign in to comment.