Skip to content

Commit 7022a44

Browse files
authored
[webview_flutter_web] Migrate integration tests to package:web. (#7115)
This PR: * Fixes a DOM timing issue that was preventing the _Legacy_ Widget from building. * Makes the `integration_tests` for the package less flaky, now that they seem to be timing out unexpectedly in CI. ## Issues * Fixes flutter/flutter#151693 * Should prevent this flake from happening: [bad](https://ci.chromium.org/ui/p/flutter/builders/try/Linux_web%20web_platform_tests_shard_3%20stable/8987/overview) / [good](https://ci.chromium.org/ui/p/flutter/builders/try/Linux_web%20web_platform_tests_shard_3%20stable/8991/overview) (`Linux_web web_platform_tests_shard_3 stable`)
1 parent 4d02058 commit 7022a44

File tree

8 files changed

+161
-160
lines changed

8 files changed

+161
-160
lines changed

packages/webview_flutter/webview_flutter_web/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.2.3+1
2+
3+
* Fixes DOM timing issue with Legacy Widget build method.
4+
15
## 0.2.3
26

37
* Migrates to `package:web`

packages/webview_flutter/webview_flutter_web/example/integration_test/legacy/webview_flutter_test.dart

Lines changed: 0 additions & 85 deletions
This file was deleted.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// This test flakes badly in headless mode!
6+
7+
import 'dart:async';
8+
9+
import 'package:flutter_test/flutter_test.dart';
10+
import 'package:integration_test/integration_test.dart';
11+
import 'package:web/web.dart' as web;
12+
import 'package:webview_flutter_web_example/legacy/web_view.dart';
13+
14+
import 'wrapped_webview.dart';
15+
16+
void main() async {
17+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
18+
19+
const String someUrl = 'about:blank';
20+
const String fakeUrl = 'https://www.flutter.dev/';
21+
22+
testWidgets('initialUrl', (WidgetTester tester) async {
23+
final Completer<WebViewController> controllerCompleter =
24+
Completer<WebViewController>();
25+
await tester.pumpWidget(
26+
wrappedLegacyWebView(fakeUrl, (WebViewController controller) {
27+
controllerCompleter.complete(controller);
28+
}),
29+
);
30+
await controllerCompleter.future;
31+
// Pump 2 frames so the framework injects the platform view into the DOM.
32+
await tester.pump();
33+
await tester.pump(const Duration(seconds: 5));
34+
35+
// Assert an iframe has been rendered to the DOM with the correct src attribute.
36+
final web.HTMLIFrameElement? element =
37+
web.document.querySelector('iframe') as web.HTMLIFrameElement?;
38+
expect(element, isNotNull);
39+
expect(element!.src, fakeUrl);
40+
});
41+
42+
testWidgets('loadUrl', (WidgetTester tester) async {
43+
final Completer<WebViewController> controllerCompleter =
44+
Completer<WebViewController>();
45+
await tester.pumpWidget(
46+
wrappedLegacyWebView(someUrl, (WebViewController controller) {
47+
controllerCompleter.complete(controller);
48+
}),
49+
);
50+
51+
final WebViewController controller = await controllerCompleter.future;
52+
await controller.loadUrl(fakeUrl);
53+
// Pump 2 frames so the framework injects the platform view into the DOM.
54+
await tester.pump();
55+
await tester.pump(const Duration(seconds: 5));
56+
57+
// Assert an iframe has been rendered to the DOM with the correct src attribute.
58+
final web.HTMLIFrameElement? element =
59+
web.document.querySelector('iframe') as web.HTMLIFrameElement?;
60+
expect(element, isNotNull);
61+
expect(element!.src, fakeUrl);
62+
});
63+
}

packages/webview_flutter/webview_flutter_web/example/integration_test/webview_flutter_test.dart

Lines changed: 30 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,82 +2,63 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'dart:async';
6-
import 'dart:html' as html;
7-
import 'dart:io';
8-
9-
// FIX (dit): Remove these integration tests, or make them run. They currently never fail.
10-
// (They won't run because they use `dart:io`. If you remove all `dart:io` bits from
11-
// this file, they start failing with `fail()`, for example.)
12-
13-
import 'package:flutter/material.dart';
14-
import 'package:flutter/widgets.dart';
155
import 'package:flutter_test/flutter_test.dart';
166
import 'package:integration_test/integration_test.dart';
7+
import 'package:web/web.dart' as web;
178
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
189
import 'package:webview_flutter_web/webview_flutter_web.dart';
1910

11+
import 'wrapped_webview.dart';
12+
2013
Future<void> main() async {
2114
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
2215

23-
final HttpServer server = await HttpServer.bind(InternetAddress.anyIPv4, 0);
24-
unawaited(server.forEach((HttpRequest request) {
25-
if (request.uri.path == '/hello.txt') {
26-
request.response.writeln('Hello, world.');
27-
} else {
28-
fail('unexpected request: ${request.method} ${request.uri}');
29-
}
30-
request.response.close();
31-
}));
32-
final String prefixUrl = 'http://${server.address.address}:${server.port}';
33-
final String primaryUrl = '$prefixUrl/hello.txt';
16+
const String fakeUrl = 'about:blank';
3417

3518
testWidgets('loadRequest', (WidgetTester tester) async {
36-
final WebWebViewController controller =
37-
WebWebViewController(const PlatformWebViewControllerCreationParams());
19+
final WebWebViewController controller = WebWebViewController(
20+
const PlatformWebViewControllerCreationParams(),
21+
);
3822
await controller.loadRequest(
39-
LoadRequestParams(uri: Uri.parse(primaryUrl)),
23+
LoadRequestParams(uri: Uri.parse(fakeUrl)),
4024
);
4125

4226
await tester.pumpWidget(
43-
Directionality(
44-
textDirection: TextDirection.ltr,
45-
child: Builder(builder: (BuildContext context) {
46-
return WebWebViewWidget(
47-
PlatformWebViewWidgetCreationParams(controller: controller),
48-
).build(context);
49-
}),
50-
),
27+
wrappedWebView(controller),
5128
);
29+
// Pump 2 frames so the framework injects the platform view into the DOM.
30+
// The duration of the second pump is set so the browser has some idle time
31+
// to actually show the contents of the iFrame.
32+
await tester.pump();
33+
await tester.pump(const Duration(seconds: 1));
5234

53-
// Assert an iframe has been rendered to the DOM with the correct src attribute.
54-
final html.IFrameElement? element =
55-
html.document.querySelector('iframe') as html.IFrameElement?;
35+
// Assert an iFrame has been rendered to the DOM with the correct src attribute.
36+
final web.HTMLIFrameElement? element =
37+
web.document.querySelector('iframe') as web.HTMLIFrameElement?;
5638
expect(element, isNotNull);
57-
expect(element!.src, primaryUrl);
39+
expect(element!.src, fakeUrl);
5840
});
5941

6042
testWidgets('loadHtmlString', (WidgetTester tester) async {
61-
final WebWebViewController controller =
62-
WebWebViewController(const PlatformWebViewControllerCreationParams());
43+
final WebWebViewController controller = WebWebViewController(
44+
const PlatformWebViewControllerCreationParams(),
45+
);
6346
await controller.loadHtmlString(
6447
'data:text/html;charset=utf-8,${Uri.encodeFull('test html')}',
6548
);
6649

6750
await tester.pumpWidget(
68-
Directionality(
69-
textDirection: TextDirection.ltr,
70-
child: Builder(builder: (BuildContext context) {
71-
return WebWebViewWidget(
72-
PlatformWebViewWidgetCreationParams(controller: controller),
73-
).build(context);
74-
}),
75-
),
51+
wrappedWebView(controller),
7652
);
53+
// Pump 2 frames so the framework injects the platform view into the DOM.
54+
// The duration of the second pump is set so the browser has some idle time
55+
// to actually show the contents of the iFrame.
56+
await tester.pump();
57+
await tester.pump(const Duration(seconds: 1));
7758

78-
// Assert an iframe has been rendered to the DOM with the correct src attribute.
79-
final html.IFrameElement? element =
80-
html.document.querySelector('iframe') as html.IFrameElement?;
59+
// Assert an iFrame has been rendered to the DOM with the correct src attribute.
60+
final web.HTMLIFrameElement? element =
61+
web.document.querySelector('iframe') as web.HTMLIFrameElement?;
8162
expect(element, isNotNull);
8263
expect(
8364
element!.src,
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/material.dart';
6+
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
7+
import 'package:webview_flutter_web/webview_flutter_web.dart';
8+
import 'package:webview_flutter_web_example/legacy/web_view.dart';
9+
10+
/// Returns the webview widget for a given [controller], wrapped so it works
11+
/// in our integration tests.
12+
Widget wrappedWebView(WebWebViewController controller) {
13+
return _wrapped(
14+
Builder(
15+
builder: (BuildContext ctx) => PlatformWebViewWidget(
16+
PlatformWebViewWidgetCreationParams(controller: controller),
17+
).build(ctx),
18+
),
19+
);
20+
}
21+
22+
/// Returns a (legacy) webview widget for an [url], that calls [onCreated] when
23+
/// done, wrapped so it works in our integration tests.
24+
Widget wrappedLegacyWebView(String url, WebViewCreatedCallback onCreated) {
25+
return _wrapped(
26+
WebView(
27+
initialUrl: url,
28+
onWebViewCreated: onCreated,
29+
),
30+
);
31+
}
32+
33+
// Wraps a [child] widget in the scaffolding this test needs.
34+
Widget _wrapped(Widget child) {
35+
return MaterialApp(
36+
home: Scaffold(
37+
body: Center(
38+
child: Container(
39+
decoration: BoxDecoration(
40+
border: Border.all(
41+
color: Colors.red,
42+
),
43+
),
44+
width: 320,
45+
height: 200,
46+
child: child,
47+
),
48+
),
49+
),
50+
);
51+
}

packages/webview_flutter/webview_flutter_web/example/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ dependencies:
1111
sdk: flutter
1212
flutter_web_plugins:
1313
sdk: flutter
14+
web: ^0.5.0
1415
webview_flutter_platform_interface: ^2.0.0
1516
webview_flutter_web:
1617
# When depending on this package from a real application you should use:

packages/webview_flutter/webview_flutter_web/lib/src/webview_flutter_web_legacy.dart

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import 'dart:async';
66
import 'dart:convert';
77
import 'dart:js_interop';
8-
import 'dart:ui_web' as ui_web;
98

109
import 'package:flutter/foundation.dart';
1110
import 'package:flutter/gestures.dart';
@@ -21,17 +20,6 @@ import 'http_request_factory.dart';
2120
///
2221
/// This is used as the default implementation for [WebView.platform] on web.
2322
class WebWebViewPlatform implements WebViewPlatform {
24-
/// Constructs a new instance of [WebWebViewPlatform].
25-
WebWebViewPlatform() {
26-
ui_web.platformViewRegistry.registerViewFactory(
27-
'webview-iframe',
28-
(int viewId) => web.HTMLIFrameElement()
29-
..id = 'webview-$viewId'
30-
..width = '100%'
31-
..height = '100%'
32-
..style.border = 'none');
33-
}
34-
3523
@override
3624
Widget build({
3725
required BuildContext context,
@@ -41,23 +29,21 @@ class WebWebViewPlatform implements WebViewPlatform {
4129
WebViewPlatformCreatedCallback? onWebViewPlatformCreated,
4230
Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
4331
}) {
44-
return HtmlElementView(
45-
viewType: 'webview-iframe',
46-
onPlatformViewCreated: (int viewId) {
47-
if (onWebViewPlatformCreated == null) {
48-
return;
49-
}
50-
final web.HTMLIFrameElement element = web.document
51-
.getElementById('webview-$viewId')! as web.HTMLIFrameElement;
52-
32+
return HtmlElementView.fromTagName(
33+
tagName: 'iframe',
34+
onElementCreated: (Object iFrame) {
35+
iFrame as web.HTMLIFrameElement;
36+
iFrame.style.border = 'none';
5337
final String? initialUrl = creationParams.initialUrl;
5438
if (initialUrl != null) {
5539
// ignore: unsafe_html
56-
element.src = initialUrl;
40+
iFrame.src = initialUrl;
41+
}
42+
if (onWebViewPlatformCreated != null) {
43+
onWebViewPlatformCreated(
44+
WebWebViewPlatformController(iFrame),
45+
);
5746
}
58-
onWebViewPlatformCreated(WebWebViewPlatformController(
59-
element,
60-
));
6147
},
6248
);
6349
}

0 commit comments

Comments
 (0)