Merge "Move KeyUtil setup to GerritBaseTests"
diff --git a/WORKSPACE b/WORKSPACE
index bb186f7..e86a25b 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -132,8 +132,8 @@
 maven_jar(
   name = 'jgit',
   artifact = 'org.eclipse.jgit:org.eclipse.jgit:' + JGIT_VERS,
-  sha1 = '4e953ecb2ed6d9ec2f90caee9de7c6a74df5c9b8',
-  src_sha1 = '35712f083d19bbef07fca2271fc9278cc76ad28a',
+  sha1 = '34315f71bb9becf6ff75947a9c43c415b929ec21',
+  src_sha1 = '8320c18472870904eb7fb860af353fea818d07e4',
   repository = GERRIT,
   unsign = True,
 )
@@ -141,7 +141,7 @@
 maven_jar(
   name = 'jgit_servlet',
   artifact = 'org.eclipse.jgit:org.eclipse.jgit.http.server:' + JGIT_VERS,
-  sha1 = 'af904b4ac10682b80486bddb86efc2f1a409ec78',
+  sha1 = '927990025d2970995dbb58f03763eeb776fec8fd',
   repository = GERRIT,
   unsign = True,
 )
@@ -156,14 +156,14 @@
 maven_jar(
   name = 'jgit_archive',
   artifact = 'org.eclipse.jgit:org.eclipse.jgit.archive:' + JGIT_VERS,
-  sha1 = 'c29ed2cf20c1b7492d6baf7ce7f8355b3adb9f82',
+  sha1 = '4a5d058915400c1ef497bfeeb5e87d235213e273',
   repository = GERRIT,
 )
 
 maven_jar(
   name = 'jgit_junit',
   artifact = 'org.eclipse.jgit:org.eclipse.jgit.junit:' + JGIT_VERS,
-  sha1 = '35d1fcd4cbbf1332ac5aee89e370b65cbe82c390',
+  sha1 = '8e3cb9b1f632fdfea76b04c286a2c0d8d260ebce',
   repository = GERRIT,
   unsign = True,
 )
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
index 03fb504..f5ffdf7 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
@@ -289,7 +289,7 @@
     AccountInfo user2 = newAccountWithFullName("jroe", "Jane Roe");
     AccountInfo user3 = newAccountWithFullName("user3", "Mr Selfish");
 
-    assertThat(internalAccountQuery.byWatchedProject(p).isEmpty());
+    assertThat(internalAccountQuery.byWatchedProject(p)).isEmpty();
 
     watch(user1, p, null);
     assertAccounts(internalAccountQuery.byWatchedProject(p), user1);
diff --git a/lib/jgit/jgit.bzl b/lib/jgit/jgit.bzl
index dfdc5ce..f87603f 100644
--- a/lib/jgit/jgit.bzl
+++ b/lib/jgit/jgit.bzl
@@ -1,4 +1,4 @@
-JGIT_VERS = '4.5.0.201609210915-r.104-g1c70dd6d2'
+JGIT_VERS = '4.5.0.201609210915-r.115-g81f9c1843'
 DOC_VERS = '4.5.0.201609210915-r'
 #DOC_VERS = JGIT_VERS # Set to VERS unless using a snapshot
 JGIT_DOC_URL="http://download.eclipse.org/jgit/site/" + DOC_VERS + "/apidocs"
