| // Copyright 2013 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. |
| |
| // @dart = 2.6 |
| import 'dart:async'; |
| import 'dart:js_util' as js_util; |
| import 'dart:html' as html; |
| import 'dart:typed_data'; |
| |
| import 'package:test/bootstrap/browser.dart'; |
| import 'package:test/test.dart'; |
| import 'package:ui/ui.dart' as ui; |
| import 'package:ui/src/engine.dart'; |
| |
| const int kPhysicalKeyA = 0x00070004; |
| const int kLogicalKeyA = 0x00000000061; |
| |
| void main() { |
| internalBootstrapBrowserTest(() => testMain); |
| } |
| |
| void testMain() { |
| test('onTextScaleFactorChanged preserves the zone', () { |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| final ui.VoidCallback callback = () { |
| expect(Zone.current, innerZone); |
| }; |
| window.onTextScaleFactorChanged = callback; |
| |
| // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. |
| expect(window.onTextScaleFactorChanged, same(callback)); |
| }); |
| |
| EnginePlatformDispatcher.instance.invokeOnTextScaleFactorChanged(); |
| }); |
| |
| test('onPlatformBrightnessChanged preserves the zone', () { |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| final ui.VoidCallback callback = () { |
| expect(Zone.current, innerZone); |
| }; |
| window.onPlatformBrightnessChanged = callback; |
| |
| // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. |
| expect(window.onPlatformBrightnessChanged, same(callback)); |
| }); |
| |
| EnginePlatformDispatcher.instance.invokeOnPlatformBrightnessChanged(); |
| }); |
| |
| test('onMetricsChanged preserves the zone', () { |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| final ui.VoidCallback callback = () { |
| expect(Zone.current, innerZone); |
| }; |
| window.onMetricsChanged = callback; |
| |
| // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. |
| expect(window.onMetricsChanged, same(callback)); |
| }); |
| |
| EnginePlatformDispatcher.instance.invokeOnMetricsChanged(); |
| }); |
| |
| test('onLocaleChanged preserves the zone', () { |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| final ui.VoidCallback callback = () { |
| expect(Zone.current, innerZone); |
| }; |
| window.onLocaleChanged = callback; |
| |
| // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. |
| expect(window.onLocaleChanged, same(callback)); |
| }); |
| |
| EnginePlatformDispatcher.instance.invokeOnLocaleChanged(); |
| }); |
| |
| test('onBeginFrame preserves the zone', () { |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| final ui.FrameCallback callback = (_) { |
| expect(Zone.current, innerZone); |
| }; |
| window.onBeginFrame = callback; |
| |
| // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. |
| expect(window.onBeginFrame, same(callback)); |
| }); |
| |
| EnginePlatformDispatcher.instance.invokeOnBeginFrame(null); |
| }); |
| |
| test('onReportTimings preserves the zone', () { |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| final ui.TimingsCallback callback = (_) { |
| expect(Zone.current, innerZone); |
| }; |
| window.onReportTimings = callback; |
| |
| // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. |
| expect(window.onReportTimings, same(callback)); |
| }); |
| |
| EnginePlatformDispatcher.instance.invokeOnReportTimings(null); |
| }); |
| |
| test('onDrawFrame preserves the zone', () { |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| final ui.VoidCallback callback = () { |
| expect(Zone.current, innerZone); |
| }; |
| window.onDrawFrame = callback; |
| |
| // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. |
| expect(window.onDrawFrame, same(callback)); |
| }); |
| |
| EnginePlatformDispatcher.instance.invokeOnDrawFrame(); |
| }); |
| |
| test('onPointerDataPacket preserves the zone', () { |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| final ui.PointerDataPacketCallback callback = (_) { |
| expect(Zone.current, innerZone); |
| }; |
| window.onPointerDataPacket = callback; |
| |
| // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. |
| expect(window.onPointerDataPacket, same(callback)); |
| }); |
| |
| EnginePlatformDispatcher.instance.invokeOnPointerDataPacket(null); |
| }); |
| |
| test('invokeOnKeyData returns normally when onKeyData is null', () { |
| final ui.KeyData keyData = ui.KeyData( |
| timeStamp: Duration(milliseconds: 1), |
| type: ui.KeyEventType.repeat, |
| physical: kPhysicalKeyA, |
| logical: kLogicalKeyA, |
| character: 'a', |
| synthesized: true, |
| ); |
| expect(() { |
| EnginePlatformDispatcher.instance.invokeOnKeyData(keyData, (bool result) { |
| expect(result, isFalse); |
| }); |
| }, returnsNormally); |
| }); |
| |
| test('onKeyData preserves the zone', () { |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| final ui.KeyDataCallback onKeyData = (_) { |
| expect(Zone.current, innerZone); |
| return false; |
| }; |
| window.onKeyData = onKeyData; |
| |
| // Test that the getter returns the exact same onKeyData, e.g. it doesn't |
| // wrap it. |
| expect(window.onKeyData, same(onKeyData)); |
| }); |
| |
| final ui.KeyData keyData = ui.KeyData( |
| timeStamp: Duration(milliseconds: 1), |
| type: ui.KeyEventType.repeat, |
| physical: kPhysicalKeyA, |
| logical: kLogicalKeyA, |
| character: 'a', |
| synthesized: true, |
| ); |
| EnginePlatformDispatcher.instance.invokeOnKeyData(keyData, (bool result) { |
| expect(result, isFalse); |
| }); |
| |
| window.onKeyData = null; |
| }); |
| |
| test('onSemanticsEnabledChanged preserves the zone', () { |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| final ui.VoidCallback callback = () { |
| expect(Zone.current, innerZone); |
| }; |
| window.onSemanticsEnabledChanged = callback; |
| |
| // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. |
| expect(window.onSemanticsEnabledChanged, same(callback)); |
| }); |
| |
| EnginePlatformDispatcher.instance.invokeOnSemanticsEnabledChanged(); |
| }); |
| |
| test('onSemanticsAction preserves the zone', () { |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| final ui.SemanticsActionCallback callback = (_, __, ___) { |
| expect(Zone.current, innerZone); |
| }; |
| window.onSemanticsAction = callback; |
| |
| // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. |
| expect(window.onSemanticsAction, same(callback)); |
| }); |
| |
| EnginePlatformDispatcher.instance.invokeOnSemanticsAction(null, null, null); |
| }); |
| |
| test('onAccessibilityFeaturesChanged preserves the zone', () { |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| final ui.VoidCallback callback = () { |
| expect(Zone.current, innerZone); |
| }; |
| window.onAccessibilityFeaturesChanged = callback; |
| |
| // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. |
| expect(window.onAccessibilityFeaturesChanged, same(callback)); |
| }); |
| |
| EnginePlatformDispatcher.instance.invokeOnAccessibilityFeaturesChanged(); |
| }); |
| |
| test('onPlatformMessage preserves the zone', () { |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| final ui.PlatformMessageCallback callback = (_, __, ___) { |
| expect(Zone.current, innerZone); |
| }; |
| window.onPlatformMessage = callback; |
| |
| // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. |
| expect(window.onPlatformMessage, same(callback)); |
| }); |
| |
| EnginePlatformDispatcher.instance.invokeOnPlatformMessage(null, null, null); |
| }); |
| |
| test('sendPlatformMessage preserves the zone', () async { |
| final Completer<void> completer = Completer<void>(); |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| final ByteData inputData = ByteData(4); |
| inputData.setUint32(0, 42); |
| window.sendPlatformMessage( |
| 'flutter/debug-echo', |
| inputData, |
| (outputData) { |
| expect(Zone.current, innerZone); |
| completer.complete(); |
| }, |
| ); |
| }); |
| |
| await completer.future; |
| }); |
| |
| test('sendPlatformMessage responds even when channel is unknown', () async { |
| bool responded = false; |
| |
| final ByteData inputData = ByteData(4); |
| inputData.setUint32(0, 42); |
| window.sendPlatformMessage( |
| 'flutter/__unknown__channel__', |
| null, |
| (outputData) { |
| responded = true; |
| expect(outputData, isNull); |
| }, |
| ); |
| |
| await Future<void>.delayed(const Duration(milliseconds: 1)); |
| expect(responded, isTrue); |
| }); |
| |
| /// Regression test for https://github.com/flutter/flutter/issues/66128. |
| test('setPreferredOrientation responds even if browser doesn\'t support api', () async { |
| final html.Screen screen = html.window.screen; |
| js_util.setProperty(screen, 'orientation', null); |
| bool responded = false; |
| |
| final Completer<void> completer = Completer<void>(); |
| final ByteData inputData = JSONMethodCodec().encodeMethodCall(MethodCall( |
| 'SystemChrome.setPreferredOrientations', |
| <dynamic>[])); |
| |
| window.sendPlatformMessage( |
| 'flutter/platform', |
| inputData, |
| (outputData) { |
| responded = true; |
| completer.complete(); |
| }, |
| ); |
| await completer.future; |
| expect(responded, true); |
| }); |
| |
| test('SingletonFlutterWindow implements locale, locales, and locale change notifications', () async { |
| // This will count how many times we notified about locale changes. |
| int localeChangedCount = 0; |
| window.onLocaleChanged = () { |
| localeChangedCount += 1; |
| }; |
| |
| // Cause DomRenderer to initialize itself. |
| domRenderer; |
| |
| // We populate the initial list of locales automatically (only test that we |
| // got some locales; some contributors may be in different locales, so we |
| // can't test the exact contents). |
| expect(window.locale, isA<ui.Locale>()); |
| expect(window.locales, isNotEmpty); |
| |
| // Trigger a change notification (reset locales because the notification |
| // doesn't actually change the list of languages; the test only observes |
| // that the list is populated again). |
| EnginePlatformDispatcher.instance.debugResetLocales(); |
| expect(window.locales, isEmpty); |
| expect(window.locale, equals(const ui.Locale.fromSubtags())); |
| expect(localeChangedCount, 0); |
| html.window.dispatchEvent(html.Event('languagechange')); |
| expect(window.locales, isNotEmpty); |
| expect(localeChangedCount, 1); |
| }); |
| |
| test('dispatches browser event on flutter/service_worker channel', () async { |
| final Completer<void> completer = Completer<void>(); |
| html.window.addEventListener('flutter-first-frame', completer.complete); |
| final Zone innerZone = Zone.current.fork(); |
| |
| innerZone.runGuarded(() { |
| window.sendPlatformMessage( |
| 'flutter/service_worker', |
| ByteData(0), |
| (outputData) { }, |
| ); |
| }); |
| |
| await expectLater(completer.future, completes); |
| }); |
| } |