Compare commits

...

19 Commits
osx ... v1.2.1

Author SHA1 Message Date
Florian Bruhin
6145786e46 Release v1.2.1 2018-03-14 20:19:14 +01:00
Florian Bruhin
404c276774 Update changelog from master 2018-03-14 20:18:57 +01:00
Florian Bruhin
2e87ee0960 Swap Control/Meta back on macOS
Fixes #3697

(cherry picked from commit fd9e7bed7fd9842eac22ed304a094a92cc953577)
(cherry picked from commit 84c7c37e8e)
2018-03-14 20:05:09 +01:00
Florian Bruhin
ca0669253a Disable test_software_rendering on macOS
For some reason, macOS doesn't care about us disabling software rendering

(cherry picked from commit d232b3ea57)
2018-03-14 20:05:07 +01:00
Florian Bruhin
4b6bfd066c Don't emit predicted_navigation for reloads at all
When we reload a page because of a config change, we won't get another
titleChanged signal (at least sometimes).

Also, the predicted_navigation signal is worthless when reloading anyways, as
we're going to load the same URL and not something different.

Fixes #3718

(cherry picked from commit 0418a865c17c26720219e33a67c88410a6ac7181)
2018-03-14 18:18:15 +01:00
Jay Kamat
d49e6d0229 Add test for #3711
(cherry picked from commit 35beff98a9)
2018-03-14 07:45:12 +01:00
Jay Kamat
bb76931be6 Fix hinting in frames on qt5.9 with input ranges
(cherry picked from commit a6e94cf30c)
2018-03-14 07:45:08 +01:00
Florian Bruhin
41f38a244e Mark BaseKeyParser.handle as noqa
This got fixed properly in master, but can stay like this in this branch.
2018-03-13 14:43:39 +01:00
Florian Bruhin
ad5fde9d05 Fall back to non-keypad keys without any keypad bindings
Fixes #3701

(cherry picked from commit b88ac51d25)
2018-03-13 14:42:43 +01:00
Florian Bruhin
4cc4426428 Don't emit predicted_navigation with invalid URLs
Fixes #3706

(cherry picked from commit 1c9598d2c0)
2018-03-13 09:51:34 +01:00
Florian Bruhin
e5bcf99295 Fix lint
(cherry picked from commit 8c5b7bcd03)
2018-03-12 09:27:18 +01:00
Florian Bruhin
2e87539b44 Normalize keys read from the config
This makes sure the internal bindings.commands object only contains normalized
key sequences.

Fixes #3699

(cherry picked from commit 9941812127)
2018-03-12 08:03:44 +01:00
Florian Bruhin
4bff190db9 Make from_obj() work for List/Dict configtypes
We can't easily make it work for ListOrValue as we don't know which of both we
get at this point.

(cherry picked from commit 990c0707f4)
2018-03-12 08:03:42 +01:00
Florian Bruhin
8b588d2635 tests: Add a yaml_config_stub fixture
(cherry picked from commit c03ef10d54)
2018-03-12 08:03:38 +01:00
Florian Bruhin
9cab6403d4 build_release: Wait before detaching volume
This hopefully helps with detaching it properly.

(cherry picked from commit 27c2650245)
2018-03-11 21:07:39 +01:00
Florian Bruhin
d4d3e76121 Force PyQt 5.10.0 with "tox -e mkvenv-pypi"
Fixes #3662

(cherry picked from commit 30ab1d0218)
2018-03-11 21:07:35 +01:00
Florian Bruhin
7fd1b4b180 Mark another GreaseMonkey test as flaky
See #3238

(cherry picked from commit f0a649e101)
2018-03-11 21:07:29 +01:00
Florian Bruhin
2f913f5932 Fix keybinding cheatsheet URLs in quickstart.asciidoc
The URLs and the patching were changed in
96e8151cce but not in quickstart.asciidoc.

(cherry picked from commit 75ab8f077d)
2018-03-11 21:07:26 +01:00
Florian Bruhin
8a23b91134 Handle ImportError in version.opengl_vendor
Fixes #3698

(cherry picked from commit d9f7d401c6)
2018-03-11 21:07:21 +01:00
25 changed files with 258 additions and 30 deletions

View File

