Merge remote-tracking branch 'upstream/master' into bdd_test_spawn_command

This commit is contained in:
Philipp Hansch 2016-03-29 19:24:43 +02:00
commit 4a1ba45efa
18 changed files with 481 additions and 60 deletions

View File

@ -44,6 +44,8 @@ Changed
- Improved detection of URLs/search terms when pasting multiple lines.
- Don't remove `qutebrowser-editor-*` temporary file if editor subprocess crashed
- Userscripts are also searched in `/usr/share/qutebrowser/userscripts`.
- Blocked hosts are now also read from a `blocked-hosts` file in the config dir
(e.g. `~/.config/qutebrowser/blocked-hosts`).
Fixed
~~~~~
@ -62,6 +64,7 @@ Fixed
- Fixed very long filenames when downloading `data://`-URLs.
- Fixed ugly UI fonts on Windows when Liberation Mono is installed
- Fixed crash when unbinding key from a section which doesn't exist in the config
- Fixed report window after a segfault
v0.5.1
------

View File

@ -102,6 +102,12 @@ $ rm -r qutebrowser-git
or you could use an AUR helper, e.g. `yaourt -S qutebrowser-git`.
If video or sound don't seem to work, try installing the gstreamer plugins:
----
# pacman -S gst-plugins-{base,good,bad,ugly} gst-libav
----
On Gentoo
---------
@ -135,6 +141,21 @@ it with:
$ nix-env -i qutebrowser
----
On openSUSE
-----------
There are prebuilt RPMs available for Tumbleweed and Leap 42.1:
http://software.opensuse.org/download.html?project=home%3Aarpraher&package=qutebrowser[One Click Install]
Or add the repo manually:
----
# zypper addrepo http://download.opensuse.org/repositories/home:arpraher/openSUSE_Tumbleweed/home:arpraher.repo
# zypper refresh
# zypper install qutebrowser
----
On Windows
----------
@ -214,7 +235,16 @@ it as part of the packaging process.
Installing qutebrowser with tox
-------------------------------
Run tox inside the qutebrowser repository to set up a
First of all, clone the repository using http://git-scm.org/[git] and switch
into the repository folder:
----
$ git clone https://github.com/The-Compiler/qutebrowser.git
$ cd qutebrowser
----
Then run tox inside the qutebrowser repository to set up a
https://docs.python.org/3/library/venv.html[virtual environment]:
----

View File

@ -165,10 +165,12 @@ Contributors, sorted by the number of commits in descending order:
* Thorsten Wißmann
* Austin Anderson
* Alexey "Averrin" Nabrodov
* avk
* ZDarian
* Milan Svoboda
* John ShaggyTwoDope Jenkins
* Peter Vilim
* Clayton Craft
* Oliver Caldwell
* Philipp Hansch
* Jonas Schürmann
@ -191,7 +193,6 @@ Contributors, sorted by the number of commits in descending order:
* Link
* Larry Hynes
* Johannes Altmanninger
* avk
* Samir Benmendil
* Regina Hug
* Mathias Fussenegger
@ -199,9 +200,9 @@ Contributors, sorted by the number of commits in descending order:
* Fritz V155 Reichwald
* Franz Fellner
* Corentin Jule
* Clayton Craft
* zwarag
* xd1le
* issue
* haxwithaxe
* evan
* dylan araps

View File

@ -72,6 +72,8 @@
=== adblock-update
Update the adblock block lists.
This updates ~/.local/share/qutebrowser/blocked-hosts with downloaded host lists and re-reads ~/.config/qutebrowser/blocked-hosts.
[[back]]
=== back
Syntax: +:back [*--tab*] [*--bg*] [*--window*]+

View File

