Merge branch 'webengine-createwindow'
This commit is contained in:
commit
050507c0a6
@ -12,6 +12,9 @@ matrix:
|
||||
- os: linux
|
||||
env: DOCKER=archlinux
|
||||
services: docker
|
||||
- os: linux
|
||||
env: DOCKER=archlinux QUTE_BDD_WEBENGINE=true
|
||||
services: docker
|
||||
- os: linux
|
||||
env: DOCKER=ubuntu-xenial
|
||||
services: docker
|
||||
|
@ -107,6 +107,8 @@ Changed
|
||||
- Lots of improvements to and bugfixes for the QtWebEngine backend, such as
|
||||
working hints. However, using qutebrowser directly from git is still advised
|
||||
when using `--backend webengine`.
|
||||
- `content -> javascript-can-open-windows` got renamed to
|
||||
`javascript-can-open-windows-automatically`.
|
||||
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
@ -157,7 +157,7 @@
|
||||
|<<content-hyperlink-auditing,hyperlink-auditing>>|Enable or disable hyperlink auditing (<a ping>).
|
||||
|<<content-geolocation,geolocation>>|Allow websites to request geolocations.
|
||||
|<<content-notifications,notifications>>|Allow websites to show notifications.
|
||||
|<<content-javascript-can-open-windows,javascript-can-open-windows>>|Whether JavaScript programs can open new windows.
|
||||
|<<content-javascript-can-open-windows-automatically,javascript-can-open-windows-automatically>>|Whether JavaScript programs can open new windows without user interaction.
|
||||
|<<content-javascript-can-close-windows,javascript-can-close-windows>>|Whether JavaScript programs can close windows.
|
||||
|<<content-javascript-can-access-clipboard,javascript-can-access-clipboard>>|Whether JavaScript programs can read or write to the clipboard.
|
||||
|<<content-ignore-javascript-prompt,ignore-javascript-prompt>>|Whether all javascript prompts should be ignored.
|
||||
@ -1427,9 +1427,9 @@ Valid values:
|
||||
|
||||
Default: +pass:[ask]+
|
||||
|
||||
[[content-javascript-can-open-windows]]
|
||||
=== javascript-can-open-windows
|
||||
Whether JavaScript programs can open new windows.
|
||||
[[content-javascript-can-open-windows-automatically]]
|
||||
=== javascript-can-open-windows-automatically
|
||||
Whether JavaScript programs can open new windows without user interaction.
|
||||
|
||||
Valid values:
|
||||
|
||||
|
@ -17,6 +17,8 @@ markers =
|
||||
flaky_once: Try to rerun this test once if it fails
|
||||
qtwebengine_todo: Features still missing with QtWebEngine
|
||||
qtwebengine_skip: Tests not applicable with QtWebEngine
|
||||
qtwebkit_skip: Tests not applicable with QtWebKit
|
||||
qtwebengine_createWindow: Tests using createWindow with QtWebEngine (QTBUG-54419)
|
||||
qt_log_level_fail = WARNING
|
||||
qt_log_ignore =
|
||||
^SpellCheck: .*
|
||||
|
@ -112,7 +112,7 @@ MAPPINGS = {
|
||||
Attribute(QWebEngineSettings.AutoLoadImages),
|
||||
'allow-javascript':
|
||||
Attribute(QWebEngineSettings.JavascriptEnabled),
|
||||
'javascript-can-open-windows':
|
||||
'javascript-can-open-windows-automatically':
|
||||
Attribute(QWebEngineSettings.JavascriptCanOpenWindows),
|
||||
'javascript-can-access-clipboard':
|
||||
Attribute(QWebEngineSettings.JavascriptCanAccessClipboard),
|
||||
|
@ -381,7 +381,7 @@ class WebEngineTab(browsertab.AbstractTab):
|
||||
|
||||
def __init__(self, win_id, mode_manager, parent=None):
|
||||
super().__init__(win_id)
|
||||
widget = webview.WebEngineView(tabdata=self.data)
|
||||
widget = webview.WebEngineView(tabdata=self.data, win_id=win_id)
|
||||
self.history = WebEngineHistory(self)
|
||||
self.scroller = WebEngineScroller(self, parent=self)
|
||||
self.caret = WebEngineCaret(win_id=win_id, mode_manager=mode_manager,
|
||||
@ -406,9 +406,9 @@ class WebEngineTab(browsertab.AbstractTab):
|
||||
])
|
||||
script = QWebEngineScript()
|
||||
script.setInjectionPoint(QWebEngineScript.DocumentCreation)
|
||||
page = self._widget.page()
|
||||
script.setSourceCode(js_code)
|
||||
|
||||
page = self._widget.page()
|
||||
try:
|
||||
page.runJavaScript("", QWebEngineScript.ApplicationWorld)
|
||||
except TypeError:
|
||||
@ -504,6 +504,11 @@ class WebEngineTab(browsertab.AbstractTab):
|
||||
if title == title_url.toDisplayString(QUrl.RemoveScheme).strip('/'):
|
||||
title = ""
|
||||
|
||||
# Don't add history entry if the URL is invalid anyways
|
||||
if not url.isValid():
|
||||
log.misc.debug("Ignoring invalid URL being added to history")
|
||||
return
|
||||
|
||||
self.add_history_item.emit(url, requested_url, title)
|
||||
|
||||
def _connect_signals(self):
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
"""The main browser widget for QtWebEngine."""
|
||||
|
||||
import os
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, QUrl
|
||||
# pylint: disable=no-name-in-module,import-error,useless-suppression
|
||||
@ -26,17 +27,73 @@ from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage
|
||||
# pylint: enable=no-name-in-module,import-error,useless-suppression
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import log, debug, usertypes
|
||||
from qutebrowser.utils import log, debug, usertypes, objreg, qtutils, message
|
||||
|
||||
|
||||
class WebEngineView(QWebEngineView):
|
||||
|
||||
"""Custom QWebEngineView subclass with qutebrowser-specific features."""
|
||||
|
||||
def __init__(self, tabdata, parent=None):
|
||||
def __init__(self, tabdata, win_id, parent=None):
|
||||
super().__init__(parent)
|
||||
self._win_id = win_id
|
||||
self.setPage(WebEnginePage(tabdata, parent=self))
|
||||
|
||||
def createWindow(self, wintype):
|
||||
"""Called by Qt when a page wants to create a new window.
|
||||
|
||||
This function is called from the createWindow() method of the
|
||||
associated QWebEnginePage, each time the page wants to create a new
|
||||
window of the given type. This might be the result, for example, of a
|
||||
JavaScript request to open a document in a new window.
|
||||
|
||||
Args:
|
||||
wintype: This enum describes the types of window that can be
|
||||
created by the createWindow() function.
|
||||
|
||||
QWebEnginePage::WebBrowserWindow:
|
||||
A complete web browser window.
|
||||
QWebEnginePage::WebBrowserTab:
|
||||
A web browser tab.
|
||||
QWebEnginePage::WebDialog:
|
||||
A window without decoration.
|
||||
QWebEnginePage::WebBrowserBackgroundTab:
|
||||
A web browser tab without hiding the current visible
|
||||
WebEngineView. (Added in Qt 5.7)
|
||||
|
||||
Return:
|
||||
The new QWebEngineView object.
|
||||
"""
|
||||
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-54419
|
||||
vercheck = qtutils.version_check
|
||||
qtbug_54419_fixed = ((vercheck('5.6.2') and not vercheck('5.7.0')) or
|
||||
qtutils.version_check('5.7.1') or
|
||||
os.environ.get('QUTE_QTBUG54419_PATCHED', ''))
|
||||
if not qtbug_54419_fixed:
|
||||
message.error(self._win_id, "Qt 5.6.2/5.7.1 or newer is required "
|
||||
"to open new tabs via JS!")
|
||||
return None
|
||||
|
||||
debug_type = debug.qenum_key(QWebEnginePage, wintype)
|
||||
log.webview.debug("createWindow with type {}".format(debug_type))
|
||||
background = False
|
||||
if wintype in [QWebEnginePage.WebBrowserWindow,
|
||||
QWebEnginePage.WebDialog]:
|
||||
log.webview.warning("{} requested, but we don't support "
|
||||
"that!".format(debug_type))
|
||||
elif wintype == QWebEnginePage.WebBrowserTab:
|
||||
pass
|
||||
elif (hasattr(QWebEnginePage, 'WebBrowserBackgroundTab') and
|
||||
wintype == QWebEnginePage.WebBrowserBackgroundTab):
|
||||
background = True
|
||||
else:
|
||||
raise ValueError("Invalid wintype {}".format(debug_type))
|
||||
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window=self._win_id)
|
||||
# pylint: disable=protected-access
|
||||
return tabbed_browser.tabopen(background=background)._widget
|
||||
|
||||
|
||||
class WebEnginePage(QWebEnginePage):
|
||||
|
||||
@ -74,11 +131,6 @@ class WebEnginePage(QWebEnginePage):
|
||||
logger = level_to_logger[level]
|
||||
logger(logstring)
|
||||
|
||||
def createWindow(self, _typ):
|
||||
"""Handle new windows via JS."""
|
||||
log.stub()
|
||||
return None
|
||||
|
||||
def acceptNavigationRequest(self,
|
||||
url: QUrl,
|
||||
typ: QWebEnginePage.NavigationType,
|
||||
|
@ -118,7 +118,7 @@ MAPPINGS = {
|
||||
Attribute(QWebSettings.AutoLoadImages),
|
||||
'allow-javascript':
|
||||
Attribute(QWebSettings.JavascriptEnabled),
|
||||
'javascript-can-open-windows':
|
||||
'javascript-can-open-windows-automatically':
|
||||
Attribute(QWebSettings.JavascriptCanOpenWindows),
|
||||
'javascript-can-close-windows':
|
||||
Attribute(QWebSettings.JavascriptCanCloseWindows),
|
||||
|
@ -29,7 +29,7 @@ from PyQt5.QtWebKitWidgets import QWebView, QWebPage, QWebFrame
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.keyinput import modeman
|
||||
from qutebrowser.utils import log, usertypes, utils, qtutils, objreg
|
||||
from qutebrowser.utils import log, usertypes, utils, qtutils, objreg, debug
|
||||
from qutebrowser.browser.webkit import webpage, webkitelem
|
||||
|
||||
|
||||
@ -218,6 +218,8 @@ class WebView(QWebView):
|
||||
Return:
|
||||
The new QWebView object.
|
||||
"""
|
||||
debug_type = debug.qenum_key(QWebPage, wintype)
|
||||
log.webview.debug("createWindow with type {}".format(debug_type))
|
||||
if wintype == QWebPage.WebModalDialog:
|
||||
log.webview.warning("WebModalDialog requested, but we don't "
|
||||
"support that!")
|
||||
|
@ -385,6 +385,8 @@ class ConfigManager(QObject):
|
||||
('completion', 'history-length'): 'cmd-history-max-items',
|
||||
('colors', 'downloads.fg'): 'downloads.fg.start',
|
||||
('ui', 'show-keyhints'): 'keyhint-blacklist',
|
||||
('content', 'javascript-can-open-windows'):
|
||||
'javascript-can-open-windows-automatically',
|
||||
}
|
||||
DELETED_OPTIONS = [
|
||||
('colors', 'tab.separator'),
|
||||
|
@ -808,9 +808,10 @@ def data(readonly=False):
|
||||
SettingValue(typ.BoolAsk(), 'ask'),
|
||||
"Allow websites to show notifications."),
|
||||
|
||||
('javascript-can-open-windows',
|
||||
('javascript-can-open-windows-automatically',
|
||||
SettingValue(typ.Bool(), 'false'),
|
||||
"Whether JavaScript programs can open new windows."),
|
||||
"Whether JavaScript programs can open new windows without user "
|
||||
"interaction."),
|
||||
|
||||
('javascript-can-close-windows',
|
||||
SettingValue(typ.Bool(), 'false',
|
||||
|
@ -99,7 +99,7 @@ def get_fatal_crash_dialog(debug, data):
|
||||
def _get_environment_vars():
|
||||
"""Gather environment variables for the crash info."""
|
||||
masks = ('DESKTOP_SESSION', 'DE', 'QT_*', 'PYTHON*', 'LC_*', 'LANG',
|
||||
'XDG_*')
|
||||
'XDG_*', 'QUTE_*')
|
||||
info = []
|
||||
for key, value in os.environ.items():
|
||||
for m in masks:
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ $DOCKER ]]; then
|
||||
docker run --privileged -v $PWD:/outside qutebrowser/travis:$DOCKER
|
||||
docker run --privileged -v $PWD:/outside -e QUTE_BDD_WEBENGINE=$QUTE_BDD_WEBENGINE qutebrowser/travis:$DOCKER
|
||||
else
|
||||
args=()
|
||||
[[ $TESTENV == docs ]] && args=('--no-authors')
|
||||
|
@ -21,11 +21,9 @@
|
||||
|
||||
"""The qutebrowser test suite conftest file."""
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
import operator
|
||||
|
||||
import pytest
|
||||
import hypothesis
|
||||
@ -37,10 +35,6 @@ from helpers.logfail import fail_on_logging
|
||||
from helpers.messagemock import message_mock
|
||||
from helpers.fixtures import * # pylint: disable=wildcard-import
|
||||
|
||||
from PyQt5.QtCore import PYQT_VERSION
|
||||
|
||||
from qutebrowser.utils import qtutils
|
||||
|
||||
|
||||
# Set hypothesis settings
|
||||
hypothesis.settings.register_profile('default',
|
||||
@ -78,7 +72,7 @@ def _apply_platform_markers(item):
|
||||
item.add_marker(skipif_marker)
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(items):
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
"""Handle custom markers.
|
||||
|
||||
pytest hook called after collection has been performed.
|
||||
@ -101,7 +95,12 @@ def pytest_collection_modifyitems(items):
|
||||
Reference:
|
||||
http://pytest.org/latest/plugins.html
|
||||
"""
|
||||
remaining_items = []
|
||||
deselected_items = []
|
||||
|
||||
for item in items:
|
||||
deselected = False
|
||||
|
||||
if 'qapp' in getattr(item, 'fixturenames', ()):
|
||||
item.add_marker('gui')
|
||||
|
||||
@ -110,9 +109,13 @@ def pytest_collection_modifyitems(items):
|
||||
item.module.__file__,
|
||||
os.path.commonprefix([__file__, item.module.__file__]))
|
||||
|
||||
module_root_dir = os.path.split(module_path)[0]
|
||||
module_root_dir = module_path.split(os.sep)[0]
|
||||
assert module_root_dir in ['end2end', 'unit', 'helpers',
|
||||
'test_conftest.py']
|
||||
if module_root_dir == 'end2end':
|
||||
item.add_marker(pytest.mark.end2end)
|
||||
elif os.environ.get('QUTE_BDD_WEBENGINE', ''):
|
||||
deselected = True
|
||||
|
||||
_apply_platform_markers(item)
|
||||
if item.get_marker('xfail_norun'):
|
||||
@ -120,6 +123,14 @@ def pytest_collection_modifyitems(items):
|
||||
if item.get_marker('flaky_once'):
|
||||
item.add_marker(pytest.mark.flaky(reruns=1))
|
||||
|
||||
if deselected:
|
||||
deselected_items.append(item)
|
||||
else:
|
||||
remaining_items.append(item)
|
||||
|
||||
config.hook.pytest_deselected(items=deselected_items)
|
||||
items[:] = remaining_items
|
||||
|
||||
|
||||
def pytest_ignore_collect(path):
|
||||
"""Ignore BDD tests if we're unable to run them."""
|
||||
@ -144,6 +155,17 @@ def pytest_addoption(parser):
|
||||
help='Use QtWebEngine for BDD tests')
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
webengine_arg = config.getoption('--qute-bdd-webengine')
|
||||
webengine_env = os.environ.get('QUTE_BDD_WEBENGINE', '')
|
||||
config.webengine = bool(webengine_arg or webengine_env)
|
||||
# Fail early if QtWebEngine is not available
|
||||
# pylint: disable=no-name-in-module,unused-variable,useless-suppression
|
||||
if config.webengine:
|
||||
import PyQt5.QtWebEngineWidgets
|
||||
# pylint: enable=no-name-in-module,unused-variable,useless-suppression
|
||||
|
||||
|
||||
@pytest.fixture(scope='session', autouse=True)
|
||||
def check_display(request):
|
||||
if (not request.config.getoption('--no-xvfb') and
|
||||
@ -182,71 +204,3 @@ def pytest_sessionfinish(exitstatus):
|
||||
status_file = os.path.join(cache_dir, 'pytest_status')
|
||||
with open(status_file, 'w', encoding='ascii') as f:
|
||||
f.write(str(exitstatus))
|
||||
|
||||
|
||||
if not getattr(sys, 'frozen', False):
|
||||
def _get_version_tag(tag):
|
||||
"""Handle tags like pyqt>=5.3.1 for BDD tests.
|
||||
|
||||
This transforms e.g. pyqt>=5.3.1 into an appropriate @pytest.mark.skip
|
||||
marker, and falls back to pytest-bdd's implementation for all other
|
||||
casesinto an appropriate @pytest.mark.skip marker, and falls back to
|
||||
"""
|
||||
version_re = re.compile(r"""
|
||||
(?P<package>qt|pyqt)
|
||||
(?P<operator>==|>|>=|<|<=|!=)
|
||||
(?P<version>\d+\.\d+\.\d+)
|
||||
""", re.VERBOSE)
|
||||
|
||||
match = version_re.match(tag)
|
||||
if not match:
|
||||
return None
|
||||
|
||||
operators = {
|
||||
'==': operator.eq,
|
||||
'>': operator.gt,
|
||||
'<': operator.lt,
|
||||
'>=': operator.ge,
|
||||
'<=': operator.le,
|
||||
'!=': operator.ne,
|
||||
}
|
||||
|
||||
package = match.group('package')
|
||||
op = operators[match.group('operator')]
|
||||
version = match.group('version')
|
||||
|
||||
if package == 'qt':
|
||||
return pytest.mark.skipif(qtutils.version_check(version, op),
|
||||
reason='Needs ' + tag)
|
||||
elif package == 'pyqt':
|
||||
major, minor, patch = [int(e) for e in version.split('.')]
|
||||
hex_version = (major << 16) | (minor << 8) | patch
|
||||
return pytest.mark.skipif(not op(PYQT_VERSION, hex_version),
|
||||
reason='Needs ' + tag)
|
||||
else:
|
||||
raise ValueError("Invalid package {!r}".format(package))
|
||||
|
||||
def _get_qtwebengine_tag(tag):
|
||||
"""Handle a @qtwebengine_* tag."""
|
||||
pytest_marks = {
|
||||
'qtwebengine_todo': pytest.mark.qtwebengine_todo,
|
||||
'qtwebengine_skip': pytest.mark.qtwebengine_skip,
|
||||
}
|
||||
if not any(tag.startswith(t + ':') for t in pytest_marks):
|
||||
return None
|
||||
name, desc = tag.split(':', maxsplit=1)
|
||||
return pytest_marks[name](desc)
|
||||
|
||||
def pytest_bdd_apply_tag(tag, function):
|
||||
"""Handle custom tags for BDD tests.
|
||||
|
||||
This tries various functions, and if none knows how to handle this tag,
|
||||
it returns None so it falls back to pytest-bdd's implementation.
|
||||
"""
|
||||
funcs = [_get_version_tag, _get_qtwebengine_tag]
|
||||
for func in funcs:
|
||||
mark = func(tag)
|
||||
if mark is not None:
|
||||
mark(function)
|
||||
return True
|
||||
return None
|
||||
|
@ -21,11 +21,16 @@
|
||||
|
||||
"""Things needed for end2end testing."""
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import pstats
|
||||
import os.path
|
||||
import operator
|
||||
|
||||
import pytest
|
||||
from PyQt5.QtCore import PYQT_VERSION
|
||||
|
||||
pytest.register_assert_rewrite('end2end.fixtures')
|
||||
|
||||
@ -33,6 +38,7 @@ from end2end.fixtures.webserver import httpbin, httpbin_after_test, ssl_server
|
||||
from end2end.fixtures.quteprocess import (quteproc_process, quteproc,
|
||||
quteproc_new)
|
||||
from end2end.fixtures.testprocess import pytest_runtest_makereport
|
||||
from qutebrowser.utils import qtutils
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
@ -51,3 +57,104 @@ def pytest_unconfigure(config):
|
||||
for fn in os.listdir('prof'):
|
||||
stats.add(os.path.join('prof', fn))
|
||||
stats.dump_stats(os.path.join('prof', 'combined.pstats'))
|
||||
|
||||
|
||||
def _get_version_tag(tag):
|
||||
"""Handle tags like pyqt>=5.3.1 for BDD tests.
|
||||
|
||||
This transforms e.g. pyqt>=5.3.1 into an appropriate @pytest.mark.skip
|
||||
marker, and falls back to pytest-bdd's implementation for all other
|
||||
casesinto an appropriate @pytest.mark.skip marker, and falls back to
|
||||
"""
|
||||
version_re = re.compile(r"""
|
||||
(?P<package>qt|pyqt)
|
||||
(?P<operator>==|>|>=|<|<=|!=)
|
||||
(?P<version>\d+\.\d+\.\d+)
|
||||
""", re.VERBOSE)
|
||||
|
||||
match = version_re.match(tag)
|
||||
if not match:
|
||||
return None
|
||||
|
||||
operators = {
|
||||
'==': operator.eq,
|
||||
'>': operator.gt,
|
||||
'<': operator.lt,
|
||||
'>=': operator.ge,
|
||||
'<=': operator.le,
|
||||
'!=': operator.ne,
|
||||
}
|
||||
|
||||
package = match.group('package')
|
||||
op = operators[match.group('operator')]
|
||||
version = match.group('version')
|
||||
|
||||
if package == 'qt':
|
||||
return pytest.mark.skipif(qtutils.version_check(version, op),
|
||||
reason='Needs ' + tag)
|
||||
elif package == 'pyqt':
|
||||
major, minor, patch = [int(e) for e in version.split('.')]
|
||||
hex_version = (major << 16) | (minor << 8) | patch
|
||||
return pytest.mark.skipif(not op(PYQT_VERSION, hex_version),
|
||||
reason='Needs ' + tag)
|
||||
else:
|
||||
raise ValueError("Invalid package {!r}".format(package))
|
||||
|
||||
|
||||
def _get_backend_tag(tag):
|
||||
"""Handle a @qtwebengine_*/@qtwebkit_skip tag."""
|
||||
pytest_marks = {
|
||||
'qtwebengine_todo': pytest.mark.qtwebengine_todo,
|
||||
'qtwebengine_skip': pytest.mark.qtwebengine_skip,
|
||||
'qtwebkit_skip': pytest.mark.qtwebkit_skip
|
||||
}
|
||||
if not any(tag.startswith(t + ':') for t in pytest_marks):
|
||||
return None
|
||||
name, desc = tag.split(':', maxsplit=1)
|
||||
return pytest_marks[name](desc)
|
||||
|
||||
|
||||
if not getattr(sys, 'frozen', False):
|
||||
def pytest_bdd_apply_tag(tag, function):
|
||||
"""Handle custom tags for BDD tests.
|
||||
|
||||
This tries various functions, and if none knows how to handle this tag,
|
||||
it returns None so it falls back to pytest-bdd's implementation.
|
||||
"""
|
||||
funcs = [_get_version_tag, _get_backend_tag]
|
||||
for func in funcs:
|
||||
mark = func(tag)
|
||||
if mark is not None:
|
||||
mark(function)
|
||||
return True
|
||||
return None
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
"""Apply @qtwebengine_* markers; skip unittests with QUTE_BDD_WEBENGINE."""
|
||||
vercheck = qtutils.version_check
|
||||
qtbug_54419_fixed = ((vercheck('5.6.2') and not vercheck('5.7.0')) or
|
||||
qtutils.version_check('5.7.1') or
|
||||
os.environ.get('QUTE_QTBUG54419_PATCHED', ''))
|
||||
|
||||
markers = [
|
||||
('qtwebengine_createWindow', 'Skipped because of QTBUG-54419',
|
||||
pytest.mark.skipif, not qtbug_54419_fixed and config.webengine),
|
||||
('qtwebengine_todo', 'QtWebEngine TODO', pytest.mark.xfail,
|
||||
config.webengine),
|
||||
('qtwebengine_skip', 'Skipped with QtWebEngine', pytest.mark.skipif,
|
||||
config.webengine),
|
||||
('qtwebkit_skip', 'Skipped with QtWebKit', pytest.mark.skipif,
|
||||
not config.webengine),
|
||||
]
|
||||
|
||||
for item in items:
|
||||
for name, prefix, pytest_mark, condition in markers:
|
||||
marker = item.get_marker(name)
|
||||
if marker and condition:
|
||||
if marker.args:
|
||||
text = '{}: {}'.format(prefix, marker.args[0])
|
||||
else:
|
||||
text = prefix
|
||||
item.add_marker(pytest_mark(condition, reason=text,
|
||||
**marker.kwargs))
|
||||
|
@ -1,22 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<button onclick="openWin()" id="open-button">Open "myWindow"</button>
|
||||
<button onclick="closeWin()" id="close-button">Close "myWindow"</button>
|
||||
|
||||
<script>
|
||||
var myWindow;
|
||||
|
||||
function openWin() {
|
||||
myWindow = window.open("", "myWindow", "width=200, height=100");
|
||||
}
|
||||
|
||||
function closeWin() {
|
||||
myWindow.close();
|
||||
myWindow.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
44
tests/end2end/data/javascript/window_open.html
Normal file
44
tests/end2end/data/javascript/window_open.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<script type="text/javascript">
|
||||
var my_window;
|
||||
|
||||
function open_modal() {
|
||||
if (window.showModalDialog) {
|
||||
window.showModalDialog();
|
||||
} else {
|
||||
window.open('about:blank', 'window', 'modal');
|
||||
}
|
||||
}
|
||||
|
||||
function open_normal() {
|
||||
my_window = window.open('about:blank', 'my_window');
|
||||
}
|
||||
|
||||
function open_invalid() {
|
||||
window.open('', 'my_window');
|
||||
}
|
||||
|
||||
function close() {
|
||||
my_window.close();
|
||||
}
|
||||
|
||||
function close_twice() {
|
||||
my_window.close();
|
||||
my_window.close();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<button onclick="open_normal()" id="open-normal">normal</button>
|
||||
<button onclick="open_modal()" id="open-modal">modal</button>
|
||||
<button onclick="open_invalid()" id="open-invalid">invalid/no URL</button>
|
||||
<button onclick="close()" id="close-normal">close</button>
|
||||
<button onclick="close_twice()" id="close-twice">close twice (issue 906)</button>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -88,7 +88,6 @@ Feature: Going back and forward.
|
||||
- url: http://localhost:*/data/backforward/2.txt
|
||||
- url: http://localhost:*/data/backforward/3.txt
|
||||
|
||||
@qtwebengine_skip: Causes 'Ignoring invalid URL being added to history' sometimes?
|
||||
Scenario: Going back too much with count.
|
||||
Given I open data/backforward/1.txt
|
||||
When I open data/backforward/2.txt
|
||||
@ -138,7 +137,6 @@ Feature: Going back and forward.
|
||||
When I run :forward
|
||||
Then the error "At end of history." should be shown
|
||||
|
||||
@qtwebengine_skip: Causes 'Ignoring invalid URL being added to history' sometimes?
|
||||
Scenario: Going forward too much with count.
|
||||
Given I open data/backforward/1.txt
|
||||
When I open data/backforward/2.txt
|
||||
|
@ -34,27 +34,6 @@ from qutebrowser.utils import log
|
||||
from helpers import utils
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
"""Apply @qtwebengine_* markers."""
|
||||
webengine = config.getoption('--qute-bdd-webengine')
|
||||
|
||||
markers = {
|
||||
'qtwebengine_todo': ('QtWebEngine TODO', pytest.mark.xfail),
|
||||
'qtwebengine_skip': ('Skipped with QtWebEngine', pytest.mark.skipif),
|
||||
}
|
||||
|
||||
for item in items:
|
||||
for name, (prefix, pytest_mark) in markers.items():
|
||||
marker = item.get_marker(name)
|
||||
if marker:
|
||||
if marker.args:
|
||||
text = '{}: {}'.format(prefix, marker.args[0])
|
||||
else:
|
||||
text = prefix
|
||||
item.add_marker(pytest_mark(webengine, reason=text,
|
||||
**marker.kwargs))
|
||||
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_runtest_makereport(item, call):
|
||||
"""Add a BDD section to the test output."""
|
||||
@ -429,7 +408,7 @@ def compare_session(request, quteproc, expected):
|
||||
partial_compare is used, which means only the keys/values listed will be
|
||||
compared.
|
||||
"""
|
||||
if request.config.getoption('--qute-bdd-webengine'):
|
||||
if request.config.webengine:
|
||||
pytest.xfail(reason="QtWebEngine TODO: Sessions are not implemented")
|
||||
quteproc.compare_session(expected)
|
||||
|
||||
@ -494,7 +473,7 @@ def check_open_tabs(quteproc, request, tabs):
|
||||
|
||||
It expects a list of URLs, with an optional "(active)" suffix.
|
||||
"""
|
||||
if request.config.getoption('--qute-bdd-webengine'):
|
||||
if request.config.webengine:
|
||||
pytest.xfail(reason="QtWebEngine TODO: Sessions are not implemented")
|
||||
session = quteproc.get_session()
|
||||
active_suffix = ' (active)'
|
||||
@ -551,7 +530,7 @@ def _get_scroll_values(quteproc):
|
||||
@bdd.then(bdd.parsers.re(r"the page should be scrolled "
|
||||
r"(?P<direction>horizontally|vertically)"))
|
||||
def check_scrolled(request, quteproc, direction):
|
||||
if request.config.getoption('--qute-bdd-webengine'):
|
||||
if request.config.webengine:
|
||||
pytest.xfail(reason="QtWebEngine TODO: Sessions are not implemented")
|
||||
x, y = _get_scroll_values(quteproc)
|
||||
if direction == 'horizontally':
|
||||
@ -564,7 +543,7 @@ def check_scrolled(request, quteproc, direction):
|
||||
|
||||
@bdd.then("the page should not be scrolled")
|
||||
def check_not_scrolled(request, quteproc):
|
||||
if request.config.getoption('--qute-bdd-webengine'):
|
||||
if request.config.webengine:
|
||||
pytest.xfail(reason="QtWebEngine TODO: Sessions are not implemented")
|
||||
x, y = _get_scroll_values(quteproc)
|
||||
assert x == 0
|
||||
|
@ -9,6 +9,7 @@ Feature: Using hints
|
||||
And I hint with args "links normal" and follow xyz
|
||||
Then the error "No hint xyz!" should be shown
|
||||
|
||||
@qtwebengine_skip: Flaky because scrolling happens async
|
||||
Scenario: Following a link after scrolling down
|
||||
When I open data/scroll/simple.html
|
||||
And I run :hint links normal
|
||||
@ -19,7 +20,7 @@ Feature: Using hints
|
||||
|
||||
### Opening in current or new tab
|
||||
|
||||
@qtwebengine_todo: createWindow is not implemented yet
|
||||
@qtwebengine_createWindow
|
||||
Scenario: Following a hint and force to open in current tab.
|
||||
When I open data/hints/link_blank.html
|
||||
And I hint with args "links current" and follow a
|
||||
@ -27,7 +28,7 @@ Feature: Using hints
|
||||
Then the following tabs should be open:
|
||||
- data/hello.txt (active)
|
||||
|
||||
@qtwebengine_todo: createWindow is not implemented yet
|
||||
@qtwebengine_createWindow
|
||||
Scenario: Following a hint and allow to open in new tab.
|
||||
When I open data/hints/link_blank.html
|
||||
And I hint with args "links normal" and follow a
|
||||
@ -36,7 +37,7 @@ Feature: Using hints
|
||||
- data/hints/link_blank.html
|
||||
- data/hello.txt (active)
|
||||
|
||||
@qtwebengine_todo: createWindow is not implemented yet
|
||||
@qtwebengine_createWindow
|
||||
Scenario: Following a hint to link with sub-element and force to open in current tab.
|
||||
When I open data/hints/link_span.html
|
||||
And I run :tab-close
|
||||
@ -154,13 +155,13 @@ Feature: Using hints
|
||||
And I hint wht args "links normal" and follow a
|
||||
Then "navigation request: url http://localhost:*/data/hello2.txt, type NavigationTypeLinkClicked, *" should be logged
|
||||
|
||||
@qtwebengine_todo: createWindow is not implemented yet
|
||||
@qtwebengine_createWindow
|
||||
Scenario: Opening a link inside a specific iframe
|
||||
When I open data/hints/iframe_target.html
|
||||
And I hint with args "links normal" and follow a
|
||||
Then "navigation request: url http://localhost:*/data/hello.txt, type NavigationTypeLinkClicked, *" should be logged
|
||||
|
||||
@qtwebengine_todo: createWindow is not implemented yet
|
||||
@qtwebengine_createWindow
|
||||
Scenario: Opening a link with specific target frame in a new tab
|
||||
When I open data/hints/iframe_target.html
|
||||
And I hint with args "links tab" and follow a
|
||||
@ -172,7 +173,7 @@ Feature: Using hints
|
||||
### hints -> auto-follow-timeout
|
||||
|
||||
Scenario: Ignoring key presses after auto-following hints
|
||||
When I set hints -> auto-follow-timeout to 200
|
||||
When I set hints -> auto-follow-timeout to 500
|
||||
And I set hints -> mode to number
|
||||
And I run :bind --force , message-error "This should not happen"
|
||||
And I open data/hints/html/simple.html
|
||||
|
@ -48,6 +48,12 @@ Feature: Page history
|
||||
Then the history file should contain:
|
||||
http://localhost:(port)/status/404 Error loading page: http://localhost:(port)/status/404
|
||||
|
||||
@qtwebengine_createWindow
|
||||
Scenario: History with invalid URL
|
||||
When I open data/javascript/window_open.html
|
||||
And I run :click-element id open-invalid
|
||||
Then "Changing title for idx 1 to 'about:blank'" should be logged
|
||||
|
||||
Scenario: Clearing history
|
||||
When I open data/title.html
|
||||
And I run :history-clear
|
||||
|
@ -7,14 +7,67 @@ Feature: Javascript stuff
|
||||
And I open data/javascript/consolelog.html
|
||||
Then the javascript message "console.log works!" should be logged
|
||||
|
||||
# Causes segfaults...
|
||||
@qtwebengine_createWindow
|
||||
Scenario: Opening/Closing a window via JS
|
||||
When I open data/javascript/window_open.html
|
||||
And I run :tab-only
|
||||
And I run :click-element id open-normal
|
||||
And I wait for "Changing title for idx 1 to 'about:blank'" in the log
|
||||
And I run :tab-focus 1
|
||||
And I run :click-element id close-normal
|
||||
Then "Focus object changed: *" should be logged
|
||||
|
||||
# Causes segfaults...
|
||||
@qtwebengine_createWindow
|
||||
Scenario: Opening/closing a modal window via JS
|
||||
When I open data/javascript/window_open.html
|
||||
And I run :tab-only
|
||||
And I run :click-element id open-modal
|
||||
And I wait for "Changing title for idx 1 to 'about:blank'" in the log
|
||||
And I run :tab-focus 1
|
||||
And I run :click-element id close-normal
|
||||
Then "Focus object changed: *" should be logged
|
||||
# WebModalDialog with QtWebKit, WebDialog with QtWebEngine
|
||||
And "Web*Dialog requested, but we don't support that!" should be logged
|
||||
|
||||
# https://github.com/The-Compiler/qutebrowser/issues/906
|
||||
|
||||
@qtwebengine_todo: createWindow is not implemented yet
|
||||
Scenario: Closing a JS window twice (issue 906)
|
||||
@qtwebengine_skip
|
||||
Scenario: Closing a JS window twice (issue 906) - qtwebkit
|
||||
When I open about:blank
|
||||
And I open data/javascript/issue906.html in a new tab
|
||||
And I run :click-element id open-button
|
||||
And I run :tab-only
|
||||
When I open data/javascript/window_open.html in a new tab
|
||||
And I run :click-element id open-normal
|
||||
And I wait for "Changing title for idx 2 to 'about:blank'" in the log
|
||||
And I run :tab-focus 2
|
||||
And I run :click-element id close-button
|
||||
And I run :click-element id close-twice
|
||||
Then "Requested to close * which does not exist!" should be logged
|
||||
|
||||
@qtwebengine_createWindow @qtwebkit_skip
|
||||
Scenario: Closing a JS window twice (issue 906) - qtwebengine
|
||||
When I open about:blank
|
||||
And I run :tab-only
|
||||
And I open data/javascript/window_open.html in a new tab
|
||||
And I run :click-element id open-normal
|
||||
And I wait for "Changing title for idx 2 to 'about:blank'" in the log
|
||||
And I run :tab-focus 2
|
||||
And I run :click-element id close-twice
|
||||
And I wait for "Focus object changed: *" in the log
|
||||
Then no crash should happen
|
||||
|
||||
@qtwebengine_createWindow
|
||||
Scenario: Opening window without user interaction with javascript-can-open-windows-automatically set to true
|
||||
When I open data/hello.txt
|
||||
And I set content -> javascript-can-open-windows-automatically to true
|
||||
And I run :tab-only
|
||||
And I run :jseval if (window.open('about:blank')) { console.log('window opened'); } else { console.log('error while opening window'); }
|
||||
Then the javascript message "window opened" should be logged
|
||||
|
||||
@qtwebengine_createWindow
|
||||
Scenario: Opening window without user interaction with javascript-can-open-windows-automatically set to false
|
||||
When I open data/hello.txt
|
||||
And I set content -> javascript-can-open-windows-automatically to false
|
||||
And I run :tab-only
|
||||
And I run :jseval if (window.open('about:blank')) { console.log('window opened'); } else { console.log('error while opening window'); }
|
||||
Then the javascript message "error while opening window" should be logged
|
||||
|
@ -551,7 +551,7 @@ Feature: Various utility commands.
|
||||
Then the page should not be scrolled
|
||||
And the error "prompt-accept: This command is only allowed in prompt/yesno mode." should be shown
|
||||
|
||||
@qtwebengine_todo: createWindow is not implemented yet
|
||||
@qtwebengine_createWindow
|
||||
Scenario: :repeat-command with mode-switching command
|
||||
Given I open data/hints/link_blank.html
|
||||
And I run :tab-only
|
||||
@ -620,6 +620,7 @@ Feature: Various utility commands.
|
||||
- data/click_element.html
|
||||
- data/hello.txt (active)
|
||||
|
||||
@qtwebengine_skip: Flaky because scrolling happens async
|
||||
Scenario: Clicking an element which is out of view
|
||||
When I open data/scroll/simple.html
|
||||
And I run :scroll-page 0 1
|
||||
|
@ -24,7 +24,7 @@ bdd.scenarios('marks.feature')
|
||||
|
||||
@bdd.then(bdd.parsers.parse("the page should be scrolled to {x} {y}"))
|
||||
def check_y(request, quteproc, x, y):
|
||||
if request.config.getoption('--qute-bdd-webengine'):
|
||||
if request.config.webengine:
|
||||
pytest.xfail(reason="QtWebEngine TODO: Sessions are not implemented")
|
||||
data = quteproc.get_session()
|
||||
pos = data['windows'][0]['tabs'][0]['history'][-1]['scroll-pos']
|
||||
|
@ -157,7 +157,6 @@ class QuteProc(testprocess.Process):
|
||||
|
||||
def __init__(self, request, *, parent=None):
|
||||
super().__init__(parent)
|
||||
self._webengine = request.config.getoption('--qute-bdd-webengine')
|
||||
self._ipc_socket = None
|
||||
self.basedir = None
|
||||
self._focus_ready = False
|
||||
@ -261,7 +260,7 @@ class QuteProc(testprocess.Process):
|
||||
return executable, args
|
||||
|
||||
def _default_args(self):
|
||||
backend = 'webengine' if self._webengine else 'webkit'
|
||||
backend = 'webengine' if self.request.config.webengine else 'webkit'
|
||||
return ['--debug', '--no-err-windows', '--temp-basedir',
|
||||
'--json-logging', '--backend', backend, 'about:blank']
|
||||
|
||||
@ -338,7 +337,7 @@ class QuteProc(testprocess.Process):
|
||||
('general', 'auto-save-interval', '0'),
|
||||
('general', 'new-instance-open-target.window', 'last-opened')
|
||||
]
|
||||
if not self._webengine:
|
||||
if not self.request.config.webengine:
|
||||
settings.append(('network', 'ssl-strict', 'false'))
|
||||
|
||||
for sect, opt, value in settings:
|
||||
|
@ -234,7 +234,7 @@ class Process(QObject):
|
||||
if not self.is_running():
|
||||
# _start ensures it actually started, but it might quit shortly
|
||||
# afterwards
|
||||
raise ProcessExited()
|
||||
raise ProcessExited('\n' + _render_log(self.captured_log))
|
||||
|
||||
if blocker.signal_triggered:
|
||||
self._after_start()
|
||||
|
@ -85,21 +85,20 @@ def _parse_file(test_name):
|
||||
@pytest.mark.parametrize('find_implementation', ['javascript', 'python'])
|
||||
def test_hints(test_name, zoom_text_only, zoom_level, find_implementation,
|
||||
quteproc, request):
|
||||
webengine = bool(request.config.getoption('--qute-bdd-webengine'))
|
||||
if zoom_text_only and webengine:
|
||||
if zoom_text_only and request.config.webengine:
|
||||
pytest.skip("QtWebEngine doesn't have zoom-text-only")
|
||||
if find_implementation == 'python' and webengine:
|
||||
if find_implementation == 'python' and request.config.webengine:
|
||||
pytest.skip("QtWebEngine doesn't have a python find implementation")
|
||||
|
||||
parsed = _parse_file(test_name)
|
||||
if parsed.qtwebengine_todo is not None and webengine:
|
||||
if parsed.qtwebengine_todo is not None and request.config.webengine:
|
||||
pytest.xfail("QtWebEngine TODO: {}".format(parsed.qtwebengine_todo))
|
||||
|
||||
url_path = 'data/hints/html/{}'.format(test_name)
|
||||
quteproc.open_path(url_path)
|
||||
|
||||
# setup
|
||||
if not webengine:
|
||||
if not request.config.webengine:
|
||||
quteproc.set_setting('ui', 'zoom-text-only', str(zoom_text_only))
|
||||
quteproc.set_setting('hints', 'find-implementation',
|
||||
find_implementation)
|
||||
@ -111,7 +110,7 @@ def test_hints(test_name, zoom_text_only, zoom_level, find_implementation,
|
||||
quteproc.wait_for_load_finished('data/' + parsed.target)
|
||||
# reset
|
||||
quteproc.send_cmd(':zoom 100')
|
||||
if not webengine:
|
||||
if not request.config.webengine:
|
||||
quteproc.set_setting('ui', 'zoom-text-only', 'false')
|
||||
quteproc.set_setting('hints', 'find-implementation', 'javascript')
|
||||
|
||||
|
@ -48,7 +48,7 @@ def test_insert_mode(file_name, elem_id, source, input_text, auto_insert,
|
||||
if source == 'keypress':
|
||||
quteproc.press_keys(input_text)
|
||||
elif source == 'clipboard':
|
||||
if request.config.getoption('--qute-bdd-webengine'):
|
||||
if request.config.webengine:
|
||||
pytest.xfail(reason="QtWebEngine TODO: :insert-text is not "
|
||||
"implemented")
|
||||
quteproc.send_cmd(':debug-set-fake-clipboard "{}"'.format(input_text))
|
||||
|
@ -76,6 +76,7 @@ def test_parse_fatal_stacktrace(text, typ, func):
|
||||
"QT_IM_MODULE = fcitx"
|
||||
),
|
||||
({'LANGUAGE': 'foo', 'LANG': 'en_US.UTF-8'}, "LANG = en_US.UTF-8"),
|
||||
({'FOO': 'bar', 'QUTE_BLAH': '1'}, "QUTE_BLAH = 1"),
|
||||
], ids=lambda e: e[1])
|
||||
def test_get_environment_vars(monkeypatch, env, expected):
|
||||
"""Test for crashdialog._get_environment_vars."""
|
||||
|
2
tox.ini
2
tox.ini
@ -13,7 +13,7 @@ skipsdist = true
|
||||
setenv =
|
||||
QT_QPA_PLATFORM_PLUGIN_PATH={envdir}/Lib/site-packages/PyQt5/plugins/platforms
|
||||
PYTEST_QT_API=pyqt5
|
||||
passenv = PYTHON DISPLAY XAUTHORITY HOME USERNAME USER CI TRAVIS XDG_*
|
||||
passenv = PYTHON DISPLAY XAUTHORITY HOME USERNAME USER CI TRAVIS XDG_* QUTE_*
|
||||
deps =
|
||||
-r{toxinidir}/misc/requirements/requirements-pip.txt
|
||||
-r{toxinidir}/requirements.txt
|
||||
|
Loading…
Reference in New Issue
Block a user