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/micheles/decorator.git
|
||||||
git+https://github.com/pallets/flask.git
|
git+https://github.com/pallets/flask.git
|
||||||
git+https://github.com/miracle2k/python-glob2.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/HypothesisWorks/hypothesis-python.git
|
||||||
git+https://github.com/pallets/itsdangerous.git
|
git+https://github.com/pallets/itsdangerous.git
|
||||||
git+https://bitbucket.org/zzzeek/mako.git
|
git+https://bitbucket.org/zzzeek/mako.git
|
||||||
|
@ -3,7 +3,6 @@ cheroot
|
|||||||
coverage
|
coverage
|
||||||
Flask
|
Flask
|
||||||
hunter
|
hunter
|
||||||
httpbin
|
|
||||||
hypothesis
|
hypothesis
|
||||||
pytest
|
pytest
|
||||||
pytest-bdd
|
pytest-bdd
|
||||||
|
@ -34,7 +34,7 @@ from PyQt5.QtCore import PYQT_VERSION
|
|||||||
|
|
||||||
pytest.register_assert_rewrite('end2end.fixtures')
|
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,
|
from end2end.fixtures.quteprocess import (quteproc_process, quteproc,
|
||||||
quteproc_new)
|
quteproc_new)
|
||||||
from end2end.fixtures.testprocess import pytest_runtest_makereport
|
from end2end.fixtures.testprocess import pytest_runtest_makereport
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<title>Failing download when redirecting/aborting</title>
|
<title>Failing download when redirecting/aborting</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<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>
|
<a href="/does-not-exist">404</a>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -116,14 +116,14 @@ def pytest_runtest_makereport(item, call):
|
|||||||
|
|
||||||
|
|
||||||
@bdd.given(bdd.parsers.parse("I set {opt} to {value}"))
|
@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.
|
"""Set a qutebrowser setting.
|
||||||
|
|
||||||
This is available as "Given:" step so it can be used as "Background:".
|
This is available as "Given:" step so it can be used as "Background:".
|
||||||
"""
|
"""
|
||||||
if value == '<empty>':
|
if value == '<empty>':
|
||||||
value = ''
|
value = ''
|
||||||
value = value.replace('(port)', str(httpbin.port))
|
value = value.replace('(port)', str(server.port))
|
||||||
quteproc.set_setting(opt, value)
|
quteproc.set_setting(opt, value)
|
||||||
|
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ def pdfjs_available():
|
|||||||
|
|
||||||
|
|
||||||
@bdd.when(bdd.parsers.parse("I open {path}"))
|
@bdd.when(bdd.parsers.parse("I open {path}"))
|
||||||
def open_path(quteproc, httpbin, path):
|
def open_path(quteproc, server, path):
|
||||||
"""Open a URL.
|
"""Open a URL.
|
||||||
|
|
||||||
- If used like "When I open ... in a new tab", the URL is opened in a new
|
- 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 "... 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.
|
- 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_tab = False
|
||||||
new_bg_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}"))
|
@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."""
|
"""Set a qutebrowser setting."""
|
||||||
if value == '<empty>':
|
if value == '<empty>':
|
||||||
value = ''
|
value = ''
|
||||||
value = value.replace('(port)', str(httpbin.port))
|
value = value.replace('(port)', str(server.port))
|
||||||
quteproc.set_setting(opt, value)
|
quteproc.set_setting(opt, value)
|
||||||
|
|
||||||
|
|
||||||
@bdd.when(bdd.parsers.parse("I run {command}"))
|
@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.
|
"""Run a qutebrowser command.
|
||||||
|
|
||||||
The suffix "with count ..." can be used to pass a count to the 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:
|
else:
|
||||||
invalid = False
|
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('(testdata)', utils.abs_datapath())
|
||||||
command = command.replace('(tmpdir)', str(tmpdir))
|
command = command.replace('(tmpdir)', str(tmpdir))
|
||||||
command = command.replace('(dirsep)', os.sep)
|
command = command.replace('(dirsep)', os.sep)
|
||||||
@ -264,9 +264,9 @@ def run_command(quteproc, httpbin, tmpdir, command):
|
|||||||
|
|
||||||
|
|
||||||
@bdd.when(bdd.parsers.parse("I reload"))
|
@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."""
|
"""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')
|
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) '
|
@bdd.when(bdd.parsers.re(r'I wait for the (?P<category>error|message|warning) '
|
||||||
r'"(?P<message>.*)"'))
|
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."""
|
"""Wait for a given statusbar message/error/warning."""
|
||||||
quteproc.log_summary('Waiting for {} "{}"'.format(category, message))
|
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"))
|
@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 '
|
@bdd.when(bdd.parsers.re(r'I put "(?P<content>.*)" into the '
|
||||||
r'(?P<what>primary selection|clipboard)'))
|
r'(?P<what>primary selection|clipboard)'))
|
||||||
def fill_clipboard(quteproc, httpbin, what, content):
|
def fill_clipboard(quteproc, server, what, content):
|
||||||
content = content.replace('(port)', str(httpbin.port))
|
content = content.replace('(port)', str(server.port))
|
||||||
content = content.replace(r'\n', '\n')
|
content = content.replace(r'\n', '\n')
|
||||||
quteproc.send_cmd(':debug-set-fake-clipboard "{}"'.format(content))
|
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 '
|
@bdd.when(bdd.parsers.re(r'I put the following lines into the '
|
||||||
r'(?P<what>primary selection|clipboard):\n'
|
r'(?P<what>primary selection|clipboard):\n'
|
||||||
r'(?P<content>.+)$', flags=re.DOTALL))
|
r'(?P<content>.+)$', flags=re.DOTALL))
|
||||||
def fill_clipboard_multiline(quteproc, httpbin, what, content):
|
def fill_clipboard_multiline(quteproc, server, what, content):
|
||||||
fill_clipboard(quteproc, httpbin, what, textwrap.dedent(content))
|
fill_clipboard(quteproc, server, what, textwrap.dedent(content))
|
||||||
|
|
||||||
|
|
||||||
@bdd.when(bdd.parsers.parse('I hint with args "{args}"'))
|
@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"))
|
@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."""
|
"""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}"))
|
@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."""
|
"""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')]
|
for path in pages.split('\n')]
|
||||||
actual_requests = httpbin.get_requests()
|
actual_requests = server.get_requests()
|
||||||
assert actual_requests == expected_requests
|
assert actual_requests == expected_requests
|
||||||
|
|
||||||
|
|
||||||
@bdd.then(bdd.parsers.parse("The unordered requests should be:\n{pages}"))
|
@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)."""
|
"""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')]
|
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
|
# 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]
|
for req in actual_requests]
|
||||||
assert (collections.Counter(actual_requests) ==
|
assert (collections.Counter(actual_requests) ==
|
||||||
collections.Counter(expected_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) '
|
@bdd.then(bdd.parsers.re(r'the (?P<category>error|message|warning) '
|
||||||
r'"(?P<message>.*)" should be shown'))
|
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."""
|
"""Expect the given message in the qutebrowser log."""
|
||||||
category_to_loglevel = {
|
category_to_loglevel = {
|
||||||
'message': logging.INFO,
|
'message': logging.INFO,
|
||||||
'error': logging.ERROR,
|
'error': logging.ERROR,
|
||||||
'warning': logging.WARNING,
|
'warning': logging.WARNING,
|
||||||
}
|
}
|
||||||
message = message.replace('(port)', str(httpbin.port))
|
message = message.replace('(port)', str(server.port))
|
||||||
quteproc.mark_expected(category='message',
|
quteproc.mark_expected(category='message',
|
||||||
loglevel=category_to_loglevel[category],
|
loglevel=category_to_loglevel[category],
|
||||||
message=message)
|
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 '
|
@bdd.then(bdd.parsers.re(r'(?P<is_regex>regex )?"(?P<pattern>[^"]+)" should '
|
||||||
r'be logged'))
|
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."""
|
"""Expect the given pattern on regex in the log."""
|
||||||
if is_regex:
|
if is_regex:
|
||||||
pattern = re.compile(pattern)
|
pattern = re.compile(pattern)
|
||||||
else:
|
else:
|
||||||
pattern = pattern.replace('(port)', str(httpbin.port))
|
pattern = pattern.replace('(port)', str(server.port))
|
||||||
line = quteproc.wait_for(message=pattern)
|
line = quteproc.wait_for(message=pattern)
|
||||||
line.expected = True
|
line.expected = True
|
||||||
|
|
||||||
@ -493,7 +493,7 @@ def no_crash():
|
|||||||
def check_header(quteproc, header, value):
|
def check_header(quteproc, header, value):
|
||||||
"""Check if a given header is set correctly.
|
"""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()
|
content = quteproc.get_content()
|
||||||
data = json.loads(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 '
|
@bdd.then(bdd.parsers.re(r'the (?P<what>primary selection|clipboard) should '
|
||||||
r'contain "(?P<content>.*)"'))
|
r'contain "(?P<content>.*)"'))
|
||||||
def clipboard_contains(quteproc, httpbin, what, content):
|
def clipboard_contains(quteproc, server, what, content):
|
||||||
expected = content.replace('(port)', str(httpbin.port))
|
expected = content.replace('(port)', str(server.port))
|
||||||
expected = expected.replace('\\n', '\n')
|
expected = expected.replace('\\n', '\n')
|
||||||
quteproc.wait_for(message='Setting fake {}: {}'.format(
|
quteproc.wait_for(message='Setting fake {}: {}'.format(
|
||||||
what, json.dumps(expected)))
|
what, json.dumps(expected)))
|
||||||
|
|
||||||
|
|
||||||
@bdd.then(bdd.parsers.parse('the clipboard should contain:\n{content}'))
|
@bdd.then(bdd.parsers.parse('the clipboard should contain:\n{content}'))
|
||||||
def clipboard_contains_multiline(quteproc, httpbin, content):
|
def clipboard_contains_multiline(quteproc, server, content):
|
||||||
expected = textwrap.dedent(content).replace('(port)', str(httpbin.port))
|
expected = textwrap.dedent(content).replace('(port)', str(server.port))
|
||||||
quteproc.wait_for(message='Setting fake clipboard: {}'.format(
|
quteproc.wait_for(message='Setting fake clipboard: {}'.format(
|
||||||
json.dumps(expected)))
|
json.dumps(expected)))
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ Feature: Downloading things from a website.
|
|||||||
|
|
||||||
Scenario: Opening a mhtml download directly
|
Scenario: Opening a mhtml download directly
|
||||||
When I set downloads.location.prompt to true
|
When I set downloads.location.prompt to true
|
||||||
And I open html
|
And I open /
|
||||||
And I run :download --mhtml
|
And I run :download --mhtml
|
||||||
And I wait for the download prompt for "*"
|
And I wait for the download prompt for "*"
|
||||||
And I directly open the download
|
And I directly open the download
|
||||||
@ -572,27 +572,27 @@ Feature: Downloading things from a website.
|
|||||||
|
|
||||||
Scenario: Downloading with redirect to itself
|
Scenario: Downloading with redirect to itself
|
||||||
When I set downloads.location.prompt to false
|
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
|
And I wait until the download is finished
|
||||||
Then the downloaded file redirect-self should exist
|
Then the downloaded file redirect-self should exist
|
||||||
|
|
||||||
Scenario: Downloading with absolute redirect
|
Scenario: Downloading with absolute redirect
|
||||||
When I set downloads.location.prompt to false
|
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
|
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
|
Scenario: Downloading with relative redirect
|
||||||
When I set downloads.location.prompt to false
|
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
|
And I wait until the download is finished
|
||||||
Then the downloaded file 1 should exist
|
Then the downloaded file relative-redirect should exist
|
||||||
|
|
||||||
## Other
|
## Other
|
||||||
|
|
||||||
Scenario: Download without a content-size
|
Scenario: Download without a content-size
|
||||||
When I set downloads.location.prompt to false
|
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
|
And I wait until the download is finished
|
||||||
Then the downloaded file content-size should exist
|
Then the downloaded file content-size should exist
|
||||||
|
|
||||||
@ -604,13 +604,13 @@ Feature: Downloading things from a website.
|
|||||||
|
|
||||||
Scenario: Downloading 20MB file
|
Scenario: Downloading 20MB file
|
||||||
When I set downloads.location.prompt to false
|
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
|
And I wait until the download is finished
|
||||||
Then the downloaded file twenty-mb should be 20971520 bytes big
|
Then the downloaded file twenty-mb should be 20971520 bytes big
|
||||||
|
|
||||||
Scenario: Downloading 20MB file with late prompt confirmation
|
Scenario: Downloading 20MB file with late prompt confirmation
|
||||||
When I set downloads.location.prompt to true
|
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 wait 1s
|
||||||
And I run :prompt-accept
|
And I run :prompt-accept
|
||||||
And I wait until the download is finished
|
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
|
Then the downloaded file download.bin should exist
|
||||||
And the downloaded file download2.bin should not 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
|
@qtwebengine_skip: We can't get the UA from the page there
|
||||||
Scenario: user-agent when using :download
|
Scenario: user-agent when using :download
|
||||||
When I open user-agent
|
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
|
@qtwebengine_skip: We can't get the UA from the page there
|
||||||
Scenario: user-agent when using hints
|
Scenario: user-agent when using hints
|
||||||
When I set hints.mode to number
|
When I open /
|
||||||
And I open /
|
|
||||||
And I run :hint links download
|
And I run :hint links download
|
||||||
And I press the keys "us" # user-agent
|
And I run :follow-hint a
|
||||||
And I run :follow-hint 0
|
|
||||||
And I wait until the download is finished
|
And I wait until the download is finished
|
||||||
Then the downloaded file user-agent should contain Safari/
|
Then the downloaded file user-agent should contain Safari/
|
||||||
|
|
||||||
@qtwebengine_skip: Handled by QtWebEngine, not by us
|
@qtwebengine_skip: Handled by QtWebEngine, not by us
|
||||||
Scenario: Downloading a "Internal server error" with disposition: inline (#2304)
|
Scenario: Downloading a "Internal server error" with disposition: inline (#2304)
|
||||||
When I set downloads.location.prompt to false
|
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
|
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
|
@qtwebengine_todo: Error page message is not implemented
|
||||||
Scenario: History with a 404
|
Scenario: History with a 404
|
||||||
When I open status/404 without waiting
|
When I open 404 without waiting
|
||||||
And I wait for "Error while loading http://localhost:*/status/404: NOT FOUND" in the log
|
And I wait for "Error while loading http://localhost:*/404: NOT FOUND" in the log
|
||||||
Then the history should contain:
|
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
|
Scenario: History with invalid URL
|
||||||
When I run :tab-only
|
When I run :tab-only
|
||||||
|
@ -186,15 +186,15 @@ Feature: Various utility commands.
|
|||||||
Given I have a fresh instance
|
Given I have a fresh instance
|
||||||
# We can't use "When I open" because we don't want to wait for load
|
# We can't use "When I open" because we don't want to wait for load
|
||||||
# finished
|
# 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 for "emitting: cur_load_status_changed('loading') (tab *)" in the log
|
||||||
And I wait 1s
|
And I wait 1s
|
||||||
And I run :stop
|
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
|
And I wait 1s
|
||||||
Then the unordered requests should be:
|
Then the unordered requests should be:
|
||||||
custom/redirect-later-continue
|
redirect-later-continue
|
||||||
custom/redirect-later?delay=-1
|
redirect-later?delay=-1
|
||||||
# no request on / because we stopped the redirect
|
# no request on / because we stopped the redirect
|
||||||
|
|
||||||
Scenario: :stop with wrong count
|
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'))
|
@bdd.when(bdd.parsers.parse('I set up "{lists}" as block lists'))
|
||||||
def set_up_blocking(quteproc, lists, httpbin):
|
def set_up_blocking(quteproc, lists, server):
|
||||||
url = 'http://localhost:{}/data/adblock/'.format(httpbin.port)
|
url = 'http://localhost:{}/data/adblock/'.format(server.port)
|
||||||
urls = [url + item.strip() for item in lists.split(',')]
|
urls = [url + item.strip() for item in lists.split(',')]
|
||||||
quteproc.set_setting('content.host_blocking.lists', json.dumps(urls))
|
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 '
|
@bdd.when(bdd.parsers.parse('I set up a fake editor replacing "{text}" by '
|
||||||
'"{replacement}"'))
|
'"{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."""
|
"""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 = tmpdir / 'script.py'
|
||||||
script.write(textwrap.dedent("""
|
script.write(textwrap.dedent("""
|
||||||
import sys
|
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}"'))
|
@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."""
|
"""Set up editor.command to a small python script inserting a text."""
|
||||||
script = tmpdir / 'script.py'
|
script = tmpdir / 'script.py'
|
||||||
script.write(textwrap.dedent("""
|
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}"))
|
@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'
|
path = tmpdir / 'history'
|
||||||
quteproc.send_cmd(':debug-dump-history "{}"'.format(path))
|
quteproc.send_cmd(':debug-dump-history "{}"'.format(path))
|
||||||
quteproc.wait_for(category='message', loglevel=logging.INFO,
|
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
|
# ignore access times, they will differ in each run
|
||||||
actual = '\n'.join(re.sub('^\\d+-?', '', line).strip() for line in f)
|
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
|
assert actual == expected
|
||||||
|
|
||||||
|
|
||||||
@bdd.then("the history should be empty")
|
@bdd.then("the history should be empty")
|
||||||
def check_history_empty(quteproc, httpbin, tmpdir):
|
def check_history_empty(quteproc, server, tmpdir):
|
||||||
check_history(quteproc, httpbin, tmpdir, '')
|
check_history(quteproc, server, tmpdir, '')
|
||||||
|
@ -27,7 +27,7 @@ bdd.scenarios('private.feature')
|
|||||||
def check_cookie(quteproc, name, value):
|
def check_cookie(quteproc, name, value):
|
||||||
"""Check if a given cookie is set correctly.
|
"""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()
|
content = quteproc.get_content()
|
||||||
data = json.loads(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 '
|
@bdd.when(bdd.parsers.parse('I replace "{pattern}" by "{replacement}" in the '
|
||||||
'"{name}" session file'))
|
'"{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
|
# First wait until the session was actually saved
|
||||||
quteproc.wait_for(category='message', loglevel=logging.INFO,
|
quteproc.wait_for(category='message', loglevel=logging.INFO,
|
||||||
message='Saved session {}.'.format(name))
|
message='Saved session {}.'.format(name))
|
||||||
filename = os.path.join(quteproc.basedir, 'data', 'sessions',
|
filename = os.path.join(quteproc.basedir, 'data', 'sessions',
|
||||||
name + '.yml')
|
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:
|
with open(filename, 'r', encoding='utf-8') as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
with open(filename, 'w', encoding='utf-8') as f:
|
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):
|
if any(path.startswith(scheme) for scheme in special_schemes):
|
||||||
return path
|
return path
|
||||||
else:
|
else:
|
||||||
httpbin = self.request.getfixturevalue('httpbin')
|
server = self.request.getfixturevalue('server')
|
||||||
return '{}://localhost:{}/{}'.format(
|
return '{}://localhost:{}/{}'.format(
|
||||||
'https' if https else 'http',
|
'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 '')
|
path if path != '/' else '')
|
||||||
|
|
||||||
def wait_for_js(self, message):
|
def wait_for_js(self, message):
|
||||||
@ -778,7 +778,7 @@ def _xpath_escape(text):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='module')
|
@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."""
|
"""Fixture for qutebrowser process which is started once per file."""
|
||||||
# Passing request so it has an initial config
|
# Passing request so it has an initial config
|
||||||
proc = QuteProc(request)
|
proc = QuteProc(request)
|
||||||
@ -788,7 +788,7 @@ def quteproc_process(qapp, httpbin, request):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def quteproc(quteproc_process, httpbin, request):
|
def quteproc(quteproc_process, server, request):
|
||||||
"""Per-test qutebrowser fixture which uses the per-file process."""
|
"""Per-test qutebrowser fixture which uses the per-file process."""
|
||||||
request.node._quteproc_log = quteproc_process.captured_log
|
request.node._quteproc_log = quteproc_process.captured_log
|
||||||
quteproc_process.before_test()
|
quteproc_process.before_test()
|
||||||
@ -798,7 +798,7 @@ def quteproc(quteproc_process, httpbin, request):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def quteproc_new(qapp, httpbin, request):
|
def quteproc_new(qapp, server, request):
|
||||||
"""Per-test qutebrowser process to test invocations."""
|
"""Per-test qutebrowser process to test invocations."""
|
||||||
proc = QuteProc(request)
|
proc = QuteProc(request)
|
||||||
request.node._quteproc_log = proc.captured_log
|
request.node._quteproc_log = proc.captured_log
|
||||||
|
@ -66,23 +66,23 @@ class FakeRequest:
|
|||||||
|
|
||||||
"""Fake for request."""
|
"""Fake for request."""
|
||||||
|
|
||||||
def __init__(self, node, config, httpbin):
|
def __init__(self, node, config, server):
|
||||||
self.node = node
|
self.node = node
|
||||||
self.config = config
|
self.config = config
|
||||||
self._httpbin = httpbin
|
self._server = server
|
||||||
|
|
||||||
def getfixturevalue(self, name):
|
def getfixturevalue(self, name):
|
||||||
assert name == 'httpbin'
|
assert name == 'server'
|
||||||
return self._httpbin
|
return self._server
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def request_mock(quteproc, monkeypatch, httpbin):
|
def request_mock(quteproc, monkeypatch, server):
|
||||||
"""Patch out a pytest request."""
|
"""Patch out a pytest request."""
|
||||||
fake_call = FakeRepCall()
|
fake_call = FakeRepCall()
|
||||||
fake_config = FakeConfig()
|
fake_config = FakeConfig()
|
||||||
fake_node = FakeNode(fake_call)
|
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')
|
assert not hasattr(fake_request.node.rep_call, 'wasxfail')
|
||||||
monkeypatch.setattr(quteproc, 'request', fake_request)
|
monkeypatch.setattr(quteproc, 'request', fake_request)
|
||||||
return fake_request
|
return fake_request
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
# 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 json
|
||||||
import urllib.request
|
import urllib.request
|
||||||
@ -27,14 +27,14 @@ import pytest
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('path, content, expected', [
|
@pytest.mark.parametrize('path, content, expected', [
|
||||||
('/', '<title>httpbin(1): HTTP Client Testing Service</title>', True),
|
('/', 'qutebrowser test webserver', True),
|
||||||
# https://github.com/Runscope/httpbin/issues/245
|
# https://github.com/Runscope/server/issues/245
|
||||||
('/', 'www.google-analytics.com', False),
|
('/', 'www.google-analytics.com', False),
|
||||||
('/data/hello.txt', 'Hello World!', True),
|
('/data/hello.txt', 'Hello World!', True),
|
||||||
])
|
])
|
||||||
def test_httpbin(httpbin, qtbot, path, content, expected):
|
def test_server(server, qtbot, path, content, expected):
|
||||||
with qtbot.waitSignal(httpbin.new_request, timeout=100):
|
with qtbot.waitSignal(server.new_request, timeout=100):
|
||||||
url = 'http://localhost:{}{}'.format(httpbin.port, path)
|
url = 'http://localhost:{}{}'.format(server.port, path)
|
||||||
try:
|
try:
|
||||||
response = urllib.request.urlopen(url)
|
response = urllib.request.urlopen(url)
|
||||||
except urllib.error.HTTPError as e:
|
except urllib.error.HTTPError as e:
|
||||||
@ -47,7 +47,7 @@ def test_httpbin(httpbin, qtbot, path, content, expected):
|
|||||||
|
|
||||||
data = response.read().decode('utf-8')
|
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
|
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': 'GET', 'path': '/', 'status': 200}, 'GET', '/foo', False),
|
||||||
({'verb': 'POST', 'path': '/', 'status': 200}, 'GET', '/', False),
|
({'verb': 'POST', 'path': '/', 'status': 200}, 'GET', '/', False),
|
||||||
])
|
])
|
||||||
def test_expected_request(httpbin, line, verb, path, equal):
|
def test_expected_request(server, line, verb, path, equal):
|
||||||
expected = httpbin.ExpectedRequest(verb, path)
|
expected = server.ExpectedRequest(verb, path)
|
||||||
request = httpbin.Request(json.dumps(line))
|
request = server.Request(json.dumps(line))
|
||||||
assert (expected == request) == equal
|
assert (expected == request) == equal
|
||||||
|
@ -90,7 +90,7 @@ def _render_log(data, threshold=100):
|
|||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_runtest_makereport(item, call):
|
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
|
outcome = yield
|
||||||
if call.when not in ['call', 'teardown']:
|
if call.when not in ['call', 'teardown']:
|
||||||
return
|
return
|
||||||
@ -100,7 +100,7 @@ def pytest_runtest_makereport(item, call):
|
|||||||
return
|
return
|
||||||
|
|
||||||
quteproc_log = getattr(item, '_quteproc_log', None)
|
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'):
|
if not hasattr(report.longrepr, 'addsection'):
|
||||||
# In some conditions (on macOS and Windows it seems), report.longrepr
|
# 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:
|
if quteproc_log is not None:
|
||||||
report.longrepr.addsection("qutebrowser output",
|
report.longrepr.addsection("qutebrowser output",
|
||||||
_render_log(quteproc_log))
|
_render_log(quteproc_log))
|
||||||
if httpbin_log is not None:
|
if server_log is not None:
|
||||||
report.longrepr.addsection("httpbin output", _render_log(httpbin_log))
|
report.longrepr.addsection("server output", _render_log(server_log))
|
||||||
|
|
||||||
|
|
||||||
class Process(QObject):
|
class Process(QObject):
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
"""Fixtures for the httpbin webserver."""
|
"""Fixtures for the server webserver."""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
@ -35,7 +35,7 @@ from qutebrowser.utils import utils
|
|||||||
|
|
||||||
class Request(testprocess.Line):
|
class Request(testprocess.Line):
|
||||||
|
|
||||||
"""A parsed line from the httpbin/flask log output.
|
"""A parsed line from the flask log output.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
verb/path/status: Parsed from the log output.
|
verb/path/status: Parsed from the log output.
|
||||||
@ -67,22 +67,20 @@ class Request(testprocess.Line):
|
|||||||
'/favicon.ico': [http.client.NOT_FOUND],
|
'/favicon.ico': [http.client.NOT_FOUND],
|
||||||
'/does-not-exist': [http.client.NOT_FOUND],
|
'/does-not-exist': [http.client.NOT_FOUND],
|
||||||
'/does-not-exist-2': [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],
|
'/redirect-later': [http.client.FOUND],
|
||||||
'/custom/redirect-self': [http.client.FOUND],
|
'/redirect-self': [http.client.FOUND],
|
||||||
'/redirect-to': [http.client.FOUND],
|
'/redirect-to': [http.client.FOUND],
|
||||||
|
'/relative-redirect': [http.client.FOUND],
|
||||||
|
'/absolute-redirect': [http.client.FOUND],
|
||||||
|
|
||||||
'/cookies/set': [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):
|
for i in range(15):
|
||||||
path_to_statuses['/redirect/{}'.format(i)] = [http.client.FOUND]
|
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']:
|
for suffix in ['', '1', '2', '3', '4', '5', '6']:
|
||||||
key = '/basic-auth/user{}/password{}'.format(suffix, suffix)
|
key = '/basic-auth/user{}/password{}'.format(suffix, suffix)
|
||||||
path_to_statuses[key] = [http.client.UNAUTHORIZED, http.client.OK]
|
path_to_statuses[key] = [http.client.UNAUTHORIZED, http.client.OK]
|
||||||
@ -130,7 +128,7 @@ class ExpectedRequest:
|
|||||||
|
|
||||||
class WebserverProcess(testprocess.Process):
|
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.
|
Reads the log from its stdout and parses it.
|
||||||
|
|
||||||
@ -186,31 +184,31 @@ class WebserverProcess(testprocess.Process):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='session', autouse=True)
|
@pytest.fixture(scope='session', autouse=True)
|
||||||
def httpbin(qapp):
|
def server(qapp):
|
||||||
"""Fixture for an httpbin object which ensures clean setup/teardown."""
|
"""Fixture for an server object which ensures clean setup/teardown."""
|
||||||
httpbin = WebserverProcess('webserver_sub')
|
server = WebserverProcess('webserver_sub')
|
||||||
httpbin.start()
|
server.start()
|
||||||
yield httpbin
|
yield server
|
||||||
httpbin.cleanup()
|
server.cleanup()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def httpbin_after_test(httpbin, request):
|
def server_after_test(server, request):
|
||||||
"""Fixture to clean httpbin request list after each test."""
|
"""Fixture to clean server request list after each test."""
|
||||||
request.node._httpbin_log = httpbin.captured_log
|
request.node._server_log = server.captured_log
|
||||||
yield
|
yield
|
||||||
httpbin.after_test()
|
server.after_test()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def ssl_server(request, qapp):
|
def ssl_server(request, qapp):
|
||||||
"""Fixture for a webserver with a self-signed SSL certificate.
|
"""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.
|
used in that test.
|
||||||
"""
|
"""
|
||||||
server = WebserverProcess('webserver_sub_ssl')
|
server = WebserverProcess('webserver_sub_ssl')
|
||||||
request.node._httpbin_log = server.captured_log
|
request.node._server_log = server.captured_log
|
||||||
server.start()
|
server.start()
|
||||||
yield server
|
yield server
|
||||||
server.after_test()
|
server.after_test()
|
||||||
|
@ -17,9 +17,13 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
# 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.
|
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
|
import sys
|
||||||
@ -29,15 +33,20 @@ import signal
|
|||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from httpbin.core import app
|
|
||||||
from httpbin.structures import CaseInsensitiveDict
|
|
||||||
import cheroot.wsgi
|
import cheroot.wsgi
|
||||||
import flask
|
import flask
|
||||||
|
|
||||||
|
app = flask.Flask(__name__)
|
||||||
_redirect_later_event = None
|
_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>')
|
@app.route('/data/<path:path>')
|
||||||
def send_data(path):
|
def send_data(path):
|
||||||
"""Send a given data file to qutebrowser.
|
"""Send a given data file to qutebrowser.
|
||||||
@ -57,15 +66,14 @@ def send_data(path):
|
|||||||
return flask.send_from_directory(data_dir, path)
|
return flask.send_from_directory(data_dir, path)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/custom/redirect-later')
|
@app.route('/redirect-later')
|
||||||
def redirect_later():
|
def redirect_later():
|
||||||
"""302 redirect to / after the given delay.
|
"""302 redirect to / after the given delay.
|
||||||
|
|
||||||
If delay is -1, wait until a request on redirect-later-continue is done.
|
If delay is -1, wait until a request on redirect-later-continue is done.
|
||||||
"""
|
"""
|
||||||
global _redirect_later_event
|
global _redirect_later_event
|
||||||
args = CaseInsensitiveDict(flask.request.args.items())
|
delay = int(flask.request.args.get('delay', '1'))
|
||||||
delay = int(args.get('delay', '1'))
|
|
||||||
if delay == -1:
|
if delay == -1:
|
||||||
_redirect_later_event = threading.Event()
|
_redirect_later_event = threading.Event()
|
||||||
ok = _redirect_later_event.wait(timeout=30 * 1000)
|
ok = _redirect_later_event.wait(timeout=30 * 1000)
|
||||||
@ -77,7 +85,7 @@ def redirect_later():
|
|||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
@app.route('/custom/redirect-later-continue')
|
@app.route('/redirect-later-continue')
|
||||||
def redirect_later_continue():
|
def redirect_later_continue():
|
||||||
"""Continue a redirect-later request."""
|
"""Continue a redirect-later request."""
|
||||||
if _redirect_later_event is None:
|
if _redirect_later_event is None:
|
||||||
@ -87,7 +95,50 @@ def redirect_later_continue():
|
|||||||
return flask.Response(b'Continued redirect.')
|
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():
|
def content_size():
|
||||||
"""Send two bytes of data without a content-size."""
|
"""Send two bytes of data without a content-size."""
|
||||||
def generate_bytes():
|
def generate_bytes():
|
||||||
@ -102,7 +153,7 @@ def content_size():
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route('/custom/twenty-mb')
|
@app.route('/twenty-mb')
|
||||||
def twenty_mb():
|
def twenty_mb():
|
||||||
"""Send 20MB of data."""
|
"""Send 20MB of data."""
|
||||||
def generate_bytes():
|
def generate_bytes():
|
||||||
@ -116,13 +167,7 @@ def twenty_mb():
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route('/custom/redirect-self')
|
@app.route('/500-inline')
|
||||||
def redirect_self():
|
|
||||||
"""302 Redirects to itself."""
|
|
||||||
return app.make_response(flask.redirect(flask.url_for('redirect_self')))
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/custom/500-inline')
|
|
||||||
def internal_error_attachment():
|
def internal_error_attachment():
|
||||||
"""A 500 error with Content-Disposition: inline."""
|
"""A 500 error with Content-Disposition: inline."""
|
||||||
response = flask.Response(b"", headers={
|
response = flask.Response(b"", headers={
|
||||||
@ -133,6 +178,85 @@ def internal_error_attachment():
|
|||||||
return response
|
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
|
@app.after_request
|
||||||
def log_request(response):
|
def log_request(response):
|
||||||
"""Log a webserver request."""
|
"""Log a webserver request."""
|
||||||
|
@ -70,7 +70,7 @@ def temp_basedir_env(tmpdir, short_tmpdir):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.linux
|
@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.
|
"""Test downloads with LC_ALL=C set.
|
||||||
|
|
||||||
https://github.com/qutebrowser/qutebrowser/issues/908
|
https://github.com/qutebrowser/qutebrowser/issues/908
|
||||||
@ -83,7 +83,7 @@ def test_ascii_locale(request, httpbin, tmpdir, quteproc_new):
|
|||||||
# Test a normal download
|
# Test a normal download
|
||||||
quteproc_new.set_setting('downloads.location.prompt', 'false')
|
quteproc_new.set_setting('downloads.location.prompt', 'false')
|
||||||
url = 'http://localhost:{port}/data/downloads/ä-issue908.bin'.format(
|
url = 'http://localhost:{port}/data/downloads/ä-issue908.bin'.format(
|
||||||
port=httpbin.port)
|
port=server.port)
|
||||||
quteproc_new.send_cmd(':download {}'.format(url))
|
quteproc_new.send_cmd(':download {}'.format(url))
|
||||||
quteproc_new.wait_for(category='downloads',
|
quteproc_new.wait_for(category='downloads',
|
||||||
message='Download ?-issue908.bin finished')
|
message='Download ?-issue908.bin finished')
|
||||||
@ -103,7 +103,7 @@ def test_ascii_locale(request, httpbin, tmpdir, quteproc_new):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.linux
|
@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):
|
tmpdir, quteproc_new):
|
||||||
"""Test downloads with a misconfigured XDG_DOWNLOAD_DIR.
|
"""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')
|
quteproc_new.set_setting('downloads.location.prompt', 'false')
|
||||||
url = 'http://localhost:{port}/data/downloads/download.bin'.format(
|
url = 'http://localhost:{port}/data/downloads/download.bin'.format(
|
||||||
port=httpbin.port)
|
port=server.port)
|
||||||
quteproc_new.send_cmd(':download {}'.format(url))
|
quteproc_new.send_cmd(':download {}'.format(url))
|
||||||
line = quteproc_new.wait_for(
|
line = quteproc_new.wait_for(
|
||||||
loglevel=logging.ERROR, category='message',
|
loglevel=logging.ERROR, category='message',
|
||||||
|
@ -85,25 +85,25 @@ def download_dir(tmpdir):
|
|||||||
return DownloadDir(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:
|
with open(os.path.join(test_dir, 'requests'), encoding='utf-8') as f:
|
||||||
expected_requests = []
|
expected_requests = []
|
||||||
for line in f:
|
for line in f:
|
||||||
if line.startswith('#'):
|
if line.startswith('#'):
|
||||||
continue
|
continue
|
||||||
path = '/{}/{}'.format(test_path, line.strip())
|
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
|
# 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]
|
for req in actual_requests]
|
||||||
assert (collections.Counter(actual_requests) ==
|
assert (collections.Counter(actual_requests) ==
|
||||||
collections.Counter(expected_requests))
|
collections.Counter(expected_requests))
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('test_name', collect_tests())
|
@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.directory', download_dir.location)
|
||||||
quteproc.set_setting('downloads.location.prompt', 'false')
|
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
|
# Wait for favicon.ico to be loaded if there is one
|
||||||
if os.path.exists(os.path.join(test_dir, 'favicon.png')):
|
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
|
# 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.send_cmd(':download --mhtml --dest "{}"'.format(download_dest))
|
||||||
quteproc.wait_for(category='downloads',
|
quteproc.wait_for(category='downloads',
|
||||||
message='File successfully written.')
|
message='File successfully written.')
|
||||||
@ -136,4 +136,4 @@ def test_mhtml(request, test_name, download_dir, quteproc, httpbin):
|
|||||||
download_dir.sanity_check_mhtml()
|
download_dir.sanity_check_mhtml()
|
||||||
|
|
||||||
if not request.config.webengine:
|
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