Skip to content

Commit 6deaba8

Browse files
jacalataclaude
andcommitted
Add failing tests for Worksheet class bugs
Tests cover: fields property returning method object instead of list, missing None checks on rows/cols/datasources elements causing crashes, and incomplete collection of datasource-dependencies columns. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 5606fcc commit 6deaba8

3 files changed

Lines changed: 120 additions & 1 deletion

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version='1.0' encoding='utf-8' ?>
2+
<workbook source-build='9.3.3 (9300.16.0603.2240)' source-platform='win' version='9.3' xmlns:user='http://www.tableausoftware.com/xml/user'>
3+
<datasources>
4+
<datasource caption='TestData' inline='true' name='federated.abc123' version='9.3'>
5+
<connection class='federated'>
6+
<named-connections />
7+
</connection>
8+
</datasource>
9+
</datasources>
10+
<worksheets>
11+
<worksheet name='Sheet 1'>
12+
<table>
13+
<view>
14+
</view>
15+
<rows></rows>
16+
<cols></cols>
17+
</table>
18+
</worksheet>
19+
</worksheets>
20+
</workbook>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version='1.0' encoding='utf-8' ?>
2+
<workbook source-build='9.3.3 (9300.16.0603.2240)' source-platform='win' version='9.3' xmlns:user='http://www.tableausoftware.com/xml/user'>
3+
<datasources>
4+
<datasource caption='TestData' inline='true' name='federated.abc123' version='9.3'>
5+
<connection class='federated'>
6+
<named-connections />
7+
</connection>
8+
</datasource>
9+
</datasources>
10+
<worksheets>
11+
<worksheet name='Sheet 1'>
12+
<table>
13+
<view>
14+
<datasources>
15+
<datasource caption='TestData' name='federated.abc123' />
16+
</datasources>
17+
</view>
18+
</table>
19+
</worksheet>
20+
</worksheets>
21+
</workbook>

test/test_workbook.py

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@
2222
'filtering.twb'
2323
)
2424

25+
WORKSHEET_NO_DATASOURCES_FILE = os.path.join(
26+
TEST_ASSET_DIR,
27+
'worksheet_no_datasources.twb'
28+
)
29+
30+
WORKSHEET_NO_SHELVES_FILE = os.path.join(
31+
TEST_ASSET_DIR,
32+
'worksheet_no_shelves.twb'
33+
)
34+
2535

2636
class EphemeralFields(unittest.TestCase):
2737
def test_ephemeral_fields_do_not_cause_errors(self):
@@ -56,4 +66,72 @@ def test_worksheets_setup(self):
5666
self.assertEqual(len(wb.worksheet_items), 2)
5767
worksheet_names = [ws.name for ws in wb.worksheet_items]
5868
worksheet_names.sort()
59-
self.assertEqual(worksheet_names[0], 'Sheet 1')
69+
self.assertEqual(worksheet_names[0], 'Sheet 1')
70+
71+
def test_worksheet_fields_returns_list_not_method(self):
72+
# fields property was returning the method object instead of self._fields
73+
wb = Workbook(DASHBOARDS_FILE)
74+
ws = wb.worksheet_items[0]
75+
fields = ws.fields
76+
self.assertIsInstance(fields, list, "fields should return a list, not a method object")
77+
78+
def test_worksheet_fields_are_field_objects(self):
79+
wb = Workbook(DASHBOARDS_FILE)
80+
ws = wb.worksheet_items[0]
81+
from tableaudocumentapi import Field
82+
for f in ws.fields:
83+
self.assertIsInstance(f, Field)
84+
85+
def test_worksheet_datasources_not_empty(self):
86+
wb = Workbook(DASHBOARDS_FILE)
87+
ws = wb.worksheet_items[0]
88+
self.assertGreater(len(ws.datasources), 0)
89+
90+
def test_worksheet_rows_returns_list(self):
91+
wb = Workbook(DASHBOARDS_FILE)
92+
ws = wb.worksheet_items[0]
93+
# Sheet 1 has two fields on rows shelf
94+
self.assertIsNotNone(ws.rows)
95+
self.assertIsInstance(ws.rows, list)
96+
self.assertEqual(len(ws.rows), 2)
97+
98+
def test_worksheet_cols_empty_shelf_returns_none_or_list(self):
99+
# Sheet 1 has an empty <cols /> element - should not crash
100+
wb = Workbook(DASHBOARDS_FILE)
101+
ws = wb.worksheet_items[0]
102+
result = ws.cols
103+
self.assertTrue(result is None or isinstance(result, list))
104+
105+
def test_worksheet_rows_field_names(self):
106+
wb = Workbook(DASHBOARDS_FILE)
107+
ws = wb.worksheet_items[0]
108+
# Sheet 1 rows: Calculation_88946136969252864 (caption: SHOW) and "Burst Out Set list"
109+
# _ds_fields_to_items resolves via datasource.fields, so we get Field objects or raw strings
110+
from tableaudocumentapi import Field
111+
names = [f.caption if isinstance(f, Field) else f for f in ws.rows]
112+
self.assertIn('SHOW', names) # Calculation resolved to its caption
113+
114+
def test_all_datasource_dependencies_collected(self):
115+
# return inside loop meant only first datasource-dependencies block was processed
116+
wb = Workbook(DASHBOARDS_FILE)
117+
ws = wb.worksheet_items[0]
118+
# Sheet 1 has 3 columns in its datasource-dependencies block
119+
self.assertEqual(len(ws.fields), 3)
120+
121+
def test_worksheet_with_no_datasources_element_does_not_crash(self):
122+
# _prepare_datasources iterates the result of find(), which is None if element absent
123+
wb = Workbook(WORKSHEET_NO_DATASOURCES_FILE)
124+
ws = wb.worksheet_items[0]
125+
self.assertEqual(ws.datasources, [])
126+
127+
def test_worksheet_with_no_rows_element_does_not_crash(self):
128+
# _prepare_rows calls .text on find() result without None check
129+
wb = Workbook(WORKSHEET_NO_SHELVES_FILE)
130+
ws = wb.worksheet_items[0]
131+
self.assertIsNone(ws.rows)
132+
133+
def test_worksheet_with_no_cols_element_does_not_crash(self):
134+
# _prepare_cols calls .text on find() result without None check
135+
wb = Workbook(WORKSHEET_NO_SHELVES_FILE)
136+
ws = wb.worksheet_items[0]
137+
self.assertIsNone(ws.cols)

0 commit comments

Comments
 (0)