Convert gr-repo-access_test.js to typescript

Change-Id: I68af2ac63ad8368b8b4f4d0fcae49865122777e2
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 7e04281..cf5d952 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
@@ -115,7 +115,8 @@
   @property({type: Boolean})
   _loading = true;
 
-  private originalInheritsFrom?: ProjectInfo;
+  // private but used in the tests
+  originalInheritsFrom?: ProjectInfo;
 
   private readonly restApiService = getAppContext().restApiService;
 
@@ -131,7 +132,7 @@
     this._modified = true;
   }
 
-  _repoChanged(repo: RepoName) {
+  _repoChanged(repo?: RepoName) {
     this._loading = true;
 
     if (!repo) {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.js b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.ts
similarity index 65%
rename from polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.js
rename to polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.ts
index 1ccfd5e..a5159ae 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.ts
@@ -15,23 +15,42 @@
  * limitations under the License.
  */
 
-import '../../../test/common-test-setup-karma.js';
-import './gr-repo-access.js';
-import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
-import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
-import {toSortedPermissionsArray} from '../../../utils/access-util.js';
+import '../../../test/common-test-setup-karma';
+import './gr-repo-access';
+import {GrRepoAccess} from './gr-repo-access';
+import {GerritNav} from '../../core/gr-navigation/gr-navigation';
+import {toSortedPermissionsArray} from '../../../utils/access-util';
 import {
   addListenerForTest,
   mockPromise,
+  queryAll,
+  queryAndAssert,
   stubRestApi,
-} from '../../../test/test-utils.js';
+} from '../../../test/test-utils';
+import {
+  ChangeInfo,
+  GitRef,
+  RepoName,
+  UrlEncodedRepoName,
+} from '../../../types/common';
+import {PermissionAction} from '../../../constants/constants';
+import {PageErrorEvent} from '../../../types/events';
+import {GrButton} from '../../shared/gr-button/gr-button';
+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');
 
 suite('gr-repo-access tests', () => {
-  let element;
+  let element: GrRepoAccess;
 
-  let repoStub;
+  let repoStub: sinon.SinonStub;
 
   const accessRes = {
     local: {
@@ -39,13 +58,13 @@
         permissions: {
           owner: {
             rules: {
-              234: {action: 'ALLOW'},
-              123: {action: 'DENY'},
+              234: {action: PermissionAction.ALLOW},
+              123: {action: PermissionAction.DENY},
             },
           },
           read: {
             rules: {
-              234: {action: 'ALLOW'},
+              234: {action: PermissionAction.ALLOW},
             },
           },
         },
@@ -59,11 +78,13 @@
         name: 'Maintainers',
       },
     },
-    config_web_links: [{
-      name: 'gitiles',
-      target: '_blank',
-      url: 'https://my/site/+log/123/project.config',
-    }],
+    config_web_links: [
+      {
+        name: 'gitiles',
+        target: '_blank',
+        url: 'https://my/site/+log/123/project.config',
+      },
+    ],
     can_upload: true,
   };
   const accessRes2 = {
@@ -73,7 +94,7 @@
           accessDatabase: {
             rules: {
               group1: {
-                action: 'ALLOW',
+                action: PermissionAction.ALLOW,
               },
             },
           },
@@ -82,15 +103,17 @@
     },
   };
   const repoRes = {
+    id: '' as UrlEncodedRepoName,
     labels: {
       'Code-Review': {
         values: {
-          ' 0': 'No score',
+          '0': 'No score',
           '-1': 'I would prefer this is not merged as is',
           '-2': 'This shall not be merged',
           '+1': 'Looks good to me, but someone else must approve',
           '+2': 'Looks good to me, approved',
         },
+        default_value: 0,
       },
     },
   };
@@ -106,7 +129,7 @@
   };
   setup(async () => {
     element = basicFixture.instantiate();
-    stubRestApi('getAccount').returns(Promise.resolve(null));
+    stubRestApi('getAccount').returns(Promise.resolve(undefined));
     repoStub = stubRestApi('getRepo').returns(Promise.resolve(repoRes));
     element._loading = false;
     element._ownerOf = [];
@@ -115,43 +138,51 @@
   });
 
   test('_repoChanged called when repo name changes', async () => {
-    sinon.stub(element, '_repoChanged');
-    element.repo = 'New Repo';
+    const repoChangedStub = sinon.stub(element, '_repoChanged');
+    element.repo = 'New Repo' as RepoName;
     await flush();
-    assert.isTrue(element._repoChanged.called);
+    assert.isTrue(repoChangedStub.called);
   });
 
   test('_repoChanged', async () => {
-    const accessStub = stubRestApi(
-        'getRepoAccessRights');
+    const accessStub = stubRestApi('getRepoAccessRights');
 
-    accessStub.withArgs('New Repo').returns(
-        Promise.resolve(JSON.parse(JSON.stringify(accessRes))));
-    accessStub.withArgs('Another New Repo')
-        .returns(Promise.resolve(JSON.parse(JSON.stringify(accessRes2))));
-    const capabilitiesStub = stubRestApi(
-        'getCapabilities');
+    accessStub
+      .withArgs('New Repo' as RepoName)
+      .returns(Promise.resolve(JSON.parse(JSON.stringify(accessRes))));
+    accessStub
+      .withArgs('Another New Repo' as RepoName)
+      .returns(Promise.resolve(JSON.parse(JSON.stringify(accessRes2))));
+    const capabilitiesStub = stubRestApi('getCapabilities');
     capabilitiesStub.returns(Promise.resolve(capabilitiesRes));
 
-    await element._repoChanged('New Repo');
+    await element._repoChanged('New Repo' as RepoName);
     assert.isTrue(accessStub.called);
     assert.isTrue(capabilitiesStub.called);
     assert.isTrue(repoStub.called);
     assert.isNotOk(element._inheritsFrom);
     assert.deepEqual(element._local, accessRes.local);
-    assert.deepEqual(element._sections,
-        toSortedPermissionsArray(accessRes.local));
+    assert.deepEqual(
+      element._sections,
+      toSortedPermissionsArray(accessRes.local)
+    );
     assert.deepEqual(element._labels, repoRes.labels);
-    assert.equal(getComputedStyle(element.shadowRoot
-        .querySelector('.weblinks')).display,
-    'block');
+    assert.equal(
+      getComputedStyle(queryAndAssert<HTMLDivElement>(element, '.weblinks'))
+        .display,
+      'block'
+    );
 
-    await element._repoChanged('Another New Repo');
-    assert.deepEqual(element._sections,
-        toSortedPermissionsArray(accessRes2.local));
-    assert.equal(getComputedStyle(element.shadowRoot
-        .querySelector('.weblinks')).display,
-    'none');
+    await element._repoChanged('Another New Repo' as RepoName);
+    assert.deepEqual(
+      element._sections,
+      toSortedPermissionsArray(accessRes2.local)
+    );
+    assert.equal(
+      getComputedStyle(queryAndAssert<HTMLDivElement>(element, '.weblinks'))
+        .display,
+      'none'
+    );
   });
 
   test('_repoChanged when repo changes to undefined returns', async () => {
@@ -161,10 +192,12 @@
         name: 'Access Database',
       },
     };
-    const accessStub = stubRestApi('getRepoAccessRights')
-        .returns(Promise.resolve(JSON.parse(JSON.stringify(accessRes2))));
-    const capabilitiesStub = stubRestApi(
-        'getCapabilities').returns(Promise.resolve(capabilitiesRes));
+    const accessStub = stubRestApi('getRepoAccessRights').returns(
+      Promise.resolve(JSON.parse(JSON.stringify(accessRes2)))
+    );
+    const capabilitiesStub = stubRestApi('getCapabilities').returns(
+      Promise.resolve(capabilitiesRes)
+    );
 
     await element._repoChanged();
     assert.isFalse(accessStub.called);
@@ -173,34 +206,39 @@
   });
 
   test('_computeParentHref', () => {
-    const repoName = 'test-repo';
-    assert.equal(element._computeParentHref(repoName),
-        '/admin/repos/test-repo,access');
+    assert.equal(
+      element._computeParentHref('test-repo' as RepoName),
+      '/admin/repos/test-repo,access'
+    );
   });
 
   test('_computeMainClass', () => {
-    let ownerOf = ['refs/*'];
+    let ownerOf = ['refs/*'] as GitRef[];
     const editing = true;
     const canUpload = false;
-    assert.equal(element._computeMainClass(ownerOf, canUpload), 'admin');
-    assert.equal(element._computeMainClass(ownerOf, canUpload, editing),
-        'admin editing');
+    assert.equal(element._computeMainClass(ownerOf, canUpload, false), 'admin');
+    assert.equal(
+      element._computeMainClass(ownerOf, canUpload, editing),
+      'admin editing'
+    );
     ownerOf = [];
-    assert.equal(element._computeMainClass(ownerOf, canUpload), '');
-    assert.equal(element._computeMainClass(ownerOf, canUpload, editing),
-        'editing');
+    assert.equal(element._computeMainClass(ownerOf, canUpload, false), '');
+    assert.equal(
+      element._computeMainClass(ownerOf, canUpload, editing),
+      'editing'
+    );
   });
 
   test('inherit section', async () => {
     element._local = {};
     element._ownerOf = [];
-    sinon.stub(element, '_computeParentHref');
+    const computeParentHrefStub = sinon.stub(element, '_computeParentHref');
     await flush();
 
     // 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(element._computeParentHref.called);
+    assert.isFalse(computeParentHrefStub.called);
     // When in edit mode, the autocomplete should appear.
     element._editing = true;
     // When editing, the autocomplete should still not be shown.
@@ -208,33 +246,45 @@
 
     element._editing = false;
     element._inheritsFrom = {
-      id: '1234',
-      name: 'another-repo',
+      id: '1234' as UrlEncodedRepoName,
+      name: 'another-repo' as RepoName,
     };
     await flush();
 
     // When there is a parent project, the link should be displayed.
     assert.notEqual(getComputedStyle(element.$.inheritsFrom).display, 'none');
-    assert.notEqual(getComputedStyle(element.$.inheritFromName).display,
-        'none');
-    assert.equal(getComputedStyle(element.$.editInheritFromInput).display,
-        'none');
-    assert.isTrue(element._computeParentHref.called);
+    assert.notEqual(
+      getComputedStyle(element.$.inheritFromName).display,
+      'none'
+    );
+    assert.equal(
+      getComputedStyle(
+        queryAndAssert<GrAutocomplete>(element, '#editInheritFromInput')
+      ).display,
+      'none'
+    );
+    assert.isTrue(computeParentHrefStub.called);
     element._editing = true;
     // When editing, the autocomplete should be shown.
     assert.notEqual(getComputedStyle(element.$.inheritsFrom).display, 'none');
     assert.equal(getComputedStyle(element.$.inheritFromName).display, 'none');
-    assert.notEqual(getComputedStyle(element.$.editInheritFromInput).display,
-        'none');
+    assert.notEqual(
+      getComputedStyle(
+        queryAndAssert<GrAutocomplete>(element, '#editInheritFromInput')
+      ).display,
+      'none'
+    );
   });
 
   test('_handleUpdateInheritFrom', async () => {
-    element._inheritFromFilter = 'foo bar baz';
-    element._handleUpdateInheritFrom({detail: {value: 'abc+123'}});
+    element._inheritFromFilter = 'foo bar baz' as RepoName;
+    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');
+    assert.equal(element._inheritsFrom!.id, 'abc+123');
+    assert.equal(element._inheritsFrom!.name, 'foo bar baz' as RepoName);
   });
 
   test('_computeLoadingClass', () => {
@@ -243,84 +293,113 @@
   });
 
   test('fires page-error', async () => {
-    const response = {status: 404};
+    const response = {status: 404} as Response;
 
-    stubRestApi('getRepoAccessRights').callsFake((repoName, errFn) => {
-      errFn(response);
+    stubRestApi('getRepoAccessRights').callsFake((_repoName, errFn) => {
+      if (errFn !== undefined) {
+        errFn(response);
+      }
       return Promise.resolve(undefined);
     });
 
     const promise = mockPromise();
     addListenerForTest(document, 'page-error', e => {
-      assert.deepEqual(e.detail.response, response);
+      assert.deepEqual((e as PageErrorEvent).detail.response, response);
       promise.resolve();
     });
 
-    element.repo = 'test';
+    element.repo = 'test' as RepoName;
     await promise;
   });
 
   suite('with defined sections', () => {
     const testEditSaveCancelBtns = async (
-        shouldShowSave,
-        shouldShowSaveReview
+      shouldShowSave: boolean,
+      shouldShowSaveReview: boolean
     ) => {
       // Edit button is visible and Save button is hidden.
-      assert.equal(getComputedStyle(element.$.saveReviewBtn).display, 'none');
-      assert.equal(getComputedStyle(element.$.saveBtn).display, 'none');
-      assert.notEqual(getComputedStyle(element.$.editBtn).display, 'none');
-      assert.equal(element.$.editBtn.innerText, 'EDIT');
       assert.equal(
-          getComputedStyle(element.$.editInheritFromInput).display,
-          'none'
+        getComputedStyle(queryAndAssert<GrButton>(element, '#saveReviewBtn'))
+          .display,
+        'none'
+      );
+      assert.equal(
+        getComputedStyle(queryAndAssert<GrButton>(element, '#saveBtn')).display,
+        'none'
+      );
+      assert.notEqual(
+        getComputedStyle(queryAndAssert<GrButton>(element, '#editBtn')).display,
+        'none'
+      );
+      assert.equal(
+        queryAndAssert<GrButton>(element, '#editBtn').innerText,
+        'EDIT'
+      );
+      assert.equal(
+        getComputedStyle(
+          queryAndAssert<GrAutocomplete>(element, '#editInheritFromInput')
+        ).display,
+        'none'
       );
       element._inheritsFrom = {
-        id: 'test-project',
+        id: 'test-project' as UrlEncodedRepoName,
       };
       await flush();
       assert.equal(
-          getComputedStyle(
-              element.shadowRoot.querySelector('#editInheritFromInput')
-          ).display,
-          'none'
+        getComputedStyle(
+          queryAndAssert<GrAutocomplete>(element, '#editInheritFromInput')
+        ).display,
+        'none'
       );
 
-      MockInteractions.tap(element.$.editBtn);
+      MockInteractions.tap(queryAndAssert<GrButton>(element, '#editBtn'));
       await flush();
 
       // Edit button changes to Cancel button, and Save button is visible but
       // disabled.
-      assert.equal(element.$.editBtn.innerText, 'CANCEL');
+      assert.equal(
+        queryAndAssert<GrButton>(element, '#editBtn').innerText,
+        'CANCEL'
+      );
       if (shouldShowSaveReview) {
         assert.notEqual(
-            getComputedStyle(element.$.saveReviewBtn).display,
-            'none'
+          getComputedStyle(queryAndAssert<GrButton>(element, '#saveReviewBtn'))
+            .display,
+          'none'
         );
-        assert.isTrue(element.$.saveReviewBtn.disabled);
+        assert.isTrue(
+          queryAndAssert<GrButton>(element, '#saveReviewBtn').disabled
+        );
       }
       if (shouldShowSave) {
-        assert.notEqual(getComputedStyle(element.$.saveBtn).display, 'none');
-        assert.isTrue(element.$.saveBtn.disabled);
+        assert.notEqual(
+          getComputedStyle(queryAndAssert<GrButton>(element, '#saveBtn'))
+            .display,
+          'none'
+        );
+        assert.isTrue(queryAndAssert<GrButton>(element, '#saveBtn').disabled);
       }
       assert.notEqual(
-          getComputedStyle(
-              element.shadowRoot.querySelector('#editInheritFromInput')
-          ).display,
-          'none'
+        getComputedStyle(
+          queryAndAssert<GrAutocomplete>(element, '#editInheritFromInput')
+        ).display,
+        'none'
       );
 
       // Save button should be enabled after access is modified
       element.dispatchEvent(
-          new CustomEvent('access-modified', {
-            composed: true,
-            bubbles: true,
-          })
+        new CustomEvent('access-modified', {
+          composed: true,
+          bubbles: true,
+        })
       );
       if (shouldShowSaveReview) {
-        assert.isFalse(element.$.saveReviewBtn.disabled);
+        assert.isFalse(
+          queryAndAssert<GrButton>(element, '#saveReviewBtn').disabled
+        );
       }
       if (shouldShowSave) {
-        assert.isFalse(element.$.saveBtn.disabled);
+        assert.isFalse(queryAndAssert<GrButton>(element, '#saveBtn').disabled);
       }
     };
 
@@ -337,16 +416,20 @@
     });
 
     test('removing an added section', async () => {
-      element.editing = true;
+      element._editing = true;
       await flush();
-      assert.equal(element._sections.length, 1);
-      element.shadowRoot
-          .querySelector('gr-access-section').dispatchEvent(
-              new CustomEvent('added-section-removed', {
-                composed: true, bubbles: true,
-              }));
+      assert.equal(element._sections!.length, 1);
+      queryAndAssert<GrAccessSection>(
+        element,
+        'gr-access-section'
+      ).dispatchEvent(
+        new CustomEvent('added-section-removed', {
+          composed: true,
+          bubbles: true,
+        })
+      );
       await flush();
-      assert.equal(element._sections.length, 0);
+      assert.equal(element._sections!.length, 0);
     });
 
     test('button visibility for non ref owner', () => {
@@ -354,64 +437,77 @@
       assert.equal(getComputedStyle(element.$.editBtn).display, 'none');
     });
 
-    test('button visibility for non ref owner with upload privilege',
-        async () => {
-          element._canUpload = true;
-          await flush();
-          testEditSaveCancelBtns(false, true);
-        });
+    test('button visibility for non ref owner with upload privilege', async () => {
+      element._canUpload = true;
+      await flush();
+      testEditSaveCancelBtns(false, true);
+    });
 
     test('button visibility for ref owner', async () => {
-      element._ownerOf = ['refs/for/*'];
+      element._ownerOf = ['refs/for/*'] as GitRef[];
       await flush();
       testEditSaveCancelBtns(true, false);
     });
 
     test('button visibility for ref owner and upload', async () => {
-      element._ownerOf = ['refs/for/*'];
+      element._ownerOf = ['refs/for/*'] as GitRef[];
       element._canUpload = true;
       await flush();
       testEditSaveCancelBtns(true, false);
     });
 
     test('_handleAccessModified called with event fired', async () => {
-      sinon.spy(element, '_handleAccessModified');
+      const handleAccessModifiedSpy = sinon.spy(
+        element,
+        '_handleAccessModified'
+      );
       element.dispatchEvent(
-          new CustomEvent('access-modified', {
-            composed: true, bubbles: true,
-          }));
+        new CustomEvent('access-modified', {
+          composed: true,
+          bubbles: true,
+        })
+      );
       await flush();
-      assert.isTrue(element._handleAccessModified.called);
+      assert.isTrue(handleAccessModifiedSpy.called);
     });
 
     test('_handleAccessModified called when parent changes', async () => {
       element._inheritsFrom = {
-        id: 'test-project',
+        id: 'test-project' as UrlEncodedRepoName,
       };
       await flush();
-      element.shadowRoot.querySelector('#editInheritFromInput').dispatchEvent(
-          new CustomEvent('commit', {
-            detail: {},
-            composed: true, bubbles: true,
-          }));
-      sinon.spy(element, '_handleAccessModified');
+      queryAndAssert<GrAutocomplete>(
+        element,
+        '#editInheritFromInput'
+      ).dispatchEvent(
+        new CustomEvent('commit', {
+          detail: {},
+          composed: true,
+          bubbles: true,
+        })
+      );
+      const handleAccessModifiedSpy = sinon.spy(
+        element,
+        '_handleAccessModified'
+      );
       element.dispatchEvent(
-          new CustomEvent('access-modified', {
-            detail: {},
-            composed: true, bubbles: true,
-          }));
+        new CustomEvent('access-modified', {
+          detail: {},
+          composed: true,
+          bubbles: true,
+        })
+      );
       await flush();
-      assert.isTrue(element._handleAccessModified.called);
+      assert.isTrue(handleAccessModifiedSpy.called);
     });
 
     test('_handleSaveForReview', async () => {
-      const saveStub =
-          stubRestApi('setRepoAccessRightsForReview');
+      const saveStub = stubRestApi('setRepoAccessRightsForReview');
       sinon.stub(element, '_computeAddAndRemove').returns({
         add: {},
         remove: {},
       });
-      element._handleSaveForReview();
+      element._handleSaveForReview(new Event('test'));
       await flush();
       assert.isFalse(saveStub.called);
     });
