window_test.dart 9.82 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6 7 8 9 10
// TODO(gspencergoog): Remove this tag once this test's state leaks/test
// dependencies have been fixed.
// https://github.com/flutter/flutter/issues/85160
// Fails with "flutter test --test-randomize-ordering-seed=123"
@Tags(<String>['no-shuffle'])

11
import 'dart:ui' as ui show window;
12
import 'dart:ui' show Size, Locale, WindowPadding, AccessibilityFeatures, Brightness;
13

14
import 'package:flutter/widgets.dart' show WidgetsBinding, WidgetsBindingObserver;
15 16 17
import 'package:flutter_test/flutter_test.dart';

void main() {
18
  test('TestWindow can handle new methods without breaking', () {
19
    final dynamic testWindow = TestWindow(window: ui.window);
20
    // ignore: avoid_dynamic_calls
21 22 23
    expect(testWindow.someNewProperty, null);
  });

24 25 26 27 28 29
  testWidgets('TestWindow can fake device pixel ratio', (WidgetTester tester) async {
    verifyThatTestWindowCanFakeProperty<double>(
      tester: tester,
      realValue: ui.window.devicePixelRatio,
      fakeValue: 2.5,
      propertyRetriever: () {
30
        return WidgetsBinding.instance!.window.devicePixelRatio;
31 32 33
      },
      propertyFaker: (TestWidgetsFlutterBinding binding, double fakeValue) {
        binding.window.devicePixelRatioTestValue = fakeValue;
34
      },
35 36 37 38 39 40 41 42 43
    );
  });

  testWidgets('TestWindow can fake physical size', (WidgetTester tester) async {
    verifyThatTestWindowCanFakeProperty<Size>(
      tester: tester,
      realValue: ui.window.physicalSize,
      fakeValue: const Size(50, 50),
      propertyRetriever: () {
44
        return WidgetsBinding.instance!.window.physicalSize;
45 46 47
      },
      propertyFaker: (TestWidgetsFlutterBinding binding, Size fakeValue) {
        binding.window.physicalSizeTestValue = fakeValue;
48
      },
49 50 51 52 53 54 55 56 57
    );
  });

  testWidgets('TestWindow can fake view insets', (WidgetTester tester) async {
    verifyThatTestWindowCanFakeProperty<WindowPadding>(
      tester: tester,
      realValue: ui.window.viewInsets,
      fakeValue: const FakeWindowPadding(),
      propertyRetriever: () {
58
        return WidgetsBinding.instance!.window.viewInsets;
59 60 61
      },
      propertyFaker: (TestWidgetsFlutterBinding binding, WindowPadding fakeValue) {
        binding.window.viewInsetsTestValue = fakeValue;
62
      },
63 64 65 66 67 68 69 70 71
    );
  });

  testWidgets('TestWindow can fake padding', (WidgetTester tester) async {
    verifyThatTestWindowCanFakeProperty<WindowPadding>(
      tester: tester,
      realValue: ui.window.padding,
      fakeValue: const FakeWindowPadding(),
      propertyRetriever: () {
72
        return WidgetsBinding.instance!.window.padding;
73 74 75
      },
      propertyFaker: (TestWidgetsFlutterBinding binding, WindowPadding fakeValue) {
        binding.window.paddingTestValue = fakeValue;
76
      },
77 78 79 80 81 82 83 84 85
    );
  });

  testWidgets('TestWindow can fake locale', (WidgetTester tester) async {
    verifyThatTestWindowCanFakeProperty<Locale>(
      tester: tester,
      realValue: ui.window.locale,
      fakeValue: const Locale('fake_language_code'),
      propertyRetriever: () {
86
        return WidgetsBinding.instance!.window.locale;
87 88 89
      },
      propertyFaker: (TestWidgetsFlutterBinding binding, Locale fakeValue) {
        binding.window.localeTestValue = fakeValue;
90
      },
91 92 93 94 95 96 97 98 99
    );
  });

  testWidgets('TestWindow can fake locales', (WidgetTester tester) async {
    verifyThatTestWindowCanFakeProperty<List<Locale>>(
      tester: tester,
      realValue: ui.window.locales,
      fakeValue: <Locale>[const Locale('fake_language_code')],
      propertyRetriever: () {
100
        return WidgetsBinding.instance!.window.locales;
101 102 103
      },
      propertyFaker: (TestWidgetsFlutterBinding binding, List<Locale> fakeValue) {
        binding.window.localesTestValue = fakeValue;
104
      },
105 106 107 108 109 110 111 112 113
    );
  });

  testWidgets('TestWindow can fake text scale factor', (WidgetTester tester) async {
    verifyThatTestWindowCanFakeProperty<double>(
      tester: tester,
      realValue: ui.window.textScaleFactor,
      fakeValue: 2.5,
      propertyRetriever: () {
114
        return WidgetsBinding.instance!.window.textScaleFactor;
115 116 117
      },
      propertyFaker: (TestWidgetsFlutterBinding binding, double fakeValue) {
        binding.window.textScaleFactorTestValue = fakeValue;
118
      },
119 120 121 122 123 124 125 126 127
    );
  });

  testWidgets('TestWindow can fake clock format', (WidgetTester tester) async {
    verifyThatTestWindowCanFakeProperty<bool>(
      tester: tester,
      realValue: ui.window.alwaysUse24HourFormat,
      fakeValue: !ui.window.alwaysUse24HourFormat,
      propertyRetriever: () {
128
        return WidgetsBinding.instance!.window.alwaysUse24HourFormat;
129 130 131
      },
      propertyFaker: (TestWidgetsFlutterBinding binding, bool fakeValue) {
        binding.window.alwaysUse24HourFormatTestValue = fakeValue;
132
      },
133 134 135 136 137 138 139 140 141
    );
  });

  testWidgets('TestWindow can fake default route name', (WidgetTester tester) async {
    verifyThatTestWindowCanFakeProperty<String>(
      tester: tester,
      realValue: ui.window.defaultRouteName,
      fakeValue: 'fake_route',
      propertyRetriever: () {
142
        return WidgetsBinding.instance!.window.defaultRouteName;
143 144 145
      },
      propertyFaker: (TestWidgetsFlutterBinding binding, String fakeValue) {
        binding.window.defaultRouteNameTestValue = fakeValue;
146
      },
147 148 149 150 151 152 153 154 155
    );
  });

  testWidgets('TestWindow can fake accessibility features', (WidgetTester tester) async {
    verifyThatTestWindowCanFakeProperty<AccessibilityFeatures>(
      tester: tester,
      realValue: ui.window.accessibilityFeatures,
      fakeValue: const FakeAccessibilityFeatures(),
      propertyRetriever: () {
156
        return WidgetsBinding.instance!.window.accessibilityFeatures;
157 158 159
      },
      propertyFaker: (TestWidgetsFlutterBinding binding, AccessibilityFeatures fakeValue) {
        binding.window.accessibilityFeaturesTestValue = fakeValue;
160
      },
161 162 163
    );
  });

164 165 166 167 168 169
  testWidgets('TestWindow can fake platform brightness', (WidgetTester tester) async {
    verifyThatTestWindowCanFakeProperty<Brightness>(
      tester: tester,
      realValue: Brightness.light,
      fakeValue: Brightness.dark,
      propertyRetriever: () {
170
        return WidgetsBinding.instance!.window.platformBrightness;
171 172 173
      },
      propertyFaker: (TestWidgetsFlutterBinding binding, Brightness fakeValue) {
        binding.window.platformBrightnessTestValue = fakeValue;
174
      },
175 176 177
    );
  });

178
  testWidgets('TestWindow can clear out fake properties all at once', (WidgetTester tester) async {
179 180 181 182 183 184 185 186 187 188 189 190
    final double originalDevicePixelRatio = ui.window.devicePixelRatio;
    final double originalTextScaleFactor = ui.window.textScaleFactor;
    final TestWindow testWindow = retrieveTestBinding(tester).window;

    // Set fake values for window properties.
    testWindow.devicePixelRatioTestValue = 2.5;
    testWindow.textScaleFactorTestValue = 3.0;

    // Erase fake window property values.
    testWindow.clearAllTestValues();

    // Verify that the window once again reports real property values.
191 192
    expect(WidgetsBinding.instance!.window.devicePixelRatio, originalDevicePixelRatio);
    expect(WidgetsBinding.instance!.window.textScaleFactor, originalTextScaleFactor);
193
  });
194 195 196 197 198 199 200 201

  testWidgets('TestWindow sends fake locales when WidgetsBindingObserver notifiers are called', (WidgetTester tester) async {
    final TestObserver observer = TestObserver();
    retrieveTestBinding(tester).addObserver(observer);
    final List<Locale> expectedValue = <Locale>[const Locale('fake_language_code')];
    retrieveTestBinding(tester).window.localesTestValue = expectedValue;
    expect(observer.locales, equals(expectedValue));
  });
202 203 204
}

