/** * @license * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import '../../../test/common-test-setup-karma.js'; import './gr-access-section.js'; import {AccessPermissions, toSortedPermissionsArray} from '../../../utils/access-util.js'; const fixture = fixtureFromElement('gr-access-section'); suite('gr-access-section tests', () => { let element; setup(() => { element = fixture.instantiate(); }); suite('unit tests', () => { setup(() => { element.section = { id: 'refs/*', value: { permissions: { read: { rules: {}, }, }, }, }; element.capabilities = { accessDatabase: { id: 'accessDatabase', name: 'Access Database', }, administrateServer: { id: 'administrateServer', name: 'Administrate Server', }, batchChangesLimit: { id: 'batchChangesLimit', name: 'Batch Changes Limit', }, createAccount: { id: 'createAccount', name: 'Create Account', }, }; element.labels = { 'Code-Review': { values: { ' 0': 'No score', '-1': 'I would prefer this is not merged as is', '-2': 'This shall not be merged', '+1': 'Looks good to me, but someone else must approve', '+2': 'Looks good to me, approved', }, default_value: 0, }, }; element._updateSection(element.section); flushAsynchronousOperations(); }); test('_updateSection', () => { // _updateSection was called in setup, so just make assertions. const expectedPermissions = [ { id: 'read', value: { rules: {}, }, }, ]; assert.deepEqual(element._permissions, expectedPermissions); assert.equal(element._originalId, element.section.id); }); test('_computeLabelOptions', () => { const expectedLabelOptions = [ { id: 'label-Code-Review', value: { name: 'Label Code-Review', id: 'label-Code-Review', }, }, { id: 'labelAs-Code-Review', value: { name: 'Label Code-Review (On Behalf Of)', id: 'labelAs-Code-Review', }, }, ]; assert.deepEqual(element._computeLabelOptions(element.labels), expectedLabelOptions); }); test('_handleAccessSaved', () => { assert.equal(element._originalId, 'refs/*'); element.section.id = 'refs/for/bar'; element._handleAccessSaved(); assert.equal(element._originalId, 'refs/for/bar'); }); test('_computePermissions', () => { const capabilities = { push: { rules: {}, }, read: { rules: {}, }, }; const expectedPermissions = [{ id: 'push', value: { rules: {}, }, }, ]; const labelOptions = [ { id: 'label-Code-Review', value: { name: 'Label Code-Review', id: 'label-Code-Review', }, }, { id: 'labelAs-Code-Review', value: { name: 'Label Code-Review (On Behalf Of)', id: 'labelAs-Code-Review', }, }, ]; // For global capabilities, just return the sorted array filtered by // existing permissions. let name = 'GLOBAL_CAPABILITIES'; assert.deepEqual(element._computePermissions(name, capabilities, element.labels), expectedPermissions); // For everything else, include possible label values before filtering. name = 'refs/for/*'; assert.deepEqual( element._computePermissions(name, capabilities, element.labels), labelOptions .concat(toSortedPermissionsArray(AccessPermissions)) .filter(permission => permission.id !== 'read')); }); test('_computePermissionName', () => { let name = 'GLOBAL_CAPABILITIES'; let permission = { id: 'administrateServer', value: {}, }; assert.equal(element._computePermissionName(name, permission, element.capabilities), element.capabilities[permission.id].name); name = 'refs/for/*'; permission = { id: 'abandon', value: {}, }; assert.equal(element._computePermissionName( name, permission, element.capabilities), AccessPermissions[permission.id].name); name = 'refs/for/*'; permission = { id: 'label-Code-Review', value: { label: 'Code-Review', }, }; assert.equal(element._computePermissionName(name, permission, element.capabilities), 'Label Code-Review'); permission = { id: 'labelAs-Code-Review', value: { label: 'Code-Review', }, }; assert.equal(element._computePermissionName(name, permission, element.capabilities), 'Label Code-Review(On Behalf Of)'); }); test('_computeSectionName', () => { let name; // When computing the section name for an undefined name, it means a // new section is being added. In this case, it should default to // 'refs/heads/*'. element._editingRef = false; assert.equal(element._computeSectionName(name), 'Reference: refs/heads/*'); assert.isTrue(element._editingRef); assert.equal(element.section.id, 'refs/heads/*'); // Reset editing to false. element._editingRef = false; name = 'GLOBAL_CAPABILITIES'; assert.equal(element._computeSectionName(name), 'Global Capabilities'); assert.isFalse(element._editingRef); name = 'refs/for/*'; assert.equal(element._computeSectionName(name), 'Reference: refs/for/*'); assert.isFalse(element._editingRef); }); test('editReference', () => { element.editReference(); assert.isTrue(element._editingRef); }); test('_computeSectionClass', () => { let editingRef = false; let canUpload = false; let ownerOf = []; let editing = false; let deleted = false; assert.equal(element._computeSectionClass(editing, canUpload, ownerOf, editingRef, deleted), ''); editing = true; assert.equal(element._computeSectionClass(editing, canUpload, ownerOf, editingRef, deleted), ''); ownerOf = ['refs/*']; assert.equal(element._computeSectionClass(editing, canUpload, ownerOf, editingRef, deleted), 'editing'); ownerOf = []; canUpload = true; assert.equal(element._computeSectionClass(editing, canUpload, ownerOf, editingRef, deleted), 'editing'); editingRef = true; assert.equal(element._computeSectionClass(editing, canUpload, ownerOf, editingRef, deleted), 'editing editingRef'); deleted = true; assert.equal(element._computeSectionClass(editing, canUpload, ownerOf, editingRef, deleted), 'editing editingRef deleted'); editingRef = false; assert.equal(element._computeSectionClass(editing, canUpload, ownerOf, editingRef, deleted), 'editing deleted'); }); test('_computeEditBtnClass', () => { let name = 'GLOBAL_CAPABILITIES'; assert.equal(element._computeEditBtnClass(name), 'global'); name = 'refs/for/*'; assert.equal(element._computeEditBtnClass(name), ''); }); }); suite('interactive tests', () => { setup(() => { element.labels = { 'Code-Review': { values: { ' 0': 'No score', '-1': 'I would prefer this is not merged as is', '-2': 'This shall not be merged', '+1': 'Looks good to me, but someone else must approve', '+2': 'Looks good to me, approved', }, default_value: 0, }, }; }); suite('Global section', () => { setup(() => { element.section = { id: 'GLOBAL_CAPABILITIES', value: { permissions: { accessDatabase: { rules: {}, }, }, }, }; element.capabilities = { accessDatabase: { id: 'accessDatabase', name: 'Access Database', }, administrateServer: { id: 'administrateServer', name: 'Administrate Server', }, batchChangesLimit: { id: 'batchChangesLimit', name: 'Batch Changes Limit', }, createAccount: { id: 'createAccount', name: 'Create Account', }, }; element._updateSection(element.section); flushAsynchronousOperations(); }); test('classes are assigned correctly', () => { assert.isFalse(element.$.section.classList.contains('editing')); assert.isFalse(element.$.section.classList.contains('deleted')); assert.isTrue(element.$.editBtn.classList.contains('global')); element.editing = true; element.canUpload = true; element.ownerOf = []; assert.equal(getComputedStyle(element.$.editBtn).display, 'none'); }); }); suite('Non-global section', () => { setup(() => { element.section = { id: 'refs/*', value: { permissions: { read: { rules: {}, }, }, }, }; element.capabilities = {}; element._updateSection(element.section); flushAsynchronousOperations(); }); test('classes are assigned correctly', () => { assert.isFalse(element.$.section.classList.contains('editing')); assert.isFalse(element.$.section.classList.contains('deleted')); assert.isFalse(element.$.editBtn.classList.contains('global')); element.editing = true; element.canUpload = true; element.ownerOf = []; flushAsynchronousOperations(); assert.notEqual(getComputedStyle(element.$.editBtn).display, 'none'); }); test('add permission', () => { element.editing = true; element.$.permissionSelect.value = 'label-Code-Review'; assert.equal(element._permissions.length, 1); assert.equal(Object.keys(element.section.value.permissions).length, 1); MockInteractions.tap(element.$.addBtn); flushAsynchronousOperations(); // The permission is added to both the permissions array and also // the section's permission object. assert.equal(element._permissions.length, 2); let permission = { id: 'label-Code-Review', value: { added: true, label: 'Code-Review', rules: {}, }, }; assert.equal(element._permissions.length, 2); assert.deepEqual(element._permissions[1], permission); assert.equal(Object.keys(element.section.value.permissions).length, 2); assert.deepEqual( element.section.value.permissions['label-Code-Review'], permission.value); element.$.permissionSelect.value = 'abandon'; MockInteractions.tap(element.$.addBtn); flushAsynchronousOperations(); permission = { id: 'abandon', value: { added: true, rules: {}, }, }; assert.equal(element._permissions.length, 3); assert.deepEqual(element._permissions[2], permission); assert.equal(Object.keys(element.section.value.permissions).length, 3); assert.deepEqual(element.section.value.permissions['abandon'], permission.value); // Unsaved changes are discarded when editing is cancelled. element.editing = false; assert.equal(element._permissions.length, 1); assert.equal(Object.keys(element.section.value.permissions).length, 1); }); test('edit section reference', done => { element.canUpload = true; element.ownerOf = []; element.section = {id: 'refs/for/bar', value: {permissions: {}}}; assert.isFalse(element.$.section.classList.contains('editing')); element.editing = true; assert.isTrue(element.$.section.classList.contains('editing')); assert.isFalse(element._editingRef); MockInteractions.tap(element.$.editBtn); element.editRefInput().bindValue='new/ref'; setTimeout(() => { assert.equal(element.section.id, 'new/ref'); assert.isTrue(element._editingRef); assert.isTrue(element.$.section.classList.contains('editingRef')); element.editing = false; assert.isFalse(element._editingRef); assert.equal(element.section.id, 'refs/for/bar'); done(); }); }); test('_handleValueChange', () => { // For an existing section. const modifiedHandler = sinon.stub(); element.section = {id: 'refs/for/bar', value: {permissions: {}}}; assert.notOk(element.section.value.updatedId); element.section.id = 'refs/for/baz'; element.addEventListener('access-modified', modifiedHandler); assert.isNotOk(element.section.value.modified); element._handleValueChange(); assert.equal(element.section.value.updatedId, 'refs/for/baz'); assert.isTrue(element.section.value.modified); assert.equal(modifiedHandler.callCount, 1); element.section.id = 'refs/for/bar'; element._handleValueChange(); assert.isFalse(element.section.value.modified); assert.equal(modifiedHandler.callCount, 2); // For a new section. element.section.value.added = true; element._handleValueChange(); assert.isFalse(element.section.value.modified); assert.equal(modifiedHandler.callCount, 2); element.section.id = 'refs/for/bar'; element._handleValueChange(); assert.isFalse(element.section.value.modified); assert.equal(modifiedHandler.callCount, 2); }); test('remove section', () => { element.editing = true; element.canUpload = true; element.ownerOf = []; assert.isFalse(element._deleted); assert.isNotOk(element.section.value.deleted); MockInteractions.tap(element.$.deleteBtn); flushAsynchronousOperations(); assert.isTrue(element._deleted); assert.isTrue(element.section.value.deleted); assert.isTrue(element.$.section.classList.contains('deleted')); assert.isTrue(element.section.value.deleted); MockInteractions.tap(element.$.undoRemoveBtn); flushAsynchronousOperations(); assert.isFalse(element._deleted); assert.isNotOk(element.section.value.deleted); MockInteractions.tap(element.$.deleteBtn); assert.isTrue(element._deleted); assert.isTrue(element.section.value.deleted); element.editing = false; assert.isFalse(element._deleted); assert.isNotOk(element.section.value.deleted); }); test('removing an added permission', () => { element.editing = true; assert.equal(element._permissions.length, 1); element.shadowRoot .querySelector('gr-permission').dispatchEvent( new CustomEvent('added-permission-removed', { composed: true, bubbles: true, })); flushAsynchronousOperations(); assert.equal(element._permissions.length, 0); }); test('remove an added section', () => { const removeStub = sinon.stub(); element.addEventListener('added-section-removed', removeStub); element.editing = true; element.section.value.added = true; MockInteractions.tap(element.$.deleteBtn); assert.isTrue(removeStub.called); }); }); }); });