From 8ac316242524ff408e240e26f818fd579b61519e Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 12:14:55 +0200 Subject: [PATCH 01/36] Call modeman.init() in mode_manager fixture --- tests/helpers/fixtures.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index 5ce081e3b..9ee2d2bd5 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -455,9 +455,8 @@ def fake_args(request): @pytest.fixture -def mode_manager(win_registry, config_stub, qapp): - mm = modeman.ModeManager(0) - objreg.register('mode-manager', mm, scope='window', window=0) +def mode_manager(win_registry, config_stub, key_config_stub, qapp): + mm = modeman.init(0, parent=qapp) yield mm objreg.delete('mode-manager', scope='window', window=0) From a58abf8f4c5614983d96f9effc160b1937276d8b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 12:15:14 +0200 Subject: [PATCH 02/36] Set __tracebackhide__ for CallbackChecker.check --- tests/helpers/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/helpers/utils.py b/tests/helpers/utils.py index 06cf54467..66b3f8133 100644 --- a/tests/helpers/utils.py +++ b/tests/helpers/utils.py @@ -203,6 +203,7 @@ class CallbackChecker(QObject): def check(self, expected): """Wait until the JS result arrived and compare it.""" + __tracebackhide__ = True if self._result is self.UNSET: with self._qtbot.waitSignal(self.got_result, timeout=2000): pass From 64e321d47c985bf1e51e09a99352704cbe00fdb9 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 12:15:32 +0200 Subject: [PATCH 03/36] Rewrite most caret tests as unit tests --- tests/end2end/features/caret.feature | 252 --------------------- tests/unit/browser/test_caret.py | 317 +++++++++++++++++++++++++++ 2 files changed, 317 insertions(+), 252 deletions(-) create mode 100644 tests/unit/browser/test_caret.py diff --git a/tests/end2end/features/caret.feature b/tests/end2end/features/caret.feature index 6d2245a89..422919cea 100644 --- a/tests/end2end/features/caret.feature +++ b/tests/end2end/features/caret.feature @@ -7,224 +7,6 @@ Feature: Caret mode Given I open data/caret.html And I run :tab-only ;; enter-mode caret - # document - - Scenario: Selecting the entire document - When I run :toggle-selection - And I run :move-to-end-of-document - And I run :yank selection - Then the clipboard should contain: - one two three - eins zwei drei - - four five six - vier fünf sechs - - Scenario: Moving to end and to start of document - When I run :move-to-end-of-document - And I run :move-to-start-of-document - And I run :toggle-selection - And I run :move-to-end-of-word - And I run :yank selection - Then the clipboard should contain "one" - - Scenario: Moving to end and to start of document (with selection) - When I run :move-to-end-of-document - And I run :toggle-selection - And I run :move-to-start-of-document - And I run :yank selection - Then the clipboard should contain: - one two three - eins zwei drei - - four five six - vier fünf sechs - - # block - - Scenario: Selecting a block - When I run :toggle-selection - And I run :move-to-end-of-next-block - And I run :yank selection - Then the clipboard should contain: - one two three - eins zwei drei - - Scenario: Moving back to the end of previous block (with selection) - When I run :move-to-end-of-next-block with count 2 - And I run :toggle-selection - And I run :move-to-end-of-prev-block - And I run :move-to-prev-word - And I run :yank selection - Then the clipboard should contain: - drei - - four five six - - Scenario: Moving back to the end of previous block - When I run :move-to-end-of-next-block with count 2 - And I run :move-to-end-of-prev-block - And I run :toggle-selection - And I run :move-to-prev-word - And I run :yank selection - Then the clipboard should contain "drei" - - Scenario: Moving back to the start of previous block (with selection) - When I run :move-to-end-of-next-block with count 2 - And I run :toggle-selection - And I run :move-to-start-of-prev-block - And I run :yank selection - Then the clipboard should contain: - eins zwei drei - - four five six - - Scenario: Moving back to the start of previous block - When I run :move-to-end-of-next-block with count 2 - And I run :move-to-start-of-prev-block - And I run :toggle-selection - And I run :move-to-next-word - And I run :yank selection - Then the clipboard should contain "eins " - - Scenario: Moving to the start of next block (with selection) - When I run :toggle-selection - And I run :move-to-start-of-next-block - And I run :yank selection - Then the clipboard should contain "one two three\n" - - Scenario: Moving to the start of next block - When I run :move-to-start-of-next-block - And I run :toggle-selection - And I run :move-to-end-of-word - And I run :yank selection - Then the clipboard should contain "eins" - - # line - - Scenario: Selecting a line - When I run :toggle-selection - And I run :move-to-end-of-line - And I run :yank selection - Then the clipboard should contain "one two three" - - Scenario: Moving and selecting a line - When I run :move-to-next-line - And I run :toggle-selection - And I run :move-to-end-of-line - And I run :yank selection - Then the clipboard should contain "eins zwei drei" - - Scenario: Selecting next line - When I run :toggle-selection - And I run :move-to-next-line - And I run :yank selection - Then the clipboard should contain "one two three\n" - - Scenario: Moving to end and to start of line - When I run :move-to-end-of-line - And I run :move-to-start-of-line - And I run :toggle-selection - And I run :move-to-end-of-word - And I run :yank selection - Then the clipboard should contain "one" - - Scenario: Selecting a line (backwards) - When I run :move-to-end-of-line - And I run :toggle-selection - When I run :move-to-start-of-line - And I run :yank selection - Then the clipboard should contain "one two three" - - Scenario: Selecting previous line - When I run :move-to-next-line - And I run :toggle-selection - When I run :move-to-prev-line - And I run :yank selection - Then the clipboard should contain "one two three\n" - - Scenario: Moving to previous line - When I run :move-to-next-line - When I run :move-to-prev-line - And I run :toggle-selection - When I run :move-to-next-line - And I run :yank selection - Then the clipboard should contain "one two three\n" - - # word - - Scenario: Selecting a word - When I run :toggle-selection - And I run :move-to-end-of-word - And I run :yank selection - Then the clipboard should contain "one" - - Scenario: Moving to end and selecting a word - When I run :move-to-end-of-word - And I run :toggle-selection - And I run :move-to-end-of-word - And I run :yank selection - Then the clipboard should contain " two" - - Scenario: Moving to next word and selecting a word - When I run :move-to-next-word - And I run :toggle-selection - And I run :move-to-end-of-word - And I run :yank selection - Then the clipboard should contain "two" - - Scenario: Moving to next word and selecting until next word - When I run :move-to-next-word - And I run :toggle-selection - And I run :move-to-next-word - And I run :yank selection - Then the clipboard should contain "two " - - Scenario: Moving to previous word and selecting a word - When I run :move-to-end-of-word - And I run :toggle-selection - And I run :move-to-prev-word - And I run :yank selection - Then the clipboard should contain "one" - - Scenario: Moving to previous word - When I run :move-to-end-of-word - And I run :move-to-prev-word - And I run :toggle-selection - And I run :move-to-end-of-word - And I run :yank selection - Then the clipboard should contain "one" - - # char - - Scenario: Selecting a char - When I run :toggle-selection - And I run :move-to-next-char - And I run :yank selection - Then the clipboard should contain "o" - - Scenario: Moving and selecting a char - When I run :move-to-next-char - And I run :toggle-selection - And I run :move-to-next-char - And I run :yank selection - Then the clipboard should contain "n" - - Scenario: Selecting previous char - When I run :move-to-end-of-word - And I run :toggle-selection - And I run :move-to-prev-char - And I run :yank selection - Then the clipboard should contain "e" - - Scenario: Moving to previous char - When I run :move-to-end-of-word - And I run :move-to-prev-char - And I run :toggle-selection - And I run :move-to-end-of-word - And I run :yank selection - Then the clipboard should contain "e" - # :yank selection Scenario: :yank selection without selection @@ -261,15 +43,6 @@ Feature: Caret mode And the message "7 chars yanked to clipboard" should be shown. And the clipboard should contain "one two" - # :drop-selection - - Scenario: :drop-selection - When I run :toggle-selection - And I run :move-to-end-of-word - And I run :drop-selection - And I run :yank selection - Then the message "Nothing to yank" should be shown. - # :follow-selected Scenario: :follow-selected without a selection @@ -356,28 +129,3 @@ Feature: Caret mode And I run :fake-key And I run :follow-selected --tab Then data/hello.txt should be loaded - - # Search + caret mode - - # https://bugreports.qt.io/browse/QTBUG-60673 - @qtbug60673 - Scenario: yanking a searched line - When I run :leave-mode - And I run :search fiv - And I wait for "search found fiv" in the log - And I run :enter-mode caret - And I run :move-to-end-of-line - And I run :yank selection - Then the clipboard should contain "five six" - - @qtbug60673 - Scenario: yanking a searched line with multiple matches - When I run :leave-mode - And I run :search w - And I wait for "search found w" in the log - And I run :search-next - And I wait for "next_result found w" in the log - And I run :enter-mode caret - And I run :move-to-end-of-line - And I run :yank selection - Then the clipboard should contain "wei drei" diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py new file mode 100644 index 000000000..199baca25 --- /dev/null +++ b/tests/unit/browser/test_caret.py @@ -0,0 +1,317 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2018 Florian Bruhin (The Compiler) +# +# This file is part of qutebrowser. +# +# qutebrowser 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. +# +# qutebrowser 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 qutebrowser. If not, see . + +"""Tests for caret browsing mode.""" + +import os.path +import textwrap + +import pytest +from PyQt5.QtCore import QUrl + +from qutebrowser.utils import usertypes +from helpers import utils + + +@pytest.fixture +def caret(web_tab, qtbot, mode_manager): + path = os.path.join(utils.abs_datapath(), 'caret.html') + with qtbot.wait_signal(web_tab.load_finished): + web_tab.openurl(QUrl.fromLocalFile(path)) + + mode_manager.enter(usertypes.KeyMode.caret) + + return web_tab.caret + + +class Selection: + + """Helper to interact with the caret selection.""" + + def __init__(self, qtbot, caret): + self._qtbot = qtbot + self._caret = caret + self._callback_checker = utils.CallbackChecker(qtbot) + + def check(self, expected): + self._caret.selection(self._callback_checker.callback) + self._callback_checker.check(expected) + + def check_multiline(self, expected): + self.check(textwrap.dedent(expected).strip()) + + def toggle(self): + with self._qtbot.wait_signal(self._caret.selection_toggled): + self._caret.toggle_selection() + + +@pytest.fixture +def selection(qtbot, caret, callback_checker): + return Selection(qtbot, caret) + + +class TestDocument: + + def test_selecting_entire_document(self, caret, selection): + selection.toggle() + caret.move_to_end_of_document() + selection.check_multiline(""" + one two three + eins zwei drei + + four five six + vier fünf sechs + """) + + def test_moving_to_end_and_start(self, caret, selection): + caret.move_to_end_of_document() + caret.move_to_start_of_document() + selection.toggle() + caret.move_to_end_of_word() + selection.check("one") + + def test_moving_to_end_and_start_with_selection(self, caret, selection): + caret.move_to_end_of_document() + selection.toggle() + caret.move_to_start_of_document() + selection.check_multiline(""" + one two three + eins zwei drei + + four five six + vier fünf sechs + """) + + +class TestBlock: + + def test_selecting_block(self, caret, selection): + selection.toggle() + caret.move_to_end_of_next_block() + selection.check_multiline(""" + one two three + eins zwei drei + """) + + def test_selecting_a_block(self, caret, selection): + selection.toggle() + caret.move_to_end_of_next_block() + selection.check_multiline(""" + one two three + eins zwei drei + """) + + def test_moving_back_to_the_end_of_prev_block_with_sel(self, caret, selection): + caret.move_to_end_of_next_block(2) + selection.toggle() + caret.move_to_end_of_prev_block() + caret.move_to_prev_word() + selection.check_multiline(""" + drei + + four five six + """) + + def test_moving_back_to_the_end_of_prev_block(self, caret, selection): + caret.move_to_end_of_next_block(2) + caret.move_to_end_of_prev_block() + selection.toggle() + caret.move_to_prev_word() + selection.check("drei") + + def test_moving_back_to_the_start_of_prev_block_with_sel(self, + caret, selection): + caret.move_to_end_of_next_block(2) + selection.toggle() + caret.move_to_start_of_prev_block() + selection.check_multiline(""" + eins zwei drei + + four five six + """) + + def test_moving_back_to_the_start_of_prev_block(self, caret, selection): + caret.move_to_end_of_next_block(2) + caret.move_to_start_of_prev_block() + selection.toggle() + caret.move_to_next_word() + selection.check("eins ") + + def test_moving_to_the_start_of_next_block_with_sel(self, + caret, selection): + selection.toggle() + caret.move_to_start_of_next_block() + selection.check("one two three\n") + + def test_moving_to_the_start_of_next_block(self, caret, selection): + caret.move_to_start_of_next_block() + selection.toggle() + caret.move_to_end_of_word() + selection.check("eins") + + +class TestLine: + + def test_selecting_a_line(self, caret, selection): + selection.toggle() + caret.move_to_end_of_line() + selection.check("one two three") + + def test_moving_and_selecting_a_line(self, caret, selection): + caret.move_to_next_line() + selection.toggle() + caret.move_to_end_of_line() + selection.check("eins zwei drei") + + def test_selecting_next_line(self, caret, selection): + selection.toggle() + caret.move_to_next_line() + selection.check("one two three\n") + + def test_moving_to_end_and_to_start_of_line(self, caret, selection): + caret.move_to_end_of_line() + caret.move_to_start_of_line() + selection.toggle() + caret.move_to_end_of_word() + selection.check("one") + + def test_selecting_a_line_backwards(self, caret, selection): + caret.move_to_end_of_line() + selection.toggle() + caret.move_to_start_of_line() + selection.check("one two three") + + def test_selecting_previous_line(self, caret, selection): + caret.move_to_next_line() + selection.toggle() + caret.move_to_prev_line() + selection.check("one two three\n") + + def test_moving_to_previous_line(self, caret, selection): + caret.move_to_next_line() + caret.move_to_prev_line() + selection.toggle() + caret.move_to_next_line() + selection.check("one two three\n") + + +class TestWord: + + def test_selecting_a_word(self, caret, selection): + selection.toggle() + caret.move_to_end_of_word() + selection.check("one") + + def test_moving_to_end_and_selecting_a_word(self, caret, selection): + caret.move_to_end_of_word() + selection.toggle() + caret.move_to_end_of_word() + selection.check(" two") + + def test_moving_to_next_word_and_selecting_a_word(self, caret, selection): + caret.move_to_next_word() + selection.toggle() + caret.move_to_end_of_word() + selection.check("two") + + def test_moving_to_next_word_and_selecting_until_next_word(self, caret, selection): + caret.move_to_next_word() + selection.toggle() + caret.move_to_next_word() + selection.check("two ") + + def test_moving_to_previous_word_and_selecting_a_word(self, caret, selection): + caret.move_to_end_of_word() + selection.toggle() + caret.move_to_prev_word() + selection.check("one") + + def test_moving_to_previous_word(self, caret, selection): + caret.move_to_end_of_word() + caret.move_to_prev_word() + selection.toggle() + caret.move_to_end_of_word() + selection.check("one") + + +class TestChar: + + def test_selecting_a_char(self, caret, selection): + selection.toggle() + caret.move_to_next_char() + selection.check("o") + + def test_moving_and_selecting_a_char(self, caret, selection): + caret.move_to_next_char() + selection.toggle() + caret.move_to_next_char() + selection.check("n") + + def test_selecting_previous_char(self, caret, selection): + caret.move_to_end_of_word() + selection.toggle() + caret.move_to_prev_char() + selection.check("e") + + def test_moving_to_previous_char(self, caret, selection): + caret.move_to_end_of_word() + caret.move_to_prev_char() + selection.toggle() + caret.move_to_end_of_word() + selection.check("e") + + +def test_drop_selection(caret, selection): + selection.toggle() + caret.move_to_end_of_word() + caret.drop_selection() + selection.check("") + + +class TestSearch: + + # https://bugreports.qt.io/browse/QTBUG-60673 + + @pytest.mark.qtbug60673 + def test_yanking_a_searched_line( + self, caret, selection, mode_manager, callback_checker, web_tab): + mode_manager.leave(usertypes.KeyMode.caret) + + web_tab.search.search('fiv', result_cb=callback_checker.callback) + callback_checker.check(True) + + mode_manager.enter(usertypes.KeyMode.caret) + caret.move_to_end_of_line() + selection.check('five six') + + @pytest.mark.qtbug60673 + def test_yanking_a_searched_line_with_multiple_matches( + self, caret, selection, mode_manager, callback_checker, web_tab): + mode_manager.leave(usertypes.KeyMode.caret) + + web_tab.search.search('w', result_cb=callback_checker.callback) + callback_checker.check(True) + + web_tab.search.next_result(result_cb=callback_checker.callback) + callback_checker.check(True) + + mode_manager.enter(usertypes.KeyMode.caret) + + caret.move_to_end_of_line() + selection.check('wei drei') From db6935b42e6ddfb51a08c98b166b6fd09d23a396 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 12:26:00 +0200 Subject: [PATCH 04/36] Fix TestSearch caret tests For some reason, they need the window to be shown on a screen to work... --- tests/unit/browser/test_caret.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index 199baca25..4437d6bc3 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -289,8 +289,10 @@ class TestSearch: # https://bugreports.qt.io/browse/QTBUG-60673 @pytest.mark.qtbug60673 + @pytest.mark.no_xvfb def test_yanking_a_searched_line( - self, caret, selection, mode_manager, callback_checker, web_tab): + self, caret, selection, mode_manager, callback_checker, web_tab, qtbot): + web_tab.show() mode_manager.leave(usertypes.KeyMode.caret) web_tab.search.search('fiv', result_cb=callback_checker.callback) @@ -301,8 +303,10 @@ class TestSearch: selection.check('five six') @pytest.mark.qtbug60673 + @pytest.mark.no_xvfb def test_yanking_a_searched_line_with_multiple_matches( - self, caret, selection, mode_manager, callback_checker, web_tab): + self, caret, selection, mode_manager, callback_checker, web_tab, qtbot): + web_tab.show() mode_manager.leave(usertypes.KeyMode.caret) web_tab.search.search('w', result_cb=callback_checker.callback) From 03dea493de6534dfe378f7bf8fba0a7bb8b83faf Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 13:18:09 +0200 Subject: [PATCH 05/36] Make sure we only update the caret selection once Otherwise, stuff goes haywire when trying to use the caret mode very fast (like in a unit test), because new stuff runs before we've managed to update the selection. --- qutebrowser/browser/webengine/webenginetab.py | 42 +++--- qutebrowser/javascript/caret.js | 123 ++++++++++-------- 2 files changed, 84 insertions(+), 81 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index cfb809097..a5367de82 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -248,7 +248,7 @@ class WebEngineCaret(browsertab.AbstractCaret): self._tab.run_js_async( javascript.assemble('caret', 'setPlatform', sys.platform, qVersion())) - self._js_call('setInitialCursor', self._selection_cb) + self._js_call('setInitialCursor', callback=self._selection_cb) def _selection_cb(self, enabled): """Emit selection_toggled based on setInitialCursor.""" @@ -266,32 +266,25 @@ class WebEngineCaret(browsertab.AbstractCaret): self._js_call('disableCaret') def move_to_next_line(self, count=1): - for _ in range(count): - self._js_call('moveDown') + self._js_call('moveDown', count) def move_to_prev_line(self, count=1): - for _ in range(count): - self._js_call('moveUp') + self._js_call('moveUp', count) def move_to_next_char(self, count=1): - for _ in range(count): - self._js_call('moveRight') + self._js_call('moveRight', count) def move_to_prev_char(self, count=1): - for _ in range(count): - self._js_call('moveLeft') + self._js_call('moveLeft', count) def move_to_end_of_word(self, count=1): - for _ in range(count): - self._js_call('moveToEndOfWord') + self._js_call('moveToEndOfWord', count) def move_to_next_word(self, count=1): - for _ in range(count): - self._js_call('moveToNextWord') + self._js_call('moveToNextWord', count) def move_to_prev_word(self, count=1): - for _ in range(count): - self._js_call('moveToPreviousWord') + self._js_call('moveToPreviousWord', count) def move_to_start_of_line(self): self._js_call('moveToStartOfLine') @@ -300,20 +293,16 @@ class WebEngineCaret(browsertab.AbstractCaret): self._js_call('moveToEndOfLine') def move_to_start_of_next_block(self, count=1): - for _ in range(count): - self._js_call('moveToStartOfNextBlock') + self._js_call('moveToStartOfNextBlock', count) def move_to_start_of_prev_block(self, count=1): - for _ in range(count): - self._js_call('moveToStartOfPrevBlock') + self._js_call('moveToStartOfPrevBlock', count) def move_to_end_of_next_block(self, count=1): - for _ in range(count): - self._js_call('moveToEndOfNextBlock') + self._js_call('moveToEndOfNextBlock', count) def move_to_end_of_prev_block(self, count=1): - for _ in range(count): - self._js_call('moveToEndOfPrevBlock') + self._js_call('moveToEndOfPrevBlock', count) def move_to_start_of_document(self): self._js_call('moveToStartOfDocument') @@ -322,7 +311,7 @@ class WebEngineCaret(browsertab.AbstractCaret): self._js_call('moveToEndOfDocument') def toggle_selection(self): - self._js_call('toggleSelection', self.selection_toggled.emit) + self._js_call('toggleSelection', callback=self.selection_toggled.emit) def drop_selection(self): self._js_call('dropSelection') @@ -383,8 +372,9 @@ class WebEngineCaret(browsertab.AbstractCaret): self._tab.run_js_async(js_code, lambda jsret: self._follow_selected_cb(jsret, tab)) - def _js_call(self, command, callback=None): - self._tab.run_js_async(javascript.assemble('caret', command), callback) + def _js_call(self, command, *args, callback=None): + code = javascript.assemble('caret', command, *args) + self._tab.run_js_async(code, callback) class WebEngineScroller(browsertab.AbstractScroller): diff --git a/qutebrowser/javascript/caret.js b/qutebrowser/javascript/caret.js index 3f858f3fb..d82d7e856 100644 --- a/qutebrowser/javascript/caret.js +++ b/qutebrowser/javascript/caret.js @@ -1163,47 +1163,47 @@ window._qutebrowser.caret = (function() { } }; - CaretBrowsing.move = function(direction, granularity) { + CaretBrowsing.move = function(direction, granularity, count = 1) { let action = "move"; if (CaretBrowsing.selectionEnabled) { action = "extend"; } - window. - getSelection(). - modify(action, direction, granularity); + + for (let i = 0; i < count; i++) { + window. + getSelection(). + modify(action, direction, granularity); + } if (CaretBrowsing.isWindows && (direction === "forward" || direction === "right") && granularity === "word") { CaretBrowsing.move("left", "character"); - } else { - window.setTimeout(() => { - CaretBrowsing.updateCaretOrSelection(true); - }, 0); } + }; + CaretBrowsing.finishMove = function() { + window.setTimeout(() => { + CaretBrowsing.updateCaretOrSelection(true); + }, 0); CaretBrowsing.stopAnimation(); }; - CaretBrowsing.moveToBlock = function(paragraph, boundary) { + CaretBrowsing.moveToBlock = function(paragraph, boundary, count = 1) { let action = "move"; if (CaretBrowsing.selectionEnabled) { action = "extend"; } - window. - getSelection(). - modify(action, paragraph, "paragraph"); + for (let i = 0; i < count; i++) { + window. + getSelection(). + modify(action, paragraph, "paragraph"); - window. - getSelection(). - modify(action, boundary, "paragraphboundary"); - - window.setTimeout(() => { - CaretBrowsing.updateCaretOrSelection(true); - }, 0); - - CaretBrowsing.stopAnimation(); + window. + getSelection(). + modify(action, boundary, "paragraphboundary"); + } }; CaretBrowsing.toggle = function(value) { @@ -1331,67 +1331,80 @@ window._qutebrowser.caret = (function() { CaretBrowsing.toggle(); }; - funcs.moveRight = () => { + funcs.moveRight = (count = 1) => { + CaretBrowsing.move("right", "character", count); + CaretBrowsing.finishMove(); + }; + + funcs.moveLeft = (count = 1) => { + CaretBrowsing.move("left", "character", count); + CaretBrowsing.finishMove(); + }; + + funcs.moveDown = (count = 1) => { + CaretBrowsing.move("forward", "line", count); + CaretBrowsing.finishMove(); + }; + + funcs.moveUp = (count = 1) => { + CaretBrowsing.move("backward", "line", count); + CaretBrowsing.finishMove(); + }; + + funcs.moveToEndOfWord = (count = 1) => { + CaretBrowsing.move("forward", "word", count); + CaretBrowsing.finishMove(); + }; + + funcs.moveToNextWord = (count = 1) => { + CaretBrowsing.move("forward", "word", count); CaretBrowsing.move("right", "character"); + CaretBrowsing.finishMove(); }; - funcs.moveLeft = () => { - CaretBrowsing.move("left", "character"); - }; - - funcs.moveDown = () => { - CaretBrowsing.move("forward", "line"); - }; - - funcs.moveUp = () => { - CaretBrowsing.move("backward", "line"); - }; - - funcs.moveToEndOfWord = () => { - funcs.moveToNextWord(); - funcs.moveLeft(); - }; - - funcs.moveToNextWord = () => { - CaretBrowsing.move("forward", "word"); - funcs.moveRight(); - }; - - funcs.moveToPreviousWord = () => { - CaretBrowsing.move("backward", "word"); + funcs.moveToPreviousWord = (count = 1) => { + CaretBrowsing.move("backward", "word", count); + CaretBrowsing.finishMove(); }; funcs.moveToStartOfLine = () => { CaretBrowsing.move("left", "lineboundary"); + CaretBrowsing.finishMove(); }; funcs.moveToEndOfLine = () => { CaretBrowsing.move("right", "lineboundary"); + CaretBrowsing.finishMove(); }; - funcs.moveToStartOfNextBlock = () => { - CaretBrowsing.moveToBlock("forward", "backward"); + funcs.moveToStartOfNextBlock = (count = 1) => { + CaretBrowsing.moveToBlock("forward", "backward", count); + CaretBrowsing.finishMove(); }; - funcs.moveToStartOfPrevBlock = () => { - CaretBrowsing.moveToBlock("backward", "backward"); + funcs.moveToStartOfPrevBlock = (count = 1) => { + CaretBrowsing.moveToBlock("backward", "backward", count); + CaretBrowsing.finishMove(); }; - funcs.moveToEndOfNextBlock = () => { - CaretBrowsing.moveToBlock("forward", "forward"); + funcs.moveToEndOfNextBlock = (count = 1) => { + CaretBrowsing.moveToBlock("forward", "forward", count); + CaretBrowsing.finishMove(); }; - funcs.moveToEndOfPrevBlock = () => { - CaretBrowsing.moveToBlock("backward", "forward"); + funcs.moveToEndOfPrevBlock = (count = 1) => { + CaretBrowsing.moveToBlock("backward", "forward", count); + CaretBrowsing.finishMove(); }; funcs.moveToStartOfDocument = () => { CaretBrowsing.move("backward", "documentboundary"); + CaretBrowsing.finishMove(); }; funcs.moveToEndOfDocument = () => { CaretBrowsing.move("forward", "documentboundary"); - funcs.moveLeft(); + CaretBrowsing.finishMove(); }; funcs.dropSelection = () => { From 5978605873dfaf4aeb88367f6aaed85669479611 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 13:22:17 +0200 Subject: [PATCH 06/36] Make caret unittests run with QtWebKit --- tests/helpers/fixtures.py | 3 ++- tests/helpers/stubs.py | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index 9ee2d2bd5..1c37301cf 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -154,7 +154,8 @@ def greasemonkey_manager(data_tmpdir): @pytest.fixture def webkit_tab(qtbot, tab_registry, cookiejar_and_cache, mode_manager, - session_manager_stub, greasemonkey_manager, fake_args): + session_manager_stub, greasemonkey_manager, fake_args, + host_blocker_stub): webkittab = pytest.importorskip('qutebrowser.browser.webkit.webkittab') tab = webkittab.WebKitTab(win_id=0, mode_manager=mode_manager, private=False) diff --git a/tests/helpers/stubs.py b/tests/helpers/stubs.py index f10522a02..9af7a6fcc 100644 --- a/tests/helpers/stubs.py +++ b/tests/helpers/stubs.py @@ -475,6 +475,9 @@ class HostBlockerStub: def __init__(self): self.blocked_hosts = set() + def is_blocked(self, url): + return url in self.blocked_hosts + class SessionManagerStub: From 7cb32dca078c3af873536c14dac3fa4d7ef9084f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 13:36:02 +0200 Subject: [PATCH 07/36] Add a container around webkit_tab This makes the selection appear properly in caret tests - otherwise, --no-xvfb would be needed. --- tests/helpers/fixtures.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index 1c37301cf..e8321e1f1 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -157,9 +157,22 @@ def webkit_tab(qtbot, tab_registry, cookiejar_and_cache, mode_manager, session_manager_stub, greasemonkey_manager, fake_args, host_blocker_stub): webkittab = pytest.importorskip('qutebrowser.browser.webkit.webkittab') + + container = QWidget() + qtbot.add_widget(container) + + vbox = QVBoxLayout(container) tab = webkittab.WebKitTab(win_id=0, mode_manager=mode_manager, private=False) - qtbot.add_widget(tab) + vbox.addWidget(tab) + # to make sure container isn't GCed + # pylint: disable=attribute-defined-outside-init + tab.container = container + # pylint: enable=attribute-defined-outside-init + + with qtbot.waitExposed(container): + container.show() + return tab From 9c672398474bc312a2c5aa84ccab617a84c950a7 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 13:59:11 +0200 Subject: [PATCH 08/36] Fix lint --- scripts/dev/run_pylint_on_tests.py | 9 ++++++--- tests/helpers/fixtures.py | 2 -- tests/unit/browser/test_caret.py | 12 ++++-------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/scripts/dev/run_pylint_on_tests.py b/scripts/dev/run_pylint_on_tests.py index 7adf45f76..f7855dfb7 100644 --- a/scripts/dev/run_pylint_on_tests.py +++ b/scripts/dev/run_pylint_on_tests.py @@ -65,9 +65,12 @@ def main(): toxinidir, ] - args = (['--disable={}'.format(','.join(disabled)), - '--ignored-modules=helpers,pytest,PyQt5'] + - sys.argv[2:] + files) + args = [ + '--disable={}'.format(','.join(disabled)), + '--ignored-modules=helpers,pytest,PyQt5', + r'--ignore-long-lines=( Date: Sat, 8 Sep 2018 14:52:57 +0200 Subject: [PATCH 09/36] Also add a container for webengine_tab Seems to be needed with Qt < 5.11 for things to work. --- tests/helpers/fixtures.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index dd36620bb..227a78e2a 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -182,11 +182,18 @@ def webengine_tab(qtbot, tab_registry, fake_args, mode_manager, tabwidget.current_index = 0 tabwidget.index_of = 0 + container = QWidget() + qtbot.add_widget(container) + + vbox = QVBoxLayout(container) webenginetab = pytest.importorskip( 'qutebrowser.browser.webengine.webenginetab') tab = webenginetab.WebEngineTab(win_id=0, mode_manager=mode_manager, private=False) - qtbot.add_widget(tab) + vbox.addWidget(tab) + # to make sure container isn't GCed + tab.container = container + return tab From 8c0366f0a77d824f79096128095e0671e133334a Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 15:34:28 +0200 Subject: [PATCH 10/36] Configure JS logging for test tab objects --- tests/helpers/fixtures.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index 227a78e2a..ea297f02c 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -155,7 +155,14 @@ def greasemonkey_manager(data_tmpdir): @pytest.fixture def webkit_tab(qtbot, tab_registry, cookiejar_and_cache, mode_manager, session_manager_stub, greasemonkey_manager, fake_args, - host_blocker_stub): + host_blocker_stub, config_stub): + config_stub.val.content.javascript.log = { + 'info': 'info', + 'error': 'info', + 'unknown': 'info', + 'warning': 'info' + } + webkittab = pytest.importorskip('qutebrowser.browser.webkit.webkittab') container = QWidget() @@ -177,7 +184,15 @@ def webkit_tab(qtbot, tab_registry, cookiejar_and_cache, mode_manager, @pytest.fixture def webengine_tab(qtbot, tab_registry, fake_args, mode_manager, session_manager_stub, greasemonkey_manager, - redirect_webengine_data, tabbed_browser_stubs): + redirect_webengine_data, tabbed_browser_stubs, + config_stub): + config_stub.val.content.javascript.log = { + 'info': 'info', + 'error': 'info', + 'unknown': 'info', + 'warning': 'info' + } + tabwidget = tabbed_browser_stubs[0].widget tabwidget.current_index = 0 tabwidget.index_of = 0 From 45eece372f27ae5262db54b564d2eaab745fe529 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 15:51:42 +0200 Subject: [PATCH 11/36] Don't access caretElement if it's gone --- qutebrowser/javascript/caret.js | 14 +++++++++----- tests/helpers/fixtures.py | 16 +++++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/qutebrowser/javascript/caret.js b/qutebrowser/javascript/caret.js index d82d7e856..ecb4f97c8 100644 --- a/qutebrowser/javascript/caret.js +++ b/qutebrowser/javascript/caret.js @@ -1271,14 +1271,18 @@ window._qutebrowser.caret = (function() { }; CaretBrowsing.startAnimation = function() { - CaretBrowsing.caretElement.style.animationIterationCount = "infinite"; + if (CaretBrowsing.caretElement) { + CaretBrowsing.caretElement.style.animationIterationCount = "infinite"; + } }; CaretBrowsing.stopAnimation = function() { - CaretBrowsing.caretElement.style.animationIterationCount = 0; - window.setTimeout(() => { - CaretBrowsing.startAnimation(); - }, 1000); + if (CaretBrowsing.caretElement) { + CaretBrowsing.caretElement.style.animationIterationCount = 0; + window.setTimeout(() => { + CaretBrowsing.startAnimation(); + }, 1000); + } }; CaretBrowsing.init = function() { diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index ea297f02c..2c8a44df8 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -156,11 +156,12 @@ def greasemonkey_manager(data_tmpdir): def webkit_tab(qtbot, tab_registry, cookiejar_and_cache, mode_manager, session_manager_stub, greasemonkey_manager, fake_args, host_blocker_stub, config_stub): + # Make sure error logging via JS fails tests config_stub.val.content.javascript.log = { 'info': 'info', - 'error': 'info', - 'unknown': 'info', - 'warning': 'info' + 'error': 'error', + 'unknown': 'error', + 'warning': 'error', } webkittab = pytest.importorskip('qutebrowser.browser.webkit.webkittab') @@ -185,12 +186,13 @@ def webkit_tab(qtbot, tab_registry, cookiejar_and_cache, mode_manager, def webengine_tab(qtbot, tab_registry, fake_args, mode_manager, session_manager_stub, greasemonkey_manager, redirect_webengine_data, tabbed_browser_stubs, - config_stub): + config_stub, qapp): + # Make sure error logging via JS fails tests config_stub.val.content.javascript.log = { 'info': 'info', - 'error': 'info', - 'unknown': 'info', - 'warning': 'info' + 'error': 'error', + 'unknown': 'error', + 'warning': 'error', } tabwidget = tabbed_browser_stubs[0].widget From 20da259de6995ab9143969e0caa38cb5f8e77d67 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 16:01:38 +0200 Subject: [PATCH 12/36] Rewrite some :follow-selected tests as unit tests --- tests/end2end/features/caret.feature | 25 ------------------------- tests/unit/browser/test_caret.py | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/tests/end2end/features/caret.feature b/tests/end2end/features/caret.feature index 422919cea..e540bafcb 100644 --- a/tests/end2end/features/caret.feature +++ b/tests/end2end/features/caret.feature @@ -45,31 +45,6 @@ Feature: Caret mode # :follow-selected - Scenario: :follow-selected without a selection - When I run :follow-selected - Then no crash should happen - - Scenario: :follow-selected with text - When I run :move-to-next-word - And I run :toggle-selection - And I run :move-to-end-of-word - And I run :follow-selected - Then no crash should happen - - Scenario: :follow-selected with link (with JS) - When I set content.javascript.enabled to true - And I run :toggle-selection - And I run :move-to-end-of-word - And I run :follow-selected - Then data/hello.txt should be loaded - - Scenario: :follow-selected with link (without JS) - When I set content.javascript.enabled to false - And I run :toggle-selection - And I run :move-to-end-of-word - And I run :follow-selected - Then data/hello.txt should be loaded - Scenario: :follow-selected with --tab (with JS) When I set content.javascript.enabled to true And I run :tab-only diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index c7e100a59..0fd434a77 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -315,3 +315,25 @@ class TestSearch: caret.move_to_end_of_line() selection.check('wei drei') + + +class TestFollowSelected: + + def test_follow_selected_without_a_selection(self, caret, selection): + caret.follow_selected() + + def test_follow_selected_with_text(self, caret, selection): + caret.move_to_next_word() + selection.toggle() + caret.move_to_end_of_word() + caret.follow_selected() + + @pytest.mark.parametrize('with_js', [True, False]) + def test_follow_selected_with_link(self, caret, selection, config_stub, + qtbot, web_tab, with_js): + config_stub.val.content.javascript.enabled = with_js + selection.toggle() + caret.move_to_end_of_word() + with qtbot.wait_signal(web_tab.load_finished): + caret.follow_selected() + assert web_tab.url().path() == '/data/hello.txt' From b8ab3780832dad227b604084c3ad88216d01ed32 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 16:04:29 +0200 Subject: [PATCH 13/36] Show web_engine_tab container This is needed for caret tests on Qt < 5.11 --- tests/helpers/fixtures.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index 2c8a44df8..1c2ef5969 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -211,6 +211,9 @@ def webengine_tab(qtbot, tab_registry, fake_args, mode_manager, # to make sure container isn't GCed tab.container = container + with qtbot.waitExposed(container): + container.show() + return tab From 1647c28632823c8bf23585433507eed66a6aa6e3 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 16:20:38 +0200 Subject: [PATCH 14/36] Allow lists for javascript.convert_js_arg --- qutebrowser/utils/javascript.py | 2 ++ tests/unit/utils/test_javascript.py | 1 + 2 files changed, 3 insertions(+) diff --git a/qutebrowser/utils/javascript.py b/qutebrowser/utils/javascript.py index 93df8e70f..21b373dd1 100644 --- a/qutebrowser/utils/javascript.py +++ b/qutebrowser/utils/javascript.py @@ -59,6 +59,8 @@ def _convert_js_arg(arg): return str(arg).lower() elif isinstance(arg, (int, float)): return str(arg) + elif isinstance(arg, list): + return '[{}]'.format(', '.join(_convert_js_arg(e) for e in arg)) else: raise TypeError("Don't know how to handle {!r} of type {}!".format( arg, type(arg).__name__)) diff --git a/tests/unit/utils/test_javascript.py b/tests/unit/utils/test_javascript.py index 29e090fd0..0a196cfa1 100644 --- a/tests/unit/utils/test_javascript.py +++ b/tests/unit/utils/test_javascript.py @@ -84,6 +84,7 @@ class TestStringEscape: (None, 'undefined'), (object(), TypeError), (True, 'true'), + ([23, True, 'x'], '[23, true, "x"]'), ]) def test_convert_js_arg(arg, expected): if expected is TypeError: From 3738a8c8a93405936502db2d2b1aeba2983db029 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 16:20:58 +0200 Subject: [PATCH 15/36] Apply caret mode workarounds correctly --- qutebrowser/browser/webengine/webenginetab.py | 15 ++++++++++-- qutebrowser/javascript/caret.js | 23 +++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index a5367de82..c307df043 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -234,6 +234,17 @@ class WebEngineCaret(browsertab.AbstractCaret): """QtWebEngine implementations related to moving the cursor/selection.""" + def _flags(self): + """Get flags to pass to JS.""" + flags = [] + if qtutils.version_check('5.7.1', compiled=False): + flags.append('filter-prefix') + if not qtutils.version_check('5.10', compiled=False): + flags.append('end-of-doc-workaround') + if utils.is_windows: + flags.append('windows') + return flags + @pyqtSlot(usertypes.KeyMode) def _on_mode_entered(self, mode): if mode != usertypes.KeyMode.caret: @@ -246,8 +257,8 @@ class WebEngineCaret(browsertab.AbstractCaret): self._tab.search.clear() self._tab.run_js_async( - javascript.assemble('caret', - 'setPlatform', sys.platform, qVersion())) + javascript.assemble('caret', 'setFlags', self._flags())) + self._js_call('setInitialCursor', callback=self._selection_cb) def _selection_cb(self, enabled): diff --git a/qutebrowser/javascript/caret.js b/qutebrowser/javascript/caret.js index ecb4f97c8..08d5b7382 100644 --- a/qutebrowser/javascript/caret.js +++ b/qutebrowser/javascript/caret.js @@ -788,9 +788,17 @@ window._qutebrowser.caret = (function() { /** * Whether we're running on on old Qt 5.7.1. + * There, we need to use -webkit-filter. * @type {boolean} */ - CaretBrowsing.isOldQt = null; + CaretBrowsing.needsFilterPrefix = null; + + /** + * Whether we're running on Qt < 5.10. + * There, we need some additional movement workarounds. + * @type {boolean} + */ + CaretBrowsing.needsEndOfDocWorkaround = null; /** * Check if a node is a control that normally allows the user to interact @@ -868,7 +876,7 @@ window._qutebrowser.caret = (function() { }; CaretBrowsing.injectCaretStyles = function() { - const prefix = CaretBrowsing.isOldQt ? "-webkit-" : ""; + const prefix = CaretBrowsing.needsFilterPrefix ? "-webkit-" : ""; const style = ` .CaretBrowsing_Caret { position: absolute; @@ -1322,9 +1330,11 @@ window._qutebrowser.caret = (function() { return CaretBrowsing.selectionEnabled; }; - funcs.setPlatform = (platform, qtVersion) => { - CaretBrowsing.isWindows = platform.startsWith("win"); - CaretBrowsing.isOldQt = qtVersion === "5.7.1"; + funcs.setFlags = (flags) => { + CaretBrowsing.isWindows = flags.includes("windows"); + CaretBrowsing.needsFilterPrefix = flags.includes("filter-prefix"); + CaretBrowsing.needsEndOfDocWorkaround = + flags.includes("end-of-doc-workaround"); }; funcs.disableCaret = () => { @@ -1408,6 +1418,9 @@ window._qutebrowser.caret = (function() { funcs.moveToEndOfDocument = () => { CaretBrowsing.move("forward", "documentboundary"); + if (CaretBrowsing.needsEndOfDocWorkaround) { + CaretBrowsing.move("left", "character"); + } CaretBrowsing.finishMove(); }; From 88cbc9bb12e8a61b41c50b9e1bb2ecb5b7126471 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 8 Sep 2018 16:32:28 +0200 Subject: [PATCH 16/36] caret: Also enable end-of-doc-workaround on Windows --- qutebrowser/browser/webengine/webenginetab.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index c307df043..3eec5669c 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -236,14 +236,15 @@ class WebEngineCaret(browsertab.AbstractCaret): def _flags(self): """Get flags to pass to JS.""" - flags = [] + flags = set() if qtutils.version_check('5.7.1', compiled=False): - flags.append('filter-prefix') + flags.add('filter-prefix') if not qtutils.version_check('5.10', compiled=False): - flags.append('end-of-doc-workaround') + flags.add('end-of-doc-workaround') if utils.is_windows: - flags.append('windows') - return flags + flags.add('windows') + flags.add('end-of-doc-workaround') + return list(flags) @pyqtSlot(usertypes.KeyMode) def _on_mode_entered(self, mode): From 62458c7a841666e19ccbf7ae434038795eba6671 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 13 Sep 2018 22:29:38 +0200 Subject: [PATCH 17/36] Register a qute://testdata/ scheme for unit tests This is more lightweight than running a webserver (probably about the same as file://), but allows us to use relative links in files. --- tests/conftest.py | 26 ++++++++++++++++++++++++++ tests/unit/browser/test_caret.py | 3 +-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 7168cd812..0169ff3d2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,6 +24,7 @@ import os import sys import warnings +import mimetypes import pytest import hypothesis @@ -35,6 +36,7 @@ from helpers import logfail from helpers.logfail import fail_on_logging from helpers.messagemock import message_mock from helpers.fixtures import * # noqa: F403 +from qutebrowser.browser import qutescheme from qutebrowser.utils import qtutils, standarddir, usertypes, utils, version from qutebrowser.misc import objects from qutebrowser.qt import sip @@ -43,6 +45,7 @@ import qutebrowser.app # To register commands ON_CI = 'CI' in os.environ +_qute_scheme_handler = None # Set hypothesis settings @@ -284,3 +287,26 @@ def pytest_runtest_makereport(item, call): outcome = yield rep = outcome.get_result() setattr(item, "rep_" + rep.when, rep) + + +@pytest.fixture(scope='session', autouse=True) +def register_testdata_scheme_handler(qapp, request): + try: + global _qute_scheme_handler + from qutebrowser.browser.webengine import webenginequtescheme + from PyQt5.QtWebEngineWidgets import QWebEngineProfile + _qute_scheme_handler = webenginequtescheme.QuteSchemeHandler(parent=qapp) + _qute_scheme_handler.install(QWebEngineProfile.defaultProfile()) + except ImportError: + pass + + @qutescheme.add_handler('testdata') + def handler(url): + file_abs = os.path.abspath(os.path.dirname(__file__)) + filename = os.path.join(file_abs, 'end2end', + url.path().lstrip('/')) + with open(filename, 'rb') as f: + data = f.read() + + mimetype, _encoding = mimetypes.guess_type(filename) + return mimetype, data diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index 0fd434a77..0864a9947 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -31,9 +31,8 @@ from helpers import utils @pytest.fixture def caret(web_tab, qtbot, mode_manager): - path = os.path.join(utils.abs_datapath(), 'caret.html') with qtbot.wait_signal(web_tab.load_finished): - web_tab.openurl(QUrl.fromLocalFile(path)) + web_tab.openurl(QUrl('qute://testdata/data/caret.html')) mode_manager.enter(usertypes.KeyMode.caret) From 68af23b76efe31784df1529299900b28e544ead9 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 13 Sep 2018 22:41:30 +0200 Subject: [PATCH 18/36] Try getting selection multiple times --- tests/unit/browser/test_caret.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index 0864a9947..2ec81da9c 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -49,8 +49,21 @@ class Selection: self._callback_checker = utils.CallbackChecker(qtbot) def check(self, expected): - self._caret.selection(self._callback_checker.callback) - self._callback_checker.check(expected) + """Check whether we got the expected selection. + + Since (especially on Windows) the selection is empty if we're too + quickly, we try to read it multiple times. + """ + for _ in range(10): + with self._qtbot.wait_signal(self._callback_checker.got_result) as blocker: + self._caret.selection(self._callback_checker.callback) + + selection = blocker.args[0] + if selection: + assert selection == expected + return + + self._qtbot.wait(50) def check_multiline(self, expected): self.check(textwrap.dedent(expected).strip()) From afe16d3a7cbe437c59aba130ced5607dcc09474f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 13 Sep 2018 22:49:25 +0200 Subject: [PATCH 19/36] Avoid using qapp with autouse=True --- tests/conftest.py | 25 ------------------------- tests/helpers/fixtures.py | 31 ++++++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 0169ff3d2..05564f370 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,7 +24,6 @@ import os import sys import warnings -import mimetypes import pytest import hypothesis @@ -36,7 +35,6 @@ from helpers import logfail from helpers.logfail import fail_on_logging from helpers.messagemock import message_mock from helpers.fixtures import * # noqa: F403 -from qutebrowser.browser import qutescheme from qutebrowser.utils import qtutils, standarddir, usertypes, utils, version from qutebrowser.misc import objects from qutebrowser.qt import sip @@ -287,26 +285,3 @@ def pytest_runtest_makereport(item, call): outcome = yield rep = outcome.get_result() setattr(item, "rep_" + rep.when, rep) - - -@pytest.fixture(scope='session', autouse=True) -def register_testdata_scheme_handler(qapp, request): - try: - global _qute_scheme_handler - from qutebrowser.browser.webengine import webenginequtescheme - from PyQt5.QtWebEngineWidgets import QWebEngineProfile - _qute_scheme_handler = webenginequtescheme.QuteSchemeHandler(parent=qapp) - _qute_scheme_handler.install(QWebEngineProfile.defaultProfile()) - except ImportError: - pass - - @qutescheme.add_handler('testdata') - def handler(url): - file_abs = os.path.abspath(os.path.dirname(__file__)) - filename = os.path.join(file_abs, 'end2end', - url.path().lstrip('/')) - with open(filename, 'rb') as f: - data = f.read() - - mimetype, _encoding = mimetypes.guess_type(filename) - return mimetype, data diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index 1c2ef5969..092b61f12 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -31,6 +31,8 @@ import itertools import textwrap import unittest.mock import types +import mimetypes +import os.path import attr import pytest @@ -44,7 +46,7 @@ import helpers.utils from qutebrowser.config import (config, configdata, configtypes, configexc, configfiles, configcache) from qutebrowser.utils import objreg, standarddir, utils, usertypes -from qutebrowser.browser import greasemonkey, history +from qutebrowser.browser import greasemonkey, history, qutescheme from qutebrowser.browser.webkit import cookies from qutebrowser.misc import savemanager, sql, objects from qutebrowser.keyinput import modeman @@ -152,10 +154,33 @@ def greasemonkey_manager(data_tmpdir): objreg.delete('greasemonkey') +@pytest.fixture(scope='session') +def testdata_scheme(qapp): + try: + global _qute_scheme_handler + from qutebrowser.browser.webengine import webenginequtescheme + from PyQt5.QtWebEngineWidgets import QWebEngineProfile + _qute_scheme_handler = webenginequtescheme.QuteSchemeHandler(parent=qapp) + _qute_scheme_handler.install(QWebEngineProfile.defaultProfile()) + except ImportError: + pass + + @qutescheme.add_handler('testdata') + def handler(url): + file_abs = os.path.abspath(os.path.dirname(__file__)) + filename = os.path.join(file_abs, '..', 'end2end', + url.path().lstrip('/')) + with open(filename, 'rb') as f: + data = f.read() + + mimetype, _encoding = mimetypes.guess_type(filename) + return mimetype, data + + @pytest.fixture def webkit_tab(qtbot, tab_registry, cookiejar_and_cache, mode_manager, session_manager_stub, greasemonkey_manager, fake_args, - host_blocker_stub, config_stub): + host_blocker_stub, config_stub, testdata_scheme): # Make sure error logging via JS fails tests config_stub.val.content.javascript.log = { 'info': 'info', @@ -186,7 +211,7 @@ def webkit_tab(qtbot, tab_registry, cookiejar_and_cache, mode_manager, def webengine_tab(qtbot, tab_registry, fake_args, mode_manager, session_manager_stub, greasemonkey_manager, redirect_webengine_data, tabbed_browser_stubs, - config_stub, qapp): + config_stub, qapp, testdata_scheme): # Make sure error logging via JS fails tests config_stub.val.content.javascript.log = { 'info': 'info', From bc45aa33e0ac58e8ccd54508b36dc0b463c4c3c3 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 14 Sep 2018 20:18:33 +0200 Subject: [PATCH 20/36] Make sure we wait until follow_selected is done --- qutebrowser/browser/browsertab.py | 2 ++ qutebrowser/browser/webengine/webenginetab.py | 6 +++++- qutebrowser/browser/webkit/webkittab.py | 5 +++++ tests/unit/browser/test_caret.py | 10 ++++++---- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 087834e53..0eb9b6493 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -395,9 +395,11 @@ class AbstractCaret(QObject): Signals: selection_toggled: Emitted when the selection was toggled. arg: Whether the selection is now active. + follow_selected_done: Emitted when a follow_selection action is done. """ selection_toggled = pyqtSignal(bool) + follow_selected_done = pyqtSignal() def __init__(self, tab, mode_manager, parent=None): super().__init__(parent) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 3eec5669c..78913e92e 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -344,10 +344,13 @@ class WebEngineCaret(browsertab.AbstractCaret): tab: Open in a new tab. """ if js_elem is None: + self.follow_selected_done.emit() return + if js_elem == "focused": # we had a focused element, not a selected one. Just send self._follow_enter(tab) + self.follow_selected_done.emit() return assert isinstance(js_elem, dict), js_elem @@ -365,7 +368,8 @@ class WebEngineCaret(browsertab.AbstractCaret): elem.click(click_type) except webelem.Error as e: message.error(str(e)) - return + + self.follow_selected_done.emit() def follow_selected(self, *, tab=False): if self._tab.search.search_displayed: diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index 7b7ad0c7d..041e058b1 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -369,11 +369,13 @@ class WebKitCaret(browsertab.AbstractCaret): # https://github.com/annulen/webkit/commit/0e75f3272d149bc64899c161f150eb341a2417af # TODO find a way to check if something is focused self._follow_enter(tab) + self.follow_selected_done.emit() return try: selected_element = xml.etree.ElementTree.fromstring( '{}'.format(selection)).find('a') except xml.etree.ElementTree.ParseError: + self.follow_selected_done.emit() raise browsertab.WebTabError('Could not parse selected ' 'element!') @@ -381,6 +383,7 @@ class WebKitCaret(browsertab.AbstractCaret): try: url = selected_element.attrib['href'] except KeyError: + self.follow_selected_done.emit() raise browsertab.WebTabError('Anchor element without ' 'href!') url = self._tab.url().resolved(QUrl(url)) @@ -389,6 +392,8 @@ class WebKitCaret(browsertab.AbstractCaret): else: self._tab.openurl(url) + self.follow_selected_done.emit() + class WebKitZoom(browsertab.AbstractZoom): diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index 2ec81da9c..273fd6881 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -331,14 +331,16 @@ class TestSearch: class TestFollowSelected: - def test_follow_selected_without_a_selection(self, caret, selection): - caret.follow_selected() + def test_follow_selected_without_a_selection(self, qtbot, caret, selection): + with qtbot.wait_signal(caret.follow_selected_done): + caret.follow_selected() - def test_follow_selected_with_text(self, caret, selection): + def test_follow_selected_with_text(self, qtbot, caret, selection): caret.move_to_next_word() selection.toggle() caret.move_to_end_of_word() - caret.follow_selected() + with qtbot.wait_signal(caret.follow_selected_done): + caret.follow_selected() @pytest.mark.parametrize('with_js', [True, False]) def test_follow_selected_with_link(self, caret, selection, config_stub, From 8d066690e67582348e0f1bf5900e40a5964bd185 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 14 Sep 2018 20:23:56 +0200 Subject: [PATCH 21/36] Fix :follow-selected tests --- tests/unit/browser/test_caret.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index 273fd6881..70677361c 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -331,16 +331,24 @@ class TestSearch: class TestFollowSelected: - def test_follow_selected_without_a_selection(self, qtbot, caret, selection): - with qtbot.wait_signal(caret.follow_selected_done): - caret.follow_selected() + LOAD_STARTED_DELAY = 50 - def test_follow_selected_with_text(self, qtbot, caret, selection): + def test_follow_selected_without_a_selection(self, qtbot, caret, selection, web_tab, + mode_manager): + mode_manager.leave(usertypes.KeyMode.caret) + with qtbot.wait_signal(caret.follow_selected_done): + with qtbot.assert_not_emitted(web_tab.load_started): + caret.follow_selected() + qtbot.wait(self.LOAD_STARTED_DELAY) + + def test_follow_selected_with_text(self, qtbot, caret, selection, web_tab): caret.move_to_next_word() selection.toggle() caret.move_to_end_of_word() with qtbot.wait_signal(caret.follow_selected_done): - caret.follow_selected() + with qtbot.assert_not_emitted(web_tab.load_started): + caret.follow_selected() + qtbot.wait(self.LOAD_STARTED_DELAY) @pytest.mark.parametrize('with_js', [True, False]) def test_follow_selected_with_link(self, caret, selection, config_stub, From 200c11625f4dd6fb2a36f3dcb674402918da6c79 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 14 Sep 2018 21:58:42 +0200 Subject: [PATCH 22/36] Revert "Make sure we wait until follow_selected is done" This reverts commit bc45aa33e0ac58e8ccd54508b36dc0b463c4c3c3. --- qutebrowser/browser/browsertab.py | 2 -- qutebrowser/browser/webengine/webenginetab.py | 6 +----- qutebrowser/browser/webkit/webkittab.py | 5 ----- tests/unit/browser/test_caret.py | 14 ++++++-------- 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 0eb9b6493..087834e53 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -395,11 +395,9 @@ class AbstractCaret(QObject): Signals: selection_toggled: Emitted when the selection was toggled. arg: Whether the selection is now active. - follow_selected_done: Emitted when a follow_selection action is done. """ selection_toggled = pyqtSignal(bool) - follow_selected_done = pyqtSignal() def __init__(self, tab, mode_manager, parent=None): super().__init__(parent) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 78913e92e..3eec5669c 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -344,13 +344,10 @@ class WebEngineCaret(browsertab.AbstractCaret): tab: Open in a new tab. """ if js_elem is None: - self.follow_selected_done.emit() return - if js_elem == "focused": # we had a focused element, not a selected one. Just send self._follow_enter(tab) - self.follow_selected_done.emit() return assert isinstance(js_elem, dict), js_elem @@ -368,8 +365,7 @@ class WebEngineCaret(browsertab.AbstractCaret): elem.click(click_type) except webelem.Error as e: message.error(str(e)) - - self.follow_selected_done.emit() + return def follow_selected(self, *, tab=False): if self._tab.search.search_displayed: diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index 041e058b1..7b7ad0c7d 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -369,13 +369,11 @@ class WebKitCaret(browsertab.AbstractCaret): # https://github.com/annulen/webkit/commit/0e75f3272d149bc64899c161f150eb341a2417af # TODO find a way to check if something is focused self._follow_enter(tab) - self.follow_selected_done.emit() return try: selected_element = xml.etree.ElementTree.fromstring( '{}'.format(selection)).find('a') except xml.etree.ElementTree.ParseError: - self.follow_selected_done.emit() raise browsertab.WebTabError('Could not parse selected ' 'element!') @@ -383,7 +381,6 @@ class WebKitCaret(browsertab.AbstractCaret): try: url = selected_element.attrib['href'] except KeyError: - self.follow_selected_done.emit() raise browsertab.WebTabError('Anchor element without ' 'href!') url = self._tab.url().resolved(QUrl(url)) @@ -392,8 +389,6 @@ class WebKitCaret(browsertab.AbstractCaret): else: self._tab.openurl(url) - self.follow_selected_done.emit() - class WebKitZoom(browsertab.AbstractZoom): diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index 70677361c..5e1be7e74 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -336,19 +336,17 @@ class TestFollowSelected: def test_follow_selected_without_a_selection(self, qtbot, caret, selection, web_tab, mode_manager): mode_manager.leave(usertypes.KeyMode.caret) - with qtbot.wait_signal(caret.follow_selected_done): - with qtbot.assert_not_emitted(web_tab.load_started): - caret.follow_selected() - qtbot.wait(self.LOAD_STARTED_DELAY) + with qtbot.assert_not_emitted(web_tab.load_started): + caret.follow_selected() + qtbot.wait(self.LOAD_STARTED_DELAY) def test_follow_selected_with_text(self, qtbot, caret, selection, web_tab): caret.move_to_next_word() selection.toggle() caret.move_to_end_of_word() - with qtbot.wait_signal(caret.follow_selected_done): - with qtbot.assert_not_emitted(web_tab.load_started): - caret.follow_selected() - qtbot.wait(self.LOAD_STARTED_DELAY) + with qtbot.assert_not_emitted(web_tab.load_started): + caret.follow_selected() + qtbot.wait(self.LOAD_STARTED_DELAY) @pytest.mark.parametrize('with_js', [True, False]) def test_follow_selected_with_link(self, caret, selection, config_stub, From 4ae78d9bb2f4c89f47c2553c784dacc152f43f92 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 14 Sep 2018 22:40:47 +0200 Subject: [PATCH 23/36] Revert "Revert "Make sure we wait until follow_selected is done"" This reverts commit 200c11625f4dd6fb2a36f3dcb674402918da6c79. --- qutebrowser/browser/browsertab.py | 2 ++ qutebrowser/browser/webengine/webenginetab.py | 6 +++++- qutebrowser/browser/webkit/webkittab.py | 5 +++++ tests/unit/browser/test_caret.py | 14 ++++++++------ 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 087834e53..0eb9b6493 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -395,9 +395,11 @@ class AbstractCaret(QObject): Signals: selection_toggled: Emitted when the selection was toggled. arg: Whether the selection is now active. + follow_selected_done: Emitted when a follow_selection action is done. """ selection_toggled = pyqtSignal(bool) + follow_selected_done = pyqtSignal() def __init__(self, tab, mode_manager, parent=None): super().__init__(parent) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 3eec5669c..78913e92e 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -344,10 +344,13 @@ class WebEngineCaret(browsertab.AbstractCaret): tab: Open in a new tab. """ if js_elem is None: + self.follow_selected_done.emit() return + if js_elem == "focused": # we had a focused element, not a selected one. Just send self._follow_enter(tab) + self.follow_selected_done.emit() return assert isinstance(js_elem, dict), js_elem @@ -365,7 +368,8 @@ class WebEngineCaret(browsertab.AbstractCaret): elem.click(click_type) except webelem.Error as e: message.error(str(e)) - return + + self.follow_selected_done.emit() def follow_selected(self, *, tab=False): if self._tab.search.search_displayed: diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index 7b7ad0c7d..041e058b1 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -369,11 +369,13 @@ class WebKitCaret(browsertab.AbstractCaret): # https://github.com/annulen/webkit/commit/0e75f3272d149bc64899c161f150eb341a2417af # TODO find a way to check if something is focused self._follow_enter(tab) + self.follow_selected_done.emit() return try: selected_element = xml.etree.ElementTree.fromstring( '{}'.format(selection)).find('a') except xml.etree.ElementTree.ParseError: + self.follow_selected_done.emit() raise browsertab.WebTabError('Could not parse selected ' 'element!') @@ -381,6 +383,7 @@ class WebKitCaret(browsertab.AbstractCaret): try: url = selected_element.attrib['href'] except KeyError: + self.follow_selected_done.emit() raise browsertab.WebTabError('Anchor element without ' 'href!') url = self._tab.url().resolved(QUrl(url)) @@ -389,6 +392,8 @@ class WebKitCaret(browsertab.AbstractCaret): else: self._tab.openurl(url) + self.follow_selected_done.emit() + class WebKitZoom(browsertab.AbstractZoom): diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index 5e1be7e74..70677361c 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -336,17 +336,19 @@ class TestFollowSelected: def test_follow_selected_without_a_selection(self, qtbot, caret, selection, web_tab, mode_manager): mode_manager.leave(usertypes.KeyMode.caret) - with qtbot.assert_not_emitted(web_tab.load_started): - caret.follow_selected() - qtbot.wait(self.LOAD_STARTED_DELAY) + with qtbot.wait_signal(caret.follow_selected_done): + with qtbot.assert_not_emitted(web_tab.load_started): + caret.follow_selected() + qtbot.wait(self.LOAD_STARTED_DELAY) def test_follow_selected_with_text(self, qtbot, caret, selection, web_tab): caret.move_to_next_word() selection.toggle() caret.move_to_end_of_word() - with qtbot.assert_not_emitted(web_tab.load_started): - caret.follow_selected() - qtbot.wait(self.LOAD_STARTED_DELAY) + with qtbot.wait_signal(caret.follow_selected_done): + with qtbot.assert_not_emitted(web_tab.load_started): + caret.follow_selected() + qtbot.wait(self.LOAD_STARTED_DELAY) @pytest.mark.parametrize('with_js', [True, False]) def test_follow_selected_with_link(self, caret, selection, config_stub, From b5af448196da878a9cf3e8b6d8b12276d0e569c8 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 14 Sep 2018 22:46:31 +0200 Subject: [PATCH 24/36] Fix test_follow_selected_without_a_selection on QtWebKit --- tests/unit/browser/test_caret.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index 70677361c..7b7db595f 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -335,6 +335,7 @@ class TestFollowSelected: def test_follow_selected_without_a_selection(self, qtbot, caret, selection, web_tab, mode_manager): + caret.move_to_next_word() # Move cursor away from the link mode_manager.leave(usertypes.KeyMode.caret) with qtbot.wait_signal(caret.follow_selected_done): with qtbot.assert_not_emitted(web_tab.load_started): From 5ea8e766f57fe5a368334b53823764e1a93c024c Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 14 Sep 2018 22:49:18 +0200 Subject: [PATCH 25/36] Run all :follow-selected tests with/without JS --- tests/unit/browser/test_caret.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index 7b7db595f..63699976c 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -333,6 +333,10 @@ class TestFollowSelected: LOAD_STARTED_DELAY = 50 + @pytest.fixture(params=[True, False], autouse=True) + def toggle_js(self, request, config_stub): + config_stub.val.content.javascript.enabled = request.param + def test_follow_selected_without_a_selection(self, qtbot, caret, selection, web_tab, mode_manager): caret.move_to_next_word() # Move cursor away from the link @@ -351,10 +355,8 @@ class TestFollowSelected: caret.follow_selected() qtbot.wait(self.LOAD_STARTED_DELAY) - @pytest.mark.parametrize('with_js', [True, False]) def test_follow_selected_with_link(self, caret, selection, config_stub, - qtbot, web_tab, with_js): - config_stub.val.content.javascript.enabled = with_js + qtbot, web_tab): selection.toggle() caret.move_to_end_of_word() with qtbot.wait_signal(web_tab.load_finished): From d74daf92940c8c1a046e58b878d5a66e8d5184cf Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 14 Sep 2018 22:49:41 +0200 Subject: [PATCH 26/36] Fix lint --- qutebrowser/browser/webengine/webenginetab.py | 3 +-- scripts/dev/run_pylint_on_tests.py | 1 + tests/helpers/fixtures.py | 8 ++++++-- tests/unit/browser/test_caret.py | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 78913e92e..ad95c197b 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -21,12 +21,11 @@ import math import functools -import sys import re import html as html_utils from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QEvent, QPoint, QPointF, - QUrl, QTimer, QObject, qVersion) + QUrl, QTimer, QObject) from PyQt5.QtGui import QKeyEvent, QIcon from PyQt5.QtNetwork import QAuthenticator from PyQt5.QtWidgets import QApplication diff --git a/scripts/dev/run_pylint_on_tests.py b/scripts/dev/run_pylint_on_tests.py index f7855dfb7..d8523a4b4 100644 --- a/scripts/dev/run_pylint_on_tests.py +++ b/scripts/dev/run_pylint_on_tests.py @@ -52,6 +52,7 @@ def main(): # pytest fixtures 'redefined-outer-name', 'unused-argument', + 'too-many-arguments', # things which are okay in tests 'missing-docstring', 'protected-access', diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index 092b61f12..ff5702e47 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -52,6 +52,9 @@ from qutebrowser.misc import savemanager, sql, objects from qutebrowser.keyinput import modeman +_qute_scheme_handler = None + + class WinRegistryHelper: """Helper class for win_registry.""" @@ -160,13 +163,14 @@ def testdata_scheme(qapp): global _qute_scheme_handler from qutebrowser.browser.webengine import webenginequtescheme from PyQt5.QtWebEngineWidgets import QWebEngineProfile - _qute_scheme_handler = webenginequtescheme.QuteSchemeHandler(parent=qapp) + _qute_scheme_handler = webenginequtescheme.QuteSchemeHandler( + parent=qapp) _qute_scheme_handler.install(QWebEngineProfile.defaultProfile()) except ImportError: pass @qutescheme.add_handler('testdata') - def handler(url): + def handler(url): # pylint: disable=unused-variable file_abs = os.path.abspath(os.path.dirname(__file__)) filename = os.path.join(file_abs, '..', 'end2end', url.path().lstrip('/')) diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index 63699976c..7ef65fa5b 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -19,7 +19,6 @@ """Tests for caret browsing mode.""" -import os.path import textwrap import pytest @@ -55,7 +54,8 @@ class Selection: quickly, we try to read it multiple times. """ for _ in range(10): - with self._qtbot.wait_signal(self._callback_checker.got_result) as blocker: + with self._qtbot.wait_signal( + self._callback_checker.got_result) as blocker: self._caret.selection(self._callback_checker.callback) selection = blocker.args[0] From 9edbcfb8281ef4ad8f2c6f59de4b3a0a42a08c18 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 14 Sep 2018 23:38:29 +0200 Subject: [PATCH 27/36] Share setup between webkit_tab and webengine_tab --- tests/helpers/fixtures.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index ff5702e47..2556a0b47 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -182,9 +182,10 @@ def testdata_scheme(qapp): @pytest.fixture -def webkit_tab(qtbot, tab_registry, cookiejar_and_cache, mode_manager, - session_manager_stub, greasemonkey_manager, fake_args, - host_blocker_stub, config_stub, testdata_scheme): +def web_tab_setup(qtbot, tab_registry, session_manager_stub, + greasemonkey_manager, fake_args, host_blocker_stub, + config_stub, testdata_scheme): + """Shared setup for webkit_tab/webengine_tab.""" # Make sure error logging via JS fails tests config_stub.val.content.javascript.log = { 'info': 'info', @@ -193,6 +194,9 @@ def webkit_tab(qtbot, tab_registry, cookiejar_and_cache, mode_manager, 'warning': 'error', } + +@pytest.fixture +def webkit_tab(web_tab_setup, qtbot, cookiejar_and_cache, mode_manager): webkittab = pytest.importorskip('qutebrowser.browser.webkit.webkittab') container = QWidget() @@ -212,18 +216,8 @@ def webkit_tab(qtbot, tab_registry, cookiejar_and_cache, mode_manager, @pytest.fixture -def webengine_tab(qtbot, tab_registry, fake_args, mode_manager, - session_manager_stub, greasemonkey_manager, - redirect_webengine_data, tabbed_browser_stubs, - config_stub, qapp, testdata_scheme): - # Make sure error logging via JS fails tests - config_stub.val.content.javascript.log = { - 'info': 'info', - 'error': 'error', - 'unknown': 'error', - 'warning': 'error', - } - +def webengine_tab(web_tab_setup, qtbot, redirect_webengine_data, + tabbed_browser_stubs, mode_manager): tabwidget = tabbed_browser_stubs[0].widget tabwidget.current_index = 0 tabwidget.index_of = 0 From 59413810bf73667acbf3d145d4b904cf9fbbc025 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 15 Sep 2018 15:46:56 +0200 Subject: [PATCH 28/36] Clean up emitting of follow_selected_done --- qutebrowser/browser/webengine/webenginetab.py | 17 ++++++++++------- qutebrowser/browser/webkit/webkittab.py | 11 ++++++----- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index ad95c197b..a70bf9285 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -335,7 +335,13 @@ class WebEngineCaret(browsertab.AbstractCaret): self._tab.run_js_async(javascript.assemble('caret', 'getSelection'), callback) - def _follow_selected_cb(self, js_elem, tab=False): + def _follow_selected_cb_wrapped(self, js_elem, tab): + try: + self._follow_selected_cb(js_elem, tab) + finally: + self.follow_selected_done.emit() + + def _follow_selected_cb(self, js_elem, tab): """Callback for javascript which clicks the selected element. Args: @@ -343,13 +349,11 @@ class WebEngineCaret(browsertab.AbstractCaret): tab: Open in a new tab. """ if js_elem is None: - self.follow_selected_done.emit() return if js_elem == "focused": # we had a focused element, not a selected one. Just send self._follow_enter(tab) - self.follow_selected_done.emit() return assert isinstance(js_elem, dict), js_elem @@ -368,8 +372,6 @@ class WebEngineCaret(browsertab.AbstractCaret): except webelem.Error as e: message.error(str(e)) - self.follow_selected_done.emit() - def follow_selected(self, *, tab=False): if self._tab.search.search_displayed: # We are currently in search mode. @@ -384,8 +386,9 @@ class WebEngineCaret(browsertab.AbstractCaret): # click an existing blue selection js_code = javascript.assemble('webelem', 'find_selected_focused_link') - self._tab.run_js_async(js_code, lambda jsret: - self._follow_selected_cb(jsret, tab)) + self._tab.run_js_async( + js_code, + lambda jsret: self._follow_selected_cb_wrapped(jsret, tab)) def _js_call(self, command, *args, callback=None): code = javascript.assemble('caret', command, *args) diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index 041e058b1..7f0740b65 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -348,7 +348,7 @@ class WebKitCaret(browsertab.AbstractCaret): def selection(self, callback): callback(self._widget.selectedText()) - def follow_selected(self, *, tab=False): + def _follow_selected(self, *, tab=False): if QWebSettings.globalSettings().testAttribute( QWebSettings.JavascriptEnabled): if tab: @@ -369,13 +369,11 @@ class WebKitCaret(browsertab.AbstractCaret): # https://github.com/annulen/webkit/commit/0e75f3272d149bc64899c161f150eb341a2417af # TODO find a way to check if something is focused self._follow_enter(tab) - self.follow_selected_done.emit() return try: selected_element = xml.etree.ElementTree.fromstring( '{}'.format(selection)).find('a') except xml.etree.ElementTree.ParseError: - self.follow_selected_done.emit() raise browsertab.WebTabError('Could not parse selected ' 'element!') @@ -383,7 +381,6 @@ class WebKitCaret(browsertab.AbstractCaret): try: url = selected_element.attrib['href'] except KeyError: - self.follow_selected_done.emit() raise browsertab.WebTabError('Anchor element without ' 'href!') url = self._tab.url().resolved(QUrl(url)) @@ -392,7 +389,11 @@ class WebKitCaret(browsertab.AbstractCaret): else: self._tab.openurl(url) - self.follow_selected_done.emit() + def follow_selected(self, *, tab=False): + try: + self._follow_selected(tab=tab) + finally: + self.follow_selected_done.emit() class WebKitZoom(browsertab.AbstractZoom): From f14b37a3c42a56b39f5d33bd3bf64d113aa46885 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 15 Sep 2018 15:47:44 +0200 Subject: [PATCH 29/36] Use os.pardir --- tests/helpers/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index 2556a0b47..5332d1433 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -172,7 +172,7 @@ def testdata_scheme(qapp): @qutescheme.add_handler('testdata') def handler(url): # pylint: disable=unused-variable file_abs = os.path.abspath(os.path.dirname(__file__)) - filename = os.path.join(file_abs, '..', 'end2end', + filename = os.path.join(file_abs, os.pardir, 'end2end', url.path().lstrip('/')) with open(filename, 'rb') as f: data = f.read() From e47bf7a1377d48107107f63f061daf587b378490 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 15 Sep 2018 15:49:48 +0200 Subject: [PATCH 30/36] Remove duplicate test --- tests/unit/browser/test_caret.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index 7ef65fa5b..ee134c63e 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -121,14 +121,6 @@ class TestBlock: eins zwei drei """) - def test_selecting_a_block(self, caret, selection): - selection.toggle() - caret.move_to_end_of_next_block() - selection.check_multiline(""" - one two three - eins zwei drei - """) - def test_moving_back_to_the_end_of_prev_block_with_sel(self, caret, selection): caret.move_to_end_of_next_block(2) selection.toggle() From abff44def642316b223a46d54f9de78d7c9679f1 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 15 Sep 2018 20:39:35 +0200 Subject: [PATCH 31/36] Get rid of end-of-doc-workaround in caret browsing In Qt < 5.10 (and also sometimes on Windows), we get extra spaces or newlines when moving to the end of the document. However, this only happens *sometimes*, and manual testing confirms that with the current workaround, we actually lose the last char in the selection. I'm not sure what's happening there, but instead of making things worse with the workaround, let's just be a bit less strict with the checking there and accept both variants... This seems like some Chromium bug we can't do much about. --- qutebrowser/browser/webengine/webenginetab.py | 3 --- qutebrowser/javascript/caret.js | 12 ------------ tests/unit/browser/test_caret.py | 12 +++++++----- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index a70bf9285..36ac2a99a 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -238,11 +238,8 @@ class WebEngineCaret(browsertab.AbstractCaret): flags = set() if qtutils.version_check('5.7.1', compiled=False): flags.add('filter-prefix') - if not qtutils.version_check('5.10', compiled=False): - flags.add('end-of-doc-workaround') if utils.is_windows: flags.add('windows') - flags.add('end-of-doc-workaround') return list(flags) @pyqtSlot(usertypes.KeyMode) diff --git a/qutebrowser/javascript/caret.js b/qutebrowser/javascript/caret.js index 08d5b7382..6903fb985 100644 --- a/qutebrowser/javascript/caret.js +++ b/qutebrowser/javascript/caret.js @@ -793,13 +793,6 @@ window._qutebrowser.caret = (function() { */ CaretBrowsing.needsFilterPrefix = null; - /** - * Whether we're running on Qt < 5.10. - * There, we need some additional movement workarounds. - * @type {boolean} - */ - CaretBrowsing.needsEndOfDocWorkaround = null; - /** * Check if a node is a control that normally allows the user to interact * with it using arrow keys. We won't override the arrow keys when such a @@ -1333,8 +1326,6 @@ window._qutebrowser.caret = (function() { funcs.setFlags = (flags) => { CaretBrowsing.isWindows = flags.includes("windows"); CaretBrowsing.needsFilterPrefix = flags.includes("filter-prefix"); - CaretBrowsing.needsEndOfDocWorkaround = - flags.includes("end-of-doc-workaround"); }; funcs.disableCaret = () => { @@ -1418,9 +1409,6 @@ window._qutebrowser.caret = (function() { funcs.moveToEndOfDocument = () => { CaretBrowsing.move("forward", "documentboundary"); - if (CaretBrowsing.needsEndOfDocWorkaround) { - CaretBrowsing.move("left", "character"); - } CaretBrowsing.finishMove(); }; diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index ee134c63e..1c3c7f794 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -47,7 +47,7 @@ class Selection: self._caret = caret self._callback_checker = utils.CallbackChecker(qtbot) - def check(self, expected): + def check(self, expected, *, strip=False): """Check whether we got the expected selection. Since (especially on Windows) the selection is empty if we're too @@ -60,13 +60,15 @@ class Selection: selection = blocker.args[0] if selection: + if strip: + selection = selection.strip() assert selection == expected return self._qtbot.wait(50) - def check_multiline(self, expected): - self.check(textwrap.dedent(expected).strip()) + def check_multiline(self, expected, *, strip=False): + self.check(textwrap.dedent(expected).strip(), strip=strip) def toggle(self): with self._qtbot.wait_signal(self._caret.selection_toggled): @@ -89,7 +91,7 @@ class TestDocument: four five six vier fünf sechs - """) + """, strip=True) def test_moving_to_end_and_start(self, caret, selection): caret.move_to_end_of_document() @@ -108,7 +110,7 @@ class TestDocument: four five six vier fünf sechs - """) + """, strip=True) class TestBlock: From f817e5d271998ab0258dc9dd9314f17543e4f85b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 15 Sep 2018 20:41:56 +0200 Subject: [PATCH 32/36] Fix typo --- tests/unit/browser/test_caret.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index 1c3c7f794..bed4e3914 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -50,8 +50,8 @@ class Selection: def check(self, expected, *, strip=False): """Check whether we got the expected selection. - Since (especially on Windows) the selection is empty if we're too - quickly, we try to read it multiple times. + Since (especially on Windows) the selection is empty if we're checking + too quickly, we try to read it multiple times. """ for _ in range(10): with self._qtbot.wait_signal( From e105edebf52980ddffce5f0d3ab3c256e5b35a28 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 15 Sep 2018 20:46:11 +0200 Subject: [PATCH 33/36] Wait for follow_selected_done --- tests/unit/browser/test_caret.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/browser/test_caret.py b/tests/unit/browser/test_caret.py index bed4e3914..c93ff2afc 100644 --- a/tests/unit/browser/test_caret.py +++ b/tests/unit/browser/test_caret.py @@ -354,5 +354,6 @@ class TestFollowSelected: selection.toggle() caret.move_to_end_of_word() with qtbot.wait_signal(web_tab.load_finished): - caret.follow_selected() + with qtbot.wait_signal(caret.follow_selected_done): + caret.follow_selected() assert web_tab.url().path() == '/data/hello.txt' From 821118356707444b1fe129d89b9bcfa7f2c307ac Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 17 Sep 2018 11:32:13 +0200 Subject: [PATCH 34/36] Work around Travis CI crash test_caret.py crashes on Travis with this stack: ?? () from /usr/lib/x86_64-linux-gnu/dri/swrast_dri.so ?? () from /usr/lib/x86_64-linux-gnu/dri/swrast_dri.so ?? () from /usr/lib/x86_64-linux-gnu/dri/swrast_dri.so QSGBatchRenderer::Renderer::renderBatches() () from .../site-packages/PyQt5/Qt/lib/libQt5Quick.so.5 QSGBatchRenderer::Renderer::render() () from .../site-packages/PyQt5/Qt/lib/libQt5Quick.so.5 QSGRenderer::renderScene(QSGBindable const&) () from .../site-packages/PyQt5/Qt/lib/libQt5Quick.so.5 QSGRenderer::renderScene(unsigned int) () from .../site-packages/PyQt5/Qt/lib/libQt5Quick.so.5 QSGDefaultRenderContext::renderNextFrame(QSGRenderer*, unsigned int) () from .../site-packages/PyQt5/Qt/lib/libQt5Quick.so.5 QQuickWindowPrivate::renderSceneGraph(QSize const&) () from .../site-packages/PyQt5/Qt/lib/libQt5Quick.so.5 QQuickRenderControl::render() () from .../site-packages/PyQt5/Qt/lib/libQt5Quick.so.5 ?? () from .../site-packages/PyQt5/Qt/lib/libQt5QuickWidgets.so.5 ?? () from .../site-packages/PyQt5/Qt/lib/libQt5QuickWidgets.so.5 QObject::event(QEvent*) () from .../site-packages/PyQt5/Qt/lib/libQt5Core.so.5 QWidget::event(QEvent*) () from .../site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5 QQuickWidget::event(QEvent*) () from .../site-packages/PyQt5/Qt/lib/libQt5QuickWidgets.so.5 ?? () from .../site-packages/PyQt5/Qt/lib/libQt5WebEngineWidgets.so.5 QApplicationPrivate::notify_helper(QObject*, QEvent*) () from .../site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5 QApplication::notify(QObject*, QEvent*) () from .../site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5 sipQApplication::notify(QObject*, QEvent*) () from .../site-packages/PyQt5/QtWidgets.so QCoreApplication::notifyInternal2(QObject*, QEvent*) () from .../site-packages/PyQt5/Qt/lib/libQt5Core.so.5 QTimerInfoList::activateTimers() () from .../site-packages/PyQt5/Qt/lib/libQt5Core.so.5 ?? () from .../site-packages/PyQt5/Qt/lib/libQt5Core.so.5 g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0 ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0 g_main_context_iteration () from /lib/x86_64-linux-gnu/libglib-2.0.so.0 QEventDispatcherGlib::processEvents(QFlags) () from .../site-packages/PyQt5/Qt/lib/libQt5Core.so.5 QEventLoop::exec(QFlags) () from .../site-packages/PyQt5/Qt/lib/libQt5Core.so.5 --- scripts/dev/ci/travis_run.sh | 4 ++++ tox.ini | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/dev/ci/travis_run.sh b/scripts/dev/ci/travis_run.sh index 55ca7c11e..a287e844e 100644 --- a/scripts/dev/ci/travis_run.sh +++ b/scripts/dev/ci/travis_run.sh @@ -28,5 +28,9 @@ else args=() [[ $TRAVIS_OS_NAME == osx ]] && args=('--qute-bdd-webengine' '--no-xvfb' 'tests/unit') + # WORKAROUND for unknown crash inside swrast_dri.so + # See https://github.com/qutebrowser/qutebrowser/pull/4218#issuecomment-421931770 + [[ $TESTENV == py36-pyqt59 ]] && export QT_QUICK_BACKEND=software + tox -e "$TESTENV" -- "${args[@]}" fi diff --git a/tox.ini b/tox.ini index 02f3ae729..37451918c 100644 --- a/tox.ini +++ b/tox.ini @@ -16,7 +16,7 @@ setenv = pyqt{,56,571,59,510,511}: LINK_PYQT_SKIP=true pyqt{,56,571,59,510,511}: QUTE_BDD_WEBENGINE=true cov: PYTEST_ADDOPTS=--cov --cov-report xml --cov-report=html --cov-report= -passenv = PYTHON DISPLAY XAUTHORITY HOME USERNAME USER CI TRAVIS XDG_* QUTE_* DOCKER +passenv = PYTHON DISPLAY XAUTHORITY HOME USERNAME USER CI TRAVIS XDG_* QUTE_* DOCKER QT_QUICK_BACKEND basepython = py35: {env:PYTHON:python3.5} py36: {env:PYTHON:python3.6} From 3c1ab71fcecf612552b1accccc738cd94aea474b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 24 Sep 2018 18:26:31 +0200 Subject: [PATCH 35/36] Remove dead CaretBrowsing code --- qutebrowser/javascript/caret.js | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/qutebrowser/javascript/caret.js b/qutebrowser/javascript/caret.js index 6903fb985..cd226dc1c 100644 --- a/qutebrowser/javascript/caret.js +++ b/qutebrowser/javascript/caret.js @@ -755,31 +755,6 @@ window._qutebrowser.caret = (function() { */ CaretBrowsing.isSelectionCollapsed = false; - /** - * The id returned by window.setInterval for our blink function, so - * we can cancel it when caret browsing is disabled. - * @type {number?} - */ - CaretBrowsing.blinkFunctionId = null; - - /** - * The desired x-coordinate to match when moving the caret up and down. - * To match the behavior as documented in Mozilla's caret browsing spec - * (http://www.mozilla.org/access/keyboard/proposal), we keep track of the - * initial x position when the user starts moving the caret up and down, - * so that the x position doesn't drift as you move throughout lines, but - * stays as close as possible to the initial position. This is reset when - * moving left or right or clicking. - * @type {number?} - */ - CaretBrowsing.targetX = null; - - /** - * A flag that flips on or off as the caret blinks. - * @type {boolean} - */ - CaretBrowsing.blinkFlag = true; - /** * Whether we're running on Windows. * @type {boolean} @@ -988,7 +963,6 @@ window._qutebrowser.caret = (function() { */ CaretBrowsing.recreateCaretElement = function() { if (CaretBrowsing.caretElement) { - window.clearInterval(CaretBrowsing.blinkFunctionId); CaretBrowsing.caretElement.parentElement.removeChild( CaretBrowsing.caretElement); CaretBrowsing.caretElement = null; @@ -1233,7 +1207,6 @@ window._qutebrowser.caret = (function() { return true; } window.setTimeout(() => { - CaretBrowsing.targetX = null; CaretBrowsing.updateCaretOrSelection(false); }, 0); return true; @@ -1251,7 +1224,6 @@ window._qutebrowser.caret = (function() { CaretBrowsing.updateCaretOrSelection(true); } else if (!CaretBrowsing.isCaretVisible && CaretBrowsing.caretElement) { - window.clearInterval(CaretBrowsing.blinkFunctionId); if (CaretBrowsing.caretElement) { CaretBrowsing.isSelectionCollapsed = false; CaretBrowsing.caretElement.parentElement.removeChild( From 119a60d498a9e92c3936b7834712e4d19a5353d8 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 24 Sep 2018 19:23:11 +0200 Subject: [PATCH 36/36] caret: Don't turn on animation repeatedly --- qutebrowser/javascript/caret.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/qutebrowser/javascript/caret.js b/qutebrowser/javascript/caret.js index cd226dc1c..8f3ec8d72 100644 --- a/qutebrowser/javascript/caret.js +++ b/qutebrowser/javascript/caret.js @@ -768,6 +768,13 @@ window._qutebrowser.caret = (function() { */ CaretBrowsing.needsFilterPrefix = null; + /** + * The id returned by window.setInterval for our stopAnimation function, so + * we can cancel it when we call stopAnimation again. + * @type {number?} + */ + CaretBrowsing.animationFunctionId = null; + /** * Check if a node is a control that normally allows the user to interact * with it using arrow keys. We won't override the arrow keys when such a @@ -1252,7 +1259,9 @@ window._qutebrowser.caret = (function() { CaretBrowsing.stopAnimation = function() { if (CaretBrowsing.caretElement) { CaretBrowsing.caretElement.style.animationIterationCount = 0; - window.setTimeout(() => { + window.clearTimeout(CaretBrowsing.animationFunctionId); + + CaretBrowsing.animationFunctionId = window.setTimeout(() => { CaretBrowsing.startAnimation(); }, 1000); }