diff --git a/lib/jgit/org.eclipse.jgit.archive/BUCK b/lib/jgit/org.eclipse.jgit.archive/BUCK
index 57a1d8d..c0272af 100644
--- a/lib/jgit/org.eclipse.jgit.archive/BUCK
+++ b/lib/jgit/org.eclipse.jgit.archive/BUCK
@@ -4,7 +4,7 @@
 maven_jar(
   name = 'jgit-archive',
   id = 'org.eclipse.jgit:org.eclipse.jgit.archive:' + JGIT_VERS,
-  sha1 = 'c29ed2cf20c1b7492d6baf7ce7f8355b3adb9f82',
+  sha1 = '4a5d058915400c1ef497bfeeb5e87d235213e273',
   license = 'jgit',
   repository = REPO,
   deps = ['//lib/jgit/org.eclipse.jgit:jgit'],
diff --git a/lib/jgit/org.eclipse.jgit.http.server/BUCK b/lib/jgit/org.eclipse.jgit.http.server/BUCK
index f75e6f2..29e7e27 100644
--- a/lib/jgit/org.eclipse.jgit.http.server/BUCK
+++ b/lib/jgit/org.eclipse.jgit.http.server/BUCK
@@ -4,7 +4,7 @@
 maven_jar(
   name = 'jgit-servlet',
   id = 'org.eclipse.jgit:org.eclipse.jgit.http.server:' + JGIT_VERS,
-  sha1 = 'af904b4ac10682b80486bddb86efc2f1a409ec78',
+  sha1 = '927990025d2970995dbb58f03763eeb776fec8fd',
   license = 'jgit',
   repository = REPO,
   deps = ['//lib/jgit/org.eclipse.jgit:jgit'],
diff --git a/lib/jgit/org.eclipse.jgit.junit/BUCK b/lib/jgit/org.eclipse.jgit.junit/BUCK
index b8fc269..255b47c 100644
--- a/lib/jgit/org.eclipse.jgit.junit/BUCK
+++ b/lib/jgit/org.eclipse.jgit.junit/BUCK
@@ -4,7 +4,7 @@
 maven_jar(
   name = 'junit',
   id = 'org.eclipse.jgit:org.eclipse.jgit.junit:' + JGIT_VERS,
-  sha1 = '35d1fcd4cbbf1332ac5aee89e370b65cbe82c390',
+  sha1 = '8e3cb9b1f632fdfea76b04c286a2c0d8d260ebce',
   license = 'DO_NOT_DISTRIBUTE',
   repository = REPO,
   unsign = True,
diff --git a/lib/jgit/org.eclipse.jgit/BUCK b/lib/jgit/org.eclipse.jgit/BUCK
index bf2e192..48e3486 100644
--- a/lib/jgit/org.eclipse.jgit/BUCK
+++ b/lib/jgit/org.eclipse.jgit/BUCK
@@ -4,8 +4,8 @@
 maven_jar(
   name = 'jgit',
   id = 'org.eclipse.jgit:org.eclipse.jgit:' + JGIT_VERS,
-  bin_sha1 = '4e953ecb2ed6d9ec2f90caee9de7c6a74df5c9b8',
-  src_sha1 = '35712f083d19bbef07fca2271fc9278cc76ad28a',
+  bin_sha1 = '34315f71bb9becf6ff75947a9c43c415b929ec21',
+  src_sha1 = '8320c18472870904eb7fb860af353fea818d07e4',
   license = 'jgit',
   repository = REPO,
   unsign = True,
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
index 2d5c0a7..d6b1505 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
@@ -49,6 +49,15 @@
 
   var ADDITIONAL_ACTION_KEY_PREFIX = '__additionalAction_';
 
+  var QUICK_APPROVE_ACTION = {
+    __key: 'review',
+    __type: 'change',
+    key: 'review',
+    label: 'Quick Approve',
+    method: 'POST',
+    title: 'Set maximal score to all labels you can.',
+  };
+
   Polymer({
     is: 'gr-change-actions',
 
@@ -98,7 +107,7 @@
       _changeActionValues: {
         type: Array,
         computed: '_computeChangeActionValues(actions.*, ' +
-            'primaryActionKeys.*, _additionalActions.*)',
+            'primaryActionKeys.*, _additionalActions.*, change)',
       },
       _additionalActions: {
         type: Array,
@@ -243,9 +252,14 @@
     },
 
     _computeChangeActionValues: function(actionsChangeRecord,
-        primariesChangeRecord, additionalActionsChangeRecord) {
-      return this._getActionValues(actionsChangeRecord, primariesChangeRecord,
-          additionalActionsChangeRecord, ActionType.CHANGE);
+        primariesChangeRecord, additionalActionsChangeRecord, change) {
+      var actions = this._getActionValues(
+        actionsChangeRecord, primariesChangeRecord,
+        additionalActionsChangeRecord, ActionType.CHANGE, change);
+      if (actions.length && this._canQuickApprove(change)) {
+        actions.unshift(QUICK_APPROVE_ACTION);
+      }
+      return actions;
     },
 
     _getActionValues: function(actionsChangeRecord, primariesChangeRecord,
@@ -289,6 +303,17 @@
       return result.concat(additionalActions);
     },
 
+    _canQuickApprove: function(change) {
+      if (!change || !change.labels || !change.permitted_labels) {
+        return false;
+      }
+      var missingApprovals = Object.keys(change.labels).filter(function(label) {
+        return !change.labels[label].approved;
+      });
+      return missingApprovals.some(
+          function(label) { return label in change.permitted_labels; });
+    },
+
     _computeLoadingLabel: function(action) {
       return ActionLoadingLabels[action] || 'Working...';
     },
@@ -344,6 +369,18 @@
         this.showRevertDialog();
       } else if (key === ChangeActions.ABANDON) {
         this._showActionDialog(this.$.confirmAbandonDialog);
+      } else if (key === QUICK_APPROVE_ACTION.key) {
+        var review = {
+          drafts: 'PUBLISH_ALL_REVISIONS',
+          labels: {},
+        };
+        var permittedLabels = this.change.permitted_labels;
+        Object.keys(permittedLabels).forEach(function(label) {
+          // Set label to maximal score permitted for it.
+          review.labels[label] = permittedLabels[label].slice(-1)[0];
+        });
+        this._fireAction(
+            this._prependSlash(key), QUICK_APPROVE_ACTION, true, review);
       } else {
         this._fireAction(this._prependSlash(key), this.actions[key], false);
       }
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
index 00e61d9..64443ff 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
@@ -84,6 +84,7 @@
       });
 
       element = fixture('basic');
+      element.change = {};
       element.changeNum = '42';
       element.patchNum = '2';
       element.actions = {
@@ -488,5 +489,79 @@
         assert.isFalse(fireActionStub.called);
       });
     });
