Skip to content

Commit

Permalink
fix!: load asset images for tests (#25)
Browse files Browse the repository at this point in the history
* fix!: load asset images for tests

* feat: add precacheImages to pumps.dart

* test precacheImages

* fix doc comment

* use expectLater

* fix merge conflict

* fix test
  • Loading branch information
Kirpal authored Mar 10, 2022
1 parent 6e7f21d commit 405788c
Show file tree
Hide file tree
Showing 16 changed files with 302 additions and 40 deletions.
17 changes: 8 additions & 9 deletions lib/src/golden_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,9 @@ Future<void> loadFonts() async {
/// golden image under the `goldens` directory. This name should be unique, and
/// may not contain an extension (such as `.png`).
///
/// The provided [widget] describes the scenarios and layout of the widgets that
/// are included in the test. A child must be provided. Alchemist provides two
/// widgets to make creating a golden test scenario. See [GoldenTestGroup] and
/// [GoldenTestScenario] for more details.
/// The provided [builder] builds the widget under test.
/// Usually, it creates multiple scenarios using [GoldenTestGroup]
/// and [GoldenTestScenario].
///
/// The [description] must be a unique description for the test.
///
Expand Down Expand Up @@ -103,9 +102,9 @@ Future<void> loadFonts() async {
/// prime the widget tree before golden evaluation. By default, it is set to
/// [onlyPumpAndSettle], which will pump the widget tree and wait for all
/// scheduled frames to be completed, but can be overridden to customize the
/// pump behavior. For example, a button tap can be simulated using
/// `tester.tap(finder)`, after which the tester can be pumped and settled.
/// See [pumpOnce], [pumpNTimes] and [onlyPumpAndSettle] for more details.
/// pump behavior.
/// See [pumpOnce], [pumpNTimes], [onlyPumpAndSettle], and [precacheImages] for
/// more details.
///
/// The [whilePerforming] interaction, if provided, will be called with the
/// [WidgetTester] to perform a desired interaction during the golden test.
Expand All @@ -131,7 +130,7 @@ Future<void> goldenTest(
BoxConstraints constraints = const BoxConstraints(),
PumpAction pumpBeforeTest = onlyPumpAndSettle,
Interaction? whilePerforming,
required Widget widget,
required ValueGetter<Widget> builder,
}) async {
if (skip) return;

Expand Down Expand Up @@ -163,7 +162,7 @@ Future<void> goldenTest(
fileName,
goldensConfig.environmentName,
),
widget: widget,
widget: builder(),
forceUpdate: config.forceUpdateGoldenFiles,
obscureText: goldensConfig.obscureText,
renderShadows: goldensConfig.renderShadows,
Expand Down
5 changes: 5 additions & 0 deletions lib/src/golden_test_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:ui' as ui;

import 'package:alchemist/src/blocked_text_image.dart';
import 'package:alchemist/src/pumps.dart';
import 'package:alchemist/src/utilities.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
Expand Down Expand Up @@ -161,6 +162,7 @@ abstract class GoldenTestAdapter {
required BoxConstraints constraints,
required ThemeData theme,
required Widget widget,
required PumpAction pumpBeforeTest,
});

/// Generates an image of the widget at the given [finder] with all text
Expand Down Expand Up @@ -220,6 +222,7 @@ class FlutterGoldenTestAdapter extends GoldenTestAdapter {
required BoxConstraints constraints,
required ThemeData theme,
required Widget widget,
required PumpAction pumpBeforeTest,
}) async {
final initialSize = Size(
constraints.hasBoundedWidth ? constraints.maxWidth : 2000,
Expand Down Expand Up @@ -261,6 +264,8 @@ class FlutterGoldenTestAdapter extends GoldenTestAdapter {

final shouldTryResize = !constraints.isTight;

await pumpBeforeTest(tester);

if (shouldTryResize) {
final childSize = tester.getSize(find.byKey(childKey));
final newSize = Size(
Expand Down
3 changes: 1 addition & 2 deletions lib/src/golden_test_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class FlutterGoldenTestRunner extends GoldenTestRunner {
rootKey: rootKey,
textScaleFactor: textScaleFactor,
constraints: constraints,
pumpBeforeTest: pumpBeforeTest,
theme: themeData.copyWith(
textTheme: obscureText
? themeData.textTheme.apply(
Expand All @@ -94,8 +95,6 @@ class FlutterGoldenTestRunner extends GoldenTestRunner {
widget: widget,
);

await pumpBeforeTest(tester);

AsyncCallback? cleanup;
if (whilePerforming != null) {
cleanup = await whilePerforming(tester);
Expand Down
32 changes: 32 additions & 0 deletions lib/src/pumps.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:alchemist/alchemist.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';

/// A function that may perform pumping actions to prime a golden test.
Expand Down Expand Up @@ -30,3 +31,34 @@ final pumpOnce = pumpNTimes(1);
///
/// See [PumpAction] for more details.
Future<void> onlyPumpAndSettle(WidgetTester tester) => tester.pumpAndSettle();

/// A custom pump action to ensure that the images for all [Image],
/// [FadeInImage], and [DecoratedBox] widgets are loaded before the golden file
/// is generated.
///
/// See [PumpAction] for more details.
Future<void> precacheImages(WidgetTester tester) async {
await tester.runAsync(() async {
final images = <Future<void>>[];
for (final element in find.byType(Image).evaluate()) {
final widget = element.widget as Image;
final image = widget.image;
images.add(precacheImage(image, element));
}
for (final element in find.byType(FadeInImage).evaluate()) {
final widget = element.widget as FadeInImage;
final image = widget.image;
images.add(precacheImage(image, element));
}
for (final element in find.byType(DecoratedBox).evaluate()) {
final widget = element.widget as DecoratedBox;
final decoration = widget.decoration;
if (decoration is BoxDecoration && decoration.image != null) {
final image = decoration.image!.image;
images.add(precacheImage(image, element));
}
}
await Future.wait(images);
});
await tester.pumpAndSettle();
}
3 changes: 2 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ dependencies:
meta: ^1.3.0

dev_dependencies:
mocktail: ^0.1.3
mocktail: ^0.3.0
mocktail_image_network: ^0.3.1
test: ^1.17.12
very_good_analysis: 2.4.0

Expand Down
22 changes: 22 additions & 0 deletions test/helpers/fake_test_asset_bundle.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'dart:convert';
import 'dart:typed_data';

import 'package:alchemist/src/utilities.dart';

final redPixelImage = base64Decode(
'iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAApElEQVR42u3RAQ0AAAjD'
'MO5fNCCDkC5z0HTVrisFCBABASIgQAQEiIAAAQJEQIAICBABASIgQAREQIAICBABASIg'
'QAREQIAICBABASIgQAREQIAICBABASIgQAREQIAICBABASIgQAREQIAICBABASIgQARE'
'QIAICBABASIgQAREQIAICBABASIgQAREQIAICBABASIgQAQECBAgAgJEQIAIyPcGFY7H'
'nV2aPXoAAAAASUVORK5CYII=',
);

class FakeTestAssetBundle extends TestAssetBundle {
@override
Future<ByteData> load(String key) async {
if (key.endsWith('png')) {
return ByteData.view(redPixelImage.buffer);
}
return super.load(key);
}
}
1 change: 1 addition & 0 deletions test/helpers/helpers.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'fake_test_asset_bundle.dart';
20 changes: 20 additions & 0 deletions test/smoke_tests/asset_image_smoke_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:alchemist/src/golden_test.dart';
import 'package:alchemist/src/pumps.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import '../helpers/helpers.dart';

void main() {
group('smoke test', () {
goldenTest(
'succeeds with an asset image',
fileName: 'asset_image_smoke_test',
pumpBeforeTest: precacheImages,
builder: () => DefaultAssetBundle(
bundle: FakeTestAssetBundle(),
child: Image.asset('test.png'),
),
);
});
}
2 changes: 1 addition & 1 deletion test/smoke_tests/composited_transform_smoke_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ void main() {
goldenTest(
'succeeds with CompositedTransformFollower',
fileName: 'composited_transform_smoke_test',
widget: _SmokeTest(),
builder: _SmokeTest.new,
);
});
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions test/smoke_tests/interactions_smoke_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,21 @@ void main() {
goldenTest(
'succeeds in regular state',
fileName: 'interactions_smoke_test_regular',
widget: buildSmokeTestGroup(),
builder: buildSmokeTestGroup,
);

goldenTest(
'succeeds while pressed',
fileName: 'interactions_smoke_test_pressed',
whilePerforming: press(find.byType(ElevatedButton)),
widget: buildSmokeTestGroup(),
builder: buildSmokeTestGroup,
);

goldenTest(
'succeeds while long pressed',
fileName: 'interactions_smoke_test_long_pressed',
whilePerforming: longPress(find.byType(ElevatedButton)),
widget: buildSmokeTestGroup(),
builder: buildSmokeTestGroup,
);
});
}
2 changes: 1 addition & 1 deletion test/smoke_tests/timer_button_smoke_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void main() {
await tester.tap(find.byType(TimerButton));
await tester.pumpAndSettle();
},
widget: const TimerButton(),
builder: () => const TimerButton(),
);
});
}
24 changes: 23 additions & 1 deletion test/src/golden_test_adapter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:alchemist/src/blocked_text_image.dart';
import 'package:alchemist/src/golden_test_adapter.dart';
import 'package:alchemist/src/golden_test_group.dart';
import 'package:alchemist/src/golden_test_scenario.dart';
import 'package:alchemist/src/pumps.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
Expand All @@ -20,7 +21,7 @@ class MockRenderObject extends Mock implements RenderObject {

void main() {
setUpAll(() {
registerFallbackValue<RenderObject>(MockRenderObject());
registerFallbackValue(MockRenderObject());
});
group('overrides', () {
group('goldenFileExpectationFn', () {
Expand Down Expand Up @@ -213,6 +214,7 @@ void main() {
textScaleFactor: 1,
constraints: const BoxConstraints(),
theme: ThemeData.light(),
pumpBeforeTest: onlyPumpAndSettle,
widget: buildGroup(),
);

Expand All @@ -233,6 +235,7 @@ void main() {
textScaleFactor: 1,
constraints: BoxConstraints.tight(providedSize),
theme: ThemeData.light(),
pumpBeforeTest: onlyPumpAndSettle,
widget: buildGroup(),
);

Expand All @@ -256,6 +259,7 @@ void main() {
textScaleFactor: 1,
constraints: const BoxConstraints(),
theme: ThemeData.light(),
pumpBeforeTest: onlyPumpAndSettle,
widget: buildGroup(),
);

Expand Down Expand Up @@ -286,6 +290,7 @@ void main() {
minHeight: minSize.height,
),
theme: ThemeData.light(),
pumpBeforeTest: onlyPumpAndSettle,
widget: buildGroup(),
);

Expand Down Expand Up @@ -326,6 +331,7 @@ void main() {
maxHeight: maxSize.height,
),
theme: ThemeData.light(),
pumpBeforeTest: onlyPumpAndSettle,
widget: buildGroup(),
);

Expand All @@ -342,6 +348,7 @@ void main() {
textScaleFactor: 2,
constraints: const BoxConstraints(),
theme: ThemeData.light(),
pumpBeforeTest: onlyPumpAndSettle,
widget: buildGroup(),
);

Expand All @@ -366,6 +373,7 @@ void main() {
textScaleFactor: 1,
constraints: const BoxConstraints(),
theme: theme,
pumpBeforeTest: onlyPumpAndSettle,
widget: buildGroup(),
);

Expand Down Expand Up @@ -397,6 +405,20 @@ void main() {
);
},
);

testWidgets('calls the provided pumpBeforeTest', (tester) async {
var pumpBeforeTestCalled = false;
await adapter.pumpGoldenTest(
tester: tester,
textScaleFactor: 2,
constraints: const BoxConstraints(),
theme: ThemeData.light(),
pumpBeforeTest: (_) async => pumpBeforeTestCalled = true,
widget: buildGroup(),
);

expect(pumpBeforeTestCalled, isTrue);
});
});
});
}
Loading

0 comments on commit 405788c

Please sign in to comment.