Merge branch 'webengine-createwindow'

This commit is contained in:
Florian Bruhin 2016-09-05 17:42:15 +02:00
commit 050507c0a6
30 changed files with 359 additions and 170 deletions

View File

@ -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

View File

@ -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
~~~~~~~~~~

View File

@ -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:

View File

@ -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: .*

View File

@ -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),

View File

@ -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):

View File

@ -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,

View File

@ -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),

View File

@ -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!")

View File

@ -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'),

View File

@ -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',

View File

@ -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:

View File

@ -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')

View File

@ -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

View File

@ -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))

View File

@ -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>

View 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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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']

View File

@ -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:

View File

@ -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()

View File

@ -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')

View File

@ -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))

View File

@ -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."""

View File

@ -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