mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
b13fb1d97e
Summary: Column description and new renaming popup for the GridView. Test Plan: Updated Reviewers: georgegevoian Reviewed By: georgegevoian Differential Revision: https://phab.getgrist.com/D3838
397 lines
13 KiB
TypeScript
397 lines
13 KiB
TypeScript
import {UserAPIImpl} from 'app/common/UserAPI';
|
|
import {assert, driver, Key} from 'mocha-webdriver';
|
|
import * as gu from 'test/nbrowser/gristUtils';
|
|
import {setupTestSuite} from 'test/nbrowser/testUtils';
|
|
|
|
describe('DescriptionColumn', function() {
|
|
this.timeout(20000);
|
|
const cleanup = setupTestSuite();
|
|
|
|
it('should show info tooltip in a Grid View', async () => {
|
|
const session = await gu.session().teamSite.login();
|
|
await session.tempDoc(cleanup, 'Hello.grist');
|
|
await gu.dismissWelcomeTourIfNeeded();
|
|
|
|
// Start renaming col A.
|
|
await doubleClickHeader('A');
|
|
await gu.sendKeys('ColumnA');
|
|
// Check that description is not visible.
|
|
await descriptionIsVisible(false);
|
|
await addDescriptionIsVisible(true);
|
|
// Press add description.
|
|
await clickAddDescription();
|
|
// Check that description is visible.
|
|
await descriptionIsVisible(true);
|
|
await addDescriptionIsVisible(false);
|
|
// Wait for focus in the description input
|
|
await waitForFocus('description');
|
|
|
|
// Measure the height of the description input
|
|
const rBefore = await driver.find(`.test-column-title-description`).getRect();
|
|
|
|
// Send some multiline text (with more than three lines to test if it auto grows).
|
|
await gu.sendKeys('Line1');
|
|
await gu.sendKeys(Key.SHIFT, Key.ENTER, Key.NULL);
|
|
await gu.sendKeys('Line2');
|
|
await gu.sendKeys(Key.SHIFT, Key.ENTER, Key.NULL);
|
|
await gu.sendKeys('Line3');
|
|
await gu.sendKeys(Key.SHIFT, Key.ENTER, Key.NULL);
|
|
await gu.sendKeys('Line4');
|
|
await gu.sendKeys(Key.SHIFT, Key.ENTER, Key.NULL);
|
|
|
|
// Measure the height of the description input again
|
|
const rAfter = await driver.find(`.test-column-title-description`).getRect();
|
|
// Make sure it is at least 13 pixel taller (default font height).
|
|
assert.isTrue(rAfter.height >= rBefore.height + 13);
|
|
|
|
// Press save
|
|
await pressSave();
|
|
|
|
// Make sure column is renamed.
|
|
let header = await gu.getColumnHeader({col: 'ColumnA'});
|
|
|
|
// Make sure it has a tooltip.
|
|
assert.isTrue(await header.find(".test-column-info-tooltip").isDisplayed());
|
|
|
|
// Click the tooltip.
|
|
await header.find(".test-column-info-tooltip").click();
|
|
|
|
// Make sure we see the popup.
|
|
await waitForTooltip();
|
|
|
|
// With a proper text.
|
|
assert.equal(await driver.find(".test-column-info-tooltip-popup").getText(), 'Line1\nLine2\nLine3\nLine4');
|
|
|
|
// Undo one (those renames should be bundled).
|
|
await gu.undo();
|
|
|
|
// Make sure column is renamed back.
|
|
header = await gu.getColumnHeader({col: 'A'});
|
|
|
|
// And there is no tooltip.
|
|
assert.isFalse(await header.find(".test-column-info-tooltip").isPresent());
|
|
});
|
|
|
|
const saveTest = async (save: () => Promise<void>) => {
|
|
const revert = await gu.begin();
|
|
// Start renaming col A.
|
|
await doubleClickHeader('B');
|
|
await gu.sendKeys('ColumnB');
|
|
// Press enter.
|
|
await save();
|
|
await gu.waitForServer();
|
|
// Make sure it is renamed.
|
|
await gu.getColumnHeader({col: 'ColumnB'});
|
|
|
|
// Change description by clicking save.
|
|
await doubleClickHeader('ColumnB');
|
|
await clickAddDescription();
|
|
await waitForFocus('description');
|
|
|
|
await gu.sendKeys('ColumnB description');
|
|
await save();
|
|
await gu.waitForServer();
|
|
// Make sure tooltip is shown.
|
|
await clickTooltip('ColumnB');
|
|
await gu.waitToPass(async () => {
|
|
assert.equal(await driver.findWait(".test-column-info-tooltip-popup", 300).getText(), 'ColumnB description');
|
|
});
|
|
await gu.sendKeys(Key.ESCAPE);
|
|
await revert();
|
|
};
|
|
|
|
it('should support saving by clicking save', async () => {
|
|
await saveTest(pressSave);
|
|
});
|
|
|
|
it('should support saving by clicking away', async () => {
|
|
await saveTest(() => gu.getCell('E', 5).click());
|
|
});
|
|
|
|
it('should support saving by clicking Ctrl+Enter', async () => {
|
|
await saveTest(async () => await gu.sendKeys(Key.chord(await gu.modKey(), Key.ENTER)));
|
|
});
|
|
|
|
it('should support saving by enter', async () => {
|
|
const revert = await gu.begin();
|
|
// Start renaming col A.
|
|
await doubleClickHeader('B');
|
|
await gu.sendKeys('ColumnB');
|
|
|
|
// Make description.
|
|
await clickAddDescription();
|
|
await gu.sendKeys('ColumnB description');
|
|
|
|
// Go to label.
|
|
await gu.sendKeys(Key.ARROW_UP);
|
|
await gu.sendKeys(Key.ARROW_UP);
|
|
await waitForFocus('label');
|
|
|
|
// Save by pressing enter.
|
|
await gu.sendKeys(Key.ENTER);
|
|
await gu.waitForServer();
|
|
// Make sure tooltip is shown.
|
|
await clickTooltip('ColumnB');
|
|
await gu.waitToPass(async () => {
|
|
assert.equal(await driver.findWait(".test-column-info-tooltip-popup", 300).getText(), 'ColumnB description');
|
|
});
|
|
await gu.sendKeys(Key.ESCAPE);
|
|
await revert();
|
|
});
|
|
|
|
it('should support saving by tab', async () => {
|
|
await saveTest(() => gu.sendKeys(Key.TAB));
|
|
await saveTest(() => gu.sendKeys(Key.SHIFT, Key.TAB, Key.NULL));
|
|
});
|
|
|
|
const cancelTest = async (makeCancel: () => Promise<void>) => {
|
|
// Rename column A.
|
|
await doubleClickHeader('A');
|
|
await gu.sendKeys('ColumnA');
|
|
await makeCancel();
|
|
await gu.waitForServer();
|
|
// Make sure we see column A.
|
|
await gu.getColumnHeader({col: 'A'});
|
|
|
|
// Check the same for description.
|
|
await doubleClickHeader('A');
|
|
await clickAddDescription();
|
|
await gu.sendKeys('ColumnA description');
|
|
await makeCancel();
|
|
await gu.waitForServer();
|
|
// Make sure that there is no tooltip.
|
|
assert.isFalse(await gu.getColumnHeader({col: 'A'}).find(".test-column-info-tooltip").isPresent());
|
|
};
|
|
|
|
it('should support canceling by cancel', async () => {
|
|
await cancelTest(pressCancel);
|
|
});
|
|
|
|
it('should support canceling by Escape', async () => {
|
|
await cancelTest(() => gu.sendKeys(Key.ESCAPE));
|
|
});
|
|
|
|
it('should add description by pressing arrow down', async () => {
|
|
await doubleClickHeader('A');
|
|
await addDescriptionIsVisible(true);
|
|
await descriptionIsVisible(false);
|
|
await gu.sendKeys(Key.ARROW_DOWN);
|
|
await waitForFocus('description');
|
|
await addDescriptionIsVisible(false);
|
|
await descriptionIsVisible(true);
|
|
// Type something.
|
|
await gu.sendKeys('ColumnA description', Key.ENTER);
|
|
await gu.sendKeys('ColumnA description');
|
|
// Now press 2 times the up key.
|
|
await gu.sendKeys(Key.ARROW_UP);
|
|
await gu.sendKeys(Key.ARROW_UP);
|
|
// We should still be in the description field.
|
|
await waitForFocus('description');
|
|
// Now press down key and test if that works.
|
|
await gu.sendKeys(Key.ARROW_DOWN);
|
|
await driver.wait(() => driver.executeScript(() => ((document as any).activeElement.selectionEnd === 39)), 500);
|
|
|
|
// Now press it 3 times, we should be back in the label field.
|
|
await gu.sendKeys(Key.ARROW_UP);
|
|
await gu.sendKeys(Key.ARROW_UP);
|
|
await gu.sendKeys(Key.ARROW_UP);
|
|
|
|
// We should be focused back in the label field.
|
|
await waitForFocus('label');
|
|
await pressCancel();
|
|
});
|
|
|
|
it('should tab to other columns and save', async () => {
|
|
const revert = await gu.begin();
|
|
// Start renaming col A.
|
|
await doubleClickHeader('B');
|
|
await gu.sendKeys('ColumnB');
|
|
// Press tab.
|
|
await gu.sendKeys(Key.TAB);
|
|
await gu.waitForServer();
|
|
|
|
// Make sure it is renamed.
|
|
await gu.getColumnHeader({col: 'ColumnB'});
|
|
// Make sure we are now at column C.
|
|
await popupIsAt('C');
|
|
|
|
// Rename column C.
|
|
await gu.sendKeys('ColumnC');
|
|
|
|
// Add description.
|
|
await driver.find(".test-column-title-add-description").click();
|
|
await waitForFocus('description');
|
|
|
|
// Rename description.
|
|
await gu.sendKeys('ColumnC description');
|
|
|
|
// Go back to column B from description by pressing shift tab
|
|
await gu.sendKeys(Key.SHIFT, Key.TAB, Key.NULL);
|
|
await gu.waitForServer();
|
|
// Make sure we are now at column B.
|
|
await popupIsAt('ColumnB');
|
|
// Make sure the label has focus.
|
|
await waitForFocus('label');
|
|
// Go to column C and from the label.
|
|
await gu.sendKeys(Key.TAB);
|
|
// Make sure we are now at column C.
|
|
await popupIsAt('ColumnC');
|
|
// Just quick test that shift tab will work.
|
|
await gu.sendKeys(Key.SHIFT, Key.TAB, Key.NULL);
|
|
// Make sure we are now at column B.
|
|
await popupIsAt('ColumnB');
|
|
// Go to column C and test if the description was saved.
|
|
await gu.sendKeys(Key.TAB);
|
|
// Make sure we are now at column C.
|
|
await popupIsAt('ColumnC');
|
|
// And it has proper description.
|
|
assert.equal(await driver.find(".test-column-title-description").getAttribute('value'), 'ColumnC description');
|
|
// Close by pressing escape.
|
|
await gu.sendKeys(Key.ESCAPE);
|
|
await gu.waitForServer();
|
|
|
|
await revert();
|
|
});
|
|
|
|
it('should support basic edition on CardList', async () => {
|
|
const mainSession = await gu.session().teamSite.login();
|
|
const api = mainSession.createHomeApi();
|
|
const doc = await mainSession.tempDoc(cleanup, "CardView.grist", { load: true });
|
|
const docId = doc.id;
|
|
|
|
await addColumnDescription(api, docId, 'B');
|
|
|
|
// Column description editable in right panel
|
|
await driver.find('.test-right-opener').click();
|
|
|
|
await gu.getCell({ rowNum: 1, col: 'B' }).click();
|
|
await driver.find('.test-right-tab-field').click();
|
|
assert.equal(await getDescriptionInput().value(), 'This is the column description\nIt is in two lines');
|
|
|
|
await gu.getCell({ rowNum: 1, col: 'A' }).click();
|
|
assert.equal(await getDescriptionInput().value(), '');
|
|
|
|
// Remove the description
|
|
await api.applyUserActions(docId, [
|
|
[ 'ModifyColumn', 'Table1', 'B', {
|
|
description: ''
|
|
} ],
|
|
]);
|
|
|
|
await gu.getCell({ rowNum: 1, col: 'B' }).click();
|
|
assert.equal(await getDescriptionInput().value(), '');
|
|
});
|
|
|
|
it('should show info tooltip only if there is a description', async () => {
|
|
const mainSession = await gu.session().teamSite.login();
|
|
const api = mainSession.createHomeApi();
|
|
const doc = await mainSession.tempDoc(cleanup, "CardView.grist", { load: true });
|
|
const docId = doc.id;
|
|
|
|
await addColumnDescription(api, docId, 'B');
|
|
|
|
await gu.changeWidget('Card');
|
|
|
|
const detailUndescribedColumnFirstRow = await gu.getDetailCell('A', 1);
|
|
assert.isFalse(
|
|
await detailUndescribedColumnFirstRow
|
|
.findClosest(".g_record_detail_el")
|
|
.find(".test-column-info-tooltip")
|
|
.isPresent()
|
|
);
|
|
|
|
const detailDescribedColumnFirstRow = await gu.getDetailCell('B', 1);
|
|
const toggle = await detailDescribedColumnFirstRow
|
|
.findClosest(".g_record_detail_el")
|
|
.find(".test-column-info-tooltip");
|
|
// The toggle to show the description is present if there is a description
|
|
assert.isTrue(await toggle.isPresent());
|
|
|
|
// Open the tooltip
|
|
await toggle.click();
|
|
await waitForTooltip();
|
|
|
|
// Check the content of the tooltip
|
|
const descriptionTooltip = await driver
|
|
.find('.test-column-info-tooltip-popup');
|
|
assert.equal(await descriptionTooltip.getText(), 'This is the column description\nIt is in two lines');
|
|
});
|
|
|
|
});
|
|
|
|
async function clickTooltip(col: string) {
|
|
await gu.getColumnHeader({col}).find(".test-column-info-tooltip").click();
|
|
}
|
|
|
|
async function addDescriptionIsVisible(visible = true) {
|
|
if (visible) {
|
|
assert.isTrue(await driver.find(".test-column-title-add-description").isDisplayed());
|
|
} else {
|
|
assert.isFalse(await driver.find(".test-column-title-add-description").isPresent());
|
|
}
|
|
}
|
|
|
|
async function descriptionIsVisible(visible = true) {
|
|
if (visible) {
|
|
assert.isTrue(await driver.find(".test-column-title-description").isDisplayed());
|
|
} else {
|
|
assert.isFalse(await driver.find(".test-column-title-description").isPresent());
|
|
}
|
|
}
|
|
|
|
async function addColumnDescription(api: UserAPIImpl, docId: string, columnName: string) {
|
|
await api.applyUserActions(docId, [
|
|
[ 'ModifyColumn', 'Table1', columnName, {
|
|
description: 'This is the column description\nIt is in two lines'
|
|
} ],
|
|
]);
|
|
}
|
|
|
|
function getDescriptionInput() {
|
|
return driver.find('.test-right-panel .test-column-description');
|
|
}
|
|
|
|
async function popupIsAt(col: string) {
|
|
// Make sure we are now at column.
|
|
assert.equal(await driver.find(".test-column-title-label").getAttribute('value'), col);
|
|
// Make sure that popup is near the column.
|
|
const headerCRect = await gu.getColumnHeader({col}).getRect();
|
|
const popup = await driver.find(".test-column-title-popup").getRect();
|
|
assert.isAtLeast(popup.x, headerCRect.x - 2);
|
|
assert.isBelow(popup.x, headerCRect.x + 2);
|
|
assert.isAtLeast(popup.y, headerCRect.y + headerCRect.height - 2);
|
|
assert.isBelow(popup.y, headerCRect.y + headerCRect.height + 2);
|
|
}
|
|
|
|
async function doubleClickHeader(col: string) {
|
|
const header = await gu.getColumnHeader({col});
|
|
await header.click();
|
|
await header.click();
|
|
await waitForFocus('label');
|
|
}
|
|
|
|
async function waitForFocus(field: 'label'|'description') {
|
|
await gu.waitToPass(async () => assert.isTrue(await driver.find(`.test-column-title-${field}`).hasFocus()), 200);
|
|
}
|
|
|
|
async function waitForTooltip() {
|
|
await gu.waitToPass(async () => {
|
|
assert.isTrue(await driver.find(".test-column-info-tooltip-popup").isDisplayed());
|
|
});
|
|
}
|
|
|
|
async function pressSave() {
|
|
await driver.find(".test-column-title-save").click();
|
|
await gu.waitForServer();
|
|
}
|
|
|
|
async function pressCancel() {
|
|
await driver.find(".test-column-title-cancel").click();
|
|
await gu.waitForServer();
|
|
}
|
|
|
|
async function clickAddDescription() {
|
|
await driver.find(".test-column-title-add-description").click();
|
|
await waitForFocus('description');
|
|
}
|