@@ -15,6 +15,29 @@ breaking changes (such as renamed commands) can happen in minor releases.
// `Fixed` for any bug fixes.
// `Security` to invite users to upgrade in case of vulnerabilities.
v1.2.1
------
Fixed
~~~~~
- qutebrowser now starts properly when the PyQt5 QOpenGLFunctions package wasn't
found.
- The keybinding cheatsheet on the quickstart page is now loaded from a local
`qute://` URL again.
- With "tox -e mkvenv-pypi", PyQt 5.10.0 is used again instead of Qt 5.10.1,
because of an issue with Qt 5.10.1 which causes qutebrowser to fail to start
("Could not find QtWebEngineProcess").
- Unbinding keys which were bound in older qutebrowser versions now doesn't
crash anymore.
- Fixed a crash when reloading a page which wasn't fully loaded with v1.2.0
- Keys on the numeric keypad now fall back to the same bindings without `Num+`
if no `Num+` binding was found.
- Fixed hinting on some pages with Qt < 5.10.
- Titles are now displayed correctly again for tabs which are cloned or loaded
from sessions.
- Shortcuts now correctly use `Ctrl` instead of `Command` on macOS again.
v1.2.0
------

View File

@@ -22,9 +22,9 @@ Basic keybindings to get you started
What to do now
--------------
* View the link:https://qutebrowser.org/img/cheatsheet-big.png[key binding cheatsheet]
* View the link:https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png[key binding cheatsheet]
to make yourself familiar with the key bindings: +
image:https://qutebrowser.org/img/cheatsheet-small.png["qutebrowser key binding cheatsheet",link="https://qutebrowser.org/img/cheatsheet-big.png"]
image:https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-small.png["qutebrowser key binding cheatsheet",link="https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png"]
* There's also a https://www.shortcutfoo.com/app/dojos/qutebrowser[free training
course] on shortcutfoo for the keybindings - note that you need to be in
insert mode (i) for it to work.

View File

@@ -0,0 +1,4 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt5==5.10 # rq.filter: != 5.10.1
sip==4.19.8

View File

@@ -0,0 +1,2 @@
PyQt5==5.10.0
#@ filter: PyQt5 != 5.10.1

View File

@@ -26,7 +26,7 @@ __copyright__ = "Copyright 2014-2018 Florian Bruhin (The Compiler)"
__license__ = "GPL"
__maintainer__ = __author__
__email__ = "mail@qutebrowser.org"
__version_info__ = (1, 2, 0)
__version_info__ = (1, 2, 1)
__version__ = '.'.join(str(e) for e in __version_info__)
__description__ = "A keyboard-driven, vim-like browser based on PyQt5."

View File

@@ -706,7 +706,6 @@ class WebEngineTab(browsertab.AbstractTab):
self._widget.shutdown()
def reload(self, *, force=False):
self.predicted_navigation.emit(self.url())
if force:
action = QWebEnginePage.ReloadAndBypassCache
else:
@@ -931,6 +930,7 @@ class WebEngineTab(browsertab.AbstractTab):
@pyqtSlot(QUrl)
def _on_predicted_navigation(self, url):
"""If we know we're going to visit an URL soon, change the settings."""
qtutils.ensure_valid(url)
self.settings.update_for_url(url)
@pyqtSlot(usertypes.NavigationRequest)

View File

@@ -701,7 +701,6 @@ class WebKitTab(browsertab.AbstractTab):
self._widget.shutdown()
def reload(self, *, force=False):
self.predicted_navigation.emit(self.url())
if force:
action = QWebPage.ReloadAndBypassCache
else:

View File

@@ -451,7 +451,7 @@ class List(BaseType):
def from_obj(self, value):
if value is None:
return []
return value
return [self.valtype.from_obj(v) for v in value]
def to_py(self, value):
self._basic_py_validation(value, list)
@@ -1199,7 +1199,9 @@ class Dict(BaseType):
def from_obj(self, value):
if value is None:
return {}
return value
return {self.keytype.from_obj(key): self.valtype.from_obj(val)
for key, val in value.items()}
def _fill_fixed_keys(self, value):
"""Fill missing fixed keys with a None-value."""
@@ -1648,6 +1650,10 @@ class Key(BaseType):
"""A name of a key."""
def from_obj(self, value):
"""Make sure key sequences are always normalized."""
return str(keyutils.KeySequence.parse(value))
def to_py(self, value):
self._basic_py_validation(value, str)
if not value:

View File

