Auth alert should have the highest priority among all error alerts

1. Auth alert can dismiss any existing alerts
2. Regular alerts should not dismiss existing auth alerts
3. Regular alerts should be able to dismiss existing regular alerts

Change-Id: Id415423d99a4e651532f64673da3063d00a5990e
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,