mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[web] Make window.locale(s) non-null; upgrade to null safe deps (flutter/engine#24922)
* Make window.locale(s) non-null * upgrade deps to null-safe; migrate some test libs
This commit is contained in:
parent
87509d8518
commit
5a5f326745
@ -270,7 +270,7 @@ Future<String> fetchLatestChromeVersion() async {
|
||||
final Client client = Client();
|
||||
try {
|
||||
final Response response = await client.get(
|
||||
'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2FLAST_CHANGE?alt=media');
|
||||
Uri.parse('https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2FLAST_CHANGE?alt=media'));
|
||||
if (response.statusCode != 200) {
|
||||
throw BrowserInstallerException(
|
||||
'Failed to fetch latest Chrome version. Server returned status code ${response.statusCode}');
|
||||
|
||||
@ -32,8 +32,8 @@ abstract class SingletonFlutterWindow extends FlutterWindow {
|
||||
platformDispatcher.onMetricsChanged = callback;
|
||||
}
|
||||
|
||||
Locale? get locale => platformDispatcher.locale;
|
||||
List<Locale>? get locales => platformDispatcher.locales;
|
||||
Locale get locale => platformDispatcher.locale;
|
||||
List<Locale> get locales => platformDispatcher.locales;
|
||||
|
||||
Locale? computePlatformResolvedLocale(List<Locale> supportedLocales) {
|
||||
return platformDispatcher.computePlatformResolvedLocale(supportedLocales);
|
||||
|
||||
@ -5,20 +5,21 @@ environment:
|
||||
sdk: ">=2.12.0-0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
js: 0.6.3-nullsafety.3
|
||||
meta: 1.3.0-nullsafety.6
|
||||
js: 0.6.3
|
||||
meta: 1.3.0
|
||||
|
||||
dev_dependencies:
|
||||
analyzer: 0.39.15
|
||||
archive: 2.0.13
|
||||
http: 0.12.1
|
||||
image: 2.1.13
|
||||
mockito: 4.1.1
|
||||
path: 1.8.0-nullsafety.3
|
||||
test: 1.16.0-nullsafety.9
|
||||
quiver: 3.0.0-nullsafety.2
|
||||
yaml: 2.2.1
|
||||
watcher: 0.9.7+15
|
||||
analyzer: 1.1.0
|
||||
archive: 3.1.2
|
||||
html: 0.15.0
|
||||
http: 0.13.0
|
||||
image: 3.0.1
|
||||
mockito: 5.0.0
|
||||
path: 1.8.0
|
||||
quiver: 3.0.0
|
||||
test: 1.16.6
|
||||
yaml: 3.0.0
|
||||
watcher: 1.0.0
|
||||
web_test_utils:
|
||||
path: ../../web_sdk/web_test_utils
|
||||
web_engine_tester:
|
||||
@ -32,4 +33,4 @@ dev_dependencies:
|
||||
git:
|
||||
url: git://github.com/flutter/web_installers.git
|
||||
path: packages/web_drivers/
|
||||
ref: 58081a8b2fbf234e9c8da86fce28adfefe3c2093
|
||||
ref: 946111ae3248132e3773bf5c5327bcbbefa0c5f2
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.6
|
||||
// @dart = 2.12
|
||||
@TestOn('!safari')
|
||||
// TODO(nurhan): https://github.com/flutter/flutter/issues/51169
|
||||
|
||||
@ -471,14 +471,14 @@ void testMain() {
|
||||
});
|
||||
|
||||
group('$HashUrlStrategy', () {
|
||||
TestPlatformLocation location;
|
||||
late TestPlatformLocation location;
|
||||
|
||||
setUp(() {
|
||||
location = TestPlatformLocation();
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
location = null;
|
||||
location = TestPlatformLocation();
|
||||
});
|
||||
|
||||
test('leading slash is optional', () {
|
||||
@ -544,11 +544,15 @@ Future<void> systemNavigatorPop() {
|
||||
|
||||
/// A mock implementation of [PlatformLocation] that doesn't access the browser.
|
||||
class TestPlatformLocation extends PlatformLocation {
|
||||
String pathname;
|
||||
String search;
|
||||
String hash;
|
||||
String? hash;
|
||||
dynamic state;
|
||||
|
||||
@override
|
||||
String get pathname => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
String get search => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
void addPopStateListener(html.EventListener fn) {
|
||||
throw UnimplementedError();
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
/// Provides utilities for testing engine code.
|
||||
// @dart = 2.6
|
||||
// @dart = 2.12
|
||||
library matchers;
|
||||
|
||||
import 'dart:html' as html;
|
||||
@ -12,7 +12,6 @@ import 'dart:math' as math;
|
||||
import 'package:html/parser.dart' as html_package;
|
||||
import 'package:html/dom.dart' as html_package;
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:ui/ui.dart';
|
||||
@ -23,9 +22,9 @@ import 'package:ui/src/engine.dart';
|
||||
/// If [root] is `null` returns all surfaces from the last rendered scene.
|
||||
///
|
||||
/// Surfaces are returned in a depth-first order.
|
||||
Iterable<PersistedSurface> enumerateSurfaces([PersistedSurface root]) {
|
||||
Iterable<PersistedSurface> enumerateSurfaces([PersistedSurface? root]) {
|
||||
root ??= SurfaceSceneBuilder.debugLastFrameScene;
|
||||
final List<PersistedSurface> surfaces = <PersistedSurface>[root];
|
||||
final List<PersistedSurface> surfaces = <PersistedSurface>[root!];
|
||||
|
||||
root.visitChildren((PersistedSurface surface) {
|
||||
surfaces.addAll(enumerateSurfaces(surface));
|
||||
@ -37,7 +36,7 @@ Iterable<PersistedSurface> enumerateSurfaces([PersistedSurface root]) {
|
||||
/// Enumerates all pictures nested under [root].
|
||||
///
|
||||
/// If [root] is `null` returns all pictures from the last rendered scene.
|
||||
Iterable<PersistedPicture> enumeratePictures([PersistedSurface root]) {
|
||||
Iterable<PersistedPicture> enumeratePictures([PersistedSurface? root]) {
|
||||
root ??= SurfaceSceneBuilder.debugLastFrameScene;
|
||||
return enumerateSurfaces(root).whereType<PersistedPicture>();
|
||||
}
|
||||
@ -45,7 +44,7 @@ Iterable<PersistedPicture> enumeratePictures([PersistedSurface root]) {
|
||||
/// Enumerates all offset surfaces nested under [root].
|
||||
///
|
||||
/// If [root] is `null` returns all pictures from the last rendered scene.
|
||||
Iterable<PersistedOffset> enumerateOffsets([PersistedSurface root]) {
|
||||
Iterable<PersistedOffset> enumerateOffsets([PersistedSurface? root]) {
|
||||
root ??= SurfaceSceneBuilder.debugLastFrameScene;
|
||||
return enumerateSurfaces(root).whereType<PersistedOffset>();
|
||||
}
|
||||
@ -76,7 +75,7 @@ typedef DistanceFunction<T> = num Function(T a, T b);
|
||||
///
|
||||
/// Calling an instance of this type must either be done dynamically, or by
|
||||
/// first casting it to a [DistanceFunction<T>] for some concrete T.
|
||||
typedef AnyDistanceFunction = num Function(Null a, Null b);
|
||||
typedef AnyDistanceFunction = num Function(Never a, Never b);
|
||||
|
||||
const Map<Type, AnyDistanceFunction> _kStandardDistanceFunctions =
|
||||
<Type, AnyDistanceFunction>{
|
||||
@ -108,7 +107,7 @@ double _rectDistance(Rect a, Rect b) {
|
||||
}
|
||||
|
||||
double _sizeDistance(Size a, Size b) {
|
||||
final Offset delta = b - a;
|
||||
final Offset delta = (b - a) as Offset;
|
||||
return delta.distance;
|
||||
}
|
||||
|
||||
@ -134,11 +133,11 @@ double _sizeDistance(Size a, Size b) {
|
||||
/// [double]s and has an optional `epsilon` parameter.
|
||||
/// * [closeTo], which specializes in numbers only.
|
||||
Matcher within<T>({
|
||||
@required num distance,
|
||||
@required T from,
|
||||
DistanceFunction<T> distanceFunction,
|
||||
required num distance,
|
||||
required T from,
|
||||
DistanceFunction<T>? distanceFunction,
|
||||
}) {
|
||||
distanceFunction ??= _kStandardDistanceFunctions[T];
|
||||
distanceFunction ??= _kStandardDistanceFunctions[T] as DistanceFunction<T>?;
|
||||
|
||||
if (distanceFunction == null) {
|
||||
throw ArgumentError(
|
||||
@ -158,7 +157,7 @@ class _IsWithinDistance<T> extends Matcher {
|
||||
final num epsilon;
|
||||
|
||||
@override
|
||||
bool matches(Object object, Map<dynamic, dynamic> matchState) {
|
||||
bool matches(Object? object, Map<dynamic, dynamic> matchState) {
|
||||
if (object is! T) {
|
||||
return false;
|
||||
}
|
||||
@ -183,7 +182,7 @@ class _IsWithinDistance<T> extends Matcher {
|
||||
|
||||
@override
|
||||
Description describeMismatch(
|
||||
Object object,
|
||||
Object? object,
|
||||
Description mismatchDescription,
|
||||
Map<dynamic, dynamic> matchState,
|
||||
bool verbose,
|
||||
@ -230,11 +229,11 @@ enum HtmlComparisonMode {
|
||||
String canonicalizeHtml(String htmlContent,
|
||||
{HtmlComparisonMode mode = HtmlComparisonMode.nonLayoutOnly,
|
||||
bool throwOnUnusedAttributes = false}) {
|
||||
if (htmlContent == null || htmlContent.trim().isEmpty) {
|
||||
if (htmlContent.trim().isEmpty) {
|
||||
return '';
|
||||
}
|
||||
|
||||
String _unusedAttribute(String name) {
|
||||
String? _unusedAttribute(String name) {
|
||||
if (throwOnUnusedAttributes) {
|
||||
fail('Provided HTML contains style attribute "$name" which '
|
||||
'is not used for comparison in the test. The HTML was:\n\n$htmlContent');
|
||||
@ -244,7 +243,7 @@ String canonicalizeHtml(String htmlContent,
|
||||
}
|
||||
|
||||
html_package.Element _cleanup(html_package.Element original) {
|
||||
String replacementTag = original.localName;
|
||||
String replacementTag = original.localName!;
|
||||
switch (replacementTag) {
|
||||
case 'flt-scene':
|
||||
replacementTag = 's';
|
||||
@ -256,7 +255,7 @@ String canonicalizeHtml(String htmlContent,
|
||||
replacementTag = 'o';
|
||||
break;
|
||||
case 'flt-clip':
|
||||
final String clipType = original.attributes['clip-type'];
|
||||
final String? clipType = original.attributes['clip-type'];
|
||||
switch (clipType) {
|
||||
case 'rect':
|
||||
replacementTag = 'clip';
|
||||
@ -314,7 +313,7 @@ String canonicalizeHtml(String htmlContent,
|
||||
});
|
||||
|
||||
if (original.attributes.containsKey('style')) {
|
||||
final String styleValue = original.attributes['style'];
|
||||
final String styleValue = original.attributes['style']!;
|
||||
|
||||
int attrCount = 0;
|
||||
final String processedAttributes = styleValue
|
||||
@ -368,7 +367,7 @@ String canonicalizeHtml(String htmlContent,
|
||||
attrCount++;
|
||||
return attr.trim();
|
||||
})
|
||||
.where((String attr) => attr != null && attr.isNotEmpty)
|
||||
.where((String? attr) => attr != null && attr.isNotEmpty)
|
||||
.join('; ');
|
||||
|
||||
if (attrCount > 0) {
|
||||
@ -412,7 +411,7 @@ void expectHtml(html.Element element, String expectedHtml,
|
||||
{HtmlComparisonMode mode = HtmlComparisonMode.nonLayoutOnly}) {
|
||||
expectedHtml =
|
||||
canonicalizeHtml(expectedHtml, mode: mode, throwOnUnusedAttributes: true);
|
||||
final String actualHtml = canonicalizeHtml(element.outerHtml, mode: mode);
|
||||
final String actualHtml = canonicalizeHtml(element.outerHtml!, mode: mode);
|
||||
expect(actualHtml, expectedHtml);
|
||||
}
|
||||
|
||||
@ -462,7 +461,7 @@ class SceneTester {
|
||||
final SurfaceScene scene;
|
||||
|
||||
void expectSceneHtml(String expectedHtml) {
|
||||
expectHtml(scene.webOnlyRootElement, expectedHtml,
|
||||
expectHtml(scene.webOnlyRootElement!, expectedHtml,
|
||||
mode: HtmlComparisonMode.noAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.6
|
||||
// @dart = 2.12
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:ui/src/engine.dart' hide window;
|
||||
@ -31,8 +31,8 @@ class PlatformMessage {
|
||||
/// It holds all intercepted platform messages in a [messages] list that can
|
||||
/// be inspected in tests.
|
||||
class PlatformMessagesSpy {
|
||||
PlatformMessageCallback _callback;
|
||||
PlatformMessageCallback _backup;
|
||||
PlatformMessageCallback? _callback;
|
||||
PlatformMessageCallback? _backup;
|
||||
|
||||
bool get _isActive => _callback != null;
|
||||
|
||||
@ -44,8 +44,8 @@ class PlatformMessagesSpy {
|
||||
/// This is typically called inside a test's `setUp` callback.
|
||||
void setUp() {
|
||||
assert(!_isActive);
|
||||
_callback = (String channel, ByteData data,
|
||||
PlatformMessageResponseCallback callback) {
|
||||
_callback = (String channel, ByteData? data,
|
||||
PlatformMessageResponseCallback? callback) {
|
||||
messages.add(PlatformMessage(
|
||||
channel,
|
||||
const JSONMethodCodec().decodeMethodCall(data),
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.6
|
||||
// @dart = 2.12
|
||||
import 'dart:async';
|
||||
import 'dart:html' as html;
|
||||
import 'dart:js_util' as js_util;
|
||||
@ -22,7 +22,7 @@ void main() {
|
||||
}
|
||||
|
||||
void testMain() {
|
||||
EngineSingletonFlutterWindow window;
|
||||
late EngineSingletonFlutterWindow window;
|
||||
|
||||
setUp(() {
|
||||
ui.webOnlyInitializeEngine();
|
||||
@ -31,7 +31,6 @@ void testMain() {
|
||||
|
||||
tearDown(() async {
|
||||
await window.resetHistory();
|
||||
window = null;
|
||||
});
|
||||
|
||||
test('window.defaultRouteName should not change', () async {
|
||||
@ -42,7 +41,7 @@ void testMain() {
|
||||
expect(window.defaultRouteName, '/initial');
|
||||
|
||||
// Changing the URL in the address bar later shouldn't affect [window.defaultRouteName].
|
||||
strategy.replaceState(null, null, '/newpath');
|
||||
strategy.replaceState(null, '', '/newpath');
|
||||
expect(window.defaultRouteName, '/initial');
|
||||
});
|
||||
|
||||
@ -86,7 +85,7 @@ void testMain() {
|
||||
);
|
||||
await callback.future;
|
||||
expect(window.browserHistory is SingleEntryBrowserHistory, true);
|
||||
expect(window.browserHistory.urlStrategy.getPath(), '/bar');
|
||||
expect(window.browserHistory.urlStrategy!.getPath(), '/bar');
|
||||
|
||||
// We can still receive nav2 update.
|
||||
callback = Completer<void>();
|
||||
@ -103,10 +102,10 @@ void testMain() {
|
||||
);
|
||||
await callback.future;
|
||||
expect(window.browserHistory is MultiEntriesBrowserHistory, true);
|
||||
expect(window.browserHistory.urlStrategy.getPath(), '/baz');
|
||||
expect(window.browserHistory.urlStrategy!.getPath(), '/baz');
|
||||
|
||||
// Throws assertion error if it receives nav1 update after nav2 update.
|
||||
AssertionError caughtAssertion;
|
||||
late AssertionError caughtAssertion;
|
||||
await window.handleNavigationMessage(
|
||||
JSONMethodCodec().encodeMethodCall(MethodCall(
|
||||
'routeUpdated',
|
||||
@ -124,7 +123,7 @@ void testMain() {
|
||||
);
|
||||
// The history does not change.
|
||||
expect(window.browserHistory is MultiEntriesBrowserHistory, true);
|
||||
expect(window.browserHistory.urlStrategy.getPath(), '/baz');
|
||||
expect(window.browserHistory.urlStrategy!.getPath(), '/baz');
|
||||
});
|
||||
|
||||
test('initialize browser history with default url strategy (single)', () async {
|
||||
@ -149,7 +148,7 @@ void testMain() {
|
||||
// The url strategy should've been set to the default, and the path
|
||||
// should've been correctly set to "/bar".
|
||||
expect(window.browserHistory.urlStrategy, isNot(isNull));
|
||||
expect(window.browserHistory.urlStrategy.getPath(), '/bar');
|
||||
expect(window.browserHistory.urlStrategy!.getPath(), '/bar');
|
||||
}, skip: browserEngine == BrowserEngine.webkit); // https://github.com/flutter/flutter/issues/50836
|
||||
|
||||
test('initialize browser history with default url strategy (multiple)', () async {
|
||||
@ -177,7 +176,7 @@ void testMain() {
|
||||
// The url strategy should've been set to the default, and the path
|
||||
// should've been correctly set to "/baz".
|
||||
expect(window.browserHistory.urlStrategy, isNot(isNull));
|
||||
expect(window.browserHistory.urlStrategy.getPath(), '/baz');
|
||||
expect(window.browserHistory.urlStrategy!.getPath(), '/baz');
|
||||
}, skip: browserEngine == BrowserEngine.webkit); // https://github.com/flutter/flutter/issues/50836
|
||||
|
||||
test('can disable location strategy', () async {
|
||||
@ -231,6 +230,13 @@ void testMain() {
|
||||
// Second time is not allowed.
|
||||
expect(() => jsSetUrlStrategy(null), throwsA(isAssertionError));
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/77817
|
||||
test('window.locale(s) are not nullable', () {
|
||||
// If the getters were nullable, these expressions would result in compiler errors.
|
||||
ui.window.locale.countryCode;
|
||||
ui.window.locales.first.countryCode;
|
||||
});
|
||||
}
|
||||
|
||||
void jsSetUrlStrategy(dynamic strategy) {
|
||||
|
||||
@ -4,9 +4,9 @@ environment:
|
||||
sdk: ">=2.2.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
js: 0.6.3-nullsafety.3
|
||||
stream_channel: 2.1.0-nullsafety.3
|
||||
test: 1.16.0-nullsafety.9
|
||||
webkit_inspection_protocol: 0.5.0
|
||||
js: 0.6.3
|
||||
stream_channel: 2.1.0
|
||||
test: 1.16.6
|
||||
webkit_inspection_protocol: 1.0.0
|
||||
ui:
|
||||
path: ../../lib/web_ui
|
||||
|
||||
@ -4,7 +4,7 @@ environment:
|
||||
sdk: ">=2.11.0-0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
path: 1.8.0-nullsafety.3
|
||||
image: 2.1.13
|
||||
js: 0.6.3-nullsafety.3
|
||||
yaml: 2.2.1
|
||||
path: 1.8.0
|
||||
image: 3.0.1
|
||||
js: 0.6.3
|
||||
yaml: 3.0.0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user