// 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. import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:url_launcher/link.dart'; import 'package:url_launcher/src/link.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; import 'mocks/mock_url_launcher_platform.dart'; void main() { late MockUrlLauncher mock; setUp(() { mock = MockUrlLauncher(); UrlLauncherPlatform.instance = mock; }); group('Link', () { testWidgets('handles null uri correctly', (WidgetTester tester) async { bool isBuilt = false; FollowLink? followLink; final Link link = Link( uri: null, builder: (BuildContext context, FollowLink? followLink2) { isBuilt = true; followLink = followLink2; return Container(); }, ); await tester.pumpWidget(link); expect(link.isDisabled, isTrue); expect(isBuilt, isTrue); expect(followLink, isNull); }); testWidgets('calls url_launcher for external URLs with blank target', (WidgetTester tester) async { FollowLink? followLink; await tester.pumpWidget(Link( uri: Uri.parse('http://example.com/foobar'), target: LinkTarget.blank, builder: (BuildContext context, FollowLink? followLink2) { followLink = followLink2; return Container(); }, )); mock ..setLaunchExpectations( url: 'http://example.com/foobar', launchMode: PreferredLaunchMode.externalApplication, universalLinksOnly: false, enableJavaScript: true, enableDomStorage: true, headers: {}, webOnlyWindowName: null, showTitle: false, ) ..setResponse(true); await followLink!(); // Calling canLaunch just to pre-check launch is an anti-pattern, since // canLaunch doesn't always work, so ensure that it's not called. expect(mock.canLaunchCalled, isFalse); expect(mock.launchCalled, isTrue); }); testWidgets('calls url_launcher for external URLs with self target', (WidgetTester tester) async { FollowLink? followLink; await tester.pumpWidget(Link( uri: Uri.parse('http://example.com/foobar'), target: LinkTarget.self, builder: (BuildContext context, FollowLink? followLink2) { followLink = followLink2; return Container(); }, )); mock ..setLaunchExpectations( url: 'http://example.com/foobar', launchMode: PreferredLaunchMode.inAppBrowserView, universalLinksOnly: false, enableJavaScript: true, enableDomStorage: true, headers: {}, webOnlyWindowName: null, showTitle: false, ) ..setResponse(true); await followLink!(); // Calling canLaunch just to pre-check launch is an anti-pattern, since // canLaunch doesn't always work, so ensure that it's not called. expect(mock.canLaunchCalled, isFalse); expect(mock.launchCalled, isTrue); }); testWidgets('pushes to framework for internal route names', (WidgetTester tester) async { final Uri uri = Uri.parse('/foo/bar'); FollowLink? followLink; await tester.pumpWidget(MaterialApp( routes: { '/': (BuildContext context) => Link( uri: uri, builder: (BuildContext context, FollowLink? followLink2) { followLink = followLink2; return Container(); }, ), '/foo/bar': (BuildContext context) => Container(), }, )); bool frameworkCalled = false; final Future Function(Object?, String) originalPushFunction = pushRouteToFrameworkFunction; pushRouteToFrameworkFunction = (Object? _, String __) { frameworkCalled = true; return Future.value(ByteData(0)); }; await followLink!(); // Shouldn't use url_launcher when uri is an internal route name. expect(mock.canLaunchCalled, isFalse); expect(mock.launchCalled, isFalse); // A route should have been pushed to the framework. expect(frameworkCalled, true); // Restore the original function. pushRouteToFrameworkFunction = originalPushFunction; }); }); }