void verifyThatTestWindowCanFakeProperty<WindowPropertyType>({
205 206 207 208 209
  required WidgetTester tester,
  required WindowPropertyType? realValue,
  required WindowPropertyType fakeValue,
  required WindowPropertyType? Function() propertyRetriever,
  required Function(TestWidgetsFlutterBinding, WindowPropertyType fakeValue) propertyFaker,
210
}) {
211 212
  WindowPropertyType? propertyBeforeFaking;
  WindowPropertyType? propertyAfterFaking;
213 214 215 216 217 218 219 220 221 222 223 224 225 226

  propertyBeforeFaking = propertyRetriever();

  propertyFaker(retrieveTestBinding(tester), fakeValue);

  propertyAfterFaking = propertyRetriever();

  expect(propertyBeforeFaking, realValue);
  expect(propertyAfterFaking, fakeValue);
}

TestWidgetsFlutterBinding retrieveTestBinding(WidgetTester tester) {
  final WidgetsBinding binding = tester.binding;
  assert(binding is TestWidgetsFlutterBinding);
227
  final TestWidgetsFlutterBinding testBinding = binding as TestWidgetsFlutterBinding;
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
  return testBinding;
}

class FakeWindowPadding implements WindowPadding {
  const FakeWindowPadding({
    this.left = 0.0,
    this.top = 0.0,
    this.right = 0.0,
    this.bottom = 0.0,
  });

