Merge remote-tracking branch 'origin/pr/4218'
This commit is contained in:
commit
a292664ca0
@ -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)
|
||||
|
@ -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
|
||||
@ -234,6 +233,15 @@ class WebEngineCaret(browsertab.AbstractCaret):
|
||||
|
||||
"""QtWebEngine implementations related to moving the cursor/selection."""
|
||||
|
||||
def _flags(self):
|
||||
"""Get flags to pass to JS."""
|
||||
flags = set()
|
||||
if qtutils.version_check('5.7.1', compiled=False):
|
||||
flags.add('filter-prefix')
|
||||
if utils.is_windows:
|
||||
flags.add('windows')
|
||||
return list(flags)
|
||||
|
||||
@pyqtSlot(usertypes.KeyMode)
|
||||
def _on_mode_entered(self, mode):
|
||||
if mode != usertypes.KeyMode.caret:
|
||||
@ -246,9 +254,9 @@ class WebEngineCaret(browsertab.AbstractCaret):
|
||||
self._tab.search.clear()
|
||||
|
||||
self._tab.run_js_async(
|
||||
javascript.assemble('caret',
|
||||
'setPlatform', sys.platform, qVersion()))
|
||||
self._js_call('setInitialCursor', self._selection_cb)
|
||||
javascript.assemble('caret', 'setFlags', self._flags()))
|
||||
|
||||
self._js_call('setInitialCursor', callback=self._selection_cb)
|
||||
|
||||
def _selection_cb(self, enabled):
|
||||
"""Emit selection_toggled based on setInitialCursor."""
|
||||
@ -266,32 +274,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 +301,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 +319,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')
|
||||
@ -335,7 +332,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:
|
||||
@ -344,6 +347,7 @@ class WebEngineCaret(browsertab.AbstractCaret):
|
||||
"""
|
||||
if js_elem is None:
|
||||
return
|
||||
|
||||
if js_elem == "focused":
|
||||
# we had a focused element, not a selected one. Just send <enter>
|
||||
self._follow_enter(tab)
|
||||
@ -364,7 +368,6 @@ class WebEngineCaret(browsertab.AbstractCaret):
|
||||
elem.click(click_type)
|
||||
except webelem.Error as e:
|
||||
message.error(str(e))
|
||||
return
|
||||
|
||||
def follow_selected(self, *, tab=False):
|
||||
if self._tab.search.search_displayed:
|
||||
@ -380,11 +383,13 @@ 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, 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):
|
||||
|
@ -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:
|
||||
@ -389,6 +389,12 @@ class WebKitCaret(browsertab.AbstractCaret):
|
||||
else:
|
||||
self._tab.openurl(url)
|
||||
|
||||
def follow_selected(self, *, tab=False):
|
||||
try:
|
||||
self._follow_selected(tab=tab)
|
||||
finally:
|
||||
self.follow_selected_done.emit()
|
||||
|
||||
|
||||
class WebKitZoom(browsertab.AbstractZoom):
|
||||
|
||||
|
@ -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}
|
||||
@ -788,9 +763,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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -868,7 +851,7 @@ window._qutebrowser.caret = (function() {
|
||||
};
|
||||
|
||||
CaretBrowsing.injectCaretStyles = function() {
|
||||
const prefix = CaretBrowsing.isOldQt ? "-webkit-" : "";
|
||||
const prefix = CaretBrowsing.needsFilterPrefix ? "-webkit-" : "";
|
||||
const style = `
|
||||
.CaretBrowsing_Caret {
|
||||
position: absolute;
|
||||
@ -987,7 +970,6 @@ window._qutebrowser.caret = (function() {
|
||||
*/
|
||||
CaretBrowsing.recreateCaretElement = function() {
|
||||
if (CaretBrowsing.caretElement) {
|
||||
window.clearInterval(CaretBrowsing.blinkFunctionId);
|
||||
CaretBrowsing.caretElement.parentElement.removeChild(
|
||||
CaretBrowsing.caretElement);
|
||||
CaretBrowsing.caretElement = null;
|
||||
@ -1163,47 +1145,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) {
|
||||
@ -1232,7 +1214,6 @@ window._qutebrowser.caret = (function() {
|
||||
return true;
|
||||
}
|
||||
window.setTimeout(() => {
|
||||
CaretBrowsing.targetX = null;
|
||||
CaretBrowsing.updateCaretOrSelection(false);
|
||||
}, 0);
|
||||
return true;
|
||||
@ -1250,7 +1231,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(
|
||||
@ -1271,14 +1251,20 @@ 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.clearTimeout(CaretBrowsing.animationFunctionId);
|
||||
|
||||
CaretBrowsing.animationFunctionId = window.setTimeout(() => {
|
||||
CaretBrowsing.startAnimation();
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
CaretBrowsing.init = function() {
|
||||
@ -1318,9 +1304,9 @@ 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");
|
||||
};
|
||||
|
||||
funcs.disableCaret = () => {
|
||||
@ -1331,67 +1317,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 = () => {
|
||||
|
@ -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__))
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
@ -65,9 +66,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=(<?https?://|^# Copyright 201\d)|^ *def [a-z]',
|
||||
r'--method-rgx=[a-z_][A-Za-z0-9_]{1,100}$',
|
||||
] + sys.argv[2:] + files
|
||||
env = os.environ.copy()
|
||||
env['PYTHONPATH'] = os.pathsep.join(pythonpath)
|
||||
|
||||
|
@ -43,6 +43,7 @@ import qutebrowser.app # To register commands
|
||||
|
||||
|
||||
ON_CI = 'CI' in os.environ
|
||||
_qute_scheme_handler = None
|
||||
|
||||
|
||||
# Set hypothesis settings
|
||||
|
@ -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,42 +43,8 @@ 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
|
||||
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
|
||||
@ -356,28 +104,3 @@ Feature: Caret mode
|
||||
And I run :fake-key <tab>
|
||||
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"
|
||||
|
@ -31,6 +31,8 @@ import itertools
|
||||
import textwrap
|
||||
import unittest.mock
|
||||
import types
|
||||
import mimetypes
|
||||
import os.path
|
||||
|
||||
import attr
|
||||
import pytest
|
||||
@ -44,12 +46,15 @@ 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
|
||||
|
||||
|
||||
_qute_scheme_handler = None
|
||||
|
||||
|
||||
class WinRegistryHelper:
|
||||
|
||||
"""Helper class for win_registry."""
|
||||
@ -152,29 +157,86 @@ 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): # pylint: disable=unused-variable
|
||||
file_abs = os.path.abspath(os.path.dirname(__file__))
|
||||
filename = os.path.join(file_abs, os.pardir, '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):
|
||||
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',
|
||||
'error': 'error',
|
||||
'unknown': 'error',
|
||||
'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()
|
||||
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
|
||||
tab.container = container
|
||||
|
||||
with qtbot.waitExposed(container):
|
||||
container.show()
|
||||
|
||||
return tab
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def webengine_tab(qtbot, tab_registry, fake_args, mode_manager,
|
||||
session_manager_stub, greasemonkey_manager,
|
||||
redirect_webengine_data, tabbed_browser_stubs):
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
with qtbot.waitExposed(container):
|
||||
container.show()
|
||||
|
||||
return tab
|
||||
|
||||
|
||||
@ -455,9 +517,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)
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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
|
||||
|
359
tests/unit/browser/test_caret.py
Normal file
359
tests/unit/browser/test_caret.py
Normal file
@ -0,0 +1,359 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Tests for caret browsing mode."""
|
||||
|
||||
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):
|
||||
with qtbot.wait_signal(web_tab.load_finished):
|
||||
web_tab.openurl(QUrl('qute://testdata/data/caret.html'))
|
||||
|
||||
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, *, strip=False):
|
||||
"""Check whether we got the expected selection.
|
||||
|
||||
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(
|
||||
self._callback_checker.got_result) as blocker:
|
||||
self._caret.selection(self._callback_checker.callback)
|
||||
|
||||
selection = blocker.args[0]
|
||||
if selection:
|
||||
if strip:
|
||||
selection = selection.strip()
|
||||
assert selection == expected
|
||||
return
|
||||
|
||||
self._qtbot.wait(50)
|
||||
|
||||
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):
|
||||
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
|
||||
""", strip=True)
|
||||
|
||||
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
|
||||
""", strip=True)
|
||||
|
||||
|
||||
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_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
|
||||
@pytest.mark.no_xvfb
|
||||
def test_yanking_a_searched_line(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)
|
||||
callback_checker.check(True)
|
||||
|
||||
mode_manager.enter(usertypes.KeyMode.caret)
|
||||
caret.move_to_end_of_line()
|
||||
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, qtbot):
|
||||
web_tab.show()
|
||||
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')
|
||||
|
||||
|
||||
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
|
||||
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):
|
||||
with qtbot.assert_not_emitted(web_tab.load_started):
|
||||
caret.follow_selected()
|
||||
qtbot.wait(self.LOAD_STARTED_DELAY)
|
||||
|
||||
def test_follow_selected_with_link(self, caret, selection, config_stub,
|
||||
qtbot, web_tab):
|
||||
selection.toggle()
|
||||
caret.move_to_end_of_word()
|
||||
with qtbot.wait_signal(web_tab.load_finished):
|
||||
with qtbot.wait_signal(caret.follow_selected_done):
|
||||
caret.follow_selected()
|
||||
assert web_tab.url().path() == '/data/hello.txt'
|
@ -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:
|
||||
|
2
tox.ini
2
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}
|
||||
|
Loading…
Reference in New Issue
Block a user