@@ -522,29 +618,35 @@
 
     test('_handleSaveForReview parent change', async () => {
       element._inheritsFrom = {
-        id: 'test-project',
+        id: 'test-project' as UrlEncodedRepoName,
       };
-      element._originalInheritsFrom = {
-        id: 'test-project-original',
+      element.originalInheritsFrom = {
+        id: 'test-project-original' as UrlEncodedRepoName,
       };
       await flush();
       assert.deepEqual(element._computeAddAndRemove(), {
-        parent: 'test-project', add: {}, remove: {},
+        parent: 'test-project',
+        add: {},
+        remove: {},
       });
     });
 
     test('_handleSaveForReview new parent with spaces', async () => {
-      element._inheritsFrom = {id: 'spaces+in+project+name'};
-      element._originalInheritsFrom = {id: 'old-project'};
+      element._inheritsFrom = {
+        id: 'spaces+in+project+name' as UrlEncodedRepoName,
+      };
+      element.originalInheritsFrom = {id: 'old-project' as UrlEncodedRepoName};
       await flush();
       assert.deepEqual(element._computeAddAndRemove(), {
-        parent: 'spaces in project name', add: {}, remove: {},
+        parent: 'spaces in project name',
+        add: {},
+        remove: {},
       });
     });
 
     test('_handleSaveForReview rules', async () => {
       // Delete a rule.
-      element._local['refs/*'].permissions.owner.rules[123].deleted = true;
+      element._local!['refs/*'].permissions.owner.rules[123].deleted = true;
       await flush();
       let expectedInput = {
         add: {},
@@ -563,10 +665,10 @@
       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;
+      element._local!['refs/*'].permissions.owner.rules[123].modified = true;
       await flush();
       expectedInput = {
         add: {
@@ -597,7 +699,9 @@
 
     test('_computeAddAndRemove permissions', async () => {
       // Add a new rule to a permission.
-      let expectedInput = {
+      let expectedInput = {};
+
+      expectedInput = {
         add: {
           'refs/*': {
             permissions: {
@@ -614,22 +718,27 @@
         },
         remove: {},
       };
-
-      element.shadowRoot
-          .querySelector('gr-access-section').shadowRoot
-          .querySelector('gr-permission')
-          ._handleAddRuleItem(
-              {detail: {value: 'Maintainers'}});
+      const grAccessSection = queryAndAssert<GrAccessSection>(
+        element,
+        'gr-access-section'
+      );
+      queryAndAssert<GrPermission>(
+        grAccessSection,
+        'gr-permission'
+      )._handleAddRuleItem({
+        detail: {value: 'Maintainers'},
+      } as AutocompleteCommitEvent);
 
       await flush();
       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;
+      element._local!['refs/*'].permissions.owner.deleted = true;
       await flush();
+
       expectedInput = {
         add: {},
         remove: {
@@ -643,10 +752,10 @@
       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;
+      element._local!['refs/*'].permissions.owner.modified = true;
       await flush();
       expectedInput = {
         add: {
@@ -675,7 +784,9 @@
 
     test('_computeAddAndRemove sections', async () => {
       // Add a new permission to a section
-      let expectedInput = {
+      let expectedInput = {};
+
+      expectedInput = {
         add: {
           'refs/*': {
             permissions: {
@@ -689,8 +800,10 @@
         },
         remove: {},
       };
-      element.shadowRoot
-          .querySelector('gr-access-section')._handleAddPermission();
+      queryAndAssert<GrAccessSection>(
+        element,
+        'gr-access-section'
+      )._handleAddPermission();
       await flush();
       assert.deepEqual(element._computeAddAndRemove(), expectedInput);
 
@@ -716,18 +829,23 @@
         },
         remove: {},
       };
-      const newPermission =
-          dom(element.shadowRoot
-              .querySelector('gr-access-section').root).querySelectorAll(
-              'gr-permission')[2];
-      newPermission._handleAddRuleItem(
-          {detail: {value: 'Maintainers'}});
+      const grAccessSection = queryAndAssert<GrAccessSection>(
+        element,
+        'gr-access-section'
+      );
+      const newPermission = queryAll<GrPermission>(
+        grAccessSection,
+        'gr-permission'
+      )[2];
+      newPermission._handleAddRuleItem({
+        detail: {value: 'Maintainers'},
+      } as AutocompleteCommitEvent);
       await flush();
       assert.deepEqual(element._computeAddAndRemove(), expectedInput);
 
       // Modify a section reference.
-      element._local['refs/*'].updatedId = 'refs/for/bar';
-      element._local['refs/*'].modified = true;
+      element._local!['refs/*'].updatedId = 'refs/for/bar';
+      element._local!['refs/*'].modified = true;
       await flush();
       expectedInput = {
         add: {
@@ -735,13 +853,13 @@
             modified: true,
             updatedId: 'refs/for/bar',
             permissions: {
-              'owner': {
+              owner: {
                 rules: {
                   234: {action: 'ALLOW'},
                   123: {action: 'DENY'},
                 },
               },
-              'read': {
+              read: {
                 rules: {
                   234: {action: 'ALLOW'},
                 },
@@ -771,7 +889,7 @@
       assert.deepEqual(element._computeAddAndRemove(), expectedInput);
 
       // Delete a section.
-      element._local['refs/*'].deleted = true;
+      element._local!['refs/*'].deleted = true;
       await flush();
       expectedInput = {
         add: {},
@@ -786,7 +904,9 @@
 
     test('_computeAddAndRemove new section', async () => {
       // Add a new permission to a section
-      let expectedInput = {
+      let expectedInput = {};
+
+      expectedInput = {
         add: {
           'refs/for/*': {
             added: true,
@@ -814,8 +934,10 @@
         },
         remove: {},
       };
-      const newSection = dom(element.root)
-          .querySelectorAll('gr-access-section')[1];
+      const newSection = queryAll<GrAccessSection>(
+        element,
+        'gr-access-section'
+      )[1];
       newSection._handleAddPermission();
       await flush();
       assert.deepEqual(element._computeAddAndRemove(), expectedInput);
@@ -844,14 +966,17 @@
         remove: {},
       };
 
-      newSection.shadowRoot
-          .querySelector('gr-permission')._handleAddRuleItem(
-              {detail: {value: 'Maintainers'}});
+      queryAndAssert<GrPermission>(
+        newSection,
+        'gr-permission'
+      )._handleAddRuleItem({
+        detail: {value: 'Maintainers'},
+      } as AutocompleteCommitEvent);
       await flush();
       assert.deepEqual(element._computeAddAndRemove(), expectedInput);
 
       // Modify a the reference from the default value.
-      element._local['refs/for/*'].updatedId = 'refs/for/new';
+      element._local!['refs/for/*'].updatedId = 'refs/for/new';
       await flush();
       expectedInput = {
         add: {
@@ -881,10 +1006,12 @@
 
     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;
+      element._local!['refs/*'].permissions.owner.rules[123].modified = true;
+      element._local!['refs/*'].permissions.owner.deleted = true;
       await flush();
-      let expectedInput = {
+      let expectedInput = {};
+
+      expectedInput = {
         add: {},
         remove: {
           'refs/*': {
@@ -896,13 +1023,13 @@
       };
       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;
+      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);
 
       // Also modify a different rule inside of another permission.
-      element._local['refs/*'].permissions.read.modified = true;
+      element._local!['refs/*'].permissions.read.modified = true;
       await flush();
       expectedInput = {
         add: {
@@ -929,10 +1056,10 @@
       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;
+      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();
       expectedInput = {
         add: {
@@ -960,12 +1087,17 @@
       assert.deepEqual(element._computeAddAndRemove(), expectedInput);
 
       // Add a rule to the existing permission;
-      const readPermission =
-          dom(element.shadowRoot
-              .querySelector('gr-access-section').root).querySelectorAll(
-              'gr-permission')[1];
-      readPermission._handleAddRuleItem(
-          {detail: {value: 'Maintainers'}});
+      const grAccessSection = queryAndAssert<GrAccessSection>(
+        element,
+        'gr-access-section'
+      );
+      const readPermission = queryAll<GrPermission>(
+        grAccessSection,
+        'gr-permission'
+      )[1];
+      readPermission._handleAddRuleItem({
+        detail: {value: 'Maintainers'},
+      } as AutocompleteCommitEvent);
       await flush();
 
       expectedInput = {
@@ -995,8 +1127,8 @@
       assert.deepEqual(element._computeAddAndRemove(), expectedInput);
 
       // Change one of the refs
-      element._local['refs/*'].updatedId = 'refs/for/bar';
-      element._local['refs/*'].modified = true;
+      element._local!['refs/*'].updatedId = 'refs/for/bar';
+      element._local!['refs/*'].modified = true;
       await flush();
 
       expectedInput = {
@@ -1032,21 +1164,26 @@
           },
         },
       };
-      element._local['refs/*'].deleted = true;
+      element._local!['refs/*'].deleted = true;
       await flush();
       assert.deepEqual(element._computeAddAndRemove(), expectedInput);
 
       // Add a new section.
       MockInteractions.tap(element.$.addReferenceBtn);
-      let newSection = dom(element.root)
-          .querySelectorAll('gr-access-section')[1];
+      let newSection = queryAll<GrAccessSection>(
+        element,
+        'gr-access-section'
+      )[1];
       newSection._handleAddPermission();
       await flush();
-      newSection.shadowRoot
-          .querySelector('gr-permission')._handleAddRuleItem(
-              {detail: {value: 'Maintainers'}});
+      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';
+      element._local!['refs/for/*'].updatedId = 'refs/for/new';
       await flush();
 
       expectedInput = {
@@ -1079,8 +1216,9 @@
       assert.deepEqual(element._computeAddAndRemove(), expectedInput);
 
       // Modify newly added rule inside new ref.
-      element._local['refs/for/*'].permissions['label-Code-Review'].
-          rules['Maintainers'].modified = true;
+      element._local!['refs/for/*'].permissions['label-Code-Review'].rules[
+        'Maintainers'
+      ].modified = true;
       await flush();
       expectedInput = {
         add: {
@@ -1115,15 +1253,17 @@
       // Add a second new section.
       MockInteractions.tap(element.$.addReferenceBtn);
       await flush();
-      newSection = dom(element.root)
-          .querySelectorAll('gr-access-section')[2];
+      newSection = queryAll<GrAccessSection>(element, 'gr-access-section')[2];
       newSection._handleAddPermission();
       await flush();
-      newSection.shadowRoot
-          .querySelector('gr-permission')._handleAddRuleItem(
-              {detail: {value: 'Maintainers'}});
+      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';
+      element._local!['refs/for/**'].updatedId = 'refs/for/new2';
       await flush();
       expectedInput = {
         add: {
@@ -1178,16 +1318,16 @@
       // 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);
+      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);
+      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);
+      assert.equal(element._sections!.length, 1);
+      assert.equal(Object.keys(element._local!).length, 1);
     });
 
     test('_handleSave', async () => {
@@ -1216,24 +1356,25 @@
         },
       };
       stubRestApi('getRepoAccessRights').returns(
-          Promise.resolve(JSON.parse(JSON.stringify(accessRes))));
-      sinon.stub(GerritNav, 'navigateToChange');
-      let resolver;
-      const saveStub = stubRestApi(
-          'setRepoAccessRights')
-          .returns(new Promise(r => resolver = r));
+        Promise.resolve(JSON.parse(JSON.stringify(accessRes)))
+      );
+      const navigateToChangeStub = sinon.stub(GerritNav, 'navigateToChange');
+      let resolver: (value: Response | PromiseLike<Response>) => void;
+      const saveStub = stubRestApi('setRepoAccessRights').returns(
+        new Promise(r => (resolver = r))
+      );
 
-      element.repo = 'test-repo';
+      element.repo = 'test-repo' as RepoName;
       sinon.stub(element, '_computeAddAndRemove').returns(repoAccessInput);
 
       element._modified = true;
       MockInteractions.tap(element.$.saveBtn);
       await flush();
       assert.equal(element.$.saveBtn.hasAttribute('loading'), true);
-      resolver({_number: 1});
+      resolver!({status: 200} as Response);
       await flush();
       assert.isTrue(saveStub.called);
-      assert.isTrue(GerritNav.navigateToChange.notCalled);
+      assert.isTrue(navigateToChangeStub.notCalled);
     });
 
     test('_handleSaveForReview', async () => {
@@ -1262,26 +1403,27 @@
         },
       };
       stubRestApi('getRepoAccessRights').returns(
-          Promise.resolve(JSON.parse(JSON.stringify(accessRes))));
-      sinon.stub(GerritNav, 'navigateToChange');
-      let resolver;
+        Promise.resolve(JSON.parse(JSON.stringify(accessRes)))
+      );
+      const navigateToChangeStub = sinon.stub(GerritNav, 'navigateToChange');
+      let resolver: (value: ChangeInfo | PromiseLike<ChangeInfo>) => void;
       const saveForReviewStub = stubRestApi(
-          'setRepoAccessRightsForReview')
-          .returns(new Promise(r => resolver = r));
+        'setRepoAccessRightsForReview'
+      ).returns(new Promise(r => (resolver = r)));
 
-      element.repo = 'test-repo';
+      element.repo = 'test-repo' as RepoName;
       sinon.stub(element, '_computeAddAndRemove').returns(repoAccessInput);
 
       element._modified = true;
       MockInteractions.tap(element.$.saveReviewBtn);
       await flush();
       assert.equal(element.$.saveReviewBtn.hasAttribute('loading'), true);
-      resolver({_number: 1});
+      resolver!(createChange());
       await flush();
       assert.isTrue(saveForReviewStub.called);
-      assert.isTrue(GerritNav.navigateToChange
-          .lastCall.calledWithExactly({_number: 1}));
+      assert.isTrue(
+        navigateToChangeStub.lastCall.calledWithExactly(createChange())
+      );
     });
   });
 });
-