Skip to content

Commit 8bd3fe4

Browse files
committed
Add tests of folded table printer
Unit tests of the Row class and some higher level integration tests of the printer itself to ensure it produces proper tabular output both with and without folding enabled.
1 parent fbc1b97 commit 8bd3fe4

File tree

4 files changed

+196
-6
lines changed

4 files changed

+196
-6
lines changed

src/globus_cli/commands/endpoint/search.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,6 @@ def endpoint_search(
158158
search_iterator,
159159
fields=ENDPOINT_LIST_FIELDS,
160160
json_converter=iterable_response_to_dict,
161-
text_mode=display.FOLDED_TABLE,
162161
)
163162

164163
if search_iterator.has_next():

src/globus_cli/commands/group/list.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,4 @@ def group_list(login_manager: LoginManager) -> None:
2222
SESSION_ENFORCEMENT_FIELD,
2323
Field("Roles", "my_memberships[].role", formatter=formatters.SortedArray),
2424
],
25-
text_mode=display.FOLDED_TABLE,
2625
)

src/globus_cli/termio/printers/folded_table_printer.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ class FoldedTablePrinter(Printer[t.Iterable[t.Any]]):
4242
:param fields: a list of Fields with load and render instructions; one per column.
4343
"""
4444

45-
def __init__(self, fields: t.Iterable[Field]) -> None:
45+
def __init__(self, fields: t.Iterable[Field], width: int | None = None) -> None:
4646
self._fields = tuple(fields)
47-
self._width = _get_terminal_content_width()
47+
self._width = width or _get_terminal_content_width()
48+
self._folding_enabled = _detect_folding_enabled()
4849

4950
def echo(self, data: t.Iterable[t.Any], stream: t.IO[str] | None = None) -> None:
5051
"""
@@ -83,7 +84,7 @@ def echo(self, data: t.Iterable[t.Any], stream: t.IO[str] | None = None) -> None
8384
echo(_separator_line(col_widths, style=table_style | OutputStyle.bottom))
8485

8586
def _fold_table(self, table: RowTable) -> RowTable:
86-
if not _detect_folding_enabled():
87+
if not self._folding_enabled:
8788
return table
8889

8990
# if the table is initially narrow enough to fit, do not fold
@@ -208,7 +209,7 @@ def fold(self, n: int) -> Row:
208209

