diff options
Diffstat (limited to '')
19 files changed, 748 insertions, 0 deletions
diff --git a/testing/firefox-ui/harness/MANIFEST.in b/testing/firefox-ui/harness/MANIFEST.in new file mode 100644 index 0000000000..cf628b039c --- /dev/null +++ b/testing/firefox-ui/harness/MANIFEST.in @@ -0,0 +1,2 @@ +exclude MANIFEST.in +include requirements.txt diff --git a/testing/firefox-ui/harness/firefox_ui_harness/__init__.py b/testing/firefox-ui/harness/firefox_ui_harness/__init__.py new file mode 100644 index 0000000000..dad49461c7 --- /dev/null +++ b/testing/firefox-ui/harness/firefox_ui_harness/__init__.py @@ -0,0 +1,7 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +__version__ = "1.4.0" + +from . import cli_functional diff --git a/testing/firefox-ui/harness/firefox_ui_harness/arguments/__init__.py b/testing/firefox-ui/harness/firefox_ui_harness/arguments/__init__.py new file mode 100644 index 0000000000..c685a8c23e --- /dev/null +++ b/testing/firefox-ui/harness/firefox_ui_harness/arguments/__init__.py @@ -0,0 +1,5 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from firefox_ui_harness.arguments.base import FirefoxUIArguments diff --git a/testing/firefox-ui/harness/firefox_ui_harness/arguments/base.py b/testing/firefox-ui/harness/firefox_ui_harness/arguments/base.py new file mode 100644 index 0000000000..d9d1fc3246 --- /dev/null +++ b/testing/firefox-ui/harness/firefox_ui_harness/arguments/base.py @@ -0,0 +1,17 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from marionette_harness import BaseMarionetteArguments + + +class FirefoxUIBaseArguments(object): + name = "Firefox UI Tests" + args = [] + + +class FirefoxUIArguments(BaseMarionetteArguments): + def __init__(self, **kwargs): + super(FirefoxUIArguments, self).__init__(**kwargs) + + self.register_argument_container(FirefoxUIBaseArguments()) diff --git a/testing/firefox-ui/harness/firefox_ui_harness/cli_functional.py b/testing/firefox-ui/harness/firefox_ui_harness/cli_functional.py new file mode 100644 index 0000000000..339a500f19 --- /dev/null +++ b/testing/firefox-ui/harness/firefox_ui_harness/cli_functional.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from marionette_harness.runtests import cli as mn_cli + +from firefox_ui_harness.arguments import FirefoxUIArguments +from firefox_ui_harness.runners import FirefoxUITestRunner + + +def cli(args=None): + mn_cli( + runner_class=FirefoxUITestRunner, + parser_class=FirefoxUIArguments, + args=args, + ) + + +if __name__ == "__main__": + cli() diff --git a/testing/firefox-ui/harness/firefox_ui_harness/runners/__init__.py b/testing/firefox-ui/harness/firefox_ui_harness/runners/__init__.py new file mode 100644 index 0000000000..e2a7ef01f6 --- /dev/null +++ b/testing/firefox-ui/harness/firefox_ui_harness/runners/__init__.py @@ -0,0 +1,5 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from firefox_ui_harness.runners.base import FirefoxUITestRunner diff --git a/testing/firefox-ui/harness/firefox_ui_harness/runners/base.py b/testing/firefox-ui/harness/firefox_ui_harness/runners/base.py new file mode 100644 index 0000000000..41cfd86544 --- /dev/null +++ b/testing/firefox-ui/harness/firefox_ui_harness/runners/base.py @@ -0,0 +1,28 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import os + +from marionette_harness import BaseMarionetteTestRunner, MarionetteTestCase + + +class FirefoxUITestRunner(BaseMarionetteTestRunner): + def __init__(self, **kwargs): + super(FirefoxUITestRunner, self).__init__(**kwargs) + + # select the appropriate GeckoInstance + self.app = "fxdesktop" + + # low-noise log messages useful in tests + # TODO: should be moved to individual tests once bug 1386810 + # is fixed + moz_log = "" + if "MOZ_LOG" in os.environ: + moz_log = os.environ["MOZ_LOG"] + if len(moz_log) > 0: + moz_log += "," + moz_log += "UrlClassifierStreamUpdater:1" + os.environ["MOZ_LOG"] = moz_log + + self.test_handlers = [MarionetteTestCase] diff --git a/testing/firefox-ui/harness/requirements.txt b/testing/firefox-ui/harness/requirements.txt new file mode 100644 index 0000000000..cfebcb1be5 --- /dev/null +++ b/testing/firefox-ui/harness/requirements.txt @@ -0,0 +1,4 @@ +marionette-harness >= 4.0.0 +mozfile >= 1.2 +mozinfo >= 0.8 +mozinstall >= 1.12 diff --git a/testing/firefox-ui/harness/setup.py b/testing/firefox-ui/harness/setup.py new file mode 100644 index 0000000000..3cec4b21d4 --- /dev/null +++ b/testing/firefox-ui/harness/setup.py @@ -0,0 +1,52 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +import os +import re + +from setuptools import find_packages, setup + +THIS_DIR = os.path.dirname(os.path.realpath(__name__)) + + +def read(*parts): + with open(os.path.join(THIS_DIR, *parts)) as f: + return f.read() + + +def get_version(): + return re.findall( + '__version__ = "([\d\.]+)"', read("firefox_ui_harness", "__init__.py"), re.M + )[0] + + +long_description = """Custom Marionette runner classes and entry scripts for Firefox Desktop +specific Marionette tests. +""" + +setup( + name="firefox-ui-harness", + version=get_version(), + description="Firefox UI Harness", + long_description=long_description, + classifiers=[ + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", + ], + # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers + keywords="mozilla", + author="DevTools", + author_email="dev-webdriver@mozilla.org", + license="MPL", + packages=find_packages(), + include_package_data=True, + zip_safe=False, + install_requires=read("requirements.txt").splitlines(), + entry_points=""" + [console_scripts] + firefox-ui-functional = firefox_ui_harness.cli_functional:cli + """, +) diff --git a/testing/firefox-ui/mach_commands.py b/testing/firefox-ui/mach_commands.py new file mode 100644 index 0000000000..4c0d4a126f --- /dev/null +++ b/testing/firefox-ui/mach_commands.py @@ -0,0 +1,91 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import logging +import os +import sys + +import six +from mach.decorators import Command +from mozbuild.base import BinaryNotFoundException +from mozbuild.base import MachCommandConditions as conditions + + +def setup_argument_parser_functional(): + from firefox_ui_harness.arguments.base import FirefoxUIArguments + from mozlog.structured import commandline + + parser = FirefoxUIArguments() + commandline.add_logging_group(parser) + return parser + + +def run_firefox_ui_test(topsrcdir=None, **kwargs): + from argparse import Namespace + + import firefox_ui_harness + from mozlog.structured import commandline + + parser = setup_argument_parser_functional() + + fxui_dir = os.path.join(topsrcdir, "testing", "firefox-ui") + + # Set the resources path which is used to serve test data via wptserve + if not kwargs["server_root"]: + kwargs["server_root"] = os.path.join(fxui_dir, "resources") + + # If called via "mach test" a dictionary of tests is passed in + if "test_objects" in kwargs: + tests = [] + for obj in kwargs["test_objects"]: + tests.append(obj["file_relpath"]) + kwargs["tests"] = tests + elif not kwargs.get("tests"): + # If no tests have been selected, set default ones + kwargs["tests"] = os.path.join(fxui_dir, "tests", "functional", "manifest.ini") + + kwargs["logger"] = kwargs.pop("log", None) + if not kwargs["logger"]: + kwargs["logger"] = commandline.setup_logging( + "Firefox UI - Functional Tests", {"mach": sys.stdout} + ) + + args = Namespace() + + for k, v in six.iteritems(kwargs): + setattr(args, k, v) + + parser.verify_usage(args) + + failed = firefox_ui_harness.cli_functional.cli(args=vars(args)) + + if failed > 0: + return 1 + else: + return 0 + + +@Command( + "firefox-ui-functional", + category="testing", + conditions=[conditions.is_firefox], + description="Run the functional test suite of Firefox UI tests.", + parser=setup_argument_parser_functional, +) +def run_firefox_ui_functional(command_context, **kwargs): + try: + kwargs["binary"] = kwargs["binary"] or command_context.get_binary_path("app") + except BinaryNotFoundException as e: + command_context.log( + logging.ERROR, + "firefox-ui-functional", + {"error": str(e)}, + "ERROR: {error}", + ) + command_context.log( + logging.INFO, "firefox-ui-functional", {"help": e.help()}, "{help}" + ) + return 1 + + return run_firefox_ui_test(topsrcdir=command_context.topsrcdir, **kwargs) diff --git a/testing/firefox-ui/moz.build b/testing/firefox-ui/moz.build new file mode 100644 index 0000000000..e23fa6ac36 --- /dev/null +++ b/testing/firefox-ui/moz.build @@ -0,0 +1,9 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +FIREFOX_UI_FUNCTIONAL_MANIFESTS += ["tests/functional/manifest.toml"] + +with Files("**"): + BUG_COMPONENT = ("Testing", "Firefox UI Tests") + SCHEDULES.exclusive = ["firefox-ui"] diff --git a/testing/firefox-ui/resources/support.html b/testing/firefox-ui/resources/support.html new file mode 100644 index 0000000000..818aa97276 --- /dev/null +++ b/testing/firefox-ui/resources/support.html @@ -0,0 +1,23 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<!DOCTYPE html> +<html> +<head> + <meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <script type="text/javascript"> + function show() { + var results = /\?topic=(.+)$/.exec(window.document.location); + var topic = decodeURIComponent(results[1].replace(/\+/g, " ")); + var node = document.getElementById("topic"); + + node.textContent = topic; + } + </script> +</head> + +<body onload="show()"> + <div id="topic"></div> +</body> +</html> diff --git a/testing/firefox-ui/tests/functional/manifest.toml b/testing/firefox-ui/tests/functional/manifest.toml new file mode 100644 index 0000000000..f3d5fce3d6 --- /dev/null +++ b/testing/firefox-ui/tests/functional/manifest.toml @@ -0,0 +1,5 @@ +[DEFAULT] + +["include:safebrowsing/manifest.toml"] + +["include:security/manifest.toml"] diff --git a/testing/firefox-ui/tests/functional/safebrowsing/manifest.toml b/testing/firefox-ui/tests/functional/safebrowsing/manifest.toml new file mode 100644 index 0000000000..d1a4f5cd04 --- /dev/null +++ b/testing/firefox-ui/tests/functional/safebrowsing/manifest.toml @@ -0,0 +1,14 @@ +[DEFAULT] + +["test_initial_download.py"] +skip-if = [ + "debug", # The GAPI key isn't available in debug or sanitizer builds + "asan", + "tsan", + "ccov && os == 'win'", # Bug 1805893 + "cc_type == 'clang' && os == 'win'", # Bug 1565818 +] + +["test_notification.py"] + +["test_warning_pages.py"] diff --git a/testing/firefox-ui/tests/functional/safebrowsing/test_initial_download.py b/testing/firefox-ui/tests/functional/safebrowsing/test_initial_download.py new file mode 100644 index 0000000000..4013c51d5b --- /dev/null +++ b/testing/firefox-ui/tests/functional/safebrowsing/test_initial_download.py @@ -0,0 +1,135 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import os +from functools import reduce + +from marionette_driver import Wait +from marionette_harness import MarionetteTestCase + + +class TestSafeBrowsingInitialDownload(MarionetteTestCase): + v2_file_extensions = [ + "vlpset", + "sbstore", + ] + + v4_file_extensions = [ + "vlpset", + "metadata", + ] + + prefs_download_lists = [ + "urlclassifier.blockedTable", + "urlclassifier.downloadAllowTable", + "urlclassifier.downloadBlockTable", + "urlclassifier.malwareTable", + "urlclassifier.phishTable", + "urlclassifier.trackingTable", + "urlclassifier.trackingWhitelistTable", + ] + + prefs_provider_update_time = { + # Force an immediate download of the safebrowsing files + "browser.safebrowsing.provider.mozilla.nextupdatetime": 1, + } + + prefs_safebrowsing = { + "browser.safebrowsing.debug": True, + "browser.safebrowsing.update.enabled": True, + } + + def get_safebrowsing_files(self, is_v4): + files = [] + + if is_v4: + my_file_extensions = self.v4_file_extensions + else: # v2 + my_file_extensions = self.v2_file_extensions + + for pref_name in self.prefs_download_lists: + base_names = self.marionette.get_pref(pref_name).split(",") + + # moztest- lists are not saved to disk + # pylint --py3k: W1639 + base_names = list( + filter(lambda x: not x.startswith("moztest-"), base_names) + ) + + for ext in my_file_extensions: + files.extend( + [ + "{name}.{ext}".format(name=f, ext=ext) + for f in base_names + if f and f.endswith("-proto") == is_v4 + ] + ) + + return set(sorted(files)) + + def setUp(self): + super(TestSafeBrowsingInitialDownload, self).setUp() + + self.safebrowsing_v2_files = self.get_safebrowsing_files(False) + if any( + f.startswith("goog-") or f.startswith("googpub-") + for f in self.safebrowsing_v2_files + ): + self.prefs_provider_update_time.update( + { + "browser.safebrowsing.provider.google.nextupdatetime": 1, + } + ) + + self.safebrowsing_v4_files = self.get_safebrowsing_files(True) + if any( + f.startswith("goog-") or f.startswith("googpub-") + for f in self.safebrowsing_v4_files + ): + self.prefs_provider_update_time.update( + { + "browser.safebrowsing.provider.google4.nextupdatetime": 1, + } + ) + + # Force the preferences for the new profile + enforce_prefs = self.prefs_safebrowsing + enforce_prefs.update(self.prefs_provider_update_time) + self.marionette.enforce_gecko_prefs(enforce_prefs) + + self.safebrowsing_path = os.path.join( + self.marionette.instance.profile.profile, "safebrowsing" + ) + + def tearDown(self): + try: + # Restart with a fresh profile + self.marionette.restart(in_app=False, clean=True) + finally: + super(TestSafeBrowsingInitialDownload, self).tearDown() + + def test_safe_browsing_initial_download(self): + def check_downloaded(_): + return reduce( + lambda state, pref: state and int(self.marionette.get_pref(pref)) != 1, + list(self.prefs_provider_update_time), + True, + ) + + try: + Wait(self.marionette, timeout=170).until( + check_downloaded, + message="Not all safebrowsing files have been downloaded", + ) + finally: + files_on_disk_toplevel = os.listdir(self.safebrowsing_path) + for f in self.safebrowsing_v2_files: + self.assertIn(f, files_on_disk_toplevel) + + if len(self.safebrowsing_v4_files) > 0: + files_on_disk_google4 = os.listdir( + os.path.join(self.safebrowsing_path, "google4") + ) + for f in self.safebrowsing_v4_files: + self.assertIn(f, files_on_disk_google4) diff --git a/testing/firefox-ui/tests/functional/safebrowsing/test_notification.py b/testing/firefox-ui/tests/functional/safebrowsing/test_notification.py new file mode 100644 index 0000000000..2489542381 --- /dev/null +++ b/testing/firefox-ui/tests/functional/safebrowsing/test_notification.py @@ -0,0 +1,132 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import time + +from marionette_driver import By, Wait, expected +from marionette_harness import MarionetteTestCase, WindowManagerMixin + + +class TestSafeBrowsingNotificationBar(WindowManagerMixin, MarionetteTestCase): + def setUp(self): + super(TestSafeBrowsingNotificationBar, self).setUp() + + self.test_data = [ + # Unwanted software URL + {"unsafe_page": "https://www.itisatrap.org/firefox/unwanted.html"}, + # Phishing URL info + {"unsafe_page": "https://www.itisatrap.org/firefox/its-a-trap.html"}, + # Malware URL object + {"unsafe_page": "https://www.itisatrap.org/firefox/its-an-attack.html"}, + ] + + self.default_homepage = self.marionette.get_pref("browser.startup.homepage") + + self.marionette.set_pref("browser.safebrowsing.phishing.enabled", True) + self.marionette.set_pref("browser.safebrowsing.malware.enabled", True) + + # Give the browser a little time, because SafeBrowsing.jsm takes a while + # between start up and adding the example urls to the db. + # hg.mozilla.org/mozilla-central/file/46aebcd9481e/browser/base/content/browser.js#l1194 + time.sleep(3) + + # Run this test in a new tab. + new_tab = self.open_tab() + self.marionette.switch_to_window(new_tab) + + def tearDown(self): + try: + self.marionette.clear_pref("browser.safebrowsing.phishing.enabled") + self.marionette.clear_pref("browser.safebrowsing.malware.enabled") + + self.remove_permission("https://www.itisatrap.org", "safe-browsing") + self.close_all_tabs() + finally: + super(TestSafeBrowsingNotificationBar, self).tearDown() + + def test_notification_bar(self): + for item in self.test_data: + unsafe_page = item["unsafe_page"] + + # Return to the unsafe page + # Check "ignore warning" link then notification bar's "get me out" button + self.marionette.navigate(unsafe_page) + # Wait for the DOM to receive events for about:blocked + time.sleep(1) + self.check_ignore_warning_link(unsafe_page) + self.check_get_me_out_of_here_button() + + # Return to the unsafe page + # Check "ignore warning" link then notification bar's "X" button + self.marionette.navigate(unsafe_page) + # Wait for the DOM to receive events for about:blocked + time.sleep(1) + self.check_ignore_warning_link(unsafe_page) + self.check_x_button() + + def get_final_url(self, url): + self.marionette.navigate(url) + return self.marionette.get_url() + + def remove_permission(self, host, permission): + with self.marionette.using_context("chrome"): + self.marionette.execute_script( + """ + let uri = Services.io.newURI(arguments[0], null, null); + let principal = Services.scriptSecurityManager.createContentPrincipal(uri, {}); + Services.perms.removeFromPrincipal(principal, arguments[1]); + """, + script_args=[host, permission], + ) + + def check_ignore_warning_link(self, unsafe_page): + button = self.marionette.find_element(By.ID, "seeDetailsButton") + button.click() + time.sleep(1) + link = self.marionette.find_element(By.ID, "ignore_warning_link") + link.click() + + Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( + expected.element_present(By.ID, "main-feature"), + message='Expected target element "#main-feature" has not been found', + ) + self.assertEqual(self.marionette.get_url(), self.get_final_url(unsafe_page)) + + # Clean up here since the permission gets set in this function + self.remove_permission("https://www.itisatrap.org", "safe-browsing") + + def check_get_me_out_of_here_button(self): + with self.marionette.using_context("chrome"): + notification_box = self.marionette.find_element( + By.CSS_SELECTOR, 'vbox.notificationbox-stack[slot="selected"]' + ) + message = notification_box.find_element( + By.CSS_SELECTOR, "notification-message" + ) + button_container = message.get_property("buttonContainer") + button = button_container.find_element( + By.CSS_SELECTOR, 'button[label="Get me out of here!"]' + ) + button.click() + + Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( + lambda mn: self.default_homepage in mn.get_url(), + message="The default home page has not been loaded", + ) + + def check_x_button(self): + with self.marionette.using_context("chrome"): + notification_box = self.marionette.find_element( + By.CSS_SELECTOR, 'vbox.notificationbox-stack[slot="selected"]' + ) + message = notification_box.find_element( + By.CSS_SELECTOR, "notification-message[value=blocked-badware-page]" + ) + button = message.get_property("closeButtonEl") + button.click() + + Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( + expected.element_stale(button), + message="The notification bar has not been closed", + ) diff --git a/testing/firefox-ui/tests/functional/safebrowsing/test_warning_pages.py b/testing/firefox-ui/tests/functional/safebrowsing/test_warning_pages.py new file mode 100644 index 0000000000..1e5b3e2f56 --- /dev/null +++ b/testing/firefox-ui/tests/functional/safebrowsing/test_warning_pages.py @@ -0,0 +1,138 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import time + +from marionette_driver import By, Wait, expected +from marionette_harness import MarionetteTestCase, WindowManagerMixin + + +class TestSafeBrowsingWarningPages(WindowManagerMixin, MarionetteTestCase): + def setUp(self): + super(TestSafeBrowsingWarningPages, self).setUp() + + self.urls = [ + # Unwanted software URL + "https://www.itisatrap.org/firefox/unwanted.html", + # Phishing URL + "https://www.itisatrap.org/firefox/its-a-trap.html", + # Malware URL + "https://www.itisatrap.org/firefox/its-an-attack.html", + ] + + self.default_homepage = self.marionette.get_pref("browser.startup.homepage") + self.support_page = self.marionette.absolute_url("support.html?topic=") + + self.marionette.set_pref("app.support.baseURL", self.support_page) + self.marionette.set_pref("browser.safebrowsing.phishing.enabled", True) + self.marionette.set_pref("browser.safebrowsing.malware.enabled", True) + + # Give the browser a little time, because SafeBrowsing.jsm takes a + # while between start up and adding the example urls to the db. + # hg.mozilla.org/mozilla-central/file/46aebcd9481e/browser/base/content/browser.js#l1194 + time.sleep(3) + + # Run this test in a new tab. + new_tab = self.open_tab() + self.marionette.switch_to_window(new_tab) + + def tearDown(self): + try: + self.marionette.clear_pref("app.support.baseURL") + self.marionette.clear_pref("browser.safebrowsing.malware.enabled") + self.marionette.clear_pref("browser.safebrowsing.phishing.enabled") + + self.remove_permission("https://www.itisatrap.org", "safe-browsing") + self.close_all_tabs() + finally: + super(TestSafeBrowsingWarningPages, self).tearDown() + + def test_warning_pages(self): + for unsafe_page in self.urls: + # Load a test page, then test the get me out button + self.marionette.navigate(unsafe_page) + # Wait for the DOM to receive events for about:blocked + time.sleep(1) + self.check_get_me_out_of_here_button(unsafe_page) + + # Load the test page again, then test the report button + self.marionette.navigate(unsafe_page) + # Wait for the DOM to receive events for about:blocked + time.sleep(1) + self.check_report_link(unsafe_page) + + # Load the test page again, then test the ignore warning button + self.marionette.navigate(unsafe_page) + # Wait for the DOM to receive events for about:blocked + time.sleep(1) + self.check_ignore_warning_button(unsafe_page) + + def get_final_url(self, url): + self.marionette.navigate(url) + return self.marionette.get_url() + + def remove_permission(self, host, permission): + with self.marionette.using_context("chrome"): + self.marionette.execute_script( + """ + let uri = Services.io.newURI(arguments[0], null, null); + let principal = Services.scriptSecurityManager.createContentPrincipal(uri, {}); + Services.perms.removeFromPrincipal(principal, arguments[1]); + """, + script_args=[host, permission], + ) + + def check_get_me_out_of_here_button(self, unsafe_page): + button = self.marionette.find_element(By.ID, "goBackButton") + button.click() + + Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( + lambda mn: self.default_homepage in mn.get_url() + ) + + def check_report_link(self, unsafe_page): + # Get the URL of the support site for phishing and malware. This may result in a redirect. + with self.marionette.using_context("chrome"): + url = self.marionette.execute_script( + """ + return Services.urlFormatter.formatURLPref("app.support.baseURL") + + "phishing-malware"; + """ + ) + + button = self.marionette.find_element(By.ID, "seeDetailsButton") + button.click() + link = self.marionette.find_element(By.ID, "firefox_support") + link.click() + + # Wait for the button to become stale, whereby a longer timeout is needed + # here to not fail in case of slow connections. + Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( + expected.element_stale(button) + ) + + # Wait for page load to be completed, so we can verify the URL even if a redirect happens. + # TODO: Bug 1140470: use replacement for mozmill's waitforPageLoad + expected_url = self.get_final_url(url) + Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( + lambda mn: expected_url == mn.get_url(), + message="The expected URL '{}' has not been loaded".format(expected_url), + ) + + topic = self.marionette.find_element(By.ID, "topic") + self.assertEqual(topic.text, "phishing-malware") + + def check_ignore_warning_button(self, unsafe_page): + button = self.marionette.find_element(By.ID, "seeDetailsButton") + button.click() + link = self.marionette.find_element(By.ID, "ignore_warning_link") + link.click() + + Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( + expected.element_present(By.ID, "main-feature") + ) + self.assertEqual(self.marionette.get_url(), self.get_final_url(unsafe_page)) + + # Clean up by removing safe browsing permission for unsafe page + self.remove_permission("https://www.itisatrap.org", "safe-browsing") diff --git a/testing/firefox-ui/tests/functional/security/manifest.toml b/testing/firefox-ui/tests/functional/security/manifest.toml new file mode 100644 index 0000000000..72bea151e7 --- /dev/null +++ b/testing/firefox-ui/tests/functional/security/manifest.toml @@ -0,0 +1,3 @@ +[DEFAULT] + +["test_ssl_status_after_restart.py"] diff --git a/testing/firefox-ui/tests/functional/security/test_ssl_status_after_restart.py b/testing/firefox-ui/tests/functional/security/test_ssl_status_after_restart.py new file mode 100644 index 0000000000..3684433f6f --- /dev/null +++ b/testing/firefox-ui/tests/functional/security/test_ssl_status_after_restart.py @@ -0,0 +1,56 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from marionette_driver import By, Wait +from marionette_harness import MarionetteTestCase, WindowManagerMixin + + +class TestSSLStatusAfterRestart(WindowManagerMixin, MarionetteTestCase): + def setUp(self): + super(TestSSLStatusAfterRestart, self).setUp() + self.marionette.set_context("chrome") + + self.test_url = "https://www.itisatrap.org/" + + # Set browser to restore previous session + self.marionette.set_pref("browser.startup.page", 3) + # Disable rcwn to make cache behavior deterministic + self.marionette.set_pref("network.http.rcwn.enable", False) + + def tearDown(self): + self.marionette.clear_pref("browser.startup.page") + self.marionette.clear_pref("network.http.rcwn.enable") + + super(TestSSLStatusAfterRestart, self).tearDown() + + def test_ssl_status_after_restart(self): + with self.marionette.using_context("content"): + self.marionette.navigate(self.test_url) + self.verify_certificate_status(self.test_url) + + self.marionette.restart(in_app=True) + + self.verify_certificate_status(self.test_url) + + def verify_certificate_status(self, url): + with self.marionette.using_context("content"): + Wait(self.marionette).until( + lambda _: self.marionette.get_url() == url, + message="Expected URL loaded", + ) + + identity_box = self.marionette.find_element(By.ID, "identity-box") + self.assertEqual(identity_box.get_attribute("pageproxystate"), "valid") + + class_list = self.marionette.execute_script( + """ + const names = []; + for (const name of arguments[0].classList) { + names.push(name); + } + return names; + """, + script_args=[identity_box], + ) + self.assertIn("verifiedDomain", class_list) |