237 lines
7.8 KiB
Python
237 lines
7.8 KiB
Python
# SPDX-FileCopyrightText: Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
|
#
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
import pytest
|
|
|
|
from qutebrowser.config.configtypes import NewTabPosition, NewChildPosition
|
|
from qutebrowser.misc.notree import Node
|
|
from qutebrowser.mainwindow import treetabbedbrowser, treetabwidget
|
|
|
|
from tests.unit.misc.test_notree import str_to_tree
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_browser(mocker):
|
|
# Mock browser used as `self` below because we are actually testing mostly
|
|
# standalone functionality apart from the tab stack related counters.
|
|
# Which are also only defined in __init__, not on the class, so mock
|
|
# doesn't see them. Hence specifying them manually here.
|
|
browser = mocker.Mock(
|
|
spec=treetabbedbrowser.TreeTabbedBrowser,
|
|
widget=mocker.Mock(spec=treetabwidget.TreeTabWidget),
|
|
_tree_tab_child_rel_idx=0,
|
|
_tree_tab_sibling_rel_idx=0,
|
|
_tree_tab_toplevel_rel_idx=0,
|
|
)
|
|
|
|
# Sad little workaround to create a bound method on a mock, because
|
|
# _position_tab calls a method on self but we are using a mock as self to
|
|
# avoid initializing the whole tabbed browser class.
|
|
def reset_passthrough():
|
|
return treetabbedbrowser.TreeTabbedBrowser._reset_stack_counters(
|
|
browser
|
|
)
|
|
browser._reset_stack_counters = reset_passthrough
|
|
|
|
return browser
|
|
|
|
|
|
class TestPositionTab:
|
|
"""Test TreeTabbedBrowser._position_tab()."""
|
|
|
|
@pytest.mark.parametrize(
|
|
" relation, cur_node, pos, expected", [
|
|
("sibling", "three", "first", "one",),
|
|
("sibling", "three", "prev", "two",),
|
|
("sibling", "three", "next", "three",),
|
|
("sibling", "three", "last", "six",),
|
|
("sibling", "one", "first", "root",),
|
|
("sibling", "one", "prev", "root",),
|
|
("sibling", "one", "next", "one",),
|
|
("sibling", "one", "last", "seven",),
|
|
|
|
("related", "one", "first", "one",),
|
|
("related", "one", "last", "six",),
|
|
("related", "two", "first", "two",),
|
|
("related", "two", "last", "two",),
|
|
|
|
(None, "five", "first", "root",),
|
|
(None, "five", "prev", "root",),
|
|
(None, "five", "next", "one",),
|
|
(None, "five", "last", "seven",),
|
|
(None, "seven", "prev", "one",),
|
|
(None, "seven", "next", "seven",),
|
|
]
|
|
)
|
|
def test_position_tab(
|
|
self,
|
|
config_stub,
|
|
mock_browser,
|
|
# parameterized
|
|
relation,
|
|
cur_node,
|
|
pos,
|
|
expected,
|
|
):
|
|
"""Test tree tab positioning.
|
|
|
|
How to use the parameters above:
|
|
* refer to the tree structure being passed to str_to_tree() below, that's
|
|
our starting state
|
|
* specify how the new node should be related to the current one
|
|
* specify cur_node by value, which is the tab currently focused when the
|
|
new tab is opened and the one the "sibling" and "related" arguments
|
|
refer to
|
|
* set "pos" which is the position of the new node in the list of
|
|
siblings it's going to end up in. It should be one of first, list, prev,
|
|
next (except the "related" relation doesn't support prev and next)
|
|
* specify the expected preceding node (the preceding sibling if there is
|
|
one, otherwise the parent) after the new node is positioned, "root" is
|
|
a valid value for this
|
|
|
|
Having the expectation being the preceding tab (sibling or parent) is
|
|
a bit limited, in particular if the new tab somehow ends up as a child
|
|
instead of the next sibling you wouldn't be able to tell those
|
|
situations apart. But I went this route to avoid having to specify
|
|
multiple trees in the parameters.
|
|
"""
|
|
root = str_to_tree(
|
|
"""
|
|
- one
|
|
- two
|
|
- three
|
|
- four
|
|
- five
|
|
- six
|
|
- seven
|
|
""",
|
|
)[0]
|
|
new_node = Node("new", parent=root)
|
|
|
|
config_stub.val.tabs.new_position.stacking = False
|
|
self.call_position_tab(
|
|
mock_browser,
|
|
root,
|
|
cur_node,
|
|
new_node,
|
|
pos,
|
|
relation,
|
|
)
|
|
|
|
preceding_node = None
|
|
if new_node.parent.children[0] == new_node:
|
|
preceding_node = new_node.parent
|
|
else:
|
|
for n in new_node.parent.children:
|
|
if n.value == "new":
|
|
break
|
|
preceding_node = n
|
|
else:
|
|
pytest.fail("new tab not found")
|
|
|
|
assert preceding_node.value == expected
|
|
|
|
def call_position_tab(
|
|
self,
|
|
mock_browser,
|
|
root,
|
|
cur_node,
|
|
new_node,
|
|
pos,
|
|
relation,
|
|
background=False,
|
|
):
|
|
sibling = related = False
|
|
if relation == "sibling":
|
|
sibling = True
|
|
elif relation == "related":
|
|
related = True
|
|
elif relation == "background":
|
|
background = True
|
|
elif relation is not None:
|
|
pytest.fail(
|
|
"Valid values for relation are: "
|
|
"sibling, related, background, None"
|
|
)
|
|
|
|
# This relation -> parent mapping is copied from
|
|
# TreeTabbedBrowser.tabopen().
|
|
cur_node = next(n for n in root.traverse() if n.value == cur_node)
|
|
assert not (related and sibling)
|
|
if related:
|
|
parent = cur_node
|
|
NewChildPosition().from_str(pos)
|
|
elif sibling:
|
|
parent = cur_node.parent
|
|
NewTabPosition().from_str(pos)
|
|
else:
|
|
parent = root
|
|
NewTabPosition().from_str(pos)
|
|
|
|
treetabbedbrowser.TreeTabbedBrowser._position_tab(
|
|
mock_browser,
|
|
cur_node=cur_node,
|
|
new_node=new_node,
|
|
pos=pos,
|
|
parent=parent,
|
|
sibling=sibling,
|
|
related=related,
|
|
background=background,
|
|
)
|
|
|
|
@pytest.mark.parametrize(
|
|
" test_tree, relation, pos, expected", [
|
|
("tree_one", "sibling", "next", "one,two,new1,new2,new3",),
|
|
("tree_one", "sibling", "prev", "one,new3,new2,new1,two",),
|
|
("tree_one", None, "next", "one,two,new1,new2,new3",),
|
|
("tree_one", None, "prev", "new3,new2,new1,one,two",),
|
|
("tree_one", "related", "first", "one,two,new1,new2,new3",),
|
|
("tree_one", "related", "last", "one,two,new1,new2,new3",),
|
|
]
|
|
)
|
|
def test_position_tab_stacking(
|
|
self,
|
|
config_stub,
|
|
mock_browser,
|
|
# parameterized
|
|
test_tree,
|
|
relation,
|
|
pos,
|
|
expected,
|
|
):
|
|
"""Test tree tab positioning with tab stacking enabled.
|
|
|
|
With tab stacking enabled the first background tab should be opened
|
|
beside the current one, successive background tabs should be opened on
|
|
the other side of prior opened tabs, not beside the current tab.
|
|
This test covers what is currently implemented, I'm not sure all the
|
|
desired behavior is implemented currently though.
|
|
"""
|
|
# Simpler tree here to make the assert string a bit simpler.
|
|
# Tab "two" is hardcoded as cur_tab.
|
|
root = str_to_tree(
|
|
"""
|
|
- one
|
|
- two
|
|
""",
|
|
)[0]
|
|
config_stub.val.tabs.new_position.stacking = True
|
|
|
|
for val in ["new1", "new2", "new3"]:
|
|
new_node = Node(val, parent=root)
|
|
|
|
self.call_position_tab(
|
|
mock_browser,
|
|
root,
|
|
"two",
|
|
new_node,
|
|
pos,
|
|
relation,
|
|
background=True,
|
|
)
|
|
|
|
actual = ",".join([n.value for n in root.traverse()])
|
|
actual = actual[len("root,"):]
|
|
assert actual == expected
|