# SPDX-License-Identifier: CC0-1.0 """ Haketilo unit tests - serving indexeddb resource script files to content scripts """ # This file is part of Haketilo # # Copyright (C) 2021,2022 Wojtek Kosior <koszko@koszko.org> # # This program is free software: you can redistribute it and/or modify # it under the terms of the CC0 1.0 Universal License as published by # the Creative Commons Corporation. # # 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 # CC0 1.0 Universal License for more details. import pytest import copy from uuid import uuid4 from selenium.webdriver.support.ui import WebDriverWait from ..script_loader import load_script from .utils import * """ How many test resources we're going to have. """ count = 15 sample_files_list = [(f'file_{n}_{i}', f'contents {n} {i}') for n in range(count) for i in range(2)] sample_files = dict(sample_files_list) sample_files, sample_files_by_sha256 = make_sample_files(sample_files) def make_sample_resource_with_deps(n): resource = make_sample_resource(with_files=False) resource['identifier'] = f'res-{n}' resource['dependencies'] = [{'identifier': f'res-{m}'} for m in range(max(n - 4, 0), n)] resource['scripts'] = [sample_file_ref(f'file_{n}_{i}', sample_files) for i in range(2)] return resource resources = [make_sample_resource_with_deps(n) for n in range(count)] sample_data = { 'resource': sample_data_dict(resources), 'mapping': {}, 'file': { 'sha256': sample_files_by_sha256 } } def prepare_test_page(initial_indexeddb_data, execute_in_page): js = load_script('background/indexeddb_files_server.js', code_to_add='#IMPORT common/broadcast.js') execute_in_page(js) mock_broadcast(execute_in_page) clear_indexeddb(execute_in_page) execute_in_page( ''' let registered_listener; const new_addListener = cb => registered_listener = cb; browser = {runtime: {onMessage: {addListener: new_addListener}}}; haketilodb.save_items(arguments[0]); start(); ''', initial_indexeddb_data) @pytest.mark.get_page('https://gotmyowndoma.in') def test_indexeddb_files_server_normal_usage(driver, execute_in_page): """ Test querying resource files (with resource dependency resolution) from IndexedDB and serving them in messages to content scripts. """ prepare_test_page(sample_data, execute_in_page) # Verify other types of messages are ignored. function_returned_value = execute_in_page( ''' returnval(registered_listener(["???"], {}, () => location.reload())); ''') assert function_returned_value == None # Verify single resource's files get properly resolved. function_returned_value = execute_in_page( ''' var result_cb, contents_prom = new Promise(cb => result_cb = cb); returnval(registered_listener(["indexeddb_files", "res-0"], {}, result_cb)); ''') assert function_returned_value == True assert execute_in_page('returnval(contents_prom);') == \ {'files': [tuple[1] for tuple in sample_files_list[0:2]]} # Verify multiple resources' files get properly resolved. function_returned_value = execute_in_page( ''' var result_cb, contents_prom = new Promise(cb => result_cb = cb); returnval(registered_listener(["indexeddb_files", arguments[0]], {}, result_cb)); ''', f'res-{count - 1}') assert function_returned_value == True assert execute_in_page('returnval(contents_prom);') == \ {'files': [tuple[1] for tuple in sample_files_list]} @pytest.mark.get_page('https://gotmyowndoma.in') @pytest.mark.parametrize('error', [ 'missing', 'circular', 'db', 'other' ]) def test_indexeddb_files_server_errors(driver, execute_in_page, error): """ Test reporting of errors when querying resource files (with resource dependency resolution) from IndexedDB and serving them in messages to content scripts. """ sample_data_copy = copy.deepcopy(sample_data) if error == 'missing': del sample_data_copy['resource']['res-3'] elif error == 'circular': res3_defs = sample_data_copy['resource']['res-3'].values() next(iter(res3_defs))['dependencies'].append({'identifier': 'res-8'}) prepare_test_page(sample_data_copy, execute_in_page) if error == 'db': execute_in_page('haketilodb.idb_get = t => t.onerror("oooops");') elif error == 'other': execute_in_page('haketilodb.idb_get = () => {throw "oooops"};') response = execute_in_page( ''' var result_cb, contents_prom = new Promise(cb => result_cb = cb); registered_listener(["indexeddb_files", arguments[0]], {}, result_cb); returnval(contents_prom); ''', f'res-{count - 1}') assert response['error']['haketilo_error_type'] == error if error == 'missing': assert response['error']['id'] == 'res-3' elif error == 'circular': assert response['error']['id'] in ('res-3', 'res-8') elif error not in ('db', 'other'): raise Exception('made a typo in test function params?')