summaryrefslogtreecommitdiffstats
path: root/tests/selenium/widgets_test.py
blob: d8bc2b51aa868d33a5350deb7a2af99d65935011 (plain)
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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import unittest

import pbtest

from time import sleep

from selenium.common.exceptions import (
    NoSuchElementException,
    StaleElementReferenceException,
    TimeoutException
)
from selenium.webdriver.common.keys import Keys


class WidgetsTest(pbtest.PBSeleniumTest):

    FIXTURES_URL = "https://efforg.github.io/privacybadger-test-fixtures/html/"
    BASIC_FIXTURE_URL = FIXTURES_URL + "widget_basic.html"
    DYNAMIC_FIXTURE_URL = FIXTURES_URL + "widget_dynamic.html"
    THIRD_PARTY_DOMAIN = "privacybadger-tests.eff.org"
    TYPE3_WIDGET_NAME = "Type 3 Widget"
    TYPE4_WIDGET_NAME = "Type 4 Widget"
    TYPE4_WIDGET_CLASS = "pb-type4-test-widget"

    def setUp(self):
        self.set_up_widgets()

    def set_up_widgets(self):
        """Reinitializes Privacy Badger's widget replacement definitions."""

        widgetsJson = {
            self.TYPE3_WIDGET_NAME: {
                "domain": self.THIRD_PARTY_DOMAIN,
                "buttonSelectors": [
                    "iframe#pb-type3-test-widget"
                ],
                "replacementButton": {
                    "unblockDomains": [
                        self.THIRD_PARTY_DOMAIN
                    ],
                    "type": 3
                }
            },
            self.TYPE4_WIDGET_NAME: {
                "domains": [
                    self.THIRD_PARTY_DOMAIN
                ],
                "buttonSelectors": [
                    "div." + self.TYPE4_WIDGET_CLASS
                ],
                "scriptSelectors": [
                    "script." + self.TYPE4_WIDGET_CLASS
                ],
                "replacementButton": {
                    "unblockDomains": [
                        self.THIRD_PARTY_DOMAIN
                    ],
                    "type": 4
                }
            }
        }

        # reinitialize widgets using above JSON
        self.load_url(self.options_url)
        self.js((
            "(function (widgetsJson) {"
            "  let bg = chrome.extension.getBackgroundPage();"
            "  bg.badger.widgetList = bg.widgetLoader.initializeWidgets(widgetsJson);"
            "}(arguments[0]));"
        ), widgetsJson)

    def switch_to_frame(self, selector):
        self.wait_for_and_switch_to_frame(selector, timeout=1)

    def assert_widget(self, kind="type3"):
        if kind == "type3":
            self._assert_type3_widget()
        elif kind == "type4":
            self._assert_type4_widget()
        else:
            self.fail("Unknown widget type")

    def _assert_type3_widget(self):
        try:
            self.switch_to_frame('iframe[src]')
        except (StaleElementReferenceException, TimeoutException):
            self.fail("Unable to find widget frame")

        try:
            self.wait_for_text('body', "Hello world!")
        except TimeoutException:
            self.fail("Unable to find expected widget text")

        self.driver.switch_to.default_content()

    def _assert_type4_widget(self):
        try:
            self.wait_for_text('div.' + self.TYPE4_WIDGET_CLASS,
                "A third-party widget script was here")
        except TimeoutException:
            self.fail("Unable to find expected widget output")

    def assert_replacement(self, widget_name=None):
        if not widget_name:
            widget_name = self.TYPE3_WIDGET_NAME

        try:
            self.switch_to_frame('iframe[srcdoc*="{}"]'.format(widget_name))
        except (StaleElementReferenceException, TimeoutException):
            self.fail("Unable to find widget placeholder frame")

        try:
            self.find_el_by_css("button[id^='btn-once-']")
            self.find_el_by_css("button[id^='btn-site-']")
        except TimeoutException:
            self.fail("Unable to find expected widget placeholder buttons")

        self.driver.switch_to.default_content()

    def assert_widget_blocked(self):
        try:
            self.switch_to_frame('iframe[src]')
        except TimeoutException:
            self.fail("Widget frame should still be here")

        self.assertFalse(
            self.txt_by_css('body'), "Widget frame should be empty")

        self.driver.switch_to.default_content()

    def assert_no_widget(self):
        try:
            self.switch_to_frame('iframe[src]')
            self.fail("Widget frame should be missing")
        except TimeoutException:
            pass
        self.driver.switch_to.default_content()

    def assert_no_replacement(self, widget_name=None):
        if not widget_name:
            widget_name = self.TYPE3_WIDGET_NAME
        try:
            self.switch_to_frame('iframe[srcdoc*="{}"]'.format(widget_name))
            self.fail("Widget placeholder frame should be missing")
        except TimeoutException:
            pass
        self.driver.switch_to.default_content()

    def activate_widget(self, widget_name=None, once=True):
        if not widget_name:
            widget_name = self.TYPE3_WIDGET_NAME
        id_prefix = 'btn-once' if once else 'btn-site'
        self.switch_to_frame('iframe[srcdoc*="{}"]'.format(widget_name))
        self.find_el_by_css("button[id^='%s']" % id_prefix).click()
        self.driver.switch_to.default_content()

    def test_replacement_basic(self):
        # visit the basic widget fixture
        self.load_url(self.BASIC_FIXTURE_URL)
        # verify the widget is present
        self.assert_widget()

        # block the test widget's domain
        self.block_domain(self.THIRD_PARTY_DOMAIN)

        # revisit the fixture
        self.load_url(self.BASIC_FIXTURE_URL)
        # verify the widget got replaced
        self.assert_replacement()

    def test_replacement_dynamic(self):
        # visit the dynamic widget fixture
        self.load_url(self.DYNAMIC_FIXTURE_URL)
        # verify the widget is initially missing
        self.assert_no_widget()

        # verify the widget shows up once you click on the trigger element
        self.find_el_by_css('#widget-trigger').click()
        self.assert_widget()

        # block the test widget's domain
        self.block_domain(self.THIRD_PARTY_DOMAIN)

        # revisit the fixture
        self.load_url(self.DYNAMIC_FIXTURE_URL)
        # click on the trigger element
        self.find_el_by_css('#widget-trigger').click()
        # verify the widget got replaced
        self.assert_replacement()

    def test_activation(self):
        self.block_domain(self.THIRD_PARTY_DOMAIN)
        self.load_url(self.BASIC_FIXTURE_URL)
        self.assert_replacement()

        # click the "allow once" button
        self.activate_widget()

        # verify the original widget is restored
        self.assert_widget()

        # verify the type 4 widget is still replaced
        try:
            self.driver.find_element_by_css_selector(
                'div.' + self.TYPE4_WIDGET_CLASS)
            self.fail("Widget output container div should be missing")
        except NoSuchElementException:
            pass
        self.assert_replacement(self.TYPE4_WIDGET_NAME)

        self.activate_widget(self.TYPE4_WIDGET_NAME)

        # assert all script attributes were copied
        script_el = self.driver.find_element_by_css_selector(
            'script.' + self.TYPE4_WIDGET_CLASS)
        self.assertEqual(script_el.get_attribute('async'), "true")
        self.assertEqual(script_el.get_attribute('data-foo'), "bar")

        self.assert_widget("type4")

    def test_activation_site(self):
        self.block_domain(self.THIRD_PARTY_DOMAIN)
        self.load_url(self.BASIC_FIXTURE_URL)
        self.assert_replacement()

        # click the "allow once" button
        self.activate_widget()

        # verify the original widget is restored
        self.assert_widget()

        # open a new window (to get around widget activation caching)
        self.open_window()
        self.load_url(self.BASIC_FIXTURE_URL)

        # verify the widget got replaced
        self.assert_replacement()

        # click the "allow on site" button
        self.activate_widget(once=False)

        # verify the original widget is restored
        self.assert_widget()

        # open a new window (to get around widget activation caching)
        self.open_window()
        self.load_url(self.BASIC_FIXTURE_URL)

        # verify basic widget is neither replaced nor blocked
        self.assert_no_replacement()
        self.assert_widget()

    def test_disabling_site(self):
        self.block_domain(self.THIRD_PARTY_DOMAIN)

        self.disable_badger_on_site(self.BASIC_FIXTURE_URL)

        # verify basic widget is neither replaced nor blocked
        self.load_url(self.BASIC_FIXTURE_URL)
        self.assert_no_replacement()
        self.assert_widget()
        # type 4 replacement should also be missing
        self.assert_no_replacement(self.TYPE4_WIDGET_NAME)
        # while the type 4 widget script should have executed
        self.assert_widget("type4")

        # verify dynamic widget is neither replaced nor blocked
        self.load_url(self.DYNAMIC_FIXTURE_URL)
        self.find_el_by_css('#widget-trigger').click()
        self.assert_no_replacement()
        self.assert_widget()

    def test_disabling_all_replacement(self):
        self.block_domain(self.THIRD_PARTY_DOMAIN)

        # disable widget replacement
        self.load_url(self.options_url)
        self.wait_for_script("return window.OPTIONS_INITIALIZED")
        self.find_el_by_css('a[href="#tab-manage-widgets"]').click()
        self.driver.find_element_by_id('replace-widgets-checkbox').click()

        # verify basic widget is no longer replaced
        self.load_url(self.BASIC_FIXTURE_URL)
        self.assert_no_replacement()
        self.assert_widget_blocked()
        # type 4 replacement should also be missing
        self.assert_no_replacement(self.TYPE4_WIDGET_NAME)
        # type 4 widget should also have gotten blocked
        try:
            widget_div = self.driver.find_element_by_css_selector(
                'div.pb-type4-test-widget')
        except NoSuchElementException:
            self.fail("Widget div should still be here")
        # check the div's text a few times to make sure it stays empty
        for _ in range(3):
            self.assertFalse(widget_div.text,
                "Widget output container should remain empty")
            sleep(1)

        # verify dynamic widget is no longer replaced
        self.load_url(self.DYNAMIC_FIXTURE_URL)
        self.find_el_by_css('#widget-trigger').click()
        self.assert_no_replacement()
        self.assert_widget_blocked()

    def test_disabling_replacement_for_one_widget(self):
        self.block_domain(self.THIRD_PARTY_DOMAIN)

        # add the widget to the list of exceptions
        self.load_url(self.options_url)
        self.wait_for_script("return window.OPTIONS_INITIALIZED")
        self.find_el_by_css('a[href="#tab-manage-widgets"]').click()
        self.find_el_by_css('input[type="search"]').send_keys(
            self.TYPE3_WIDGET_NAME, Keys.ENTER)

        # verify basic widget is no longer replaced
        self.load_url(self.BASIC_FIXTURE_URL)
        self.assert_no_replacement()
        self.assert_widget_blocked()
        # verify the type 4 widget is still replaced
        self.assert_replacement(self.TYPE4_WIDGET_NAME)

        # verify dynamic widget is no longer replaced
        self.load_url(self.DYNAMIC_FIXTURE_URL)
        self.find_el_by_css('#widget-trigger').click()
        self.assert_no_replacement()
        self.assert_widget_blocked()

    def test_no_replacement_when_cookieblocked(self):
        self.cookieblock_domain(self.THIRD_PARTY_DOMAIN)
        self.load_url(self.BASIC_FIXTURE_URL)

        self.assert_no_replacement()
        self.assert_no_replacement(self.TYPE4_WIDGET_NAME)

        self.assert_widget()
        self.assert_widget("type4")


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