summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaladox none <thomasmulhall410@yahoo.com>2022-04-21 13:10:43 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2022-04-21 13:10:43 +0000
commit5b17467c95c5b56b5b2ebcec38b0363ad39a2d65 (patch)
tree1d1c9ec06b9929a552bee9ef6135eb0d4a3bfe70
parent8b5b9c50eb8f4cfe5233bf186559cee1cd33ae8a (diff)
parentfe69211ee01bdff2138802a51e450b9c8c3b1084 (diff)
Merge changes Ifd3c323b,Ia8a19a22 into stable-3.6
* changes: Convert gr-repo-access to lit Migrate gr-access-section to lit
-rw-r--r--polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.ts398
-rw-r--r--polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_html.ts160
-rw-r--r--polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.ts437
-rw-r--r--polygerrit-ui/app/elements/admin/gr-permission/gr-permission.ts14
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts511
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_html.ts151
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.ts498
7 files changed, 1124 insertions, 1045 deletions
diff --git a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.ts b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.ts
index be6f56e573..8fa2e90042 100644
--- a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.ts
+++ b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.ts
@@ -14,22 +14,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
import '@polymer/iron-input/iron-input';
-import '../../../styles/gr-font-styles';
-import '../../../styles/gr-form-styles';
-import '../../../styles/shared-styles';
import '../../shared/gr-button/gr-button';
import '../../shared/gr-icons/gr-icons';
import '../gr-permission/gr-permission';
-import {PolymerElement} from '@polymer/polymer/polymer-element';
-import {htmlTemplate} from './gr-access-section_html';
import {
AccessPermissions,
PermissionArray,
PermissionArrayItem,
toSortedPermissionsArray,
} from '../../../utils/access-util';
-import {customElement, property} from '@polymer/decorators';
import {
EditablePermissionInfo,
PermissionAccessSection,
@@ -41,10 +36,15 @@ import {
LabelNameToLabelTypeInfoMap,
RepoName,
} from '../../../types/common';
-import {PolymerDomRepeatEvent} from '../../../types/types';
-import {fireEvent} from '../../../utils/event-util';
-import {GrButton} from '../../shared/gr-button/gr-button';
+import {fire, fireEvent} from '../../../utils/event-util';
import {IronInputElement} from '@polymer/iron-input/iron-input';
+import {fontStyles} from '../../../styles/gr-font-styles';
+import {formStyles} from '../../../styles/gr-form-styles';
+import {sharedStyles} from '../../../styles/shared-styles';
+import {LitElement, PropertyValues, html, css} from 'lit';
+import {customElement, property, query, state} from 'lit/decorators';
+import {BindValueChangeEvent, ValueChangedEvent} from '../../../types/events';
+import {assertIsDefined, queryAndAssert} from '../../../utils/common-util';
/**
* Fired when the section has been modified or removed.
@@ -66,22 +66,9 @@ const REFS_NAME = 'refs/';
const ON_BEHALF_OF = '(On Behalf Of)';
const LABEL = 'Label';
-export interface GrAccessSection {
- $: {
- addBtn: GrButton;
- deleteBtn: GrButton;
- editBtn: GrButton;
- permissionSelect: HTMLSelectElement;
- section: HTMLFieldSetElement;
- undoRemoveBtn: GrButton;
- };
-}
-
@customElement('gr-access-section')
-export class GrAccessSection extends PolymerElement {
- static get template() {
- return htmlTemplate;
- }
+export class GrAccessSection extends LitElement {
+ @query('#permissionSelect') private permissionSelect?: HTMLSelectElement;
@property({type: String})
repo?: RepoName;
@@ -89,7 +76,7 @@ export class GrAccessSection extends PolymerElement {
@property({type: Object})
capabilities?: CapabilityInfoMap;
- @property({type: Object, notify: true, observer: '_updateSection'})
+ @property({type: Object})
section?: PermissionAccessSection;
@property({type: Object})
@@ -98,7 +85,7 @@ export class GrAccessSection extends PolymerElement {
@property({type: Object})
labels?: LabelNameToLabelTypeInfoMap;
- @property({type: Boolean, observer: '_handleEditingChanged'})
+ @property({type: Boolean})
editing = false;
@property({type: Boolean})
@@ -107,43 +94,220 @@ export class GrAccessSection extends PolymerElement {
@property({type: Array})
ownerOf?: GitRef[];
- @property({type: String})
- _originalId?: GitRef;
+ // private but used in test
+ @state() originalId?: GitRef;
- @property({type: Boolean})
- _editingRef = false;
+ // private but used in test
+ @state() editingRef = false;
- @property({type: Boolean})
- _deleted = false;
+ // private but used in test
+ @state() deleted = false;
- @property({type: Array})
- _permissions?: PermissionArray<EditablePermissionInfo>;
+ // private but used in test
+ @state() permissions?: PermissionArray<EditablePermissionInfo>;
constructor() {
super();
- this.addEventListener('access-saved', () => this._handleAccessSaved());
+ this.addEventListener('access-saved', () => this.handleAccessSaved());
+ }
+
+ static override get styles() {
+ return [
+ formStyles,
+ fontStyles,
+ sharedStyles,
+ css`
+ :host {
+ display: block;
+ margin-bottom: var(--spacing-l);
+ }
+ fieldset {
+ border: 1px solid var(--border-color);
+ }
+ .name {
+ align-items: center;
+ display: flex;
+ }
+ .header,
+ #deletedContainer {
+ align-items: center;
+ background: var(--table-header-background-color);
+ border-bottom: 1px dotted var(--border-color);
+ display: flex;
+ justify-content: space-between;
+ min-height: 3em;
+ padding: 0 var(--spacing-m);
+ }
+ #deletedContainer {
+ border-bottom: 0;
+ }
+ .sectionContent {
+ padding: var(--spacing-m);
+ }
+ #editBtn,
+ .editing #editBtn.global,
+ #deletedContainer,
+ .deleted #mainContainer,
+ #addPermission,
+ #deleteBtn,
+ .editingRef .name,
+ .editRefInput {
+ display: none;
+ }
+ .editing #editBtn,
+ .editingRef .editRefInput {
+ display: flex;
+ }
+ .deleted #deletedContainer {
+ display: flex;
+ }
+ .editing #addPermission,
+ #mainContainer,
+ .editing #deleteBtn {
+ display: block;
+ }
+ .editing #deleteBtn,
+ #undoRemoveBtn {
+ padding-right: var(--spacing-m);
+ }
+ `,
+ ];
}
- _updateSection(section: PermissionAccessSection) {
- this._permissions = toSortedPermissionsArray(section.value.permissions);
- this._originalId = section.id;
+ override render() {
+ if (!this.section) return;
+ return html`
+ <fieldset
+ id="section"
+ class="gr-form-styles ${this.computeSectionClass()}"
+ >
+ <div id="mainContainer">
+ <div class="header">
+ <div class="name">
+ <h3 class="heading-3">${this.computeSectionName()}</h3>
+ <gr-button
+ id="editBtn"
+ link
+ class=${this.section?.id === GLOBAL_NAME ? 'global' : ''}
+ @click=${this.editReference}
+ >
+ <iron-icon id="icon" icon="gr-icons:create"></iron-icon>
+ </gr-button>
+ </div>
+ <iron-input
+ class="editRefInput"
+ .bindValue=${this.section?.id}
+ @input=${this.handleValueChange}
+ @bind-value-changed=${this.handleIdBindValueChanged}
+ >
+ <input
+ class="editRefInput"
+ type="text"
+ @input=${this.handleValueChange}
+ />
+ </iron-input>
+ <gr-button link id="deleteBtn" @click=${this.handleRemoveReference}
+ >Remove</gr-button
+ >
+ </div>
+ <!-- end header -->
+ <div class="sectionContent">
+ ${this.permissions?.map((permission, index) =>
+ this.renderPermission(permission, index)
+ )}
+ <div id="addPermission">
+ Add permission:
+ <select id="permissionSelect">
+ ${this.computePermissions().map(item =>
+ this.renderPermissionOptions(item)
+ )}
+ </select>
+ <gr-button link id="addBtn" @click=${this.handleAddPermission}
+ >Add</gr-button
+ >
+ </div>
+ <!-- end addPermission -->
+ </div>
+ <!-- end sectionContent -->
+ </div>
+ <!-- end mainContainer -->
+ <div id="deletedContainer">
+ <span>${this.computeSectionName()} was deleted</span>
+ <gr-button link="" id="undoRemoveBtn" @click=${this._handleUndoRemove}
+ >Undo</gr-button
+ >
+ </div>
+ <!-- end deletedContainer -->
+ </fieldset>
+ `;
}
- _handleAccessSaved() {
- if (!this.section) {
- return;
+ private renderPermission(
+ permission: PermissionArrayItem<EditablePermissionInfo>,
+ index: number
+ ) {
+ return html`
+ <gr-permission
+ .name=${this.computePermissionName(permission)}
+ .permission=${permission}
+ .labels=${this.labels}
+ .section=${this.section?.id}
+ .editing=${this.editing}
+ .groups=${this.groups}
+ .repo=${this.repo}
+ @added-permission-removed=${() => {
+ this.handleAddedPermissionRemoved(index);
+ }}
+ @permission-changed=${(
+ e: ValueChangedEvent<PermissionArrayItem<EditablePermissionInfo>>
+ ) => {
+ this.handlePermissionChanged(e, index);
+ }}
+ >
+ </gr-permission>
+ `;
+ }
+
+ private renderPermissionOptions(item: {
+ id: string;
+ value: {name: string; id: string};
+ }) {
+ return html`<option value=${item.value.id}>${item.value.name}</option>`;
+ }
+
+ override willUpdate(changedProperties: PropertyValues) {
+ if (changedProperties.has('section')) {
+ this.updateSection();
+ }
+ if (changedProperties.has('editing')) {
+ this.handleEditingChanged(changedProperties.get('editing') as boolean);
}
+ }
+
+ // private but used in test
+ updateSection() {
+ this.permissions = toSortedPermissionsArray(
+ this.section!.value.permissions
+ );
+ this.originalId = this.section!.id;
+ }
+
+ // private but used in test
+ handleAccessSaved() {
+ if (!this.section) return;
// Set a new 'original' value to keep track of after the value has been
// saved.
- this._updateSection(this.section);
+ this.updateSection();
}
- _handleValueChange() {
+ // private but used in test
+ handleValueChange() {
if (!this.section) {
return;
}
if (!this.section.value.added) {
- this.section.value.modified = this.section.id !== this._originalId;
+ this.section.value.modified = this.section.id !== this.originalId;
+ this.requestUpdate();
// Allows overall access page to know a change has been made.
// For a new section, this is not fired because new permissions and
// rules have to be added in order to save, modifying the ref is not
@@ -151,25 +315,28 @@ export class GrAccessSection extends PolymerElement {
fireEvent(this, 'access-modified');
}
this.section.value.updatedId = this.section.id;
+ this.requestUpdate();
}
- _handleEditingChanged(editing: boolean, editingOld: boolean) {
+ private handleEditingChanged(editingOld: boolean) {
// Ignore when editing gets set initially.
if (!editingOld) {
return;
}
- if (!this.section || !this._permissions) {
+ if (!this.section || !this.permissions) {
return;
}
// Restore original values if no longer editing.
- if (!editing) {
- this._editingRef = false;
- this._deleted = false;
+ if (!this.editing) {
+ this.editingRef = false;
+ this.deleted = false;
delete this.section.value.deleted;
// Restore section ref.
- this.set(['section', 'id'], this._originalId);
+ this.section.id = this.originalId as GitRef;
+ this.requestUpdate();
+ fire(this, 'section-changed', {value: this.section});
// Remove any unsaved but added permissions.
- this._permissions = this._permissions.filter(p => !p.value.added);
+ this.permissions = this.permissions.filter(p => !p.value.added);
for (const key of Object.keys(this.section.value.permissions)) {
if (this.section.value.permissions[key].added) {
delete this.section.value.permissions[key];
@@ -178,22 +345,17 @@ export class GrAccessSection extends PolymerElement {
}
}
- _computePermissions(
- name: string,
- capabilities?: CapabilityInfoMap,
- labels?: LabelNameToLabelTypeInfoMap,
- // This is just for triggering re-computation. We don't use the value.
- _?: unknown
- ) {
+ // private but used in test
+ computePermissions() {
let allPermissions;
const section = this.section;
if (!section || !section.value) {
return [];
}
- if (name === GLOBAL_NAME) {
- allPermissions = toSortedPermissionsArray(capabilities);
+ if (section.id === GLOBAL_NAME) {
+ allPermissions = toSortedPermissionsArray(this.capabilities);
} else {
- const labelOptions = this._computeLabelOptions(labels);
+ const labelOptions = this.computeLabelOptions();
allPermissions = labelOptions.concat(
toSortedPermissionsArray(AccessPermissions)
);
@@ -203,22 +365,21 @@ export class GrAccessSection extends PolymerElement {
);
}
- _handleAddedPermissionRemoved(e: PolymerDomRepeatEvent) {
- if (!this._permissions) {
+ private handleAddedPermissionRemoved(index: number) {
+ if (!this.permissions) {
return;
}
- const index = e.model.index;
- this._permissions = this._permissions
+ this.permissions = this.permissions
.slice(0, index)
- .concat(this._permissions.slice(index + 1, this._permissions.length));
+ .concat(this.permissions.slice(index + 1, this.permissions.length));
}
- _computeLabelOptions(labels?: LabelNameToLabelTypeInfoMap) {
+ computeLabelOptions() {
const labelOptions = [];
- if (!labels) {
+ if (!this.labels) {
return [];
}
- for (const labelName of Object.keys(labels)) {
+ for (const labelName of Object.keys(this.labels)) {
labelOptions.push({
id: 'label-' + labelName,
value: {
@@ -237,13 +398,12 @@ export class GrAccessSection extends PolymerElement {
return labelOptions;
}
- _computePermissionName(
- name: string,
- permission: PermissionArrayItem<EditablePermissionInfo>,
- capabilities?: CapabilityInfoMap
+ // private but used in test
+ computePermissionName(
+ permission: PermissionArrayItem<EditablePermissionInfo>
): string | undefined {
- if (name === GLOBAL_NAME) {
- return capabilities?.[permission.id].name;
+ if (this.section?.id === GLOBAL_NAME) {
+ return this.capabilities?.[permission.id].name;
} else if (AccessPermissions[permission.id]) {
return AccessPermissions[permission.id].name;
} else if (permission.value.label) {
@@ -256,15 +416,19 @@ export class GrAccessSection extends PolymerElement {
return undefined;
}
- _computeSectionName(name: string) {
+ // private but used in test
+ computeSectionName() {
+ let name = this.section?.id;
// When a new section is created, it doesn't yet have a ref. Set into
// edit mode so that the user can input one.
if (!name) {
- this._editingRef = true;
+ this.editingRef = true;
// Needed for the title value. This is the same default as GWT.
- name = NEW_NAME;
+ name = NEW_NAME as GitRef;
// Needed for the input field value.
- this.set('section.id', name);
+ this.section!.id = name;
+ fire(this, 'section-changed', {value: this.section!});
+ this.requestUpdate();
}
if (name === GLOBAL_NAME) {
return 'Global Capabilities';
@@ -274,14 +438,14 @@ export class GrAccessSection extends PolymerElement {
return name;
}
- _handleRemoveReference() {
+ private handleRemoveReference() {
if (!this.section) {
return;
}
if (this.section.value.added) {
fireEvent(this, 'added-section-removed');
}
- this._deleted = true;
+ this.deleted = true;
this.section.value.deleted = true;
fireEvent(this, 'access-modified');
}
@@ -290,59 +454,46 @@ export class GrAccessSection extends PolymerElement {
if (!this.section) {
return;
}
- this._deleted = false;
+ this.deleted = false;
delete this.section.value.deleted;
+ this.requestUpdate();
}
editRefInput() {
- return this.root!.querySelector(
- 'iron-input.editRefInput'
- ) as IronInputElement;
+ return queryAndAssert<IronInputElement>(this, 'iron-input.editRefInput');
}
editReference() {
- this._editingRef = true;
+ this.editingRef = true;
this.editRefInput().focus();
}
- _isEditEnabled(
- canUpload: boolean | undefined,
- ownerOf: GitRef[] | undefined,
- sectionId: GitRef
- ) {
- return canUpload || (ownerOf && ownerOf.indexOf(sectionId) >= 0);
+ private isEditEnabled() {
+ return (
+ this.canUpload ||
+ (this.ownerOf && this.ownerOf.indexOf(this.section!.id) >= 0)
+ );
}
- _computeSectionClass(
- editing: boolean,
- canUpload: boolean | undefined,
- ownerOf: GitRef[] | undefined,
- editingRef: boolean,
- deleted: boolean
- ) {
+ // private but used in test
+ computeSectionClass() {
const classList = [];
- if (
- editing &&
- this.section &&
- this._isEditEnabled(canUpload, ownerOf, this.section.id)
- ) {
+ if (this.editing && this.section && this.isEditEnabled()) {
classList.push('editing');
}
- if (editingRef) {
+ if (this.editingRef) {
classList.push('editingRef');
}
- if (deleted) {
+ if (this.deleted) {
classList.push('deleted');
}
return classList.join(' ');
}
- _computeEditBtnClass(name: string) {
- return name === GLOBAL_NAME ? 'global' : '';
- }
-
- _handleAddPermission() {
- const value = this.$.permissionSelect.value as GitRef;
+ // private but used in test
+ handleAddPermission() {
+ assertIsDefined(this.permissionSelect, 'permissionSelect');
+ const value = this.permissionSelect.value as GitRef;
const permission: PermissionArrayItem<EditablePermissionInfo> = {
id: value,
value: {rules: {}, added: true},
@@ -370,12 +521,31 @@ export class GrAccessSection extends PolymerElement {
}
// Add to the end of the array (used in dom-repeat) and also to the
// section object that is two way bound with its parent element.
- this.push('_permissions', permission);
- this.set(['section.value.permissions', permission.id], permission.value);
+ this.permissions!.push(permission);
+ this.section!.value.permissions[permission.id] = permission.value;
+ this.requestUpdate();
+ fire(this, 'section-changed', {value: this.section!});
}
+
+ private handleIdBindValueChanged = (e: BindValueChangeEvent) => {
+ this.section!.id = e.detail.value as GitRef;
+ this.requestUpdate();
+ fire(this, 'section-changed', {value: this.section!});
+ };
+
+ private handlePermissionChanged = (
+ e: ValueChangedEvent<PermissionArrayItem<EditablePermissionInfo>>,
+ index: number
+ ) => {
+ this.permissions![index] = e.detail.value;
+ this.requestUpdate();
+ };
}
declare global {
+ interface HTMLElementEventMap {
+ 'section-changed': ValueChangedEvent<PermissionAccessSection>;
+ }
interface HTMLElementTagNameMap {
'gr-access-section': GrAccessSection;
}
diff --git a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_html.ts b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_html.ts
deleted file mode 100644
index 65a31991d9..0000000000
--- a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_html.ts
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
- * @license
- * Copyright (C) 2020 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 {html} from '@polymer/polymer/lib/utils/html-tag';
-
-export const htmlTemplate = html`
- <style include="gr-font-styles">
- /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
- </style>
- <style include="shared-styles">
- :host {
- display: block;
- margin-bottom: var(--spacing-l);
- }
- fieldset {
- border: 1px solid var(--border-color);
- }
- .name {
- align-items: center;
- display: flex;
- }
- .header,
- #deletedContainer {
- align-items: center;
- background: var(--table-header-background-color);
- border-bottom: 1px dotted var(--border-color);
- display: flex;
- justify-content: space-between;
- min-height: 3em;
- padding: 0 var(--spacing-m);
- }
- #deletedContainer {
- border-bottom: 0;
- }
- .sectionContent {
- padding: var(--spacing-m);
- }
- #editBtn,
- .editing #editBtn.global,
- #deletedContainer,
- .deleted #mainContainer,
- #addPermission,
- #deleteBtn,
- .editingRef .name,
- .editRefInput {
- display: none;
- }
- .editing #editBtn,
- .editingRef .editRefInput {
- display: flex;
- }
- .deleted #deletedContainer {
- display: flex;
- }
- .editing #addPermission,
- #mainContainer,
- .editing #deleteBtn {
- display: block;
- }
- .editing #deleteBtn,
- #undoRemoveBtn {
- padding-right: var(--spacing-m);
- }
- </style>
- <style include="gr-form-styles">
- /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
- </style>
- <fieldset
- id="section"
- class$="gr-form-styles [[_computeSectionClass(editing, canUpload, ownerOf, _editingRef, _deleted)]]"
- >
- <div id="mainContainer">
- <div class="header">
- <div class="name">
- <h3 class="heading-3">[[_computeSectionName(section.id)]]</h3>
- <gr-button
- id="editBtn"
- link=""
- class$="[[_computeEditBtnClass(section.id)]]"
- on-click="editReference"
- >
- <iron-icon id="icon" icon="gr-icons:create"></iron-icon>
- </gr-button>
- </div>
- <iron-input
- class="editRefInput"
- bind-value="{{section.id}}"
- type="text"
- on-input="_handleValueChange"
- >
- <input
- class="editRefInput"
- bind-value="{{section.id}}"
- is="iron-input"
- type="text"
- on-input="_handleValueChange"
- />
- </iron-input>
- <gr-button link="" id="deleteBtn" on-click="_handleRemoveReference"
- >Remove</gr-button
- >
- </div>
- <!-- end header -->
- <div class="sectionContent">
- <template is="dom-repeat" items="{{_permissions}}" as="permission">
- <gr-permission
- name="[[_computePermissionName(section.id, permission, capabilities)]]"
- permission="{{permission}}"
- labels="[[labels]]"
- section="[[section.id]]"
- editing="[[editing]]"
- groups="[[groups]]"
- repo="[[repo]]"
- on-added-permission-removed="_handleAddedPermissionRemoved"
- >
- </gr-permission>
- </template>
- <div id="addPermission">
- Add permission:
- <select id="permissionSelect">
- <!-- called with a third parameter so that permissions update
- after a new section is added. -->
- <template
- is="dom-repeat"
- items="[[_computePermissions(section.id, capabilities, labels, section.value.permissions.*)]]"
- >
- <option value="[[item.value.id]]">[[item.value.name]]</option>
- </template>
- </select>
- <gr-button link="" id="addBtn" on-click="_handleAddPermission"
- >Add</gr-button
- >
- </div>
- <!-- end addPermission -->
- </div>
- <!-- end sectionContent -->
- </div>
- <!-- end mainContainer -->
- <div id="deletedContainer">
- <span>[[_computeSectionName(section.id)]] was deleted</span>
- <gr-button link="" id="undoRemoveBtn" on-click="_handleUndoRemove"
- >Undo</gr-button
- >
- </div>
- <!-- end deletedContainer -->
- </fieldset>
-`;
diff --git a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.ts b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.ts
index 88961ed771..f4d81c41e6 100644
--- a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.ts
+++ b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section_test.ts
@@ -23,19 +23,21 @@ import {
} 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');
+import {queryAndAssert} from '../../../utils/common-util';
+import {GrButton} from '../../shared/gr-button/gr-button';
+import {fixture, html} from '@open-wc/testing-helpers';
suite('gr-access-section tests', () => {
let element: GrAccessSection;
- setup(() => {
- element = fixture.instantiate();
+ setup(async () => {
+ element = await fixture<GrAccessSection>(html`
+ <gr-access-section></gr-access-section>
+ `);
});
suite('unit tests', () => {
- setup(() => {
+ setup(async () => {
element.section = {
id: 'refs/*' as GitRef,
value: {
@@ -76,12 +78,12 @@ suite('gr-access-section tests', () => {
default_value: 0,
},
};
- element._updateSection(element.section);
- flush();
+ element.updateSection();
+ await element.updateComplete;
});
- test('_updateSection', () => {
- // _updateSection was called in setup, so just make assertions.
+ test('updateSection', () => {
+ // updateSection was called in setup, so just make assertions.
const expectedPermissions = [
{
id: 'read' as GitRef,
@@ -90,11 +92,11 @@ suite('gr-access-section tests', () => {
},
},
];
- assert.deepEqual(element._permissions, expectedPermissions);
- assert.equal(element._originalId, element.section!.id);
+ assert.deepEqual(element.permissions, expectedPermissions);
+ assert.equal(element.originalId, element.section!.id);
});
- test('_computeLabelOptions', () => {
+ test('computeLabelOptions', () => {
const expectedLabelOptions = [
{
id: 'label-Code-Review',
@@ -112,20 +114,17 @@ suite('gr-access-section tests', () => {
},
];
- assert.deepEqual(
- element._computeLabelOptions(element.labels),
- expectedLabelOptions
- );
+ assert.deepEqual(element.computeLabelOptions(), expectedLabelOptions);
});
- test('_handleAccessSaved', () => {
- assert.equal(element._originalId, 'refs/*' as GitRef);
+ 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);
+ element.handleAccessSaved();
+ assert.equal(element.originalId, 'refs/for/bar' as GitRef);
});
- test('_computePermissions', () => {
+ test('computePermissions', () => {
const capabilities = {
push: {
id: '',
@@ -166,26 +165,54 @@ suite('gr-access-section tests', () => {
},
];
+ element.section = {
+ id: 'refs/*' as GitRef,
+ value: {
+ permissions: {
+ read: {
+ rules: {},
+ },
+ },
+ },
+ };
+
// 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
- );
+ 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.
- name = 'refs/for/*';
+ element.section.id = 'refs/for/*' as GitRef;
assert.deepEqual(
- element._computePermissions(name, capabilities, element.labels),
+ element.computePermissions(),
labelOptions
.concat(toSortedPermissionsArray(AccessPermissions))
.filter(permission => permission.id !== 'read')
);
});
- test('_computePermissionName', () => {
- let name = 'GLOBAL_CAPABILITIES';
+ test('computePermissionName', () => {
+ element.section = {
+ id: 'GLOBAL_CAPABILITIES' as GitRef,
+ value: {
+ permissions: {
+ read: {
+ rules: {},
+ },
+ },
+ },
+ };
+
let permission;
permission = {
@@ -193,22 +220,22 @@ suite('gr-access-section tests', () => {
value: {rules: {}},
};
assert.equal(
- element._computePermissionName(name, permission, element.capabilities),
+ element.computePermissionName(permission),
element.capabilities![permission.id].name
);
- name = 'refs/for/*';
+ element.section.id = 'refs/for/*' as GitRef;
permission = {
id: 'abandon' as GitRef,
value: {rules: {}},
};
assert.equal(
- element._computePermissionName(name, permission, element.capabilities),
+ element.computePermissionName(permission),
AccessPermissions[permission.id].name
);
- name = 'refs/for/*';
+ element.section.id = 'refs/for/*' as GitRef;
permission = {
id: 'label-Code-Review' as GitRef,
value: {
@@ -218,7 +245,7 @@ suite('gr-access-section tests', () => {
};
assert.equal(
- element._computePermissionName(name, permission, element.capabilities),
+ element.computePermissionName(permission),
'Label Code-Review'
);
@@ -231,136 +258,63 @@ suite('gr-access-section tests', () => {
};
assert.equal(
- element._computePermissionName(name, permission, element.capabilities),
+ element.computePermissionName(permission),
'Label Code-Review(On Behalf Of)'
);
});
- test('_computeSectionName', () => {
- let name = '';
+ 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;
- assert.equal(
- element._computeSectionName(name),
- 'Reference: refs/heads/*'
- );
- assert.isTrue(element._editingRef);
+ 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;
- 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);
+ 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);
+ 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
- ),
- ''
- );
+ test('computeSectionClass', () => {
+ element.editingRef = false;
+ element.canUpload = false;
+ element.ownerOf = [];
+ element.editing = false;
+ element.deleted = false;
+ assert.equal(element.computeSectionClass(), '');
- editing = true;
- assert.equal(
- element._computeSectionClass(
- editing,
- canUpload,
- ownerOf,
- editingRef,
- deleted
- ),
- ''
- );
+ element.editing = true;
+ assert.equal(element.computeSectionClass(), '');
- ownerOf = ['refs/*' as GitRef];
- assert.equal(
- element._computeSectionClass(
- editing,
- canUpload,
- ownerOf,
- editingRef,
- deleted
- ),
- 'editing'
- );
+ element.ownerOf = ['refs/*' as GitRef];
+ assert.equal(element.computeSectionClass(), 'editing');
- ownerOf = [];
- canUpload = true;
- assert.equal(
- element._computeSectionClass(
- editing,
- canUpload,
- ownerOf,
- editingRef,
- deleted
- ),
- 'editing'
- );
+ element.ownerOf = [];
+ element.canUpload = true;
+ assert.equal(element.computeSectionClass(), 'editing');
- editingRef = true;
- assert.equal(
- element._computeSectionClass(
- editing,
- canUpload,
- ownerOf,
- editingRef,
- deleted
- ),
- 'editing editingRef'
- );
+ element.editingRef = true;
+ assert.equal(element.computeSectionClass(), 'editing editingRef');
- deleted = true;
- assert.equal(
- element._computeSectionClass(
- editing,
- canUpload,
- ownerOf,
- editingRef,
- deleted
- ),
- 'editing editingRef deleted'
- );
+ element.deleted = true;
+ assert.equal(element.computeSectionClass(), '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), '');
+ element.editingRef = false;
+ assert.equal(element.computeSectionClass(), 'editing deleted');
});
});
@@ -380,7 +334,7 @@ suite('gr-access-section tests', () => {
};
});
suite('Global section', () => {
- setup(() => {
+ setup(async () => {
element.section = {
id: 'GLOBAL_CAPABILITIES' as GitRef,
value: {
@@ -409,23 +363,41 @@ suite('gr-access-section tests', () => {
name: 'Create Account',
},
};
- element._updateSection(element.section);
- flush();
+ element.updateSection();
+ await element.updateComplete;
});
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'));
+ 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(element.$.editBtn).display, 'none');
+ assert.equal(
+ getComputedStyle(queryAndAssert<GrButton>(element, '#editBtn'))
+ .display,
+ 'none'
+ );
});
});
suite('Non-global section', () => {
- setup(() => {
+ setup(async () => {
element.section = {
id: 'refs/*' as GitRef,
value: {
@@ -437,32 +409,51 @@ suite('gr-access-section tests', () => {
},
};
element.capabilities = {};
- element._updateSection(element.section);
- flush();
+ element.updateSection();
+ await element.updateComplete;
});
- 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'));
+ 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 = [];
- flush();
- assert.notEqual(getComputedStyle(element.$.editBtn).display, 'none');
+ await element.updateComplete;
+ assert.notEqual(
+ getComputedStyle(queryAndAssert<GrButton>(element, '#editBtn'))
+ .display,
+ 'none'
+ );
});
- test('add permission', () => {
+ test('add permission', async () => {
element.editing = true;
- element.$.permissionSelect.value = 'label-Code-Review';
- assert.equal(element._permissions!.length, 1);
+ queryAndAssert<HTMLSelectElement>(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();
+ 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);
+ assert.equal(element.permissions!.length, 2);
let permission;
permission = {
@@ -473,17 +464,18 @@ suite('gr-access-section tests', () => {
rules: {},
},
};
- assert.equal(element._permissions!.length, 2);
- assert.deepEqual(element._permissions![1], permission);
+ 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();
+ queryAndAssert<HTMLSelectElement>(element, '#permissionSelect').value =
+ 'abandon';
+ queryAndAssert<GrButton>(element, '#addBtn').click();
+ await element.updateComplete;
permission = {
id: 'abandon' as GitRef,
@@ -493,8 +485,8 @@ suite('gr-access-section tests', () => {
},
};
- assert.equal(element._permissions!.length, 3);
- assert.deepEqual(element._permissions![2], permission);
+ 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'],
@@ -503,7 +495,8 @@ suite('gr-access-section tests', () => {
// Unsaved changes are discarded when editing is cancelled.
element.editing = false;
- assert.equal(element._permissions!.length, 1);
+ await element.updateComplete;
+ assert.equal(element.permissions!.length, 1);
assert.equal(Object.keys(element.section!.value.permissions).length, 1);
});
@@ -514,97 +507,131 @@ suite('gr-access-section tests', () => {
id: 'refs/for/bar' as GitRef,
value: {permissions: {}},
};
- assert.isFalse(element.$.section.classList.contains('editing'));
+ await element.updateComplete;
+ assert.isFalse(
+ queryAndAssert<HTMLFieldSetElement>(
+ element,
+ '#section'
+ ).classList.contains('editing')
+ );
element.editing = true;
- assert.isTrue(element.$.section.classList.contains('editing'));
- assert.isFalse(element._editingRef);
- MockInteractions.tap(element.$.editBtn);
+ 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 flush();
+ await element.updateComplete;
assert.equal(element.section.id, 'new/ref');
- assert.isTrue(element._editingRef);
- assert.isTrue(element.$.section.classList.contains('editingRef'));
+ assert.isTrue(element.editingRef);
+ assert.isTrue(
+ queryAndAssert<HTMLFieldSetElement>(
+ element,
+ '#section'
+ ).classList.contains('editingRef')
+ );
element.editing = false;
- assert.isFalse(element._editingRef);
+ await element.updateComplete;
+ assert.isFalse(element.editingRef);
assert.equal(element.section.id, 'refs/for/bar');
});
- test('_handleValueChange', () => {
+ 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();
+ 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();
+ 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;
- element._handleValueChange();
+ await element.updateComplete;
+ element.handleValueChange();
assert.isFalse(element.section.value.modified);
assert.equal(modifiedHandler.callCount, 2);
element.section.id = 'refs/for/bar' as GitRef;
- element._handleValueChange();
+ await element.updateComplete;
+ element.handleValueChange();
assert.isFalse(element.section.value.modified);
assert.equal(modifiedHandler.callCount, 2);
});
- test('remove section', () => {
+ test('remove section', async () => {
element.editing = true;
element.canUpload = true;
element.ownerOf = [];
- assert.isFalse(element._deleted);
+ await element.updateComplete;
+ assert.isFalse(element.deleted);
assert.isNotOk(element.section!.value.deleted);
- MockInteractions.tap(element.$.deleteBtn);
- flush();
- assert.isTrue(element._deleted);
+ queryAndAssert<GrButton>(element, '#deleteBtn').click();
+ await element.updateComplete;
+ assert.isTrue(element.deleted);
assert.isTrue(element.section!.value.deleted);
- assert.isTrue(element.$.section.classList.contains('deleted'));
+ assert.isTrue(
+ queryAndAssert<HTMLFieldSetElement>(
+ element,
+ '#section'
+ ).classList.contains('deleted')
+ );
assert.isTrue(element.section!.value.deleted);
- MockInteractions.tap(element.$.undoRemoveBtn);
- flush();
- assert.isFalse(element._deleted);
+ queryAndAssert<GrButton>(element, '#undoRemoveBtn').click();
+ await element.updateComplete;
+ assert.isFalse(element.deleted);
assert.isNotOk(element.section!.value.deleted);
- MockInteractions.tap(element.$.deleteBtn);
- assert.isTrue(element._deleted);
+ queryAndAssert<GrButton>(element, '#deleteBtn').click();
+ await element.updateComplete;
+ assert.isTrue(element.deleted);
assert.isTrue(element.section!.value.deleted);
element.editing = false;
- assert.isFalse(element._deleted);
+ await element.updateComplete;
+ assert.isFalse(element.deleted);
assert.isNotOk(element.section!.value.deleted);
});
- test('removing an added permission', () => {
+ test('removing an added permission', async () => {
element.editing = true;
- assert.equal(element._permissions!.length, 1);
+ await element.updateComplete;
+ 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);
+ await element.updateComplete;
+ assert.equal(element.permissions!.length, 0);
});
- test('remove an added section', () => {
+ test('remove an added section', async () => {
const removeStub = sinon.stub();
element.addEventListener('added-section-removed', removeStub);
element.editing = true;
element.section!.value.added = true;
- MockInteractions.tap(element.$.deleteBtn);
+ await element.updateComplete;
+ queryAndAssert<GrButton>(element, '#deleteBtn').click();
+ await element.updateComplete;
assert.isTrue(removeStub.called);
});
});
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.ts b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.ts
index 81d53f2c28..fba349d997 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.ts
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.ts
@@ -46,7 +46,7 @@ import {
EditableProjectAccessGroups,
} from '../gr-repo-access/gr-repo-access-interfaces';
import {getAppContext} from '../../../services/app-context';
-import {fireEvent} from '../../../utils/event-util';
+import {fire, fireEvent} from '../../../utils/event-util';
import {sharedStyles} from '../../../styles/shared-styles';
import {paperStyles} from '../../../styles/gr-paper-styles';
import {formStyles} from '../../../styles/gr-form-styles';
@@ -353,6 +353,7 @@ export class GrPermission extends LitElement {
// Restore exclusive bit to original.
this.permission.value.exclusive = this.originalExclusiveValue;
+ fire(this, 'permission-changed', {value: this.permission});
this.requestUpdate();
}
}
@@ -578,17 +579,18 @@ export class GrPermission extends LitElement {
}
private handleRuleChanged(e: CustomEvent, index: number) {
- if (this.rules === undefined || e.detail.value === undefined) return;
- if (isNaN(index)) {
- return;
- }
- this.rules.splice(index, e.detail.value);
+ this.rules!.splice(index, e.detail.value);
this.handleRulesChanged();
this.requestUpdate();
}
}
declare global {
+ interface HTMLElementEventMap {
+ 'permission-changed': ValueChangedEvent<
+ PermissionArrayItem<EditablePermissionInfo>
+ >;
+ }
interface HTMLElementTagNameMap {
'gr-permission': GrPermission;
}
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts
index f8b36c4c97..07a91883a8 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts
@@ -14,18 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import '../../../styles/gr-font-styles';
-import '../../../styles/gr-menu-page-styles';
-import '../../../styles/gr-subpage-styles';
-import '../../../styles/shared-styles';
+
import '../gr-access-section/gr-access-section';
-import {flush} from '@polymer/polymer/lib/legacy/polymer.dom';
-import {PolymerElement} from '@polymer/polymer/polymer-element';
-import {htmlTemplate} from './gr-repo-access_html';
import {encodeURL, getBaseUrl, singleDecodeURL} from '../../../utils/url-util';
import {GerritNav} from '../../core/gr-navigation/gr-navigation';
import {toSortedPermissionsArray} from '../../../utils/access-util';
-import {customElement, property} from '@polymer/decorators';
import {
RepoName,
ProjectInfo,
@@ -51,106 +44,296 @@ import {
import {firePageError, fireAlert} from '../../../utils/event-util';
import {getAppContext} from '../../../services/app-context';
import {WebLinkInfo} from '../../../types/diff';
+import {fontStyles} from '../../../styles/gr-font-styles';
+import {menuPageStyles} from '../../../styles/gr-menu-page-styles';
+import {subpageStyles} from '../../../styles/gr-subpage-styles';
+import {sharedStyles} from '../../../styles/shared-styles';
+import {LitElement, PropertyValues, css, html} from 'lit';
+import {customElement, property, query, state} from 'lit/decorators';
+import {assertIsDefined} from '../../../utils/common-util';
+import {ValueChangedEvent} from '../../../types/events';
+import {ifDefined} from 'lit/directives/if-defined';
const NOTHING_TO_SAVE = 'No changes to save.';
const MAX_AUTOCOMPLETE_RESULTS = 50;
+declare global {
+ interface HTMLElementEventMap {
+ 'text-changed': CustomEvent<string>;
+ }
+ interface HTMLElementTagNameMap {
+ 'gr-repo-access': GrRepoAccess;
+ }
+}
+
/**
* Fired when save is a no-op
*
* @event show-alert
*/
@customElement('gr-repo-access')
-export class GrRepoAccess extends PolymerElement {
- static get template() {
- return htmlTemplate;
- }
+export class GrRepoAccess extends LitElement {
+ @query('gr-access-section:last-of-type') accessSection?: GrAccessSection;
- @property({type: String, observer: '_repoChanged'})
+ @property({type: String})
repo?: RepoName;
@property({type: String})
path?: string;
- @property({type: Boolean})
- _canUpload?: boolean = false; // restAPI can return undefined
-
- @property({type: String})
- _inheritFromFilter?: RepoName;
+ // private but used in test
+ @state() canUpload?: boolean = false; // restAPI can return undefined
- @property({type: Object})
- _query: AutocompleteQuery;
+ // private but used in test
+ @state() inheritFromFilter?: RepoName;
- @property({type: Array})
- _ownerOf?: GitRef[];
+ // private but used in test
+ @state() ownerOf?: GitRef[];
- @property({type: Object})
- _capabilities?: CapabilityInfoMap;
+ // private but used in test
+ @state() capabilities?: CapabilityInfoMap;
- @property({type: Object})
- _groups?: ProjectAccessGroups;
+ // private but used in test
+ @state() groups?: ProjectAccessGroups;
- @property({type: Object})
- _inheritsFrom?: ProjectInfo;
+ // private but used in test
+ @state() inheritsFrom?: ProjectInfo;
- @property({type: Object})
- _labels?: LabelNameToLabelTypeInfoMap;
+ // private but used in test
+ @state() labels?: LabelNameToLabelTypeInfoMap;
- @property({type: Object})
- _local?: EditableLocalAccessSectionInfo;
+ // private but used in test
+ @state() local?: EditableLocalAccessSectionInfo;
- @property({type: Boolean, observer: '_handleEditingChanged'})
- _editing = false;
+ // private but used in test
+ @state() editing = false;
- @property({type: Boolean})
- _modified = false;
+ // private but used in test
+ @state() modified = false;
- @property({type: Array})
- _sections?: PermissionAccessSection[];
+ // private but used in test
+ @state() sections?: PermissionAccessSection[];
- @property({type: Array})
- _weblinks?: WebLinkInfo[];
+ @state() private weblinks?: WebLinkInfo[];
- @property({type: Boolean})
- _loading = true;
+ // private but used in test
+ @state() loading = true;
// private but used in the tests
originalInheritsFrom?: ProjectInfo;
+ private readonly query: AutocompleteQuery;
+
private readonly restApiService = getAppContext().restApiService;
constructor() {
super();
- this._query = () => this._getInheritFromSuggestions();
+ this.query = () => this.getInheritFromSuggestions();
this.addEventListener('access-modified', () =>
this._handleAccessModified()
);
}
+ static override get styles() {
+ return [
+ fontStyles,
+ menuPageStyles,
+ subpageStyles,
+ sharedStyles,
+ css`
+ gr-button,
+ #inheritsFrom,
+ #editInheritFromInput,
+ .editing #inheritFromName,
+ .weblinks,
+ .editing .invisible {
+ display: none;
+ }
+ #inheritsFrom.show {
+ display: flex;
+ min-height: 2em;
+ align-items: center;
+ }
+ .weblink {
+ margin-right: var(--spacing-xs);
+ }
+ gr-access-section {
+ margin-top: var(--spacing-l);
+ }
+ .weblinks.show,
+ .referenceContainer {
+ display: block;
+ }
+ .rightsText {
+ margin-right: var(--spacing-s);
+ }
+
+ .editing gr-button,
+ .admin #editBtn {
+ display: inline-block;
+ margin: var(--spacing-l) 0;
+ }
+ .editing #editInheritFromInput {
+ display: inline-block;
+ }
+ `,
+ ];
+ }
+
+ override render() {
+ return html`
+ <div class="main ${this.computeMainClass()}">
+ <div id="loading" class=${this.loading ? 'loading' : ''}>
+ Loading...
+ </div>
+ <div id="loadedContent" class=${this.loading ? 'loading' : ''}>
+ <h3
+ id="inheritsFrom"
+ class="heading-3 ${this.editing || this.inheritsFrom?.id?.length
+ ? 'show'
+ : ''}"
+ >
+ <span class="rightsText">Rights Inherit From</span>
+ <a
+ id="inheritFromName"
+ href=${this.computeParentHref()}
+ rel="noopener"
+ >
+ ${this.inheritsFrom?.name}</a
+ >
+ <gr-autocomplete
+ id="editInheritFromInput"
+ .text=${this.inheritFromFilter}
+ .query=${this.query}
+ @commit=${(e: ValueChangedEvent) => {
+ this.handleUpdateInheritFrom(e);
+ }}
+ @bind-value-changed=${(e: ValueChangedEvent) => {
+ this.handleUpdateInheritFrom(e);
+ }}
+ @text-changed=${(e: ValueChangedEvent) => {
+ this.handleEditInheritFromTextChanged(e);
+ }}
+ ></gr-autocomplete>
+ </h3>
+ <div class="weblinks ${this.weblinks?.length ? 'show' : ''}">
+ History:
+ ${this.weblinks?.map(webLink => this.renderWebLinks(webLink))}
+ </div>
+ ${this.sections?.map((section, index) =>
+ this.renderPermissionSections(section, index)
+ )}
+ <div class="referenceContainer">
+ <gr-button
+ id="addReferenceBtn"
+ @click=${() => this.handleCreateSection()}
+ >Add Reference</gr-button
+ >
+ </div>
+ <div>
+ <gr-button
+ id="editBtn"
+ @click=${() => {
+ this.handleEdit();
+ }}
+ >${this.editing ? 'Cancel' : 'Edit'}</gr-button
+ >
+ <gr-button
+ id="saveBtn"
+ class=${this.ownerOf && this.ownerOf.length === 0
+ ? 'invisible'
+ : ''}
+ primary
+ ?disabled=${!this.modified}
+ @click=${this.handleSave}
+ >Save</gr-button
+ >
+ <gr-button
+ id="saveReviewBtn"
+ class=${!this.canUpload ? 'invisible' : ''}
+ primary
+ ?disabled=${!this.modified}
+ @click=${this.handleSaveForReview}
+ >Save for review</gr-button
+ >
+ </div>
+ </div>
+ </div>
+ `;
+ }
+
+ private renderWebLinks(webLink: WebLinkInfo) {
+ return html`
+ <a
+ class="weblink"
+ href=${webLink.url}
+ rel="noopener"
+ target=${ifDefined(webLink.target)}
+ >
+ ${webLink.name}
+ </a>
+ `;
+ }
+
+ private renderPermissionSections(
+ section: PermissionAccessSection,
+ index: number
+ ) {
+ return html`
+ <gr-access-section
+ .capabilities=${this.capabilities}
+ .section=${section}
+ .labels=${this.labels}
+ .canUpload=${this.canUpload}
+ .editing=${this.editing}
+ .ownerOf=${this.ownerOf}
+ .groups=${this.groups}
+ .repo=${this.repo}
+ @added-section-removed=${() => {
+ this.handleAddedSectionRemoved(index);
+ }}
+ @section-changed=${(e: ValueChangedEvent<PermissionAccessSection>) => {
+ this.handleAccessSectionChanged(e, index);
+ }}
+ ></gr-access-section>
+ `;
+ }
+
+ override willUpdate(changedProperties: PropertyValues) {
+ if (changedProperties.has('repo')) {
+ this._repoChanged(this.repo);
+ }
+
+ if (changedProperties.has('editing')) {
+ this.handleEditingChanged(changedProperties.get('editing') as boolean);
+ this.requestUpdate();
+ }
+ }
+
_handleAccessModified() {
- this._modified = true;
+ this.modified = true;
}
_repoChanged(repo?: RepoName) {
- this._loading = true;
+ this.loading = true;
if (!repo) {
return Promise.resolve();
}
- return this._reload(repo);
+ return this.reload(repo);
}
- _reload(repo: RepoName) {
+ private reload(repo: RepoName) {
const errFn = (response?: Response | null) => {
firePageError(response);
};
- this._editing = false;
+ this.editing = false;
// Always reset sections when a project changes.
- this._sections = [];
+ this.sections = [];
const sectionsPromises = this.restApiService
.getRepoAccessRights(repo, errFn)
.then(res => {
@@ -162,26 +345,26 @@ export class GrRepoAccess extends PolymerElement {
// the ones data bound to gr-autocomplete, so the original value
// can be restored if the user cancels.
if (res.inherits_from) {
- this._inheritsFrom = {...res.inherits_from};
+ this.inheritsFrom = {...res.inherits_from};
this.originalInheritsFrom = {...res.inherits_from};
} else {
- this._inheritsFrom = undefined;
+ this.inheritsFrom = undefined;
this.originalInheritsFrom = undefined;
}
// Initialize the filter value so when the user clicks edit, the
// current value appears. If there is no parent repo, it is
// initialized as an empty string.
- this._inheritFromFilter = res.inherits_from
+ this.inheritFromFilter = res.inherits_from
? res.inherits_from.name
: ('' as RepoName);
// 'as EditableLocalAccessSectionInfo' is required because res.local
// type doesn't have index signature
- this._local = res.local as EditableLocalAccessSectionInfo;
- this._groups = res.groups;
- this._weblinks = res.config_web_links || [];
- this._canUpload = res.can_upload;
- this._ownerOf = res.owner_of || [];
- return toSortedPermissionsArray(this._local);
+ this.local = res.local as EditableLocalAccessSectionInfo;
+ this.groups = res.groups;
+ this.weblinks = res.config_web_links || [];
+ this.canUpload = res.can_upload;
+ this.ownerOf = res.owner_of || [];
+ return toSortedPermissionsArray(this.local);
});
const capabilitiesPromises = this.restApiService
@@ -209,25 +392,26 @@ export class GrRepoAccess extends PolymerElement {
capabilitiesPromises,
labelsPromises,
]).then(([sections, capabilities, labels]) => {
- this._capabilities = capabilities;
- this._labels = labels;
- this._sections = sections;
- this._loading = false;
+ this.capabilities = capabilities;
+ this.labels = labels;
+ this.sections = sections;
+ this.loading = false;
});
}
- _handleUpdateInheritFrom(e: CustomEvent<{value: string}>) {
- this._inheritsFrom = {
- ...(this._inheritsFrom ?? {}),
+ // private but used in test
+ handleUpdateInheritFrom(e: ValueChangedEvent) {
+ this.inheritsFrom = {
+ ...(this.inheritsFrom ?? {}),
id: e.detail.value as UrlEncodedRepoName,
- name: this._inheritFromFilter,
+ name: this.inheritFromFilter,
};
this._handleAccessModified();
}
- _getInheritFromSuggestions(): Promise<AutocompleteSuggestion[]> {
+ private getInheritFromSuggestions(): Promise<AutocompleteSuggestion[]> {
return this.restApiService
- .getRepos(this._inheritFromFilter, MAX_AUTOCOMPLETE_RESULTS)
+ .getRepos(this.inheritFromFilter, MAX_AUTOCOMPLETE_RESULTS)
.then(response => {
const projects: AutocompleteSuggestion[] = [];
if (!response) {
@@ -243,67 +427,47 @@ export class GrRepoAccess extends PolymerElement {
});
}
- _computeLoadingClass(loading: boolean) {
- return loading ? 'loading' : '';
- }
-
- _handleEdit() {
- this._editing = !this._editing;
- }
-
- _editOrCancel(editing: boolean) {
- return editing ? 'Cancel' : 'Edit';
+ private handleEdit() {
+ this.editing = !this.editing;
}
- _computeWebLinkClass(weblinks?: string[]) {
- return weblinks && weblinks.length ? 'show' : '';
- }
-
- _computeShowInherit(inheritsFrom?: ProjectInfo) {
- return this._editing || inheritsFrom?.id?.length ? 'show' : '';
- }
-
- // TODO(TS): Unclear what is model here, provide a better explanation
- _handleAddedSectionRemoved(e: CustomEvent & {model: {index: string}}) {
- if (!this._sections) {
- return;
- }
- const index = Number(e.model.index);
- if (isNaN(index)) {
- return;
- }
- this._sections = this._sections
+ private handleAddedSectionRemoved(index: number) {
+ if (!this.sections) return;
+ this.sections = this.sections
.slice(0, index)
- .concat(this._sections.slice(index + 1, this._sections.length));
+ .concat(this.sections.slice(index + 1, this.sections.length));
}
- _handleEditingChanged(editing: boolean, editingOld: boolean) {
+ private handleEditingChanged(editingOld: boolean) {
// Ignore when editing gets set initially.
- if (!editingOld || editing) {
+ if (!editingOld || this.editing) {
return;
}
// Remove any unsaved but added refs.
- if (this._sections) {
- this._sections = this._sections.filter(p => !p.value.added);
+ if (this.sections) {
+ this.sections = this.sections.filter(p => !p.value.added);
}
// Restore inheritFrom.
- if (this._inheritsFrom) {
- this._inheritsFrom = this.originalInheritsFrom
+ if (this.inheritsFrom) {
+ this.inheritsFrom = this.originalInheritsFrom
? {...this.originalInheritsFrom}
: undefined;
- this._inheritFromFilter = this.originalInheritsFrom?.name;
+ this.inheritFromFilter = this.originalInheritsFrom?.name;
}
- if (!this._local) {
+ if (!this.local) {
return;
}
- for (const key of Object.keys(this._local)) {
- if (this._local[key].added) {
- delete this._local[key];
+ for (const key of Object.keys(this.local)) {
+ if (this.local[key].added) {
+ delete this.local[key];
}
}
}
- _updateRemoveObj(addRemoveObj: {remove: PropertyTreeNode}, path: string[]) {
+ private updateRemoveObj(
+ addRemoveObj: {remove: PropertyTreeNode},
+ path: string[]
+ ) {
let curPos: PropertyTreeNode = addRemoveObj.remove;
for (const item of path) {
if (!curPos[item]) {
@@ -327,7 +491,7 @@ export class GrRepoAccess extends PolymerElement {
return addRemoveObj;
}
- _updateAddObj(
+ private updateAddObj(
addRemoveObj: {add: PropertyTreeNode},
path: string[],
value: PropertyTreeNode | PrimitiveValue
@@ -351,8 +515,10 @@ export class GrRepoAccess extends PolymerElement {
/**
* Used to recursively remove any objects with a 'deleted' bit.
+ *
+ * private but used in test
*/
- _recursivelyRemoveDeleted(obj?: PropertyTreeNode) {
+ recursivelyRemoveDeleted(obj?: PropertyTreeNode) {
if (!obj) return;
for (const k of Object.keys(obj)) {
const node = obj[k];
@@ -361,12 +527,13 @@ export class GrRepoAccess extends PolymerElement {
delete obj[k];
return;
}
- this._recursivelyRemoveDeleted(node);
+ this.recursivelyRemoveDeleted(node);
}
}
}
- _recursivelyUpdateAddRemoveObj(
+ // private but used in test
+ recursivelyUpdateAddRemoveObj(
obj: PropertyTreeNode | undefined,
addRemoveObj: {
add: PropertyTreeNode;
@@ -381,36 +548,36 @@ export class GrRepoAccess extends PolymerElement {
const updatedId = node.updatedId;
const ref = updatedId ? updatedId : k;
if (node.deleted) {
- this._updateRemoveObj(addRemoveObj, path.concat(k));
+ this.updateRemoveObj(addRemoveObj, path.concat(k));
continue;
} else if (node.modified) {
- this._updateRemoveObj(addRemoveObj, path.concat(k));
- this._updateAddObj(addRemoveObj, path.concat(ref), node);
+ this.updateRemoveObj(addRemoveObj, path.concat(k));
+ this.updateAddObj(addRemoveObj, path.concat(ref), node);
/* Special case for ref changes because they need to be added and
- removed in a different way. The new ref needs to include all
- changes but also the initial state. To do this, instead of
- continuing with the same recursion, just remove anything that is
- deleted in the current state. */
+ removed in a different way. The new ref needs to include all
+ changes but also the initial state. To do this, instead of
+ continuing with the same recursion, just remove anything that is
+ deleted in the current state. */
if (updatedId && updatedId !== k) {
- this._recursivelyRemoveDeleted(
+ this.recursivelyRemoveDeleted(
addRemoveObj.add[updatedId] as PropertyTreeNode
);
}
continue;
} else if (node.added) {
- this._updateAddObj(addRemoveObj, path.concat(ref), node);
+ this.updateAddObj(addRemoveObj, path.concat(ref), node);
/**
* As add / delete both can happen in the new section,
* so here to make sure it will remove the deleted ones.
*
* @see Issue 11339
*/
- this._recursivelyRemoveDeleted(
+ this.recursivelyRemoveDeleted(
addRemoveObj.add[k] as PropertyTreeNode
);
continue;
}
- this._recursivelyUpdateAddRemoveObj(node, addRemoveObj, path.concat(k));
+ this.recursivelyUpdateAddRemoveObj(node, addRemoveObj, path.concat(k));
}
}
}
@@ -418,8 +585,10 @@ export class GrRepoAccess extends PolymerElement {
/**
* Returns an object formatted for saving or submitting access changes for
* review
+ *
+ * private but used in test
*/
- _computeAddAndRemove() {
+ computeAddAndRemove() {
const addRemoveObj: {
add: PropertyTreeNode;
remove: PropertyTreeNode;
@@ -432,8 +601,8 @@ export class GrRepoAccess extends PolymerElement {
const originalInheritsFromId = this.originalInheritsFrom
? singleDecodeURL(this.originalInheritsFrom.id)
: undefined;
- const inheritsFromId = this._inheritsFrom
- ? singleDecodeURL(this._inheritsFrom.id)
+ const inheritsFromId = this.inheritsFrom
+ ? singleDecodeURL(this.inheritsFrom.id)
: undefined;
const inheritFromChanged =
@@ -442,12 +611,12 @@ export class GrRepoAccess extends PolymerElement {
// Inherit from added (did not have one initially);
(!originalInheritsFromId && inheritsFromId);
- if (!this._local) {
+ if (!this.local) {
return addRemoveObj;
}
- this._recursivelyUpdateAddRemoveObj(
- this._local as unknown as PropertyTreeNode,
+ this.recursivelyUpdateAddRemoveObj(
+ this.local as unknown as PropertyTreeNode,
addRemoveObj
);
@@ -457,30 +626,25 @@ export class GrRepoAccess extends PolymerElement {
return addRemoveObj;
}
- _handleCreateSection() {
- if (!this._local) {
- return;
- }
+ private async handleCreateSection() {
+ if (!this.local) return;
let newRef = 'refs/for/*';
// Avoid using an already used key for the placeholder, since it
// immediately gets added to an object.
- while (this._local[newRef]) {
+ while (this.local[newRef]) {
newRef = `${newRef}*`;
}
const section = {permissions: {}, added: true};
- this.push('_sections', {id: newRef, value: section});
- this.set(['_local', newRef], section);
- flush();
+ this.sections!.push({id: newRef as GitRef, value: section});
+ this.local[newRef] = section;
+ this.requestUpdate();
+ assertIsDefined(this.accessSection, 'accessSection');
// Template already instantiated at this point
- (
- this.root!.querySelector(
- 'gr-access-section:last-of-type'
- ) as GrAccessSection
- ).editReference();
+ this.accessSection.editReference();
}
- _getObjforSave(): ProjectAccessInput | undefined {
- const addRemoveObj = this._computeAddAndRemove();
+ private getObjforSave(): ProjectAccessInput | undefined {
+ const addRemoveObj = this.computeAddAndRemove();
// If there are no changes, don't actually save.
if (
!Object.keys(addRemoveObj.add).length &&
@@ -500,8 +664,9 @@ export class GrRepoAccess extends PolymerElement {
return obj;
}
- _handleSave(e: Event) {
- const obj = this._getObjforSave();
+ // private but used in test
+ handleSave(e: Event) {
+ const obj = this.getObjforSave();
if (!obj) {
return;
}
@@ -516,18 +681,19 @@ export class GrRepoAccess extends PolymerElement {
return this.restApiService
.setRepoAccessRights(repo, obj)
.then(() => {
- this._reload(repo);
+ this.reload(repo);
})
.finally(() => {
- this._modified = false;
+ this.modified = false;
if (button) {
button.loading = false;
}
});
}
- _handleSaveForReview(e: Event) {
- const obj = this._getObjforSave();
+ // private but used in test
+ handleSaveForReview(e: Event) {
+ const obj = this.getObjforSave();
if (!obj) {
return;
}
@@ -544,43 +710,42 @@ export class GrRepoAccess extends PolymerElement {
GerritNav.navigateToChange(change);
})
.finally(() => {
- this._modified = false;
+ this.modified = false;
if (button) {
button.loading = false;
}
});
}
- _computeSaveReviewBtnClass(canUpload?: boolean) {
- return !canUpload ? 'invisible' : '';
- }
-
- _computeSaveBtnClass(ownerOf?: GitRef[]) {
- return ownerOf && ownerOf.length === 0 ? 'invisible' : '';
- }
-
- _computeMainClass(
- ownerOf: GitRef[] | undefined,
- canUpload: boolean,
- editing: boolean
- ) {
+ // private but used in test
+ computeMainClass() {
const classList = [];
- if ((ownerOf && ownerOf.length > 0) || canUpload) {
+ if ((this.ownerOf && this.ownerOf.length > 0) || this.canUpload) {
classList.push('admin');
}
- if (editing) {
+ if (this.editing) {
classList.push('editing');
}
return classList.join(' ');
}
- _computeParentHref(repoName: RepoName) {
- return getBaseUrl() + `/admin/repos/${encodeURL(repoName, true)},access`;
+ computeParentHref() {
+ if (!this.inheritsFrom?.name) return '';
+ return `${getBaseUrl()}/admin/repos/${encodeURL(
+ this.inheritsFrom.name,
+ true
+ )},access`;
}
-}
-declare global {
- interface HTMLElementTagNameMap {
- 'gr-repo-access': GrRepoAccess;
+ private handleEditInheritFromTextChanged(e: ValueChangedEvent) {
+ this.inheritFromFilter = e.detail.value as RepoName;
+ }
+
+ private handleAccessSectionChanged(
+ e: ValueChangedEvent<PermissionAccessSection>,
+ index: number
+ ) {
+ this.sections![index] = e.detail.value;
+ this.requestUpdate();
}
}
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_html.ts b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_html.ts
deleted file mode 100644
index 8f88619fae..0000000000
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_html.ts
+++ /dev/null
@@ -1,151 +0,0 @@
-/**
- * @license
- * Copyright (C) 2020 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 {html} from '@polymer/polymer/lib/utils/html-tag';
-
-export const htmlTemplate = html`
- <style include="gr-font-styles">
- /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
- </style>
- <style include="shared-styles">
- /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
- </style>
- <style include="gr-subpage-styles">
- gr-button,
- #inheritsFrom,
- #editInheritFromInput,
- .editing #inheritFromName,
- .weblinks,
- .editing .invisible {
- display: none;
- }
- #inheritsFrom.show {
- display: flex;
- min-height: 2em;
- align-items: center;
- }
- .weblink {
- margin-right: var(--spacing-xs);
- }
- gr-access-section {
- margin-top: var(--spacing-l);
- }
- .weblinks.show,
- .referenceContainer {
- display: block;
- }
- .rightsText {
- margin-right: var(--spacing-s);
- }
-
- .editing gr-button,
- .admin #editBtn {
- display: inline-block;
- margin: var(--spacing-l) 0;
- }
- .editing #editInheritFromInput {
- display: inline-block;
- }
- </style>
- <style include="gr-menu-page-styles">
- /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
- </style>
- <div class$="main [[_computeMainClass(_ownerOf, _canUpload, _editing)]]">
- <div id="loading" class$="[[_computeLoadingClass(_loading)]]">
- Loading...
- </div>
- <div id="loadedContent" class$="[[_computeLoadingClass(_loading)]]">
- <h3
- id="inheritsFrom"
- class$="heading-3 [[_computeShowInherit(_inheritsFrom)]]"
- >
- <span class="rightsText">Rights Inherit From</span>
- <a
- href$="[[_computeParentHref(_inheritsFrom.name)]]"
- rel="noopener"
- id="inheritFromName"
- >
- [[_inheritsFrom.name]]</a
- >
- <gr-autocomplete
- id="editInheritFromInput"
- text="{{_inheritFromFilter}}"
- query="[[_query]]"
- on-commit="_handleUpdateInheritFrom"
- on-bind-value-changed="_handleUpdateInheritFrom"
- ></gr-autocomplete>
- </h3>
- <div class$="weblinks [[_computeWebLinkClass(_weblinks)]]">
- History:
- <template is="dom-repeat" items="[[_weblinks]]" as="link">
- <a
- href="[[link.url]]"
- class="weblink"
- rel="noopener"
- target="[[link.target]]"
- >
- [[link.name]]
- </a>
- </template>
- </div>
- <template
- is="dom-repeat"
- items="{{_sections}}"
- initial-count="5"
- target-framerate="60"
- as="section"
- >
- <gr-access-section
- capabilities="[[_capabilities]]"
- section="{{section}}"
- labels="[[_labels]]"
- can-upload="[[_canUpload]]"
- editing="[[_editing]]"
- owner-of="[[_ownerOf]]"
- groups="[[_groups]]"
- repo="[[repo]]"
- on-added-section-removed="_handleAddedSectionRemoved"
- ></gr-access-section>
- </template>
- <div class="referenceContainer">
- <gr-button id="addReferenceBtn" on-click="_handleCreateSection"
- >Add Reference</gr-button
- >
- </div>
- <div>
- <gr-button id="editBtn" on-click="_handleEdit"
- >[[_editOrCancel(_editing)]]</gr-button
- >
- <gr-button
- id="saveBtn"
- primary=""
- class$="[[_computeSaveBtnClass(_ownerOf)]]"
- on-click="_handleSave"
- disabled="[[!_modified]]"
- >Save</gr-button
- >
- <gr-button
- id="saveReviewBtn"
- primary=""
- class$="[[_computeSaveReviewBtnClass(_canUpload)]]"
- on-click="_handleSaveForReview"
- disabled="[[!_modified]]"
- >Save for review</gr-button
- >
- </div>
- </div>
- </div>
-`;
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.ts b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.ts
index 37cc4882d4..caa2d13133 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.ts
@@ -40,12 +40,10 @@ import {
AutocompleteCommitEvent,
GrAutocomplete,
} from '../../shared/gr-autocomplete/gr-autocomplete';
-import * as MockInteractions from '@polymer/iron-test-helpers/mock-interactions';
import {GrAccessSection} from '../gr-access-section/gr-access-section';
import {GrPermission} from '../gr-permission/gr-permission';
import {createChange} from '../../../test/test-data-generators';
-
-const basicFixture = fixtureFromElement('gr-repo-access');
+import {fixture, html} from '@open-wc/testing-helpers';
suite('gr-repo-access tests', () => {
let element: GrRepoAccess;
@@ -128,19 +126,21 @@ suite('gr-repo-access tests', () => {
},
};
setup(async () => {
- element = basicFixture.instantiate();
+ element = await fixture<GrRepoAccess>(html`
+ <gr-repo-access></gr-repo-access>
+ `);
stubRestApi('getAccount').returns(Promise.resolve(undefined));
repoStub = stubRestApi('getRepo').returns(Promise.resolve(repoRes));
- element._loading = false;
- element._ownerOf = [];
- element._canUpload = false;
- await flush();
+ element.loading = false;
+ element.ownerOf = [];
+ element.canUpload = false;
+ await element.updateComplete;
});
test('_repoChanged called when repo name changes', async () => {
const repoChangedStub = sinon.stub(element, '_repoChanged');
element.repo = 'New Repo' as RepoName;
- await flush();
+ await element.updateComplete;
assert.isTrue(repoChangedStub.called);
});
@@ -160,13 +160,13 @@ suite('gr-repo-access tests', () => {
assert.isTrue(accessStub.called);
assert.isTrue(capabilitiesStub.called);
assert.isTrue(repoStub.called);
- assert.isNotOk(element._inheritsFrom);
- assert.deepEqual(element._local, accessRes.local);
+ assert.isNotOk(element.inheritsFrom);
+ assert.deepEqual(element.local, accessRes.local);
assert.deepEqual(
- element._sections,
+ element.sections,
toSortedPermissionsArray(accessRes.local)
);
- assert.deepEqual(element._labels, repoRes.labels);
+ assert.deepEqual(element.labels, repoRes.labels);
assert.equal(
getComputedStyle(queryAndAssert<HTMLDivElement>(element, '.weblinks'))
.display,
@@ -175,7 +175,7 @@ suite('gr-repo-access tests', () => {
await element._repoChanged('Another New Repo' as RepoName);
assert.deepEqual(
- element._sections,
+ element.sections,
toSortedPermissionsArray(accessRes2.local)
);
assert.equal(
@@ -205,56 +205,66 @@ suite('gr-repo-access tests', () => {
assert.isFalse(repoStub.called);
});
- test('_computeParentHref', () => {
- assert.equal(
- element._computeParentHref('test-repo' as RepoName),
- '/admin/repos/test-repo,access'
- );
+ test('computeParentHref', () => {
+ element.inheritsFrom!.name = 'test-repo' as RepoName;
+ assert.equal(element.computeParentHref(), '/admin/repos/test-repo,access');
});
- test('_computeMainClass', () => {
- let ownerOf = ['refs/*'] as GitRef[];
- const editing = true;
- const canUpload = false;
- assert.equal(element._computeMainClass(ownerOf, canUpload, false), 'admin');
- assert.equal(
- element._computeMainClass(ownerOf, canUpload, editing),
- 'admin editing'
- );
- ownerOf = [];
- assert.equal(element._computeMainClass(ownerOf, canUpload, false), '');
- assert.equal(
- element._computeMainClass(ownerOf, canUpload, editing),
- 'editing'
- );
+ test('computeMainClass', () => {
+ element.ownerOf = ['refs/*'] as GitRef[];
+ element.editing = false;
+ element.canUpload = false;
+ assert.equal(element.computeMainClass(), 'admin');
+ element.editing = true;
+ assert.equal(element.computeMainClass(), 'admin editing');
+ element.ownerOf = [];
+ element.editing = false;
+ assert.equal(element.computeMainClass(), '');
+ element.editing = true;
+ assert.equal(element.computeMainClass(), 'editing');
});
test('inherit section', async () => {
- element._local = {};
- element._ownerOf = [];
- const computeParentHrefStub = sinon.stub(element, '_computeParentHref');
- await flush();
+ element.local = {};
+ element.ownerOf = [];
+ const computeParentHrefStub = sinon.stub(element, 'computeParentHref');
+ await element.updateComplete;
// Nothing should appear when no inherit from and not in edit mode.
- assert.equal(getComputedStyle(element.$.inheritsFrom).display, 'none');
- // The autocomplete should be hidden, and the link should be displayed.
- assert.isFalse(computeParentHrefStub.called);
+ assert.equal(
+ getComputedStyle(
+ queryAndAssert<HTMLHeadingElement>(element, '#inheritsFrom')
+ ).display,
+ 'none'
+ );
// When in edit mode, the autocomplete should appear.
- element._editing = true;
+ element.editing = true;
// When editing, the autocomplete should still not be shown.
- assert.equal(getComputedStyle(element.$.inheritsFrom).display, 'none');
+ assert.equal(
+ getComputedStyle(
+ queryAndAssert<HTMLHeadingElement>(element, '#inheritsFrom')
+ ).display,
+ 'none'
+ );
- element._editing = false;
- element._inheritsFrom = {
+ element.editing = false;
+ element.inheritsFrom = {
id: '1234' as UrlEncodedRepoName,
name: 'another-repo' as RepoName,
};
- await flush();
+ await element.updateComplete;
// When there is a parent project, the link should be displayed.
- assert.notEqual(getComputedStyle(element.$.inheritsFrom).display, 'none');
assert.notEqual(
- getComputedStyle(element.$.inheritFromName).display,
+ getComputedStyle(
+ queryAndAssert<HTMLHeadingElement>(element, '#inheritsFrom')
+ ).display,
+ 'none'
+ );
+ assert.notEqual(
+ getComputedStyle(
+ queryAndAssert<HTMLAnchorElement>(element, '#inheritFromName')
+ ).display,
'none'
);
assert.equal(
@@ -264,10 +274,21 @@ suite('gr-repo-access tests', () => {
'none'
);
assert.isTrue(computeParentHrefStub.called);
- element._editing = true;
+ element.editing = true;
+ await element.updateComplete;
// When editing, the autocomplete should be shown.
- assert.notEqual(getComputedStyle(element.$.inheritsFrom).display, 'none');
- assert.equal(getComputedStyle(element.$.inheritFromName).display, 'none');
+ assert.notEqual(
+ getComputedStyle(
+ queryAndAssert<HTMLHeadingElement>(element, '#inheritsFrom')
+ ).display,
+ 'none'
+ );
+ assert.equal(
+ getComputedStyle(
+ queryAndAssert<HTMLAnchorElement>(element, '#inheritFromName')
+ ).display,
+ 'none'
+ );
assert.notEqual(
getComputedStyle(
queryAndAssert<GrAutocomplete>(element, '#editInheritFromInput')
@@ -276,20 +297,16 @@ suite('gr-repo-access tests', () => {
);
});
- test('_handleUpdateInheritFrom', async () => {
- element._inheritFromFilter = 'foo bar baz' as RepoName;
- element._handleUpdateInheritFrom({
+ test('handleUpdateInheritFrom', async () => {
+ element.inheritFromFilter = 'foo bar baz' as RepoName;
+ await element.updateComplete;
+ element.handleUpdateInheritFrom({
detail: {value: 'abc+123'},
} as CustomEvent);
- await flush();
- assert.isOk(element._inheritsFrom);
- assert.equal(element._inheritsFrom!.id, 'abc+123');
- assert.equal(element._inheritsFrom!.name, 'foo bar baz' as RepoName);
- });
-
- test('_computeLoadingClass', () => {
- assert.equal(element._computeLoadingClass(true), 'loading');
- assert.equal(element._computeLoadingClass(false), '');
+ await element.updateComplete;
+ assert.isOk(element.inheritsFrom);
+ assert.equal(element.inheritsFrom!.id, 'abc+123');
+ assert.equal(element.inheritsFrom!.name, 'foo bar baz' as RepoName);
});
test('fires page-error', async () => {
@@ -341,10 +358,10 @@ suite('gr-repo-access tests', () => {
).display,
'none'
);
- element._inheritsFrom = {
+ element.inheritsFrom = {
id: 'test-project' as UrlEncodedRepoName,
};
- await flush();
+ await element.updateComplete;
assert.equal(
getComputedStyle(
queryAndAssert<GrAutocomplete>(element, '#editInheritFromInput')
@@ -352,8 +369,8 @@ suite('gr-repo-access tests', () => {
'none'
);
- MockInteractions.tap(queryAndAssert<GrButton>(element, '#editBtn'));
- await flush();
+ queryAndAssert<GrButton>(element, '#editBtn').click();
+ await element.updateComplete;
// Edit button changes to Cancel button, and Save button is visible but
// disabled.
@@ -406,19 +423,19 @@ suite('gr-repo-access tests', () => {
setup(async () => {
// Create deep copies of these objects so the originals are not modified
// by any tests.
- element._local = JSON.parse(JSON.stringify(accessRes.local));
- element._ownerOf = [];
- element._sections = toSortedPermissionsArray(element._local);
- element._groups = JSON.parse(JSON.stringify(accessRes.groups));
- element._capabilities = JSON.parse(JSON.stringify(capabilitiesRes));
- element._labels = JSON.parse(JSON.stringify(repoRes.labels));
- await flush();
+ element.local = JSON.parse(JSON.stringify(accessRes.local));
+ element.ownerOf = [];
+ element.sections = toSortedPermissionsArray(element.local);
+ element.groups = JSON.parse(JSON.stringify(accessRes.groups));
+ element.capabilities = JSON.parse(JSON.stringify(capabilitiesRes));
+ element.labels = JSON.parse(JSON.stringify(repoRes.labels));
+ await element.updateComplete;
});
test('removing an added section', async () => {
- element._editing = true;
- await flush();
- assert.equal(element._sections!.length, 1);
+ element.editing = true;
+ await element.updateComplete;
+ assert.equal(element.sections!.length, 1);
queryAndAssert<GrAccessSection>(
element,
'gr-access-section'
@@ -428,31 +445,38 @@ suite('gr-repo-access tests', () => {
bubbles: true,
})
);
- await flush();
- assert.equal(element._sections!.length, 0);
+ await element.updateComplete;
+ assert.equal(element.sections!.length, 0);
});
- test('button visibility for non ref owner', () => {
- assert.equal(getComputedStyle(element.$.saveReviewBtn).display, 'none');
- assert.equal(getComputedStyle(element.$.editBtn).display, 'none');
+ test('button visibility for non ref owner', async () => {
+ assert.equal(
+ getComputedStyle(queryAndAssert<GrButton>(element, '#saveReviewBtn'))
+ .display,
+ 'none'
+ );
+ assert.equal(
+ getComputedStyle(queryAndAssert<GrButton>(element, '#editBtn')).display,
+ 'none'
+ );
});
test('button visibility for non ref owner with upload privilege', async () => {
- element._canUpload = true;
- await flush();
+ element.canUpload = true;
+ await element.updateComplete;
testEditSaveCancelBtns(false, true);
});
test('button visibility for ref owner', async () => {
- element._ownerOf = ['refs/for/*'] as GitRef[];
- await flush();
+ element.ownerOf = ['refs/for/*'] as GitRef[];
+ await element.updateComplete;
testEditSaveCancelBtns(true, false);
});
test('button visibility for ref owner and upload', async () => {
- element._ownerOf = ['refs/for/*'] as GitRef[];
- element._canUpload = true;
- await flush();
+ element.ownerOf = ['refs/for/*'] as GitRef[];
+ element.canUpload = true;
+ await element.updateComplete;
testEditSaveCancelBtns(true, false);
});
@@ -467,15 +491,15 @@ suite('gr-repo-access tests', () => {
bubbles: true,
})
);
- await flush();
+ await element.updateComplete;
assert.isTrue(handleAccessModifiedSpy.called);
});
test('_handleAccessModified called when parent changes', async () => {
- element._inheritsFrom = {
+ element.inheritsFrom = {
id: 'test-project' as UrlEncodedRepoName,
};
- await flush();
+ await element.updateComplete;
queryAndAssert<GrAutocomplete>(
element,
'#editInheritFromInput'
@@ -497,22 +521,22 @@ suite('gr-repo-access tests', () => {
bubbles: true,
})
);
- await flush();
+ await element.updateComplete;
assert.isTrue(handleAccessModifiedSpy.called);
});
- test('_handleSaveForReview', async () => {
+ test('handleSaveForReview', async () => {
const saveStub = stubRestApi('setRepoAccessRightsForReview');
- sinon.stub(element, '_computeAddAndRemove').returns({
+ sinon.stub(element, 'computeAddAndRemove').returns({
add: {},
remove: {},
});
- element._handleSaveForReview(new Event('test'));
- await flush();
+ element.handleSaveForReview(new Event('test'));
+ await element.updateComplete;
assert.isFalse(saveStub.called);
});
- test('_recursivelyRemoveDeleted', () => {
+ test('recursivelyRemoveDeleted', () => {
const obj = {
'refs/*': {
permissions: {
@@ -542,11 +566,11 @@ suite('gr-repo-access tests', () => {
},
},
};
- element._recursivelyRemoveDeleted(obj);
+ element.recursivelyRemoveDeleted(obj);
assert.deepEqual(obj, expectedResult);
});
- test('_recursivelyUpdateAddRemoveObj on new added section', () => {
+ test('recursivelyUpdateAddRemoveObj on new added section', () => {
const obj = {
'refs/for/*': {
permissions: {
@@ -608,46 +632,46 @@ suite('gr-repo-access tests', () => {
remove: {},
};
const updateObj = {add: {}, remove: {}};
- element._recursivelyUpdateAddRemoveObj(obj, updateObj);
+ element.recursivelyUpdateAddRemoveObj(obj, updateObj);
assert.deepEqual(updateObj, expectedResult);
});
- test('_handleSaveForReview with no changes', () => {
- assert.deepEqual(element._computeAddAndRemove(), {add: {}, remove: {}});
+ test('handleSaveForReview with no changes', () => {
+ assert.deepEqual(element.computeAddAndRemove(), {add: {}, remove: {}});
});
- test('_handleSaveForReview parent change', async () => {
- element._inheritsFrom = {
+ test('handleSaveForReview parent change', async () => {
+ element.inheritsFrom = {
id: 'test-project' as UrlEncodedRepoName,
};
element.originalInheritsFrom = {
id: 'test-project-original' as UrlEncodedRepoName,
};
- await flush();
- assert.deepEqual(element._computeAddAndRemove(), {
+ await element.updateComplete;
+ assert.deepEqual(element.computeAddAndRemove(), {
parent: 'test-project',
add: {},
remove: {},
});
});
- test('_handleSaveForReview new parent with spaces', async () => {
- element._inheritsFrom = {
+ test('handleSaveForReview new parent with spaces', async () => {
+ element.inheritsFrom = {
id: 'spaces+in+project+name' as UrlEncodedRepoName,
};
element.originalInheritsFrom = {id: 'old-project' as UrlEncodedRepoName};
- await flush();
- assert.deepEqual(element._computeAddAndRemove(), {
+ await element.updateComplete;
+ assert.deepEqual(element.computeAddAndRemove(), {
parent: 'spaces in project name',
add: {},
remove: {},
});
});
- test('_handleSaveForReview rules', async () => {
+ test('handleSaveForReview rules', async () => {
// Delete a rule.
- element._local!['refs/*'].permissions.owner.rules[123].deleted = true;
- await flush();
+ element.local!['refs/*'].permissions.owner.rules[123].deleted = true;
+ await element.updateComplete;
let expectedInput = {
add: {},
remove: {
@@ -662,14 +686,14 @@ suite('gr-repo-access tests', () => {
},
},
};
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Undo deleting a rule.
- delete element._local!['refs/*'].permissions.owner.rules[123].deleted;
+ delete element.local!['refs/*'].permissions.owner.rules[123].deleted;
// Modify a rule.
- element._local!['refs/*'].permissions.owner.rules[123].modified = true;
- await flush();
+ element.local!['refs/*'].permissions.owner.rules[123].modified = true;
+ await element.updateComplete;
expectedInput = {
add: {
'refs/*': {
@@ -694,10 +718,10 @@ suite('gr-repo-access tests', () => {
},
},
};
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
});
- test('_computeAddAndRemove permissions', async () => {
+ test('computeAddAndRemove permissions', async () => {
// Add a new rule to a permission.
let expectedInput = {};
@@ -722,22 +746,20 @@ suite('gr-repo-access tests', () => {
element,
'gr-access-section'
);
- queryAndAssert<GrPermission>(
+ await queryAndAssert<GrPermission>(
grAccessSection,
'gr-permission'
).handleAddRuleItem({
detail: {value: 'Maintainers'},
} as AutocompleteCommitEvent);
-
- await flush();
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Remove the added rule.
- delete element._local!['refs/*'].permissions.owner.rules.Maintainers;
+ delete element.local!['refs/*'].permissions.owner.rules.Maintainers;
// Delete a permission.
- element._local!['refs/*'].permissions.owner.deleted = true;
- await flush();
+ element.local!['refs/*'].permissions.owner.deleted = true;
+ await element.updateComplete;
expectedInput = {
add: {},
@@ -749,14 +771,14 @@ suite('gr-repo-access tests', () => {
},
},
};
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Undo delete permission.
- delete element._local!['refs/*'].permissions.owner.deleted;
+ delete element.local!['refs/*'].permissions.owner.deleted;
// Modify a permission.
- element._local!['refs/*'].permissions.owner.modified = true;
- await flush();
+ element.local!['refs/*'].permissions.owner.modified = true;
+ await element.updateComplete;
expectedInput = {
add: {
'refs/*': {
@@ -779,10 +801,10 @@ suite('gr-repo-access tests', () => {
},
},
};
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
});
- test('_computeAddAndRemove sections', async () => {
+ test('computeAddAndRemove sections', async () => {
// Add a new permission to a section
let expectedInput = {};
@@ -803,9 +825,9 @@ suite('gr-repo-access tests', () => {
queryAndAssert<GrAccessSection>(
element,
'gr-access-section'
- )._handleAddPermission();
- await flush();
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ ).handleAddPermission();
+ await element.updateComplete;
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Add a new rule to the new permission.
expectedInput = {
@@ -833,20 +855,19 @@ suite('gr-repo-access tests', () => {
element,
'gr-access-section'
);
- const newPermission = queryAll<GrPermission>(
+ await queryAll<GrPermission>(
grAccessSection,
'gr-permission'
- )[2];
- newPermission.handleAddRuleItem({
+ )[2].handleAddRuleItem({
detail: {value: 'Maintainers'},
} as AutocompleteCommitEvent);
- await flush();
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Modify a section reference.
- element._local!['refs/*'].updatedId = 'refs/for/bar';
- element._local!['refs/*'].modified = true;
- await flush();
+ element.local!['refs/*'].updatedId = 'refs/for/bar';
+ element.local!['refs/*'].modified = true;
+ await element.updateComplete;
+
expectedInput = {
add: {
'refs/for/bar': {
@@ -885,12 +906,11 @@ suite('gr-repo-access tests', () => {
},
},
};
- await flush();
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Delete a section.
- element._local!['refs/*'].deleted = true;
- await flush();
+ element.local!['refs/*'].deleted = true;
+ await element.updateComplete;
expectedInput = {
add: {},
remove: {
@@ -899,10 +919,10 @@ suite('gr-repo-access tests', () => {
},
},
};
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
});
- test('_computeAddAndRemove new section', async () => {
+ test('computeAddAndRemove new section', async () => {
// Add a new permission to a section
let expectedInput = {};
@@ -915,9 +935,9 @@ suite('gr-repo-access tests', () => {
},
remove: {},
};
- MockInteractions.tap(element.$.addReferenceBtn);
- await flush();
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ queryAndAssert<GrButton>(element, '#addReferenceBtn').click();
+ await element.updateComplete;
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
expectedInput = {
add: {
@@ -938,9 +958,9 @@ suite('gr-repo-access tests', () => {
element,
'gr-access-section'
)[1];
- newSection._handleAddPermission();
- await flush();
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ newSection.handleAddPermission();
+ await element.updateComplete;
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Add rule to the new permission.
expectedInput = {
@@ -966,18 +986,17 @@ suite('gr-repo-access tests', () => {
remove: {},
};
- queryAndAssert<GrPermission>(
+ await queryAndAssert<GrPermission>(
newSection,
'gr-permission'
).handleAddRuleItem({
detail: {value: 'Maintainers'},
} as AutocompleteCommitEvent);
- await flush();
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Modify a the reference from the default value.
- element._local!['refs/for/*'].updatedId = 'refs/for/new';
- await flush();
+ element.local!['refs/for/*'].updatedId = 'refs/for/new';
+ await element.updateComplete;
expectedInput = {
add: {
'refs/for/new': {
@@ -1001,14 +1020,14 @@ suite('gr-repo-access tests', () => {
},
remove: {},
};
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
});
- test('_computeAddAndRemove combinations', async () => {
+ test('computeAddAndRemove combinations', async () => {
// Modify rule and delete permission that it is inside of.
- element._local!['refs/*'].permissions.owner.rules[123].modified = true;
- element._local!['refs/*'].permissions.owner.deleted = true;
- await flush();
+ element.local!['refs/*'].permissions.owner.rules[123].modified = true;
+ element.local!['refs/*'].permissions.owner.deleted = true;
+ await element.updateComplete;
let expectedInput = {};
expectedInput = {
@@ -1021,16 +1040,16 @@ suite('gr-repo-access tests', () => {
},
},
};
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Delete rule and delete permission that it is inside of.
- element._local!['refs/*'].permissions.owner.rules[123].modified = false;
- element._local!['refs/*'].permissions.owner.rules[123].deleted = true;
- await flush();
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ element.local!['refs/*'].permissions.owner.rules[123].modified = false;
+ element.local!['refs/*'].permissions.owner.rules[123].deleted = true;
+ await element.updateComplete;
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Also modify a different rule inside of another permission.
- element._local!['refs/*'].permissions.read.modified = true;
- await flush();
+ element.local!['refs/*'].permissions.read.modified = true;
+ await element.updateComplete;
expectedInput = {
add: {
'refs/*': {
@@ -1053,14 +1072,14 @@ suite('gr-repo-access tests', () => {
},
},
};
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Modify both permissions with an exclusive bit. Owner is still
// deleted.
- element._local!['refs/*'].permissions.owner.exclusive = true;
- element._local!['refs/*'].permissions.owner.modified = true;
- element._local!['refs/*'].permissions.read.exclusive = true;
- element._local!['refs/*'].permissions.read.modified = true;
- await flush();
+ element.local!['refs/*'].permissions.owner.exclusive = true;
+ element.local!['refs/*'].permissions.owner.modified = true;
+ element.local!['refs/*'].permissions.read.exclusive = true;
+ element.local!['refs/*'].permissions.read.modified = true;
+ await element.updateComplete;
expectedInput = {
add: {
'refs/*': {
@@ -1084,21 +1103,19 @@ suite('gr-repo-access tests', () => {
},
},
};
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Add a rule to the existing permission;
const grAccessSection = queryAndAssert<GrAccessSection>(
element,
'gr-access-section'
);
- const readPermission = queryAll<GrPermission>(
+ await queryAll<GrPermission>(
grAccessSection,
'gr-permission'
- )[1];
- readPermission.handleAddRuleItem({
+ )[1].handleAddRuleItem({
detail: {value: 'Maintainers'},
} as AutocompleteCommitEvent);
- await flush();
expectedInput = {
add: {
@@ -1124,12 +1141,12 @@ suite('gr-repo-access tests', () => {
},
},
};
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Change one of the refs
- element._local!['refs/*'].updatedId = 'refs/for/bar';
- element._local!['refs/*'].modified = true;
- await flush();
+ element.local!['refs/*'].updatedId = 'refs/for/bar';
+ element.local!['refs/*'].modified = true;
+ await element.updateComplete;
expectedInput = {
add: {
@@ -1154,7 +1171,7 @@ suite('gr-repo-access tests', () => {
},
},
};
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
expectedInput = {
add: {},
@@ -1164,27 +1181,28 @@ suite('gr-repo-access tests', () => {
},
},
};
- element._local!['refs/*'].deleted = true;
- await flush();
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ element.local!['refs/*'].deleted = true;
+ await element.updateComplete;
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Add a new section.
- MockInteractions.tap(element.$.addReferenceBtn);
+ queryAndAssert<GrButton>(element, '#addReferenceBtn').click();
+ await element.updateComplete;
let newSection = queryAll<GrAccessSection>(
element,
'gr-access-section'
)[1];
- newSection._handleAddPermission();
- await flush();
- queryAndAssert<GrPermission>(
+ newSection.handleAddPermission();
+ await element.updateComplete;
+ await queryAndAssert<GrPermission>(
newSection,
'gr-permission'
).handleAddRuleItem({
detail: {value: 'Maintainers'},
} as AutocompleteCommitEvent);
// Modify a the reference from the default value.
- element._local!['refs/for/*'].updatedId = 'refs/for/new';
- await flush();
+ element.local!['refs/for/*'].updatedId = 'refs/for/new';
+ await element.updateComplete;
expectedInput = {
add: {
@@ -1213,13 +1231,13 @@ suite('gr-repo-access tests', () => {
},
},
};
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Modify newly added rule inside new ref.
- element._local!['refs/for/*'].permissions['label-Code-Review'].rules[
+ element.local!['refs/for/*'].permissions['label-Code-Review'].rules[
'Maintainers'
].modified = true;
- await flush();
+ await element.updateComplete;
expectedInput = {
add: {
'refs/for/new': {
@@ -1248,23 +1266,23 @@ suite('gr-repo-access tests', () => {
},
},
};
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
// Add a second new section.
- MockInteractions.tap(element.$.addReferenceBtn);
- await flush();
+ queryAndAssert<GrButton>(element, '#addReferenceBtn').click();
+ await element.updateComplete;
newSection = queryAll<GrAccessSection>(element, 'gr-access-section')[2];
- newSection._handleAddPermission();
- await flush();
- queryAndAssert<GrPermission>(
+ newSection.handleAddPermission();
+ await element.updateComplete;
+ await queryAndAssert<GrPermission>(
newSection,
'gr-permission'
).handleAddRuleItem({
detail: {value: 'Maintainers'},
} as AutocompleteCommitEvent);
// Modify a the reference from the default value.
- element._local!['refs/for/**'].updatedId = 'refs/for/new2';
- await flush();
+ element.local!['refs/for/**'].updatedId = 'refs/for/new2';
+ await element.updateComplete;
expectedInput = {
add: {
'refs/for/new': {
@@ -1311,26 +1329,26 @@ suite('gr-repo-access tests', () => {
},
},
};
- assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+ assert.deepEqual(element.computeAddAndRemove(), expectedInput);
});
test('Unsaved added refs are discarded when edit cancelled', async () => {
// Unsaved changes are discarded when editing is cancelled.
- MockInteractions.tap(element.$.editBtn);
- await flush();
- assert.equal(element._sections!.length, 1);
- assert.equal(Object.keys(element._local!).length, 1);
- MockInteractions.tap(element.$.addReferenceBtn);
- await flush();
- assert.equal(element._sections!.length, 2);
- assert.equal(Object.keys(element._local!).length, 2);
- MockInteractions.tap(element.$.editBtn);
- await flush();
- assert.equal(element._sections!.length, 1);
- assert.equal(Object.keys(element._local!).length, 1);
+ queryAndAssert<GrButton>(element, '#editBtn').click();
+ await element.updateComplete;
+ assert.equal(element.sections!.length, 1);
+ assert.equal(Object.keys(element.local!).length, 1);
+ queryAndAssert<GrButton>(element, '#addReferenceBtn').click();
+ await element.updateComplete;
+ assert.equal(element.sections!.length, 2);
+ assert.equal(Object.keys(element.local!).length, 2);
+ queryAndAssert<GrButton>(element, '#editBtn').click();
+ await element.updateComplete;
+ assert.equal(element.sections!.length, 1);
+ assert.equal(Object.keys(element.local!).length, 1);
});
- test('_handleSave', async () => {
+ test('handleSave', async () => {
const repoAccessInput = {
add: {
'refs/*': {
@@ -1365,19 +1383,22 @@ suite('gr-repo-access tests', () => {
);
element.repo = 'test-repo' as RepoName;
- sinon.stub(element, '_computeAddAndRemove').returns(repoAccessInput);
+ sinon.stub(element, 'computeAddAndRemove').returns(repoAccessInput);
- element._modified = true;
- MockInteractions.tap(element.$.saveBtn);
- await flush();
- assert.equal(element.$.saveBtn.hasAttribute('loading'), true);
+ element.modified = true;
+ queryAndAssert<GrButton>(element, '#saveBtn').click();
+ await element.updateComplete;
+ assert.equal(
+ queryAndAssert<GrButton>(element, '#saveBtn').hasAttribute('loading'),
+ true
+ );
resolver!({status: 200} as Response);
- await flush();
+ await element.updateComplete;
assert.isTrue(saveStub.called);
assert.isTrue(navigateToChangeStub.notCalled);
});
- test('_handleSaveForReview', async () => {
+ test('handleSaveForReview', async () => {
const repoAccessInput = {
add: {
'refs/*': {
@@ -1412,14 +1433,19 @@ suite('gr-repo-access tests', () => {
).returns(new Promise(r => (resolver = r)));
element.repo = 'test-repo' as RepoName;
- sinon.stub(element, '_computeAddAndRemove').returns(repoAccessInput);
+ sinon.stub(element, 'computeAddAndRemove').returns(repoAccessInput);
- element._modified = true;
- MockInteractions.tap(element.$.saveReviewBtn);
- await flush();
- assert.equal(element.$.saveReviewBtn.hasAttribute('loading'), true);
+ element.modified = true;
+ queryAndAssert<GrButton>(element, '#saveReviewBtn').click();
+ await element.updateComplete;
+ assert.equal(
+ queryAndAssert<GrButton>(element, '#saveReviewBtn').hasAttribute(
+ 'loading'
+ ),
+ true
+ );
resolver!(createChange());
- await flush();
+ await element.updateComplete;
assert.isTrue(saveForReviewStub.called);
assert.isTrue(
navigateToChangeStub.lastCall.calledWithExactly(createChange())