+
+    suite('quick approve', function() {
+      setup(function() {
+        element.change = {
+          current_revision: 'abc1234',
+        };
+        element.change = {
+          current_revision: 'abc1234',
+          labels: {
+            'foo': {},
+          },
+          permitted_labels: {
+            'foo': ['-1', '0', '+1'],
+          },
+        };
+        flushAsynchronousOperations();
+      });
+
+      test('added when can approve', function() {
+        var approveButton = element.$$('gr-button[data-action-key=\'review\']');
+        assert.isNotNull(approveButton);
+      });
+
+      test('not added when no actions available', function() {
+        element.actions = [];
+        flushAsynchronousOperations();
+        var approveButton = element.$$('gr-button[data-action-key=\'review\']');
+        assert.isNull(approveButton);
+      });
+
+      test('not added when already approved', function() {
+        element.change = {
+          current_revision: 'abc1234',
+          labels: {
+            'foo': {
+              approved: {},
+            },
+          },
+          permitted_labels: {
+            'foo': [],
+          },
+        };
+        flushAsynchronousOperations();
+        var approveButton = element.$$('gr-button[data-action-key=\'review\']');
+        assert.isNull(approveButton);
+      });
+
+      test('not added when can not approve', function() {
+        element.change = {
+          current_revision: 'abc1234',
+          labels: {
+            'foo': {},
+          },
+          permitted_labels: {
+            'bar': [],
+          },
+        };
+        flushAsynchronousOperations();
+        var approveButton = element.$$('gr-button[data-action-key=\'review\']');
+        assert.isNull(approveButton);
+      });
+
+      test('approves when taped', function() {
+        var fireActionStub = sinon.stub(element, '_fireAction');
+        MockInteractions.tap(
+            element.$$('gr-button[data-action-key=\'review\']'));
+        flushAsynchronousOperations();
+        assert.isTrue(fireActionStub.called);
+        assert.isTrue(fireActionStub.calledWith('/review'));
+        var payload = fireActionStub.lastCall.args[3];
+        assert.deepEqual(payload.labels, {foo: '+1'});
+        fireActionStub.restore();
+      });
+    });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index 31f1b5c..fdc3b23 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -96,7 +96,7 @@
       }
       /* Prevent plugin text from overflowing. */
       #change_plugins {
-        word-break: break-all;
+        word-break: break-word;
       }
       .commitMessage {
         font-family: var(--monospace-font-family);
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
index ade0afc..39db8e4 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
@@ -209,12 +209,6 @@
         </iron-autogrow-textarea>
       </section>
       <section class="labelsContainer">
-        <template is="dom-if" if="[[_isClosed(change)]]" id="labelDisabled">
-          <div class="labelDisabledMessage">
-            Setting labels are disabled for this change because it has been
-            closed.
-          </div>
-        </template>
         <template is="dom-repeat"
             items="[[_labels]]" as="label">
           <div class="labelContainer">
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
index cb3bcb5..3f27234 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
@@ -23,8 +23,6 @@
     REVIEWERS: 'reviewers',
   };
 
-  var CLOSED_CHANGE_STATUSES = ['ABANDONED', 'MERGED'];
-
   Polymer({
     is: 'gr-reply-dialog',
 
@@ -277,10 +275,6 @@
       }.bind(this));
     },
 
-    _isClosed: function(change) {
-      return CLOSED_CHANGE_STATUSES.indexOf(change.status) !== -1;
-    },
-
     _computeHideDraftList: function(drafts) {
       return Object.keys(drafts || {}).length == 0;
     },
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
index 968adfe..72fbaaf 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
@@ -262,18 +262,6 @@
       }).then(done);
     });
 
-    test('message disabled dialogue appears for closed change', function() {
-      element.change = {status: 'ABANDONED'};
-      flushAsynchronousOperations();
-      assert.isOk(element.$$('.labelDisabledMessage'));
-    });
-
-    test('message disabled dialogue does not appear for open change',
-        function() {
-      element.change = {status: 'NEW'};
-      assert.isNotOk(element.$$('.labelDisabledMessage'));
-    });
-
     test('_getStorageLocation', function() {
       var actual = element._getStorageLocation();
       assert.equal(actual.changeNum, changeNum);
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.html b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.html
index 55eeb4e..11560f1 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.html
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.html
@@ -26,7 +26,7 @@
       p,
       ul,
       blockquote,
-      gr-linked-text[pre] {
+      gr-linked-text.pre {
         margin: 0 0 1.4em 0;
       }
       blockquote {
@@ -36,7 +36,7 @@
       li {
         margin-left: 1.4em;
       }
-      gr-linked-text[pre] {
+      gr-linked-text.pre {
         font-family: var(--monospace-font-family);
       }
     </style>
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
index 3d2d3d3..06dfdd4 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
@@ -189,8 +189,9 @@
       var text = document.createElement('gr-linked-text');
       text.config = this.config;
       text.content = content;
+      text.pre = true;
       if (isPre) {
-        text.pre = true;
+        text.classList.add('pre');
       }
       return text;
     },