diff options
Diffstat (limited to 'tests/selenium/options_test.py')
-rw-r--r-- | tests/selenium/options_test.py | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/tests/selenium/options_test.py b/tests/selenium/options_test.py new file mode 100644 index 0000000..ce95588 --- /dev/null +++ b/tests/selenium/options_test.py @@ -0,0 +1,327 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- + +import time +import unittest + +import pbtest + +from selenium.common.exceptions import ( + ElementNotInteractableException, + ElementNotVisibleException, + NoSuchElementException, + TimeoutException, +) +from selenium.webdriver.common.action_chains import ActionChains +from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait + + +class OptionsTest(pbtest.PBSeleniumTest): + """Make sure the options page works correctly.""" + + def assert_slider_state(self, origin, action, failure_msg): + clicker = self.driver.find_element_by_css_selector( + 'div[data-origin="{}"]'.format(origin)) + self.assertEqual( + clicker.get_attribute("class"), + "clicker userset", + failure_msg + ) + + switches_div = clicker.find_element_by_css_selector(".switch-container") + self.assertEqual( + switches_div.get_attribute("class"), + "switch-container " + action, + failure_msg + ) + + def find_origin_by_xpath(self, origin): + origins = self.driver.find_element_by_id("blockedResourcesInner") + return origins.find_element_by_xpath(( + './/div[@data-origin="{origin}"]' + # test that "origin" is one of the classes on the element: + # https://stackoverflow.com/a/1390680 + '//div[contains(concat(" ", normalize-space(@class), " "), " origin ")]' + '//span[text()="{origin}"]' + ).format(origin=origin)) + + def select_domain_list_tab(self): + self.find_el_by_css('a[href="#tab-tracking-domains"]').click() + try: + self.driver.find_element_by_id('show-tracking-domains-checkbox').click() + except (ElementNotInteractableException, ElementNotVisibleException): + # The list will be loaded directly if we're opening the tab for the second time in this test + pass + + def select_manage_data_tab(self): + self.find_el_by_css('a[href="#tab-manage-data"]').click() + + def check_tracker_messages(self, error_message, many, none): + self.assertEqual(many, + self.driver.find_element_by_id("options_domain_list_trackers").is_displayed(), error_message) + self.assertEqual(none, + self.driver.find_element_by_id("options_domain_list_no_trackers").is_displayed(), error_message) + + def load_options_page(self): + self.load_url(self.options_url) + self.wait_for_script("return window.OPTIONS_INITIALIZED") + + def clear_seed_data(self): + """Clear the seed dataset to make test checks easier""" + self.load_options_page() + self.js("chrome.extension.getBackgroundPage().badger.storage.clearTrackerData();") + + def add_test_origin(self, origin, action): + """Add given origin to backend storage.""" + self.load_options_page() + self.js(( + "chrome.extension.getBackgroundPage()" + ".badger.storage.setupHeuristicAction('{}', '{}');" + ).format(origin, action)) + + def user_overwrite(self, origin, action): + # Get the slider that corresponds to this radio button + origin_div = self.find_el_by_css('div[data-origin="' + origin + '"]') + slider = origin_div.find_element_by_css_selector('.switch-toggle') + + # Click on the correct place over the slider to block this origin + click_action = ActionChains(self.driver) + if action == 'block': + # Top left (+2px) + click_action.move_to_element_with_offset(slider, 2, 2) + if action == 'cookieblock': + # Top middle + click_action.move_to_element_with_offset(slider, slider.size['width']/2, 2) + if action == 'allow': + # Top right + click_action.move_to_element_with_offset(slider, slider.size['width']-2, 2) + click_action.click() + click_action.perform() + + def test_page_title(self): + self.load_options_page() + localized_title = self.js('return chrome.i18n.getMessage("options_title")') + try: + WebDriverWait(self.driver, 3).until( + EC.title_contains(localized_title)) + except TimeoutException: + self.fail("Unexpected title for the Options page. Got (%s)," + " expected (%s)" + % (self.driver.title, localized_title)) + + def test_added_origin_display(self): + """Ensure origin and tracker message is displayed when there is 1 origin.""" + self.clear_seed_data() + + self.add_test_origin("pbtest.org", "block") + + self.load_options_page() + self.select_domain_list_tab() + + error_message = "The 'multiple tracker' message should be displayed after adding an origin" + self.check_tracker_messages(error_message, many=True, none=False) + + try: + self.find_origin_by_xpath("pbtest.org") + except NoSuchElementException: + self.fail("Tracking origin is not displayed") + + def test_added_multiple_origins_display(self): + """Ensure origin and tracker count is displayed when there are multiple origins.""" + self.clear_seed_data() + + self.add_test_origin("pbtest.org", "block") + self.add_test_origin("pbtest1.org", "block") + + self.load_options_page() + self.select_domain_list_tab() + + error_message = "The 'multiple tracker' message should be displayed after adding 2 origins" + self.check_tracker_messages(error_message, many=True, none=False) + + # check tracker count + self.assertEqual( + self.driver.find_element_by_id("options_domain_list_trackers").text, + "Privacy Badger has decided to block 2 potential tracking domains so far", + "Origin tracker count should be 2 after adding origin" + ) + + # Check those origins are displayed. + try: + self.find_origin_by_xpath("pbtest.org") + self.find_origin_by_xpath("pbtest1.org") + except NoSuchElementException: + self.fail("Tracking origin is not displayed") + + def test_removed_origin_display(self): + """Ensure origin is removed properly.""" + self.clear_seed_data() + self.add_test_origin("pbtest.org", "block") + + self.load_options_page() + self.select_domain_list_tab() + + # Remove displayed origin. + remove_origin_element = self.find_el_by_xpath( + './/div[@data-origin="pbtest.org"]' + '//a[@class="removeOrigin"]') + remove_origin_element.click() + + # Make sure the alert is present. Otherwise we get intermittent errors. + WebDriverWait(self.driver, 3).until(EC.alert_is_present()) + self.driver.switch_to.alert.accept() + + # Check that only the 'no trackers' message is displayed. + try: + WebDriverWait(self.driver, 5).until( + EC.visibility_of_element_located((By.ID, "options_domain_list_no_trackers"))) + except TimeoutException: + self.fail("There should be a 'no trackers' message after deleting origin") + + error_message = "Only the 'no trackers' message should be displayed before adding an origin" + self.assertFalse( + self.driver.find_element_by_id( + "options_domain_list_trackers").is_displayed(), error_message) + + # Check that no origins are displayed. + try: + origins = self.driver.find_element_by_id("blockedResourcesInner") + except NoSuchElementException: + origins = None + error_message = "Origin should not be displayed after removal" + self.assertIsNone(origins, error_message) + + def test_reset_data(self): + self.load_options_page() + self.select_domain_list_tab() + + # make sure the default tracker list includes many trackers + error_message = "By default, the tracker list should contain many trackers" + self.check_tracker_messages(error_message, many=True, none=False) + + # get the number of trackers in the seed data + default_summary_text = self.driver.find_element_by_id("options_domain_list_trackers").text + + # Click on the "remove all data" button to empty the tracker lists, and + # click "OK" in the popup that ensues + self.select_manage_data_tab() + self.driver.find_element_by_id('removeAllData').click() + self.driver.switch_to.alert.accept() + time.sleep(1) # wait for page to reload + + # now make sure the tracker list is empty + self.select_domain_list_tab() + error_message = "No trackers should be displayed after removing all data" + self.check_tracker_messages(error_message, many=False, none=True) + + # add new blocked domains + self.add_test_origin("pbtest.org", "block") + self.add_test_origin("pbtest1.org", "block") + + # reload the options page + self.load_options_page() + self.select_domain_list_tab() + + # make sure only two trackers are displayed now + self.assertEqual( + self.driver.find_element_by_id("options_domain_list_trackers").text, + "Privacy Badger has decided to block 2 potential tracking domains so far", + "Origin tracker count should be 2 after clearing and adding origins" + ) + + # click the "reset data" button to restore seed data and get rid of the + # domains we learned + self.select_manage_data_tab() + self.driver.find_element_by_id('resetData').click() + self.driver.switch_to.alert.accept() + time.sleep(1) + + # make sure the same number of trackers are displayed as by default + self.select_domain_list_tab() + error_message = "After resetting data, tracker count should return to default" + self.assertEqual(self.driver.find_element_by_id("options_domain_list_trackers").text, + default_summary_text, error_message) + + def tracking_user_overwrite(self, original_action, overwrite_action): + """Ensure preferences are persisted when a user overwrites pb's default behaviour for an origin.""" + self.clear_seed_data() + self.add_test_origin("pbtest.org", original_action) + + self.load_options_page() + self.wait_for_script("return window.OPTIONS_INITIALIZED") + # enable learning to reveal the show-not-yet-blocked checkbox + self.find_el_by_css('#local-learning-checkbox').click() + self.select_domain_list_tab() + self.find_el_by_css('#tracking-domains-show-not-yet-blocked').click() + # wait for sliders to finish rendering + self.wait_for_script("return window.SLIDERS_DONE") + + # Change user preferences + self.user_overwrite("pbtest.org", overwrite_action) + + # Re-open the tab + self.load_options_page() + self.select_domain_list_tab() + self.find_el_by_css('#tracking-domains-show-not-yet-blocked').click() + # wait for sliders to finish rendering + self.wait_for_script("return window.SLIDERS_DONE") + + # Check the user preferences for the origins are still displayed + failure_msg = ( + "Origin should be displayed as {} after user overwrite of " + "PB's decision to {}".format(overwrite_action, original_action) + ) + self.assert_slider_state("pbtest.org", overwrite_action, failure_msg) + + def test_tracking_user_overwrite_allowed_block(self): + self.tracking_user_overwrite('allow', 'block') + + def test_tracking_user_overwrite_allowed_cookieblock(self): + self.tracking_user_overwrite('allow', 'cookieblock') + + def test_tracking_user_overwrite_cookieblocked_allow(self): + self.tracking_user_overwrite('cookieblock', 'allow') + + def test_tracking_user_overwrite_cookieblocked_block(self): + self.tracking_user_overwrite('cookieblock', 'block') + + def test_tracking_user_overwrite_blocked_allow(self): + self.tracking_user_overwrite('block', 'allow') + + def test_tracking_user_overwrite_blocked_cookieblock(self): + self.tracking_user_overwrite('block', 'cookieblock') + + # early-warning check for the open_in_tab attribute of options_ui + # https://github.com/EFForg/privacybadger/pull/1775#pullrequestreview-76940251 + def test_options_ui_open_in_tab(self): + # open options page manually, keeping the new user intro page + self.open_window() + self.load_options_page() + + # switch to new user intro page + self.switch_to_window_with_url(self.first_run_url) + + # save open windows + handles_before = set(self.driver.window_handles) + + # open options page using dedicated chrome API + self.js("chrome.runtime.openOptionsPage();") + + # if we switched to the previously manually opened options page, all good + # if we haven't, this must mean options_ui's open_in_tab no longer works + new_handles = set(self.driver.window_handles).difference(handles_before) + num_newly_opened_windows = len(new_handles) + + if num_newly_opened_windows: + self.driver.switch_to.window(new_handles.pop()) + + self.assertEqual(num_newly_opened_windows, 0, + "Expected to switch to existing options page, " + "opened a new page ({}) instead: {}".format( + self.driver.title, self.driver.current_url)) + + +if __name__ == "__main__": + unittest.main() |