Merge "Auth alert should have the highest priority among all error alerts"
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
index 9ef2461..5a7e58d 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
@@ -178,6 +178,8 @@
     _showAlert(text, opt_actionText, opt_actionCallback,
         opt_dismissOnNavigation) {
       if (this._alertElement) {
+        // do not override auth alerts
+        if (this._alertElement.type === 'AUTH') return;
         this._hideAlert();
       }
 
@@ -212,10 +214,14 @@
     }
 
     _showAuthErrorAlert(errorText, actionText) {
-      // TODO(viktard): close alert if it's not for auth error.
-      if (this._alertElement) { return; }
+      // hide any existing alert like `reload`
+      // as auth error should have the highest priority
+      if (this._alertElement) {
+        this._alertElement.hide();
+      }
 
       this._alertElement = this._createToastAlert();
+      this._alertElement.type = 'AUTH';
       this._alertElement.show(errorText, actionText,
           this._createLoginPopup.bind(this));
 
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
index a0a5731..72f5d5b 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
@@ -52,6 +52,7 @@
         sandbox.stub(window, 'fetch')
             .returns(Promise.resolve({ok: true, status: 204}));
         element = fixture('basic');
+        element._authService.clearCache();
       });
 
       test('does not show auth error on 403 by default', done => {
@@ -184,7 +185,8 @@
         );
         assert.equal(window.fetch.callCount, 1);
         flush(() => {
-          // auth check again
+          // here needs two flush as there are two chanined
+          // promises on server-error handler and flush only flushes one
           assert.equal(window.fetch.callCount, 2);
           flush(() => {
             // auth-error fired
@@ -238,6 +240,97 @@
         });
       });
 
+      test('auth toast should dismiss existing toast', done => {
+        // starts with authed state
+        element.$.restAPI.getLoggedIn();
+        const toastSpy = sandbox.spy(element, '_createToastAlert');
+        const responseText = Promise.resolve('Authentication required\n');
+
+        // fake an alert
+        element.fire('show-alert', {message: 'test reload', action: 'reload'});
+        const toast = toastSpy.lastCall.returnValue;
+        assert.isOk(toast);
+        assert.include(
+            Polymer.dom(toast.root).textContent, 'test reload');
+
+        // fake auth
+        window.fetch.returns(Promise.resolve({status: 403}));
+        element.fire('server-error',
+            {response: {status: 403, text() { return responseText; }}}
+        );
+        assert.equal(window.fetch.callCount, 1);
+        flush(() => {
+          // here needs two flush as there are two chanined
+          // promises on server-error handler and flush only flushes one
+          assert.equal(window.fetch.callCount, 2);
+          flush(() => {
+            // toast
+            const toast = toastSpy.lastCall.returnValue;
+            assert.include(
+                Polymer.dom(toast.root).textContent, 'Credentails expired.');
+            assert.include(
+                Polymer.dom(toast.root).textContent, 'Refresh credentials');
+            done();
+          });
+        });
+      });
+
+      test('regular toast should dismiss regular toast', () => {
+        // starts with authed state
+        element.$.restAPI.getLoggedIn();
+        const toastSpy = sandbox.spy(element, '_createToastAlert');
+
+        // fake an alert
+        element.fire('show-alert', {message: 'test reload', action: 'reload'});
+        let toast = toastSpy.lastCall.returnValue;
+        assert.isOk(toast);
+        assert.include(
+            Polymer.dom(toast.root).textContent, 'test reload');
+
+        // new alert
+        element.fire('show-alert', {message: 'second-test', action: 'reload'});
+
+        toast = toastSpy.lastCall.returnValue;
+        assert.include(Polymer.dom(toast.root).textContent, 'second-test');
+      });
+
+      test('regular toast should not dismiss auth toast', done => {
+        // starts with authed state
+        element.$.restAPI.getLoggedIn();
+        const toastSpy = sandbox.spy(element, '_createToastAlert');
+        const responseText = Promise.resolve('Authentication required\n');
+
+        // fake auth
+        window.fetch.returns(Promise.resolve({status: 403}));
+        element.fire('server-error',
+            {response: {status: 403, text() { return responseText; }}}
+        );
+        assert.equal(window.fetch.callCount, 1);
+        flush(() => {
+          // here needs two flush as there are two chanined
+          // promises on server-error handler and flush only flushes one
+          assert.equal(window.fetch.callCount, 2);
+          flush(() => {
+            let toast = toastSpy.lastCall.returnValue;
+            assert.include(
+                Polymer.dom(toast.root).textContent, 'Credentails expired.');
+            assert.include(
+                Polymer.dom(toast.root).textContent, 'Refresh credentials');
+
+            // fake an alert
+            element.fire('show-alert', {
+              message: 'test-alert', action: 'reload',
+            });
+            toast = toastSpy.lastCall.returnValue;
+            assert.isOk(toast);
+            assert.include(
+                Polymer.dom(toast.root).textContent, 'Credentails expired.');
+
+            done();
+          });
+        });
+      });
+
       test('show alert', () => {
         const alertObj = {message: 'foo'};
         sandbox.stub(element, '_showAlert');
diff --git a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
index 215f469..6a0769d 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
@@ -32,6 +32,8 @@
       return {
         text: String,
         actionText: String,
+        /** @type {?string} */
+        type: String,
         shown: {
           type: Boolean,
           value: true,