@@ -22,7 +22,7 @@
from PyQt5.QtGui import QFont
from qutebrowser.config import config, configutils
from qutebrowser.utils import log, usertypes, urlmatch
from qutebrowser.utils import log, usertypes, urlmatch, qtutils
from qutebrowser.misc import objects
UNSET = object()
@@ -141,6 +141,7 @@ class AbstractSettings:
Return:
A set of settings which actually changed.
"""
qtutils.ensure_valid(url)
changed_settings = set()
for values in config.instance:
if not values.opt.supports_pattern:

View File

@@ -74,9 +74,8 @@ window._qutebrowser.webelem = (function() {
try {
return elem.selectionStart;
} catch (err) {
if (err instanceof (frame
? frame.DOMException
: DOMException) &&
if ((err instanceof DOMException ||
(frame && err instanceof frame.DOMException)) &&
err.name === "InvalidStateError") {
// nothing to do, caret_position is already null
} else {

View File

@@ -114,7 +114,7 @@ class BaseKeyParser(QObject):
return (result, None)
def handle(self, e, *, dry_run=False):
def handle(self, e, *, dry_run=False): # noqa
"""Handle a new keypress.
Separate the keypress into count/command, then check if it matches
@@ -147,10 +147,18 @@ class BaseKeyParser(QObject):
return QKeySequence.NoMatch
# First, try a straightforward match
self._debug_log("Trying simple match")
match, binding = self._match_key(sequence)
# Then try without optional modifiers
if match == QKeySequence.NoMatch:
self._debug_log("Trying match without modifiers")
sequence = sequence.strip_modifiers()
match, binding = self._match_key(sequence)
# If that doesn't match, try a key_mapping
if match == QKeySequence.NoMatch:
self._debug_log("Trying match with key_mappings")
mapped = sequence.with_mappings(config.val.bindings.key_mappings)
if sequence != mapped:
self._debug_log("Mapped {} -> {}".format(
@@ -159,10 +167,12 @@ class BaseKeyParser(QObject):
sequence = mapped
# If that doesn't match either, try treating it as count.
txt = str(sequence[-1]) # To account for sequences changed above.
if (match == QKeySequence.NoMatch and
txt.isdigit() and
self._supports_count and
not (not self._count and txt == '0')):
self._debug_log("Trying match as count")
assert len(txt) == 1, txt
if not dry_run:
self._count += txt

View File

@@ -505,11 +505,29 @@ class KeySequence:
not ev.text().isupper()):
modifiers = Qt.KeyboardModifiers()
# On macOS, swap Ctrl and Meta back
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-51293
if utils.is_mac:
if modifiers & Qt.ControlModifier and modifiers & Qt.MetaModifier:
pass
elif modifiers & Qt.ControlModifier:
modifiers &= ~Qt.ControlModifier
modifiers |= Qt.MetaModifier
elif modifiers & Qt.MetaModifier:
modifiers &= ~Qt.MetaModifier
modifiers |= Qt.ControlModifier
keys = list(self._iter_keys())
keys.append(key | int(modifiers))
return self.__class__(*keys)
def strip_modifiers(self):
"""Strip optional modifiers from keys."""
modifiers = Qt.KeypadModifier
keys = [key & ~modifiers for key in self._iter_keys()]
return self.__class__(*keys)
def with_mappings(self, mappings):
"""Get a new KeySequence with the given mappings applied."""
keys = []

View File

@@ -453,7 +453,13 @@ def opengl_vendor(): # pragma: no cover
vp = QOpenGLVersionProfile()
vp.setVersion(2, 0)
vf = ctx.versionFunctions(vp)
try:
vf = ctx.versionFunctions(vp)
except ImportError as e:
log.init.debug("opengl_vendor: Importing version functions "
"failed: {}".format(e))
return None
if vf is None:
log.init.debug("opengl_vendor: Getting version functions failed!")
return None

View File

@@ -24,6 +24,7 @@
import os
import os.path
import sys
import time
import glob
import shutil
import plistlib
@@ -195,6 +196,7 @@ def build_mac():
'MacOS', 'qutebrowser')
smoke_test(binary)
finally:
time.sleep(5)
subprocess.run(['hdiutil', 'detach', tmpdir])
except PermissionError as e:
print("Failed to remove tempdir: {}".format(e))

View File

@@ -0,0 +1,13 @@
<html>
<!-- https://github.com/qutebrowser/qutebrowser/issues/3711 -->
<head>
<title>Issue 3711</title>
</head>
<body>
<!--
Verify no hint error occurs when hinting input range elements in iframes on qt5.9
Possibly an issue in chrome.
-->
<input min="0" max="1" step="0.001" type="range">
</body>
</html>

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Issue 3771 Parent Frame</title>
</head>
<body>
<iframe src="./issue3711.html"></iframe>
</body>
</html>

View File

