blob: 88961ed77172378bb229c577dec8db931f933c34 [file] [log] [blame]
/**
* @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';
import './gr-access-section';
import {
AccessPermissions,
toSortedPermissionsArray,
} from '../../../utils/access-util';
import {GrAccessSection} from './gr-access-section';
import {GitRef} from '../../../types/common';
import * as MockInteractions from '@polymer/iron-test-helpers/mock-interactions';
const fixture = fixtureFromElement('gr-access-section');
suite('gr-access-section tests', () => {
let element: GrAccessSection;
setup(() => {
element = fixture.instantiate();
});
suite('unit tests', () => {
setup(() => {
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(element.section);
flush();
});
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(element.labels),
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',
},
},
];
// 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;
permission = {
id: 'administrateServer' as GitRef,
value: {rules: {}},
};
assert.equal(
element._computePermissionName(name, permission, element.capabilities),
element.capabilities![permission.id].name
);
name = 'refs/for/*';
permission = {
id: 'abandon' as GitRef,
value: {rules: {}},
};
assert.equal(
element._computePermissionName(name, permission, element.capabilities),
AccessPermissions[permission.id].name
);
name = 'refs/for/*';
permission = {
id: 'label-Code-Review' as GitRef,
value: {
label: 'Code-Review',
rules: {},
},
};
assert.equal(
element._computePermissionName(name, permission, element.capabilities),
'Label Code-Review'
);
permission = {
id: 'labelAs-Code-Review' as GitRef,
value: {
label: 'Code-Review',
rules: {},
},
};
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: GitRef[] | undefined = [];
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/*' as GitRef];
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 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(() => {
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(element.section);
flush();
});
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/*' as GitRef,
value: {
permissions: {
read: {
rules: {},
},
},
},
};
element.capabilities = {};
element._updateSection(element.section);
flush();
});
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 = [];
flush();
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);
flush();
// 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
);
element.$.permissionSelect.value = 'abandon';
MockInteractions.tap(element.$.addBtn);
flush();
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;
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: {}},
};
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';
await flush();
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');
});
test('_handleValueChange', () => {
// For an existing section.
const modifiedHandler = sinon.stub();
element.section = {
id: 'refs/for/bar' as GitRef,
value: {permissions: {}},
};
assert.notOk(element.section.value.updatedId);
element.section.id = 'refs/for/baz' as GitRef;
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;
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' as GitRef;
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);
flush();
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);
flush();
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,
})
);
flush();
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);
});
});
});
});