diff --git a/lib/web_ui/dart_test_safari.yaml b/lib/web_ui/dart_test_safari.yaml new file mode 100644 index 0000000000000..54f2064859b9e --- /dev/null +++ b/lib/web_ui/dart_test_safari.yaml @@ -0,0 +1,7 @@ +# For more information on test and runner configurations: +# +# * https://github.com/dart-lang/test/blob/master/pkgs/test/doc/configuration.md#platforms + +platforms: + - safari + - vm diff --git a/lib/web_ui/dev/common.dart b/lib/web_ui/dev/common.dart index 57102e8411519..49e82a96706a0 100644 --- a/lib/web_ui/dev/common.dart +++ b/lib/web_ui/dev/common.dart @@ -48,6 +48,7 @@ abstract class PlatformBinding { String getChromeExecutablePath(io.Directory versionDir); String getFirefoxExecutablePath(io.Directory versionDir); String getFirefoxLatestVersionUrl(); + String getSafariSystemExecutablePath(); } const String _kBaseDownloadUrl = @@ -79,6 +80,10 @@ class _LinuxBinding implements PlatformBinding { @override String getFirefoxLatestVersionUrl() => 'https://download.mozilla.org/?product=firefox-latest&os=linux64&lang=en-US'; + + @override + String getSafariSystemExecutablePath() => + throw UnsupportedError('Safari is not supported on Linux'); } class _MacBinding implements PlatformBinding { @@ -112,6 +117,10 @@ class _MacBinding implements PlatformBinding { @override String getFirefoxLatestVersionUrl() => 'https://download.mozilla.org/?product=firefox-latest&os=osx&lang=en-US'; + + @override + String getSafariSystemExecutablePath() => + '/Applications/Safari.app/Contents/MacOS/Safari'; } class BrowserInstallation { diff --git a/lib/web_ui/dev/firefox_installer.dart b/lib/web_ui/dev/firefox_installer.dart index d9bd9b6989277..1554f7b584eea 100644 --- a/lib/web_ui/dev/firefox_installer.dart +++ b/lib/web_ui/dev/firefox_installer.dart @@ -15,7 +15,7 @@ import 'environment.dart'; class FirefoxArgParser extends BrowserArgParser { static final FirefoxArgParser _singletonInstance = FirefoxArgParser._(); - /// The [ChromeArgParser] singleton. + /// The [FirefoxArgParser] singleton. static FirefoxArgParser get instance => _singletonInstance; String _version; diff --git a/lib/web_ui/dev/safari.dart b/lib/web_ui/dev/safari.dart new file mode 100644 index 0000000000000..f96bd7cbf7772 --- /dev/null +++ b/lib/web_ui/dev/safari.dart @@ -0,0 +1,67 @@ +// 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 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'environment.dart'; + +import 'package:path/path.dart' as path; +import 'package:pedantic/pedantic.dart'; + +import 'package:test_core/src/util/io.dart'; // ignore: implementation_imports + +import 'browser.dart'; +import 'safari_installation.dart'; +import 'common.dart'; + +/// A class for running an instance of Safari. +/// +/// Most of the communication with the browser is expected to happen via HTTP, +/// so this exposes a bare-bones API. The browser starts as soon as the class is +/// constructed, and is killed when [close] is called. +/// +/// Any errors starting or running the process are reported through [onExit]. +class Safari extends Browser { + @override + final name = 'Safari'; + + static String version; + + /// Starts a new instance of Safari open to the given [url], which may be a + /// [Uri] or a [String]. + factory Safari(Uri url, {bool debug = false}) { + version = SafariArgParser.instance.version; + + assert(version != null); + return Safari._(() async { + // TODO(nurhan): Configure info log for LUCI. + final BrowserInstallation installation = await getOrInstallSafari( + version, + infoLog: DevNull(), + ); + + // Safari will only open files (not general URLs) via the command-line + // API, so we create a dummy file to redirect it to the page we actually + // want it to load. + final Directory redirectDir = Directory( + path.join(environment.webUiDartToolDir.path), + ); + final redirect = path.join(redirectDir.path, 'redirect.html'); + File(redirect).writeAsStringSync( + ''); + + var process = + await Process.start(installation.executable, [redirect] /* args */); + + unawaited(process.exitCode + .then((_) => File(redirect).deleteSync(recursive: true))); + + return process; + }); + } + + Safari._(Future startBrowser()) : super(startBrowser); +} diff --git a/lib/web_ui/dev/safari_installation.dart b/lib/web_ui/dev/safari_installation.dart new file mode 100644 index 0000000000000..2f651d72d2202 --- /dev/null +++ b/lib/web_ui/dev/safari_installation.dart @@ -0,0 +1,79 @@ +// 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 'dart:async'; +import 'dart:io' as io; + +import 'package:args/args.dart'; + +import 'common.dart'; + +class SafariArgParser extends BrowserArgParser { + static final SafariArgParser _singletonInstance = SafariArgParser._(); + + /// The [SafariArgParser] singleton. + static SafariArgParser get instance => _singletonInstance; + + String _version; + + SafariArgParser._(); + + @override + void populateOptions(ArgParser argParser) { + argParser + ..addOption( + 'safari-version', + defaultsTo: 'system', + help: 'The Safari version to use while running tests. The Safari ' + 'browser installed on the system is used as the only option now.' + 'Soon we will add support for using different versions using the ' + 'tech previews.', + ); + } + + @override + void parseOptions(ArgResults argResults) { + _version = argResults['safari-version']; + assert(_version == 'system'); + } + + @override + String get version => _version; +} + +/// Returns the installation of Safari. +/// +/// Currently uses the Safari version installed on the operating system. +/// +/// Latest Safari version for Catalina, Mojave, High Siera is 13. +/// +/// Latest Safari version for Sierra is 12. +// TODO(nurhan): user latest version to download and install the latest +// technology preview. +Future getOrInstallSafari( + String requestedVersion, { + StringSink infoLog, +}) async { + + // These tests are aimed to run only on MacOs machines local or on LUCI. + if (!io.Platform.isMacOS) { + throw UnimplementedError('Safari on ${io.Platform.operatingSystem} is' + ' not supported. Safari is only supported on MacOS.'); + } + + infoLog ??= io.stdout; + + if (requestedVersion == 'system') { + // Since Safari is included in MacOS, always assume there will be one on the + // system. + infoLog.writeln('Using the system version that is already installed.'); + return BrowserInstallation( + version: 'system', + executable: PlatformBinding.instance.getSafariSystemExecutablePath(), + ); + } else { + infoLog.writeln('Unsupported version $requestedVersion.'); + throw UnimplementedError(); + } +} diff --git a/lib/web_ui/dev/supported_browsers.dart b/lib/web_ui/dev/supported_browsers.dart index 3d16f1a62013a..5581e1ba82eb2 100644 --- a/lib/web_ui/dev/supported_browsers.dart +++ b/lib/web_ui/dev/supported_browsers.dart @@ -10,7 +10,9 @@ import 'chrome_installer.dart'; import 'common.dart'; import 'environment.dart'; import 'firefox.dart'; -import 'firefox_installer.dart'; // ignore: implementation_imports +import 'firefox_installer.dart'; +import 'safari.dart'; +import 'safari_installation.dart'; /// Utilities for browsers, that tests are supported. /// @@ -22,18 +24,20 @@ import 'firefox_installer.dart'; // ignore: implementation_imports /// One should also implement [BrowserArgParser] and add it to the [argParsers]. class SupportedBrowsers { final List argParsers = - List.of([ChromeArgParser.instance, FirefoxArgParser.instance]); + List.of([ChromeArgParser.instance, FirefoxArgParser.instance, SafariArgParser.instance]); - final List supportedBrowserNames = ['chrome', 'firefox']; + final List supportedBrowserNames = ['chrome', 'firefox', 'safari']; final Map supportedBrowsersToRuntimes = { 'chrome': Runtime.chrome, - 'firefox': Runtime.firefox + 'firefox': Runtime.firefox, + 'safari': Runtime.safari, }; final Map browserToConfiguration = { 'chrome': '--configuration=${environment.webUiRootDir.path}/dart_test_chrome.yaml', 'firefox': '--configuration=${environment.webUiRootDir.path}/dart_test_firefox.yaml', + 'safari': '--configuration=${environment.webUiRootDir.path}/dart_test_safari.yaml', }; static final SupportedBrowsers _singletonInstance = SupportedBrowsers._(); @@ -48,6 +52,8 @@ class SupportedBrowsers { return Chrome(url, debug: debug); } else if (runtime == Runtime.firefox) { return Firefox(url, debug: debug); + } else if (runtime == Runtime.safari) { + return Safari(url, debug: debug); } else { throw new UnsupportedError('The browser type not supported in tests'); }