209210
def _split_level(
210211
self, level: tuple[str, ...], modulus: int
211-
) -> t.Iterator[tuple[str, ...], ...]:
212+
) -> t.Iterator[tuple[str, ...]]:
212213
bins = collections.defaultdict(list)
213214
for i, x in enumerate(level):
214215
bins[i % modulus].append(x)
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
from io import StringIO
2+
3+
import pytest
4+
5+
from globus_cli.termio import Field
6+
from globus_cli.termio.printers import FoldedTablePrinter
7+
from globus_cli.termio.printers.folded_table_printer import Row
8+
9+
10+
@pytest.mark.parametrize(
11+
"folding_enabled, width",
12+
(
13+
(False, 10),
14+
(False, 1000),
15+
(True, 1000),
16+
),
17+
)
18+
def test_folded_table_printer_can_print_unfolded_output(folding_enabled, width):
19+
fields = (
20+
Field("Column A", "a"),
21+
Field("Column B", "b"),
22+
Field("Column C", "c"),
23+
)
24+
data = (
25+
{"a": 1, "b": 4, "c": 7},
26+
{"a": 2, "b": 5, "c": 8},
27+
{"a": 3, "b": 6, "c": 9},
28+
)
29+
printer = FoldedTablePrinter(fields=fields, width=width)
30+
# override detection, set by test
31+
printer._folding_enabled = folding_enabled
32+
33+
with StringIO() as stream:
34+
printer.echo(data, stream)
35+
printed_table = stream.getvalue()
36+
37+
# fmt: off
38+
assert printed_table == (
39+
"Column A | Column B | Column C\n"
40+
"---------+----------+---------\n"
41+
"1 | 4 | 7 \n"
42+
"2 | 5 | 8 \n"
43+
"3 | 6 | 9 \n"
44+
)
45+
# fmt: on
46+
47+
48+
def test_folded_table_printer_can_fold_in_half():
49+
fields = (
50+
Field("Column A", "a"),
51+
Field("Column B", "b"),
52+
Field("Column C", "c"),
53+
Field("Column D", "d"),
54+
)
55+
data = (
56+
{"a": 1, "b": 4, "c": 7, "d": "alpha"},
57+
{"a": 2, "b": 5, "c": 8, "d": "beta"},
58+
{"a": 3, "b": 6, "c": 9, "d": "gamma"},
59+
)
60+
61+
printer = FoldedTablePrinter(fields=fields, width=25)
62+
# override detection of an interactive session
63+
printer._folding_enabled = True
64+
65+
with StringIO() as stream:
66+
printer.echo(data, stream)
67+
printed_table = stream.getvalue()
68+
69+
# fmt: off
70+
assert printed_table == (
71+
".----------+----------.\n"
72+
"| Column A | Column C |\n"
73+
"| Column B | Column D |\n"
74+
"+==========+==========+\n"
75+
"| 1 | 7 |\n"
76+
"| 4 | alpha |\n"
77+
"+----------+----------+\n"
78+
"| 2 | 8 |\n"
79+
"| 5 | beta |\n"
80+
"+----------+----------+\n"
81+
"| 3 | 9 |\n"
82+
"| 6 | gamma |\n"
83+
"'----------+----------'\n"
84+
)
85+
# fmt: on
86+
87+
88+
def test_folded_table_printer_can_fold_in_half_unevenly():
89+
fields = (
90+
Field("Column A", "a"),
91+
Field("Column B", "b"),
92+
Field("Column C", "c"),
93+
)
94+
data = (
95+
{"a": 1, "b": 4, "c": 7, "d": "alpha"},
96+
{"a": 2, "b": 5, "c": 8, "d": "beta"},
97+
{"a": 3, "b": 6, "c": 9, "d": "gamma"},
98+
)
99+
100+
printer = FoldedTablePrinter(fields=fields, width=25)
101+
# override detection of an interactive session
102+
printer._folding_enabled = True
103+
104+
with StringIO() as stream:
105+
printer.echo(data, stream)
106+
printed_table = stream.getvalue()
107+
108+
# fmt: off
109+
assert printed_table == (
110+
".----------+----------.\n"
111+
"| Column A | Column C |\n"
112+
"| Column B | |\n"
113+
"+==========+==========+\n"
114+
"| 1 | 7 |\n"
115+
"| 4 | |\n"
116+
"+----------+----------+\n"
117+
"| 2 | 8 |\n"
118+
"| 5 | |\n"
119+
"+----------+----------+\n"
120+
"| 3 | 9 |\n"
121+
"| 6 | |\n"
122+
"'----------+----------'\n"
123+
)
124+
# fmt: on
125+
126+
127+
def test_row_folding_no_remainder():
128+
six_items = Row((("1", "2", "3", "4", "5", "6"),))
129+
130+
# fold by 2 or "in half"
131+
fold2 = six_items.fold(2)
132+
assert len(fold2.grid) == 2
133+
assert fold2.grid == (
134+
("1", "3", "5"), # odds
135+
("2", "4", "6"), # evens
136+
)
137+
138+
# fold by 3 or "in thirds"
139+
fold3 = six_items.fold(3)
140+
assert len(fold3.grid) == 3
141+
assert fold3.grid == (
142+
("1", "4"),
143+
("2", "5"),
144+
("3", "6"),
145+
)
146+
147+
# fold by N where N is the number of columns
148+
fold6 = six_items.fold(6)
149+
assert len(fold6.grid) == 6
150+
assert fold6.grid == (
151+
("1",),
152+
("2",),
153+
("3",),
154+
("4",),
155+
("5",),
156+
("6",),
157+
)
158+
159+
160+
def test_row_folding_with_remainder():
161+
five_items = Row(
162+
(
163+
(
164+
"1",
165+
"2",
166+
"3",
167+
"4",
168+
"5",
169+
),
170+
)
171+
)
172+
173+
# fold by 2 or "in half"
174+
fold2 = five_items.fold(2)
175+
assert len(fold2.grid) == 2
176+
assert fold2.grid == (
177+
("1", "3", "5"), # odds
178+
(
179+
"2",
180+
"4",
181+
), # evens
182+
)
183+
184+
# fold by 3 or "in thirds"
185+
fold3 = five_items.fold(3)
186+
assert len(fold3.grid) == 3
187+
assert fold3.grid == (
188+
("1", "4"),
189+
("2", "5"),
190+
("3",),
191+
)

0 commit comments

Comments
 (0)