@@ -249,6 +249,11 @@ Feature: Using hints
And I hint with args "all current" and follow a
Then no crash should happen
Scenario: No error when hinting ranged input in frames
When I open data/hints/issue3711_frame.html
And I hint with args "all current" and follow a
Then no crash should happen
### hints.auto_follow.timeout
@not_mac @flaky

View File

@@ -130,6 +130,7 @@ Feature: Javascript stuff
And I run :tab-next
Then the window sizes should be the same
@flaky
Scenario: Have a GreaseMonkey script run at page start
When I have a GreaseMonkey file saved for document-start with noframes unset
And I run :greasemonkey-reload

View File

@@ -379,6 +379,7 @@ def test_qute_settings_persistence(short_tmpdir, request, quteproc_new):
@pytest.mark.no_xvfb
@pytest.mark.no_ci
@pytest.mark.not_mac
def test_force_software_rendering(request, quteproc_new):
"""Make sure we can force software rendering with -s."""
if not request.config.webengine:

View File

@@ -193,11 +193,15 @@ def configdata_init():
@pytest.fixture
def config_stub(stubs, monkeypatch, configdata_init, config_tmpdir):
"""Fixture which provides a fake config object."""
yaml_config = configfiles.YamlConfig()
def yaml_config_stub(config_tmpdir):
"""Fixture which provides a YamlConfig object."""
return configfiles.YamlConfig()
conf = config.Config(yaml_config=yaml_config)
@pytest.fixture
def config_stub(stubs, monkeypatch, configdata_init, yaml_config_stub):
"""Fixture which provides a fake config object."""
conf = config.Config(yaml_config=yaml_config_stub)
monkeypatch.setattr(config, 'instance', conf)
container = config.ConfigContainer(conf)

View File

@@ -339,6 +339,24 @@ class TestKeyConfig:
key_config_stub.unbind(seq)
assert key_config_stub.get_command(seq, mode='normal') is None
def test_unbind_old_syntax(self, yaml_config_stub, key_config_stub,
config_stub):
"""Test unbinding bindings added before the keybinding refactoring.
We used to normalize keys differently, so we can have <ctrl+q> in the
config.
See https://github.com/qutebrowser/qutebrowser/issues/3699
"""
bindings = {'normal': {'<ctrl+q>': 'nop'}}
yaml_config_stub.set_obj('bindings.commands', bindings)
config_stub.read_yaml()
key_config_stub.unbind(keyutils.KeySequence.parse('<ctrl+q>'),
save_yaml=True)
assert config.instance.get_obj('bindings.commands') == {'normal': {}}
def test_empty_command(self, key_config_stub):
"""Try binding a key to an empty command."""
message = "Can't add binding 'x' with empty command in normal mode"

View File

@@ -533,6 +533,17 @@ class FlagListSubclass(configtypes.FlagList):
'foo', 'bar', 'baz')
class FromObjType(configtypes.BaseType):
"""Config type to test from_obj for List/Dict."""
def from_obj(self, value):
return int(value)
def to_py(self, value):
return value
class TestList:
"""Test List and FlagList."""
@@ -647,6 +658,12 @@ class TestList:
with pytest.raises(AssertionError):
typ.to_doc([['foo']])
def test_from_obj_sub(self):
"""Make sure the list calls from_obj() on sub-types."""
typ = configtypes.List(valtype=FromObjType())
value = typ.from_obj(['1', '2'])
assert value == [1, 2]
class TestFlagList:
@@ -1665,6 +1682,13 @@ class TestDict:
print(doc)
assert doc == expected
def test_from_obj_sub(self):
"""Make sure the dict calls from_obj() on sub-types."""
typ = configtypes.Dict(keytype=configtypes.String(),
valtype=FromObjType())
value = typ.from_obj({'1': '2'})
assert value == {'1': 2}
def unrequired_class(**kwargs):
return configtypes.File(required=False, **kwargs)

View File

