blob: 60bcf8aa3fe4b92fa776a75055668d06c9aa5edf [file] [log] [blame]
/**
* @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);
});
});
});
});