Skip to content
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

feat(fluttium): support pixel ratio for the screenshot action #229

Merged
merged 4 commits into from
Apr 12, 2023
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
8 changes: 8 additions & 0 deletions docs/docs/actions/built-in/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ Taking screenshots can be done through the `takeScreenshot` action. This action

The full YAML syntax of this action is as followed:

```yaml
- takeScreenshot:
path: 'path/to/screenshot.png'
pixelRatio: 1.5 # Defaults to device pixel ratio.
```

The short-hand syntax for this action is:

```yaml
- takeScreenshot: 'path/to/screenshot.png'
```
10 changes: 9 additions & 1 deletion packages/fluttium/lib/src/actions/take_screenshot.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,33 @@ import 'package:fluttium/fluttium.dart';
/// ```yaml
/// - takeScreenshot:
/// path: "my_screenshot.png"
/// pixelRatio: 1.5
/// ```
/// {@endtemplate}
class TakeScreenshot extends Action {
/// {@macro take_screenshot}
const TakeScreenshot({
required this.path,
this.pixelRatio,
});

/// The file path to save the screenshot to.
final String path;

/// Optional pixel ratio to take screenshot with, defaults to device pixel
/// ratio.
final double? pixelRatio;

@override
Future<bool> execute(Tester tester) async {
final boundary = tester.getRenderRepaintBoundary();
if (boundary == null) {
return false;
}

final image = await boundary.toImage();
final image = await boundary.toImage(
pixelRatio: pixelRatio ?? tester.mediaQuery.devicePixelRatio,
);
final byteData = await image.toByteData(format: ImageByteFormat.png);
final pngBytes = byteData!.buffer.asUint8List();
await tester.storeFile(path, pngBytes);
Expand Down
3 changes: 3 additions & 0 deletions packages/fluttium/lib/src/tester.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ class Tester {

SemanticsOwner get _semanticsOwner => _binding.pipelineOwner.semanticsOwner!;

/// The current screen's media query information.
MediaQueryData get mediaQuery => MediaQueryData.fromWindow(_binding.window);

/// Converts the [steps] into a list of executable actions.
Future<List<Future<void> Function()>> convert(
List<UserFlowStep> steps,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import 'dart:typed_data';
import 'dart:ui';

import 'package:flutter/material.dart' hide Image;
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fluttium/fluttium.dart';
Expand All @@ -13,7 +14,7 @@ import '../../helpers/helpers.dart';
class _MockImage extends Mock implements Image {}

void main() {
group('TakeScreenshot', () {
group('$TakeScreenshot', () {
late Tester tester;
late RenderObject renderObject;
late RenderRepaintBoundary renderRepaintBoundary;
Expand All @@ -22,6 +23,7 @@ void main() {
setUp(() {
tester = MockTester();
when(() => tester.storeFile(any(), any())).thenAnswer((_) async {});
when(() => tester.mediaQuery).thenReturn(MediaQueryData());

renderObject = MockRenderObject();

Expand Down Expand Up @@ -68,6 +70,7 @@ void main() {
Uint8List.fromList([1, 2, 3, 4]),
),
).called(1);
verify(() => tester.mediaQuery).called(1);
});

test('stops early if no repaint boundary was found', () async {
Expand All @@ -93,6 +96,7 @@ void main() {
Uint8List.fromList([1, 2, 3, 4]),
),
);
verifyNever(() => tester.mediaQuery);
});

test('Readable representation', () {
Expand Down
37 changes: 34 additions & 3 deletions packages/fluttium/test/src/tester_test.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// ignore_for_file: prefer_const_constructors

import 'dart:ui';

import 'package:fake_async/fake_async.dart';
import 'package:flutter/material.dart' hide Action;
import 'package:flutter/rendering.dart';
import 'package:flutter/rendering.dart' hide ViewConfiguration;
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fluttium/fluttium.dart';
Expand Down Expand Up @@ -36,14 +38,18 @@ class _MockSemanticsData extends Mock
with DiagnosticableToStringMixin
implements SemanticsData {}

class _MockSingletonFlutterWindow extends Mock
implements SingletonFlutterWindow {}

void main() {
group('Tester', () {
group('$Tester', () {
late Tester tester;
late Action action;
late WidgetsBinding binding;
late Emitter emitter;
late Registry registry;
late BinaryMessenger binaryMessenger;
late SingletonFlutterWindow window;

setUp(() {
action = _MockAction();
Expand All @@ -63,6 +69,21 @@ void main() {
).thenAnswer((_) async {});
when(() => emitter.done(any())).thenAnswer((_) async {});
when(() => emitter.fatal(any())).thenAnswer((_) async {});

window = _MockSingletonFlutterWindow();
when(() => window.physicalSize).thenReturn(Size(800, 600));
when(() => window.devicePixelRatio).thenReturn(1);
when(() => window.platformDispatcher)
.thenReturn(PlatformDispatcher.instance);
when(() => window.padding).thenReturn(WindowPadding.zero);
when(() => window.viewPadding).thenReturn(WindowPadding.zero);
when(() => window.viewInsets).thenReturn(WindowPadding.zero);
when(() => window.systemGestureInsets).thenReturn(WindowPadding.zero);
when(() => window.padding).thenReturn(WindowPadding.zero);
when(() => window.viewConfiguration).thenReturn(ViewConfiguration());
when(() => window.displayFeatures).thenReturn([]);

when(() => binding.window).thenReturn(window);
});

setUpAll(() {
Expand All @@ -75,7 +96,17 @@ void main() {
expect(Tester(binding, registry), isNotNull);
});

group('compute', () {
test('retrieves correct media query information', () {
final mediaQuery = tester.mediaQuery;

expect(mediaQuery.devicePixelRatio, equals(1));
expect(mediaQuery.size, equals(Size(800, 600)));
expect(mediaQuery.padding, equals(EdgeInsets.zero));
expect(mediaQuery.viewPadding, equals(EdgeInsets.zero));
expect(mediaQuery.viewInsets, equals(EdgeInsets.zero));
});

group('convert', () {
late List<UserFlowStep> steps;

setUp(() {
Expand Down