(core) Bug with empty, self-referencing RefList columns.

Summary:
If a table T contains a RefList:T showing RowId and there
are any empty cells, the table can't be removed.

Test Plan: Added new test

Reviewers: dsagal

Reviewed By: dsagal

Subscribers: dsagal

Differential Revision: https://phab.getgrist.com/D4215
This commit is contained in:
Jarosław Sadziński 2024-03-20 17:48:46 +01:00
parent 07fcce548b
commit ff8477cbe4
3 changed files with 38 additions and 5 deletions

View File

@ -1178,7 +1178,7 @@ class UserActions(object):
# to strings containing comma-separated row IDs. # to strings containing comma-separated row IDs.
# We need to get the values before changing the column type. # We need to get the values before changing the column type.
table = self._engine.tables[table_id] table = self._engine.tables[table_id]
new_values = [",".join(map(str, row)) for row in self._get_column_values(col)] new_values = [",".join(map(str, row or [])) for row in self._get_column_values(col)]
self.ModifyColumn(table_id, col_id, dict(type="Text")) self.ModifyColumn(table_id, col_id, dict(type="Text"))
self.BulkUpdateRecord(table_id, list(table.row_ids), {col_id: new_values}) self.BulkUpdateRecord(table_id, list(table.row_ids), {col_id: new_values})
return return

View File

@ -10,14 +10,40 @@ describe('ReferenceList', function() {
before(async function() { before(async function() {
session = await gu.session().teamSite.login(); session = await gu.session().teamSite.login();
await session.tempDoc(cleanup, 'Favorite_Films.grist'); });
describe('other', function() {
it('allows to delete document with self reference', async function() {
const docId = await session.tempNewDoc(cleanup);
await gu.sendActions([
['AddEmptyTable', 'Table2'],
['ModifyColumn', 'Table1', 'B', {type: 'RefList:Table1'}],
['AddRecord', 'Table1', null, {A: 'a'}],
['AddRecord', 'Table1', null, {A: 'b', B: ["L", 1]}],
['AddRecord', 'Table1', null, {A: 'c', B: ["L", 2]}],
]);
// Now try to delete the table.
await gu.removeTable('Table1');
await gu.checkForErrors();
// Make sure table is deleted. Previously it ended with an engine error
// in the 'a' row which has NULL instead of a list of ids.
const api = session.createHomeApi().getDocAPI(docId);
const tables = await api.getRows('_grist_Tables');
assert.deepEqual(tables.tableId, ['Table2']);
});
});
describe('transforms', function() {
before(async function() {
await session.tempDoc(cleanup, 'Favorite_Films.grist');
await gu.toggleSidePanel('right'); await gu.toggleSidePanel('right');
await driver.find(".test-right-tab-pagewidget").click(); await driver.find(".test-right-tab-pagewidget").click();
await driver.find('.test-config-data').click(); await driver.find('.test-config-data').click();
}); });
describe('transforms', function() {
afterEach(() => gu.checkForErrors()); afterEach(() => gu.checkForErrors());
it('should correctly transform references to reference lists', async function() { it('should correctly transform references to reference lists', async function() {

View File

@ -1016,6 +1016,13 @@ export async function sendActions(actions: UserAction[]) {
await driver.manage().setTimeouts({ await driver.manage().setTimeouts({
script: 1000 * 2, /* 2 seconds, default is 0.5s */ script: 1000 * 2, /* 2 seconds, default is 0.5s */
}); });
// Make quick test that we have a list of actions not just a single action, by checking
// if the first element is an array.
if (actions.length && !Array.isArray(actions[0])) {
throw new Error('actions argument should be a list of actions, not a single action');
}
const result = await driver.executeAsyncScript(` const result = await driver.executeAsyncScript(`
const done = arguments[arguments.length - 1]; const done = arguments[arguments.length - 1];
const prom = gristDocPageModel.gristDoc.get().docModel.docData.sendActions(${JSON.stringify(actions)}); const prom = gristDocPageModel.gristDoc.get().docModel.docData.sendActions(${JSON.stringify(actions)});