mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
863 lines
40 KiB
TypeScript
863 lines
40 KiB
TypeScript
|
/**
|
||
|
* Test of the UI for Granular Access Control, part 2.
|
||
|
*/
|
||
|
import escapeRegExp from 'lodash/escapeRegExp';
|
||
|
import { assert, driver, Key, stackWrapFunc, WebElement } from 'mocha-webdriver';
|
||
|
import { enterRulePart, findDefaultRuleSet, findRuleSet, findTable,
|
||
|
getRuleText } from 'test/nbrowser/aclTestUtils';
|
||
|
import * as gu from 'test/nbrowser/gristUtils';
|
||
|
import { setupTestSuite } from 'test/nbrowser/testUtils';
|
||
|
|
||
|
const isChecked = stackWrapFunc(async function(el: WebElement): Promise<boolean> {
|
||
|
return await el.getAttribute('checked') !== null;
|
||
|
});
|
||
|
|
||
|
const isDisabled = stackWrapFunc(async function(el: WebElement): Promise<boolean> {
|
||
|
return await el.getAttribute('disabled') !== null;
|
||
|
});
|
||
|
|
||
|
describe("AccessRules2", function() {
|
||
|
this.timeout(40000);
|
||
|
const cleanup = setupTestSuite();
|
||
|
let docId: string;
|
||
|
|
||
|
before(async function() {
|
||
|
// Import a test document we've set up for this.
|
||
|
const mainSession = await gu.session().teamSite.user('user1').login();
|
||
|
docId = (await mainSession.tempDoc(cleanup, 'ACL-Test.grist', {load: false})).id;
|
||
|
|
||
|
// Share it with a few users.
|
||
|
const api = mainSession.createHomeApi();
|
||
|
await api.updateDocPermissions(docId, { users: {
|
||
|
[gu.translateUser("user2").email]: 'owners',
|
||
|
[gu.translateUser("user3").email]: 'editors',
|
||
|
} });
|
||
|
return docId;
|
||
|
});
|
||
|
|
||
|
afterEach(() => gu.checkForErrors());
|
||
|
|
||
|
let viewAsUrl: string;
|
||
|
|
||
|
it('should list users with access to the document, plus examples', async function() {
|
||
|
// Open AccessRules page, and click Users button.
|
||
|
const mainSession = await gu.session().teamSite.user('user1').login();
|
||
|
await mainSession.loadDoc(`/doc/${docId}`);
|
||
|
await driver.find('.test-tools-access-rules').click();
|
||
|
await driver.findContentWait('button', /View As/, 3000).click();
|
||
|
|
||
|
const [email1, email2, email3] = ['user1', 'user2', 'user3'].map(u => gu.translateUser(u as any).email);
|
||
|
// All users the doc is shared with should be listed, with correct Access.
|
||
|
assert.equal(await driver.findContent('.test-acl-user-item', email1).isPresent(), false);
|
||
|
assert.equal(await driver.findContent('.test-acl-user-item', email2)
|
||
|
.find('.test-acl-user-access').getText(), '(Owner)');
|
||
|
assert.equal(await driver.findContent('.test-acl-user-item', email3)
|
||
|
.find('.test-acl-user-access').getText(), '(Editor)');
|
||
|
|
||
|
// Check examples are present.
|
||
|
assert.deepEqual(
|
||
|
(await driver.findAll('.test-acl-user-item span', e => e.getText()))
|
||
|
.filter(txt => txt.includes('@example')),
|
||
|
['owner@example.com', 'editor1@example.com', 'editor2@example.com', 'viewer@example.com', 'unknown@example.com']);
|
||
|
|
||
|
// Add a user attribute table.
|
||
|
await mainSession.createHomeApi().applyUserActions(docId, [
|
||
|
['AddTable', 'Zones', [{id: 'Email'}, {id: 'City'}]],
|
||
|
['AddRecord', 'Zones', null, {Email: email1, City: 'Seattle'}],
|
||
|
['AddRecord', 'Zones', null, {Email: email2, City: 'Boston'}],
|
||
|
['AddRecord', 'Zones', null, {Email: 'fast@speed.com', City: 'Cambridge'}],
|
||
|
['AddRecord', 'Zones', null, {Email: 'slow@speed.com', City: 'Springfield'}],
|
||
|
]);
|
||
|
const records = await mainSession.createHomeApi().applyUserActions(docId, [
|
||
|
['AddRecord', '_grist_ACLResources', -1, {tableId: '*', colIds: '*'}],
|
||
|
['AddRecord', '_grist_ACLRules', null, {
|
||
|
resource: -1, userAttributes: JSON.stringify({
|
||
|
name: 'Zone',
|
||
|
tableId: 'Zones',
|
||
|
charId: 'Email',
|
||
|
lookupColId: 'Email',
|
||
|
}),
|
||
|
}],
|
||
|
]);
|
||
|
|
||
|
// Refresh list.
|
||
|
await mainSession.loadDoc(`/doc/${docId}`);
|
||
|
await driver.find('.test-tools-access-rules').click();
|
||
|
await driver.findContentWait('button', /View As/, 3000).click();
|
||
|
|
||
|
// Check users from attribute table are present.
|
||
|
assert.deepEqual(
|
||
|
(await driver.findAll('.test-acl-user-item span', e => e.getText()))
|
||
|
.filter(txt => txt.includes('@speed')),
|
||
|
['fast@speed.com', 'slow@speed.com']);
|
||
|
|
||
|
// 'View As' is present, except for current user.
|
||
|
assert.equal(await driver.findContent('.test-acl-user-item', email1)
|
||
|
.isPresent(), false);
|
||
|
assert.equal(await driver.findContent('.test-acl-user-item', email2)
|
||
|
.isPresent(), true);
|
||
|
|
||
|
// Click "View As", and wait for doc to reload.
|
||
|
await driver.findContent('.test-acl-user-item', email3).click();
|
||
|
await gu.waitForUrl(/aclAsUser/);
|
||
|
await gu.waitForDocToLoad();
|
||
|
// Make sure we are not on ACL page.
|
||
|
viewAsUrl = await driver.getCurrentUrl();
|
||
|
assert.notMatch(viewAsUrl, /\/p\/acl/);
|
||
|
|
||
|
// Check for a tag in the doc header.
|
||
|
assert.equal(await driver.findWait('.test-view-as-banner', 2000).isPresent(), true);
|
||
|
assert.match(await driver.find('.test-view-as-banner .test-select-open').getText(),
|
||
|
new RegExp(gu.translateUser('user3').name, 'i'));
|
||
|
|
||
|
// check the aclAsUser parameter on the url persists after navigating to another page
|
||
|
await gu.getPageItem('FinancialsTable').click();
|
||
|
await gu.waitForDocToLoad();
|
||
|
assert.equal(await gu.getActiveSectionTitle(), 'FINANCIALSTABLE');
|
||
|
assert.match(await driver.getCurrentUrl(), /aclAsUser/);
|
||
|
|
||
|
// Revert.
|
||
|
await driver.find('.test-view-as-banner .test-revert').click();
|
||
|
await gu.waitForDocToLoad();
|
||
|
|
||
|
// Delete user attribute table.
|
||
|
await mainSession.createHomeApi().applyUserActions(docId, [
|
||
|
['RemoveRecord', '_grist_ACLRules', records.retValues[1]],
|
||
|
['RemoveRecord', '_grist_ACLResources', records.retValues[0]],
|
||
|
['RemoveTable', 'Zones'],
|
||
|
]);
|
||
|
});
|
||
|
|
||
|
it('should allow returning from view-as mode via View As banner', async function() {
|
||
|
// Check that we can revert view-as and visit Access Rules page with the button next to the
|
||
|
// page item.
|
||
|
await driver.get(viewAsUrl);
|
||
|
await gu.waitForUrl(/aclAsUser/);
|
||
|
await gu.waitForDocToLoad();
|
||
|
|
||
|
await driver.find('.test-view-as-banner .test-revert').click();
|
||
|
await gu.waitForDocToLoad();
|
||
|
assert.equal(await driver.find('.test-view-as-banner').isPresent(), false);
|
||
|
});
|
||
|
|
||
|
it('should allow switching users in view-as mode', async function() {
|
||
|
|
||
|
// open doc in view-as mode
|
||
|
await driver.get(viewAsUrl);
|
||
|
await gu.waitForUrl(/aclAsUser/);
|
||
|
await gu.waitForDocToLoad();
|
||
|
|
||
|
// check name
|
||
|
assert.match(await driver.find('.test-view-as-banner .test-select-open').getText(),
|
||
|
new RegExp(gu.translateUser('user3').name, 'i'));
|
||
|
|
||
|
// select other user
|
||
|
await driver.find('.test-view-as-banner .test-select-open').click();
|
||
|
await driver.findContent('.test-acl-user-item', gu.translateUser('user1').name).click();
|
||
|
|
||
|
// check name changed
|
||
|
await gu.waitForDocToLoad();
|
||
|
|
||
|
// check name updated correctly
|
||
|
assert.match(await driver.find('.test-view-as-banner .test-select-open').getText(), /Chimpy/);
|
||
|
});
|
||
|
|
||
|
it('should make all tables/columns available to editor of ACL rules', async function() {
|
||
|
const mainSession = await gu.session().teamSite.user('user1').login();
|
||
|
await mainSession.loadDoc(`/doc/${docId}`);
|
||
|
await driver.find('.test-tools-access-rules').click();
|
||
|
await driver.findWait('.test-rule-set', 2000);
|
||
|
|
||
|
// Add table rules for ClientsTable.
|
||
|
await driver.findContentWait('button', /Add Table Rules/, 2000).click();
|
||
|
const options = await driver.findAll('.grist-floating-menu li', e => e.getText());
|
||
|
assert.deepEqual(options, ["ClientsTable", "ClientsTable [by Shared]", "FinancialsTable"]);
|
||
|
await driver.findContent('.grist-floating-menu li', /ClientsTable/).click();
|
||
|
|
||
|
// Add rules hiding First_Name, Last_Name columns.
|
||
|
await findTable(/ClientsTable/).find('.test-rule-table-menu-btn').click();
|
||
|
await driver.findContent('.grist-floating-menu li', /Add Column Rule/).click();
|
||
|
let ruleSet = findRuleSet(/ClientsTable/, 1);
|
||
|
await ruleSet.find('.test-rule-resource .test-select-open').click();
|
||
|
await driver.findContent('.test-select-menu li', 'First_Name').click();
|
||
|
await ruleSet.find('.test-rule-resource .test-select-open').click();
|
||
|
await driver.findContent('.test-select-menu li', 'Last_Name').click();
|
||
|
await enterRulePart(ruleSet, 1, null, {R: 'deny'});
|
||
|
|
||
|
// Add table rule entirely hiding FinancialsTable.
|
||
|
await driver.findContentWait('button', /Add Table Rules/, 2000).click();
|
||
|
await driver.findContent('.grist-floating-menu li', /FinancialsTable/).click();
|
||
|
ruleSet = findDefaultRuleSet(/FinancialsTable/);
|
||
|
await enterRulePart(ruleSet, 1, null, {R: 'deny'});
|
||
|
|
||
|
// Save and reload.
|
||
|
await driver.find('.test-rules-save').click();
|
||
|
await gu.waitForServer();
|
||
|
await driver.navigate().refresh();
|
||
|
await driver.findWait('.test-rule-set', 5000);
|
||
|
|
||
|
// Now this user isn't aware of the inaccessible columns and table, but ACL rules should
|
||
|
// still list them.
|
||
|
|
||
|
// Remove Last_Name column from being blocked. Check that it's now available in dropdown.
|
||
|
await driver.findWait('.test-rule-set', 2000);
|
||
|
ruleSet = findRuleSet(/ClientsTable/, 1);
|
||
|
await ruleSet.find('.test-rule-resource').click();
|
||
|
await ruleSet.findContent('.test-acl-column', 'Last_Name').find('.test-acl-col-remove').click();
|
||
|
await ruleSet.find('.test-rule-resource .test-select-open').click();
|
||
|
assert.equal(await driver.findContent('.test-select-menu li', 'Last_Name').isPresent(), true);
|
||
|
await driver.sendKeys(Key.ESCAPE); // Close menu.
|
||
|
await driver.sendKeys(Key.ESCAPE); // Close editing of columns.
|
||
|
|
||
|
// Remove FinancialsTable from being blocked. Check that it's now available in dropdown.
|
||
|
ruleSet = findRuleSet(/FinancialsTable/, 1);
|
||
|
await ruleSet.find('.test-rule-part-and-memo:nth-child(1) .test-rule-remove').click();
|
||
|
await driver.findContentWait('button', /Add Table Rules/, 2000).click();
|
||
|
assert.equal(await driver.findContent('.grist-floating-menu li', /FinancialsTable/).isPresent(), true);
|
||
|
assert.equal(await driver.findContent('.grist-floating-menu li', /FinancialsTable/).matches('.disabled'), false);
|
||
|
await driver.sendKeys(Key.ESCAPE);
|
||
|
|
||
|
// Save
|
||
|
await driver.find('.test-rules-save').click();
|
||
|
await gu.waitForServer();
|
||
|
|
||
|
// Remove First_Name column from being blocked, and add back Last_Name column.
|
||
|
ruleSet = findRuleSet(/ClientsTable/, 1);
|
||
|
await ruleSet.find('.test-rule-resource').click();
|
||
|
await ruleSet.findContent('.test-acl-column', 'First_Name').find('.test-acl-col-remove').click();
|
||
|
await ruleSet.find('.test-rule-resource .test-select-open').click();
|
||
|
await driver.findContent('.test-select-menu li', 'Last_Name').click();
|
||
|
|
||
|
// Add back FinancialsTable to be blocked.
|
||
|
assert.equal(await findRuleSet(/FinancialsTable/, 1).isPresent(), false);
|
||
|
await driver.findContentWait('button', /Add Table Rules/, 2000).click();
|
||
|
await driver.findContent('.grist-floating-menu li', /FinancialsTable/).click();
|
||
|
ruleSet = findDefaultRuleSet(/FinancialsTable/);
|
||
|
await enterRulePart(ruleSet, 1, null, {R: 'deny'});
|
||
|
|
||
|
// Save and reload.
|
||
|
await driver.find('.test-rules-save').click();
|
||
|
await gu.waitForServer();
|
||
|
await driver.navigate().refresh();
|
||
|
await driver.findWait('.test-rule-set', 5000);
|
||
|
|
||
|
// Verify the results.
|
||
|
ruleSet = findRuleSet(/ClientsTable/, 1);
|
||
|
assert.deepEqual(await ruleSet.findAll('.test-acl-column', el => el.getText()), ['Last_Name']);
|
||
|
assert.equal(await findDefaultRuleSet(/FinancialsTable/).isPresent(), true);
|
||
|
|
||
|
// Remove the installed "Deny" rules to restore the initial state.
|
||
|
await findRuleSet(/ClientsTable/, 1).find('.test-rule-remove').click();
|
||
|
await findRuleSet(/FinancialsTable/, 1).find('.test-rule-remove').click();
|
||
|
await driver.find('.test-rules-save').click();
|
||
|
await gu.waitForServer();
|
||
|
});
|
||
|
|
||
|
it('should support user-attribute rules', async function() {
|
||
|
// Add a table to user for a user-attribute rule. User the API for test simplicity.
|
||
|
const mainSession = await gu.session().teamSite.user('user1').login();
|
||
|
const api = mainSession.createHomeApi();
|
||
|
await api.applyUserActions(docId, [
|
||
|
['AddTable', 'Access', [{id: 'Email'}, {id: 'SharedOnly', type: 'Bool'}]],
|
||
|
['AddRecord', 'Access', null, {Email: gu.translateUser('user1').email, SharedOnly: true}],
|
||
|
['AddRecord', 'Access', null, {Email: gu.translateUser('user2').email, SharedOnly: true}],
|
||
|
['AddRecord', 'Access', null, {Email: gu.translateUser('user3').email, SharedOnly: true}],
|
||
|
]);
|
||
|
|
||
|
// Now use the UI to add a user-attribute rule.
|
||
|
await mainSession.loadDoc(`/doc/${docId}`);
|
||
|
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("MyAccess", Key.ENTER);
|
||
|
|
||
|
// Type 'Email' into the attribute ace editor, which has 'user.' prefilled.
|
||
|
await userAttrRule.find('.test-rule-userattr-attr').click();
|
||
|
await driver.sendKeys("Email", Key.ENTER);
|
||
|
|
||
|
// Check that Table field offers a dropdown.
|
||
|
await userAttrRule.find('.test-rule-userattr-table').click();
|
||
|
assert.deepEqual(await driver.findAll('.test-select-menu li', el => el.getText()),
|
||
|
['Access', 'ClientsTable', 'ClientsTable [by Shared]', 'FinancialsTable']);
|
||
|
|
||
|
// Select a table and check that the Column field offers a dropdown.
|
||
|
await driver.findContent('.test-select-menu li', 'ClientsTable').click();
|
||
|
await userAttrRule.find('.test-rule-userattr-col').click();
|
||
|
assert.includeMembers(await driver.findAll('.test-select-menu li', el => el.getText()),
|
||
|
['Agent_Email', 'Email', 'First_Name']);
|
||
|
|
||
|
// Select a different table, and check that the Column field dropdown gets updated.
|
||
|
await userAttrRule.find('.test-rule-userattr-table').click();
|
||
|
await driver.sendKeys("Access", Key.ENTER);
|
||
|
await userAttrRule.find('.test-rule-userattr-col').click();
|
||
|
assert.deepEqual(await driver.findAll('.test-select-menu li', el => el.getText()),
|
||
|
['Email', 'SharedOnly', 'id']);
|
||
|
await driver.sendKeys("Email", Key.ENTER);
|
||
|
|
||
|
// Remove ClientTable rules, and add a new one using the new UserAttribute.
|
||
|
if (await findTable(/ClientsTable/).isPresent()) {
|
||
|
await findTable(/ClientsTable/).find('.test-rule-table-menu-btn').click();
|
||
|
await driver.findContent('.grist-floating-menu li', /Delete Table Rules/).click();
|
||
|
}
|
||
|
await driver.findContentWait('button', /Add Table Rules/, 2000).click();
|
||
|
await driver.findContent('.grist-floating-menu li', /ClientsTable/).click();
|
||
|
const ruleSet = findDefaultRuleSet(/ClientsTable/);
|
||
|
await ruleSet.find('.test-rule-part .test-rule-add').click();
|
||
|
// newRec term in the following does nothing, it is just there to test renaming later.
|
||
|
await enterRulePart(ruleSet, 1, `not user.MyAccess.SharedOnly or rec.Shared or newRec.Shared`,
|
||
|
{R: 'allow'});
|
||
|
await enterRulePart(ruleSet, 2, null, 'Deny All');
|
||
|
await driver.find('.test-rules-save').click();
|
||
|
await gu.waitToPass(async () => {
|
||
|
await gu.openPage('ClientsTable');
|
||
|
assert.equal(await gu.getGridRowCount(), 6);
|
||
|
});
|
||
|
|
||
|
// Now toggle the value in the Access table.
|
||
|
await gu.getPageItem('Access').click();
|
||
|
await gu.getCell({col: 'SharedOnly', rowNum: 1}).find('.widget_checkbox').click();
|
||
|
await gu.waitToPass(async () => {
|
||
|
await gu.openPage('ClientsTable');
|
||
|
assert.equal(await gu.getGridRowCount(), 20);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it('should allow adding an "Everyone Else" rule if one does not exist', async function() {
|
||
|
// Load the page with rules.
|
||
|
const mainSession = await gu.session().teamSite.user('user1').login();
|
||
|
await mainSession.loadDoc(`/doc/${docId}`);
|
||
|
await driver.find('.test-tools-access-rules').click();
|
||
|
await driver.findWait('.test-rule-set', 2000);
|
||
|
|
||
|
// ClientsTable rules, based on previous tests, includes an "Everyone Else" setting.
|
||
|
assert.isTrue(await findTable(/ClientsTable/).isPresent());
|
||
|
let ruleSet = findDefaultRuleSet(/ClientsTable/);
|
||
|
assert.lengthOf(await ruleSet.findAll('.test-rule-part'), 2);
|
||
|
assert.lengthOf(await ruleSet.findAll('.test-rule-add'), 2);
|
||
|
assert.equal(
|
||
|
await ruleSet.find('.test-rule-part-and-memo:nth-child(2) .test-rule-acl-formula').getText(),
|
||
|
"Everyone Else"
|
||
|
);
|
||
|
assert.equal(await ruleSet.find('.test-rule-extra-add').isPresent(), false);
|
||
|
|
||
|
// Delete it, and check that a plus button appears.
|
||
|
await ruleSet.find('.test-rule-part-and-memo:nth-child(2) .test-rule-remove').click();
|
||
|
assert.lengthOf(await ruleSet.findAll('.test-rule-part'), 1);
|
||
|
assert.lengthOf(await ruleSet.findAll('.test-rule-add'), 2);
|
||
|
assert.equal(await ruleSet.find('.test-rule-extra-add').isPresent(), true);
|
||
|
|
||
|
// Click "+" button, check that a rule appears.
|
||
|
await ruleSet.find('.test-rule-extra-add .test-rule-add').click();
|
||
|
assert.lengthOf(await ruleSet.findAll('.test-rule-part'), 2);
|
||
|
assert.lengthOf(await ruleSet.findAll('.test-rule-add'), 2);
|
||
|
assert.equal(
|
||
|
await ruleSet.find('.test-rule-part-and-memo:nth-child(2) .test-rule-acl-formula').getText(),
|
||
|
"Everyone Else"
|
||
|
);
|
||
|
assert.equal(await ruleSet.find('.test-rule-extra-add').isPresent(), false);
|
||
|
|
||
|
// Enter a condition into the new default rule.
|
||
|
await enterRulePart(ruleSet, 2, `True`, 'Deny All');
|
||
|
|
||
|
// A new "+" button should appear.
|
||
|
assert.lengthOf(await ruleSet.findAll('.test-rule-part'), 2);
|
||
|
assert.lengthOf(await ruleSet.findAll('.test-rule-add'), 3);
|
||
|
assert.equal(
|
||
|
await ruleSet.find('.test-rule-part-and-memo:nth-child(2) .test-rule-acl-formula').getText(),
|
||
|
"True"
|
||
|
);
|
||
|
assert.equal(await ruleSet.find('.test-rule-extra-add').isPresent(), true);
|
||
|
|
||
|
// Save
|
||
|
await driver.find('.test-rules-save').click();
|
||
|
await gu.waitForServer();
|
||
|
await driver.findWait('.test-rule-set', 2000);
|
||
|
|
||
|
// Check that the final "+" still appears.
|
||
|
ruleSet = findDefaultRuleSet(/ClientsTable/);
|
||
|
assert.lengthOf(await ruleSet.findAll('.test-rule-part'), 2);
|
||
|
assert.lengthOf(await ruleSet.findAll('.test-rule-add'), 3);
|
||
|
assert.equal(
|
||
|
await ruleSet.find('.test-rule-part-and-memo:nth-child(2) .test-rule-acl-formula').getText(),
|
||
|
"True"
|
||
|
);
|
||
|
assert.equal(await ruleSet.find('.test-rule-extra-add').isPresent(), true);
|
||
|
|
||
|
await gu.undo();
|
||
|
await driver.findWait('.test-rule-set', 2000);
|
||
|
|
||
|
ruleSet = findDefaultRuleSet(/ClientsTable/);
|
||
|
assert.lengthOf(await ruleSet.findAll('.test-rule-part'), 2);
|
||
|
assert.lengthOf(await ruleSet.findAll('.test-rule-add'), 2);
|
||
|
assert.equal(
|
||
|
await ruleSet.find('.test-rule-part-and-memo:nth-child(2) .test-rule-acl-formula').getText(),
|
||
|
"Everyone Else"
|
||
|
);
|
||
|
assert.equal(await ruleSet.find('.test-rule-extra-add').isPresent(), false);
|
||
|
});
|
||
|
|
||
|
it('should support renames and refresh rules when they get updated', async function() {
|
||
|
// Prepare to use the API.
|
||
|
const mainSession = await gu.session().teamSite.user('user1').login();
|
||
|
const api = mainSession.createHomeApi();
|
||
|
|
||
|
// Load the page with rules.
|
||
|
await mainSession.loadDoc(`/doc/${docId}`);
|
||
|
await driver.find('.test-tools-access-rules').click();
|
||
|
await driver.findWait('.test-rule-set', 2000);
|
||
|
|
||
|
// After the previous test case, we have these rules:
|
||
|
// - UserAttribute rule "MyAccess" that looks up user.Email in ClientsTable.Email.
|
||
|
// - On ClientsTable, rule allowing read when "not user.MyAccess.SharedOnly or rec.Shared ...".
|
||
|
// - On ClientsTable, default rule to Deny All.
|
||
|
|
||
|
// Check that it's what we see.
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-userattr-attr', getRuleText),
|
||
|
['user.Email']);
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-userattr .test-select-open', el => el.getText()),
|
||
|
['Access', 'Email']);
|
||
|
assert.match(await driver.find('.test-rule-table-header').getText(), / ClientsTable$/);
|
||
|
assert.lengthOf(await driver.findAll('.test-rule-set'), 2);
|
||
|
assert.deepEqual(await driver.find('.test-rule-set').findAll('.test-rule-acl-formula', getRuleText),
|
||
|
["not user.MyAccess.SharedOnly or rec.Shared or newRec.Shared", "Everyone Else"]);
|
||
|
|
||
|
// Rename some tables (raw view sections) and columns.
|
||
|
await api.applyUserActions(docId, [
|
||
|
['UpdateRecord', '_grist_Views_section', 3, {title: 'CLIENT LIST'}],
|
||
|
['UpdateRecord', '_grist_Views_section', 10, {title: 'ACCESS2'}],
|
||
|
['RenameColumn', 'CLIENT_LIST', 'Shared', 'PUBLIC'],
|
||
|
['RenameColumn', 'ACCESS2', 'Email', 'EMAIL_ADDR'],
|
||
|
['RenameColumn', 'ACCESS2', 'SharedOnly', 'ONLY_SHARED'],
|
||
|
]);
|
||
|
await gu.waitForServer();
|
||
|
|
||
|
// Rules should auto-update.
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-userattr .test-select-open', el => el.getText()),
|
||
|
['ACCESS2', 'EMAIL_ADDR']);
|
||
|
assert.match(await driver.find('.test-rule-table-header').getText(), / CLIENT LIST/);
|
||
|
assert.lengthOf(await driver.findAll('.test-rule-set'), 2);
|
||
|
assert.deepEqual(await driver.find('.test-rule-set').findAll('.test-rule-acl-formula', getRuleText),
|
||
|
["not user.MyAccess.ONLY_SHARED or rec.PUBLIC or newRec.PUBLIC", "Everyone Else"]);
|
||
|
|
||
|
// Table options should update
|
||
|
await driver.findContentWait('button', /Add Table Rules/, 2000).click();
|
||
|
const options = await driver.findAll('.grist-floating-menu li', e => e.getText());
|
||
|
assert.deepEqual(options, ["ACCESS2", "CLIENT LIST", "CLIENT LIST [by Shared]", "FinancialsTable"]);
|
||
|
await driver.sendKeys(Key.ESCAPE); // Close menu.
|
||
|
|
||
|
// Make an unsaved change to the rules, e.g. add a new one.
|
||
|
const ruleSet = findDefaultRuleSet(/CLIENT LIST/);
|
||
|
await ruleSet.find('.test-rule-part .test-rule-add').click();
|
||
|
await enterRulePart(ruleSet, 1, `True`, {R: 'allow'});
|
||
|
|
||
|
// Partially undo the renames.
|
||
|
await api.applyUserActions(docId, [
|
||
|
['UpdateRecord', '_grist_Views_section', 3, {title: 'ClientsTable'}],
|
||
|
['RenameColumn', 'ClientsTable', 'PUBLIC', 'Shared'],
|
||
|
]);
|
||
|
await gu.waitForServer();
|
||
|
|
||
|
// Rules should NOT auto-update, but show a message instead.
|
||
|
assert.match(await driver.find('.test-rule-table-header').getText(), / CLIENT LIST/);
|
||
|
assert.match(await driver.find('.test-access-rules-error').getText(), /Access rules have changed/);
|
||
|
|
||
|
// Click "Reset" to update them.
|
||
|
await driver.find('.test-rules-revert').click();
|
||
|
await gu.waitForServer();
|
||
|
|
||
|
// Finish undoing the renames. Fiddling with a user attribute table currently forces
|
||
|
// a reload.
|
||
|
await api.applyUserActions(docId, [
|
||
|
['UpdateRecord', '_grist_Views_section', 10, {title: 'Access'}],
|
||
|
['RenameColumn', 'Access', 'EMAIL_ADDR', 'Email'],
|
||
|
['RenameColumn', 'Access', 'ONLY_SHARED', 'SharedOnly'],
|
||
|
]);
|
||
|
await gu.waitForServer();
|
||
|
|
||
|
// Check results; it should be what we started with.
|
||
|
await gu.waitToPass(async () =>
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-userattr .test-select-open', el => el.getText()),
|
||
|
['Access', 'Email']));
|
||
|
assert.match(await driver.find('.test-rule-table-header').getText(), / ClientsTable$/);
|
||
|
assert.lengthOf(await driver.findAll('.test-rule-set'), 2);
|
||
|
assert.deepEqual(await driver.find('.test-rule-set').findAll('.test-rule-acl-formula', getRuleText),
|
||
|
["not user.MyAccess.SharedOnly or rec.Shared or newRec.Shared", "Everyone Else"]);
|
||
|
});
|
||
|
|
||
|
it('should support special rules', async function() {
|
||
|
// Prepare to use the API.
|
||
|
const mainSession = await gu.session().teamSite.user('user1').login();
|
||
|
|
||
|
// Load the page with rules.
|
||
|
await mainSession.loadDoc(`/doc/${docId}`);
|
||
|
await driver.find('.test-tools-access-rules').click();
|
||
|
await driver.findWait('.test-rule-set', 2000);
|
||
|
|
||
|
// Check that the special checkboxes are unchecked, and advanced UI is hidden.
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-special-checkbox', isChecked), [false, true, false, false]);
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-special', el => el.find('.test-rule-set').isPresent()),
|
||
|
[false, false, false, false]);
|
||
|
|
||
|
// Mark the 'Allow everyone to view Access Rules' checkbox and save.
|
||
|
await gu.scrollIntoView(driver.find('.test-rule-special-AccessRules .test-rule-special-checkbox')).click();
|
||
|
await driver.find('.test-rules-save').click();
|
||
|
await gu.waitForServer();
|
||
|
|
||
|
// Verify that it's checked after saving.
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-special-checkbox', isChecked), [false, true, true, false]);
|
||
|
|
||
|
// Expand advanced UI and delete the added rule there.
|
||
|
await gu.scrollIntoView(driver.find('.test-rule-special-AccessRules .test-rule-special-expand')).click();
|
||
|
await driver.find('.test-rule-special-AccessRules .test-rule-part-and-memo:nth-child(1) .test-rule-remove').click();
|
||
|
|
||
|
// The checkbox should now be unchecked.
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-special-checkbox', isChecked), [false, true, false, false]);
|
||
|
|
||
|
// Add a new non-standard rule
|
||
|
const ruleSet = await driver.find('.test-rule-special-AccessRules .test-rule-set');
|
||
|
await ruleSet.find('.test-rule-part .test-rule-add').click();
|
||
|
await enterRulePart(ruleSet, 1, 'user.Access == EDITOR', {R: 'allow'});
|
||
|
|
||
|
// The checkbox should now be disabled.
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-special-checkbox', isChecked), [false, true, false, false]);
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-special-checkbox', isDisabled), [false, false, true, false]);
|
||
|
|
||
|
// Mark the checkbox for the other rule.
|
||
|
await gu.scrollIntoView(driver.find('.test-rule-special-FullCopies .test-rule-special-checkbox')).click();
|
||
|
|
||
|
// Save and reload the page.
|
||
|
await driver.find('.test-rules-save').click();
|
||
|
await gu.waitForServer();
|
||
|
await driver.navigate().refresh();
|
||
|
await driver.findWait('.test-rule-set', 5000);
|
||
|
|
||
|
// Verify the state of the checkboxes.
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-special-checkbox', isChecked), [false, true, false, true]);
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-special-checkbox', isDisabled), [false, false, true, false]);
|
||
|
|
||
|
// Check that the advanced UI is shown for only the non-standard rule.
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-special', el => el.find('.test-rule-set').isPresent()),
|
||
|
[false, false, true, false]);
|
||
|
});
|
||
|
|
||
|
it('should allow opening rules when they refer to a deleted table', async function() {
|
||
|
// After deleting a table, an invalid rule will remain. This test is NOT saying that this is
|
||
|
// the desired behavior; it only checks that in the presence of such an invalid rule, we can
|
||
|
// still open the Access Rules page.
|
||
|
|
||
|
const mainSession = await gu.session().teamSite.user('user1').login();
|
||
|
await mainSession.loadDoc(`/doc/${docId}`);
|
||
|
await driver.find('.test-tools-access-rules').click();
|
||
|
await driver.findWait('.test-rule-set', 2000);
|
||
|
assert.match(await driver.find('.test-rule-table-header').getText(), / ClientsTable$/);
|
||
|
|
||
|
// TODO: Something seems wrong about being able to remove a table to which one has no
|
||
|
// update-record or delete-record access. On the other hand, in this situation, an undo of the
|
||
|
// delete does get blocked.
|
||
|
await gu.removePage('ClientsTable', {withData: true});
|
||
|
await gu.waitForServer();
|
||
|
await gu.checkForErrors();
|
||
|
await driver.navigate().refresh();
|
||
|
await driver.findWait('.test-rule-set', 5000);
|
||
|
assert.match(await driver.find('.test-rule-table-header').getText(), / #Invalid \(ClientsTable\)$/);
|
||
|
|
||
|
// Remove access rule
|
||
|
await driver.findContent('.test-rule-table-header', / #Invalid \(ClientsTable\)$/)
|
||
|
.find('.test-rule-table-menu-btn').click();
|
||
|
await driver.findContent('.grist-floating-menu li', /Delete/).click();
|
||
|
await driver.find('.test-rules-save').click();
|
||
|
await gu.waitForServer();
|
||
|
assert.isTrue(await driver.find('.test-rules-non-save').isPresent());
|
||
|
});
|
||
|
|
||
|
it('should offer fixes for rules referring to deleted tables/columns', async function() {
|
||
|
// Create some temporary tables.
|
||
|
const mainSession = await gu.session().teamSite.user('user1').login();
|
||
|
await mainSession.loadDoc(`/doc/${docId}`);
|
||
|
await gu.addNewTable('TmpTable1');
|
||
|
await gu.addNewTable('TmpTable2');
|
||
|
|
||
|
// Add a user attribute referencing a column we will soon delete.
|
||
|
const api = mainSession.createHomeApi();
|
||
|
await api.applyUserActions(docId, [
|
||
|
['AddRecord', '_grist_ACLResources', -1, {tableId: '*', colIds: '*'}],
|
||
|
['AddRecord', '_grist_ACLRules', null, {
|
||
|
resource: -1, userAttributes: JSON.stringify({
|
||
|
name: 'Zig',
|
||
|
tableId: 'TmpTable2',
|
||
|
charId: 'Email',
|
||
|
lookupColId: 'B',
|
||
|
}),
|
||
|
}],
|
||
|
]);
|
||
|
|
||
|
// Open access control rules.
|
||
|
await mainSession.loadDoc(`/doc/${docId}`);
|
||
|
await driver.find('.test-tools-access-rules').click();
|
||
|
await driver.findWait('.test-rule-set', 2000);
|
||
|
|
||
|
// Add a rule for TmpTable1.
|
||
|
await driver.findContentWait('button', /Add Table Rules/, 2000).click();
|
||
|
await driver.findContentWait('.grist-floating-menu li', /TmpTable1/, 3000).click();
|
||
|
let ruleSet = findDefaultRuleSet(/TmpTable1/);
|
||
|
await enterRulePart(ruleSet, 1, null, 'Allow All');
|
||
|
|
||
|
// Add a rule for columns of TmpTable2.
|
||
|
await driver.findContentWait('button', /Add Table Rules/, 2000).click();
|
||
|
await driver.findContentWait('.grist-floating-menu li', /TmpTable2/, 3000).click();
|
||
|
ruleSet = findDefaultRuleSet(/TmpTable2/);
|
||
|
await enterRulePart(ruleSet, 1, null, 'Allow All');
|
||
|
await findTable(/TmpTable2/).find('.test-rule-table-menu-btn').click();
|
||
|
await driver.findContent('.grist-floating-menu li', /Add Column Rule/).click();
|
||
|
ruleSet = findRuleSet(/TmpTable2/, 1);
|
||
|
await ruleSet.find('.test-rule-resource .test-select-open').click();
|
||
|
await driver.findContent('.test-select-menu li', 'A').click();
|
||
|
await ruleSet.find('.test-rule-resource .test-select-open').click();
|
||
|
await driver.findContent('.test-select-menu li', 'B').click();
|
||
|
await ruleSet.find('.test-rule-resource .test-select-open').click();
|
||
|
await driver.findContent('.test-select-menu li', 'C').click();
|
||
|
await enterRulePart(ruleSet, 1, 'True', {R: 'allow'});
|
||
|
|
||
|
// Save the rules.
|
||
|
await driver.find('.test-rules-save').click();
|
||
|
await gu.waitForServer();
|
||
|
|
||
|
// Now remove TmpTable1 and some columns of TmpTable2.
|
||
|
await gu.removePage('TmpTable1', {withData: true});
|
||
|
await api.applyUserActions(docId, [
|
||
|
['RemoveColumn', 'TmpTable2', 'B'],
|
||
|
['RemoveColumn', 'TmpTable2', 'C'],
|
||
|
]);
|
||
|
await driver.find('.test-tools-access-rules').click();
|
||
|
await driver.findWait('.test-rule-set', 5000);
|
||
|
await driver.navigate().refresh();
|
||
|
|
||
|
// Check the list of rules looks plausible.
|
||
|
await driver.findWait('.test-rule-set', 5000);
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-table-header', el => el.getText()),
|
||
|
['Rules for table #Invalid (TmpTable1)',
|
||
|
'Rules for table TmpTable2',
|
||
|
'Default Rules', 'Special Rules']);
|
||
|
|
||
|
// Press the buttons we expect to be offered for clean-up.
|
||
|
await driver.findContentWait('button', /Remove TmpTable1 rules/, 5000).click();
|
||
|
await driver.findContentWait('button', /Remove column B from TmpTable2 rules/, 5000).click();
|
||
|
await driver.findContentWait('button', /Remove column C from TmpTable2 rules/, 5000).click();
|
||
|
await driver.findContentWait('button', /Remove Zig user attribute/, 5000).click();
|
||
|
await driver.find('.test-rules-save').click();
|
||
|
await gu.waitForServer();
|
||
|
|
||
|
// Check the list of rules looks cleaner.
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-table-header', el => el.getText()),
|
||
|
['Rules for table TmpTable2', 'Default Rules', 'Special Rules']);
|
||
|
|
||
|
// Check only the remaining column is mentioned.
|
||
|
assert.deepEqual(await driver.findAll('.test-acl-column', el => el.getText()),
|
||
|
['A']);
|
||
|
|
||
|
// Remove TmpTable2.
|
||
|
await gu.removePage('TmpTable2', {withData: true});
|
||
|
await driver.find('.test-tools-access-rules').click();
|
||
|
await driver.findWait('.test-rule-set', 5000);
|
||
|
await driver.navigate().refresh();
|
||
|
|
||
|
// Press the clean-up button we are offered, then check that "reset" works,
|
||
|
// then press and save.
|
||
|
await driver.findWait('.test-rule-set', 5000);
|
||
|
await driver.findContentWait('button', /Remove TmpTable2 rules/, 5000).click();
|
||
|
await driver.find('.test-rules-revert').click();
|
||
|
await gu.waitForServer();
|
||
|
await driver.findContentWait('button', /Remove TmpTable2 rules/, 5000).click();
|
||
|
await driver.find('.test-rules-save').click();
|
||
|
await gu.waitForServer();
|
||
|
|
||
|
// Check the list of rules looks cleaner.
|
||
|
assert.deepEqual(await driver.findAll('.test-rule-table-header', el => el.getText()),
|
||
|
['Default Rules', 'Special Rules']);
|
||
|
});
|
||
|
|
||
|
it.skip('should prevent combination of Public Edit access with granular ACLs', async function() {
|
||
|
// Share doc with everyone as viewer.
|
||
|
const mainSession = await gu.session().teamSite.user('user1').login();
|
||
|
const doc = await mainSession.tempDoc(cleanup, 'ACL-Test.grist', {load: false});
|
||
|
const api = mainSession.createHomeApi();
|
||
|
await api.updateDocPermissions(doc.id, { users: { 'everyone@getgrist.com': 'viewers' } });
|
||
|
|
||
|
// Open rules. There should be no warning message.
|
||
|
await mainSession.loadDoc(`/doc/${doc.id}`);
|
||
|
await driver.find('.test-tools-access-rules').click();
|
||
|
await driver.findWait('.test-rule-set', 2000);
|
||
|
assert.equal(await driver.find('.test-access-rules-error').getText(), '');
|
||
|
|
||
|
// Add a rule, and save.
|
||
|
await driver.find('.test-rule-special-AccessRules .test-rule-special-checkbox').click();
|
||
|
await driver.find('.test-rules-save').click();
|
||
|
await gu.waitForServer();
|
||
|
|
||
|
// Check that it worked.
|
||
|
assert.isTrue(await isChecked(await driver.find('.test-rule-special-AccessRules .test-rule-special-checkbox')));
|
||
|
assert.equal(await driver.find('.test-rules-save').isDisplayed(), false);
|
||
|
assert.equal(await driver.find('.test-rules-non-save').getText(), 'Saved');
|
||
|
|
||
|
// Try to change public access to editor. It should downgrade to viewer with a warning toast.
|
||
|
await driver.find('.test-tb-share').click();
|
||
|
await driver.findContent('.test-tb-share-option', /Manage Users/).doClick();
|
||
|
await driver.findWait('.test-um-public-access', 3000).click();
|
||
|
assert.match(await driver.find('.test-um-public-member .test-um-member-role').getText(), /Viewer/);
|
||
|
await driver.find('.test-um-public-member .test-um-member-role').click();
|
||
|
await driver.findContent('.test-um-role-option', /Editor/).click();
|
||
|
await gu.saveAcls();
|
||
|
await gu.waitForServer();
|
||
|
|
||
|
const toast = driver.findWait('.test-notifier-toast-wrapper', 500);
|
||
|
assert.match(await toast.getText(), /incompatible.*Reduced to "Viewer"/i);
|
||
|
await toast.find('.test-notifier-toast-close').click(); // Close the toast.
|
||
|
|
||
|
// Remove the rule we added, and save.
|
||
|
await driver.find('.test-rule-special-AccessRules .test-rule-special-checkbox').click();
|
||
|
await driver.find('.test-rules-save').click();
|
||
|
await gu.waitForServer();
|
||
|
assert.isFalse(await isChecked(await driver.find('.test-rule-special-AccessRules .test-rule-special-checkbox')));
|
||
|
|
||
|
// Now change public access to editor. There should be no warning notifications.
|
||
|
await driver.find('.test-tb-share').click();
|
||
|
await driver.findContent('.test-tb-share-option', /Manage Users/).doClick();
|
||
|
await driver.findWait('.test-um-public-access', 3000).click();
|
||
|
assert.match(await driver.find('.test-um-public-member .test-um-member-role').getText(), /Viewer/);
|
||
|
await driver.find('.test-um-public-member .test-um-member-role').click();
|
||
|
await driver.findContent('.test-um-role-option', /Editor/).click();
|
||
|
await gu.saveAcls();
|
||
|
await gu.waitForServer();
|
||
|
assert.lengthOf(await gu.getToasts(), 0);
|
||
|
|
||
|
// Open rules. There should be a warning message.
|
||
|
await driver.find('.test-tools-access-rules').click();
|
||
|
assert.match(await driver.find('.test-access-rules-error').getText(),
|
||
|
/Public "Editor".*incompatible.*remove it or reduce to "Viewer"/i);
|
||
|
|
||
|
// Try to add a rule. We should not be able to save.
|
||
|
await driver.find('.test-rule-special-AccessRules .test-rule-special-checkbox').click();
|
||
|
assert.equal(await driver.find('.test-rules-save').isDisplayed(), false);
|
||
|
assert.equal(await driver.find('.test-rules-non-save').getText(), 'Invalid');
|
||
|
});
|
||
|
|
||
|
it("Should update save button every 1 second when editing a formula", async function() {
|
||
|
// Prepare to use the API.
|
||
|
const mainSession = await gu.session().teamSite.user('user1').login();
|
||
|
|
||
|
// Load the page with rules.
|
||
|
await mainSession.loadDoc(`/doc/${docId}`);
|
||
|
await driver.find('.test-tools-access-rules').click();
|
||
|
await driver.findWait('.test-rule-set', 2000);
|
||
|
|
||
|
// check the save button is in 'saved' state
|
||
|
assert.equal(await driver.find('.test-rules-non-save').getText(), 'Saved');
|
||
|
|
||
|
// add new rule
|
||
|
const ruleSet = findDefaultRuleSet('*');
|
||
|
await ruleSet.find('.test-rule-add').click();
|
||
|
|
||
|
// check the save button is still in 'saved' state
|
||
|
// (nothing useful to save yet)
|
||
|
assert.equal(await driver.find('.test-rules-non-save').getText(), 'Saved');
|
||
|
|
||
|
// start typing invalid formula
|
||
|
const part = ruleSet.find(`.test-rule-part`);
|
||
|
await part.findWait('.test-rule-acl-formula .ace_editor', 500);
|
||
|
await part.find('.test-rule-acl-formula').doClick();
|
||
|
await driver.findWait('.test-rule-acl-formula .ace_focus', 500);
|
||
|
await gu.sendKeys('fdsa');
|
||
|
|
||
|
// check save button is still in 'saved' state
|
||
|
// (UI hasn't caught up with user yet)
|
||
|
assert.equal(await driver.find('.test-rules-non-save').getText(), 'Saved');
|
||
|
|
||
|
// wait 1 second
|
||
|
await driver.sleep(1000);
|
||
|
|
||
|
// check the save button is now enabled
|
||
|
assert.equal(await driver.find('.test-rules-non-save').getText(), 'Invalid');
|
||
|
|
||
|
// click reset
|
||
|
await driver.find('.test-rules-revert').click();
|
||
|
});
|
||
|
|
||
|
it("should have a working dots menu", async function() {
|
||
|
const mainSession = await gu.session().teamSite.user('user1').login();
|
||
|
await mainSession.loadDoc(`/doc/${docId}/p/3`);
|
||
|
let url = '';
|
||
|
|
||
|
// check page is correct
|
||
|
url = await driver.getCurrentUrl();
|
||
|
assert.isTrue(url.includes('p/3'));
|
||
|
assert.equal(await gu.getCurrentPageName(), "Access");
|
||
|
|
||
|
await gu.openAccessRulesDropdown();
|
||
|
|
||
|
const [name1, name2, name3] = ['user1', 'user2', 'user3'].map(u => gu.translateUser(u as any).name)
|
||
|
.map(name => new RegExp(escapeRegExp(name), 'i'));
|
||
|
|
||
|
// All users the doc is shared with should be listed, with correct Access.
|
||
|
assert.equal(await driver.findContent('.grist-floating-menu a', name1).isPresent(), false);
|
||
|
assert.include(await driver.findContent('.grist-floating-menu a', name2).getText(), '(Owner)');
|
||
|
assert.include(await driver.findContent('.grist-floating-menu a', name3).getText(), '(Editor)');
|
||
|
|
||
|
await driver.findContent('.grist-floating-menu a', name3).click();
|
||
|
await gu.waitForUrl(/aclAsUser/);
|
||
|
await gu.waitForDocToLoad();
|
||
|
|
||
|
// Check for a tag in the doc header.
|
||
|
assert.equal(await driver.findWait('.test-view-as-banner', 2000).isPresent(), true);
|
||
|
|
||
|
// check doc is still on same page
|
||
|
url = await driver.getCurrentUrl();
|
||
|
assert.isTrue(url.includes('p/3'));
|
||
|
assert.equal(await gu.getCurrentPageName(), "Access");
|
||
|
|
||
|
// Revert.
|
||
|
await driver.find('.test-view-as-banner .test-revert').click();
|
||
|
await gu.waitForDocToLoad();
|
||
|
});
|
||
|
|
||
|
it('should keep dots menu in sync', async function() {
|
||
|
const checkAccess = async (role: string) => {
|
||
|
await gu.openAccessRulesDropdown();
|
||
|
await gu.waitToPass(async () => {
|
||
|
assert.include(await driver.findContent('.grist-floating-menu a', /kiwi/i).getText(), role);
|
||
|
});
|
||
|
await driver.sendKeys(Key.ESCAPE);
|
||
|
};
|
||
|
|
||
|
// open doc
|
||
|
const mainSession = await gu.session().teamSite.user('user1').login();
|
||
|
await mainSession.loadDoc(`/doc/${docId}`);
|
||
|
|
||
|
// check kiwi matches (Editor)
|
||
|
await checkAccess('(Editor)');
|
||
|
|
||
|
// update kiwi access to viewers
|
||
|
const api = mainSession.createHomeApi();
|
||
|
await api.updateDocPermissions(docId, { users: { [gu.translateUser('user3').email]: 'viewers' } });
|
||
|
|
||
|
// check kiwi matches (Viewer)
|
||
|
await checkAccess('(Viewer)');
|
||
|
});
|
||
|
|
||
|
it('should hide dots menu for users without ACL access', async function() {
|
||
|
// Login as user3 and open doc
|
||
|
const mainSession = gu.session().teamSite.user('user1');
|
||
|
await mainSession.teamSite.user('user3').login();
|
||
|
await mainSession.loadDoc(`/doc/${docId}`);
|
||
|
|
||
|
// Check absence of dots menu
|
||
|
await driver.find('.test-tools-access-rules').mouseMove();
|
||
|
await gu.waitToPass(async () => {
|
||
|
assert.equal(await driver.find('.test-tools-access-rules-trigger').isPresent(), true);
|
||
|
});
|
||
|
assert.equal(await driver.find('.test-tools-access-rules-trigger').isDisplayed(), false);
|
||
|
});
|
||
|
|
||
|
});
|