// Copyright 2014 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:ui' show AccessibilityFeatures, Brightness, Display, FlutterView, Locale, PlatformDispatcher, VoidCallback; import 'package:flutter/widgets.dart' show WidgetsBinding, WidgetsBindingObserver; import 'package:flutter_test/flutter_test.dart'; import 'utils/fake_and_mock_utils.dart'; void main() { test('TestPlatformDispatcher can handle new methods without breaking', () { final dynamic testPlatformDispatcher = TestPlatformDispatcher(platformDispatcher: PlatformDispatcher.instance); // ignore: avoid_dynamic_calls expect(testPlatformDispatcher.someNewProperty, null); }); testWidgets('TestPlatformDispatcher can fake locale', (WidgetTester tester) async { verifyPropertyFaked<Locale>( tester: tester, realValue: PlatformDispatcher.instance.locale, fakeValue: const Locale('fake_language_code'), propertyRetriever: () { return WidgetsBinding.instance.platformDispatcher.locale; }, propertyFaker: (TestWidgetsFlutterBinding binding, Locale fakeValue) { binding.platformDispatcher.localeTestValue = fakeValue; }, ); }); testWidgets('TestPlatformDispatcher can fake locales', (WidgetTester tester) async { verifyPropertyFaked<List<Locale>>( tester: tester, realValue: PlatformDispatcher.instance.locales, fakeValue: <Locale>[const Locale('fake_language_code')], propertyRetriever: () { return WidgetsBinding.instance.platformDispatcher.locales; }, propertyFaker: (TestWidgetsFlutterBinding binding, List<Locale> fakeValue) { binding.platformDispatcher.localesTestValue = fakeValue; }, ); }); testWidgets('TestPlatformDispatcher can fake text scale factor', (WidgetTester tester) async { verifyPropertyFaked<double>( tester: tester, realValue: PlatformDispatcher.instance.textScaleFactor, fakeValue: 2.5, propertyRetriever: () { return WidgetsBinding.instance.platformDispatcher.textScaleFactor; }, propertyFaker: (TestWidgetsFlutterBinding binding, double fakeValue) { binding.platformDispatcher.textScaleFactorTestValue = fakeValue; }, ); }); testWidgets('TestPlatformDispatcher can fake clock format', (WidgetTester tester) async { verifyPropertyFaked<bool>( tester: tester, realValue: PlatformDispatcher.instance.alwaysUse24HourFormat, fakeValue: !PlatformDispatcher.instance.alwaysUse24HourFormat, propertyRetriever: () { return WidgetsBinding.instance.platformDispatcher.alwaysUse24HourFormat; }, propertyFaker: (TestWidgetsFlutterBinding binding, bool fakeValue) { binding.platformDispatcher.alwaysUse24HourFormatTestValue = fakeValue; }, ); }); testWidgets('TestPlatformDispatcher can fake brieflyShowPassword', (WidgetTester tester) async { verifyPropertyFaked<bool>( tester: tester, realValue: PlatformDispatcher.instance.brieflyShowPassword, fakeValue: !PlatformDispatcher.instance.brieflyShowPassword, propertyRetriever: () => WidgetsBinding.instance.platformDispatcher.brieflyShowPassword, propertyFaker: (TestWidgetsFlutterBinding binding, bool fakeValue) { binding.platformDispatcher.brieflyShowPasswordTestValue = fakeValue; }, ); }); testWidgets('TestPlatformDispatcher can fake default route name', (WidgetTester tester) async { verifyPropertyFaked<String>( tester: tester, realValue: PlatformDispatcher.instance.defaultRouteName, fakeValue: 'fake_route', propertyRetriever: () { return WidgetsBinding.instance.platformDispatcher.defaultRouteName; }, propertyFaker: (TestWidgetsFlutterBinding binding, String fakeValue) { binding.platformDispatcher.defaultRouteNameTestValue = fakeValue; }, ); }); testWidgets('TestPlatformDispatcher can fake accessibility features', (WidgetTester tester) async { verifyPropertyFaked<AccessibilityFeatures>( tester: tester, realValue: PlatformDispatcher.instance.accessibilityFeatures, fakeValue: const FakeAccessibilityFeatures(), propertyRetriever: () { return WidgetsBinding.instance.platformDispatcher.accessibilityFeatures; }, propertyFaker: (TestWidgetsFlutterBinding binding, AccessibilityFeatures fakeValue) { binding.platformDispatcher.accessibilityFeaturesTestValue = fakeValue; }, ); }); testWidgets('TestPlatformDispatcher can fake platform brightness', (WidgetTester tester) async { verifyPropertyFaked<Brightness>( tester: tester, realValue: Brightness.light, fakeValue: Brightness.dark, propertyRetriever: () { return WidgetsBinding.instance.platformDispatcher.platformBrightness; }, propertyFaker: (TestWidgetsFlutterBinding binding, Brightness fakeValue) { binding.platformDispatcher.platformBrightnessTestValue = fakeValue; }, ); }); testWidgets('TestPlatformDispatcher can clear out fake properties all at once', (WidgetTester tester) async { final Locale originalLocale = PlatformDispatcher.instance.locale; final double originalTextScaleFactor = PlatformDispatcher.instance.textScaleFactor; final TestPlatformDispatcher testPlatformDispatcher = retrieveTestBinding(tester).platformDispatcher; // Set fake values for window properties. testPlatformDispatcher.localeTestValue = const Locale('foobar'); testPlatformDispatcher.textScaleFactorTestValue = 3.0; // Erase fake window property values. testPlatformDispatcher.clearAllTestValues(); // Verify that the window once again reports real property values. expect(WidgetsBinding.instance.platformDispatcher.locale, originalLocale); expect(WidgetsBinding.instance.platformDispatcher.textScaleFactor, originalTextScaleFactor); }); testWidgets('TestPlatformDispatcher sends fake locales when WidgetsBindingObserver notifiers are called', (WidgetTester tester) async { final List<Locale> defaultLocales = WidgetsBinding.instance.platformDispatcher.locales; final TestObserver observer = TestObserver(); retrieveTestBinding(tester).addObserver(observer); final List<Locale> expectedValue = <Locale>[const Locale('fake_language_code')]; retrieveTestBinding(tester).platformDispatcher.localesTestValue = expectedValue; expect(observer.locales, equals(expectedValue)); retrieveTestBinding(tester).platformDispatcher.localesTestValue = defaultLocales; }); testWidgets('TestPlatformDispatcher.view getter returns the implicit view', (WidgetTester tester) async { expect(WidgetsBinding.instance.platformDispatcher.view(id: tester.view.viewId), same(tester.view)); }); // TODO(pdblasi-google): Removed this group of tests when the Display API is stable and supported on all platforms. group('TestPlatformDispatcher with unsupported Display API', () { testWidgets('can initialize with empty displays', (WidgetTester tester) async { expect(() { TestPlatformDispatcher( platformDispatcher: _FakePlatformDispatcher( displays: <Display>[], views: <FlutterView>[ _FakeFlutterView(), ], ) ); }, isNot(throwsA(anything))); }); testWidgets('can initialize with mismatched displays', (WidgetTester tester) async { expect(() { TestPlatformDispatcher( platformDispatcher: _FakePlatformDispatcher( displays: <Display>[ _FakeDisplay(id: 2), ], views: <FlutterView>[ _FakeFlutterView(display: _FakeDisplay(id: 1)), ], ) ); }, isNot(throwsA(anything))); }); testWidgets('creates test views for all views', (WidgetTester tester) async { final PlatformDispatcher backingDispatcher = _FakePlatformDispatcher( displays: <Display>[], views: <FlutterView>[ _FakeFlutterView(), ], ); final TestPlatformDispatcher testDispatcher = TestPlatformDispatcher( platformDispatcher: backingDispatcher, ); expect(testDispatcher.views.length, backingDispatcher.views.length); }); group('creates TestFlutterViews', () { testWidgets('that defaults to the correct devicePixelRatio', (WidgetTester tester) async { const double expectedDpr = 2.5; final TestPlatformDispatcher testDispatcher = TestPlatformDispatcher( platformDispatcher: _FakePlatformDispatcher( displays: <Display>[], views: <FlutterView>[ _FakeFlutterView(devicePixelRatio: expectedDpr), ], ) ); expect(testDispatcher.views.single.devicePixelRatio, expectedDpr); }); testWidgets('with working devicePixelRatio setter', (WidgetTester tester) async { const double expectedDpr = 2.5; const double defaultDpr = 4; final TestPlatformDispatcher testDispatcher = TestPlatformDispatcher( platformDispatcher: _FakePlatformDispatcher( displays: <Display>[], views: <FlutterView>[ _FakeFlutterView(devicePixelRatio: defaultDpr), ], ) ); testDispatcher.views.single.devicePixelRatio = expectedDpr; expect(testDispatcher.views.single.devicePixelRatio, expectedDpr); }); testWidgets('with working resetDevicePixelRatio', (WidgetTester tester) async { const double changedDpr = 2.5; const double defaultDpr = 4; final TestPlatformDispatcher testDispatcher = TestPlatformDispatcher( platformDispatcher: _FakePlatformDispatcher( displays: <Display>[], views: <FlutterView>[ _FakeFlutterView(devicePixelRatio: defaultDpr), ], ) ); testDispatcher.views.single.devicePixelRatio = changedDpr; testDispatcher.views.single.resetDevicePixelRatio(); expect(testDispatcher.views.single.devicePixelRatio, defaultDpr); }); }); }); } class TestObserver with WidgetsBindingObserver { List<Locale>? locales; @override void didChangeLocales(List<Locale>? locales) { this.locales = locales; } } class _FakeDisplay extends Fake implements Display { _FakeDisplay({this.id = 0}); @override final int id; } class _FakeFlutterView extends Fake implements FlutterView { _FakeFlutterView({ this.devicePixelRatio = 1, Display? display, }) : _display = display; @override final double devicePixelRatio; // This emulates the PlatformDispatcher not having a display on the engine // side. We don't have access to the `_displayId` used in the engine to try // to find it and can't directly extend `FlutterView` to emulate it closer. @override Display get display { assert(_display != null); return _display!; } final Display? _display; @override final int viewId = 1; } class _FakePlatformDispatcher extends Fake implements PlatformDispatcher { _FakePlatformDispatcher({required this.displays, required this.views}); @override final Iterable<Display> displays; @override final Iterable<FlutterView> views; @override VoidCallback? onMetricsChanged; }