| 
      1
     | 
    
      # SPDX-License-Identifier: CC0-1.0
 
     | 
  
  
    | 
      2
     | 
    
      
 
     | 
  
  
    | 
      3
     | 
    
      """
 
     | 
  
  
    | 
      4
     | 
    
      Haketilo unit tests - repository querying
 
     | 
  
  
    | 
      5
     | 
    
      """
 
     | 
  
  
    | 
      6
     | 
    
      
 
     | 
  
  
    | 
      7
     | 
    
      # This file is part of Haketilo
 
     | 
  
  
    | 
      8
     | 
    
      #
 
     | 
  
  
    | 
      9
     | 
    
      # Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
 
     | 
  
  
    | 
      10
     | 
    
      #
 
     | 
  
  
    | 
      11
     | 
    
      # This program is free software: you can redistribute it and/or modify
 
     | 
  
  
    | 
      12
     | 
    
      # it under the terms of the CC0 1.0 Universal License as published by
 
     | 
  
  
    | 
      13
     | 
    
      # the Creative Commons Corporation.
 
     | 
  
  
    | 
      14
     | 
    
      #
 
     | 
  
  
    | 
      15
     | 
    
      # This program is distributed in the hope that it will be useful,
 
     | 
  
  
    | 
      16
     | 
    
      # but WITHOUT ANY WARRANTY; without even the implied warranty of
 
     | 
  
  
    | 
      17
     | 
    
      # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
     | 
  
  
    | 
      18
     | 
    
      # CC0 1.0 Universal License for more details.
 
     | 
  
  
    | 
      19
     | 
    
      
 
     | 
  
  
    | 
      20
     | 
    
      import pytest
 
     | 
  
  
    | 
      21
     | 
    
      import json
 
     | 
  
  
    | 
      22
     | 
    
      from selenium.webdriver.support.ui import WebDriverWait
 
     | 
  
  
    | 
      23
     | 
    
      
 
     | 
  
  
    | 
      24
     | 
    
      from ..extension_crafting import ExtraHTML
 
     | 
  
  
    | 
      25
     | 
    
      from ..script_loader import load_script
 
     | 
  
  
    | 
      26
     | 
    
      from .utils import *
 
     | 
  
  
    | 
      27
     | 
    
      
 
     | 
  
  
    | 
      28
     | 
    
      unprivileged_page_info = {
     | 
  
  
    | 
      29
     | 
    
          'url': 'https://example_a.com/something',
 
     | 
  
  
    | 
      30
     | 
    
          'allow': False
 
     | 
  
  
    | 
      31
     | 
    
      }
 
     | 
  
  
    | 
      32
     | 
    
      
 
     | 
  
  
    | 
      33
     | 
    
      mapping_page_info = {
     | 
  
  
    | 
      34
     | 
    
          **unprivileged_page_info,
 
     | 
  
  
    | 
      35
     | 
    
          'mapping': 'm1',
 
     | 
  
  
    | 
      36
     | 
    
          'payload': {'identifier': 'res1'}
     | 
  
  
    | 
      37
     | 
    
      }
 
     | 
  
  
    | 
      38
     | 
    
      
 
     | 
  
  
    | 
      39
     | 
    
      mocked_page_infos = {
     | 
  
  
    | 
      40
     | 
    
          'privileged': {
     | 
  
  
    | 
      41
     | 
    
              'url': 'moz-extension://<some-id>/file.html',
 
     | 
  
  
    | 
      42
     | 
    
              'privileged': True
 
     | 
  
  
    | 
      43
     | 
    
          },
 
     | 
  
  
    | 
      44
     | 
    
          'blocked_default': unprivileged_page_info,
 
     | 
  
  
    | 
      45
     | 
    
          'allowed_default': {
     | 
  
  
    | 
      46
     | 
    
              **unprivileged_page_info,
 
     | 
  
  
    | 
      47
     | 
    
              'allow': True
 
     | 
  
  
    | 
      48
     | 
    
          },
 
     | 
  
  
    | 
      49
     | 
    
          'blocked_rule': {
     | 
  
  
    | 
      50
     | 
    
              **unprivileged_page_info,
 
     | 
  
  
    | 
      51
     | 
    
              'mapping': '~allow'
 
     | 
  
  
    | 
      52
     | 
    
          },
 
     | 
  
  
    | 
      53
     | 
    
          'allowed_rule': {
     | 
  
  
    | 
      54
     | 
    
              **unprivileged_page_info,
 
     | 
  
  
    | 
      55
     | 
    
              'allow': True,
 
     | 
  
  
    | 
      56
     | 
    
              'mapping': '~allow'
 
     | 
  
  
    | 
      57
     | 
    
          },
 
     | 
  
  
    | 
      58
     | 
    
          'mapping': mapping_page_info,
 
     | 
  
  
    | 
      59
     | 
    
          'error_deciding_policy': {
     | 
  
  
    | 
      60
     | 
    
              **mapping_page_info,
 
     | 
  
  
    | 
      61
     | 
    
              'error': {'haketilo_error_type': 'deciding_policy'}
     | 
  
  
    | 
      62
     | 
    
          },
 
     | 
  
  
    | 
      63
     | 
    
          'error_missing': {
     | 
  
  
    | 
      64
     | 
    
              **mapping_page_info,
 
     | 
  
  
    | 
      65
     | 
    
              'error': {'haketilo_error_type': 'missing', 'id': 'some-missing-res'}
     | 
  
  
    | 
      66
     | 
    
          },
 
     | 
  
  
    | 
      67
     | 
    
          'error_circular': {
     | 
  
  
    | 
      68
     | 
    
              **mapping_page_info,
 
     | 
  
  
    | 
      69
     | 
    
              'error': {'haketilo_error_type': 'circular', 'id': 'some-circular-res'}
     | 
  
  
    | 
      70
     | 
    
          },
 
     | 
  
  
    | 
      71
     | 
    
          'error_db': {
     | 
  
  
    | 
      72
     | 
    
              **mapping_page_info,
 
     | 
  
  
    | 
      73
     | 
    
              'error': {'haketilo_error_type': 'db'}
     | 
  
  
    | 
      74
     | 
    
          },
 
     | 
  
  
    | 
      75
     | 
    
          'error_other': {
     | 
  
  
    | 
      76
     | 
    
              **mapping_page_info,
 
     | 
  
  
    | 
      77
     | 
    
              'error': {'haketilo_error_type': 'other'}
     | 
  
  
    | 
      78
     | 
    
          }
 
     | 
  
  
    | 
      79
     | 
    
      }
 
     | 
  
  
    | 
      80
     | 
    
      
 
     | 
  
  
    | 
      81
     | 
    
      tab_mock_js = '''
 
     | 
  
  
    | 
      82
     | 
    
      ;
 
     | 
  
  
    | 
      83
     | 
    
      const mocked_page_info = (%s)[/#mock_page_info-(.*)$/.exec(document.URL)[1]];
 
     | 
  
  
    | 
      84
     | 
    
      const old_sendMessage = browser.tabs.sendMessage;
 
     | 
  
  
    | 
      85
     | 
    
      browser.tabs.sendMessage = async function(tab_id, msg) {
     | 
  
  
    | 
      86
     | 
    
          const this_tab_id = (await browser.tabs.getCurrent()).id;
 
     | 
  
  
    | 
      87
     | 
    
          if (tab_id !== this_tab_id)
 
     | 
  
  
    | 
      88
     | 
    
              throw `not current tab id (${tab_id} instead of ${this_tab_id})`;
     | 
  
  
    | 
      89
     | 
    
      
 
     | 
  
  
    | 
      90
     | 
    
          if (msg[0] === "page_info")
 
     | 
  
  
    | 
      91
     | 
    
              return mocked_page_info;
 
     | 
  
  
    | 
      92
     | 
    
          else if (msg[0] === "repo_query")
 
     | 
  
  
    | 
      93
     | 
    
              return old_sendMessage(tab_id, msg);
 
     | 
  
  
    | 
      94
     | 
    
          else
 
     | 
  
  
    | 
      95
     | 
    
              throw `bad sendMessage message type: '${msg[0]}'`;
     | 
  
  
    | 
      96
     | 
    
      }
 
     | 
  
  
    | 
      97
     | 
    
      
 
     | 
  
  
    | 
      98
     | 
    
      const old_tabs_query = browser.tabs.query;
 
     | 
  
  
    | 
      99
     | 
    
      browser.tabs.query = async function(query) {
     | 
  
  
    | 
      100
     | 
    
          const tabs = await old_tabs_query(query);
 
     | 
  
  
    | 
      101
     | 
    
          tabs.forEach(t => t.url = mocked_page_info.url);
 
     | 
  
  
    | 
      102
     | 
    
          return tabs;
 
     | 
  
  
    | 
      103
     | 
    
      }
 
     | 
  
  
    | 
      104
     | 
    
      ''' % json.dumps(mocked_page_infos)
 
     | 
  
  
    | 
      105
     | 
    
      
 
     | 
  
  
    | 
      106
     | 
    
      tab_mock_js = mock_cacher_code + tab_mock_js
 
     | 
  
  
    | 
      107
     | 
    
      
 
     | 
  
  
    | 
      108
     | 
    
      popup_ext_data = {
     | 
  
  
    | 
      109
     | 
    
          'background_script': broker_js,
 
     | 
  
  
    | 
      110
     | 
    
          'extra_html': ExtraHTML(
 
     | 
  
  
    | 
      111
     | 
    
              'html/popup.html',
 
     | 
  
  
    | 
      112
     | 
    
              {
     | 
  
  
    | 
      113
     | 
    
                  'common/browser.js':   tab_mock_js,
 
     | 
  
  
    | 
      114
     | 
    
                  'common/indexeddb.js': '; set_repo("https://hydril.la/");'
     | 
  
  
    | 
      115
     | 
    
              },
 
     | 
  
  
    | 
      116
     | 
    
              wrap_into_htmldoc=False
 
     | 
  
  
    | 
      117
     | 
    
          ),
 
     | 
  
  
    | 
      118
     | 
    
          'navigate_to': 'html/popup.html'
 
     | 
  
  
    | 
      119
     | 
    
      }
 
     | 
  
  
    | 
      120
     | 
    
      
 
     | 
  
  
    | 
      121
     | 
    
      @pytest.mark.ext_data(popup_ext_data)
 
     | 
  
  
    | 
      122
     | 
    
      @pytest.mark.usefixtures('webextension')
     | 
  
  
    | 
      123
     | 
    
      @pytest.mark.parametrize('page_info_key', ['', *mocked_page_infos.keys()])
     | 
  
  
    | 
      124
     | 
    
      def test_popup_display(driver, execute_in_page, page_info_key):
 
     | 
  
  
    | 
      125
     | 
    
          """
 
     | 
  
  
    | 
      126
     | 
    
          Test popup viewing while on a page. Test parametrized with different
 
     | 
  
  
    | 
      127
     | 
    
          possible values of page_info object passed in message from the content
 
     | 
  
  
    | 
      128
     | 
    
          script.
 
     | 
  
  
    | 
      129
     | 
    
          """
 
     | 
  
  
    | 
      130
     | 
    
          initial_url = driver.current_url
 
     | 
  
  
    | 
      131
     | 
    
          driver.get('about:blank')
     | 
  
  
    | 
      132
     | 
    
          driver.get(f'{initial_url}#mock_page_info-{page_info_key}')
     | 
  
  
    | 
      133
     | 
    
      
 
     | 
  
  
    | 
      134
     | 
    
          by_id = driver.execute_script(
 
     | 
  
  
    | 
      135
     | 
    
              '''
 
     | 
  
  
    | 
      136
     | 
    
              const nodes = [...document.querySelectorAll("[id]")];
     | 
  
  
    | 
      137
     | 
    
              const reductor = (ob, node) => Object.assign(ob, {[node.id]: node});
     | 
  
  
    | 
      138
     | 
    
              return nodes.reduce(reductor, {});
     | 
  
  
    | 
      139
     | 
    
              ''')
 
     | 
  
  
    | 
      140
     | 
    
      
 
     | 
  
  
    | 
      141
     | 
    
          if page_info_key == '':
 
     | 
  
  
    | 
      142
     | 
    
              error_msg = 'Page info not avaialable. Try reloading the page.'
 
     | 
  
  
    | 
      143
     | 
    
              error_msg_shown = lambda d: by_id['loading_info'].text == error_msg
 
     | 
  
  
    | 
      144
     | 
    
              WebDriverWait(driver, 10).until(error_msg_shown)
 
     | 
  
  
    | 
      145
     | 
    
              return
 
     | 
  
  
    | 
      146
     | 
    
      
 
     | 
  
  
    | 
      147
     | 
    
          WebDriverWait(driver, 10).until(lambda d: by_id['info_form'].is_displayed())
 
     | 
  
  
    | 
      148
     | 
    
          assert (page_info_key == 'privileged') == \
 
     | 
  
  
    | 
      149
     | 
    
              by_id['privileged_page_info'].is_displayed()
 
     | 
  
  
    | 
      150
     | 
    
          assert (page_info_key == 'privileged') ^ \
 
     | 
  
  
    | 
      151
     | 
    
              by_id['unprivileged_page_info'].is_displayed()
 
     | 
  
  
    | 
      152
     | 
    
          assert by_id['page_url'].text == mocked_page_infos[page_info_key]['url']
 
     | 
  
  
    | 
      153
     | 
    
          assert not by_id['repo_query_container'].is_displayed()
 
     | 
  
  
    | 
      154
     | 
    
      
 
     | 
  
  
    | 
      155
     | 
    
          if 'allow' in page_info_key:
 
     | 
  
  
    | 
      156
     | 
    
              assert by_id['scripts_blocked'].text.lower() == 'no'
 
     | 
  
  
    | 
      157
     | 
    
          elif page_info_key != 'privileged':
 
     | 
  
  
    | 
      158
     | 
    
              assert by_id['scripts_blocked'].text.lower() == 'yes'
 
     | 
  
  
    | 
      159
     | 
    
      
 
     | 
  
  
    | 
      160
     | 
    
          payload_text = by_id['injected_payload'].text
 
     | 
  
  
    | 
      161
     | 
    
          if page_info_key == 'mapping':
 
     | 
  
  
    | 
      162
     | 
    
              assert payload_text == 'res1'
 
     | 
  
  
    | 
      163
     | 
    
          elif page_info_key == 'error_missing':
 
     | 
  
  
    | 
      164
     | 
    
              assert payload_text == \
 
     | 
  
  
    | 
      165
     | 
    
                  "None (error: resource with id 'some-missing-res' missing from the database)"
 
     | 
  
  
    | 
      166
     | 
    
          elif page_info_key == 'error_circular':
 
     | 
  
  
    | 
      167
     | 
    
              assert payload_text == \
 
     | 
  
  
    | 
      168
     | 
    
                  "None (error: circular dependency of resource with id 'some-circular-res' on itself)"
 
     | 
  
  
    | 
      169
     | 
    
          elif page_info_key == 'error_db':
 
     | 
  
  
    | 
      170
     | 
    
              assert payload_text == \
 
     | 
  
  
    | 
      171
     | 
    
                  'None (error: failure reading Haketilo internal database)'
 
     | 
  
  
    | 
      172
     | 
    
          elif page_info_key == 'error_other':
 
     | 
  
  
    | 
      173
     | 
    
              assert payload_text == \
 
     | 
  
  
    | 
      174
     | 
    
                  'None (error: unknown failure occured)'
 
     | 
  
  
    | 
      175
     | 
    
          elif page_info_key != 'privileged':
 
     | 
  
  
    | 
      176
     | 
    
              assert payload_text == 'None'
 
     | 
  
  
    | 
      177
     | 
    
      
 
     | 
  
  
    | 
      178
     | 
    
          mapping_text = by_id['mapping_used'].text
 
     | 
  
  
    | 
      179
     | 
    
      
 
     | 
  
  
    | 
      180
     | 
    
          if page_info_key == 'error_deciding_policy':
 
     | 
  
  
    | 
      181
     | 
    
              assert mapping_text == 'None (error occured when determining policy)'
 
     | 
  
  
    | 
      182
     | 
    
          elif page_info_key == 'mapping' or page_info_key.startswith('error'):
     | 
  
  
    | 
      183
     | 
    
              assert mapping_text == 'm1'
 
     | 
  
  
    | 
      184
     | 
    
      
 
     | 
  
  
    | 
      185
     | 
    
          if 'allowed' in page_info_key:
 
     | 
  
  
    | 
      186
     | 
    
              assert 'None (scripts allowed by' in mapping_text
 
     | 
  
  
    | 
      187
     | 
    
          elif 'blocked' in page_info_key:
 
     | 
  
  
    | 
      188
     | 
    
              assert 'None (scripts blocked by' in mapping_text
 
     | 
  
  
    | 
      189
     | 
    
      
 
     | 
  
  
    | 
      190
     | 
    
          if 'rule' in page_info_key:
 
     | 
  
  
    | 
      191
     | 
    
              assert 'by a rule)' in mapping_text
 
     | 
  
  
    | 
      192
     | 
    
          elif 'default' in page_info_key:
 
     | 
  
  
    | 
      193
     | 
    
              assert 'by default policy)' in mapping_text
 
     | 
  
  
    | 
      194
     | 
    
      
 
     | 
  
  
    | 
      195
     | 
    
      @pytest.mark.ext_data(popup_ext_data)
 
     | 
  
  
    | 
      196
     | 
    
      @pytest.mark.usefixtures('webextension')
     | 
  
  
    | 
      197
     | 
    
      def test_popup_repo_query(driver, execute_in_page):
 
     | 
  
  
    | 
      198
     | 
    
          """
 
     | 
  
  
    | 
      199
     | 
    
          Test opening and closing the repo query view in popup.
 
     | 
  
  
    | 
      200
     | 
    
          """
 
     | 
  
  
    | 
      201
     | 
    
          initial_url = driver.current_url
 
     | 
  
  
    | 
      202
     | 
    
          driver.get('about:blank')
     | 
  
  
    | 
      203
     | 
    
          driver.get(f'{initial_url}#mock_page_info-blocked_rule')
     | 
  
  
    | 
      204
     | 
    
      
 
     | 
  
  
    | 
      205
     | 
    
          search_but = driver.find_element_by_id("search_resources_but")
     | 
  
  
    | 
      206
     | 
    
          WebDriverWait(driver, 10).until(lambda d: search_but.is_displayed())
 
     | 
  
  
    | 
      207
     | 
    
          search_but.click()
 
     | 
  
  
    | 
      208
     | 
    
      
 
     | 
  
  
    | 
      209
     | 
    
          containers = dict([(name, driver.find_element_by_id(f'{name}_container'))
     | 
  
  
    | 
      210
     | 
    
                             for name in ('page_info', 'repo_query')])
     | 
  
  
    | 
      211
     | 
    
          assert not containers['page_info'].is_displayed()
 
     | 
  
  
    | 
      212
     | 
    
          assert containers['repo_query'].is_displayed()
 
     | 
  
  
    | 
      213
     | 
    
          shown = lambda d: 'https://hydril.la/' in containers['repo_query'].text
 
     | 
  
  
    | 
      214
     | 
    
          WebDriverWait(driver, 10).until(shown)
 
     | 
  
  
    | 
      215
     | 
    
      
 
     | 
  
  
    | 
      216
     | 
    
          # Click the "Show results" button.
 
     | 
  
  
    | 
      217
     | 
    
          selector = '.repo_query_buttons > button:first-child'
 
     | 
  
  
    | 
      218
     | 
    
          driver.find_element_by_css_selector(selector).click()
 
     | 
  
  
    | 
      219
     | 
    
          shown = lambda d: 'MAPPING-A' in containers['repo_query'].text
 
     | 
  
  
    | 
      220
     | 
    
          WebDriverWait(driver, 10).until(shown)
 
     | 
  
  
    | 
      221
     | 
    
      
 
     | 
  
  
    | 
      222
     | 
    
          # Click the "Cancel" button
 
     | 
  
  
    | 
      223
     | 
    
          selector = '.repo_query_bottom_buttons > button'
 
     | 
  
  
    | 
      224
     | 
    
          driver.find_element_by_css_selector(selector).click()
 
     | 
  
  
    | 
      225
     | 
    
          assert containers['page_info'].is_displayed()
 
     | 
  
  
    | 
      226
     | 
    
          assert not containers['repo_query'].is_displayed()
 
     | 
  
  
    | 
      227
     | 
    
      
 
     | 
  
  
    | 
      228
     | 
    
      @pytest.mark.ext_data(popup_ext_data)
 
     | 
  
  
    | 
      229
     | 
    
      @pytest.mark.usefixtures('webextension')
     | 
  
  
    | 
      230
     | 
    
      # Under Parabola's Iceweasel 75 the settings page's window opened during this
 
     | 
  
  
    | 
      231
     | 
    
      # test is impossible to close using driver.close() - it raises an exception with
 
     | 
  
  
    | 
      232
     | 
    
      # message 'closeTab() not supported in iceweasel'. To avoid such error during
 
     | 
  
  
    | 
      233
     | 
    
      # test cleanup, we use the mark below to tell our driver fixture to span a
 
     | 
  
  
    | 
      234
     | 
    
      # separate browser instance for this test.
 
     | 
  
  
    | 
      235
     | 
    
      @pytest.mark.second_driver()
 
     | 
  
  
    | 
      236
     | 
    
      def test_popup_settings_opening(driver, execute_in_page):
 
     | 
  
  
    | 
      237
     | 
    
          """
 
     | 
  
  
    | 
      238
     | 
    
          Test opening the settings page from popup through button click.
 
     | 
  
  
    | 
      239
     | 
    
          """
 
     | 
  
  
    | 
      240
     | 
    
          driver.find_element_by_id("settings_but").click()
     | 
  
  
    | 
      241
     | 
    
      
 
     | 
  
  
    | 
      242
     | 
    
          first_handle = driver.current_window_handle
 
     | 
  
  
    | 
      243
     | 
    
          WebDriverWait(driver, 10).until(lambda d: len(d.window_handles) == 2)
 
     | 
  
  
    | 
      244
     | 
    
          new_handle = [h for h in driver.window_handles if h != first_handle][0]
 
     | 
  
  
    | 
      245
     | 
    
      
 
     | 
  
  
    | 
      246
     | 
    
          driver.switch_to.window(new_handle)
 
     | 
  
  
    | 
      247
     | 
    
          driver.implicitly_wait(10)
 
     | 
  
  
    | 
      248
     | 
    
          assert "Extension's options page for testing" in \
 
     | 
  
  
    | 
      249
     | 
    
              driver.find_element_by_tag_name("h1").text
     |