Merge "Require REST endpoints to return Response<?>"
diff --git a/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html b/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html
index e949a87..499970e 100644
--- a/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html
+++ b/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html
@@ -142,6 +142,7 @@
      *    object.
      */
     toSortedArray(obj) {
+      if (!obj) { return []; }
       return Object.keys(obj).map(key => {
         return {
           id: key,
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
index 4d7fd0c..5b728eb 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
@@ -198,10 +198,6 @@
       this.reload();
     },
 
-    _computeSelectedTitle(params) {
-      return this.getSelectedTitle(params.view);
-    },
-
     // TODO (beckysiegel): Update these functions after router abstraction is
     // updated. They are currently copied from gr-dropdown (and should be
     // updated there as well once complete).
@@ -228,6 +224,7 @@
      * @param {string=} opt_detailType
      */
     _computeSelectedClass(itemView, params, opt_detailType) {
+      if (!params) return '';
       // Group params are structured differently from admin params. Compute
       // selected differently for groups.
       // TODO(wyatta): Simplify this when all routes work like group params.
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
index 97e9bdc..ca494d2 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
@@ -206,7 +206,7 @@
       document.dispatchEvent(new CustomEvent(type, {detail}));
       if (opt_noLog) { return; }
       if (type === ERROR.TYPE && category === ERROR.CATEGORY) {
-        console.error(eventValue.error || eventName);
+        console.error(eventValue && eventValue.error || eventName);
       } else {
         if (eventValue !== undefined) {
           console.log(`Reporting: ${eventName}: ${eventValue}`);
@@ -228,7 +228,7 @@
      */
     cachingReporter(type, category, eventName, eventValue, opt_noLog) {
       if (type === ERROR.TYPE && category === ERROR.CATEGORY) {
-        console.error(eventValue.error || eventName);
+        console.error(eventValue && eventValue.error || eventName);
       }
       if (this._isMetricsPluginLoaded()) {
         if (pending.length) {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 80c8480..c57a1eb 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -326,12 +326,12 @@
         const isLoggedIn = !!this._cache.get('/accounts/self/detail');
         if (isLoggedIn && err && err.message === FAILED_TO_FETCH_ERROR) {
           this.checkCredentials();
-          return;
-        }
-        if (req.errFn) {
-          req.errFn.call(undefined, null, err);
         } else {
-          this.fire('network-error', {error: err});
+          if (req.errFn) {
+            req.errFn.call(undefined, null, err);
+          } else {
+            this.fire('network-error', {error: err});
+          }
         }
         throw err;
       });
@@ -1127,11 +1127,15 @@
       }).then(res => {
         this._credentialCheck.checking = false;
         if (res) {
-          this._cache.delete('/accounts/self/detail');
+          this._cache.set('/accounts/self/detail', res);
         }
         return res;
       }).catch(err => {
         this._credentialCheck.checking = false;
+        if (err && err.message === FAILED_TO_FETCH_ERROR) {
+          this.fire('auth-error');
+          this._cache.delete('/accounts/self/detail');
+        }
       });
     },
 
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index d00e00d..4c35151 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -465,7 +465,26 @@
       element.addEventListener('server-error', serverErrorStub);
       const authErrorStub = sandbox.stub();
       element.addEventListener('auth-error', authErrorStub);
-      element._fetchJSON('/bar').then(r => {
+      element._fetchJSON('/bar').finally(r => {
+        flush(() => {
+          assert.isTrue(authErrorStub.called);
+          assert.isFalse(serverErrorStub.called);
+          assert.isFalse(element._cache.has('/accounts/self/detail'));
+          done();
+        });
+      });
+    });
+
+    test('auth failure - test all failed to fetch', done => {
+      window.fetch.returns(
+          Promise.reject(new Error('Failed to fetch')));
+      // Emulate logged in.
+      element._cache.set('/accounts/self/detail', {});
+      const serverErrorStub = sandbox.stub();
+      element.addEventListener('server-error', serverErrorStub);
+      const authErrorStub = sandbox.stub();
+      element.addEventListener('auth-error', authErrorStub);
+      element._fetchJSON('/bar').finally(r => {
         flush(() => {
           assert.isTrue(authErrorStub.called);
           assert.isFalse(serverErrorStub.called);
diff --git a/polygerrit-ui/app/elements/shared/revision-info/revision-info.html b/polygerrit-ui/app/elements/shared/revision-info/revision-info.html
index d337153..fca8ae1 100644
--- a/polygerrit-ui/app/elements/shared/revision-info/revision-info.html
+++ b/polygerrit-ui/app/elements/shared/revision-info/revision-info.html
@@ -35,6 +35,9 @@
      * @return {Number}
      */
     RevisionInfo.prototype.getMaxParents = function() {
+      if (!this._change || !this._change.revisions) {
+        return 0;
+      }
       return Object.values(this._change.revisions)
           .reduce((acc, rev) => Math.max(rev.commit.parents.length, acc), 0);
     };
@@ -46,6 +49,9 @@
      */
     RevisionInfo.prototype.getParentCountMap = function() {
       const result = {};
+      if (!this._change || !this._change.revisions) {
+        return {};
+      }
       Object.values(this._change.revisions)
           .forEach(rev => { result[rev._number] = rev.commit.parents.length; });
       return result;
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 9448662..2b052a0 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -197,7 +197,6 @@
   for (let file of elements) {
     file = elementsPath + file;
     testFiles.push(file);
-    testFiles.push(file + '?dom=shadow');
   }
 
   // Behaviors tests.