File: options_test.py

package info (click to toggle)
privacybadger 2026.2.20-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 13,040 kB
  • sloc: javascript: 56,619; python: 2,214; sh: 406; makefile: 57; xml: 6
file content (272 lines) | stat: -rw-r--r-- 11,872 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
#!/usr/bin/env python

import unittest

import pytest

import pbtest

from selenium.common.exceptions import (
    NoSuchElementException,
    TimeoutException,
)
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.ui import WebDriverWait


class OptionsTest(pbtest.PBSeleniumTest):
    """Make sure the options page works correctly."""

    def assert_domain_toggle_state(self, domain, action, failure_msg):
        clicker = self.find_el_by_css(f'div[data-origin="{domain}"]')
        assert clicker.get_dom_attribute("class") == "clicker userset", failure_msg

        switches_div = clicker.find_element(By.CSS_SELECTOR, ".switch-container")
        assert switches_div.get_dom_attribute("class") == f"switch-container {action}", failure_msg

    def find_domain_toggle_by_xpath(self, domain):
        toggles_div = self.driver.find_element(By.ID, "blockedResourcesInner")
        return toggles_div.find_element(By.XPATH, (
            './/div[@data-origin="{domain}"]'
            # 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()="{domain}"]'
        ).format(domain=domain))

    def select_domain_list_tab(self):
        self.find_el_by_css('a[href="#tab-tracking-domains"]').click()

    def select_manage_data_tab(self):
        self.find_el_by_css('a[href="#tab-manage-data"]').click()

    def load_options_page(self):
        self.load_url(self.options_url + '?all')
        self.wait_for_script("return window.OPTIONS_INITIALIZED")

    def test_reloading_should_reapply_filters(self):
        FILTERVAL = "user"

        self.load_options_page()
        self.select_domain_list_tab()

        # change a domain list filter
        Select(self.find_el_by_css('#tracking-domains-type-filter')).select_by_value(FILTERVAL)

        # reload page and assert filters are set
        self.driver.refresh()
        sel = Select(self.find_el_by_css('#tracking-domains-type-filter'))
        assert sel.first_selected_option.get_property('value') == FILTERVAL

        # open options page in a new window and assert filters are not set
        self.open_window()
        self.load_options_page()
        self.select_domain_list_tab()
        sel = Select(self.find_el_by_css('#tracking-domains-type-filter'))
        assert not sel.first_selected_option.get_property('value')

    @pytest.mark.flaky(reruns=5, condition=pbtest.shim.browser_type == "edge")
    def test_adding_domain(self):
        """Ensure domain and tracker count are displayed."""
        self.clear_tracker_data()

        self.add_domain("pbtest.org", "block")

        self.load_options_page()
        self.select_domain_list_tab()

        error_message = "The 'multiple tracker' message should be displayed after adding a domain"
        assert self.driver.find_element(By.ID, "options_domain_list_trackers").is_displayed(), error_message
        assert not self.driver.find_element(By.ID, "options_domain_list_no_trackers").is_displayed(), error_message

        try:
            self.find_domain_toggle_by_xpath("pbtest.org")
        except NoSuchElementException:
            self.fail("Tracking domain is not displayed")

    @pytest.mark.flaky(reruns=5, condition=pbtest.shim.browser_type == "edge")
    def test_removing_domain(self):
        """Ensure domain is removed properly."""
        self.clear_tracker_data()
        self.add_domain("pbtest.org", "block")

        self.load_options_page()
        self.select_domain_list_tab()

        domains = self.driver.find_elements(By.CSS_SELECTOR, 'div.clicker')
        assert len(domains) == 1, "Should see exactly one domain in the list"

        # remove displayed domain
        remove_domain_element = self.find_el_by_xpath(
            './/div[@data-origin="pbtest.org"]'
            '//a[@class="removeOrigin"]')
        remove_domain_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()

        # verify 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 domain")

        error_message = "Only the 'no trackers' message should be displayed"
        assert not self.driver.find_element(By.ID, "options_domain_list_trackers").is_displayed(), error_message

        # verify that no domains are displayed
        try:
            domains = self.driver.find_elements(By.CSS_SELECTOR, 'div.clicker')
        except NoSuchElementException:
            domains = []
        assert len(domains) == 0, "No domains should be displayed after removal"

    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"
        assert self.driver.find_element(By.ID, "options_domain_list_trackers").is_displayed(), error_message
        assert not self.driver.find_element(By.ID, "options_domain_list_no_trackers").is_displayed(), error_message

        # 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()
        with self.wait_for_reload():
            self.driver.find_element(By.ID, 'removeAllData').click()
            self.driver.switch_to.alert.accept()

        # now make sure the tracker list is empty
        self.select_domain_list_tab()
        error_message = "No trackers should be displayed after removing all data"
        assert not self.driver.find_element(By.ID, "options_domain_list_trackers").is_displayed(), error_message
        assert self.driver.find_element(By.ID, "options_domain_list_no_trackers").is_displayed(), error_message

        # add new blocked domains
        self.add_domain("pbtest.org", "block")
        self.add_domain("pbtest1.org", "block")

        # reload the options page
        self.load_options_page()
        self.select_domain_list_tab()

        # make sure only two trackers are displayed now
        actual_text = self.driver.find_element(By.ID, "options_domain_list_trackers").text
        expected_text = self.js("return chrome.i18n.getMessage("
            "'options_domain_list_trackers', [2, '']);").replace("</a>", "")
        assert actual_text == expected_text

        # click the "reset data" button to restore seed data and get rid of the
        # domains we learned
        self.select_manage_data_tab()
        with self.wait_for_reload():
            self.driver.find_element(By.ID, 'resetData').click()
            self.driver.switch_to.alert.accept()

        # 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"
        assert 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 a domain."""

        DOMAIN = "pbtest.org"

        self.clear_tracker_data()
        self.add_domain(DOMAIN, original_action)

        self.load_options_page()
        self.wait_for_script("return window.OPTIONS_INITIALIZED")
        self.find_el_by_css('a[href="#tab-general-settings"]').click()
        # 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 toggles to finish rendering
        self.wait_for_script("return window.SLIDERS_DONE")

        # Change user preferences
        domain_id = DOMAIN.replace(".", "-")
        self.wait_for_script(f"return $('#{overwrite_action}-{domain_id}')[0];")
        self.js(f"$('#{overwrite_action}-{domain_id}').trigger('click');")

        # 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 toggles to finish rendering
        self.wait_for_script("return window.SLIDERS_DONE")

        # Check the user preferences for the domains are still displayed
        failure_msg = (
            f"Domain should be displayed as {overwrite_action} "
            f"after user overwrite of PB's decision to {original_action}")
        self.assert_domain_toggle_state(DOMAIN, overwrite_action, failure_msg)

    @pytest.mark.flaky(reruns=5, condition=pbtest.shim.browser_type in ("chrome", "edge"))
    def test_tracking_user_overwrite_allowed_block(self):
        self.tracking_user_overwrite('allow', 'block')

    @pytest.mark.flaky(reruns=5, condition=pbtest.shim.browser_type in ("chrome", "edge"))
    def test_tracking_user_overwrite_allowed_cookieblock(self):
        self.tracking_user_overwrite('allow', 'cookieblock')

    @pytest.mark.flaky(reruns=5, condition=pbtest.shim.browser_type in ("chrome", "edge"))
    def test_tracking_user_overwrite_cookieblocked_allow(self):
        self.tracking_user_overwrite('cookieblock', 'allow')

    @pytest.mark.flaky(reruns=5, condition=pbtest.shim.browser_type in ("chrome", "edge"))
    def test_tracking_user_overwrite_cookieblocked_block(self):
        self.tracking_user_overwrite('cookieblock', 'block')

    @pytest.mark.flaky(reruns=5, condition=pbtest.shim.browser_type in ("chrome", "edge"))
    def test_tracking_user_overwrite_blocked_allow(self):
        self.tracking_user_overwrite('block', 'allow')

    @pytest.mark.flaky(reruns=5, condition=pbtest.shim.browser_type in ("chrome", "edge"))
    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
        self.open_window()
        self.load_options_page()

        # open the new user intro page
        self.open_window()
        self.load_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())

        assert num_newly_opened_windows == 0, (
            "Expected to switch to existing options page, "
            f"opened a new page ({self.driver.title}) instead: "
            f"{self.driver.current_url}")


if __name__ == "__main__":
    unittest.main()