mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
176 lines
8.3 KiB
TypeScript
176 lines
8.3 KiB
TypeScript
|
import {assert, driver, WebElement, WebElementPromise} from 'mocha-webdriver';
|
||
|
import * as gu from 'test/nbrowser/gristUtils';
|
||
|
import {setupTestSuite} from 'test/nbrowser/testUtils';
|
||
|
|
||
|
|
||
|
describe('ActionLog', function() {
|
||
|
this.timeout(20000);
|
||
|
const cleanup = setupTestSuite();
|
||
|
|
||
|
afterEach(() => gu.checkForErrors());
|
||
|
|
||
|
async function getActionUndoState(limit: number): Promise<string[]> {
|
||
|
const state = await driver.findAll('.action_log .action_log_item', (el) => el.getAttribute('class'));
|
||
|
return state.slice(0, limit).map((s) => s.replace(/action_log_item/, '').trim());
|
||
|
}
|
||
|
|
||
|
function getActionLogItems(): Promise<WebElement[]> {
|
||
|
// Use a fancy negation of style selector to exclude hidden log entries.
|
||
|
return driver.findAll(".action_log .action_log_item:not([style*='display: none'])");
|
||
|
}
|
||
|
|
||
|
function getActionLogItem(index: number): WebElementPromise {
|
||
|
return new WebElementPromise(driver, getActionLogItems().then((elems) => elems[index]));
|
||
|
}
|
||
|
|
||
|
before(async function() {
|
||
|
const session = await gu.session().login();
|
||
|
await session.tempDoc(cleanup, 'Hello.grist');
|
||
|
await gu.dismissWelcomeTourIfNeeded();
|
||
|
});
|
||
|
|
||
|
it("should cross out undone actions", async function() {
|
||
|
// Open the action-log tab.
|
||
|
await driver.findWait('.test-tools-log', 1000).click();
|
||
|
await gu.waitToPass(() => // Click might not work while panel is sliding out to open.
|
||
|
driver.findContentWait('.test-doc-history-tabs .test-select-button', 'Activity', 500).click());
|
||
|
|
||
|
// Perform some actions and check that they all appear as default.
|
||
|
await gu.enterGridRows({rowNum: 1, col: 0}, [['a'], ['b'], ['c'], ['d']]);
|
||
|
|
||
|
assert.deepEqual(await getActionUndoState(4), ['default', 'default', 'default', 'default']);
|
||
|
|
||
|
// Undo and check that the most recent action is crossed out.
|
||
|
await gu.undo();
|
||
|
assert.deepEqual(await getActionUndoState(4), ['undone', 'default', 'default', 'default']);
|
||
|
|
||
|
await gu.undo(2);
|
||
|
assert.deepEqual(await getActionUndoState(4), ['undone', 'undone', 'undone', 'default']);
|
||
|
await gu.redo(2);
|
||
|
assert.deepEqual(await getActionUndoState(4), ['undone', 'default', 'default', 'default']);
|
||
|
});
|
||
|
|
||
|
it("should indicate that actions that cannot be redone are buried", async function() {
|
||
|
// Add an item after the undo actions and check that they get buried.
|
||
|
await gu.getCell({rowNum: 1, col: 0}).click();
|
||
|
await gu.enterCell('e');
|
||
|
assert.deepEqual(await getActionUndoState(4), ['default', 'buried', 'default', 'default']);
|
||
|
|
||
|
// Check that undos skip the buried actions.
|
||
|
await gu.undo(2);
|
||
|
assert.deepEqual(await getActionUndoState(4), ['undone', 'buried', 'undone', 'default']);
|
||
|
|
||
|
// Check that burying around already buried actions works.
|
||
|
await gu.enterCell('f');
|
||
|
await gu.waitForServer();
|
||
|
assert.deepEqual(await getActionUndoState(5), ['default', 'buried', 'buried', 'buried', 'default']);
|
||
|
});
|
||
|
|
||
|
it("should properly rebuild the action log on refresh", async function() {
|
||
|
// Undo past buried actions to add complexity to the current state of the log
|
||
|
// and refresh.
|
||
|
await gu.undo(2);
|
||
|
await driver.navigate().refresh();
|
||
|
await gu.waitForDocToLoad();
|
||
|
// refreshing browser will restore position on last cell
|
||
|
// switch active cell to the first cell in the first row
|
||
|
await gu.getCell(0, 1).click();
|
||
|
await driver.findWait('.test-tools-log', 1000).click();
|
||
|
await driver.findContentWait('.test-doc-history-tabs .test-select-button', 'Activity', 500).click();
|
||
|
await gu.waitForServer();
|
||
|
assert.deepEqual(await getActionUndoState(6), ['undone', 'buried', 'buried', 'buried', 'undone', 'default']);
|
||
|
});
|
||
|
|
||
|
it("should indicate to the user when they cannot undo or redo", async function() {
|
||
|
assert.equal(await driver.find('.test-undo').matches('[class*=-disabled]'), false);
|
||
|
assert.equal(await driver.find('.test-redo').matches('[class*=-disabled]'), false);
|
||
|
|
||
|
// Undo and check that undo button gets disabled.
|
||
|
await gu.undo();
|
||
|
assert.equal(await driver.find('.test-undo').matches('[class*=-disabled]'), true);
|
||
|
assert.equal(await driver.find('.test-redo').matches('[class*=-disabled]'), false);
|
||
|
|
||
|
// Redo to the top of the log and check that redo button gets disabled.
|
||
|
await gu.redo(3);
|
||
|
assert.equal(await driver.find('.test-undo').matches('[class*=-disabled]'), false);
|
||
|
assert.equal(await driver.find('.test-redo').matches('[class*=-disabled]'), true);
|
||
|
});
|
||
|
|
||
|
it("should show clickable tabular diffs", async function() {
|
||
|
const item0 = await getActionLogItem(0);
|
||
|
assert.equal(await item0.find('table caption').getText(), 'Table1');
|
||
|
assert.equal(await item0.find('table th:nth-child(2)').getText(), 'A');
|
||
|
assert.equal(await item0.find('table td:nth-child(2)').getText(), 'f');
|
||
|
assert.equal(await gu.getActiveCell().getText(), 'a');
|
||
|
await item0.find('table td:nth-child(2)').click();
|
||
|
assert.equal(await gu.getActiveCell().getText(), 'f');
|
||
|
});
|
||
|
|
||
|
it("clickable tabular diffs should work across renames", async function() {
|
||
|
// Add another table just to mix things up a bit.
|
||
|
await gu.addNewTable();
|
||
|
// Rename our old table.
|
||
|
await gu.renameTable('Table1', 'Table1Renamed');
|
||
|
await gu.getPageItem('Table1Renamed').click();
|
||
|
await gu.renameColumn({col: 'A'}, 'ARenamed');
|
||
|
|
||
|
// Check that it's still usable. (It doesn't reflect the new names in the content of prior
|
||
|
// actions though -- e.g. the action below still mentions 'A' for column name -- and it's
|
||
|
// unclear if it should.)
|
||
|
const item2 = await getActionLogItem(2);
|
||
|
assert.equal(await item2.find('table caption').getText(), 'Table1');
|
||
|
assert.equal(await item2.find('table td:nth-child(2)').getText(), 'f');
|
||
|
await gu.getCell({rowNum: 1, col: 0}).click();
|
||
|
assert.notEqual(await gu.getActiveCell().getText(), 'f');
|
||
|
await item2.find('table td:nth-child(2)').click();
|
||
|
assert.equal(await gu.getActiveCell().getText(), 'f');
|
||
|
|
||
|
// Delete the page and table for Table1Renamed.
|
||
|
await gu.openPageMenu('Table1Renamed');
|
||
|
await driver.find('.grist-floating-menu .test-docpage-remove').click();
|
||
|
await driver.findWait('.test-modal-confirm', 500).click();
|
||
|
await gu.waitForServer();
|
||
|
await driver.findContent('.action_log label', /All tables/).find('input').click();
|
||
|
|
||
|
const item4 = await getActionLogItem(4);
|
||
|
await gu.scrollIntoView(item4);
|
||
|
await item4.find('table td:nth-child(2)').click();
|
||
|
assert.include(await driver.findWait('.test-notifier-toast-wrapper', 1000).getText(),
|
||
|
'Table1Renamed was subsequently removed');
|
||
|
await driver.find('.test-notifier-toast-wrapper .test-notifier-toast-close').click();
|
||
|
await driver.findContent('.action_log label', /All tables/).find('input').click();
|
||
|
});
|
||
|
|
||
|
it("should filter cell changes and renames by table", async function() {
|
||
|
// Have Table2, now add some more
|
||
|
await gu.enterGridRows({rowNum: 1, col: 0}, [['2']]);
|
||
|
await gu.addNewTable(); // Table1
|
||
|
await gu.enterGridRows({rowNum: 1, col: 0}, [['1']]);
|
||
|
await gu.addNewTable(); // Table3
|
||
|
await gu.enterGridRows({rowNum: 1, col: 0}, [['3']]);
|
||
|
await gu.getPageItem('Table1').click();
|
||
|
|
||
|
assert.lengthOf(await getActionLogItems(), 2);
|
||
|
|
||
|
assert.equal(await getActionLogItem(0).find("table:not([style*='display: none']) caption").getText(), 'Table1');
|
||
|
assert.equal(await getActionLogItem(1).find('.action_log_rename').getText(), 'Add Table1');
|
||
|
await gu.renameTable('Table1', 'Table1Renamed');
|
||
|
assert.equal(await getActionLogItem(0).find('.action_log_rename').getText(),
|
||
|
'Rename Table1 to Table1Renamed');
|
||
|
|
||
|
await gu.renameColumn({col: 'A'}, 'ARenamed');
|
||
|
assert.equal(await getActionLogItem(0).find('.action_log_rename').getText(),
|
||
|
'Rename Table1Renamed.A to ARenamed');
|
||
|
await gu.getPageItem('Table2').click();
|
||
|
assert.equal(await getActionLogItem(0).find("table:not([style*='display: none']) caption").getText(), 'Table2');
|
||
|
await gu.getPageItem('Table3').click();
|
||
|
assert.equal(await getActionLogItem(0).find("table:not([style*='display: none']) caption").getText(), 'Table3');
|
||
|
|
||
|
// Now show all tables and make sure the result is a longer (visible) log.
|
||
|
const filteredCount = (await getActionLogItems()).length;
|
||
|
await driver.findContent('.action_log label', /All tables/).find('input').click();
|
||
|
const fullCount = (await getActionLogItems()).length;
|
||
|
assert.isAbove(fullCount, filteredCount);
|
||
|
});
|
||
|
});
|