| /** |
| * @license |
| * Copyright 2017 Google LLC |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| import * as sinon from 'sinon'; |
| 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); |
| }); |
| }); |
| }); |
| }); |