Merge branch 'css' of github.com:willmcgugan/textual into text-justify

This commit is contained in:
Darren Burns
2022-08-26 12:28:23 +01:00
170 changed files with 4620 additions and 1354 deletions

View File

@@ -268,7 +268,7 @@ async def test_scrollbar_gutter(
text_widget = TextWidget()
text_widget.styles.height = "auto"
container.add_child(text_widget)
container._add_child(text_widget)
class MyTestApp(AppTest):
def compose(self) -> ComposeResult:

View File

@@ -21,7 +21,7 @@ def test_nodes_take_display_property_into_account_when_they_display_their_childr
screen = Screen()
screen.styles.layout = layout
screen.add_child(widget)
screen._add_child(widget)
displayed_children = screen.displayed_children
assert isinstance(displayed_children, list)

View File

@@ -243,7 +243,7 @@ def test_bound_animator():
assert animator._animations[(id(animate_test), "foo")] == expected
def test_animator_on_complete_callback_not_fired_before_duration_ends():
async def test_animator_on_complete_callback_not_fired_before_duration_ends():
callback = Mock()
animate_test = AnimateTest()
animator = MockAnimator(Mock())
@@ -251,7 +251,7 @@ def test_animator_on_complete_callback_not_fired_before_duration_ends():
animator.animate(animate_test, "foo", 200, duration=10, on_complete=callback)
animator._time = 9
animator()
await animator()
assert not callback.called

View File

@@ -32,10 +32,10 @@ def parent():
child1 = DOMNode(id="child1")
child2 = DOMNode(id="child2")
grandchild1 = DOMNode(id="grandchild1")
child1.add_child(grandchild1)
child1._add_child(grandchild1)
parent.add_child(child1)
parent.add_child(child2)
parent._add_child(child1)
parent._add_child(child2)
yield parent

View File

@@ -20,7 +20,7 @@ async def test_focus_chain():
# Check empty focus chain
assert not app.focus_chain
app.screen.add_children(
app.screen._add_children(
Focusable(id="foo"),
NonFocusable(id="bar"),
Focusable(Focusable(id="Paul"), id="container1"),
@@ -37,7 +37,7 @@ async def test_focus_next_and_previous():
app = App()
app._set_active()
app.push_screen(Screen())
app.screen.add_children(
app.screen._add_children(
Focusable(id="foo"),
NonFocusable(id="bar"),
Focusable(Focusable(id="Paul"), id="container1"),

View File

@@ -11,8 +11,8 @@ def test_query():
app = App()
main_view = View(id="main")
help_view = View(id="help")
app.add_child(main_view)
app.add_child(help_view)
app._add_child(main_view)
app._add_child(help_view)
widget1 = Widget(id="widget1")
widget2 = Widget(id="widget2")
@@ -22,21 +22,21 @@ def test_query():
helpbar = Widget(id="helpbar")
helpbar.add_class("float")
main_view.add_child(widget1)
main_view.add_child(widget2)
main_view.add_child(sidebar)
main_view._add_child(widget1)
main_view._add_child(widget2)
main_view._add_child(sidebar)
sub_view = View(id="sub")
sub_view.add_class("-subview")
main_view.add_child(sub_view)
main_view._add_child(sub_view)
tooltip = Widget(id="tooltip")
tooltip.add_class("float", "transient")
sub_view.add_child(tooltip)
sub_view._add_child(tooltip)
help = Widget(id="markdown")
help_view.add_child(help)
help_view.add_child(helpbar)
help_view._add_child(help)
help_view._add_child(helpbar)
# repeat tests to account for caching
for repeat in range(3):

View File

@@ -37,7 +37,7 @@ def parser():
return XTermParser(sender=mock.sentinel, more_data=lambda: False)
@pytest.mark.parametrize("chunk_size", [2,3,4,5,6])
@pytest.mark.parametrize("chunk_size", [2, 3, 4, 5, 6])
def test_varying_parser_chunk_sizes_no_missing_data(parser, chunk_size):
end = "\x1b[8~"
text = "ABCDEFGH"
@@ -54,7 +54,7 @@ def test_varying_parser_chunk_sizes_no_missing_data(parser, chunk_size):
def test_bracketed_paste(parser):
""" When bracketed paste mode is enabled in the terminal emulator and
"""When bracketed paste mode is enabled in the terminal emulator and
the user pastes in some text, it will surround the pasted input
with the escape codes "\x1b[200~" and "\x1b[201~". The text between
these codes corresponds to a single `Paste` event in Textual.
@@ -90,7 +90,7 @@ def test_bracketed_paste_amongst_other_codes(parser):
def test_cant_match_escape_sequence_too_long(parser):
""" The sequence did not match, and we hit the maximum sequence search
"""The sequence did not match, and we hit the maximum sequence search
length threshold, so each character should be issued as a key-press instead.
"""
sequence = "\x1b[123456789123456789123"
@@ -109,15 +109,22 @@ def test_cant_match_escape_sequence_too_long(parser):
assert events[index].key == character
@pytest.mark.parametrize("chunk_size", [
pytest.param(2, marks=pytest.mark.xfail(reason="Fails when ESC at end of chunk")),
3,
pytest.param(4, marks=pytest.mark.xfail(reason="Fails when ESC at end of chunk")),
5,
6,
])
@pytest.mark.parametrize(
"chunk_size",
[
pytest.param(
2, marks=pytest.mark.xfail(reason="Fails when ESC at end of chunk")
),
3,
pytest.param(
4, marks=pytest.mark.xfail(reason="Fails when ESC at end of chunk")
),
5,
6,
],
)
def test_unknown_sequence_followed_by_known_sequence(parser, chunk_size):
""" When we feed the parser an unknown sequence followed by a known
"""When we feed the parser an unknown sequence followed by a known
sequence. The characters in the unknown sequence are delivered as keys,
and the known escape sequence that follows is delivered as expected.
"""
@@ -174,16 +181,19 @@ def test_double_escape(parser):
assert [event.key for event in events] == ["escape"]
@pytest.mark.parametrize("sequence, event_type, shift, meta", [
# Mouse down, with and without modifiers
("\x1b[<0;50;25M", MouseDown, False, False),
("\x1b[<4;50;25M", MouseDown, True, False),
("\x1b[<8;50;25M", MouseDown, False, True),
# Mouse up, with and without modifiers
("\x1b[<0;50;25m", MouseUp, False, False),
("\x1b[<4;50;25m", MouseUp, True, False),
("\x1b[<8;50;25m", MouseUp, False, True),
])
@pytest.mark.parametrize(
"sequence, event_type, shift, meta",
[
# Mouse down, with and without modifiers
("\x1b[<0;50;25M", MouseDown, False, False),
("\x1b[<4;50;25M", MouseDown, True, False),
("\x1b[<8;50;25M", MouseDown, False, True),
# Mouse up, with and without modifiers
("\x1b[<0;50;25m", MouseUp, False, False),
("\x1b[<4;50;25m", MouseUp, True, False),
("\x1b[<8;50;25m", MouseUp, False, True),
],
)
def test_mouse_click(parser, sequence, event_type, shift, meta):
"""ANSI codes for mouse should be converted to Textual events"""
events = list(parser.feed(sequence))
@@ -201,12 +211,15 @@ def test_mouse_click(parser, sequence, event_type, shift, meta):
assert event.shift is shift
@pytest.mark.parametrize("sequence, shift, meta, button", [
("\x1b[<32;15;38M", False, False, 1), # Click and drag
("\x1b[<35;15;38M", False, False, 0), # Basic cursor movement
("\x1b[<39;15;38M", True, False, 0), # Shift held down
("\x1b[<43;15;38M", False, True, 0), # Meta held down
])
@pytest.mark.parametrize(
"sequence, shift, meta, button",
[
("\x1b[<32;15;38M", False, False, 1), # Click and drag
("\x1b[<35;15;38M", False, False, 0), # Basic cursor movement
("\x1b[<39;15;38M", True, False, 0), # Shift held down
("\x1b[<43;15;38M", False, True, 0), # Meta held down
],
)
def test_mouse_move(parser, sequence, shift, meta, button):
events = list(parser.feed(sequence))
@@ -222,12 +235,15 @@ def test_mouse_move(parser, sequence, shift, meta, button):
assert event.button == button
@pytest.mark.parametrize("sequence", [
"\x1b[<64;18;25M",
"\x1b[<68;18;25M",
"\x1b[<72;18;25M",
])
def test_mouse_scroll_down(parser, sequence):
@pytest.mark.parametrize(
"sequence",
[
"\x1b[<64;18;25M",
"\x1b[<68;18;25M",
"\x1b[<72;18;25M",
],
)
def test_mouse_scroll_up(parser, sequence):
"""Scrolling the mouse with and without modifiers held down.
We don't currently capture modifier keys in scroll events.
"""
@@ -237,24 +253,27 @@ def test_mouse_scroll_down(parser, sequence):
event = events[0]
assert isinstance(event, MouseScrollDown)
assert isinstance(event, MouseScrollUp)
assert event.x == 17
assert event.y == 24
@pytest.mark.parametrize("sequence, shift, meta", [
("\x1b[<65;18;25M", False, False),
("\x1b[<69;18;25M", True, False),
("\x1b[<73;18;25M", False, True),
])
def test_mouse_scroll_up(parser, sequence, shift, meta):
@pytest.mark.parametrize(
"sequence",
[
"\x1b[<65;18;25M",
"\x1b[<69;18;25M",
"\x1b[<73;18;25M",
],
)
def test_mouse_scroll_down(parser, sequence):
events = list(parser.feed(sequence))
assert len(events) == 1
event = events[0]
assert isinstance(event, MouseScrollUp)
assert isinstance(event, MouseScrollDown)
assert event.x == 17
assert event.y == 24

View File

@@ -70,7 +70,7 @@ class AppTest(App):
waiting_duration_after_yield: float = 0,
) -> AsyncContextManager[ClockMock]:
async def run_app() -> None:
await self.process_messages()
await self._process_messages()
@contextlib.asynccontextmanager
async def get_running_state_context_manager():
@@ -103,6 +103,7 @@ class AppTest(App):
# End of simulated time: we just shut down ourselves:
assert not run_task.done()
await self.shutdown()
await run_task
return get_running_state_context_manager()