From fd9f2fc4783cc606734e61116185c032a63d54a0 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Wed, 16 Feb 2022 22:01:38 +0100 Subject: fix out-of-source builds --- test/haketilo_test/extension_crafting.py | 215 +++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 test/haketilo_test/extension_crafting.py (limited to 'test/haketilo_test/extension_crafting.py') diff --git a/test/haketilo_test/extension_crafting.py b/test/haketilo_test/extension_crafting.py new file mode 100644 index 0000000..97f5027 --- /dev/null +++ b/test/haketilo_test/extension_crafting.py @@ -0,0 +1,215 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +""" +Making temporary WebExtensions for use in the test suite +""" + +# This file is part of Haketilo. +# +# Copyright (C) 2021, 2022 Wojtek Kosior +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# I, Wojtek Kosior, thereby promise not to sue for violation of this file's +# license. Although I request that you do not make use of this code in a +# proprietary program, I am not going to enforce this in court. + +import json +import zipfile +import re +import shutil +import subprocess + +from pathlib import Path +from uuid import uuid4 +from tempfile import TemporaryDirectory + +from selenium.webdriver.support.ui import WebDriverWait +from selenium.common.exceptions import NoSuchElementException + +from .misc_constants import * + +class ManifestTemplateValueToFill: + pass + +def manifest_template(): + return { + 'manifest_version': 2, + 'name': 'Haketilo test extension', + 'version': '1.0', + 'applications': { + 'gecko': { + 'id': ManifestTemplateValueToFill(), + 'strict_min_version': '60.0' + } + }, + 'permissions': [ + 'contextMenus', + 'webRequest', + 'webRequestBlocking', + 'activeTab', + 'notifications', + 'sessions', + 'storage', + 'tabs', + '', + 'unlimitedStorage' + ], + 'content_security_policy': "object-src 'none'; script-src 'self' https://serve.scrip.ts;", + 'web_accessible_resources': ['testpage.html'], + 'options_ui': { + 'page': 'testpage.html', + 'open_in_tab': True + }, + 'background': { + 'persistent': True, + 'scripts': ['__open_test_page.js', 'background.js'] + }, + 'content_scripts': [ + { + 'run_at': 'document_start', + 'matches': [''], + 'match_about_blank': True, + 'all_frames': True, + 'js': ['content.js'] + } + ] + } + +class ExtraHTML: + def __init__(self, html_path, append={}, wrap_into_htmldoc=True): + self.html_path = html_path + self.append = append + self.wrap_into_htmldoc = wrap_into_htmldoc + + def add_to_xpi(self, xpi, tmpdir=None): + if tmpdir is None: + with TemporaryDirectory() as tmpdir: + return self.add_to_xpi(xpi, tmpdir) + + append_flags = [] + for filename, code in self.append.items(): + append_flags.extend(['-A', f'{filename}:{code}']) + + awk = subprocess.run( + ['awk', '-f', awk_script_name, '--', *unit_test_defines, + *append_flags, '-H', self.html_path, '--write-js-deps', + '--output=files-to-copy', f'--output-dir={tmpdir}'], + stdout=subprocess.PIPE, cwd=proj_root, check=True + ) + + for path in filter(None, awk.stdout.decode().split('\n')): + xpi.write(proj_root / path, path) + + tmpdir = Path(tmpdir) + for path in tmpdir.rglob('*'): + relpath = str(path.relative_to(tmpdir)) + if not path.is_dir() and relpath != self.html_path: + xpi.write(path, relpath) + + with open(tmpdir / self.html_path, 'rt') as html_file: + html = html_file.read() + if self.wrap_into_htmldoc: + html = f'{html}' + xpi.writestr(self.html_path, html) + +default_background_script = '' +default_content_script = '' +default_test_page = ''' + + + + Extension's options page for testing + + +

Extension's options page for testing

+ + +''' + +open_test_page_script = '''(() => { +const page_url = browser.runtime.getURL("testpage.html"); +const execute_details = { + code: `window.wrappedJSObject.ext_page_url=${JSON.stringify(page_url)};` +}; +browser.tabs.query({currentWindow: true, active: true}) + .then(t => browser.tabs.executeScript(t.id, execute_details)); +})();''' + +def make_extension(destination_dir, + background_script=default_background_script, + content_script=default_content_script, + test_page=default_test_page, + extra_files={}, extra_html=[]): + if not hasattr(extra_html, '__iter__'): + extra_html = [extra_html] + manifest = manifest_template() + extension_id = '{%s}' % uuid4() + manifest['applications']['gecko']['id'] = extension_id + files = { + 'manifest.json' : json.dumps(manifest), + '__open_test_page.js': open_test_page_script, + 'background.js' : background_script, + 'content.js' : content_script, + 'testpage.html' : test_page, + **extra_files + } + destination_path = destination_dir / f'{extension_id}.xpi' + with zipfile.ZipFile(destination_path, 'x') as xpi: + for filename, contents in files.items(): + if hasattr(contents, '__call__'): + contents = contents() + xpi.writestr(filename, contents) + for html in extra_html: + html.add_to_xpi(xpi) + + return destination_path + +extract_base_url_re = re.compile(r'^(.*)manifest.json$') + +def get_extension_base_url(driver): + """ + Extension's internall UUID is not directly exposed in Selenium. Instead, we + can navigate to about:debugging and inspect the manifest URL present there + to get the base url like: + moz-extension://b225c78f-d108-4caa-8406-f38b37d8dee5/ + which can then be used to navigate to extension-bundled pages. + """ + # For newer Firefoxes + driver.get('about:debugging#/runtime/this-firefox') + + def get_manifest_link_newer_ff(driver): + try: + return driver.find_element_by_class_name('qa-manifest-url') + except NoSuchElementException: + pass + + try: + details = driver.find_element_by_class_name('error-page-details') + except NoSuchElementException: + return False + + if '#/runtime/this-firefox' in details.text: + return "not_newer_ff" + + manifest_link = WebDriverWait(driver, 10).until(get_manifest_link_newer_ff) + + if manifest_link == "not_newer_ff": + driver.get("about:debugging#addons") + driver.implicitly_wait(10) + manifest_link = driver.find_element_by_class_name('manifest-url') + driver.implicitly_wait(0) + + manifest_url = manifest_link.get_attribute('href') + return extract_base_url_re.match(manifest_url).group(1) -- cgit v1.2.3