mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
WIP Commits separated as follows: - Update lints in analysis_options files - Run `dart fix --apply` - Clean up leftover analysis issues - Run `dart format .` in the right places. Local analysis and testing passes. Checking CI now. Part of https://github.com/flutter/flutter/issues/178827 - Adoption of flutter_lints in examples/api coming in a separate change (cc @loic-sharma) ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
349 lines
12 KiB
Dart
349 lines
12 KiB
Dart
// 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,
|
|
ViewFocusChangeCallback,
|
|
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 supportsShowingSystemContextMenu', (
|
|
WidgetTester tester,
|
|
) async {
|
|
verifyPropertyFaked<bool>(
|
|
tester: tester,
|
|
realValue: PlatformDispatcher.instance.supportsShowingSystemContextMenu,
|
|
fakeValue: !PlatformDispatcher.instance.supportsShowingSystemContextMenu,
|
|
propertyRetriever: () =>
|
|
WidgetsBinding.instance.platformDispatcher.supportsShowingSystemContextMenu,
|
|
propertyFaker: (TestWidgetsFlutterBinding binding, bool fakeValue) {
|
|
binding.platformDispatcher.supportsShowingSystemContextMenu = 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 observer = TestObserver();
|
|
retrieveTestBinding(tester).addObserver(observer);
|
|
final 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),
|
|
);
|
|
});
|
|
|
|
testWidgets('TestPlatformDispatcher has a working scaleFontSize implementation', (
|
|
WidgetTester tester,
|
|
) async {
|
|
expect(
|
|
TestPlatformDispatcher(
|
|
platformDispatcher: _FakePlatformDispatcher(
|
|
displays: <Display>[_FakeDisplay(id: 2)],
|
|
views: <FlutterView>[_FakeFlutterView(display: _FakeDisplay(id: 1))],
|
|
),
|
|
).scaleFontSize(2.0),
|
|
2.0,
|
|
);
|
|
});
|
|
|
|
// 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 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 expectedDpr = 2.5;
|
|
final 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 expectedDpr = 2.5;
|
|
const double defaultDpr = 4;
|
|
final 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 changedDpr = 2.5;
|
|
const double defaultDpr = 4;
|
|
final 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;
|
|
|
|
@override
|
|
ViewFocusChangeCallback? onViewFocusChange;
|
|
|
|
@override
|
|
double get textScaleFactor => 1.0;
|
|
}
|