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. - Improved detection of URLs/search terms when pasting multiple lines.
- Don't remove `qutebrowser-editor-*` temporary file if editor subprocess crashed - Don't remove `qutebrowser-editor-*` temporary file if editor subprocess crashed
- Userscripts are also searched in `/usr/share/qutebrowser/userscripts`. - 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 Fixed
~~~~~ ~~~~~
@ -62,6 +64,7 @@ Fixed
- Fixed very long filenames when downloading `data://`-URLs. - Fixed very long filenames when downloading `data://`-URLs.
- Fixed ugly UI fonts on Windows when Liberation Mono is installed - 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 crash when unbinding key from a section which doesn't exist in the config
- Fixed report window after a segfault
v0.5.1 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`. 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 On Gentoo
--------- ---------
@ -135,6 +141,21 @@ it with:
$ nix-env -i qutebrowser $ 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 On Windows
---------- ----------
@ -214,7 +235,16 @@ it as part of the packaging process.
Installing qutebrowser with tox 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]: 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 * Thorsten Wißmann
* Austin Anderson * Austin Anderson
* Alexey "Averrin" Nabrodov * Alexey "Averrin" Nabrodov
* avk
* ZDarian * ZDarian
* Milan Svoboda * Milan Svoboda
* John ShaggyTwoDope Jenkins * John ShaggyTwoDope Jenkins
* Peter Vilim * Peter Vilim
* Clayton Craft
* Oliver Caldwell * Oliver Caldwell
* Philipp Hansch * Philipp Hansch
* Jonas Schürmann * Jonas Schürmann
@ -191,7 +193,6 @@ Contributors, sorted by the number of commits in descending order:
* Link * Link
* Larry Hynes * Larry Hynes
* Johannes Altmanninger * Johannes Altmanninger
* avk
* Samir Benmendil * Samir Benmendil
* Regina Hug * Regina Hug
* Mathias Fussenegger * Mathias Fussenegger
@ -199,9 +200,9 @@ Contributors, sorted by the number of commits in descending order:
* Fritz V155 Reichwald * Fritz V155 Reichwald
* Franz Fellner * Franz Fellner
* Corentin Jule * Corentin Jule
* Clayton Craft
* zwarag * zwarag
* xd1le * xd1le
* issue
* haxwithaxe * haxwithaxe
* evan * evan
* dylan araps * dylan araps

View File

@ -72,6 +72,8 @@
=== adblock-update === adblock-update
Update the adblock block lists. 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]]
=== back === back
Syntax: +:back [*--tab*] [*--bg*] [*--window*]+ 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 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 --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 -Suyy --noconfirm
RUN pacman-db-upgrade RUN pacman-db-upgrade
RUN pacman -S --noconfirm \ RUN pacman -S --noconfirm \
git \ git \
python-tox \ python-tox \
qt5-base-debug \ qt5-base \
qt5-webkit-debug \ qt5-webkit \
python-pyqt5-debug \ python-pyqt5 \
xorg-xinit \ xorg-xinit \
herbstluftwm \ herbstluftwm \
xorg-server-xvfb xorg-server-xvfb

View File

@ -92,9 +92,11 @@ class HostBlocker:
Attributes: Attributes:
_blocked_hosts: A set of blocked hosts. _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. _in_progress: The DownloadItems which are currently downloading.
_done_count: How many files have been read successfully. _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: Class attributes:
WHITELISTED: Hosts which never should be blocked. WHITELISTED: Hosts which never should be blocked.
@ -105,13 +107,22 @@ class HostBlocker:
def __init__(self): def __init__(self):
self._blocked_hosts = set() self._blocked_hosts = set()
self._config_blocked_hosts = set()
self._in_progress = [] self._in_progress = []
self._done_count = 0 self._done_count = 0
data_dir = standarddir.data() data_dir = standarddir.data()
if data_dir is None: if data_dir is None:
self._hosts_file = None self._local_hosts_file = None
else: 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) objreg.get('config').changed.connect(self.on_config_changed)
def is_blocked(self, url): def is_blocked(self, url):
@ -119,21 +130,46 @@ class HostBlocker:
if not config.get('content', 'host-blocking-enabled'): if not config.get('content', 'host-blocking-enabled'):
return False return False
host = url.host() 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): def read_hosts(self):
"""Read hosts from the existing blocked-hosts file.""" """Read hosts from the existing blocked-hosts file."""
self._blocked_hosts = set() self._blocked_hosts = set()
if self._hosts_file is None:
if self._local_hosts_file is None:
return return
if os.path.exists(self._hosts_file):
try: self._read_hosts_file(self._config_hosts_file,
with open(self._hosts_file, 'r', encoding='utf-8') as f: self._config_blocked_hosts)
for line in f:
self._blocked_hosts.add(line.strip()) found = self._read_hosts_file(self._local_hosts_file,
except OSError: self._blocked_hosts)
log.misc.exception("Failed to read host blocklist!")
else: if not found:
args = objreg.get('args') args = objreg.get('args')
if (config.get('content', 'host-block-lists') is not None and if (config.get('content', 'host-block-lists') is not None and
args.basedir is None): args.basedir is None):
@ -142,8 +178,14 @@ class HostBlocker:
@cmdutils.register(instance='host-blocker', win_id='win_id') @cmdutils.register(instance='host-blocker', win_id='win_id')
def adblock_update(self, win_id): def adblock_update(self, win_id):
"""Update the adblock block lists.""" """Update the adblock block lists.
if self._hosts_file is None:
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!") raise cmdexc.CommandError("No data storage is configured!")
self._blocked_hosts = set() self._blocked_hosts = set()
self._done_count = 0 self._done_count = 0
@ -221,7 +263,7 @@ class HostBlocker:
def on_lists_downloaded(self): def on_lists_downloaded(self):
"""Install block lists after files have been downloaded.""" """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): for host in sorted(self._blocked_hosts):
f.write(host + '\n') f.write(host + '\n')
message.info('current', "adblock: Read {} hosts from {} sources." message.info('current', "adblock: Read {} hosts from {} sources."
@ -233,7 +275,7 @@ class HostBlocker:
urls = config.get('content', 'host-block-lists') urls = config.get('content', 'host-block-lists')
if urls is None: if urls is None:
try: try:
os.remove(self._hosts_file) os.remove(self._local_hosts_file)
except OSError: except OSError:
log.misc.exception("Failed to delete hosts file.") 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 PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl
from qutebrowser.misc import httpclient
class PastebinClient(QObject): class PastebinClient(QObject):
@ -47,11 +45,17 @@ class PastebinClient(QObject):
success = pyqtSignal(str) success = pyqtSignal(str)
error = 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) super().__init__(parent)
self._client = httpclient.HTTPClient(self) client.setParent(self)
self._client.error.connect(self.error) client.error.connect(self.error)
self._client.success.connect(self.on_client_success) client.success.connect(self.on_client_success)
self._client = client
def paste(self, name, title, text, parent=None): def paste(self, name, title, text, parent=None):
"""Paste the text into a pastebin and return the URL. """Paste the text into a pastebin and return the URL.

View File

@ -219,14 +219,12 @@ class BrowserPage(QWebPage):
def _show_pdfjs(self, reply): def _show_pdfjs(self, reply):
"""Show the reply with pdfjs.""" """Show the reply with pdfjs."""
try: try:
page = pdfjs.generate_pdfjs_page(reply.url()).encode('utf-8') page = pdfjs.generate_pdfjs_page(reply.url())
except pdfjs.PDFJSNotFound: except pdfjs.PDFJSNotFound:
# pylint: disable=no-member page = jinja.render('no_pdfjs.html',
# WORKAROUND for https://bitbucket.org/logilab/pylint/issue/490/ url=reply.url().toDisplayString())
page = (jinja.env.get_template('no_pdfjs.html') self.mainFrame().setContent(page.encode('utf-8'), 'text/html',
.render(url=reply.url().toDisplayString()) reply.url())
.encode('utf-8'))
self.mainFrame().setContent(page, 'text/html', reply.url())
reply.deleteLater() reply.deleteLater()
def shutdown(self): 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 import qutebrowser
from qutebrowser.utils import version, log, utils, objreg, qtutils 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.browser.network import pastebin
from qutebrowser.config import config from qutebrowser.config import config
@ -141,7 +141,8 @@ class _CrashDialog(QDialog):
self.setWindowTitle("Whoops!") self.setWindowTitle("Whoops!")
self.resize(QSize(640, 600)) self.resize(QSize(640, 600))
self._vbox = QVBoxLayout(self) 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._pypi_client = autoupdate.PyPIVersionClient(self)
self._init_text() self._init_text()
@ -508,11 +509,23 @@ class FatalCrashDialog(_CrashDialog):
def _init_text(self): def _init_text(self):
super()._init_text() super()._init_text()
text = ("<b>qutebrowser was restarted after a fatal crash.</b><br/>" 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 " "contain the information necessary to fix an issue. Please "
"follow the steps in <a href='https://github.com/The-Compiler/" "follow the steps in <a href='https://github.com/The-Compiler/"
"qutebrowser/blob/master/doc/stacktrace.asciidoc'>" "qutebrowser/blob/master/doc/stacktrace.asciidoc'>"
"stacktrace.asciidoc</a> to submit a stacktrace.<br/>") "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) self._lbl.setText(text)
def _init_checkboxes(self): def _init_checkboxes(self):

View File

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

View File

@ -21,10 +21,12 @@
import os import os
import os.path import os.path
import traceback
import jinja2 import jinja2
import jinja2.exceptions
from qutebrowser.utils import utils from qutebrowser.utils import utils, log
from PyQt5.QtCore import QUrl from PyQt5.QtCore import QUrl
@ -74,8 +76,14 @@ def resource_url(path):
def render(template, **kwargs): def render(template, **kwargs):
"""Render the given template and pass the given arguments to it.""" """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 = jinja2.Environment(loader=Loader('html'), autoescape=_guess_autoescape)
_env.globals['resource_url'] = resource_url _env.globals['resource_url'] = resource_url

View File

@ -65,6 +65,8 @@ PERFECT_FILES = [
'qutebrowser/browser/network/filescheme.py'), 'qutebrowser/browser/network/filescheme.py'),
('tests/unit/browser/network/test_networkreply.py', ('tests/unit/browser/network/test_networkreply.py',
'qutebrowser/browser/network/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', ('tests/unit/browser/test_signalfilter.py',
'qutebrowser/browser/signalfilter.py'), 'qutebrowser/browser/signalfilter.py'),
@ -102,6 +104,8 @@ PERFECT_FILES = [
'qutebrowser/mainwindow/statusbar/textbase.py'), 'qutebrowser/mainwindow/statusbar/textbase.py'),
('tests/unit/mainwindow/statusbar/test_prompt.py', ('tests/unit/mainwindow/statusbar/test_prompt.py',
'qutebrowser/mainwindow/statusbar/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', ('tests/unit/config/test_configtypes.py',
'qutebrowser/config/configtypes.py'), 'qutebrowser/config/configtypes.py'),

View File

@ -31,23 +31,27 @@ import argparse
def main(): def main():
args = get_args() args = get_args()
if args.browser in ['chromium', 'firefox', 'ie']: if args.browser in ['chromium', 'firefox', 'ie']:
import_netscape_bookmarks(args.bookmarks) import_netscape_bookmarks(args.bookmarks, args.bookmark_format)
def get_args(): def get_args():
"""Get the argparse parser.""" """Get the argparse parser."""
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
epilog="To import bookmarks from Chromium, Firefox or IE, " epilog="To import bookmarks from Chromium, Firefox or IE, "
"export them to HTML in your browsers bookmark manager.") "export them to HTML in your browsers bookmark manager. "
parser.add_argument('browser', help="Which browser?", "By default, this script will output in a quickmarks format.")
parser.add_argument('browser', help="Which browser? (chromium, firefox)",
choices=['chromium', 'firefox', 'ie'], choices=['chromium', 'firefox', 'ie'],
metavar='browser') 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() args = parser.parse_args()
return 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. """Import bookmarks from a NETSCAPE-Bookmark-file v1.
Generated by Chromium, Firefox, IE and possibly more browsers 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') soup = bs4.BeautifulSoup(f, 'html.parser')
html_tags = soup.findAll('a') html_tags = soup.findAll('a')
if is_bookmark_format:
output_template = '{tag[href]} {tag.string}'
else:
output_template = '{tag.string} {tag[href]}'
bookmarks = [] bookmarks = []
for tag in html_tags: for tag in html_tags:
if tag['href'] not in bookmarks: 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: for bookmark in bookmarks:
print(bookmark) 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 os.path
import pytest import pytest
import logging
import jinja2 import jinja2
from PyQt5.QtCore import QUrl from PyQt5.QtCore import QUrl
from qutebrowser.utils import jinja from qutebrowser.utils import utils, jinja
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def patch_read_file(monkeypatch): def patch_read_file(monkeypatch):
"""pytest fixture to patch utils.read_file.""" """pytest fixture to patch utils.read_file."""
real_read_file = utils.read_file
def _read_file(path): def _read_file(path):
"""A read_file which returns a simple template if the path is right.""" """A read_file which returns a simple template if the path is right."""
if path == os.path.join('html', 'test.html'): if path == os.path.join('html', 'test.html'):
return """Hello {{var}}""" return """Hello {{var}}"""
elif path == os.path.join('html', 'test2.html'): elif path == os.path.join('html', 'test2.html'):
return """{{ resource_url('utils/testfile') }}""" 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: else:
raise IOError("Invalid path {}!".format(path)) raise IOError("Invalid path {}!".format(path))
@ -87,6 +94,18 @@ def test_utf8():
assert data == "Hello \u2603" 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', [ @pytest.mark.parametrize('name, expected', [
(None, False), (None, False),
('foo', False), ('foo', False),

12
tox.ini
View File

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