qutebrowser/tests/end2end/features/conftest.py

650 lines
21 KiB
Python
Raw Normal View History

2015-11-02 23:49:44 +01:00
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
2018-02-05 12:19:50 +01:00
# Copyright 2015-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
2015-11-02 23:49:44 +01:00
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Steps for bdd-like tests."""
2016-09-11 15:53:46 +02:00
import os
import re
import sys
2015-11-21 14:23:44 +01:00
import time
2015-11-23 14:37:40 +01:00
import json
2015-11-01 22:10:48 +01:00
import logging
import collections
import textwrap
2015-10-11 13:53:59 +02:00
2015-11-27 11:50:23 +01:00
import pytest
2015-11-02 20:32:15 +01:00
import pytest_bdd as bdd
2015-10-11 13:53:59 +02:00
from qutebrowser.utils import log, utils
from qutebrowser.browser import pdfjs
from helpers import utils as testutils
2015-10-11 13:53:59 +02:00
def _get_echo_exe_path():
"""Return the path to an echo-like command, depending on the system.
Return:
Path to the "echo"-utility.
"""
if utils.is_windows:
return os.path.join(testutils.abs_datapath(), 'userscripts',
'echo.bat')
else:
return 'echo'
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""Add a BDD section to the test output."""
outcome = yield
if call.when not in ['call', 'teardown']:
return
report = outcome.get_result()
if report.passed:
return
if (not hasattr(report.longrepr, 'addsection') or
not hasattr(report, 'scenario')):
2017-07-09 11:51:22 +02:00
# In some conditions (on macOS and Windows it seems), report.longrepr
2018-02-03 19:08:42 +01:00
# is actually a tuple. This is handled similarly in pytest-qt too.
#
# Since this hook is invoked for any test, we also need to skip it for
# non-BDD ones.
return
if sys.stdout.isatty() and item.config.getoption('--color') != 'no':
colors = {
'failed': log.COLOR_ESCAPES['red'],
'passed': log.COLOR_ESCAPES['green'],
'keyword': log.COLOR_ESCAPES['cyan'],
'reset': log.RESET_ESCAPE,
}
else:
colors = {
'failed': '',
'passed': '',
'keyword': '',
'reset': '',
}
output = []
output.append("{kw_color}Feature:{reset} {name}".format(
kw_color=colors['keyword'],
name=report.scenario['feature']['name'],
reset=colors['reset'],
))
2016-06-07 08:23:35 +02:00
output.append(
" {kw_color}Scenario:{reset} {name} ({filename}:{line})".format(
kw_color=colors['keyword'],
2016-06-07 08:23:35 +02:00
name=report.scenario['name'],
filename=report.scenario['feature']['rel_filename'],
line=report.scenario['line_number'],
reset=colors['reset'])
)
for step in report.scenario['steps']:
output.append(
" {kw_color}{keyword}{reset} {color}{name}{reset} "
"({duration:.2f}s)".format(
kw_color=colors['keyword'],
color=colors['failed'] if step['failed'] else colors['passed'],
keyword=step['keyword'],
name=step['name'],
duration=step['duration'],
reset=colors['reset'])
)
report.longrepr.addsection("BDD scenario", '\n'.join(output))
2015-11-26 14:25:33 +01:00
## Given
2017-06-16 16:22:41 +02:00
@bdd.given(bdd.parsers.parse("I set {opt} to {value}"))
2017-09-19 10:35:54 +02:00
def set_setting_given(quteproc, server, opt, value):
2015-11-26 14:25:33 +01:00
"""Set a qutebrowser setting.
This is available as "Given:" step so it can be used as "Background:".
"""
if value == '<empty>':
value = ''
2017-09-19 10:35:54 +02:00
value = value.replace('(port)', str(server.port))
2017-06-16 16:22:41 +02:00
quteproc.set_setting(opt, value)
2015-10-11 13:53:59 +02:00
@bdd.given(bdd.parsers.parse("I open {path}"))
def open_path_given(quteproc, path):
2015-11-26 14:25:33 +01:00
"""Open a URL.
This is available as "Given:" step so it can be used as "Background:".
It always opens a new tab, unlike "When I open ..."
"""
2015-10-13 07:14:32 +02:00
quteproc.open_path(path, new_tab=True)
2015-10-11 13:53:59 +02:00
2015-11-26 14:25:33 +01:00
@bdd.given(bdd.parsers.parse("I run {command}"))
def run_command_given(quteproc, command):
"""Run a qutebrowser command.
This is available as "Given:" step so it can be used as "Background:".
"""
quteproc.send_cmd(command)
@bdd.given("I have a fresh instance")
def fresh_instance(quteproc):
"""Restart qutebrowser instance for tests needing a fresh state."""
quteproc.terminate()
quteproc.start()
@bdd.given("I clean up open tabs")
def clean_open_tabs(quteproc):
"""Clean up open windows and tabs."""
2017-06-16 16:22:41 +02:00
quteproc.set_setting('tabs.last_close', 'blank')
quteproc.send_cmd(':window-only')
quteproc.send_cmd(':tab-only --force')
2016-11-16 08:19:21 +01:00
quteproc.send_cmd(':tab-close --force')
quteproc.wait_for_load_finished_url('about:blank')
@bdd.given('pdfjs is available')
2018-10-05 14:05:54 +02:00
def pdfjs_available(data_tmpdir):
if not pdfjs.is_available():
pytest.skip("No pdfjs installation found.")
2015-11-26 14:25:33 +01:00
## When
2015-10-11 13:53:59 +02:00
@bdd.when(bdd.parsers.parse("I open {path}"))
2017-09-19 10:35:54 +02:00
def open_path(quteproc, server, path):
2015-11-26 14:25:33 +01:00
"""Open a URL.
2017-05-10 08:51:48 +02:00
- If used like "When I open ... in a new tab", the URL is opened in a new
tab.
- With "... in a new window", it's opened in a new 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.
2015-11-26 14:25:33 +01:00
"""
2017-09-19 10:35:54 +02:00
path = path.replace('(port)', str(server.port))
new_tab = False
new_bg_tab = False
new_window = False
2017-05-10 08:51:48 +02:00
private = False
as_url = False
wait = True
new_tab_suffix = ' in a new tab'
new_bg_tab_suffix = ' in a new background tab'
new_window_suffix = ' in a new window'
2017-05-10 08:51:48 +02:00
private_suffix = ' in a private window'
do_not_wait_suffix = ' without waiting'
as_url_suffix = ' as a URL'
while True:
if path.endswith(new_tab_suffix):
path = path[:-len(new_tab_suffix)]
new_tab = True
2016-11-24 08:20:16 +01:00
elif path.endswith(new_bg_tab_suffix):
path = path[:-len(new_bg_tab_suffix)]
new_bg_tab = True
elif path.endswith(new_window_suffix):
path = path[:-len(new_window_suffix)]
new_window = True
2017-05-10 08:51:48 +02:00
elif path.endswith(private_suffix):
path = path[:-len(private_suffix)]
private = True
elif path.endswith(as_url_suffix):
path = path[:-len(as_url_suffix)]
as_url = True
elif path.endswith(do_not_wait_suffix):
path = path[:-len(do_not_wait_suffix)]
wait = False
else:
break
quteproc.open_path(path, new_tab=new_tab, new_bg_tab=new_bg_tab,
2017-05-10 08:51:48 +02:00
new_window=new_window, private=private, as_url=as_url,
wait=wait)
2015-10-11 13:53:59 +02:00
2017-06-16 16:22:41 +02:00
@bdd.when(bdd.parsers.parse("I set {opt} to {value}"))
2017-09-19 10:35:54 +02:00
def set_setting(quteproc, server, opt, value):
2015-11-26 14:25:33 +01:00
"""Set a qutebrowser setting."""
if value == '<empty>':
value = ''
2017-09-19 10:35:54 +02:00
value = value.replace('(port)', str(server.port))
2017-06-16 16:22:41 +02:00
quteproc.set_setting(opt, value)
2015-10-11 13:53:59 +02:00
@bdd.when(bdd.parsers.parse("I run {command}"))
2017-09-19 10:35:54 +02:00
def run_command(quteproc, server, tmpdir, command):
2015-11-26 14:25:33 +01:00
"""Run a qutebrowser command.
The suffix "with count ..." can be used to pass a count to the command.
"""
2015-11-15 20:48:07 +01:00
if 'with count' in command:
command, count = command.split(' with count ')
count = int(count)
else:
count = None
invalid_tag = ' (invalid command)'
if command.endswith(invalid_tag):
command = command[:-len(invalid_tag)]
invalid = True
else:
invalid = False
2017-09-19 10:35:54 +02:00
command = command.replace('(port)', str(server.port))
command = command.replace('(testdata)', testutils.abs_datapath())
command = command.replace('(tmpdir)', str(tmpdir))
2016-09-11 15:53:46 +02:00
command = command.replace('(dirsep)', os.sep)
command = command.replace('(echo-exe)', _get_echo_exe_path())
quteproc.send_cmd(command, count=count, invalid=invalid)
2015-10-11 13:53:59 +02:00
@bdd.when(bdd.parsers.parse("I reload"))
2017-09-19 10:35:54 +02:00
def reload(qtbot, server, quteproc, command):
2015-11-26 14:25:33 +01:00
"""Reload and wait until a new request is received."""
2017-09-19 10:35:54 +02:00
with qtbot.waitSignal(server.new_request):
quteproc.send_cmd(':reload')
@bdd.when(bdd.parsers.parse("I wait until {path} is loaded"))
def wait_until_loaded(quteproc, path):
2015-11-26 14:25:33 +01:00
"""Wait until the given path is loaded (as per qutebrowser log)."""
quteproc.wait_for_load_finished(path)
@bdd.when(bdd.parsers.re(r'I wait for (?P<is_regex>regex )?"'
r'(?P<pattern>[^"]+)" in the log(?P<do_skip> or skip '
r'the test)?'))
def wait_in_log(quteproc, is_regex, pattern, do_skip):
2015-11-26 14:25:33 +01:00
"""Wait for a given pattern in the qutebrowser log.
If used like "When I wait for regex ... in the log" the argument is treated
as regex. Otherwise, it's treated as a pattern (* can be used as wildcard).
"""
if is_regex:
pattern = re.compile(pattern)
line = quteproc.wait_for(message=pattern, do_skip=bool(do_skip))
line.expected = True
@bdd.when(bdd.parsers.re(r'I wait for the (?P<category>error|message|warning) '
r'"(?P<message>.*)"'))
2017-09-19 10:35:54 +02:00
def wait_for_message(quteproc, server, category, message):
2015-11-26 14:25:33 +01:00
"""Wait for a given statusbar message/error/warning."""
2016-04-20 07:16:56 +02:00
quteproc.log_summary('Waiting for {} "{}"'.format(category, message))
2017-09-19 10:35:54 +02:00
expect_message(quteproc, server, category, message)
2015-11-26 14:25:33 +01:00
@bdd.when(bdd.parsers.parse("I wait {delay}s"))
def wait_time(quteproc, delay):
"""Sleep for the given delay."""
time.sleep(float(delay))
@bdd.when(bdd.parsers.re('I press the keys? "(?P<keys>[^"]*)"'))
def press_keys(quteproc, keys):
"""Send the given fake keys to qutebrowser."""
quteproc.press_keys(keys)
@bdd.when("selection is supported")
def selection_supported(qapp):
"""Skip the test if selection isn't supported."""
if not qapp.clipboard().supportsSelection():
pytest.skip("OS doesn't support primary selection!")
@bdd.when("selection is not supported")
def selection_not_supported(qapp):
"""Skip the test if selection is supported."""
if qapp.clipboard().supportsSelection():
pytest.skip("OS supports primary selection!")
@bdd.when(bdd.parsers.re(r'I put "(?P<content>.*)" into the '
r'(?P<what>primary selection|clipboard)'))
2017-09-19 10:35:54 +02:00
def fill_clipboard(quteproc, server, what, content):
content = content.replace('(port)', str(server.port))
2016-02-02 06:37:49 +01:00
content = content.replace(r'\n', '\n')
quteproc.send_cmd(':debug-set-fake-clipboard "{}"'.format(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))
2017-09-19 10:35:54 +02:00
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}"'))
def hint(quteproc, args):
quteproc.send_cmd(':hint {}'.format(args))
quteproc.wait_for(message='hints: *')
@bdd.when(bdd.parsers.parse('I hint with args "{args}" and follow {letter}'))
def hint_and_follow(quteproc, args, letter):
args = args.replace('(testdata)', testutils.abs_datapath())
quteproc.send_cmd(':hint {}'.format(args))
quteproc.wait_for(message='hints: *')
quteproc.send_cmd(':follow-hint {}'.format(letter))
@bdd.when("I wait until the scroll position changed")
def wait_scroll_position(quteproc):
quteproc.wait_scroll_pos_changed()
@bdd.when(bdd.parsers.parse("I wait until the scroll position changed to "
"{x}/{y}"))
def wait_scroll_position_arg(quteproc, x, y):
quteproc.wait_scroll_pos_changed(x, y)
@bdd.when(bdd.parsers.parse('I wait for the javascript message "{message}"'))
def javascript_message_when(quteproc, message):
"""Make sure the given message was logged via javascript."""
quteproc.wait_for_js(message)
@bdd.when("I clear SSL errors")
def clear_ssl_errors(request, quteproc):
if request.config.webengine:
quteproc.terminate()
quteproc.start()
else:
quteproc.send_cmd(':debug-clear-ssl-errors')
2015-11-26 14:25:33 +01:00
## Then
2015-10-11 13:53:59 +02:00
@bdd.then(bdd.parsers.parse("{path} should be loaded"))
def path_should_be_loaded(quteproc, path):
"""Make sure the given path was loaded according to the log.
2018-02-03 19:08:42 +01:00
This is usually the better check compared to "should be requested" as the
page could be loaded from local cache.
"""
quteproc.wait_for_load_finished(path)
@bdd.then(bdd.parsers.parse("{path} should be requested"))
2017-09-19 10:35:54 +02:00
def path_should_be_requested(server, path):
2015-11-26 14:25:33 +01:00
"""Make sure the given path was loaded from the webserver."""
2017-09-19 10:35:54 +02:00
server.wait_for(verb='GET', path='/' + path)
2015-10-11 13:53:59 +02:00
@bdd.then(bdd.parsers.parse("The requests should be:\n{pages}"))
2017-09-19 10:35:54 +02:00
def list_of_requests(server, pages):
2015-11-26 14:25:33 +01:00
"""Make sure the given requests were done from the webserver."""
2017-09-19 10:35:54 +02:00
expected_requests = [server.ExpectedRequest('GET', '/' + path.strip())
for path in pages.split('\n')]
2017-09-19 10:35:54 +02:00
actual_requests = server.get_requests()
assert actual_requests == expected_requests
2015-10-22 06:44:05 +02:00
@bdd.then(bdd.parsers.parse("The unordered requests should be:\n{pages}"))
2017-09-19 10:35:54 +02:00
def list_of_requests_unordered(server, pages):
2015-11-26 14:25:33 +01:00
"""Make sure the given requests were done (in no particular order)."""
2017-09-19 10:35:54 +02:00
expected_requests = [server.ExpectedRequest('GET', '/' + path.strip())
for path in pages.split('\n')]
2017-09-19 10:35:54 +02:00
actual_requests = server.get_requests()
# Requests are not hashable, we need to convert to ExpectedRequests
2017-09-19 10:35:54 +02:00
actual_requests = [server.ExpectedRequest.from_request(req)
2015-11-26 14:25:33 +01:00
for req in actual_requests]
assert (collections.Counter(actual_requests) ==
collections.Counter(expected_requests))
@bdd.then(bdd.parsers.re(r'the (?P<category>error|message|warning) '
2015-11-26 17:50:39 +01:00
r'"(?P<message>.*)" should be shown'))
2017-09-19 10:35:54 +02:00
def expect_message(quteproc, server, category, message):
2015-11-26 14:25:33 +01:00
"""Expect the given message in the qutebrowser log."""
2015-11-02 07:43:37 +01:00
category_to_loglevel = {
'message': logging.INFO,
'error': logging.ERROR,
'warning': logging.WARNING,
2015-11-02 07:43:37 +01:00
}
2017-09-19 10:35:54 +02:00
message = message.replace('(port)', str(server.port))
2015-11-02 07:43:37 +01:00
quteproc.mark_expected(category='message',
loglevel=category_to_loglevel[category],
2015-11-01 22:10:48 +01:00
message=message)
2015-11-26 14:25:33 +01:00
@bdd.then(bdd.parsers.re(r'(?P<is_regex>regex )?"(?P<pattern>[^"]+)" should '
r'be logged( with level (?P<loglevel>.*))?'))
def should_be_logged(quteproc, server, is_regex, pattern, loglevel):
2015-11-26 14:25:33 +01:00
"""Expect the given pattern on regex in the log."""
if is_regex:
pattern = re.compile(pattern)
2016-04-20 16:58:14 +02:00
else:
2017-09-19 10:35:54 +02:00
pattern = pattern.replace('(port)', str(server.port))
args = {
'message': pattern,
}
if loglevel:
args['loglevel'] = getattr(logging, loglevel.upper())
line = quteproc.wait_for(**args)
line.expected = True
2015-11-25 17:19:16 +01:00
@bdd.then(bdd.parsers.parse('"{pattern}" should not be logged'))
def ensure_not_logged(quteproc, pattern):
2015-11-26 14:25:33 +01:00
"""Make sure the given pattern was *not* logged."""
quteproc.ensure_not_logged(message=pattern)
@bdd.then(bdd.parsers.parse('the javascript message "{message}" should be '
'logged'))
def javascript_message_logged(quteproc, message):
2015-11-26 14:25:33 +01:00
"""Make sure the given message was logged via javascript."""
quteproc.wait_for_js(message)
@bdd.then(bdd.parsers.parse('the javascript message "{message}" should not be '
'logged'))
def javascript_message_not_logged(quteproc, message):
2015-11-26 14:25:33 +01:00
"""Make sure the given message was *not* logged via javascript."""
quteproc.ensure_not_logged(category='js',
message='[*] {}'.format(message))
2015-11-26 14:25:33 +01:00
@bdd.then(bdd.parsers.parse("The session should look like:\n{expected}"))
def compare_session(request, quteproc, expected):
2015-11-26 14:25:33 +01:00
"""Compare the current sessions against the given template.
partial_compare is used, which means only the keys/values listed will be
compared.
"""
quteproc.compare_session(expected)
@bdd.then("no crash should happen")
def no_crash():
"""Don't do anything.
2016-04-08 07:35:53 +02:00
This is actually a NOP as a crash is already checked in the log.
"""
time.sleep(0.5)
2015-11-23 14:37:40 +01:00
@bdd.then(bdd.parsers.parse("the header {header} should be set to {value}"))
def check_header(quteproc, header, value):
"""Check if a given header is set correctly.
2017-09-19 10:35:54 +02:00
This assumes we're on the server header page.
2015-11-23 14:37:40 +01:00
"""
content = quteproc.get_content()
data = json.loads(content)
print(data)
2017-07-03 15:57:22 +02:00
if value == '<unset>':
assert header not in data['headers']
else:
actual = data['headers'][header]
assert testutils.pattern_match(pattern=value, value=actual)
@bdd.then(bdd.parsers.parse('the page should contain the html "{text}"'))
def check_contents_html(quteproc, text):
"""Check the current page's content based on a substring."""
content = quteproc.get_content(plain=False)
assert text in content
@bdd.then(bdd.parsers.parse('the page should contain the plaintext "{text}"'))
2016-01-06 07:55:42 +01:00
def check_contents_plain(quteproc, text):
"""Check the current page's content based on a substring."""
content = quteproc.get_content().strip()
assert text in content
@bdd.then(bdd.parsers.parse('the page should not contain the plaintext '
'"{text}"'))
def check_not_contents_plain(quteproc, text):
"""Check the current page's content based on a substring."""
content = quteproc.get_content().strip()
assert text not in content
2016-01-14 22:27:00 +01:00
@bdd.then(bdd.parsers.parse('the json on the page should be:\n{text}'))
def check_contents_json(quteproc, text):
"""Check the current page's content as json."""
content = quteproc.get_content().strip()
expected = json.loads(text)
actual = json.loads(content)
assert actual == expected
@bdd.then(bdd.parsers.parse("the following tabs should be open:\n{tabs}"))
def check_open_tabs(quteproc, request, tabs):
2015-11-26 14:25:33 +01:00
"""Check the list of open tabs in the session.
This is a lightweight alternative for "The session should look like: ...".
It expects a list of URLs, with an optional "(active)" suffix.
"""
session = quteproc.get_session()
active_suffix = ' (active)'
pinned_suffix = ' (pinned)'
tabs = tabs.splitlines()
assert len(session['windows']) == 1
assert len(session['windows'][0]['tabs']) == len(tabs)
# If we don't have (active) anywhere, don't check it
has_active = any(active_suffix in line for line in tabs)
has_pinned = any(pinned_suffix in line for line in tabs)
for i, line in enumerate(tabs):
line = line.strip()
assert line.startswith('- ')
line = line[2:] # remove "- " prefix
active = False
pinned = False
while line.endswith(active_suffix) or line.endswith(pinned_suffix):
if line.endswith(active_suffix):
# active
line = line[:-len(active_suffix)]
active = True
else:
# pinned
line = line[:-len(pinned_suffix)]
pinned = True
session_tab = session['windows'][0]['tabs'][i]
current_page = session_tab['history'][-1]
assert current_page['url'] == quteproc.path_to_url(line)
if active:
assert session_tab['active']
elif has_active:
assert 'active' not in session_tab
if pinned:
assert current_page['pinned']
elif has_pinned:
assert not current_page['pinned']
@bdd.then(bdd.parsers.re(r'the (?P<what>primary selection|clipboard) should '
r'contain "(?P<content>.*)"'))
2017-09-19 10:35:54 +02:00
def clipboard_contains(quteproc, server, what, content):
expected = content.replace('(port)', str(server.port))
expected = expected.replace('\\n', '\n')
expected = expected.replace('(linesep)', os.linesep)
quteproc.wait_for(message='Setting fake {}: {}'.format(
what, json.dumps(expected)))
@bdd.then(bdd.parsers.parse('the clipboard should contain:\n{content}'))
2017-09-19 10:35:54 +02:00
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)))
@bdd.then("qutebrowser should quit")
def should_quit(qtbot, quteproc):
quteproc.wait_for_quit()
2016-07-03 22:32:07 +02:00
def _get_scroll_values(quteproc):
data = quteproc.get_session()
pos = data['windows'][0]['tabs'][0]['history'][-1]['scroll-pos']
2016-07-03 22:32:07 +02:00
return (pos['x'], pos['y'])
@bdd.then(bdd.parsers.re(r"the page should be scrolled "
r"(?P<direction>horizontally|vertically)"))
def check_scrolled(quteproc, direction):
quteproc.wait_scroll_pos_changed()
2016-07-03 22:32:07 +02:00
x, y = _get_scroll_values(quteproc)
if direction == 'horizontally':
2016-09-29 07:13:34 +02:00
assert x > 0
2016-07-03 22:32:07 +02:00
assert y == 0
else:
assert x == 0
2016-09-29 07:13:34 +02:00
assert y > 0
2016-07-03 22:32:07 +02:00
@bdd.then("the page should not be scrolled")
def check_not_scrolled(request, quteproc):
2016-07-03 22:32:07 +02:00
x, y = _get_scroll_values(quteproc)
assert x == 0
assert y == 0
@bdd.then(bdd.parsers.parse("the option {option} should be set to {value}"))
def check_option(quteproc, option, value):
actual_value = quteproc.get_setting(option)
assert actual_value == value