@ -4,19 +4,15 @@ MAINTAINER Florian Bruhin <me@the-compiler.org>
RUN echo 'Server = http://mirror.de.leaseweb.net/archlinux/$repo/os/$arch' > /etc/pacman.d/mirrorlist
RUN pacman-key --init && pacman-key --populate archlinux && pacman -Sy --noconfirm archlinux-keyring
RUN pacman-key -r 0xD6A1C70FE80A0C82 && \
pacman-key --lsign-key 0xD6A1C70FE80A0C82 && \
echo -e '[qt-debug]\nServer = http://qutebrowser.org/qt-debug/$arch' >> /etc/pacman.conf
RUN pacman -Suyy --noconfirm
RUN pacman-db-upgrade
RUN pacman -S --noconfirm \
git \
python-tox \
qt5-base-debug \
qt5-webkit-debug \
python-pyqt5-debug \
qt5-base \
qt5-webkit \
python-pyqt5 \
xorg-xinit \
herbstluftwm \
xorg-server-xvfb

View File

@ -92,9 +92,11 @@ class HostBlocker:
Attributes:
_blocked_hosts: A set of blocked hosts.
_config_blocked_hosts: A set of blocked hosts from ~/.config.
_in_progress: The DownloadItems which are currently downloading.
_done_count: How many files have been read successfully.
_hosts_file: The path to the blocked-hosts file.
_local_hosts_file: The path to the blocked-hosts file.
_config_hosts_file: The path to a blocked-hosts in ~/.config
Class attributes:
WHITELISTED: Hosts which never should be blocked.
@ -105,13 +107,22 @@ class HostBlocker:
def __init__(self):
self._blocked_hosts = set()
self._config_blocked_hosts = set()
self._in_progress = []
self._done_count = 0
data_dir = standarddir.data()
if data_dir is None:
self._hosts_file = None
self._local_hosts_file = None
else:
self._hosts_file = os.path.join(data_dir, 'blocked-hosts')
self._local_hosts_file = os.path.join(data_dir, 'blocked-hosts')
config_dir = standarddir.config()
if config_dir is None:
self._config_hosts_file = None
else:
self._config_hosts_file = os.path.join(config_dir, 'blocked-hosts')
objreg.get('config').changed.connect(self.on_config_changed)
def is_blocked(self, url):
@ -119,21 +130,46 @@ class HostBlocker:
if not config.get('content', 'host-blocking-enabled'):
return False
host = url.host()
return host in self._blocked_hosts and not is_whitelisted_host(host)
return ((host in self._blocked_hosts or
host in self._config_blocked_hosts) and
not is_whitelisted_host(host))
def _read_hosts_file(self, filename, target):
"""Read hosts from the given filename.
Args:
filename: The file to read.
target: The set to store the hosts in.
Return:
True if a read was attempted, False otherwise
"""
if filename is None or not os.path.exists(filename):
return False
try:
with open(filename, 'r', encoding='utf-8') as f:
for line in f:
target.add(line.strip())
except OSError:
log.misc.exception("Failed to read host blocklist!")
return True
def read_hosts(self):
"""Read hosts from the existing blocked-hosts file."""
self._blocked_hosts = set()
if self._hosts_file is None:
if self._local_hosts_file is None:
return
if os.path.exists(self._hosts_file):
try:
with open(self._hosts_file, 'r', encoding='utf-8') as f:
for line in f:
self._blocked_hosts.add(line.strip())
except OSError:
log.misc.exception("Failed to read host blocklist!")
else:
self._read_hosts_file(self._config_hosts_file,
self._config_blocked_hosts)
found = self._read_hosts_file(self._local_hosts_file,
self._blocked_hosts)
if not found:
args = objreg.get('args')
if (config.get('content', 'host-block-lists') is not None and
args.basedir is None):
@ -142,8 +178,14 @@ class HostBlocker:
@cmdutils.register(instance='host-blocker', win_id='win_id')
def adblock_update(self, win_id):
"""Update the adblock block lists."""
if self._hosts_file is None:
"""Update the adblock block lists.
This updates ~/.local/share/qutebrowser/blocked-hosts with downloaded
host lists and re-reads ~/.config/qutebrowser/blocked-hosts.
"""
self._read_hosts_file(self._config_hosts_file,
self._config_blocked_hosts)
if self._local_hosts_file is None:
raise cmdexc.CommandError("No data storage is configured!")
self._blocked_hosts = set()
self._done_count = 0
@ -221,7 +263,7 @@ class HostBlocker:
def on_lists_downloaded(self):
"""Install block lists after files have been downloaded."""
with open(self._hosts_file, 'w', encoding='utf-8') as f:
with open(self._local_hosts_file, 'w', encoding='utf-8') as f:
for host in sorted(self._blocked_hosts):
f.write(host + '\n')
message.info('current', "adblock: Read {} hosts from {} sources."
@ -233,7 +275,7 @@ class HostBlocker:
urls = config.get('content', 'host-block-lists')
if urls is None:
try:
os.remove(self._hosts_file)
os.remove(self._local_hosts_file)
except OSError:
log.misc.exception("Failed to delete hosts file.")

View File

@ -23,8 +23,6 @@ import urllib.parse
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl
from qutebrowser.misc import httpclient
class PastebinClient(QObject):
@ -47,11 +45,17 @@ class PastebinClient(QObject):
success = pyqtSignal(str)
error = pyqtSignal(str)
def __init__(self, parent=None):
def __init__(self, client, parent=None):
"""Constructor.
Args:
client: The HTTPClient to use. Will be reparented.
"""
super().__init__(parent)
self._client = httpclient.HTTPClient(self)
self._client.error.connect(self.error)
self._client.success.connect(self.on_client_success)
client.setParent(self)
client.error.connect(self.error)
client.success.connect(self.on_client_success)
self._client = client
def paste(self, name, title, text, parent=None):
"""Paste the text into a pastebin and return the URL.

View File

@ -219,14 +219,12 @@ class BrowserPage(QWebPage):
def _show_pdfjs(self, reply):
"""Show the reply with pdfjs."""
try:
page = pdfjs.generate_pdfjs_page(reply.url()).encode('utf-8')
page = pdfjs.generate_pdfjs_page(reply.url())
except pdfjs.PDFJSNotFound:
# pylint: disable=no-member
# WORKAROUND for https://bitbucket.org/logilab/pylint/issue/490/
page = (jinja.env.get_template('no_pdfjs.html')
.render(url=reply.url().toDisplayString())
.encode('utf-8'))
self.mainFrame().setContent(page, 'text/html', reply.url())
page = jinja.render('no_pdfjs.html',
url=reply.url().toDisplayString())
self.mainFrame().setContent(page.encode('utf-8'), 'text/html',
reply.url())
reply.deleteLater()
def shutdown(self):

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<!--
vim: ft=html fileencoding=utf-8 sts=4 sw=4 et:
-->
<html>
<head>
<meta charset="utf-8">
<title>Error while rendering HTML</title>
</head>
<body>
<h1>Error while rendering internal qutebrowser page</h1>
<p>There was an error while rendering {pagename}.</p>
<p>This most likely happened because you updated qutebrowser but didn't restart yet.</p>
<p>If you believe this isn't the case and this is a bug, please do :report.<p>
<h2>Traceback</h2>
<pre>{traceback}</pre>
</body>
</html>

View File

@ -36,7 +36,7 @@ from PyQt5.QtWidgets import (QDialog, QLabel, QTextEdit, QPushButton,
import qutebrowser
from qutebrowser.utils import version, log, utils, objreg, qtutils
from qutebrowser.misc import miscwidgets, autoupdate, msgbox
from qutebrowser.misc import miscwidgets, autoupdate, msgbox, httpclient
from qutebrowser.browser.network import pastebin
from qutebrowser.config import config
@ -141,7 +141,8 @@ class _CrashDialog(QDialog):
self.setWindowTitle("Whoops!")
self.resize(QSize(640, 600))
self._vbox = QVBoxLayout(self)
self._paste_client = pastebin.PastebinClient(self)
http_client = httpclient.HTTPClient()
self._paste_client = pastebin.PastebinClient(http_client, self)
self._pypi_client = autoupdate.PyPIVersionClient(self)
self._init_text()
@ -508,11 +509,23 @@ class FatalCrashDialog(_CrashDialog):
def _init_text(self):
super()._init_text()
text = ("<b>qutebrowser was restarted after a fatal crash.</b><br/>"
"<br/>Note: Crash reports for fatal crashes sometimes don't "
"QTWEBENGINE_NOTE"
"<br/>Crash reports for fatal crashes sometimes don't "
"contain the information necessary to fix an issue. Please "
"follow the steps in <a href='https://github.com/The-Compiler/"
"qutebrowser/blob/master/doc/stacktrace.asciidoc'>"
"stacktrace.asciidoc</a> to submit a stacktrace.<br/>")
if datetime.datetime.now() < datetime.datetime(2016, 4, 23):
note = ("<br/>Fatal crashes like this are often caused by the "
"current QtWebKit backend.<br/><b>I'm currently running a "
"crowdfunding for the new QtWebEngine backend, based on "
"Chromium:</b> <a href='http://igg.me/at/qutebrowser'>"
"igg.me/at/qutebrowser</a><br/>")
text = text.replace('QTWEBENGINE_NOTE', note)
else:
text = text.replace('QTWEBENGINE_NOTE', '')
self._lbl.setText(text)
def _init_checkboxes(self):

View File

@ -72,7 +72,7 @@ class CrashHandler(QObject):
def handle_segfault(self):
"""Handle a segfault from a previous run."""
data_dir = None
data_dir = standarddir.data()
if data_dir is None:
return
logname = os.path.join(data_dir, 'crash.log')

View File

@ -21,10 +21,12 @@
import os
import os.path
import traceback
import jinja2
import jinja2.exceptions
from qutebrowser.utils import utils
from qutebrowser.utils import utils, log
from PyQt5.QtCore import QUrl
@ -74,8 +76,14 @@ def resource_url(path):
def render(template, **kwargs):
"""Render the given template and pass the given arguments to it."""
return _env.get_template(template).render(**kwargs)
try:
return _env.get_template(template).render(**kwargs)
except jinja2.exceptions.UndefinedError:
log.misc.exception("UndefinedError while rendering " + template)
err_path = os.path.join('html', 'undef_error.html')
err_template = utils.read_file(err_path)
tb = traceback.format_exc()
return err_template.format(pagename=template, traceback=tb)
_env = jinja2.Environment(loader=Loader('html'), autoescape=_guess_autoescape)
_env.globals['resource_url'] = resource_url

View File

@ -65,6 +65,8 @@ PERFECT_FILES = [
'qutebrowser/browser/network/filescheme.py'),
('tests/unit/browser/network/test_networkreply.py',
'qutebrowser/browser/network/networkreply.py'),
('tests/unit/browser/network/test_pastebin.py',
'qutebrowser/browser/network/pastebin.py'),
('tests/unit/browser/test_signalfilter.py',
'qutebrowser/browser/signalfilter.py'),
@ -102,6 +104,8 @@ PERFECT_FILES = [
'qutebrowser/mainwindow/statusbar/textbase.py'),
('tests/unit/mainwindow/statusbar/test_prompt.py',
'qutebrowser/mainwindow/statusbar/prompt.py'),
('tests/unit/mainwindow/statusbar/test_url.py',
'qutebrowser/mainwindow/statusbar/url.py'),
('tests/unit/config/test_configtypes.py',
'qutebrowser/config/configtypes.py'),

View File

@ -31,23 +31,27 @@ import argparse
def main():
args = get_args()
if args.browser in ['chromium', 'firefox', 'ie']:
import_netscape_bookmarks(args.bookmarks)
import_netscape_bookmarks(args.bookmarks, args.bookmark_format)
def get_args():
"""Get the argparse parser."""
parser = argparse.ArgumentParser(
epilog="To import bookmarks from Chromium, Firefox or IE, "
"export them to HTML in your browsers bookmark manager.")
parser.add_argument('browser', help="Which browser?",
"export them to HTML in your browsers bookmark manager. "
"By default, this script will output in a quickmarks format.")
parser.add_argument('browser', help="Which browser? (chromium, firefox)",
choices=['chromium', 'firefox', 'ie'],
metavar='browser')
parser.add_argument('bookmarks', help="Bookmarks file")
parser.add_argument('-b', help="Output in bookmark format.",
dest='bookmark_format', action='store_true',
default=False, required=False)
parser.add_argument('bookmarks', help="Bookmarks file (html format)")
args = parser.parse_args()
return args
def import_netscape_bookmarks(bookmarks_file):
def import_netscape_bookmarks(bookmarks_file, is_bookmark_format):
"""Import bookmarks from a NETSCAPE-Bookmark-file v1.
Generated by Chromium, Firefox, IE and possibly more browsers
@ -57,11 +61,15 @@ def import_netscape_bookmarks(bookmarks_file):
soup = bs4.BeautifulSoup(f, 'html.parser')
html_tags = soup.findAll('a')
if is_bookmark_format:
output_template = '{tag[href]} {tag.string}'
else:
output_template = '{tag.string} {tag[href]}'
bookmarks = []
for tag in html_tags:
if tag['href'] not in bookmarks:
bookmarks.append('{tag.string} {tag[href]}'.format(tag=tag))
bookmarks.append(output_template.format(tag=tag))
for bookmark in bookmarks:
print(bookmark)

View File

@ -0,0 +1,125 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2016 Anna Kobak (avk) <awerk@onet.eu>:
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
import pytest
from PyQt5.QtCore import pyqtSignal, QUrl, QObject
from qutebrowser.browser.network import pastebin
from qutebrowser.misc import httpclient
class HTTPPostStub(QObject):
"""A stub class for HTTPClient.
Attributes:
url: the last url send by post()
data: the last data send by post()
"""
success = pyqtSignal(str)
error = pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.url = None
self.data = None
def post(self, url, data=None):
self.url = url
self.data = data
@pytest.fixture
def pbclient():
http_stub = HTTPPostStub()
client = pastebin.PastebinClient(http_stub)
return client
def test_constructor(qapp):
http_client = httpclient.HTTPClient()
pbclient = pastebin.PastebinClient(http_client)
@pytest.mark.parametrize('data', [
{
"name": "XYZ",
"title": "hello world",
"text": "xyz. 123 \n 172ANB",
"reply": "abc"
},
{
"name": "the name",
"title": "the title",
"text": "some Text",
"reply": "some parent"
}
])
def test_paste_with_parent(data, pbclient):
http_stub = pbclient._client
pbclient.paste(data["name"], data["title"], data["text"], data["reply"])
assert http_stub.data == data
assert http_stub.url == QUrl('http://paste.the-compiler.org/api/create')
@pytest.mark.parametrize('data', [
{
"name": "XYZ",
"title": "hello world",
"text": "xyz. 123 \n 172ANB"
},
{
"name": "the name",
"title": "the title",
"text": "some Text"
}
])
def test_paste_without_parent(data, pbclient):
http_stub = pbclient._client
pbclient.paste(data["name"], data["title"], data["text"])
assert pbclient._client.data == data
assert http_stub.url == QUrl('http://paste.the-compiler.org/api/create')
@pytest.mark.parametrize('http', [
"http://paste.the-compiler.org/view/ges83nt3",
"http://paste.the-compiler.org/view/3gjnwg4"
])
def test_on_client_success(http, pbclient, qtbot):
with qtbot.assertNotEmitted(pbclient.error):
with qtbot.waitSignal(pbclient.success):
pbclient._client.success.emit(http)
@pytest.mark.parametrize('http', [
"http invalid",
"http:/invalid.org"
"http//invalid.com"
])
def test_client_success_invalid_http(http, pbclient, qtbot):
with qtbot.assertNotEmitted(pbclient.success):
with qtbot.waitSignal(pbclient.error):
pbclient._client.success.emit(http)
def test_client_error(pbclient, qtbot):
with qtbot.assertNotEmitted(pbclient.success):
with qtbot.waitSignal(pbclient.error):
pbclient._client.error.emit("msg")

View File

@ -0,0 +1,146 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2016 Clayton Craft (craftyguy) <craftyguy@gmail.com>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Test Statusbar url."""
import pytest
import collections
from qutebrowser.browser import webview
from qutebrowser.mainwindow.statusbar import url
@pytest.fixture
def tab_widget():
"""Fixture providing a fake tab widget."""
tab = collections.namedtuple('Tab', 'cur_url load_status')
tab.cur_url = collections.namedtuple('cur_url', 'toDisplayString')
return tab
@pytest.fixture
def url_widget(qtbot, monkeypatch, config_stub):
"""Fixture providing a Url widget."""
config_stub.data = {
'colors': {
'statusbar.url.bg': 'white',
'statusbar.url.fg': 'black',
'statusbar.url.fg.success': 'yellow',
'statusbar.url.fg.success.https': 'green',
'statusbar.url.fg.error': 'red',
'statusbar.url.fg.warn': 'orange',
'statusbar.url.fg.hover': 'blue'
},
'fonts': {},
}
monkeypatch.setattr(
'qutebrowser.mainwindow.statusbar.url.style.config', config_stub)
widget = url.UrlText()
qtbot.add_widget(widget)
assert not widget.isVisible()
return widget
@pytest.mark.parametrize('url_text', [
'http://abc123.com/this/awesome/url.html',
'https://supersecret.gov/nsa/files.txt',
'Th1$ i$ n0t @ n0rm@L uRL! P@n1c! <-->',
None
])
def test_set_url(url_widget, url_text):
"""Test text displayed by the widget."""
url_widget.set_url(url_text)
if url_text is not None:
assert url_widget.text() == url_text
else:
assert url_widget.text() == ""
@pytest.mark.parametrize('url_text, title, text', [
('http://abc123.com/this/awesome/url.html', 'Awesome site', 'click me!'),
('https://supersecret.gov/nsa/files.txt', 'Secret area', None),
('Th1$ i$ n0t @ n0rm@L uRL! P@n1c! <-->', 'Probably spam', 'definitely'),
(None, None, 'did I break?!')
])
def test_set_hover_url(url_widget, url_text, title, text):
"""Test text when hovering over a link."""
url_widget.set_hover_url(url_text, title, text)
if url_text is not None:
assert url_widget.text() == url_text
assert url_widget._urltype == url.UrlType.hover
else:
assert url_widget.text() == ''
assert url_widget._urltype == url.UrlType.normal
@pytest.mark.parametrize('status, expected', [
(webview.LoadStatus.success, url.UrlType.success),
(webview.LoadStatus.success_https, url.UrlType.success_https),
(webview.LoadStatus.error, url.UrlType.error),
(webview.LoadStatus.warn, url.UrlType.warn),
(webview.LoadStatus.loading, url.UrlType.normal),
(webview.LoadStatus.none, url.UrlType.normal)
])
def test_on_load_status_changed(url_widget, status, expected):
"""Test text when status is changed."""
url_widget.set_url('www.example.com')
url_widget.on_load_status_changed(status.name)
assert url_widget._urltype == expected
@pytest.mark.parametrize('load_status, url_text', [
(url.UrlType.success, 'http://abc123.com/this/awesome/url.html'),
(url.UrlType.success, 'http://reddit.com/r/linux'),
(url.UrlType.success_https, 'www.google.com'),
(url.UrlType.success_https, 'https://supersecret.gov/nsa/files.txt'),
(url.UrlType.warn, 'www.shadysite.org/some/path/to/file/with/issues.htm'),
(url.UrlType.error, 'Th1$ i$ n0t @ n0rm@L uRL! P@n1c! <-->'),
(url.UrlType.error, None)
])
def test_on_tab_changed(url_widget, tab_widget, load_status, url_text):
tab_widget.load_status = load_status
tab_widget.cur_url.toDisplayString = lambda: url_text
url_widget.on_tab_changed(tab_widget)
if url_text is not None:
assert url_widget._urltype == load_status
assert url_widget.text() == url_text
else:
assert url_widget._urltype == url.UrlType.normal
assert url_widget.text() == ''
@pytest.mark.parametrize('url_text, load_status, expected_status', [
('http://abc123.com/this/awesome/url.html', webview.LoadStatus.success,
url.UrlType.success),
('https://supersecret.gov/nsa/files.txt', webview.LoadStatus.success_https,
url.UrlType.success_https),
('Th1$ i$ n0t @ n0rm@L uRL! P@n1c! <-->', webview.LoadStatus.error,
url.UrlType.error),
('http://www.qutebrowser.org/CONTRIBUTING.html', webview.LoadStatus.loading,
url.UrlType.normal),
('www.whatisthisurl.com', webview.LoadStatus.warn, url.UrlType.warn)
])
def test_normal_url(url_widget, url_text, load_status, expected_status):
url_widget.set_url(url_text)
url_widget.on_load_status_changed(load_status.name)
url_widget.set_hover_url(url_text, "", "")
url_widget.set_hover_url("", "", "")
assert url_widget.text() == url_text
assert url_widget._urltype == expected_status

View File

@ -23,21 +23,28 @@ import os
import os.path
import pytest
import logging
import jinja2
from PyQt5.QtCore import QUrl
from qutebrowser.utils import jinja
from qutebrowser.utils import utils, jinja
@pytest.fixture(autouse=True)
def patch_read_file(monkeypatch):
"""pytest fixture to patch utils.read_file."""
real_read_file = utils.read_file
def _read_file(path):
"""A read_file which returns a simple template if the path is right."""
if path == os.path.join('html', 'test.html'):
return """Hello {{var}}"""
elif path == os.path.join('html', 'test2.html'):
return """{{ resource_url('utils/testfile') }}"""
elif path == os.path.join('html', 'undef.html'):
return """{{ does_not_exist() }}"""
elif path == os.path.join('html', 'undef_error.html'):
return real_read_file(path)
else:
raise IOError("Invalid path {}!".format(path))
@ -87,6 +94,18 @@ def test_utf8():
assert data == "Hello \u2603"
def test_undefined_function(caplog):
"""Make sure we don't crash if an undefined function is called."""
with caplog.at_level(logging.ERROR):
data = jinja.render('undef.html')
assert 'There was an error while rendering undef.html' in data
assert "'does_not_exist' is undefined" in data
assert data.startswith('<!DOCTYPE html>')
assert len(caplog.records) == 1
assert caplog.records[0].msg == "UndefinedError while rendering undef.html"
@pytest.mark.parametrize('name, expected', [
(None, False),
('foo', False),

12
tox.ini
View File

@ -37,17 +37,17 @@ deps =
pytest-instafail==0.3.0
pytest-travis-fold==1.2.0
pytest-repeat==0.2
pytest-rerunfailures==1.0.1
pytest-rerunfailures==1.0.2
pytest-xvfb==0.2.0
six==1.10.0
termcolor==1.1.0
vulture==0.8.1
Werkzeug==0.11.4
Werkzeug==0.11.5
wheel==0.29.0
cherrypy==5.1.0
commands =
{envpython} scripts/link_pyqt.py --tox {envdir}
{envpython} -m py.test {posargs:tests}
{envpython} -m pytest {posargs:tests}
[testenv:py35-cov]
basepython = python3.5
@ -56,7 +56,7 @@ passenv = {[testenv]passenv}
deps = {[testenv]deps}
commands =
{envpython} scripts/link_pyqt.py --tox {envdir}
{envpython} -m py.test --cov --cov-report xml --cov-report=html --cov-report= {posargs:tests}
{envpython} -m pytest --cov --cov-report xml --cov-report=html --cov-report= {posargs:tests}
{envpython} scripts/dev/check_coverage.py {posargs}
[testenv:py34-cov]
@ -66,7 +66,7 @@ passenv = {[testenv]passenv}
deps = {[testenv]deps}
commands =
{envpython} scripts/link_pyqt.py --tox {envdir}
{envpython} -m py.test --cov --cov-report xml --cov-report=html --cov-report= {posargs:tests}
{envpython} -m pytest --cov --cov-report xml --cov-report=html --cov-report= {posargs:tests}
{envpython} scripts/dev/check_coverage.py {posargs}
[testenv:mkvenv]
@ -99,7 +99,7 @@ setenv =
QUTE_NO_DISPLAY=1
commands =
{envpython} scripts/link_pyqt.py --tox {envdir}
{envpython} -m py.test {posargs:tests}
{envpython} -m pytest {posargs:tests}
[testenv:misc]
ignore_errors = true