parent
40b26d7492
commit
55a4eb18f2
@ -4,7 +4,6 @@ hg+https://bitbucket.org/ned/coveragepy
|
||||
git+https://github.com/micheles/decorator.git
|
||||
git+https://github.com/pallets/flask.git
|
||||
git+https://github.com/miracle2k/python-glob2.git
|
||||
git+https://github.com/Runscope/httpbin.git
|
||||
git+https://github.com/HypothesisWorks/hypothesis-python.git
|
||||
git+https://github.com/pallets/itsdangerous.git
|
||||
git+https://bitbucket.org/zzzeek/mako.git
|
||||
|
@ -3,7 +3,6 @@ cheroot
|
||||
coverage
|
||||
Flask
|
||||
hunter
|
||||
httpbin
|
||||
hypothesis
|
||||
pytest
|
||||
pytest-bdd
|
||||
|
@ -34,7 +34,7 @@ from PyQt5.QtCore import PYQT_VERSION
|
||||
|
||||
pytest.register_assert_rewrite('end2end.fixtures')
|
||||
|
||||
from end2end.fixtures.webserver import httpbin, httpbin_after_test, ssl_server
|
||||
from end2end.fixtures.webserver import server, server_after_test, ssl_server
|
||||
from end2end.fixtures.quteprocess import (quteproc_process, quteproc,
|
||||
quteproc_new)
|
||||
from end2end.fixtures.testprocess import pytest_runtest_makereport
|
||||
|
@ -5,7 +5,7 @@
|
||||
<title>Failing download when redirecting/aborting</title>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/custom/redirect-later?delay=1">redirect after 1s</a>
|
||||
<a href="/redirect-later?delay=1">redirect after 1s</a>
|
||||
<a href="/does-not-exist">404</a>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -116,14 +116,14 @@ def pytest_runtest_makereport(item, call):
|
||||
|
||||
|
||||
@bdd.given(bdd.parsers.parse("I set {opt} to {value}"))
|
||||
def set_setting_given(quteproc, httpbin, opt, value):
|
||||
def set_setting_given(quteproc, server, opt, value):
|
||||
"""Set a qutebrowser setting.
|
||||
|
||||
This is available as "Given:" step so it can be used as "Background:".
|
||||
"""
|
||||
if value == '<empty>':
|
||||
value = ''
|
||||
value = value.replace('(port)', str(httpbin.port))
|
||||
value = value.replace('(port)', str(server.port))
|
||||
quteproc.set_setting(opt, value)
|
||||
|
||||
|
||||
@ -174,7 +174,7 @@ def pdfjs_available():
|
||||
|
||||
|
||||
@bdd.when(bdd.parsers.parse("I open {path}"))
|
||||
def open_path(quteproc, httpbin, path):
|
||||
def open_path(quteproc, server, path):
|
||||
"""Open a URL.
|
||||
|
||||
- If used like "When I open ... in a new tab", the URL is opened in a new
|
||||
@ -183,7 +183,7 @@ def open_path(quteproc, httpbin, path):
|
||||
- With "... in a private window" it's opened in a new private window.
|
||||
- With "... as a URL", it's opened according to new_instance_open_target.
|
||||
"""
|
||||
path = path.replace('(port)', str(httpbin.port))
|
||||
path = path.replace('(port)', str(server.port))
|
||||
|
||||
new_tab = False
|
||||
new_bg_tab = False
|
||||
@ -227,16 +227,16 @@ def open_path(quteproc, httpbin, path):
|
||||
|
||||
|
||||
@bdd.when(bdd.parsers.parse("I set {opt} to {value}"))
|
||||
def set_setting(quteproc, httpbin, opt, value):
|
||||
def set_setting(quteproc, server, opt, value):
|
||||
"""Set a qutebrowser setting."""
|
||||
if value == '<empty>':
|
||||
value = ''
|
||||
value = value.replace('(port)', str(httpbin.port))
|
||||
value = value.replace('(port)', str(server.port))
|
||||
quteproc.set_setting(opt, value)
|
||||
|
||||
|
||||
@bdd.when(bdd.parsers.parse("I run {command}"))
|
||||
def run_command(quteproc, httpbin, tmpdir, command):
|
||||
def run_command(quteproc, server, tmpdir, command):
|
||||
"""Run a qutebrowser command.
|
||||
|
||||
The suffix "with count ..." can be used to pass a count to the command.
|
||||
@ -254,7 +254,7 @@ def run_command(quteproc, httpbin, tmpdir, command):
|
||||
else:
|
||||
invalid = False
|
||||
|
||||
command = command.replace('(port)', str(httpbin.port))
|
||||
command = command.replace('(port)', str(server.port))
|
||||
command = command.replace('(testdata)', utils.abs_datapath())
|
||||
command = command.replace('(tmpdir)', str(tmpdir))
|
||||
command = command.replace('(dirsep)', os.sep)
|
||||
@ -264,9 +264,9 @@ def run_command(quteproc, httpbin, tmpdir, command):
|
||||
|
||||
|
||||
@bdd.when(bdd.parsers.parse("I reload"))
|
||||
def reload(qtbot, httpbin, quteproc, command):
|
||||
def reload(qtbot, server, quteproc, command):
|
||||
"""Reload and wait until a new request is received."""
|
||||
with qtbot.waitSignal(httpbin.new_request):
|
||||
with qtbot.waitSignal(server.new_request):
|
||||
quteproc.send_cmd(':reload')
|
||||
|
||||
|
||||
@ -294,10 +294,10 @@ def wait_in_log(quteproc, is_regex, pattern, do_skip):
|
||||
|
||||
@bdd.when(bdd.parsers.re(r'I wait for the (?P<category>error|message|warning) '
|
||||
r'"(?P<message>.*)"'))
|
||||
def wait_for_message(quteproc, httpbin, category, message):
|
||||
def wait_for_message(quteproc, server, category, message):
|
||||
"""Wait for a given statusbar message/error/warning."""
|
||||
quteproc.log_summary('Waiting for {} "{}"'.format(category, message))
|
||||
expect_message(quteproc, httpbin, category, message)
|
||||
expect_message(quteproc, server, category, message)
|
||||
|
||||
|
||||
@bdd.when(bdd.parsers.parse("I wait {delay}s"))
|
||||
@ -328,8 +328,8 @@ def selection_not_supported(qapp):
|
||||
|
||||
@bdd.when(bdd.parsers.re(r'I put "(?P<content>.*)" into the '
|
||||
r'(?P<what>primary selection|clipboard)'))
|
||||
def fill_clipboard(quteproc, httpbin, what, content):
|
||||
content = content.replace('(port)', str(httpbin.port))
|
||||
def fill_clipboard(quteproc, server, what, content):
|
||||
content = content.replace('(port)', str(server.port))
|
||||
content = content.replace(r'\n', '\n')
|
||||
quteproc.send_cmd(':debug-set-fake-clipboard "{}"'.format(content))
|
||||
|
||||
@ -337,8 +337,8 @@ def fill_clipboard(quteproc, httpbin, what, content):
|
||||
@bdd.when(bdd.parsers.re(r'I put the following lines into the '
|
||||
r'(?P<what>primary selection|clipboard):\n'
|
||||
r'(?P<content>.+)$', flags=re.DOTALL))
|
||||
def fill_clipboard_multiline(quteproc, httpbin, what, content):
|
||||
fill_clipboard(quteproc, httpbin, what, textwrap.dedent(content))
|
||||
def fill_clipboard_multiline(quteproc, server, what, content):
|
||||
fill_clipboard(quteproc, server, what, textwrap.dedent(content))
|
||||
|
||||
|
||||
@bdd.when(bdd.parsers.parse('I hint with args "{args}"'))
|
||||
@ -395,28 +395,28 @@ def path_should_be_loaded(quteproc, path):
|
||||
|
||||
|
||||
@bdd.then(bdd.parsers.parse("{path} should be requested"))
|
||||
def path_should_be_requested(httpbin, path):
|
||||
def path_should_be_requested(server, path):
|
||||
"""Make sure the given path was loaded from the webserver."""
|
||||
httpbin.wait_for(verb='GET', path='/' + path)
|
||||
server.wait_for(verb='GET', path='/' + path)
|
||||
|
||||
|
||||
@bdd.then(bdd.parsers.parse("The requests should be:\n{pages}"))
|
||||
def list_of_requests(httpbin, pages):
|
||||
def list_of_requests(server, pages):
|
||||
"""Make sure the given requests were done from the webserver."""
|
||||
expected_requests = [httpbin.ExpectedRequest('GET', '/' + path.strip())
|
||||
expected_requests = [server.ExpectedRequest('GET', '/' + path.strip())
|
||||
for path in pages.split('\n')]
|
||||
actual_requests = httpbin.get_requests()
|
||||
actual_requests = server.get_requests()
|
||||
assert actual_requests == expected_requests
|
||||
|
||||
|
||||
@bdd.then(bdd.parsers.parse("The unordered requests should be:\n{pages}"))
|
||||
def list_of_requests_unordered(httpbin, pages):
|
||||
def list_of_requests_unordered(server, pages):
|
||||
"""Make sure the given requests were done (in no particular order)."""
|
||||
expected_requests = [httpbin.ExpectedRequest('GET', '/' + path.strip())
|
||||
expected_requests = [server.ExpectedRequest('GET', '/' + path.strip())
|
||||
for path in pages.split('\n')]
|
||||
actual_requests = httpbin.get_requests()
|
||||
actual_requests = server.get_requests()
|
||||
# Requests are not hashable, we need to convert to ExpectedRequests
|
||||
actual_requests = [httpbin.ExpectedRequest.from_request(req)
|
||||
actual_requests = [server.ExpectedRequest.from_request(req)
|
||||
for req in actual_requests]
|
||||
assert (collections.Counter(actual_requests) ==
|
||||
collections.Counter(expected_requests))
|
||||
@ -424,14 +424,14 @@ def list_of_requests_unordered(httpbin, pages):
|
||||
|
||||
@bdd.then(bdd.parsers.re(r'the (?P<category>error|message|warning) '
|
||||
r'"(?P<message>.*)" should be shown'))
|
||||
def expect_message(quteproc, httpbin, category, message):
|
||||
def expect_message(quteproc, server, category, message):
|
||||
"""Expect the given message in the qutebrowser log."""
|
||||
category_to_loglevel = {
|
||||
'message': logging.INFO,
|
||||
'error': logging.ERROR,
|
||||
'warning': logging.WARNING,
|
||||
}
|
||||
message = message.replace('(port)', str(httpbin.port))
|
||||
message = message.replace('(port)', str(server.port))
|
||||
quteproc.mark_expected(category='message',
|
||||
loglevel=category_to_loglevel[category],
|
||||
message=message)
|
||||
@ -439,12 +439,12 @@ def expect_message(quteproc, httpbin, category, message):
|
||||
|
||||
@bdd.then(bdd.parsers.re(r'(?P<is_regex>regex )?"(?P<pattern>[^"]+)" should '
|
||||
r'be logged'))
|
||||
def should_be_logged(quteproc, httpbin, is_regex, pattern):
|
||||
def should_be_logged(quteproc, server, is_regex, pattern):
|
||||
"""Expect the given pattern on regex in the log."""
|
||||
if is_regex:
|
||||
pattern = re.compile(pattern)
|
||||
else:
|
||||
pattern = pattern.replace('(port)', str(httpbin.port))
|
||||
pattern = pattern.replace('(port)', str(server.port))
|
||||
line = quteproc.wait_for(message=pattern)
|
||||
line.expected = True
|
||||
|
||||
@ -493,7 +493,7 @@ def no_crash():
|
||||
def check_header(quteproc, header, value):
|
||||
"""Check if a given header is set correctly.
|
||||
|
||||
This assumes we're on the httpbin header page.
|
||||
This assumes we're on the server header page.
|
||||
"""
|
||||
content = quteproc.get_content()
|
||||
data = json.loads(content)
|
||||
@ -589,16 +589,16 @@ def check_open_tabs(quteproc, request, tabs):
|
||||
|
||||
@bdd.then(bdd.parsers.re(r'the (?P<what>primary selection|clipboard) should '
|
||||
r'contain "(?P<content>.*)"'))
|
||||
def clipboard_contains(quteproc, httpbin, what, content):
|
||||
expected = content.replace('(port)', str(httpbin.port))
|
||||
def clipboard_contains(quteproc, server, what, content):
|
||||
expected = content.replace('(port)', str(server.port))
|
||||
expected = expected.replace('\\n', '\n')
|
||||
quteproc.wait_for(message='Setting fake {}: {}'.format(
|
||||
what, json.dumps(expected)))
|
||||
|
||||
|
||||
@bdd.then(bdd.parsers.parse('the clipboard should contain:\n{content}'))
|
||||
def clipboard_contains_multiline(quteproc, httpbin, content):
|
||||
expected = textwrap.dedent(content).replace('(port)', str(httpbin.port))
|
||||
def clipboard_contains_multiline(quteproc, server, content):
|
||||
expected = textwrap.dedent(content).replace('(port)', str(server.port))
|
||||
quteproc.wait_for(message='Setting fake clipboard: {}'.format(
|
||||
json.dumps(expected)))
|
||||
|
||||
|
@ -292,7 +292,7 @@ Feature: Downloading things from a website.
|
||||
|
||||
Scenario: Opening a mhtml download directly
|
||||
When I set downloads.location.prompt to true
|
||||
And I open html
|
||||
And I open /
|
||||
And I run :download --mhtml
|
||||
And I wait for the download prompt for "*"
|
||||
And I directly open the download
|
||||
@ -572,27 +572,27 @@ Feature: Downloading things from a website.
|
||||
|
||||
Scenario: Downloading with redirect to itself
|
||||
When I set downloads.location.prompt to false
|
||||
And I run :download http://localhost:(port)/custom/redirect-self
|
||||
And I run :download http://localhost:(port)/redirect-self
|
||||
And I wait until the download is finished
|
||||
Then the downloaded file redirect-self should exist
|
||||
|
||||
Scenario: Downloading with absolute redirect
|
||||
When I set downloads.location.prompt to false
|
||||
And I run :download http://localhost:(port)/absolute-redirect/1
|
||||
And I run :download http://localhost:(port)/absolute-redirect
|
||||
And I wait until the download is finished
|
||||
Then the downloaded file 1 should exist
|
||||
Then the downloaded file absolute-redirect should exist
|
||||
|
||||
Scenario: Downloading with relative redirect
|
||||
When I set downloads.location.prompt to false
|
||||
And I run :download http://localhost:(port)/relative-redirect/1
|
||||
And I run :download http://localhost:(port)/relative-redirect
|
||||
And I wait until the download is finished
|
||||
Then the downloaded file 1 should exist
|
||||
Then the downloaded file relative-redirect should exist
|
||||
|
||||
## Other
|
||||
|
||||
Scenario: Download without a content-size
|
||||
When I set downloads.location.prompt to false
|
||||
When I run :download http://localhost:(port)/custom/content-size
|
||||
When I run :download http://localhost:(port)/content-size
|
||||
And I wait until the download is finished
|
||||
Then the downloaded file content-size should exist
|
||||
|
||||
@ -604,13 +604,13 @@ Feature: Downloading things from a website.
|
||||
|
||||
Scenario: Downloading 20MB file
|
||||
When I set downloads.location.prompt to false
|
||||
And I run :download http://localhost:(port)/custom/twenty-mb
|
||||
And I run :download http://localhost:(port)/twenty-mb
|
||||
And I wait until the download is finished
|
||||
Then the downloaded file twenty-mb should be 20971520 bytes big
|
||||
|
||||
Scenario: Downloading 20MB file with late prompt confirmation
|
||||
When I set downloads.location.prompt to true
|
||||
And I run :download http://localhost:(port)/custom/twenty-mb
|
||||
And I run :download http://localhost:(port)/twenty-mb
|
||||
And I wait 1s
|
||||
And I run :prompt-accept
|
||||
And I wait until the download is finished
|
||||
@ -645,12 +645,6 @@ Feature: Downloading things from a website.
|
||||
Then the downloaded file download.bin should exist
|
||||
And the downloaded file download2.bin should not exist
|
||||
|
||||
Scenario: Downloading a file with unknown size
|
||||
When I set downloads.location.prompt to false
|
||||
And I open stream-bytes/1024 without waiting
|
||||
And I wait until the download is finished
|
||||
Then the downloaded file 1024 should exist
|
||||
|
||||
@qtwebengine_skip: We can't get the UA from the page there
|
||||
Scenario: user-agent when using :download
|
||||
When I open user-agent
|
||||
@ -660,16 +654,14 @@ Feature: Downloading things from a website.
|
||||
|
||||
@qtwebengine_skip: We can't get the UA from the page there
|
||||
Scenario: user-agent when using hints
|
||||
When I set hints.mode to number
|
||||
And I open /
|
||||
When I open /
|
||||
And I run :hint links download
|
||||
And I press the keys "us" # user-agent
|
||||
And I run :follow-hint 0
|
||||
And I run :follow-hint a
|
||||
And I wait until the download is finished
|
||||
Then the downloaded file user-agent should contain Safari/
|
||||
|
||||
@qtwebengine_skip: Handled by QtWebEngine, not by us
|
||||
Scenario: Downloading a "Internal server error" with disposition: inline (#2304)
|
||||
When I set downloads.location.prompt to false
|
||||
And I open custom/500-inline
|
||||
And I open 500-inline
|
||||
Then the error "Download error: *INTERNAL SERVER ERROR" should be shown
|
||||
|
@ -46,10 +46,10 @@ Feature: Page history
|
||||
|
||||
@qtwebengine_todo: Error page message is not implemented
|
||||
Scenario: History with a 404
|
||||
When I open status/404 without waiting
|
||||
And I wait for "Error while loading http://localhost:*/status/404: NOT FOUND" in the log
|
||||
When I open 404 without waiting
|
||||
And I wait for "Error while loading http://localhost:*/404: NOT FOUND" in the log
|
||||
Then the history should contain:
|
||||
http://localhost:(port)/status/404 Error loading page: http://localhost:(port)/status/404
|
||||
http://localhost:(port)/404 Error loading page: http://localhost:(port)/404
|
||||
|
||||
Scenario: History with invalid URL
|
||||
When I run :tab-only
|
||||
|
@ -186,15 +186,15 @@ Feature: Various utility commands.
|
||||
Given I have a fresh instance
|
||||
# We can't use "When I open" because we don't want to wait for load
|
||||
# finished
|
||||
When I run :open http://localhost:(port)/custom/redirect-later?delay=-1
|
||||
When I run :open http://localhost:(port)/redirect-later?delay=-1
|
||||
And I wait for "emitting: cur_load_status_changed('loading') (tab *)" in the log
|
||||
And I wait 1s
|
||||
And I run :stop
|
||||
And I open custom/redirect-later-continue in a new tab
|
||||
And I open redirect-later-continue in a new tab
|
||||
And I wait 1s
|
||||
Then the unordered requests should be:
|
||||
custom/redirect-later-continue
|
||||
custom/redirect-later?delay=-1
|
||||
redirect-later-continue
|
||||
redirect-later?delay=-1
|
||||
# no request on / because we stopped the redirect
|
||||
|
||||
Scenario: :stop with wrong count
|
||||
|
@ -25,7 +25,7 @@ bdd.scenarios('adblock.feature')
|
||||
|
||||
|
||||
@bdd.when(bdd.parsers.parse('I set up "{lists}" as block lists'))
|
||||
def set_up_blocking(quteproc, lists, httpbin):
|
||||
url = 'http://localhost:{}/data/adblock/'.format(httpbin.port)
|
||||
def set_up_blocking(quteproc, lists, server):
|
||||
url = 'http://localhost:{}/data/adblock/'.format(server.port)
|
||||
urls = [url + item.strip() for item in lists.split(',')]
|
||||
quteproc.set_setting('content.host_blocking.lists', json.dumps(urls))
|
||||
|
@ -27,9 +27,9 @@ bdd.scenarios('editor.feature')
|
||||
|
||||
@bdd.when(bdd.parsers.parse('I set up a fake editor replacing "{text}" by '
|
||||
'"{replacement}"'))
|
||||
def set_up_editor_replacement(quteproc, httpbin, tmpdir, text, replacement):
|
||||
def set_up_editor_replacement(quteproc, server, tmpdir, text, replacement):
|
||||
"""Set up editor.command to a small python script doing a replacement."""
|
||||
text = text.replace('(port)', str(httpbin.port))
|
||||
text = text.replace('(port)', str(server.port))
|
||||
script = tmpdir / 'script.py'
|
||||
script.write(textwrap.dedent("""
|
||||
import sys
|
||||
@ -47,7 +47,7 @@ def set_up_editor_replacement(quteproc, httpbin, tmpdir, text, replacement):
|
||||
|
||||
|
||||
@bdd.when(bdd.parsers.parse('I set up a fake editor returning "{text}"'))
|
||||
def set_up_editor(quteproc, httpbin, tmpdir, text):
|
||||
def set_up_editor(quteproc, server, tmpdir, text):
|
||||
"""Set up editor.command to a small python script inserting a text."""
|
||||
script = tmpdir / 'script.py'
|
||||
script.write(textwrap.dedent("""
|
||||
|
@ -35,7 +35,7 @@ def turn_on_sql_history(quteproc):
|
||||
|
||||
|
||||
@bdd.then(bdd.parsers.parse("the history should contain:\n{expected}"))
|
||||
def check_history(quteproc, httpbin, tmpdir, expected):
|
||||
def check_history(quteproc, server, tmpdir, expected):
|
||||
path = tmpdir / 'history'
|
||||
quteproc.send_cmd(':debug-dump-history "{}"'.format(path))
|
||||
quteproc.wait_for(category='message', loglevel=logging.INFO,
|
||||
@ -45,10 +45,10 @@ def check_history(quteproc, httpbin, tmpdir, expected):
|
||||
# ignore access times, they will differ in each run
|
||||
actual = '\n'.join(re.sub('^\\d+-?', '', line).strip() for line in f)
|
||||
|
||||
expected = expected.replace('(port)', str(httpbin.port))
|
||||
expected = expected.replace('(port)', str(server.port))
|
||||
assert actual == expected
|
||||
|
||||
|
||||
@bdd.then("the history should be empty")
|
||||
def check_history_empty(quteproc, httpbin, tmpdir):
|
||||
check_history(quteproc, httpbin, tmpdir, '')
|
||||
def check_history_empty(quteproc, server, tmpdir):
|
||||
check_history(quteproc, server, tmpdir, '')
|
||||
|
@ -27,7 +27,7 @@ bdd.scenarios('private.feature')
|
||||
def check_cookie(quteproc, name, value):
|
||||
"""Check if a given cookie is set correctly.
|
||||
|
||||
This assumes we're on the httpbin cookies page.
|
||||
This assumes we're on the server cookies page.
|
||||
"""
|
||||
content = quteproc.get_content()
|
||||
data = json.loads(content)
|
||||
|
@ -34,13 +34,13 @@ def create_session_file(quteproc, name, contents):
|
||||
|
||||
@bdd.when(bdd.parsers.parse('I replace "{pattern}" by "{replacement}" in the '
|
||||
'"{name}" session file'))
|
||||
def session_replace(quteproc, httpbin, pattern, replacement, name):
|
||||
def session_replace(quteproc, server, pattern, replacement, name):
|
||||
# First wait until the session was actually saved
|
||||
quteproc.wait_for(category='message', loglevel=logging.INFO,
|
||||
message='Saved session {}.'.format(name))
|
||||
filename = os.path.join(quteproc.basedir, 'data', 'sessions',
|
||||
name + '.yml')
|
||||
replacement = replacement.replace('(port)', str(httpbin.port)) # yo dawg
|
||||
replacement = replacement.replace('(port)', str(server.port)) # yo dawg
|
||||
with open(filename, 'r', encoding='utf-8') as f:
|
||||
data = f.read()
|
||||
with open(filename, 'w', encoding='utf-8') as f:
|
||||
|
@ -415,10 +415,10 @@ class QuteProc(testprocess.Process):
|
||||
if any(path.startswith(scheme) for scheme in special_schemes):
|
||||
return path
|
||||
else:
|
||||
httpbin = self.request.getfixturevalue('httpbin')
|
||||
server = self.request.getfixturevalue('server')
|
||||
return '{}://localhost:{}/{}'.format(
|
||||
'https' if https else 'http',
|
||||
httpbin.port if port is None else port,
|
||||
server.port if port is None else port,
|
||||
path if path != '/' else '')
|
||||
|
||||
def wait_for_js(self, message):
|
||||
@ -778,7 +778,7 @@ def _xpath_escape(text):
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def quteproc_process(qapp, httpbin, request):
|
||||
def quteproc_process(qapp, server, request):
|
||||
"""Fixture for qutebrowser process which is started once per file."""
|
||||
# Passing request so it has an initial config
|
||||
proc = QuteProc(request)
|
||||
@ -788,7 +788,7 @@ def quteproc_process(qapp, httpbin, request):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def quteproc(quteproc_process, httpbin, request):
|
||||
def quteproc(quteproc_process, server, request):
|
||||
"""Per-test qutebrowser fixture which uses the per-file process."""
|
||||
request.node._quteproc_log = quteproc_process.captured_log
|
||||
quteproc_process.before_test()
|
||||
@ -798,7 +798,7 @@ def quteproc(quteproc_process, httpbin, request):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def quteproc_new(qapp, httpbin, request):
|
||||
def quteproc_new(qapp, server, request):
|
||||
"""Per-test qutebrowser process to test invocations."""
|
||||
proc = QuteProc(request)
|
||||
request.node._quteproc_log = proc.captured_log
|
||||
|
@ -66,23 +66,23 @@ class FakeRequest:
|
||||
|
||||
"""Fake for request."""
|
||||
|
||||
def __init__(self, node, config, httpbin):
|
||||
def __init__(self, node, config, server):
|
||||
self.node = node
|
||||
self.config = config
|
||||
self._httpbin = httpbin
|
||||
self._server = server
|
||||
|
||||
def getfixturevalue(self, name):
|
||||
assert name == 'httpbin'
|
||||
return self._httpbin
|
||||
assert name == 'server'
|
||||
return self._server
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def request_mock(quteproc, monkeypatch, httpbin):
|
||||
def request_mock(quteproc, monkeypatch, server):
|
||||
"""Patch out a pytest request."""
|
||||
fake_call = FakeRepCall()
|
||||
fake_config = FakeConfig()
|
||||
fake_node = FakeNode(fake_call)
|
||||
fake_request = FakeRequest(fake_node, fake_config, httpbin)
|
||||
fake_request = FakeRequest(fake_node, fake_config, server)
|
||||
assert not hasattr(fake_request.node.rep_call, 'wasxfail')
|
||||
monkeypatch.setattr(quteproc, 'request', fake_request)
|
||||
return fake_request
|
||||
|
@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Test the httpbin webserver used for tests."""
|
||||
"""Test the server webserver used for tests."""
|
||||
|
||||
import json
|
||||
import urllib.request
|
||||
@ -27,14 +27,14 @@ import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize('path, content, expected', [
|
||||
('/', '<title>httpbin(1): HTTP Client Testing Service</title>', True),
|
||||
# https://github.com/Runscope/httpbin/issues/245
|
||||
('/', 'qutebrowser test webserver', True),
|
||||
# https://github.com/Runscope/server/issues/245
|
||||
('/', 'www.google-analytics.com', False),
|
||||
('/data/hello.txt', 'Hello World!', True),
|
||||
])
|
||||
def test_httpbin(httpbin, qtbot, path, content, expected):
|
||||
with qtbot.waitSignal(httpbin.new_request, timeout=100):
|
||||
url = 'http://localhost:{}{}'.format(httpbin.port, path)
|
||||
def test_server(server, qtbot, path, content, expected):
|
||||
with qtbot.waitSignal(server.new_request, timeout=100):
|
||||
url = 'http://localhost:{}{}'.format(server.port, path)
|
||||
try:
|
||||
response = urllib.request.urlopen(url)
|
||||
except urllib.error.HTTPError as e:
|
||||
@ -47,7 +47,7 @@ def test_httpbin(httpbin, qtbot, path, content, expected):
|
||||
|
||||
data = response.read().decode('utf-8')
|
||||
|
||||
assert httpbin.get_requests() == [httpbin.ExpectedRequest('GET', path)]
|
||||
assert server.get_requests() == [server.ExpectedRequest('GET', path)]
|
||||
assert (content in data) == expected
|
||||
|
||||
|
||||
@ -58,7 +58,7 @@ def test_httpbin(httpbin, qtbot, path, content, expected):
|
||||
({'verb': 'GET', 'path': '/', 'status': 200}, 'GET', '/foo', False),
|
||||
({'verb': 'POST', 'path': '/', 'status': 200}, 'GET', '/', False),
|
||||
])
|
||||
def test_expected_request(httpbin, line, verb, path, equal):
|
||||
expected = httpbin.ExpectedRequest(verb, path)
|
||||
request = httpbin.Request(json.dumps(line))
|
||||
def test_expected_request(server, line, verb, path, equal):
|
||||
expected = server.ExpectedRequest(verb, path)
|
||||
request = server.Request(json.dumps(line))
|
||||
assert (expected == request) == equal
|
||||
|
@ -90,7 +90,7 @@ def _render_log(data, threshold=100):
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_runtest_makereport(item, call):
|
||||
"""Add qutebrowser/httpbin sections to captured output if a test failed."""
|
||||
"""Add qutebrowser/server sections to captured output if a test failed."""
|
||||
outcome = yield
|
||||
if call.when not in ['call', 'teardown']:
|
||||
return
|
||||
@ -100,7 +100,7 @@ def pytest_runtest_makereport(item, call):
|
||||
return
|
||||
|
||||
quteproc_log = getattr(item, '_quteproc_log', None)
|
||||
httpbin_log = getattr(item, '_httpbin_log', None)
|
||||
server_log = getattr(item, '_server_log', None)
|
||||
|
||||
if not hasattr(report.longrepr, 'addsection'):
|
||||
# In some conditions (on macOS and Windows it seems), report.longrepr
|
||||
@ -114,8 +114,8 @@ def pytest_runtest_makereport(item, call):
|
||||
if quteproc_log is not None:
|
||||
report.longrepr.addsection("qutebrowser output",
|
||||
_render_log(quteproc_log))
|
||||
if httpbin_log is not None:
|
||||
report.longrepr.addsection("httpbin output", _render_log(httpbin_log))
|
||||
if server_log is not None:
|
||||
report.longrepr.addsection("server output", _render_log(server_log))
|
||||
|
||||
|
||||
class Process(QObject):
|
||||
|
@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Fixtures for the httpbin webserver."""
|
||||
"""Fixtures for the server webserver."""
|
||||
|
||||
import re
|
||||
import sys
|
||||
@ -35,7 +35,7 @@ from qutebrowser.utils import utils
|
||||
|
||||
class Request(testprocess.Line):
|
||||
|
||||
"""A parsed line from the httpbin/flask log output.
|
||||
"""A parsed line from the flask log output.
|
||||
|
||||
Attributes:
|
||||
verb/path/status: Parsed from the log output.
|
||||
@ -67,22 +67,20 @@ class Request(testprocess.Line):
|
||||
'/favicon.ico': [http.client.NOT_FOUND],
|
||||
'/does-not-exist': [http.client.NOT_FOUND],
|
||||
'/does-not-exist-2': [http.client.NOT_FOUND],
|
||||
'/status/404': [http.client.NOT_FOUND],
|
||||
'/404': [http.client.NOT_FOUND],
|
||||
|
||||
'/custom/redirect-later': [http.client.FOUND],
|
||||
'/custom/redirect-self': [http.client.FOUND],
|
||||
'/redirect-later': [http.client.FOUND],
|
||||
'/redirect-self': [http.client.FOUND],
|
||||
'/redirect-to': [http.client.FOUND],
|
||||
'/relative-redirect': [http.client.FOUND],
|
||||
'/absolute-redirect': [http.client.FOUND],
|
||||
|
||||
'/cookies/set': [http.client.FOUND],
|
||||
|
||||
'/custom/500-inline': [http.client.INTERNAL_SERVER_ERROR],
|
||||
'/500-inline': [http.client.INTERNAL_SERVER_ERROR],
|
||||
}
|
||||
for i in range(15):
|
||||
path_to_statuses['/redirect/{}'.format(i)] = [http.client.FOUND]
|
||||
path_to_statuses['/relative-redirect/{}'.format(i)] = [
|
||||
http.client.FOUND]
|
||||
path_to_statuses['/absolute-redirect/{}'.format(i)] = [
|
||||
http.client.FOUND]
|
||||
for suffix in ['', '1', '2', '3', '4', '5', '6']:
|
||||
key = '/basic-auth/user{}/password{}'.format(suffix, suffix)
|
||||
path_to_statuses[key] = [http.client.UNAUTHORIZED, http.client.OK]
|
||||
@ -130,7 +128,7 @@ class ExpectedRequest:
|
||||
|
||||
class WebserverProcess(testprocess.Process):
|
||||
|
||||
"""Abstraction over a running HTTPbin server process.
|
||||
"""Abstraction over a running Flask server process.
|
||||
|
||||
Reads the log from its stdout and parses it.
|
||||
|
||||
@ -186,31 +184,31 @@ class WebserverProcess(testprocess.Process):
|
||||
|
||||
|
||||
@pytest.fixture(scope='session', autouse=True)
|
||||
def httpbin(qapp):
|
||||
"""Fixture for an httpbin object which ensures clean setup/teardown."""
|
||||
httpbin = WebserverProcess('webserver_sub')
|
||||
httpbin.start()
|
||||
yield httpbin
|
||||
httpbin.cleanup()
|
||||
def server(qapp):
|
||||
"""Fixture for an server object which ensures clean setup/teardown."""
|
||||
server = WebserverProcess('webserver_sub')
|
||||
server.start()
|
||||
yield server
|
||||
server.cleanup()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def httpbin_after_test(httpbin, request):
|
||||
"""Fixture to clean httpbin request list after each test."""
|
||||
request.node._httpbin_log = httpbin.captured_log
|
||||
def server_after_test(server, request):
|
||||
"""Fixture to clean server request list after each test."""
|
||||
request.node._server_log = server.captured_log
|
||||
yield
|
||||
httpbin.after_test()
|
||||
server.after_test()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ssl_server(request, qapp):
|
||||
"""Fixture for a webserver with a self-signed SSL certificate.
|
||||
|
||||
This needs to be explicitly used in a test, and overwrites the httpbin log
|
||||
This needs to be explicitly used in a test, and overwrites the server log
|
||||
used in that test.
|
||||
"""
|
||||
server = WebserverProcess('webserver_sub_ssl')
|
||||
request.node._httpbin_log = server.captured_log
|
||||
request.node._server_log = server.captured_log
|
||||
server.start()
|
||||
yield server
|
||||
server.after_test()
|
||||
|
@ -17,9 +17,13 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""httpbin web server for end2end tests.
|
||||
"""Web server for end2end tests.
|
||||
|
||||
This script gets called as a QProcess from end2end/conftest.py.
|
||||
|
||||
Some of the handlers here are inspired by the server project, but simplified
|
||||
for qutebrowser's needs. Note that it probably doesn't handle e.g. multiple
|
||||
parameters or headers with the same name properly.
|
||||
"""
|
||||
|
||||
import sys
|
||||
@ -29,15 +33,20 @@ import signal
|
||||
import os
|
||||
import threading
|
||||
|
||||
from httpbin.core import app
|
||||
from httpbin.structures import CaseInsensitiveDict
|
||||
import cheroot.wsgi
|
||||
import flask
|
||||
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
_redirect_later_event = None
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def root():
|
||||
"""Show simple text."""
|
||||
return flask.Response(b'qutebrowser test webserver, '
|
||||
b'<a href="/user-agent">user agent</a>')
|
||||
|
||||
|
||||
@app.route('/data/<path:path>')
|
||||
def send_data(path):
|
||||
"""Send a given data file to qutebrowser.
|
||||
@ -57,15 +66,14 @@ def send_data(path):
|
||||
return flask.send_from_directory(data_dir, path)
|
||||
|
||||
|
||||
@app.route('/custom/redirect-later')
|
||||
@app.route('/redirect-later')
|
||||
def redirect_later():
|
||||
"""302 redirect to / after the given delay.
|
||||
|
||||
If delay is -1, wait until a request on redirect-later-continue is done.
|
||||
"""
|
||||
global _redirect_later_event
|
||||
args = CaseInsensitiveDict(flask.request.args.items())
|
||||
delay = int(args.get('delay', '1'))
|
||||
delay = int(flask.request.args.get('delay', '1'))
|
||||
if delay == -1:
|
||||
_redirect_later_event = threading.Event()
|
||||
ok = _redirect_later_event.wait(timeout=30 * 1000)
|
||||
@ -77,7 +85,7 @@ def redirect_later():
|
||||
return x
|
||||
|
||||
|
||||
@app.route('/custom/redirect-later-continue')
|
||||
@app.route('/redirect-later-continue')
|
||||
def redirect_later_continue():
|
||||
"""Continue a redirect-later request."""
|
||||
if _redirect_later_event is None:
|
||||
@ -87,7 +95,50 @@ def redirect_later_continue():
|
||||
return flask.Response(b'Continued redirect.')
|
||||
|
||||
|
||||
@app.route('/custom/content-size')
|
||||
@app.route('/redirect-self')
|
||||
def redirect_self():
|
||||
"""302 Redirects to itself."""
|
||||
return app.make_response(flask.redirect(flask.url_for('redirect_self')))
|
||||
|
||||
|
||||
@app.route('/redirect/<int:n>')
|
||||
def redirect_n_times(n):
|
||||
"""302 Redirects n times."""
|
||||
assert n > 0
|
||||
return flask.redirect(flask.url_for('redirect_n_times', n=n-1))
|
||||
|
||||
|
||||
@app.route('/relative-redirect')
|
||||
def relative_redirect():
|
||||
"""302 Redirect once."""
|
||||
response = app.make_response('')
|
||||
response.status_code = 302
|
||||
response.headers['Location'] = flask.url_for('root')
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/absolute-redirect')
|
||||
def absolute_redirect():
|
||||
"""302 Redirect once."""
|
||||
response = app.make_response('')
|
||||
response.status_code = 302
|
||||
response.headers['Location'] = flask.url_for('root', _external=True)
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/redirect-to')
|
||||
def redirect_to():
|
||||
"""302/3XX Redirects to the given URL."""
|
||||
# We need to build the response manually and convert to UTF-8 to prevent
|
||||
# werkzeug from "fixing" the URL. This endpoint should set the Location
|
||||
# header to the exact string supplied.
|
||||
response = app.make_response('')
|
||||
response.status_code = 302
|
||||
response.headers['Location'] = flask.request.args['url'].encode('utf-8')
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/content-size')
|
||||
def content_size():
|
||||
"""Send two bytes of data without a content-size."""
|
||||
def generate_bytes():
|
||||
@ -102,7 +153,7 @@ def content_size():
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/custom/twenty-mb')
|
||||
@app.route('/twenty-mb')
|
||||
def twenty_mb():
|
||||
"""Send 20MB of data."""
|
||||
def generate_bytes():
|
||||
@ -116,13 +167,7 @@ def twenty_mb():
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/custom/redirect-self')
|
||||
def redirect_self():
|
||||
"""302 Redirects to itself."""
|
||||
return app.make_response(flask.redirect(flask.url_for('redirect_self')))
|
||||
|
||||
|
||||
@app.route('/custom/500-inline')
|
||||
@app.route('/500-inline')
|
||||
def internal_error_attachment():
|
||||
"""A 500 error with Content-Disposition: inline."""
|
||||
response = flask.Response(b"", headers={
|
||||
@ -133,6 +178,85 @@ def internal_error_attachment():
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/cookies')
|
||||
def view_cookies():
|
||||
"""Show cookies."""
|
||||
return flask.jsonify(cookies=flask.request.cookies)
|
||||
|
||||
|
||||
@app.route('/cookies/set')
|
||||
def set_cookies():
|
||||
"""Set cookie(s) as provided by the query string."""
|
||||
r = app.make_response(flask.redirect(flask.url_for('view_cookies')))
|
||||
for key, value in flask.request.args.items():
|
||||
r.set_cookie(key=key, value=value)
|
||||
return r
|
||||
|
||||
|
||||
@app.route('/basic-auth/<user>/<passwd>')
|
||||
def basic_auth(user='user', passwd='passwd'):
|
||||
"""Prompt the user for authorization using HTTP Basic Auth."""
|
||||
auth = flask.request.authorization
|
||||
if not auth or auth.username != user or auth.password != passwd:
|
||||
r = flask.make_response()
|
||||
r.status_code = 401
|
||||
r.headers = {'WWW-Authenticate': 'Basic realm="Fake Realm"'}
|
||||
return r
|
||||
|
||||
return flask.jsonify(authenticated=True, user=user)
|
||||
|
||||
|
||||
@app.route('/drip')
|
||||
def drip():
|
||||
"""Drip data over a duration."""
|
||||
duration = float(flask.request.args.get('duration'))
|
||||
numbytes = int(flask.request.args.get('numbytes'))
|
||||
pause = duration / numbytes
|
||||
|
||||
def generate_bytes():
|
||||
for _ in range(numbytes):
|
||||
yield u"*".encode('utf-8')
|
||||
time.sleep(pause)
|
||||
|
||||
response = flask.Response(generate_bytes(), headers={
|
||||
"Content-Type": "application/octet-stream",
|
||||
"Content-Length": str(numbytes),
|
||||
})
|
||||
response.status_code = 200
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/404')
|
||||
def status_404():
|
||||
r = flask.make_response()
|
||||
r.status_code = 404
|
||||
return r
|
||||
|
||||
|
||||
@app.route('/headers')
|
||||
def view_headers():
|
||||
"""Return HTTP headers."""
|
||||
return flask.jsonify(headers=dict(flask.request.headers))
|
||||
|
||||
|
||||
@app.route('/response-headers')
|
||||
def response_headers():
|
||||
"""Return a set of response headers from the query string."""
|
||||
headers = flask.request.args
|
||||
response = flask.jsonify(headers)
|
||||
response.headers.extend(headers)
|
||||
|
||||
response = flask.jsonify(dict(response.headers))
|
||||
response.headers.extend(headers)
|
||||
return response
|
||||
|
||||
|
||||
@app.route('/user-agent')
|
||||
def view_user_agent():
|
||||
"""Return User-Agent."""
|
||||
return flask.jsonify({'user-agent': flask.request.headers['user-agent']})
|
||||
|
||||
|
||||
@app.after_request
|
||||
def log_request(response):
|
||||
"""Log a webserver request."""
|
||||
|
@ -70,7 +70,7 @@ def temp_basedir_env(tmpdir, short_tmpdir):
|
||||
|
||||
|
||||
@pytest.mark.linux
|
||||
def test_ascii_locale(request, httpbin, tmpdir, quteproc_new):
|
||||
def test_ascii_locale(request, server, tmpdir, quteproc_new):
|
||||
"""Test downloads with LC_ALL=C set.
|
||||
|
||||
https://github.com/qutebrowser/qutebrowser/issues/908
|
||||
@ -83,7 +83,7 @@ def test_ascii_locale(request, httpbin, tmpdir, quteproc_new):
|
||||
# Test a normal download
|
||||
quteproc_new.set_setting('downloads.location.prompt', 'false')
|
||||
url = 'http://localhost:{port}/data/downloads/ä-issue908.bin'.format(
|
||||
port=httpbin.port)
|
||||
port=server.port)
|
||||
quteproc_new.send_cmd(':download {}'.format(url))
|
||||
quteproc_new.wait_for(category='downloads',
|
||||
message='Download ?-issue908.bin finished')
|
||||
@ -103,7 +103,7 @@ def test_ascii_locale(request, httpbin, tmpdir, quteproc_new):
|
||||
|
||||
|
||||
@pytest.mark.linux
|
||||
def test_misconfigured_user_dirs(request, httpbin, temp_basedir_env,
|
||||
def test_misconfigured_user_dirs(request, server, temp_basedir_env,
|
||||
tmpdir, quteproc_new):
|
||||
"""Test downloads with a misconfigured XDG_DOWNLOAD_DIR.
|
||||
|
||||
@ -122,7 +122,7 @@ def test_misconfigured_user_dirs(request, httpbin, temp_basedir_env,
|
||||
|
||||
quteproc_new.set_setting('downloads.location.prompt', 'false')
|
||||
url = 'http://localhost:{port}/data/downloads/download.bin'.format(
|
||||
port=httpbin.port)
|
||||
port=server.port)
|
||||
quteproc_new.send_cmd(':download {}'.format(url))
|
||||
line = quteproc_new.wait_for(
|
||||
loglevel=logging.ERROR, category='message',
|
||||
|
@ -85,25 +85,25 @@ def download_dir(tmpdir):
|
||||
return DownloadDir(tmpdir)
|
||||
|
||||
|
||||
def _test_mhtml_requests(test_dir, test_path, httpbin):
|
||||
def _test_mhtml_requests(test_dir, test_path, server):
|
||||
with open(os.path.join(test_dir, 'requests'), encoding='utf-8') as f:
|
||||
expected_requests = []
|
||||
for line in f:
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
path = '/{}/{}'.format(test_path, line.strip())
|
||||
expected_requests.append(httpbin.ExpectedRequest('GET', path))
|
||||
expected_requests.append(server.ExpectedRequest('GET', path))
|
||||
|
||||
actual_requests = httpbin.get_requests()
|
||||
actual_requests = server.get_requests()
|
||||
# Requests are not hashable, we need to convert to ExpectedRequests
|
||||
actual_requests = [httpbin.ExpectedRequest.from_request(req)
|
||||
actual_requests = [server.ExpectedRequest.from_request(req)
|
||||
for req in actual_requests]
|
||||
assert (collections.Counter(actual_requests) ==
|
||||
collections.Counter(expected_requests))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test_name', collect_tests())
|
||||
def test_mhtml(request, test_name, download_dir, quteproc, httpbin):
|
||||
def test_mhtml(request, test_name, download_dir, quteproc, server):
|
||||
quteproc.set_setting('downloads.location.directory', download_dir.location)
|
||||
quteproc.set_setting('downloads.location.prompt', 'false')
|
||||
|
||||
@ -119,10 +119,10 @@ def test_mhtml(request, test_name, download_dir, quteproc, httpbin):
|
||||
|
||||
# Wait for favicon.ico to be loaded if there is one
|
||||
if os.path.exists(os.path.join(test_dir, 'favicon.png')):
|
||||
httpbin.wait_for(path='/{}/favicon.png'.format(test_path))
|
||||
server.wait_for(path='/{}/favicon.png'.format(test_path))
|
||||
|
||||
# Discard all requests that were necessary to display the page
|
||||
httpbin.clear_data()
|
||||
server.clear_data()
|
||||
quteproc.send_cmd(':download --mhtml --dest "{}"'.format(download_dest))
|
||||
quteproc.wait_for(category='downloads',
|
||||
message='File successfully written.')
|
||||
@ -136,4 +136,4 @@ def test_mhtml(request, test_name, download_dir, quteproc, httpbin):
|
||||
download_dir.sanity_check_mhtml()
|
||||
|
||||
if not request.config.webengine:
|
||||
_test_mhtml_requests(test_dir, test_path, httpbin)
|
||||
_test_mhtml_requests(test_dir, test_path, server)
|
||||
|
Loading…
Reference in New Issue
Block a user