import {UserAPI} 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('DropdownConditionEditor', function () { this.timeout(20000); const cleanup = setupTestSuite(); let api: UserAPI; let docId: string; before(async () => { const session = await gu.session().user('user1').login(); api = session.createHomeApi(); docId = (await session.tempDoc(cleanup, 'DropdownCondition.grist')).id; await api.updateDocPermissions(docId, {users: { [gu.translateUser('user2').email]: 'editors', }}); await addUserAttributes(); await gu.openPage('Employees'); await gu.openColumnPanel(); }); afterEach(() => gu.checkForErrors()); async function addUserAttributes() { await api.applyUserActions(docId, [ ['AddTable', 'Roles', [{id: 'Email'}, {id: 'Admin', type: 'Bool'}]], ['AddRecord', 'Roles', null, {Email: gu.translateUser('user1').email, Admin: true}], ['AddRecord', 'Roles', null, {Email: gu.translateUser('user2').email, Admin: false}], ]); await driver.find('.test-tools-access-rules').click(); await gu.waitForServer(); await driver.findContentWait('button', /Add User Attributes/, 2000).click(); const userAttrRule = await driver.find('.test-rule-userattr'); await userAttrRule.find('.test-rule-userattr-name').click(); await driver.sendKeys('Roles', Key.ENTER); await userAttrRule.find('.test-rule-userattr-attr').click(); await driver.sendKeys('Email', Key.ENTER); await userAttrRule.find('.test-rule-userattr-table').click(); await driver.findContent('.test-select-menu li', 'Roles').click(); await userAttrRule.find('.test-rule-userattr-col').click(); await driver.sendKeys('Email', Key.ENTER); await driver.find('.test-rules-save').click(); await gu.waitForServer(); } describe(`in choice columns`, function() { before(async () => { const session = await gu.session().user('user1').login(); await session.loadDoc(`/doc/${docId}`); }); it('creates dropdown conditions', async function() { await gu.getCell(1, 1).click(); await driver.find('.test-field-dropdown-condition').click(); await gu.waitAppFocus(false); await gu.sendKeys(await gu.selectAllKey(), Key.DELETE, 'c'); await gu.waitToPass(async () => { const completions = await driver.findAll('.ace_autocomplete .ace_line', el => el.getText()); assert.deepEqual(completions, [ 'c\nhoice\n ', 're\nc\n.Name\n ', 're\nc\n.Role\n ', 're\nc\n.Supervisor\n ', 'user.A\nc\ncess\n ', ]); }); await gu.sendKeysSlowly(['hoice not in ']); // Attempts to reduce test flakiness by delaying input of $. Not guaranteed to do anything. await driver.sleep(100); await gu.sendKeys('$'); await gu.waitToPass(async () => { // This test is sometimes flaky here. It will consistently return the wrong value, usually an array of // empty strings. The running theory is it's an issue in Ace editor. const completions = await driver.findAll('.ace_autocomplete .ace_line', el => el.getText()); assert.deepEqual(completions, [ '$\nName\n ', '$\nRole\n ', '$\nSupervisor\n ', ]); }); await gu.sendKeys('Role', Key.ENTER); await gu.waitForServer(); assert.equal( await driver.find('.test-field-dropdown-condition').getText(), 'choice not in $Role' ); // Check that autocomplete values are filtered. await gu.sendKeys(Key.ENTER); assert.deepEqual(await driver.findAll('.test-autocomplete li', (el) => el.getText()), [ 'Supervisor', ]); await gu.sendKeys(Key.ESCAPE); await gu.getCell(1, 4).click(); await gu.sendKeys(Key.ENTER); assert.deepEqual(await driver.findAll('.test-autocomplete li', (el) => el.getText()), [ 'Trainee', ]); await gu.sendKeys(Key.ESCAPE); await gu.getCell(1, 6).click(); await gu.sendKeys(Key.ENTER); assert.deepEqual(await driver.findAll('.test-autocomplete li', (el) => el.getText()), [ 'Trainee', 'Supervisor', ]); await gu.sendKeys(Key.ESCAPE); // Change the column type to Choice List and check values are still filtered. await gu.setType('Choice List', {apply: true}); assert.equal( await driver.find('.test-field-dropdown-condition').getText(), 'choice not in $Role' ); await gu.getCell(1, 4).click(); await gu.sendKeys(Key.ENTER); assert.deepEqual(await driver.findAll('.test-autocomplete li', (el) => el.getText()), [ 'Trainee', ]); await gu.sendKeys(Key.ESCAPE); }); it('removes dropdown conditions', async function() { await driver.find('.test-field-dropdown-condition').click(); await gu.sendKeys(await gu.selectAllKey(), Key.DELETE, Key.ENTER); await gu.waitForServer(); // Check that autocomplete values are no longer filtered. await gu.sendKeys(Key.ENTER); assert.deepEqual(await driver.findAll('.test-autocomplete li', (el) => el.getText()), [ 'Trainee', 'Supervisor', ]); await gu.sendKeys(Key.ESCAPE); // Change the column type back to Choice and check values are still no longer filtered. await gu.setType('Choice', {apply: true}); assert.isFalse(await driver.find('.test-field-dropdown-condition').isPresent()); await gu.sendKeys(Key.ENTER); assert.deepEqual(await driver.findAll('.test-autocomplete li', (el) => el.getText()), [ 'Supervisor', 'Trainee', ]); await gu.sendKeys(Key.ESCAPE); }); it('reports errors', async function() { // Check syntax errors are reported, but not saved. await driver.find('.test-field-set-dropdown-condition').click(); await gu.sendKeys('!@#$%^', Key.ENTER); await gu.waitForServer(); assert.equal( await driver.find('.test-field-dropdown-condition-error').getText(), 'SyntaxError invalid syntax on line 1 col 1' ); await gu.reloadDoc(); assert.isFalse(await driver.find('.test-field-dropdown-condition-error').isPresent()); // Check compilation errors are reported and saved. await driver.find('.test-field-set-dropdown-condition').click(); await gu.sendKeys('foo', Key.ENTER); await gu.waitForServer(); assert.equal( await driver.find('.test-field-dropdown-condition-error').getText(), "Unknown variable 'foo'" ); await gu.reloadDoc(); assert.equal( await driver.find('.test-field-dropdown-condition-error').getText(), "Unknown variable 'foo'" ); // Check that the autocomplete dropdown also reports an error. await gu.sendKeys(Key.ENTER); assert.equal( await driver.find('.test-autocomplete-no-items-message').getText(), 'Error in dropdown condition' ); await gu.sendKeys(Key.ESCAPE); }); }); describe(`in reference columns`, function() { before(async () => { const session = await gu.session().user('user1').login(); await session.loadDoc(`/doc/${docId}`); }); it('creates dropdown conditions', async function() { await gu.getCell(2, 1).click(); assert.isFalse(await driver.find('.test-field-dropdown-condition').isPresent()); await driver.find('.test-field-set-dropdown-condition').click(); await gu.waitAppFocus(false); await gu.sendKeysSlowly(['choice']); await gu.waitToPass(async () => { const completions = await driver.findAll('.ace_autocomplete .ace_line', el => el.getText()); assert.deepEqual(completions, [ 'choice\n ', 'choice\n.id\n ', 'choice\n.Name\n ', 'choice\n.Role\n ', 'choice\n.Supervisor\n ' ]); }); await gu.sendKeys('.Role == "Supervisor" and $Role != "Supervisor" and $id != 2', Key.ENTER); await gu.waitForServer(); assert.equal( await driver.find('.test-field-dropdown-condition .ace_line').getAttribute('textContent'), 'choice.Role == "Supervisor" and $Role != "Supervisor" and $id != 2\n' ); // Check that autocomplete values are filtered. await gu.sendKeys(Key.ENTER); assert.deepEqual(await driver.findAll('.test-autocomplete li', (el) => el.getText()), [ 'Pavan Madilyn', 'Marie Ziyad', ]); await gu.sendKeys(Key.ESCAPE); // Should be no options on row 2 because of $id != 2 part of condition. await gu.getCell(2, 2).click(); await gu.sendKeys(Key.ENTER); assert.deepEqual(await driver.findAll('.test-autocomplete li', (el) => el.getText()), [ ]); await gu.sendKeys(Key.ESCAPE); // Row 3 should be like row 1. await gu.getCell(2, 3).click(); await gu.sendKeys(Key.ENTER); assert.deepEqual(await driver.findAll('.test-autocomplete li', (el) => el.getText()), [ 'Pavan Madilyn', 'Marie Ziyad', ]); await gu.sendKeys(Key.ESCAPE); await gu.getCell(2, 4).click(); await gu.sendKeys(Key.ENTER); assert.isEmpty(await driver.findAll('.test-autocomplete li', (el) => el.getText())); await gu.sendKeys(Key.ESCAPE); await gu.getCell(2, 6).click(); await gu.sendKeys(Key.ENTER); assert.deepEqual(await driver.findAll('.test-autocomplete li', (el) => el.getText()), [ 'Marie Ziyad', 'Pavan Madilyn', ]); await gu.sendKeys(Key.ESCAPE); // Change the column type to Reference List and check values are still filtered. await gu.setType('Reference List', {apply: true}); assert.equal( await driver.find('.test-field-dropdown-condition .ace_line').getAttribute('textContent'), 'choice.Role == "Supervisor" and $Role != "Supervisor" and $id != 2\n' ); await gu.getCell(2, 4).click(); await gu.sendKeys(Key.ENTER); assert.isEmpty(await driver.findAll('.test-autocomplete li', (el) => el.getText())); await gu.sendKeys(Key.ESCAPE); }); it('removes dropdown conditions', async function() { await driver.find('.test-field-dropdown-condition').click(); await gu.sendKeys(await gu.selectAllKey(), Key.DELETE, Key.ENTER); await gu.waitForServer(); // Check that autocomplete values are no longer filtered. await gu.sendKeys(Key.ENTER); assert.deepEqual(await driver.findAll('.test-autocomplete li', (el) => el.getText()), [ 'Emma Thamir', 'Holger Klyment', 'Marie Ziyad', 'Olivier Bipin', 'Pavan Madilyn', ]); await gu.sendKeys(Key.ESCAPE); // Change the column type back to Reference and check values are still no longer filtered. await gu.setType('Reference', {apply: true}); assert.isFalse(await driver.find('.test-field-dropdown-condition').isPresent()); await gu.sendKeys(Key.ENTER); assert.deepEqual(await driver.findAll('.test-autocomplete li', (el) => el.getText()), [ 'Emma Thamir', 'Holger Klyment', 'Marie Ziyad', 'Olivier Bipin', 'Pavan Madilyn', ]); await gu.sendKeys(Key.ESCAPE); }); it('reports errors', async function() { // Check syntax errors are reported, but not saved. await driver.find('.test-field-set-dropdown-condition').click(); await gu.sendKeys('!@#$%^', Key.ENTER); await gu.waitForServer(); assert.equal( await driver.find('.test-field-dropdown-condition-error').getText(), 'SyntaxError invalid syntax on line 1 col 1' ); await gu.reloadDoc(); assert.isFalse(await driver.find('.test-field-dropdown-condition-error').isPresent()); // Check compilation errors are reported and saved. await driver.find('.test-field-set-dropdown-condition').click(); await gu.sendKeys('foo', Key.ENTER); await gu.waitForServer(); assert.equal( await driver.find('.test-field-dropdown-condition-error').getText(), "Unknown variable 'foo'" ); await gu.reloadDoc(); assert.equal( await driver.find('.test-field-dropdown-condition-error').getText(), "Unknown variable 'foo'" ); // Check that the autocomplete dropdown also reports an error. await gu.sendKeys(Key.ENTER); assert.equal( await driver.find('.test-autocomplete-no-items-message').getText(), 'Error in dropdown condition' ); await gu.sendKeys(Key.ESCAPE); // Check evaluation errors are also reported in the dropdown. await driver.find('.test-field-dropdown-condition').click(); await gu.sendKeys(await gu.selectAllKey(), Key.DELETE, '[] not in 5', Key.ENTER); await gu.waitForServer(); await gu.sendKeys(Key.ENTER); assert.equal( await driver.find('.test-autocomplete-no-items-message').getText(), 'Error in dropdown condition' ); await gu.sendKeys(Key.ESCAPE); }); }); it('supports user variable', async function() { // Filter dropdown values based on a user attribute. await gu.getCell(1, 1).click(); await driver.find('.test-field-set-dropdown-condition').click(); await gu.waitAppFocus(false); await gu.sendKeysSlowly(['user.']); await gu.waitToPass(async () => { const completions = await driver.findAll('.ace_autocomplete .ace_line', el => el.getText()); assert.deepEqual(completions, [ 'user.\nAccess\n ', 'user.\nEmail\n ', 'user.\nIsLoggedIn\n ', 'user.\nLinkKey.\n ', 'user.\nName\n ', 'user.\nOrigin\n ', 'user.\nRoles.Admin\n ', 'user.\nRoles.Email\n ', '' ]); }); await gu.sendKeys('Roles.Admin == True', Key.ENTER); await gu.waitForServer(); // Check that user1 (who is an admin) can see dropdown values. await gu.sendKeys(Key.ENTER); assert.deepEqual(await driver.findAll('.test-autocomplete li', (el) => el.getText()), [ 'Trainee', 'Supervisor', ]); await gu.sendKeys(Key.ESCAPE); // Switch to user2 (who is not an admin), and check that they can't see any dropdown values. const session = await gu.session().user('user2').login(); await session.loadDoc(`/doc/${docId}`); await gu.getCell(1, 1).click(); await gu.sendKeys(Key.ENTER); assert.deepEqual(await driver.findAll('.test-autocomplete li', (el) => el.getText()), []); await gu.sendKeys(Key.ESCAPE); }); });