  @override
  final double left;

  @override
  final double top;

  @override
  final double right;

  @override
  final double bottom;
}

class FakeAccessibilityFeatures implements AccessibilityFeatures {
  const FakeAccessibilityFeatures({
    this.accessibleNavigation = false,
    this.invertColors = false,
    this.disableAnimations = false,
    this.boldText = false,
    this.reduceMotion = false,
259
    this.highContrast = false,
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
  });

  @override
  final bool accessibleNavigation;

  @override
  final bool invertColors;

  @override
  final bool disableAnimations;

  @override
  final bool boldText;

  @override
  final bool reduceMotion;
276

277 278 279
  @override
  final bool highContrast;

280 281 282
  /// This gives us some grace time when the dart:ui side adds something to
  /// [AccessibilityFeatures], and makes things easier when we do rolls to
  /// give us time to catch up.
283 284 285
  ///
  /// If you would like to add to this class, changes must first be made in the
  /// engine, followed by the framework.
286 287 288 289
  @override
  dynamic noSuchMethod(Invocation invocation) {
    return null;
  }
290
}
291 292 293 294 295 296 297 298 299 300

class TestObserver with WidgetsBindingObserver {
  List<Locale>? locales;
  Locale? locale;

  @override
  void didChangeLocales(List<Locale>? locales) {
    this.locales = locales;
  }
}