Add concept of auto generated messages to PolyGerrit UI (front end)

This change allows users to hide all robot generated messages in the
Polygerrit UI.

Feature: Issue 4595
Change-Id: I80f11dc48d15bd59a24fd008f9db72ff37cfdbda
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.js b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
index acb95c1..5c903a85 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.js
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
@@ -50,6 +50,19 @@
         value: true,
         reflectToAttribute: true,
       },
+      hideAutomated: {
+        type: Boolean,
+        value: false,
+      },
+      hidden: {
+        type: Boolean,
+        computed: '_computeIsHidden(hideAutomated, isAutomated)',
+        reflectToAttribute: true,
+      },
+      isAutomated: {
+        type: Boolean,
+        computed: '_computeIsAutomated(message)',
+      },
       showAvatar: {
         type: Boolean,
         computed: '_computeShowAvatar(author, config)',
@@ -101,6 +114,14 @@
       this.expanded = false;
     },
 
+    _computeIsAutomated: function(message) {
+      return !!message.tag && message.tag.indexOf('autogenerated') === 0;
+    },
+
+    _computeIsHidden: function(hideAutomated, isAutomated) {
+      return hideAutomated && isAutomated;
+    },
+
     _computeClass: function(expanded, showAvatar) {
       var classes = [];
       classes.push(expanded ? 'expanded' : 'collapsed');
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
index 1587500d..4615392 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
@@ -87,6 +87,34 @@
       assert.equal(0, content.textContent.trim().indexOf(updatedBy.name));
     });
 
+    test('autogenerated prefix hiding', function() {
+      element.message = {
+        tag: 'autogenerated:gerrit:test',
+        updated: '2016-01-12 20:24:49.448000000',
+      };
+
+      assert.isTrue(element.isAutomated);
+      assert.isFalse(element.hidden);
+
+      element.hideAutomated = true;
+
+      assert.isTrue(element.hidden);
+    });
+
+    test('tag that is not autogenerated prefix does not hide', function() {
+      element.message = {
+        tag: 'something',
+        updated: '2016-01-12 20:24:49.448000000',
+      };
+
+      assert.isFalse(element.isAutomated);
+      assert.isFalse(element.hidden);
+
+      element.hideAutomated = true;
+
+      assert.isFalse(element.hidden);
+    });
+
     test('reply button hidden unless logged in', function() {
       var message = {
         'message': 'Uploaded patch set 1.',
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
index 3ae6b44..c80ba4a 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
@@ -43,9 +43,17 @@
     </style>
     <div class="header">
       <h3>Messages</h3>
-      <gr-button link on-tap="_handleExpandCollapseTap">
-        [[_computeExpandCollapseMessage(_expanded)]]
-      </gr-button>
+      <div>
+        <gr-button id="collapse-messages" link
+            on-tap="_handleExpandCollapseTap">
+          [[_computeExpandCollapseMessage(_expanded)]]
+        </gr-button>
+        <gr-button id="automatedMessageToggle" link
+            on-tap="_handleAutomatedMessageToggleTap"
+            hidden$="[[!_hasAutomatedMessages(messages)]]">
+          [[_computeAutomatedToggleText(_hideAutomated)]]
+        </gr-button>
+      </div>
     </div>
     <template
         is="dom-repeat"
@@ -55,6 +63,7 @@
           change-num="[[changeNum]]"
           message="[[message]]"
           comments="[[_computeCommentsForMessage(comments, message)]]"
+          hide-automated="[[_hideAutomated]]"
           project-config="[[projectConfig]]"
           show-reply-button="[[showReplyButtons]]"
           on-scroll-to="_handleScrollTo"
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
index fb72dd4..aa0703f 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
@@ -38,6 +38,10 @@
         type: Boolean,
         value: false,
       },
+      _hideAutomated: {
+        type: Boolean,
+        value: false,
+      },
     },
 
     scrollToMessage: function(messageID) {
@@ -111,14 +115,33 @@
       }
     },
 
+    _handleAutomatedMessageToggleTap: function(e) {
+      e.preventDefault();
+      this._hideAutomated = !this._hideAutomated;
+    },
+
     _handleScrollTo: function(e) {
       this.scrollToMessage(e.detail.message.id);
     },
 
