| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:async'; |
| import 'dart:js_interop'; |
| import 'dart:typed_data'; |
| |
| import 'package:test/test.dart'; |
| |
| import 'package:ui/src/engine.dart'; |
| import 'package:ui/ui.dart' as ui; |
| import 'package:web_engine_tester/golden_tester.dart'; |
| |
| import '../common/rendering.dart'; |
| import '../common/test_initialization.dart'; |
| |
| export '../common/rendering.dart' show renderScene; |
| |
| const MethodCodec codec = StandardMethodCodec(); |
| |
| /// Common test setup for all CanvasKit unit-tests. |
| void setUpCanvasKitTest({bool withImplicitView = false}) { |
| setUpUnitTests( |
| withImplicitView: withImplicitView, |
| emulateTesterEnvironment: false, |
| setUpTestViewDimensions: false, |
| ); |
| |
| setUp(() => debugOverrideJsConfiguration(<String, Object?>{ |
| 'fontFallbackBaseUrl': 'assets/fallback_fonts/', |
| }.jsify() as JsFlutterConfiguration?)); |
| } |
| |
| /// Convenience getter for the implicit view. |
| ui.FlutterView get implicitView => |
| EnginePlatformDispatcher.instance.implicitView!; |
| |
| /// Utility function for CanvasKit tests to draw pictures without |
| /// the [CkPictureRecorder] boilerplate. |
| CkPicture paintPicture( |
| ui.Rect cullRect, void Function(CkCanvas canvas) painter) { |
| final CkPictureRecorder recorder = CkPictureRecorder(); |
| final CkCanvas canvas = recorder.beginRecording(cullRect); |
| painter(canvas); |
| return recorder.endRecording(); |
| } |
| |
| Future<void> matchSceneGolden( |
| String goldenFile, |
| ui.Scene scene, { |
| required ui.Rect region, |
| }) async { |
| await renderScene(scene); |
| await matchGoldenFile(goldenFile, region: region); |
| } |
| |
| /// Checks that a [picture] matches the [goldenFile]. |
| /// |
| /// The picture is drawn onto the UI at [ui.Offset.zero] with no additional |
| /// layers. |
| Future<void> matchPictureGolden(String goldenFile, CkPicture picture, |
| {required ui.Rect region}) async { |
| final LayerSceneBuilder sb = LayerSceneBuilder(); |
| sb.pushOffset(0, 0); |
| sb.addPicture(ui.Offset.zero, picture); |
| await renderScene(sb.build()); |
| await matchGoldenFile(goldenFile, region: region); |
| } |
| |
| Future<bool> matchImage(ui.Image left, ui.Image right) async { |
| if (left.width != right.width || left.height != right.height) { |
| return false; |
| } |
| int getPixel(ByteData data, int x, int y) => |
| data.getUint32((x + y * left.width) * 4); |
| final ByteData leftData = (await left.toByteData())!; |
| final ByteData rightData = (await right.toByteData())!; |
| for (int y = 0; y < left.height; y++) { |
| for (int x = 0; x < left.width; x++) { |
| if (getPixel(leftData, x, y) != getPixel(rightData, x, y)) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| /// Sends a platform message to create a Platform View with the given id and viewType. |
| Future<void> createPlatformView(int id, String viewType) { |
| final Completer<void> completer = Completer<void>(); |
| ui.PlatformDispatcher.instance.sendPlatformMessage( |
| 'flutter/platform_views', |
| codec.encodeMethodCall(MethodCall( |
| 'create', |
| <String, dynamic>{ |
| 'id': id, |
| 'viewType': viewType, |
| }, |
| )), |
| (dynamic _) => completer.complete(), |
| ); |
| return completer.future; |
| } |
| |
| /// Disposes of the platform view with the given [id]. |
| Future<void> disposePlatformView(int id) { |
| final Completer<void> completer = Completer<void>(); |
| ui.PlatformDispatcher.instance.sendPlatformMessage( |
| 'flutter/platform_views', |
| codec.encodeMethodCall(MethodCall('dispose', id)), |
| (dynamic _) => completer.complete(), |
| ); |
| return completer.future; |
| } |
| |
| /// Creates a pre-laid out one-line paragraph of text. |
| /// |
| /// Useful in tests that need a simple label to annotate goldens. |
| CkParagraph makeSimpleText( |
| String text, { |
| String? fontFamily, |
| double? fontSize, |
| ui.FontStyle? fontStyle, |
| ui.FontWeight? fontWeight, |
| ui.Color? color, |
| }) { |
| final CkParagraphBuilder builder = CkParagraphBuilder(CkParagraphStyle( |
| fontFamily: fontFamily ?? 'Roboto', |
| fontSize: fontSize ?? 14, |
| fontStyle: fontStyle ?? ui.FontStyle.normal, |
| fontWeight: fontWeight ?? ui.FontWeight.normal, |
| )); |
| builder.pushStyle(CkTextStyle( |
| color: color ?? const ui.Color(0xFF000000), |
| )); |
| builder.addText(text); |
| builder.pop(); |
| final CkParagraph paragraph = builder.build(); |
| paragraph.layout(const ui.ParagraphConstraints(width: 10000)); |
| return paragraph; |
| } |