// Copyright 2018 The Chromium 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:typed_data' show ByteData; import 'dart:ui' hide window; import 'package:meta/meta.dart'; /// [Window] that wraps another [Window] and allows faking of some properties /// for testing purposes. /// /// Tests for certain widgets, e.g., [MaterialApp], might require faking certain /// properties of a [Window]. [TestWindow] facilitates the faking of these /// properties by overriding the properties of a real [Window] with desired fake /// values. The binding used within tests, [TestWidgetsFlutterBinding], contains /// a [TestWindow] that is used by all tests. /// /// ## Sample Code /// /// A test can utilize a [TestWindow] in the following way: /// /// ```dart /// testWidgets('your test name here', (WidgetTester tester) async { /// // Retrieve the TestWidgetsFlutterBinding. /// final TestWidgetsFlutterBinding testBinding = tester.binding; /// /// // Fake the desired properties of the TestWindow. All code running /// // within this test will perceive the following fake text scale /// // factor as the real text scale factor of the window. /// testBinding.window.textScaleFactorFakeValue = 2.5; /// /// // Test code that depends on text scale factor here. /// }); /// ``` /// /// The [TestWidgetsFlutterBinding] is recreated for each test and /// therefore any fake values defined in one test will not persist /// to the next. /// /// If a test needs to override a real [Window] property and then later /// return to using the real [Window] property, [TestWindow] provides /// methods to clear each individual test value, e.g., [clearLocaleTestValue()]. /// /// To clear all fake test values in a [TestWindow], consider using /// [clearAllTestValues()]. class TestWindow implements Window { /// Constructs a [TestWindow] that defers all behavior to the given [Window] /// unless explicitly overridden for test purposes. TestWindow({ @required Window window, }) : _window = window; /// The [Window] that is wrapped by this [TestWindow]. final Window _window; @override double get devicePixelRatio => _devicePixelRatio ?? _window.devicePixelRatio; double _devicePixelRatio; /// Hides the real device pixel ratio and reports the given [devicePixelRatio] /// instead. set devicePixelRatioTestValue(double devicePixelRatio) { _devicePixelRatio = devicePixelRatio; onMetricsChanged(); } /// Deletes any existing test device pixel ratio and returns to using the real /// device pixel ratio. void clearDevicePixelRatioTestValue() { _devicePixelRatio = null; onMetricsChanged(); } @override Size get physicalSize => _physicalSizeTestValue ?? _window.physicalSize; Size _physicalSizeTestValue; /// Hides the real physical size and reports the given [physicalSizeTestValue] /// instead. set physicalSizeTestValue (Size physicalSizeTestValue) { _physicalSizeTestValue = physicalSizeTestValue; onMetricsChanged(); } /// Deletes any existing test physical size and returns to using the real /// physical size. void clearPhysicalSizeTestValue() { _physicalSizeTestValue = null; onMetricsChanged(); } @override double get physicalDepth => _physicalDepthTestValue ?? _window.physicalDepth; double _physicalDepthTestValue; /// Hides the real physical depth and reports the given /// [physicalDepthTestValue] instead. set physicalDepthTestValue (double physicalDepthTestValue) { _physicalDepthTestValue = physicalDepthTestValue; onMetricsChanged(); } /// Deletes any existing test physical depth and returns to using the real /// physical depth. void clearPhysicalDepthTestValue() { _physicalDepthTestValue = null; onMetricsChanged(); } @override WindowPadding get viewInsets => _viewInsetsTestValue ?? _window.viewInsets; WindowPadding _viewInsetsTestValue; /// Hides the real view insets and reports the given [viewInsetsTestValue] /// instead. set viewInsetsTestValue(WindowPadding viewInsetsTestValue) { _viewInsetsTestValue = viewInsetsTestValue; onMetricsChanged(); } /// Deletes any existing test view insets and returns to using the real view /// insets. void clearViewInsetsTestValue() { _viewInsetsTestValue = null; onMetricsChanged(); } @override WindowPadding get viewPadding => _viewPaddingTestValue ?? _window.padding; WindowPadding _viewPaddingTestValue; /// Hides the real view padding and reports the given [paddingTestValue] /// instead. set viewPaddingTestValue(WindowPadding viewPaddingTestValue) { _viewPaddingTestValue = viewPaddingTestValue; onMetricsChanged(); } /// Deletes any existing test view padding and returns to using the real /// viewPadding. void clearViewPaddingTestValue() { _viewPaddingTestValue = null; onMetricsChanged(); } @override WindowPadding get padding => _paddingTestValue ?? _window.padding; WindowPadding _paddingTestValue; /// Hides the real padding and reports the given [paddingTestValue] instead. set paddingTestValue(WindowPadding paddingTestValue) { _paddingTestValue = paddingTestValue; onMetricsChanged(); } /// Deletes any existing test padding and returns to using the real padding. void clearPaddingTestValue() { _paddingTestValue = null; onMetricsChanged(); } @override WindowPadding get systemGestureInsets => _systemGestureInsetsTestValue ?? _window.systemGestureInsets; WindowPadding _systemGestureInsetsTestValue; /// Hides the real system gesture insets and reports the given [systemGestureInsetsTestValue] instead. set systemGestureInsetsTestValue(WindowPadding systemGestureInsetsTestValue) { _systemGestureInsetsTestValue = systemGestureInsetsTestValue; onMetricsChanged(); } /// Deletes any existing test system gesture insets and returns to using the real system gesture insets. void clearSystemGestureInsetsTestValue() { _systemGestureInsetsTestValue = null; onMetricsChanged(); } @override VoidCallback get onMetricsChanged => _window.onMetricsChanged; @override set onMetricsChanged(VoidCallback callback) { _window.onMetricsChanged = callback; } @override Locale get locale => _localeTestValue ?? _window.locale; Locale _localeTestValue; /// Hides the real locale and reports the given [localeTestValue] instead. set localeTestValue(Locale localeTestValue) { _localeTestValue = localeTestValue; onLocaleChanged(); } /// Deletes any existing test locale and returns to using the real locale. void clearLocaleTestValue() { _localeTestValue = null; onLocaleChanged(); } @override List<Locale> get locales => _localesTestValue ?? _window.locales; List<Locale> _localesTestValue; /// Hides the real locales and reports the given [localesTestValue] instead. set localesTestValue(List<Locale> localesTestValue) { _localesTestValue = localesTestValue; onLocaleChanged(); } /// Deletes any existing test locales and returns to using the real locales. void clearLocalesTestValue() { _localesTestValue = null; onLocaleChanged(); } @override VoidCallback get onLocaleChanged => _window.onLocaleChanged; @override set onLocaleChanged(VoidCallback callback) { _window.onLocaleChanged = callback; } @override String get initialLifecycleState => _initialLifecycleStateTestValue; String _initialLifecycleStateTestValue; /// Sets a faked initialLifecycleState for testing. set initialLifecycleStateTestValue(String state) { _initialLifecycleStateTestValue = state; } @override double get textScaleFactor => _textScaleFactorTestValue ?? _window.textScaleFactor; double _textScaleFactorTestValue; /// Hides the real text scale factor and reports the given /// [textScaleFactorTestValue] instead. set textScaleFactorTestValue(double textScaleFactorTestValue) { _textScaleFactorTestValue = textScaleFactorTestValue; onTextScaleFactorChanged(); } /// Deletes any existing test text scale factor and returns to using the real /// text scale factor. void clearTextScaleFactorTestValue() { _textScaleFactorTestValue = null; onTextScaleFactorChanged(); } @override Brightness get platformBrightness => _platformBrightnessTestValue ?? _window.platformBrightness; Brightness _platformBrightnessTestValue; @override VoidCallback get onPlatformBrightnessChanged => _window.onPlatformBrightnessChanged; @override set onPlatformBrightnessChanged(VoidCallback callback) { _window.onPlatformBrightnessChanged = callback; } /// Hides the real text scale factor and reports the given /// [platformBrightnessTestValue] instead. set platformBrightnessTestValue(Brightness platformBrightnessTestValue) { _platformBrightnessTestValue = platformBrightnessTestValue; onPlatformBrightnessChanged(); } /// Deletes any existing test platform brightness and returns to using the /// real platform brightness. void clearPlatformBrightnessTestValue() { _platformBrightnessTestValue = null; onPlatformBrightnessChanged(); } @override bool get alwaysUse24HourFormat => _alwaysUse24HourFormatTestValue ?? _window.alwaysUse24HourFormat; bool _alwaysUse24HourFormatTestValue; /// Hides the real clock format and reports the given /// [alwaysUse24HourFormatTestValue] instead. set alwaysUse24HourFormatTestValue(bool alwaysUse24HourFormatTestValue) { _alwaysUse24HourFormatTestValue = alwaysUse24HourFormatTestValue; } /// Deletes any existing test clock format and returns to using the real clock /// format. void clearAlwaysUse24HourTestValue() { _alwaysUse24HourFormatTestValue = null; } @override VoidCallback get onTextScaleFactorChanged => _window.onTextScaleFactorChanged; @override set onTextScaleFactorChanged(VoidCallback callback) { _window.onTextScaleFactorChanged = callback; } @override FrameCallback get onBeginFrame => _window.onBeginFrame; @override set onBeginFrame(FrameCallback callback) { _window.onBeginFrame = callback; } @override VoidCallback get onDrawFrame => _window.onDrawFrame; @override set onDrawFrame(VoidCallback callback) { _window.onDrawFrame = callback; } @override // use frameTimings. https://github.com/flutter/flutter/issues/38838 // ignore: deprecated_member_use TimingsCallback get onReportTimings => _window.onReportTimings; @override set onReportTimings(TimingsCallback callback) { // use frameTimings. https://github.com/flutter/flutter/issues/38838 // ignore: deprecated_member_use _window.onReportTimings = callback; } @override PointerDataPacketCallback get onPointerDataPacket => _window.onPointerDataPacket; @override set onPointerDataPacket(PointerDataPacketCallback callback) { _window.onPointerDataPacket = callback; } @override String get defaultRouteName => _defaultRouteNameTestValue ?? _window.defaultRouteName; String _defaultRouteNameTestValue; /// Hides the real default route name and reports the given /// [defaultRouteNameTestValue] instead. set defaultRouteNameTestValue(String defaultRouteNameTestValue) { _defaultRouteNameTestValue = defaultRouteNameTestValue; } /// Deletes any existing test default route name and returns to using the real /// default route name. void clearDefaultRouteNameTestValue() { _defaultRouteNameTestValue = null; } @override void scheduleFrame() { _window.scheduleFrame(); } @override void render(Scene scene) { _window.render(scene); } @override bool get semanticsEnabled => _semanticsEnabledTestValue ?? _window.semanticsEnabled; bool _semanticsEnabledTestValue; /// Hides the real semantics enabled and reports the given /// [semanticsEnabledTestValue] instead. set semanticsEnabledTestValue(bool semanticsEnabledTestValue) { _semanticsEnabledTestValue = semanticsEnabledTestValue; onSemanticsEnabledChanged(); } /// Deletes any existing test semantics enabled and returns to using the real /// semantics enabled. void clearSemanticsEnabledTestValue() { _semanticsEnabledTestValue = null; onSemanticsEnabledChanged(); } @override VoidCallback get onSemanticsEnabledChanged => _window.onSemanticsEnabledChanged; @override set onSemanticsEnabledChanged(VoidCallback callback) { _window.onSemanticsEnabledChanged = callback; } @override SemanticsActionCallback get onSemanticsAction => _window.onSemanticsAction; @override set onSemanticsAction(SemanticsActionCallback callback) { _window.onSemanticsAction = callback; } @override AccessibilityFeatures get accessibilityFeatures => _accessibilityFeaturesTestValue ?? _window.accessibilityFeatures; AccessibilityFeatures _accessibilityFeaturesTestValue; /// Hides the real accessibility features and reports the given /// [accessibilityFeaturesTestValue] instead. set accessibilityFeaturesTestValue(AccessibilityFeatures accessibilityFeaturesTestValue) { _accessibilityFeaturesTestValue = accessibilityFeaturesTestValue; onAccessibilityFeaturesChanged(); } /// Deletes any existing test accessibility features and returns to using the /// real accessibility features. void clearAccessibilityFeaturesTestValue() { _accessibilityFeaturesTestValue = null; onAccessibilityFeaturesChanged(); } @override VoidCallback get onAccessibilityFeaturesChanged => _window.onAccessibilityFeaturesChanged; @override set onAccessibilityFeaturesChanged(VoidCallback callback) { _window.onAccessibilityFeaturesChanged = callback; } @override void updateSemantics(SemanticsUpdate update) { _window.updateSemantics(update); } @override void setIsolateDebugName(String name) { _window.setIsolateDebugName(name); } @override void sendPlatformMessage( String name, ByteData data, PlatformMessageResponseCallback callback, ) { _window.sendPlatformMessage(name, data, callback); } @override PlatformMessageCallback get onPlatformMessage => _window.onPlatformMessage; @override set onPlatformMessage(PlatformMessageCallback callback) { _window.onPlatformMessage = callback; } /// Delete any test value properties that have been set on this [TestWindow] /// and return to reporting the real [Window] values for all [Window] /// properties. /// /// If desired, clearing of properties can be done on an individual basis, /// e.g., [clearLocaleTestValue()]. void clearAllTestValues() { clearAccessibilityFeaturesTestValue(); clearAlwaysUse24HourTestValue(); clearDefaultRouteNameTestValue(); clearDevicePixelRatioTestValue(); clearPlatformBrightnessTestValue(); clearLocaleTestValue(); clearLocalesTestValue(); clearPaddingTestValue(); clearPhysicalSizeTestValue(); clearPhysicalDepthTestValue(); clearSemanticsEnabledTestValue(); clearTextScaleFactorTestValue(); clearViewInsetsTestValue(); } /// This gives us some grace time when the dart:ui side adds something to /// Window, and makes things easier when we do rolls to give us time to catch /// up. @override dynamic noSuchMethod(Invocation invocation) { return null; } }