@@ -25,6 +25,7 @@ from PyQt5.QtCore import Qt
import pytest
from qutebrowser.keyinput import basekeyparser, keyutils
from qutebrowser.utils import utils
# Alias because we need this a lot in here.
@@ -153,14 +154,16 @@ class TestHandle:
keyparser._read_config('prompt')
def test_valid_key(self, fake_keyevent, keyparser):
keyparser.handle(fake_keyevent(Qt.Key_A, Qt.ControlModifier))
keyparser.handle(fake_keyevent(Qt.Key_X, Qt.ControlModifier))
modifier = Qt.MetaModifier if utils.is_mac else Qt.ControlModifier
keyparser.handle(fake_keyevent(Qt.Key_A, modifier))
keyparser.handle(fake_keyevent(Qt.Key_X, modifier))
keyparser.execute.assert_called_once_with('message-info ctrla', None)
assert not keyparser._sequence
def test_valid_key_count(self, fake_keyevent, keyparser):
modifier = Qt.MetaModifier if utils.is_mac else Qt.ControlModifier
keyparser.handle(fake_keyevent(Qt.Key_5))
keyparser.handle(fake_keyevent(Qt.Key_A, Qt.ControlModifier))
keyparser.handle(fake_keyevent(Qt.Key_A, modifier))
keyparser.execute.assert_called_once_with('message-info ctrla', 5)
@pytest.mark.parametrize('keys', [
@@ -198,13 +201,34 @@ class TestHandle:
keyparser.execute.assert_called_with('message-info ba', None)
assert not keyparser._sequence
@pytest.mark.parametrize('key, number', [(Qt.Key_0, 0), (Qt.Key_1, 1)])
def test_number_press(self, handle_text, keyparser, key, number):
handle_text(key)
@pytest.mark.parametrize('key, modifiers, number', [
(Qt.Key_0, Qt.NoModifier, 0),
(Qt.Key_1, Qt.NoModifier, 1),
(Qt.Key_1, Qt.KeypadModifier, 1),
])
def test_number_press(self, fake_keyevent, keyparser,
key, modifiers, number):
keyparser.handle(fake_keyevent(key, modifiers))
command = 'message-info {}'.format(number)
keyparser.execute.assert_called_once_with(command, None)
assert not keyparser._sequence
@pytest.mark.parametrize('modifiers, text', [
(Qt.NoModifier, '2'),
(Qt.KeypadModifier, 'num-2'),
])
def test_number_press_keypad(self, fake_keyevent, keyparser, config_stub,
modifiers, text):
"""Make sure a <Num+2> binding overrides the 2 binding."""
config_stub.val.bindings.commands = {'normal': {
'2': 'message-info 2',
'<Num+2>': 'message-info num-2'}}
keyparser._read_config('normal')
keyparser.handle(fake_keyevent(Qt.Key_2, modifiers))
command = 'message-info {}'.format(text)
keyparser.execute.assert_called_once_with(command, None)
assert not keyparser._sequence
def test_umlauts(self, handle_text, keyparser, config_stub):
config_stub.val.bindings.commands = {'normal': {'ü': 'message-info ü'}}
keyparser._read_config('normal')
@@ -215,6 +239,15 @@ class TestHandle:
handle_text(Qt.Key_X)
keyparser.execute.assert_called_once_with('message-info a', None)
def test_mapping_keypad(self, config_stub, fake_keyevent, keyparser):
"""Make sure falling back to non-numpad keys works with mappings."""
config_stub.val.bindings.commands = {'normal': {'a': 'nop'}}
config_stub.val.bindings.key_mappings = {'1': 'a'}
keyparser._read_config('normal')
keyparser.handle(fake_keyevent(Qt.Key_1, Qt.KeypadModifier))
keyparser.execute.assert_called_once_with('nop', None)
def test_binding_and_mapping(self, config_stub, handle_text, keyparser):
"""with a conflicting binding/mapping, the binding should win."""
handle_text(Qt.Key_B)
@@ -296,6 +329,15 @@ class TestCount:
assert sig1.args == ('4',)
assert sig2.args == ('42',)
def test_numpad(self, fake_keyevent, keyparser):
"""Make sure we can enter a count via numpad."""
for key, modifiers in [(Qt.Key_4, Qt.KeypadModifier),
(Qt.Key_2, Qt.KeypadModifier),
(Qt.Key_B, Qt.NoModifier),
(Qt.Key_A, Qt.NoModifier)]:
keyparser.handle(fake_keyevent(key, modifiers))
keyparser.execute.assert_called_once_with('message-info ba', 42)
def test_clear_keystring(qtbot, keyparser):
"""Test that the keystring is cleared and the signal is emitted."""

View File

@@ -28,6 +28,7 @@ from PyQt5.QtWidgets import QWidget
from tests.unit.keyinput import key_data
from qutebrowser.keyinput import keyutils
from qutebrowser.utils import utils
@pytest.fixture(params=key_data.KEYS, ids=lambda k: k.attribute)
@@ -346,20 +347,28 @@ class TestKeySequence:
@pytest.mark.parametrize('old, key, modifiers, text, expected', [
('a', Qt.Key_B, Qt.NoModifier, 'b', 'ab'),
('a', Qt.Key_B, Qt.ShiftModifier, 'B', 'aB'),
('a', Qt.Key_B, Qt.ControlModifier | Qt.ShiftModifier, 'B',
'a<Ctrl+Shift+b>'),
('a', Qt.Key_B, Qt.AltModifier | Qt.ShiftModifier, 'B',
'a<Alt+Shift+b>'),
# Modifier stripping with symbols
('', Qt.Key_Colon, Qt.NoModifier, ':', ':'),
('', Qt.Key_Colon, Qt.ShiftModifier, ':', ':'),
('', Qt.Key_Colon, Qt.ControlModifier | Qt.ShiftModifier, ':',
'<Ctrl+Shift+:>'),
('', Qt.Key_Colon, Qt.AltModifier | Qt.ShiftModifier, ':',
'<Alt+Shift+:>'),
# Swapping Control/Meta on macOS
('', Qt.Key_A, Qt.ControlModifier, '',
'<Meta+A>' if utils.is_mac else '<Ctrl+A>'),
('', Qt.Key_A, Qt.ControlModifier | Qt.ShiftModifier, '',
'<Meta+Shift+A>' if utils.is_mac else '<Ctrl+Shift+A>'),
('', Qt.Key_A, Qt.MetaModifier, '',
'<Ctrl+A>' if utils.is_mac else '<Meta+A>'),
# Handling of Backtab
('', Qt.Key_Backtab, Qt.NoModifier, '', '<Backtab>'),
('', Qt.Key_Backtab, Qt.ShiftModifier, '', '<Shift+Tab>'),
('', Qt.Key_Backtab, Qt.ControlModifier | Qt.ShiftModifier, '',
'<Control+Shift+Tab>'),
('', Qt.Key_Backtab, Qt.AltModifier | Qt.ShiftModifier, '',
'<Alt+Shift+Tab>'),
# Stripping of Qt.GroupSwitchModifier
('', Qt.Key_A, Qt.GroupSwitchModifier, 'a', 'a'),
@@ -370,6 +379,27 @@ class TestKeySequence:
new = seq.append_event(event)
assert new == keyutils.KeySequence.parse(expected)
@pytest.mark.fake_os('mac')
@pytest.mark.parametrize('modifiers, expected', [
(Qt.ControlModifier,
Qt.MetaModifier),
(Qt.MetaModifier,
Qt.ControlModifier),
(Qt.ControlModifier | Qt.MetaModifier,
Qt.ControlModifier | Qt.MetaModifier),
(Qt.ControlModifier | Qt.ShiftModifier,
Qt.MetaModifier | Qt.ShiftModifier),
(Qt.MetaModifier | Qt.ShiftModifier,
Qt.ControlModifier | Qt.ShiftModifier),
(Qt.ShiftModifier, Qt.ShiftModifier),
])
def test_fake_mac(self, fake_keyevent, modifiers, expected):
"""Make sure Control/Meta are swapped with a simulated Mac."""
seq = keyutils.KeySequence()
event = fake_keyevent(key=Qt.Key_A, modifiers=modifiers)
new = seq.append_event(event)
assert new[0] == keyutils.KeyInfo(Qt.Key_A, expected)
@pytest.mark.parametrize('key', [Qt.Key_unknown, 0x0])
def test_append_event_invalid(self, key):
seq = keyutils.KeySequence()
@@ -377,6 +407,15 @@ class TestKeySequence:
with pytest.raises(keyutils.KeyParseError):
seq.append_event(event)
def test_strip_modifiers(self):
seq = keyutils.KeySequence(Qt.Key_0,
Qt.Key_1 | Qt.KeypadModifier,
Qt.Key_A | Qt.ControlModifier)
expected = keyutils.KeySequence(Qt.Key_0,
Qt.Key_1,
Qt.Key_A | Qt.ControlModifier)
assert seq.strip_modifiers() == expected
def test_with_mappings(self):
seq = keyutils.KeySequence.parse('foobar')
mappings = {keyutils.KeySequence('b'): keyutils.KeySequence('t')}

View File

@@ -59,7 +59,7 @@ commands = {envpython} -c ""
usedevelop = true
deps =
-r{toxinidir}/requirements.txt
-r{toxinidir}/misc/requirements/requirements-pyqt.txt
-r{toxinidir}/misc/requirements/requirements-pyqt-old.txt
[testenv:misc]
ignore_errors = true