Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 4df0880

Browse files
ditmanmvanbeusekom
andauthored
[url_launcher_web] Migrate to null-safety (#3522)
This version uses auto-generated Mocks from mockito5 for its tests. For now, tests only run in the `master` channel. Co-authored-by: Maurits van Beusekom <[email protected]>
1 parent 7a3b4ae commit 4df0880

18 files changed

+956
-261
lines changed

analysis_options.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ analyzer:
44
# Ignore generated files
55
- '**/*.g.dart'
66
- 'lib/src/generated/*.dart'
7+
- '**/*.mocks.dart' # Mockito @GenerateMocks
78
errors:
89
always_require_non_null_named_parameters: false # not needed with nnbd
910
unnecessary_null_comparison: false # Turned as long as nnbd mix-mode is supported.

packages/url_launcher/url_launcher_web/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 2.0.0-nullsafety
2+
3+
- Migrate to null safety.
4+
15
# 0.1.5+3
26

37
- Fix Link misalignment [issue](https://github.com/flutter/flutter/issues/70053).
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Testing
2+
3+
This package utilizes the `integration_test` package to run its tests in a web browser.
4+
5+
See [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) for more info.
6+
7+
## Running the tests
8+
9+
Make sure you have updated to the latest Flutter master.
10+
11+
1. Check what version of Chrome is running on the machine you're running tests on.
12+
13+
2. Download and install driver for that version from here:
14+
* <https://chromedriver.chromium.org/downloads>
15+
16+
3. Start the driver using `chromedriver --port=4444`
17+
18+
4. Run tests: `flutter drive -d web-server --browser-name=chrome --driver=test_driver/integration_test_driver.dart --target=integration_test/TEST_NAME.dart`, or (in Linux):
19+
20+
* Single: `./run_test.sh integration_test/TEST_NAME.dart`
21+
* All: `./run_test.sh`
22+
23+
## Mocks
24+
25+
There's `.mocks.dart` files next to the test files that use them.
26+
27+
They're [generated by Mockito](https://github.com/dart-lang/mockito/blob/master/NULL_SAFETY_README.md#code-generation).
28+
29+
Mocks might be manually re-generated with the following command: `flutter pub run build_runner build`. If there are any changes in the mocks, feel free to commit them.
30+
31+
(Mocks will be auto-generated by the `run_test.sh` script as well.)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
targets:
2+
$default:
3+
sources:
4+
- integration_test/*.dart
5+
- lib/$lib$
6+
- $package$
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// Copyright 2019 The Chromium 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 'dart:html' as html;
6+
import 'dart:js_util';
7+
import 'package:flutter/widgets.dart';
8+
import 'package:flutter_test/flutter_test.dart';
9+
import 'package:url_launcher_platform_interface/link.dart';
10+
import 'package:url_launcher_web/src/link.dart';
11+
import 'package:integration_test/integration_test.dart';
12+
13+
void main() {
14+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
15+
16+
group('Link Widget', () {
17+
testWidgets('creates anchor with correct attributes',
18+
(WidgetTester tester) async {
19+
final Uri uri = Uri.parse('http://foobar/example?q=1');
20+
await tester.pumpWidget(Directionality(
21+
textDirection: TextDirection.ltr,
22+
child: WebLinkDelegate(TestLinkInfo(
23+
uri: uri,
24+
target: LinkTarget.blank,
25+
builder: (BuildContext context, FollowLink? followLink) {
26+
return Container(width: 100, height: 100);
27+
},
28+
)),
29+
));
30+
// Platform view creation happens asynchronously.
31+
await tester.pumpAndSettle();
32+
33+
final html.Element anchor = _findSingleAnchor();
34+
expect(anchor.getAttribute('href'), uri.toString());
35+
expect(anchor.getAttribute('target'), '_blank');
36+
37+
final Uri uri2 = Uri.parse('http://foobar2/example?q=2');
38+
await tester.pumpWidget(Directionality(
39+
textDirection: TextDirection.ltr,
40+
child: WebLinkDelegate(TestLinkInfo(
41+
uri: uri2,
42+
target: LinkTarget.self,
43+
builder: (BuildContext context, FollowLink? followLink) {
44+
return Container(width: 100, height: 100);
45+
},
46+
)),
47+
));
48+
await tester.pumpAndSettle();
49+
50+
// Check that the same anchor has been updated.
51+
expect(anchor.getAttribute('href'), uri2.toString());
52+
expect(anchor.getAttribute('target'), '_self');
53+
});
54+
55+
testWidgets('sizes itself correctly', (WidgetTester tester) async {
56+
final Key containerKey = GlobalKey();
57+
final Uri uri = Uri.parse('http://foobar');
58+
await tester.pumpWidget(Directionality(
59+
textDirection: TextDirection.ltr,
60+
child: Center(
61+
child: ConstrainedBox(
62+
constraints: BoxConstraints.tight(Size(100.0, 100.0)),
63+
child: WebLinkDelegate(TestLinkInfo(
64+
uri: uri,
65+
target: LinkTarget.blank,
66+
builder: (BuildContext context, FollowLink? followLink) {
67+
return Container(
68+
key: containerKey,
69+
child: SizedBox(width: 50.0, height: 50.0),
70+
);
71+
},
72+
)),
73+
),
74+
),
75+
));
76+
await tester.pumpAndSettle();
77+
78+
final Size containerSize = tester.getSize(find.byKey(containerKey));
79+
// The Stack widget inserted by the `WebLinkDelegate` shouldn't loosen the
80+
// constraints before passing them to the inner container. So the inner
81+
// container should respect the tight constraints given by the ancestor
82+
// `ConstrainedBox` widget.
83+
expect(containerSize.width, 100.0);
84+
expect(containerSize.height, 100.0);
85+
});
86+
87+
// See: https://github.com/flutter/plugins/pull/3522#discussion_r574703724
88+
testWidgets('uri can be null', (WidgetTester tester) async {
89+
await tester.pumpWidget(Directionality(
90+
textDirection: TextDirection.ltr,
91+
child: WebLinkDelegate(TestLinkInfo(
92+
uri: null,
93+
target: LinkTarget.defaultTarget,
94+
builder: (BuildContext context, FollowLink? followLink) {
95+
return Container(width: 100, height: 100);
96+
},
97+
)),
98+
));
99+
// Platform view creation happens asynchronously.
100+
await tester.pumpAndSettle();
101+
102+
final html.Element anchor = _findSingleAnchor();
103+
expect(anchor.hasAttribute('href'), false);
104+
});
105+
});
106+
}
107+
108+
html.Element _findSingleAnchor() {
109+
final List<html.Element> foundAnchors = <html.Element>[];
110+
for (final html.Element anchor in html.document.querySelectorAll('a')) {
111+
if (hasProperty(anchor, linkViewIdProperty)) {
112+
foundAnchors.add(anchor);
113+
}
114+
}
115+
116+
// Search inside platform views with shadow roots as well.
117+
for (final html.Element platformView
118+
in html.document.querySelectorAll('flt-platform-view')) {
119+
final html.ShadowRoot shadowRoot = platformView.shadowRoot!;
120+
if (shadowRoot != null) {
121+
for (final html.Element anchor in shadowRoot.querySelectorAll('a')) {
122+
if (hasProperty(anchor, linkViewIdProperty)) {
123+
foundAnchors.add(anchor);
124+
}
125+
}
126+
}
127+
}
128+
129+
return foundAnchors.single;
130+
}
131+
132+
class TestLinkInfo extends LinkInfo {
133+
@override
134+
final LinkWidgetBuilder builder;
135+
136+
@override
137+
final Uri? uri;
138+
139+
@override
140+
final LinkTarget target;
141+
142+
@override
143+
bool get isDisabled => uri == null;
144+
145+
TestLinkInfo({
146+
required this.uri,
147+
required this.target,
148+
required this.builder,
149+
});
150+
}

0 commit comments

Comments
 (0)