(core) move more tests to grist-core
Summary:
* Tie build and run-time docker base images to a consistent version (buster)
* Extend the test login system activated by GRIST_TEST_LOGIN to ease porting tests that currently rely on cognito (many)
* Make org resets work in absence of billing endpoints
* When in-memory session caches are used, add missing invalidation steps
* Pass org information through sign-ups/sign-ins more carefully
* For CORS, explicitly trust GRIST_HOST origin when set
* Move some fixtures and tests to core, focussing on tests that cover existing failures or are in the set of tests run on deployments
* Retain regular `test` target to run the test suite directly, without docker
* Add a `test:smoke` target to run a single simple test without `GRIST_TEST_LOGIN` activated
* Add a `test:docker` target to run the tests against a grist-core docker image - since tests rely on certain fixture teams/docs, added `TEST_SUPPORT_API_KEY` and `TEST_ADD_SAMPLES` flags to ease porting
The tests ported were `nbrowser` tests: `ActionLog.ts` (the first test I tend to port to anything, out of habit), `Fork.ts` (exercises a lot of doc creation paths), `HomeIntro.ts` (a lot of DocMenu exercise), and `DuplicateDocument.ts` (covers a feature known to be failing prior to this diff, the CORS tweak resolves it).
Test Plan: Manually tested via `buildtools/build_core.sh`. In follow up, I want to add running the `test:docker` target in grist-core's workflows. In jenkins, only the smoke test is run. There'd be an argument for running all tests, but they include particularly slow tests, and are duplicates of tests already run (in different configuration admittedly), so I'd like to try first just using them in grist-core to gate updates to any packaged version of Grist (the docker image currently).
Reviewers: alexmojaki
Reviewed By: alexmojaki
Subscribers: alexmojaki
Differential Revision: https://phab.getgrist.com/D3176
2021-12-10 22:42:54 +00:00
|
|
|
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() {
|
2022-04-01 21:31:24 +00:00
|
|
|
const session = await gu.session().user('user4').login();
|
(core) move more tests to grist-core
Summary:
* Tie build and run-time docker base images to a consistent version (buster)
* Extend the test login system activated by GRIST_TEST_LOGIN to ease porting tests that currently rely on cognito (many)
* Make org resets work in absence of billing endpoints
* When in-memory session caches are used, add missing invalidation steps
* Pass org information through sign-ups/sign-ins more carefully
* For CORS, explicitly trust GRIST_HOST origin when set
* Move some fixtures and tests to core, focussing on tests that cover existing failures or are in the set of tests run on deployments
* Retain regular `test` target to run the test suite directly, without docker
* Add a `test:smoke` target to run a single simple test without `GRIST_TEST_LOGIN` activated
* Add a `test:docker` target to run the tests against a grist-core docker image - since tests rely on certain fixture teams/docs, added `TEST_SUPPORT_API_KEY` and `TEST_ADD_SAMPLES` flags to ease porting
The tests ported were `nbrowser` tests: `ActionLog.ts` (the first test I tend to port to anything, out of habit), `Fork.ts` (exercises a lot of doc creation paths), `HomeIntro.ts` (a lot of DocMenu exercise), and `DuplicateDocument.ts` (covers a feature known to be failing prior to this diff, the CORS tweak resolves it).
Test Plan: Manually tested via `buildtools/build_core.sh`. In follow up, I want to add running the `test:docker` target in grist-core's workflows. In jenkins, only the smoke test is run. There'd be an argument for running all tests, but they include particularly slow tests, and are duplicates of tests already run (in different configuration admittedly), so I'd like to try first just using them in grist-core to gate updates to any packaged version of Grist (the docker image currently).
Reviewers: alexmojaki
Reviewed By: alexmojaki
Subscribers: alexmojaki
Differential Revision: https://phab.getgrist.com/D3176
2021-12-10 22:42:54 +00:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
});
|