mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
fa9e6eee88
Summary: This is the first step towards raw data views, merely adding metadata without any UI. Every 'normal' table now has a widget referenced by `rawViewSectionRef`. It has no parent view/page and cannot actually be viewed for now. The widget is created during the AddTable user action, and the migration creates a widget for existing tables. Test Plan: Many tests had to be updated, especially tests that listed all view sections and/or fields. Reviewers: jarek, dsagal Reviewed By: jarek Differential Revision: https://phab.getgrist.com/D3232
256 lines
9.8 KiB
Python
256 lines
9.8 KiB
Python
import actions
|
|
import logger
|
|
|
|
import testsamples
|
|
import test_engine
|
|
from test_engine import Table, Column
|
|
|
|
log = logger.Logger(__name__, logger.INFO)
|
|
|
|
class TestDocModel(test_engine.EngineTestCase):
|
|
|
|
def test_meta_tables(self):
|
|
"""
|
|
Test changes to records accessed via lookup.
|
|
"""
|
|
self.load_sample(testsamples.sample_students)
|
|
self.assertPartialData("_grist_Tables", ["id", "columns"], [
|
|
[1, [1,2,4,5,6]],
|
|
[2, [10,12]],
|
|
[3, [21]],
|
|
])
|
|
|
|
# Test that adding a column produces a change to 'columns' without emitting an action.
|
|
out_actions = self.add_column('Students', 'test', type='Text', isFormula=False)
|
|
self.assertPartialData("_grist_Tables", ["id", "columns"], [
|
|
[1, [1,2,4,5,6,22]],
|
|
[2, [10,12]],
|
|
[3, [21]],
|
|
])
|
|
self.assertPartialOutActions(out_actions, {
|
|
"calc": [],
|
|
"stored": [
|
|
["AddColumn", "Students", "test",
|
|
{"formula": "", "isFormula": False, "type": "Text"}
|
|
],
|
|
["AddRecord", "_grist_Tables_column", 22,
|
|
{"colId": "test", "formula": "", "isFormula": False, "label": "test",
|
|
"parentId": 1, "parentPos": 6.0, "type": "Text", "widgetOptions": ""}
|
|
],
|
|
],
|
|
"undo": [
|
|
["RemoveColumn", "Students", "test"],
|
|
["RemoveRecord", "_grist_Tables_column", 22],
|
|
]
|
|
})
|
|
|
|
# Undo the AddColumn action. Check that actions are in correct order, and still produce undos.
|
|
out_actions = self.apply_user_action(
|
|
['ApplyUndoActions', [actions.get_action_repr(a) for a in out_actions.undo]])
|
|
self.assertPartialOutActions(out_actions, {
|
|
"calc": [],
|
|
"stored": [
|
|
["RemoveRecord", "_grist_Tables_column", 22],
|
|
["RemoveColumn", "Students", "test"],
|
|
],
|
|
"undo": [
|
|
["AddRecord", "_grist_Tables_column", 22, {"colId": "test", "label": "test",
|
|
"parentId": 1, "parentPos": 6.0, "type": "Text"}],
|
|
["AddColumn", "Students", "test", {"formula": "", "isFormula": False, "type": "Text"}],
|
|
]
|
|
})
|
|
|
|
# Test that when we add a table, .column is set correctly.
|
|
out_actions = self.apply_user_action(['AddTable', 'Test2', [
|
|
{'id': 'A', 'type': 'Text'},
|
|
{'id': 'B', 'type': 'Numeric'},
|
|
{'id': 'C', 'type': 'Numeric', 'formula': 'len($A)', 'isFormula': True}
|
|
]])
|
|
self.assertPartialData("_grist_Tables", ["id", "columns"], [
|
|
[1, [1,2,4,5,6]],
|
|
[2, [10,12]],
|
|
[3, [21]],
|
|
[4, [22,23,24,25]],
|
|
])
|
|
self.assertPartialData("_grist_Tables_column", ["id", "colId", "parentId"], [
|
|
[1, "firstName", 1],
|
|
[2, "lastName", 1],
|
|
[4, "schoolName", 1],
|
|
[5, "schoolIds", 1],
|
|
[6, "schoolCities", 1],
|
|
[10, "name", 2],
|
|
[12, "address", 2],
|
|
[21, "city", 3],
|
|
# Newly added columns:
|
|
[22, 'manualSort', 4],
|
|
[23, 'A', 4],
|
|
[24, 'B', 4],
|
|
[25, 'C', 4],
|
|
])
|
|
|
|
def test_add_column_position(self):
|
|
self.load_sample(testsamples.sample_students)
|
|
|
|
# Client may send AddColumn actions with fractional positions. Test that it works.
|
|
# TODO: this should probably use parentPos in the future and be done via metadata AddRecord.
|
|
out_actions = self.add_column('Students', 'test', type='Text', _position=2.75)
|
|
self.assertPartialData("_grist_Tables", ["id", "columns"], [
|
|
[1, [1,2,22,4,5,6]],
|
|
[2, [10,12]],
|
|
[3, [21]],
|
|
])
|
|
|
|
out_actions = self.add_column('Students', None, type='Text', _position=6)
|
|
self.assertPartialData("_grist_Tables", ["id", "columns"], [
|
|
[1, [1,2,22,4,5,6,23]],
|
|
[2, [10,12]],
|
|
[3, [21]],
|
|
])
|
|
self.assertPartialData("_grist_Tables_column", ["id", "colId", "parentId"], [
|
|
[1, "firstName", 1],
|
|
[2, "lastName", 1],
|
|
[4, "schoolName", 1],
|
|
[5, "schoolIds", 1],
|
|
[6, "schoolCities", 1],
|
|
[10, "name", 2],
|
|
[12, "address", 2],
|
|
[21, "city", 3],
|
|
[22, "test", 1],
|
|
[23, "A", 1],
|
|
])
|
|
|
|
def assertRecordSet(self, record_set, expected_row_ids):
|
|
self.assertEqual(list(record_set.id), expected_row_ids)
|
|
|
|
def test_lookup_recompute(self):
|
|
self.load_sample(testsamples.sample_students)
|
|
self.apply_user_action(['AddTable', 'Test2', [
|
|
{'id': 'A', 'type': 'Text'},
|
|
{'id': 'B', 'type': 'Numeric'},
|
|
]])
|
|
self.apply_user_action(['AddTable', 'Test3', [
|
|
{'id': 'A', 'type': 'Text'},
|
|
{'id': 'B', 'type': 'Numeric'},
|
|
]])
|
|
self.apply_user_action(['AddViewSection', 'Section2', 'record', 1, 'Test2'])
|
|
self.apply_user_action(['AddViewSection', 'Section3', 'record', 1, 'Test3'])
|
|
self.assertPartialData('_grist_Views', ["id"], [
|
|
[1],
|
|
[2],
|
|
])
|
|
self.assertPartialData('_grist_Views_section', ["id", "parentId", "tableRef"], [
|
|
[1, 1, 4],
|
|
[2, 0, 4],
|
|
[3, 2, 5],
|
|
[4, 0, 5],
|
|
[5, 1, 4],
|
|
[6, 1, 5],
|
|
])
|
|
self.assertPartialData('_grist_Views_section_field', ["id", "parentId", "parentPos"], [
|
|
[1, 1, 1.0],
|
|
[2, 1, 2.0],
|
|
[3, 2, 3.0],
|
|
[4, 2, 4.0],
|
|
[5, 3, 5.0],
|
|
[6, 3, 6.0],
|
|
[7, 4, 7.0],
|
|
[8, 4, 8.0],
|
|
[9, 5, 9.0],
|
|
[10, 5, 10.0],
|
|
[11, 6, 11.0],
|
|
[12, 6, 12.0],
|
|
])
|
|
|
|
table = self.engine.docmodel.tables.lookupOne(tableId='Test2')
|
|
self.assertRecordSet(table.viewSections, [1, 2, 5])
|
|
self.assertRecordSet(list(table.viewSections)[0].fields, [1, 2])
|
|
self.assertRecordSet(list(table.viewSections)[2].fields, [9, 10])
|
|
view = self.engine.docmodel.views.lookupOne(id=1)
|
|
self.assertRecordSet(view.viewSections, [1, 5, 6])
|
|
|
|
self.engine.docmodel.remove(f for vs in table.viewSections for f in vs.fields)
|
|
self.engine.docmodel.remove(table.viewSections)
|
|
self.assertRecordSet(view.viewSections, [6])
|
|
|
|
|
|
def test_modifications(self):
|
|
# Test the add/remove/update methods of DocModel.
|
|
self.load_sample(testsamples.sample_students)
|
|
table = self.engine.docmodel.get_table('Students')
|
|
records = table.lookupRecords(lastName='Bush')
|
|
self.assertEqual([r.id for r in records], [2, 4])
|
|
self.assertEqual([r.schoolName for r in records], ["Yale", "Yale"])
|
|
self.assertEqual([r.firstName for r in records], ["George W", "George H"])
|
|
|
|
# Test the update() method.
|
|
self.engine.docmodel.update(records, schoolName="Test", firstName=["george w", "george h"])
|
|
self.assertEqual([r.schoolName for r in records], ["Test", "Test"])
|
|
self.assertEqual([r.firstName for r in records], ["george w", "george h"])
|
|
|
|
# Test the remove() method.
|
|
self.engine.docmodel.remove(records)
|
|
records = table.lookupRecords(lastName='Bush')
|
|
self.assertEqual(list(records), [])
|
|
self.assertTableData("Students", cols="subset", data=[
|
|
["id","firstName","lastName", "schoolName" ],
|
|
[1, "Barack", "Obama", "Columbia" ],
|
|
[3, "Bill", "Clinton", "Columbia" ],
|
|
[5, "Ronald", "Reagan", "Eureka" ],
|
|
[6, "Gerald", "Ford", "Yale" ]])
|
|
|
|
# Test the add() method.
|
|
self.engine.docmodel.add(table, schoolName="Foo", firstName=["X", "Y"])
|
|
self.assertTableData("Students", cols="subset", data=[
|
|
["id","firstName","lastName", "schoolName" ],
|
|
[1, "Barack", "Obama", "Columbia" ],
|
|
[3, "Bill", "Clinton", "Columbia" ],
|
|
[5, "Ronald", "Reagan", "Eureka" ],
|
|
[6, "Gerald", "Ford", "Yale" ],
|
|
[7, "X", "", "Foo" ],
|
|
[8, "Y", "", "Foo" ],
|
|
])
|
|
|
|
def test_inserts(self):
|
|
# Test the insert() method. We do this on the columns metadata table, so that we can sort by
|
|
# a PositionNumber column.
|
|
self.load_sample(testsamples.sample_students)
|
|
student_columns = self.engine.docmodel.tables.lookupOne(tableId='Students').columns
|
|
school_columns = self.engine.docmodel.tables.lookupOne(tableId='Schools').columns
|
|
|
|
# Should go at the end of the Students table.
|
|
cols = self.engine.docmodel.insert(student_columns, None, colId=["a", "b"], type="Text")
|
|
# Should go at the start of the Schools table.
|
|
self.engine.docmodel.insert_after(school_columns, None, colId="foo", type="Int")
|
|
# Should go before the new "a", "b" columns of the Students table.
|
|
self.engine.docmodel.insert(student_columns, cols[0].parentPos, colId="bar", type="Date")
|
|
|
|
# Verify that the right columns were added to the right tables. This doesn't check positions.
|
|
self.assertTables([
|
|
Table(1, "Students", 0, 0, columns=[
|
|
Column(1, "firstName", "Text", False, "", 0),
|
|
Column(2, "lastName", "Text", False, "", 0),
|
|
Column(4, "schoolName", "Text", False, "", 0),
|
|
Column(5, "schoolIds", "Text", True,
|
|
"':'.join(str(id) for id in Schools.lookupRecords(name=$schoolName).id)", 0),
|
|
Column(6, "schoolCities", "Text", True,
|
|
"':'.join(r.address.city for r in Schools.lookupRecords(name=$schoolName))", 0),
|
|
Column(22, "a", "Text", False, "", 0),
|
|
Column(23, "b", "Text", False, "", 0),
|
|
Column(25, "bar", "Date", False, "", 0),
|
|
]),
|
|
Table(2, "Schools", 0, 0, columns=[
|
|
Column(10, "name", "Text", False, "", 0),
|
|
Column(12, "address", "Ref:Address",False, "", 0),
|
|
Column(24, "foo", "Int", False, "", 0),
|
|
]),
|
|
Table(3, "Address", 0, 0, columns=[
|
|
Column(21, "city", "Text", False, "", 0),
|
|
])
|
|
])
|
|
|
|
# Verify that positions are set such that the order is what we asked for.
|
|
student_columns = self.engine.docmodel.tables.lookupOne(tableId='Students').columns
|
|
self.assertEqual(list(map(int, student_columns)), [1,2,4,5,6,25,22,23])
|
|
school_columns = self.engine.docmodel.tables.lookupOne(tableId='Schools').columns
|
|
self.assertEqual(list(map(int, school_columns)), [24,10,12])
|