Fix Delete vote button not checking removable_labels.

Whether or not a vote can be removed by a current user is controlled by
removable_labels field of ChangeInfo. Frontend code however was
erroneously checking removable_reviewers instead.

Bug: Google b/447150520
Release-Notes: Fixed Delete vote button not checking removable_labels
Change-Id: Ib9b46c533ca27c2edea8fc3dc99602ffb4a6ee6b
diff --git a/polygerrit-ui/app/api/rest-api.ts b/polygerrit-ui/app/api/rest-api.ts
index 382a043..ac31822 100644
--- a/polygerrit-ui/app/api/rest-api.ts
+++ b/polygerrit-ui/app/api/rest-api.ts
@@ -391,6 +391,9 @@
   labels?: LabelNameToInfoMap;
   permitted_labels?: LabelNameToValuesMap;
   removable_reviewers?: AccountInfo[];
+  removable_labels?: {
+    [labelName: string]: {[labelValue: string]: AccountInfo[]};
+  };
   // This is documented as optional, but actually always set.
   reviewers: Reviewers;
   pending_reviewers?: AccountInfo[];
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
index f9289f4..e400319 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
@@ -173,7 +173,7 @@
       ></gr-account-chip>
       ${noVoteYet
         ? this.renderVoteAbility(reviewer)
-        : html`${this.renderRemoveVote(reviewer)}`}
+        : html`${this.renderRemoveVote(reviewer, approvalInfo)}`}
     </div>`;
   }
 
@@ -190,7 +190,10 @@
     return html`<span class="no-votes">No votes</span>`;
   }
 
-  private renderRemoveVote(reviewer: AccountInfo) {
+  private renderRemoveVote(
+    reviewer: AccountInfo,
+    approvalInfo: ApprovalInfo | undefined
+  ) {
     return html`<gr-tooltip-content has-tooltip title="Remove vote">
       <gr-button
         link
@@ -200,7 +203,8 @@
         class="deleteBtn ${this.computeDeleteClass(
           reviewer,
           this.mutable,
-          this.change
+          this.change,
+          approvalInfo
         )}"
       >
         <gr-icon icon="delete" filled></gr-icon>
@@ -244,7 +248,7 @@
 
   /**
    * A user is able to delete a vote iff the mutable property is true and the
-   * reviewer that left the vote exists in the list of removable_reviewers
+   * reviewer that left the vote exists in the list of removable_labels
    * received from the backend.
    *
    * @param reviewer An object describing the reviewer that left the
@@ -253,13 +257,24 @@
   private computeDeleteClass(
     reviewer: ApprovalInfo,
     mutable: boolean,
-    change?: ParsedChangeInfo
+    change?: ParsedChangeInfo,
+    approvalInfo?: ApprovalInfo
   ) {
-    if (!mutable || !change || !change.removable_reviewers) {
+    if (
+      !mutable ||
+      !change ||
+      !approvalInfo ||
+      !approvalInfo.value ||
+      !change.removable_labels
+    ) {
       return 'hidden';
     }
-    const removable = change.removable_reviewers;
-    if (removable.find(r => r._account_id === reviewer?._account_id)) {
+    const removableAccounts =
+      change.removable_labels[this.label]?.[valueString(approvalInfo.value)];
+    if (!removableAccounts) {
+      return 'hidden';
+    }
+    if (removableAccounts.find(r => r._account_id === reviewer?._account_id)) {
       return '';
     }
     return 'hidden';
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.ts b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.ts
index 6345754..79a5036 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.ts
@@ -105,7 +105,7 @@
       await element.updateComplete;
       const removeButton = queryAndAssert<GrButton>(element, 'gr-button');
       assert.isTrue(isHidden(removeButton));
-      element.change!.removable_reviewers = [account];
+      element.change!.removable_labels = {'Code-Review': {'+1': [account]}};
       element.mutable = true;
       await element.updateComplete;
       assert.isFalse(isHidden(removeButton));
@@ -115,7 +115,7 @@
       const mock = mockPromise();
       const deleteResponse = mock.then(() => new Response(null, {status: 200}));
       const deleteStub = stubRestApi('deleteVote').returns(deleteResponse);
-      element.change!.removable_reviewers = [account];
+      element.change!.removable_labels = {'Code-Review': {'+1': [account]}};
       element.change!.labels!['Code-Review'] = {
         ...label,
         recommended: account,