-
Josenilson Ferreira da SIlva authoredJosenilson Ferreira da SIlva authored
test_table_creator.py 34.85 KiB
# coding=utf-8
# flake8: noqa E501
"""
Unit testing for cmd2/table_creator.py module
"""
import pytest
from cmd2 import (
Bg,
Fg,
TextStyle,
ansi,
)
from cmd2.table_creator import (
AlternatingTable,
BorderedTable,
Column,
HorizontalAlignment,
SimpleTable,
TableCreator,
VerticalAlignment,
)
# Turn off black formatting for entire file so multiline strings
# can be visually aligned to match the tables being tested.
# fmt: off
def test_column_creation():
# Width less than 1
with pytest.raises(ValueError) as excinfo:
Column("Column 1", width=0)
assert "Column width cannot be less than 1" in str(excinfo.value)
# Width specified
c = Column("header", width=20)
assert c.width == 20
# max_data_lines less than 1
with pytest.raises(ValueError) as excinfo:
Column("Column 1", max_data_lines=0)
assert "Max data lines cannot be less than 1" in str(excinfo.value)
# No width specified, blank label
c = Column("")
assert c.width < 0
tc = TableCreator([c])
assert tc.cols[0].width == 1
# No width specified, label isn't blank but has no width
c = Column(ansi.style('', fg=Fg.GREEN))
assert c.width < 0
tc = TableCreator([c])
assert tc.cols[0].width == 1
# No width specified, label has width
c = Column("a line")
assert c.width < 0
tc = TableCreator([c])
assert tc.cols[0].width == ansi.style_aware_wcswidth("a line")
# No width specified, label has width and multiple lines
c = Column("short\nreally long")
assert c.width < 0
tc = TableCreator([c])
assert tc.cols[0].width == ansi.style_aware_wcswidth("really long")
# No width specified, label has tabs
c = Column("line\twith\ttabs")
assert c.width < 0
tc = TableCreator([c])
assert tc.cols[0].width == ansi.style_aware_wcswidth("line with tabs")
# Add basic tests for style_header_text and style_data_text to make sure these members don't get removed.
c = Column("Column 1")
assert c.style_header_text is True
assert c.style_data_text is True
c = Column("Column 1", style_header_text=False)
assert c.style_header_text is False
assert c.style_data_text is True
c = Column("Column 1", style_data_text=False)
assert c.style_header_text is True
assert c.style_data_text is False
def test_column_alignment():
column_1 = Column(
"Col 1",
width=10,
header_horiz_align=HorizontalAlignment.LEFT,
header_vert_align=VerticalAlignment.TOP,
data_horiz_align=HorizontalAlignment.RIGHT,
data_vert_align=VerticalAlignment.BOTTOM,
)
column_2 = Column(
"Col 2",
width=10,
header_horiz_align=HorizontalAlignment.RIGHT,
header_vert_align=VerticalAlignment.BOTTOM,
data_horiz_align=HorizontalAlignment.CENTER,
data_vert_align=VerticalAlignment.MIDDLE,
)
column_3 = Column(
"Col 3",
width=10,
header_horiz_align=HorizontalAlignment.CENTER,
header_vert_align=VerticalAlignment.MIDDLE,
data_horiz_align=HorizontalAlignment.LEFT,
data_vert_align=VerticalAlignment.TOP,
)
column_4 = Column("Three\nline\nheader", width=10)
columns = [column_1, column_2, column_3, column_4]
tc = TableCreator(columns)
# Check defaults
assert column_4.header_horiz_align == HorizontalAlignment.LEFT
assert column_4.header_vert_align == VerticalAlignment.BOTTOM
assert column_4.data_horiz_align == HorizontalAlignment.LEFT
assert column_4.data_vert_align == VerticalAlignment.TOP
# Create a header row
row_data = [col.header for col in columns]
header = tc.generate_row(row_data=row_data, is_header=True)
assert header == (
'Col 1 Three \n'
' Col 3 line \n'
' Col 2 header '
)
# Create a data row
row_data = ["Val 1", "Val 2", "Val 3", "Three\nline\ndata"]
row = tc.generate_row(row_data=row_data, is_header=False)
assert row == (
' Val 3 Three \n'
' Val 2 line \n'
' Val 1 data '
)
def test_blank_last_line():
"""This tests that an empty line is inserted when the last data line is blank"""
column_1 = Column("Col 1", width=10)
tc = TableCreator([column_1])
row_data = ['my line\n\n']
row = tc.generate_row(row_data=row_data, is_header=False)
assert row == ('my line \n'
' ')
row_data = ['\n']
row = tc.generate_row(row_data=row_data, is_header=False)
assert row == ' '
row_data = ['']
row = tc.generate_row(row_data=row_data, is_header=False)
assert row == ' '
def test_wrap_text():
column_1 = Column("Col 1", width=10)
tc = TableCreator([column_1])
# Test normal wrapping
row_data = ['Some text to wrap\nA new line that will wrap\nNot wrap\n 1 2 3']
row = tc.generate_row(row_data=row_data, is_header=False)
assert row == ('Some text \n'
'to wrap \n'
'A new line\n'
'that will \n'
'wrap \n'
'Not wrap \n'
' 1 2 3 ')
# Test preserving a multiple space sequence across a line break
row_data = ['First last one']
row = tc.generate_row(row_data=row_data, is_header=False)
assert row == ('First \n'
' last one ')
def test_wrap_text_max_lines():
column_1 = Column("Col 1", width=10, max_data_lines=2)
tc = TableCreator([column_1])
# Test not needing to truncate the final line
row_data = ['First line last line']
row = tc.generate_row(row_data=row_data, is_header=False)
assert row == ('First line\n'
'last line ')
# Test having to truncate the last word because it's too long for the final line
row_data = ['First line last lineextratext']
row = tc.generate_row(row_data=row_data, is_header=False)
assert row == ('First line\n'
'last line…')
# Test having to truncate the last word because it fits the final line but there is more text not being included
row_data = ['First line thistxtfit extra']
row = tc.generate_row(row_data=row_data, is_header=False)
assert row == ('First line\n'
'thistxtfi…')
# Test having to truncate the last word because it fits the final line but there are more lines not being included
row_data = ['First line thistxtfit\nextra']
row = tc.generate_row(row_data=row_data, is_header=False)
assert row == ('First line\n'
'thistxtfi…')
# Test having space left on the final line and adding an ellipsis because there are more lines not being included
row_data = ['First line last line\nextra line']
row = tc.generate_row(row_data=row_data, is_header=False)
assert row == ('First line\n'
'last line…')
def test_wrap_long_word():
# Make sure words wider than column start on own line and wrap
column_1 = Column("LongColumnName", width=10)
column_2 = Column("Col 2", width=10)
columns = [column_1, column_2]
tc = TableCreator(columns)
# Test header row
row_data = [col.header for col in columns]
header = tc.generate_row(row_data, is_header=True)
assert header == ('LongColumn \n'
'Name Col 2 ')
# Test data row
row_data = list()
# Long word should start on the first line (style should not affect width)
row_data.append(ansi.style("LongerThan10", fg=Fg.GREEN))
# Long word should start on the second line
row_data.append("Word LongerThan10")
row = tc.generate_row(row_data=row_data, is_header=False)
expected = (
TextStyle.RESET_ALL
+ Fg.GREEN
+ "LongerThan"
+ TextStyle.RESET_ALL
+ " Word \n"
+ TextStyle.RESET_ALL
+ Fg.GREEN
+ "10"
+ Fg.RESET
+ TextStyle.RESET_ALL
+ ' '
+ TextStyle.RESET_ALL
+ ' LongerThan\n'
' 10 '
)
assert row == expected
def test_wrap_long_word_max_data_lines():
column_1 = Column("Col 1", width=10, max_data_lines=2)
column_2 = Column("Col 2", width=10, max_data_lines=2)
column_3 = Column("Col 3", width=10, max_data_lines=2)
column_4 = Column("Col 4", width=10, max_data_lines=1)
columns = [column_1, column_2, column_3, column_4]
tc = TableCreator(columns)
row_data = list()
# This long word will exactly fit the last line and it's the final word in the text. No ellipsis should appear.
row_data.append("LongerThan10FitsLast")
# This long word will exactly fit the last line but it's not the final word in the text.
# Make sure ellipsis word's final character.
row_data.append("LongerThan10FitsLast\nMore lines")
# This long word will run over the last line. Make sure it is truncated.
row_data.append("LongerThan10RunsOverLast")
# This long word will start on the final line after another word. Therefore it won't wrap but will instead be truncated.
row_data.append("A LongerThan10RunsOverLast")
row = tc.generate_row(row_data=row_data, is_header=False)
assert row == ('LongerThan LongerThan LongerThan A LongerT…\n'
'10FitsLast 10FitsLas… 10RunsOve… ')
def test_wrap_long_char_wider_than_max_width():
"""
This tests case where a character is wider than max_width. This can happen if max_width
is 1 and the text contains wide characters (e.g. East Asian). Replace it with an ellipsis.
"""
column_1 = Column("Col 1", width=1)
tc = TableCreator([column_1])
row = tc.generate_row(row_data=['深'], is_header=False)
assert row == '…'
def test_generate_row_exceptions():
column_1 = Column("Col 1")
tc = TableCreator([column_1])
row_data = ['fake']
# fill_char too long
with pytest.raises(TypeError) as excinfo:
tc.generate_row(row_data=row_data, is_header=False, fill_char='too long')
assert "Fill character must be exactly one character long" in str(excinfo.value)
# Unprintable characters
for arg in ['fill_char', 'pre_line', 'inter_cell', 'post_line']:
kwargs = {arg: '\n'}
with pytest.raises(ValueError) as excinfo:
tc.generate_row(row_data=row_data, is_header=False, **kwargs)
assert "{} contains an unprintable character".format(arg) in str(excinfo.value)
# Data with too many columns
row_data = ['Data 1', 'Extra Column']
with pytest.raises(ValueError) as excinfo:
tc.generate_row(row_data=row_data, is_header=False)
assert "Length of row_data must match length of cols" in str(excinfo.value)
def test_tabs():
column_1 = Column("Col\t1", width=20)
column_2 = Column("Col 2")
columns = [column_1, column_2]
tc = TableCreator(columns, tab_width=2)
row_data = [col.header for col in columns]
row = tc.generate_row(row_data, is_header=True, fill_char='\t', pre_line='\t', inter_cell='\t', post_line='\t')
assert row == ' Col 1 Col 2 '
with pytest.raises(ValueError) as excinfo:
TableCreator([column_1, column_2], tab_width=0)
assert "Tab width cannot be less than 1" in str(excinfo.value)
def test_simple_table_creation():
column_1 = Column("Col 1", width=16)
column_2 = Column("Col 2", width=16)
row_data = list()
row_data.append(["Col 1 Row 1", "Col 2 Row 1"])
row_data.append(["Col 1 Row 2", "Col 2 Row 2"])
# Default options
st = SimpleTable([column_1, column_2])
table = st.generate_table(row_data)
assert table == (
'Col 1 Col 2 \n'
'----------------------------------\n'
'Col 1 Row 1 Col 2 Row 1 \n'
' \n'
'Col 1 Row 2 Col 2 Row 2 '
)
# Custom column spacing
st = SimpleTable([column_1, column_2], column_spacing=5)
table = st.generate_table(row_data)
assert table == (
'Col 1 Col 2 \n'
'-------------------------------------\n'
'Col 1 Row 1 Col 2 Row 1 \n'
' \n'
'Col 1 Row 2 Col 2 Row 2 '
)
# Custom divider
st = SimpleTable([column_1, column_2], divider_char='─')
table = st.generate_table(row_data)
assert table == (
'Col 1 Col 2 \n'
'──────────────────────────────────\n'
'Col 1 Row 1 Col 2 Row 1 \n'
' \n'
'Col 1 Row 2 Col 2 Row 2 '
)
# No divider
st = SimpleTable([column_1, column_2], divider_char=None)
no_divider_1 = st.generate_table(row_data)
st = SimpleTable([column_1, column_2], divider_char='')
no_divider_2 = st.generate_table(row_data)
assert no_divider_1 == no_divider_2 == (
'Col 1 Col 2 \n'
'Col 1 Row 1 Col 2 Row 1 \n'
' \n'
'Col 1 Row 2 Col 2 Row 2 '
)
# No row spacing
st = SimpleTable([column_1, column_2])
table = st.generate_table(row_data, row_spacing=0)
assert table == (
'Col 1 Col 2 \n'
'----------------------------------\n'
'Col 1 Row 1 Col 2 Row 1 \n'
'Col 1 Row 2 Col 2 Row 2 '
)
# No header
st = SimpleTable([column_1, column_2])
table = st.generate_table(row_data, include_header=False)
assert table == ('Col 1 Row 1 Col 2 Row 1 \n'
' \n'
'Col 1 Row 2 Col 2 Row 2 ')
# Wide custom divider (divider needs no padding)
st = SimpleTable([column_1, column_2], divider_char='深')
table = st.generate_table(row_data)
assert table == (
'Col 1 Col 2 \n'
'深深深深深深深深深深深深深深深深深\n'
'Col 1 Row 1 Col 2 Row 1 \n'
' \n'
'Col 1 Row 2 Col 2 Row 2 '
)
# Wide custom divider (divider needs padding)
st = SimpleTable([column_1, Column("Col 2", width=17)],
divider_char='深')
table = st.generate_table(row_data)
assert table == (
'Col 1 Col 2 \n'
'深深深深深深深深深深深深深深深深深 \n'
'Col 1 Row 1 Col 2 Row 1 \n'
' \n'
'Col 1 Row 2 Col 2 Row 2 '
)
# Invalid column spacing
with pytest.raises(ValueError) as excinfo:
SimpleTable([column_1, column_2], column_spacing=-1)
assert "Column spacing cannot be less than 0" in str(excinfo.value)
# Invalid divider character
with pytest.raises(TypeError) as excinfo:
SimpleTable([column_1, column_2], divider_char='too long')
assert "Divider character must be exactly one character long" in str(excinfo.value)
with pytest.raises(ValueError) as excinfo:
SimpleTable([column_1, column_2], divider_char='\n')
assert "Divider character is an unprintable character" in str(excinfo.value)
# Invalid row spacing
st = SimpleTable([column_1, column_2])
with pytest.raises(ValueError) as excinfo:
st.generate_table(row_data, row_spacing=-1)
assert "Row spacing cannot be less than 0" in str(excinfo.value)
# Test header and data colors
st = SimpleTable([column_1, column_2], divider_char=None, header_bg=Bg.GREEN, data_bg=Bg.LIGHT_BLUE)
table = st.generate_table(row_data)
assert table == (
'\x1b[0m\x1b[42mCol 1\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 2\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[0m\n'
'\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 2 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\n'
'\x1b[0m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\n'
'\x1b[0m\x1b[104mCol 1 Row 2\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 2 Row 2\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m'
)
# Make sure SimpleTable respects style_header_text and style_data_text flags.
# Don't apply parent table's background colors to header or data text in second column.
st = SimpleTable([column_1, Column("Col 2", width=16, style_header_text=False, style_data_text=False)],
divider_char=None, header_bg=Bg.GREEN, data_bg=Bg.LIGHT_BLUE)
table = st.generate_table(row_data)
assert table == (
'\x1b[0m\x1b[42mCol 1\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[0mCol 2\x1b[0m\x1b[42m \x1b[49m\x1b[0m\n'
'\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0mCol 2 Row 1\x1b[0m\x1b[104m \x1b[49m\x1b[0m\n'
'\x1b[0m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\n'
'\x1b[0m\x1b[104mCol 1 Row 2\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0mCol 2 Row 2\x1b[0m\x1b[104m \x1b[49m\x1b[0m'
)
def test_simple_table_width():
# Base width
for num_cols in range(1, 10):
assert SimpleTable.base_width(num_cols) == (num_cols - 1) * 2
# Invalid num_cols value
with pytest.raises(ValueError) as excinfo:
SimpleTable.base_width(0)
assert "Column count cannot be less than 1" in str(excinfo.value)
# Total width
column_1 = Column("Col 1", width=16)
column_2 = Column("Col 2", width=16)
row_data = list()
row_data.append(["Col 1 Row 1", "Col 2 Row 1"])
row_data.append(["Col 1 Row 2", "Col 2 Row 2"])
st = SimpleTable([column_1, column_2])
assert st.total_width() == 34
def test_simple_generate_data_row_exceptions():
column_1 = Column("Col 1")
tc = SimpleTable([column_1])
# Data with too many columns
row_data = ['Data 1', 'Extra Column']
with pytest.raises(ValueError) as excinfo:
tc.generate_data_row(row_data=row_data)
assert "Length of row_data must match length of cols" in str(excinfo.value)
def test_bordered_table_creation():
column_1 = Column("Col 1", width=15)
column_2 = Column("Col 2", width=15)
row_data = list()
row_data.append(["Col 1 Row 1", "Col 2 Row 1"])
row_data.append(["Col 1 Row 2", "Col 2 Row 2"])
# Default options
bt = BorderedTable([column_1, column_2])
table = bt.generate_table(row_data)
assert table == (
'╔═════════════════╤═════════════════╗\n'
'║ Col 1 │ Col 2 ║\n'
'╠═════════════════╪═════════════════╣\n'
'║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
'╟─────────────────┼─────────────────╢\n'
'║ Col 1 Row 2 │ Col 2 Row 2 ║\n'
'╚═════════════════╧═════════════════╝'
)
# No column borders
bt = BorderedTable([column_1, column_2], column_borders=False)
table = bt.generate_table(row_data)
assert table == (
'╔══════════════════════════════════╗\n'
'║ Col 1 Col 2 ║\n'
'╠══════════════════════════════════╣\n'
'║ Col 1 Row 1 Col 2 Row 1 ║\n'
'╟──────────────────────────────────╢\n'
'║ Col 1 Row 2 Col 2 Row 2 ║\n'
'╚══════════════════════════════════╝'
)
# No header
bt = BorderedTable([column_1, column_2])
table = bt.generate_table(row_data, include_header=False)
assert table == (
'╔═════════════════╤═════════════════╗\n'
'║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
'╟─────────────────┼─────────────────╢\n'
'║ Col 1 Row 2 │ Col 2 Row 2 ║\n'
'╚═════════════════╧═════════════════╝'
)
# Non-default padding
bt = BorderedTable([column_1, column_2], padding=2)
table = bt.generate_table(row_data)
assert table == (
'╔═══════════════════╤═══════════════════╗\n'
'║ Col 1 │ Col 2 ║\n'
'╠═══════════════════╪═══════════════════╣\n'
'║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
'╟───────────────────┼───────────────────╢\n'
'║ Col 1 Row 2 │ Col 2 Row 2 ║\n'
'╚═══════════════════╧═══════════════════╝'
)
# Invalid padding
with pytest.raises(ValueError) as excinfo:
BorderedTable([column_1, column_2], padding=-1)
assert "Padding cannot be less than 0" in str(excinfo.value)
# Test border, header, and data colors
bt = BorderedTable([column_1, column_2], border_fg=Fg.LIGHT_YELLOW, border_bg=Bg.WHITE,
header_bg=Bg.GREEN, data_bg=Bg.LIGHT_BLUE)
table = bt.generate_table(row_data)
assert table == (
'\x1b[93m\x1b[107m╔═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╤═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╗\x1b[39m\x1b[49m\n'
'\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 1\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[93m\x1b[107m│\x1b[39m\x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 2\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\n'
'\x1b[93m\x1b[107m╠═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╪═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╣\x1b[39m\x1b[49m\n'
'\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m\x1b[107m│\x1b[39m\x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 2 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\n'
'\x1b[93m\x1b[107m╟─\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m───────────────\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m─┼─\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m───────────────\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m─╢\x1b[39m\x1b[49m\n'
'\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 2\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m\x1b[107m│\x1b[39m\x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 2 Row 2\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\n'
'\x1b[93m\x1b[107m╚═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╧═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╝\x1b[39m\x1b[49m'
)
# Make sure BorderedTable respects style_header_text and style_data_text flags.
# Don't apply parent table's background colors to header or data text in second column.
bt = BorderedTable([column_1, Column("Col 2", width=15, style_header_text=False, style_data_text=False)],
header_bg=Bg.GREEN, data_bg=Bg.LIGHT_BLUE)
table = bt.generate_table(row_data)
assert table == (
'╔═════════════════╤═════════════════╗\n'
'║\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 1\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[0m\x1b[42m \x1b[49m│\x1b[42m \x1b[49m\x1b[0mCol 2\x1b[0m\x1b[42m \x1b[49m\x1b[0m\x1b[42m \x1b[49m║\n'
'╠═════════════════╪═════════════════╣\n'
'║\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m│\x1b[104m \x1b[49m\x1b[0mCol 2 Row 1\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m║\n'
'╟─────────────────┼─────────────────╢\n'
'║\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 2\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m│\x1b[104m \x1b[49m\x1b[0mCol 2 Row 2\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m║\n'
'╚═════════════════╧═════════════════╝'
)
def test_bordered_table_width():
# Default behavior (column_borders=True, padding=1)
assert BorderedTable.base_width(1) == 4
assert BorderedTable.base_width(2) == 7
assert BorderedTable.base_width(3) == 10
# No column borders
assert BorderedTable.base_width(1, column_borders=False) == 4
assert BorderedTable.base_width(2, column_borders=False) == 6
assert BorderedTable.base_width(3, column_borders=False) == 8
# No padding
assert BorderedTable.base_width(1, padding=0) == 2
assert BorderedTable.base_width(2, padding=0) == 3
assert BorderedTable.base_width(3, padding=0) == 4
# Extra padding
assert BorderedTable.base_width(1, padding=3) == 8
assert BorderedTable.base_width(2, padding=3) == 15
assert BorderedTable.base_width(3, padding=3) == 22
# Invalid num_cols value
with pytest.raises(ValueError) as excinfo:
BorderedTable.base_width(0)
assert "Column count cannot be less than 1" in str(excinfo.value)
# Total width
column_1 = Column("Col 1", width=15)
column_2 = Column("Col 2", width=15)
row_data = list()
row_data.append(["Col 1 Row 1", "Col 2 Row 1"])
row_data.append(["Col 1 Row 2", "Col 2 Row 2"])
bt = BorderedTable([column_1, column_2])
assert bt.total_width() == 37
def test_bordered_generate_data_row_exceptions():
column_1 = Column("Col 1")
tc = BorderedTable([column_1])
# Data with too many columns
row_data = ['Data 1', 'Extra Column']
with pytest.raises(ValueError) as excinfo:
tc.generate_data_row(row_data=row_data)
assert "Length of row_data must match length of cols" in str(excinfo.value)
def test_alternating_table_creation():
column_1 = Column("Col 1", width=15)
column_2 = Column("Col 2", width=15)
row_data = list()
row_data.append(["Col 1 Row 1", "Col 2 Row 1"])
row_data.append(["Col 1 Row 2", "Col 2 Row 2"])
# Default options
at = AlternatingTable([column_1, column_2])
table = at.generate_table(row_data)
assert table == (
'╔═════════════════╤═════════════════╗\n'
'║ Col 1 │ Col 2 ║\n'
'╠═════════════════╪═════════════════╣\n'
'║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
'║\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m│\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m║\n'
'╚═════════════════╧═════════════════╝'
)
# No column borders
at = AlternatingTable([column_1, column_2], column_borders=False)
table = at.generate_table(row_data)
assert table == (
'╔══════════════════════════════════╗\n'
'║ Col 1 Col 2 ║\n'
'╠══════════════════════════════════╣\n'
'║ Col 1 Row 1 Col 2 Row 1 ║\n'
'║\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m║\n'
'╚══════════════════════════════════╝'
)
# No header
at = AlternatingTable([column_1, column_2])
table = at.generate_table(row_data, include_header=False)
assert table == (
'╔═════════════════╤═════════════════╗\n'
'║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
'║\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m│\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m║\n'
'╚═════════════════╧═════════════════╝'
)
# Non-default padding
at = AlternatingTable([column_1, column_2], padding=2)
table = at.generate_table(row_data)
assert table == (
'╔═══════════════════╤═══════════════════╗\n'
'║ Col 1 │ Col 2 ║\n'
'╠═══════════════════╪═══════════════════╣\n'
'║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
'║\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m│\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m║\n'
'╚═══════════════════╧═══════════════════╝'
)
# Invalid padding
with pytest.raises(ValueError) as excinfo:
AlternatingTable([column_1, column_2], padding=-1)
assert "Padding cannot be less than 0" in str(excinfo.value)
# Test border, header, and data colors
at = AlternatingTable([column_1, column_2], border_fg=Fg.LIGHT_YELLOW, border_bg=Bg.WHITE,
header_bg=Bg.GREEN, odd_bg=Bg.LIGHT_BLUE, even_bg=Bg.LIGHT_RED)
table = at.generate_table(row_data)
assert table == (
'\x1b[93m\x1b[107m╔═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╤═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╗\x1b[39m\x1b[49m\n'
'\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 1\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[93m\x1b[107m│\x1b[39m\x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 2\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\n'
'\x1b[93m\x1b[107m╠═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╪═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╣\x1b[39m\x1b[49m\n'
'\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m\x1b[107m│\x1b[39m\x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 2 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\n'
'\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\x1b[101m \x1b[49m\x1b[0m\x1b[101mCol 1 Row 2\x1b[49m\x1b[0m\x1b[101m \x1b[49m\x1b[0m\x1b[101m \x1b[49m\x1b[93m\x1b[107m│\x1b[39m\x1b[49m\x1b[101m \x1b[49m\x1b[0m\x1b[101mCol 2 Row 2\x1b[49m\x1b[0m\x1b[101m \x1b[49m\x1b[0m\x1b[101m \x1b[49m\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\n'
'\x1b[93m\x1b[107m╚═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╧═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╝\x1b[39m\x1b[49m'
)
# Make sure AlternatingTable respects style_header_text and style_data_text flags.
# Don't apply parent table's background colors to header or data text in second column.
at = AlternatingTable([column_1, Column("Col 2", width=15, style_header_text=False, style_data_text=False)],
header_bg=Bg.GREEN, odd_bg=Bg.LIGHT_BLUE, even_bg=Bg.LIGHT_RED)
table = at.generate_table(row_data)
assert table == (
'╔═════════════════╤═════════════════╗\n'
'║\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 1\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[0m\x1b[42m \x1b[49m│\x1b[42m \x1b[49m\x1b[0mCol 2\x1b[0m\x1b[42m \x1b[49m\x1b[0m\x1b[42m \x1b[49m║\n'
'╠═════════════════╪═════════════════╣\n'
'║\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m│\x1b[104m \x1b[49m\x1b[0mCol 2 Row 1\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m║\n'
'║\x1b[101m \x1b[49m\x1b[0m\x1b[101mCol 1 Row 2\x1b[49m\x1b[0m\x1b[101m \x1b[49m\x1b[0m\x1b[101m \x1b[49m│\x1b[101m \x1b[49m\x1b[0mCol 2 Row 2\x1b[0m\x1b[101m \x1b[49m\x1b[0m\x1b[101m \x1b[49m║\n'
'╚═════════════════╧═════════════════╝'
)