| /** | |
| * @license | |
| * Copyright 2017 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import '../../../test/common-test-setup'; | |
| import './gr-access-section'; | |
| import { | |
| AccessPermissions, | |
| toSortedPermissionsArray, | |
| } from '../../../utils/access-util'; | |
| import {GrAccessSection} from './gr-access-section'; | |
| import {GitRef} from '../../../types/common'; | |
| import {queryAndAssert} from '../../../utils/common-util'; | |
| import {GrButton} from '../../shared/gr-button/gr-button'; | |
| import {fixture, html, assert} from '@open-wc/testing'; | |
| suite('gr-access-section tests', () => { | |
| let element: GrAccessSection; | |
| setup(async () => { | |
| element = await fixture<GrAccessSection>(html` | |
| <gr-access-section></gr-access-section> | |
| `); | |
| }); | |
| suite('unit tests', () => { | |
| setup(async () => { | |
| element.section = { | |
| id: 'refs/*' as GitRef, | |
| 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 submitted as is', | |
| '-2': 'This shall not be submitted', | |
| '+1': 'Looks good to me, but someone else must approve', | |
| '+2': 'Looks good to me, approved', | |
| }, | |
| default_value: 0, | |
| }, | |
| }; | |
| element.updateSection(); | |
| await element.updateComplete; | |
| }); | |
| test('render', () => { | |
| assert.shadowDom.equal( | |
| element, | |
| /* HTML */ ` | |
| <fieldset class="gr-form-styles" id="section"> | |
| <div id="mainContainer"> | |
| <div class="header"> | |
| <div class="name"> | |
| <h3 class="heading-3">Reference: refs/*</h3> | |
| <gr-button | |
| aria-disabled="false" | |
| id="editBtn" | |
| link="" | |
| role="button" | |
| tabindex="0" | |
| > | |
| <gr-icon icon="edit" id="icon" small filled></gr-icon> | |
| </gr-button> | |
| </div> | |
| <iron-input class="editRefInput"> | |
| <input class="editRefInput" type="text" /> | |
| </iron-input> | |
| <gr-button | |
| aria-disabled="false" | |
| id="deleteBtn" | |
| link="" | |
| role="button" | |
| tabindex="0" | |
| > | |
| Remove | |
| </gr-button> | |
| </div> | |
| <div class="sectionContent"> | |
| <gr-permission> </gr-permission> | |
| <div id="addPermission"> | |
| Add permission: | |
| <select id="permissionSelect"> | |
| <option value="label-Code-Review">Label Code-Review</option> | |
| <option value="labelAs-Code-Review"> | |
| Label Code-Review (On Behalf Of) | |
| </option> | |
| <option value="abandon">Abandon</option> | |
| <option value="addPatchSet">Add Patch Set</option> | |
| <option value="create">Create Reference</option> | |
| <option value="createSignedTag">Create Signed Tag</option> | |
| <option value="createTag">Create Annotated Tag</option> | |
| <option value="delete">Delete Reference</option> | |
| <option value="deleteChanges">Delete Changes</option> | |
| <option value="deleteOwnChanges">Delete Own Changes</option> | |
| <option value="editHashtags">Edit Hashtags</option> | |
| <option value="editTopicName">Edit Topic Name</option> | |
| <option value="forgeAuthor">Forge Author Identity</option> | |
| <option value="forgeCommitter"> | |
| Forge Committer Identity | |
| </option> | |
| <option value="forgeServerAsCommitter"> | |
| Forge Server Identity | |
| </option> | |
| <option value="owner">Owner</option> | |
| <option value="push">Push</option> | |
| <option value="pushMerge">Push Merge Commit</option> | |
| <option value="rebase">Rebase</option> | |
| <option value="removeReviewer">Remove Reviewer</option> | |
| <option value="revert">Revert</option> | |
| <option value="submit">Submit</option> | |
| <option value="submitAs">Submit (On Behalf Of)</option> | |
| <option value="toggleWipState"> | |
| Toggle Work In Progress State | |
| </option> | |
| <option value="viewPrivateChanges"> | |
| View Private Changes | |
| </option> | |
| </select> | |
| <gr-button | |
| aria-disabled="false" | |
| id="addBtn" | |
| link="" | |
| role="button" | |
| tabindex="0" | |
| > | |
| Add | |
| </gr-button> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="deletedContainer"> | |
| <span> Reference: refs/* was deleted </span> | |
| <gr-button | |
| aria-disabled="false" | |
| id="undoRemoveBtn" | |
| link="" | |
| role="button" | |
| tabindex="0" | |
| > | |
| Undo | |
| </gr-button> | |
| </div> | |
| </fieldset> | |
| ` | |
| ); | |
| }); | |
| test('updateSection', () => { | |
| // updateSection was called in setup, so just make assertions. | |
| const expectedPermissions = [ | |
| { | |
| id: 'read' as GitRef, | |
| 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(), expectedLabelOptions); | |
| }); | |
| test('handleAccessSaved', () => { | |
| assert.equal(element.originalId, 'refs/*' as GitRef); | |
| element.section!.id = 'refs/for/bar' as GitRef; | |
| element.handleAccessSaved(); | |
| assert.equal(element.originalId, 'refs/for/bar' as GitRef); | |
| }); | |
| test('computePermissions', () => { | |
| const capabilities = { | |
| push: { | |
| id: '', | |
| name: '', | |
| rules: {}, | |
| }, | |
| read: { | |
| id: '', | |
| name: '', | |
| rules: {}, | |
| }, | |
| }; | |
| const expectedPermissions = [ | |
| { | |
| id: 'push', | |
| value: { | |
| id: '', | |
| name: '', | |
| 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', | |
| }, | |
| }, | |
| ]; | |
| element.section = { | |
| id: 'refs/*' as GitRef, | |
| value: { | |
| permissions: { | |
| read: { | |
| rules: {}, | |
| }, | |
| }, | |
| }, | |
| }; | |
| // For global capabilities, just return the sorted array filtered by | |
| // existing permissions. | |
| element.section = { | |
| id: 'GLOBAL_CAPABILITIES' as GitRef, | |
| value: { | |
| permissions: { | |
| read: { | |
| rules: {}, | |
| }, | |
| }, | |
| }, | |
| }; | |
| element.capabilities = capabilities; | |
| assert.deepEqual(element.computePermissions(), expectedPermissions); | |
| // For everything else, include possible label values before filtering. | |
| element.section.id = 'refs/for/*' as GitRef; | |
| assert.deepEqual( | |
| element.computePermissions(), | |
| labelOptions | |
| .concat(toSortedPermissionsArray(AccessPermissions)) | |
| .filter(permission => permission.id !== 'read') | |
| ); | |
| }); | |
| test('computePermissionName', () => { | |
| element.section = { | |
| id: 'GLOBAL_CAPABILITIES' as GitRef, | |
| value: { | |
| permissions: { | |
| read: { | |
| rules: {}, | |
| }, | |
| }, | |
| }, | |
| }; | |
| let permission; | |
| permission = { | |
| id: 'administrateServer' as GitRef, | |
| value: {rules: {}}, | |
| }; | |
| assert.equal( | |
| element.computePermissionName(permission), | |
| element.capabilities![permission.id].name | |
| ); | |
| permission = { | |
| id: 'non-existent' as GitRef, | |
| value: {rules: {}}, | |
| }; | |
| assert.isUndefined(element.computePermissionName(permission)); | |
| element.section.id = 'refs/for/*' as GitRef; | |
| permission = { | |
| id: 'abandon' as GitRef, | |
| value: {rules: {}}, | |
| }; | |
| assert.equal( | |
| element.computePermissionName(permission), | |
| AccessPermissions[permission.id].name | |
| ); | |
| element.section.id = 'refs/for/*' as GitRef; | |
| permission = { | |
| id: 'label-Code-Review' as GitRef, | |
| value: { | |
| label: 'Code-Review', | |
| rules: {}, | |
| }, | |
| }; | |
| assert.equal( | |
| element.computePermissionName(permission), | |
| 'Label Code-Review' | |
| ); | |
| permission = { | |
| id: 'labelAs-Code-Review' as GitRef, | |
| value: { | |
| label: 'Code-Review', | |
| rules: {}, | |
| }, | |
| }; | |
| assert.equal( | |
| element.computePermissionName(permission), | |
| 'Label Code-Review (On Behalf Of)' | |
| ); | |
| permission = { | |
| id: 'removeLabel-Code-Review' as GitRef, | |
| value: { | |
| label: 'Code-Review', | |
| rules: {}, | |
| }, | |
| }; | |
| assert.equal( | |
| element.computePermissionName(permission), | |
| 'Remove Label Code-Review' | |
| ); | |
| }); | |
| test('computeSectionName', () => { | |
| // 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; | |
| element.section!.id = '' as GitRef; | |
| assert.equal(element.computeSectionName(), 'Reference: refs/heads/*'); | |
| assert.isTrue(element.editingRef); | |
| assert.equal(element.section!.id, 'refs/heads/*'); | |
| // Reset editing to false. | |
| element.editingRef = false; | |
| element.section!.id = 'GLOBAL_CAPABILITIES' as GitRef; | |
| assert.equal(element.computeSectionName(), 'Global Capabilities'); | |
| assert.isFalse(element.editingRef); | |
| element.section!.id = 'refs/for/*' as GitRef; | |
| assert.equal(element.computeSectionName(), 'Reference: refs/for/*'); | |
| assert.isFalse(element.editingRef); | |
| }); | |
| test('editReference', () => { | |
| element.editReference(); | |
| assert.isTrue(element.editingRef); | |
| }); | |
| test('computeSectionClass', () => { | |
| element.editingRef = false; | |
| element.canUpload = false; | |
| element.ownerOf = []; | |
| element.editing = false; | |
| element.deleted = false; | |
| assert.equal(element.computeSectionClass(), ''); | |
| element.editing = true; | |
| assert.equal(element.computeSectionClass(), ''); | |
| element.ownerOf = ['refs/*' as GitRef]; | |
| assert.equal(element.computeSectionClass(), 'editing'); | |
| element.ownerOf = []; | |
| element.canUpload = true; | |
| assert.equal(element.computeSectionClass(), 'editing'); | |
| element.editingRef = true; | |
| assert.equal(element.computeSectionClass(), 'editing editingRef'); | |
| element.deleted = true; | |
| assert.equal(element.computeSectionClass(), 'editing editingRef deleted'); | |
| element.editingRef = false; | |
| assert.equal(element.computeSectionClass(), 'editing deleted'); | |
| }); | |
| }); | |
| suite('interactive tests', () => { | |
| setup(() => { | |
| element.labels = { | |
| 'Code-Review': { | |
| values: { | |
| ' 0': 'No score', | |
| '-1': 'I would prefer this is not submitted as is', | |
| '-2': 'This shall not be submitted', | |
| '+1': 'Looks good to me, but someone else must approve', | |
| '+2': 'Looks good to me, approved', | |
| }, | |
| default_value: 0, | |
| }, | |
| }; | |
| }); | |
| suite('Global section', () => { | |
| setup(async () => { | |
| element.section = { | |
| id: 'GLOBAL_CAPABILITIES' as GitRef, | |
| 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(); | |
| await element.updateComplete; | |
| }); | |
| test('classes are assigned correctly', () => { | |
| assert.isFalse( | |
| queryAndAssert<HTMLFieldSetElement>( | |
| element, | |
| '#section' | |
| ).classList.contains('editing') | |
| ); | |
| assert.isFalse( | |
| queryAndAssert<HTMLFieldSetElement>( | |
| element, | |
| '#section' | |
| ).classList.contains('deleted') | |
| ); | |
| assert.isTrue( | |
| queryAndAssert<GrButton>(element, '#editBtn').classList.contains( | |
| 'global' | |
| ) | |
| ); | |
| element.editing = true; | |
| element.canUpload = true; | |
| element.ownerOf = []; | |
| assert.equal( | |
| getComputedStyle(queryAndAssert<GrButton>(element, '#editBtn')) | |
| .display, | |
| 'none' | |
| ); | |
| }); | |
| }); | |
| suite('Non-global section', () => { | |
| setup(async () => { | |
| element.section = { | |
| id: 'refs/*' as GitRef, | |
| value: { | |
| permissions: { | |
| read: { | |
| rules: {}, | |
| }, | |
| }, | |
| }, | |
| }; | |
| element.capabilities = {}; | |
| element.updateSection(); | |
| await element.updateComplete; | |
| }); | |
| test('classes are assigned correctly', async () => { | |
| assert.isFalse( | |
| queryAndAssert<HTMLFieldSetElement>( | |
| element, | |
| '#section' | |
| ).classList.contains('editing') | |
| ); | |
| assert.isFalse( | |
| queryAndAssert<HTMLFieldSetElement>( | |
| element, | |
| '#section' | |
| ).classList.contains('deleted') | |
| ); | |
| assert.isFalse( | |
| queryAndAssert<GrButton>(element, '#editBtn').classList.contains( | |
| 'global' | |
| ) | |
| ); | |
| element.editing = true; | |
| element.canUpload = true; | |
| element.ownerOf = []; | |
| await element.updateComplete; | |
| assert.notEqual( | |
| getComputedStyle(queryAndAssert<GrButton>(element, '#editBtn')) | |
| .display, | |
| 'none' | |
| ); | |
| }); | |
| test('add permission', async () => { | |
| element.editing = true; | |
| queryAndAssert<HTMLSelectElement>(element, '#permissionSelect').value = | |
| 'label-Code-Review'; | |
| assert.equal(element.permissions!.length, 1); | |
| assert.equal(Object.keys(element.section!.value.permissions).length, 1); | |
| queryAndAssert<GrButton>(element, '#addBtn').click(); | |
| await element.updateComplete; | |
| // The permission is added to both the permissions array and also | |
| // the section's permission object. | |
| assert.equal(element.permissions!.length, 2); | |
| let permission; | |
| permission = { | |
| id: 'label-Code-Review' as GitRef, | |
| 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 | |
| ); | |
| queryAndAssert<HTMLSelectElement>(element, '#permissionSelect').value = | |
| 'abandon'; | |
| queryAndAssert<GrButton>(element, '#addBtn').click(); | |
| await element.updateComplete; | |
| permission = { | |
| id: 'abandon' as GitRef, | |
| 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; | |
| await element.updateComplete; | |
| assert.equal(element.permissions!.length, 1); | |
| assert.equal(Object.keys(element.section!.value.permissions).length, 1); | |
| }); | |
| test('edit section reference', async () => { | |
| element.canUpload = true; | |
| element.ownerOf = []; | |
| element.section = { | |
| id: 'refs/for/bar' as GitRef, | |
| value: {permissions: {}}, | |
| }; | |
| await element.updateComplete; | |
| assert.isFalse( | |
| queryAndAssert<HTMLFieldSetElement>( | |
| element, | |
| '#section' | |
| ).classList.contains('editing') | |
| ); | |
| element.editing = true; | |
| await element.updateComplete; | |
| assert.isTrue( | |
| queryAndAssert<HTMLFieldSetElement>( | |
| element, | |
| '#section' | |
| ).classList.contains('editing') | |
| ); | |
| assert.isFalse(element.editingRef); | |
| queryAndAssert<GrButton>(element, '#editBtn').click(); | |
| element.editRefInput().bindValue = 'new/ref'; | |
| await element.updateComplete; | |
| assert.equal(element.section.id, 'new/ref'); | |
| assert.isTrue(element.editingRef); | |
| assert.isTrue( | |
| queryAndAssert<HTMLFieldSetElement>( | |
| element, | |
| '#section' | |
| ).classList.contains('editingRef') | |
| ); | |
| element.editing = false; | |
| await element.updateComplete; | |
| assert.isFalse(element.editingRef); | |
| assert.equal(element.section.id, 'refs/for/bar'); | |
| }); | |
| test('handleValueChange', async () => { | |
| // For an existing section. | |
| const modifiedHandler = sinon.stub(); | |
| element.section = { | |
| id: 'refs/for/bar' as GitRef, | |
| value: {permissions: {}}, | |
| }; | |
| await element.updateComplete; | |
| assert.notOk(element.section.value.updatedId); | |
| element.section.id = 'refs/for/baz' as GitRef; | |
| await element.updateComplete; | |
| 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' as GitRef; | |
| await element.updateComplete; | |
| element.handleValueChange(); | |
| assert.isFalse(element.section.value.modified); | |
| assert.equal(modifiedHandler.callCount, 2); | |
| // For a new section. | |
| element.section.value.added = true; | |
| await element.updateComplete; | |
| element.handleValueChange(); | |
| assert.isFalse(element.section.value.modified); | |
| assert.equal(modifiedHandler.callCount, 2); | |
| element.section.id = 'refs/for/bar' as GitRef; | |
| await element.updateComplete; | |
| element.handleValueChange(); | |
| assert.isFalse(element.section.value.modified); | |
| assert.equal(modifiedHandler.callCount, 2); | |
| }); | |
| test('remove section', async () => { | |
| element.editing = true; | |
| element.canUpload = true; | |
| element.ownerOf = []; | |
| await element.updateComplete; | |
| assert.isFalse(element.deleted); | |
| assert.isNotOk(element.section!.value.deleted); | |
| queryAndAssert<GrButton>(element, '#deleteBtn').click(); | |
| await element.updateComplete; | |
| assert.isTrue(element.deleted); | |
| assert.isTrue(element.section!.value.deleted); | |
| assert.isTrue( | |
| queryAndAssert<HTMLFieldSetElement>( | |
| element, | |
| '#section' | |
| ).classList.contains('deleted') | |
| ); | |
| assert.isTrue(element.section!.value.deleted); | |
| queryAndAssert<GrButton>(element, '#undoRemoveBtn').click(); | |
| await element.updateComplete; | |
| assert.isFalse(element.deleted); | |
| assert.isNotOk(element.section!.value.deleted); | |
| queryAndAssert<GrButton>(element, '#deleteBtn').click(); | |
| await element.updateComplete; | |
| assert.isTrue(element.deleted); | |
| assert.isTrue(element.section!.value.deleted); | |
| element.editing = false; | |
| await element.updateComplete; | |
| assert.isFalse(element.deleted); | |
| assert.isNotOk(element.section!.value.deleted); | |
| }); | |
| test('removing an added permission', async () => { | |
| element.editing = true; | |
| await element.updateComplete; | |
| assert.equal(element.permissions!.length, 1); | |
| element.shadowRoot!.querySelector('gr-permission')!.dispatchEvent( | |
| new CustomEvent('added-permission-removed', { | |
| composed: true, | |
| bubbles: true, | |
| }) | |
| ); | |
| await element.updateComplete; | |
| assert.equal(element.permissions!.length, 0); | |
| }); | |
| test('remove an added section', async () => { | |
| const removeStub = sinon.stub(); | |
| element.addEventListener('added-section-removed', removeStub); | |
| element.editing = true; | |
| element.section!.value.added = true; | |
| await element.updateComplete; | |
| queryAndAssert<GrButton>(element, '#deleteBtn').click(); | |
| await element.updateComplete; | |
| assert.isTrue(removeStub.called); | |
| }); | |
| }); | |
| }); | |
| }); |