From 05dd1a4c33fb6ce053e553fc2e463c80901a04ca Mon Sep 17 00:00:00 2001 From: Jakub Wilk Date: Sat, 30 Jan 2016 22:36:50 +0100 Subject: [PATCH 1/6] Add the test suite --- test/.gitignore | 1 + test/firefox.py | 148 ++++++++++++++++++++++++++++++++++++++++++++++++ test/httpd.pem | 44 ++++++++++++++ test/httpd.py | 65 +++++++++++++++++++++ test/run | 106 ++++++++++++++++++++++++++++++++++ 5 files changed, 364 insertions(+) create mode 100644 test/.gitignore create mode 100644 test/firefox.py create mode 100644 test/httpd.pem create mode 100644 test/httpd.py create mode 100755 test/run diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..bee8a64 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/test/firefox.py b/test/firefox.py new file mode 100644 index 0000000..9a8fa7a --- /dev/null +++ b/test/firefox.py @@ -0,0 +1,148 @@ +# Copyright © 2014 Jakub Wilk +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the “Software”), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import contextlib +import os +import shutil +import subprocess as ipc +import tempfile +import xml.etree.ElementTree as et + +class Firefox(object): + + def __init__(self, url): + self.url = url + self.child = None + + def __enter__(self): + with open(os.devnull, 'wb') as dev_null: + self.child = ipc.Popen( + ['firefox', '-no-remote', self.url], + stderr=dev_null, + ) + return self + + def __exit__(self, exc_type, exc_value, traceback): + if exc_type is not None: + self.child.terminate() + self.child.wait() + + def wait(self): + self.child.wait() + + def get_window_id(self, window_name): + cmdline = [ + 'xdotool', + 'search', + '--sync', + '--limit', '1', + '--all', + '--pid', str(self.child.pid), + '--name', window_name, + ] + output = ipc.check_output(cmdline) + return int(output) + + def talk(self, window, *args): + cmdline = ['xdotool'] + if isinstance(window, int): + cmdline += [ + 'key', '--window', str(window) + ] + else: + cmdline += [ + 'search', + '--sync', + '--limit', '1', + '--all', + '--pid', str(self.child.pid), + '--name', window, + 'key', + ] + for arg in args: + if arg[:1] + arg[-1:] == '<>': + for ch in arg[1:-1]: + if ch == ' ': + cmdline += ['space'] + else: + cmdline += [ch] + else: + cmdline += [arg] + ipc.check_call(cmdline) + +profiles_ini = '''\ +[General] +StartWithLastProfile=1 + +[Profile0] +Name=default +IsRelative=1 +Path=default +''' + +xml_namespaces = dict( + em='http://www.mozilla.org/2004/em-rdf#' +) + +def get_addon_id(): + rdf_path = os.path.dirname(__file__) + '/../src/install.rdf' + with open(rdf_path, 'rb') as rdf_file: + rdf_tree = et.parse(rdf_file) + id_elem = rdf_tree.find('.//em:id', namespaces=xml_namespaces) + return id_elem.text + +extensions_ini = '''\ +[ExtensionDirs] +Extension0=/usr/share/mozilla/extensions/{firefox_id}/{addon_id} +'''.format( + firefox_id='{ec8030f7-c20a-464f-9b0e-13a3a9e97384}', + addon_id=get_addon_id() +) + +prefs_js = ''' +user_pref("network.proxy.http", "127.0.0.1"); +user_pref("network.proxy.http_port", 9); +user_pref("network.proxy.ssl", "127.0.0.1"); +user_pref("network.proxy.ssl_port", 9); +user_pref("network.proxy.type", 1); +''' + +@contextlib.contextmanager +def clean_home_dir(): + home = tempfile.mkdtemp(prefix='y-u-no-validate.') + try: + mozilla_home = home + '/.mozilla/firefox' + os.makedirs(mozilla_home + '/default') + with open(mozilla_home + '/profiles.ini', 'wt', encoding='ASCII') as file: + file.write(profiles_ini) + with open(mozilla_home + '/default/extensions.ini', 'wt', encoding='ASCII') as file: + file.write(extensions_ini) + with open(mozilla_home + '/default/prefs.js', 'wt', encoding='ASCII') as file: + file.write(prefs_js) + old_home = os.environ['HOME'] + os.environ['HOME'] = home + try: + yield + finally: + os.environ['HOME'] = old_home + finally: + shutil.rmtree(home) + +# vim:ts=4 sts=4 sw=4 et diff --git a/test/httpd.pem b/test/httpd.pem new file mode 100644 index 0000000..b008f9e --- /dev/null +++ b/test/httpd.pem @@ -0,0 +1,44 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA8wYxnI1NiEhG3t/+b2XGBOiH0WUhLvG13aIrc/Ttm76uEa4/ +Jpt0YtU+XNIN3gBSdzu7XYPsf2OZD8VhimrMDQHawECfOxEgyGfFEg3rqSFgJzwm +hiWf4+ZsTd0kFltohxkadpbNdSTKeU0/gCYUxmc1MsZf/S9NeWbZBcelj2+pfzFv +PwYfuuy7o+aOpCR/7CeOfTPStI0ngKPjM00Ozn9Lcpd/BVJ6hf+/K/3EontaYp1s +011z/h+pqKWUox9fOMsP90GQis3sB0D9rYffgN77f6Kc+8QcMYBjnHkD9/FXTKYP +hUjNVsqDD6sB5yIg/Che6T4RyStq31NzZghY5QIDAQABAoIBAFUUtgm47ovnwegF +Q258kvbk8ae9YACvXpxZSh0ugoEkIIzQFAvQIM75GPwmDfTK6BQWNWJn7UQr+kor +MBrliMqE+7CS7ywesvt2WOgZN4fex2r1BAey5GdFJGWeJiQtnEqv3zPlV8jLOJd/ +E9wpeNx9BYLhMHnTNaxq9U+wtaWhI2fWa/0VqwY0Cf+N0BoOGRG4Zw1+slEg0gN7 +AYws+r8hs0D446wATmMqtjrR6QUEBewXwvNpTNGidhHlDJwNeM62IGoSLN3FAnb6 +SCFwYnB5GhfEp9e/KFdk1DNjhuazpyH+PPO/ngKYDuaR1Jbx5w2s46FPH5eRA/pp +0GT1+4ECgYEA/kScHaj+xwpUVsz7xrGTZBuQAQx3qI5I8M+BA3RmPL7GF/nS9yze +c6zk4adtW8NG12ml0RBdmiknXETDxnrUI25+OW1Ubvup4ALHuaoH3dy52SMOb+QO +D+bGUNT0T1TdCcLSt7WF3ROByqp6eSLG4tB7oS4G212MTDEHoVrIpV8CgYEA9K36 +JAYfLYid7QLsv6XSl1qSonCrviVWvq1rp5wbOoo6mkU3zcV8TSWW1T5+KFkrG0AL +b10a+866zg4/AqFlx2/NNHBeajfSo3LWG1vF8ZCOs31w1UzVHBtzP9qIsmFxsaBC +ZS2zvpvxDKw7qBbCzscfhBbEPA72ScFsMNpDRDsCgYEA7k2DO1Edp5IIxWlMN0ZK +azJh3nm+09y2g+sWcRRwlVH++o2LqVkGC8foo4x1M+FqzY0YeT4rW8ZiO1m/Wo/X +rnVqG4xZ68I1zdsNMPcodEjgbZ8rcrZ8b27MQwmzB37zwqgzMTYRhcc4h6cLLejo +Gb9nfwJSLtoYGXiCPDmHf2UCgYBNM0+Haj8QGNjrXU7hsSpfAv7dLfuiRRm3k/Qx +sDmPIOoYntpanIL5vHB42/zmMiw9rtlsy82lwbaDKU+MPuxkHsx6TTIdBXv6glM3 +0p8D9v5vd5bQViuvcKHOdd9HmSNMTipkziS2cXF+9CDmijfxEjbJcH1+DaJ75wGB +8Hvk3QKBgQC7KOnGiImY3TNJ3ZWPzSPEedZJhrEVukiwU1qhKTNTbnEShaS34mY3 +GsQkvtbBXE9wqS44tQTLDSUxSdvWHGUUOgw92MBBRIN9tTOdqi2NiminaoWw3TYf +tOCE8N88Bao/H8ZQjV6nbXl0IbnalyGv8bh9yDOs+hGFYLL3GquW8Q== +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIICwjCCAaqgAwIBAgIEVY7wojANBgkqhkiG9w0BAQsFADAAMCIYDzIwMTUwNjI3 +MTg1MTE0WhgPMjAxNjA2MjYxODUxMjFaMAAwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDzBjGcjU2ISEbe3/5vZcYE6IfRZSEu8bXdoitz9O2bvq4Rrj8m +m3Ri1T5c0g3eAFJ3O7tdg+x/Y5kPxWGKaswNAdrAQJ87ESDIZ8USDeupIWAnPCaG +JZ/j5mxN3SQWW2iHGRp2ls11JMp5TT+AJhTGZzUyxl/9L015ZtkFx6WPb6l/MW8/ +Bh+67Luj5o6kJH/sJ459M9K0jSeAo+MzTQ7Of0tyl38FUnqF/78r/cSie1pinWzT +XXP+H6mopZSjH184yw/3QZCKzewHQP2th9+A3vt/opz7xBwxgGOceQP38VdMpg+F +SM1WyoMPqwHnIiD8KF7pPhHJK2rfU3NmCFjlAgMBAAGjQDA+MAwGA1UdEwEB/wQC +MAAwDwYDVR0PAQH/BAUDAwegADAdBgNVHQ4EFgQUbSMH+XYdHjNit33yjqLwq3Nv +K+MwDQYJKoZIhvcNAQELBQADggEBAI9Ao+e6zukr+uf0olKW0NSoueBvGL6NJIPD +6veuRSdQaxO8JXBWujdShGaAKZAhGnM8gqNF0sR2LPD0GM5Gi2jZJfMmV5g8Rjpb +g9jt8RkBbsVa7mtfuXJj7CM73LXrlIG5zGuGGqILr/6w+7+TVIAETqNcocRQks7w +KppAufUu4kj1snNe/K3a7TDiI2A7wjNHojOVfCHoE1v/a+4jw7APHGFzMPAEWpqi +RVp6hO9191hK5Ruts+6xAM3m8aRRHmJ1D4YQ/CaIt7NqFPAhmsdbRUJrICG5+08K +y33qjhhpgNC0OEsOIVc/s4Z4hSetdnVdyuLAhPulU55GDNUsVTQ= +-----END CERTIFICATE----- diff --git a/test/httpd.py b/test/httpd.py new file mode 100644 index 0000000..035164f --- /dev/null +++ b/test/httpd.py @@ -0,0 +1,65 @@ +# Copyright © 2014 Jakub Wilk +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the “Software”), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import http.server +import os +import ssl +import sys +import threading + +page_event = threading.Event() + +class RequestHandler(http.server.BaseHTTPRequestHandler): + + def do_GET(self): + self.send_response(200) + self.send_header('Content-Type', 'text/plain') + self.end_headers() + self.wfile.write(b'Hello world!') + self.wfile.flush() + page_event.set() + + def log_request(self, *args, **kwargs): + pass + +class HTTPServer(http.server.HTTPServer): + + def handle_error(self, request, client_address): + exc_type = sys.exc_info()[0] + if issubclass(exc_type, ssl.SSLError): + # SSL errors are expected and boring. + return + return super().handle_error(request, client_address) + +def run(): + httpd = HTTPServer(('127.0.0.1', 0), RequestHandler) + certfile = os.path.dirname(__file__) + '/httpd.pem' + httpd.socket = ssl.wrap_socket( + httpd.socket, + certfile=certfile, + server_side=True + ) + httpd_thread = threading.Thread(target=httpd.serve_forever, daemon=True) + httpd_thread.start() + host, port = httpd.socket.getsockname() + url = 'https://{host}:{port}/'.format(host=host, port=port) + return url + +# vim:ts=4 sts=4 sw=4 et diff --git a/test/run b/test/run new file mode 100755 index 0000000..f4b43bb --- /dev/null +++ b/test/run @@ -0,0 +1,106 @@ +#!/usr/bin/python3 + +# Copyright © 2014 Jakub Wilk +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the “Software”), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import contextlib +import os +import sys +import signal +import threading + +import firefox +import httpd + +class Timeout(RuntimeError): + default = 20 + + def raise_(*args, **kwargs): + raise Timeout + +@contextlib.contextmanager +def check(message, *, timeout=Timeout.default): + if timeout is not None: + signal.signal(signal.SIGALRM, Timeout.raise_) + signal.alarm(timeout) + print(message, '...', end=' ') + sys.stdout.flush() + try: + yield + print('ok') + except Timeout: + print('TIMEOUT') + raise + except Exception: + print('ERROR') + raise + finally: + if timeout is not None: + signal.alarm(0) + signal.signal(signal.SIGALRM, signal.SIG_DFL) + +def main(): + with firefox.clean_home_dir(): + _main() + +def _main(): + url = httpd.run() + with firefox.Firefox(url) as ff: + with check('[1] untrusted connection'): + main_window_id = ff.get_window_id('Untrusted Connection') + with check('[1] open "add security exception" dialog'): + ff.talk(main_window_id, + 'ctrl+f', + '', + 'shift+Tab', 'space', 'ctrl+f', + '', + 'shift+Tab', 'space', + ) + with check('[1] add security exception'): + ff.talk('Add Security Exception', + 'alt+c', + ) + with check('[1] page loaded'): + httpd.page_event.wait() + with check('[1] quit Firefox'): + ff.talk(main_window_id, 'ctrl+q') + with check('[1] wait for Firefox termination'): + ff.wait() + httpd.page_event.clear() + with firefox.Firefox(url) as ff: + def alarm_on_page_event(): + httpd.page_event.wait() + os.kill(os.getpid(), signal.SIGALRM) + thread = threading.Thread(target=alarm_on_page_event, daemon=True) + thread.start() + with check('[2] untrusted connection'): + try: + ff.talk('Untrusted Connection', 'ctrl+q') + except Timeout: + if httpd.page_event.is_set(): + raise RuntimeError('trusted connection') + raise + with check('[2] wait for Firefox termination'): + ff.wait() + +if __name__ == '__main__': + main() + +# vim:ts=4 sts=4 sw=4 et From 701f8af3e716ec912b8bd3f5eba6eddb0367d57d Mon Sep 17 00:00:00 2001 From: Jakub Wilk Date: Sun, 31 Jan 2016 17:08:35 +0100 Subject: [PATCH 2/6] test: Fix support for Firefox 44 The certificate error UI was modernized in Firefox 44: https://bugzilla.mozilla.org/show_bug.cgi?id=1207107 --- test/firefox.py | 5 +++++ test/run | 17 +++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/test/firefox.py b/test/firefox.py index 9a8fa7a..fe5f356 100644 --- a/test/firefox.py +++ b/test/firefox.py @@ -20,6 +20,7 @@ import contextlib import os +import re import shutil import subprocess as ipc import tempfile @@ -30,6 +31,10 @@ class Firefox(object): def __init__(self, url): self.url = url self.child = None + version = ipc.check_output(['firefox', '--version']) + version = version.decode('ASCII') + version = re.search(r'(\d+)', version).group(0) + self.version = int(version) def __enter__(self): with open(os.devnull, 'wb') as dev_null: diff --git a/test/run b/test/run index f4b43bb..8de3d51 100755 --- a/test/run +++ b/test/run @@ -63,14 +63,19 @@ def main(): def _main(): url = httpd.run() with firefox.Firefox(url) as ff: + if ff.version >= 44: + untrusted_connection_title = 'Insecure Connection' + advanced_options_button = 'Advanced' + else: + untrusted_connection_title = 'Untrusted Connection' + advanced_options_button = 'I Understand The Risks' with check('[1] untrusted connection'): - main_window_id = ff.get_window_id('Untrusted Connection') + main_window_id = ff.get_window_id(untrusted_connection_title) with check('[1] open "add security exception" dialog'): ff.talk(main_window_id, - 'ctrl+f', - '', - 'shift+Tab', 'space', 'ctrl+f', - '', + 'ctrl+f', '<{0}>'.format(advanced_options_button.lower()), + 'shift+Tab', 'space', + 'ctrl+f', '', 'shift+Tab', 'space', ) with check('[1] add security exception'): @@ -92,7 +97,7 @@ def _main(): thread.start() with check('[2] untrusted connection'): try: - ff.talk('Untrusted Connection', 'ctrl+q') + ff.talk(untrusted_connection_title, 'ctrl+q') except Timeout: if httpd.page_event.is_set(): raise RuntimeError('trusted connection') From 868ffc28ba57e684bc5cfbd7edebce06d845d875 Mon Sep 17 00:00:00 2001 From: Jakub Wilk Date: Sun, 31 Jan 2016 20:56:04 +0100 Subject: [PATCH 3/6] test: Update copyright years --- test/firefox.py | 2 +- test/run | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/firefox.py b/test/firefox.py index fe5f356..9133388 100644 --- a/test/firefox.py +++ b/test/firefox.py @@ -1,4 +1,4 @@ -# Copyright © 2014 Jakub Wilk +# Copyright © 2014-2016 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal diff --git a/test/run b/test/run index 8de3d51..2af7938 100755 --- a/test/run +++ b/test/run @@ -1,6 +1,6 @@ #!/usr/bin/python3 -# Copyright © 2014 Jakub Wilk +# Copyright © 2014-2016 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal From 0c224ceeac4613c64fb74d9dccb8bdedd080f4a2 Mon Sep 17 00:00:00 2001 From: Jakub Wilk Date: Sun, 31 Jan 2016 20:56:20 +0100 Subject: [PATCH 4/6] test: Send keys only to the active window Instead of sending keys directly to a specific window, activate the window, then send the keys to the active window. This is work-around for an xdotool bug[0], which broke sending synthetic keys, such as Ctrl+F, to a specific window. The downside of this approach is that the user could break the test if they switched to another window while the test is running. This is not a big deal, because one should run automated tests in an isolated environment (for example, under Xvfb) anyway. This commit should be reverted once the xdotool bug is fixed. [0] https://github.com/jordansissel/xdotool/issues/52 --- test/firefox.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/firefox.py b/test/firefox.py index 9133388..bbd80e0 100644 --- a/test/firefox.py +++ b/test/firefox.py @@ -65,11 +65,11 @@ def get_window_id(self, window_name): output = ipc.check_output(cmdline) return int(output) - def talk(self, window, *args): + def activate_window(self, window): cmdline = ['xdotool'] if isinstance(window, int): cmdline += [ - 'key', '--window', str(window) + 'windowactivate', '--sync', str(window) ] else: cmdline += [ @@ -79,8 +79,13 @@ def talk(self, window, *args): '--all', '--pid', str(self.child.pid), '--name', window, - 'key', + 'windowactivate', '--sync', ] + ipc.check_call(cmdline) + + def talk(self, window, *args): + self.activate_window(window) + cmdline = ['xdotool', 'key'] for arg in args: if arg[:1] + arg[-1:] == '<>': for ch in arg[1:-1]: From ca7b79ee141f28f38b4cb8a8afe30f66dc97a782 Mon Sep 17 00:00:00 2001 From: Jakub Wilk Date: Wed, 22 Jun 2016 13:01:38 +0200 Subject: [PATCH 5/6] test: Fix selecting buttons Use Escape followed by Shift+Tab to select buttons after searching for their label. Shift+Tab alone, which is was what used previously, happened to work only by accident, and broke in Firefox 46. Thanks to Sean Whitton for the bug report. --- test/run | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/run b/test/run index 2af7938..9fa61b1 100755 --- a/test/run +++ b/test/run @@ -74,9 +74,9 @@ def _main(): with check('[1] open "add security exception" dialog'): ff.talk(main_window_id, 'ctrl+f', '<{0}>'.format(advanced_options_button.lower()), - 'shift+Tab', 'space', + 'Escape', 'shift+Tab', 'space', 'ctrl+f', '', - 'shift+Tab', 'space', + 'Escape', 'shift+Tab', 'space', ) with check('[1] add security exception'): ff.talk('Add Security Exception', From d43c78ce5cb1b061f844fca4c51aa5d13d806c07 Mon Sep 17 00:00:00 2001 From: Jakub Wilk Date: Thu, 4 Aug 2016 12:26:24 +0200 Subject: [PATCH 6/6] test: Enable extension in prefs.js The previous approach of enabling the extension in extensions.ini was unnecessarily complicated and stopped working with Firefox 48. --- test/firefox.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/test/firefox.py b/test/firefox.py index bbd80e0..cf3a3b9 100644 --- a/test/firefox.py +++ b/test/firefox.py @@ -19,6 +19,7 @@ # SOFTWARE. import contextlib +import datetime import os import re import shutil @@ -118,21 +119,14 @@ def get_addon_id(): id_elem = rdf_tree.find('.//em:id', namespaces=xml_namespaces) return id_elem.text -extensions_ini = '''\ -[ExtensionDirs] -Extension0=/usr/share/mozilla/extensions/{firefox_id}/{addon_id} -'''.format( - firefox_id='{ec8030f7-c20a-464f-9b0e-13a3a9e97384}', - addon_id=get_addon_id() -) - prefs_js = ''' user_pref("network.proxy.http", "127.0.0.1"); user_pref("network.proxy.http_port", 9); user_pref("network.proxy.ssl", "127.0.0.1"); user_pref("network.proxy.ssl_port", 9); user_pref("network.proxy.type", 1); -''' +user_pref("extensions.enabledItems", "{addon_id}:{today}"); +'''.format(addon_id=get_addon_id(), today=datetime.date.today()) @contextlib.contextmanager def clean_home_dir(): @@ -142,8 +136,6 @@ def clean_home_dir(): os.makedirs(mozilla_home + '/default') with open(mozilla_home + '/profiles.ini', 'wt', encoding='ASCII') as file: file.write(profiles_ini) - with open(mozilla_home + '/default/extensions.ini', 'wt', encoding='ASCII') as file: - file.write(extensions_ini) with open(mozilla_home + '/default/prefs.js', 'wt', encoding='ASCII') as file: file.write(prefs_js) old_home = os.environ['HOME']