+    _hasAutomatedMessages: function(messages) {
+      for (var i = 0; messages && i < messages.length; i++) {
+        if (messages[i].tag &&
+            messages[i].tag.indexOf('autogenerated') === 0) {
+          return true;
+        }
+      }
+      return false;
+    },
+
     _computeExpandCollapseMessage: function(expanded) {
       return expanded ? 'Collapse all' : 'Expand all';
     },
 
+    _computeAutomatedToggleText: function(hideAutomated) {
+      return hideAutomated ? 'Show automated' : 'Hide automated';
+    },
+
     /**
      * Computes message author's file comments for change's message.
      * Method uses this.messages to find next message and relies on messages
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
index 2398287..704c638 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
@@ -78,7 +78,7 @@
         assert.isTrue(allMessageEls[i].expanded);
       }
 
-      MockInteractions.tap(element.$$('.header gr-button'));
+      MockInteractions.tap(element.$$('#collapse-messages'));
       allMessageEls =
           Polymer.dom(element.root).querySelectorAll('gr-message');
       for (var i = 0; i < allMessageEls.length; i++) {
@@ -86,6 +86,11 @@
       }
     });
 
+    test('hide messages does not appear when no automated messages',
+        function() {
+      assert.isOk(element.$$('#automatedMessageToggle[hidden]'));
+    });
+
     test('scroll to message', function() {
       var allMessageEls =
           Polymer.dom(element.root).querySelectorAll('gr-message');
@@ -204,4 +209,77 @@
       assert.deepEqual(messageElements[2].comments, {});
     });
   });
+
+  suite('gr-messages-list automate tests', function() {
+    var element;
+    var messages;
+
+    var randomMessage = function(opt_params) {
+      var params = opt_params || {};
+      var author1 = {
+        _account_id: 1115495,
+        name: 'Andrew Bonventre',
+        email: 'andybons@chromium.org',
+      };
+      return {
+        id: params.id || Math.random().toString(),
+        date: params.date || '2016-01-12 20:28:33.038000',
+        message: params.message || Math.random().toString(),
+        _revision_number: params._revision_number || 1,
+        author: params.author || author1,
+        tag: 'autogenerated:gerrit:replace',
+      };
+    };
+
+    setup(function() {
+      stub('gr-rest-api-interface', {
+        getConfig: function() { return Promise.resolve({}); },
+        getLoggedIn: function() { return Promise.resolve(false); },
+      });
+      element = fixture('basic');
+      messages = _.times(3, randomMessage);
+      element.messages = messages;
+      flushAsynchronousOperations();
+    });
+
+    test('hide autogenerated button is not hidden', function() {
+      assert.isNotOk(element.$$('#automatedMessageToggle[hidden]'));
+    });
+
+    test('autogenerated messages are not hidden initially', function() {
+      var allHiddenMessageEls =
+          Polymer.dom(element.root).querySelectorAll('gr-message[hidden]');
+
+      //There are no hidden messages.
+      assert.isFalse(!!allHiddenMessageEls.length);
+    });
+
+    test('autogenerated messages are hidden after clicking hide button',
+        function() {
+      var allHiddenMessageEls =
+          Polymer.dom(element.root).querySelectorAll('gr-message[hidden]');
+
+      element._hideAutomated = false;
+      MockInteractions.tap(element.$$('#automatedMessageToggle'));
+      allHiddenMessageEls =
+          Polymer.dom(element.root).querySelectorAll('gr-message[hidden]');
+
+      //Autogenerated messages are now hidden.
+      assert.isTrue(!!allHiddenMessageEls.length);
+    });
+
+    test('autogenerated messages are not hidden after clicking show button',
+        function() {
+      var allHiddenMessageEls =
+          Polymer.dom(element.root).querySelectorAll('gr-message[hidden]');
+
+      element._hideAutomated = true;
+      MockInteractions.tap(element.$$('#automatedMessageToggle'));
+      allHiddenMessageEls =
+          Polymer.dom(element.root).querySelectorAll('gr-message[hidden]');
+
+      //Autogenerated messages are now hidden.
+      assert.isFalse(!!allHiddenMessageEls.length);
+    });
+  });
 </script>