Merge "Hide rebase dialog only if rebase succeeds"
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
index 29bd1d9..3ff0780 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
@@ -111,7 +111,7 @@
   LabelNameToValuesMap,
   PatchSetNumber,
 } from '../../../api/rest-api';
-import {css, html, PropertyValues, LitElement} from 'lit';
+import {css, html, PropertyValues, LitElement, nothing} from 'lit';
 import {sharedStyles} from '../../../styles/shared-styles';
 import {when} from 'lit/directives/when.js';
 import {classMap} from 'lit/directives/class-map.js';
@@ -606,6 +606,16 @@
       .patchsetLevelContainer.unresolved {
         background-color: var(--unresolved-comment-background-color);
       }
+      .infoMessage {
+        display: flex;
+        justify-content: center;
+        background-color: var(--info-background);
+        padding: var(--spacing-s) 0;
+      }
+      .infoMessage gr-icon {
+        margin-right: var(--spacing-m);
+        color: var(--info-foreground);
+      }
     `,
   ];
 
@@ -791,6 +801,8 @@
             <gr-endpoint-slot name="below"></gr-endpoint-slot>
           </gr-endpoint-decorator>
           ${this.renderCCList()} ${this.renderReviewConfirmation()}
+          ${this.renderPrivateVisiblityInfo()}
+          ${this.renderNotificationForWipChangesInfo()}
         </section>
         <section class="labelsContainer">${this.renderLabels()}</section>
         <section class="newReplyDialog textareaContainer">
@@ -893,6 +905,36 @@
     `;
   }
 
+  private renderPrivateVisiblityInfo() {
+    const addedAccounts = [
+      ...(this.reviewersList?.additions() ?? []),
+      ...(this.ccsList?.additions() ?? []),
+    ];
+    if (!this.change?.is_private || !addedAccounts.length) return nothing;
+    return html`
+      <div class="infoMessage">
+        <gr-icon icon="info"></gr-icon>
+        <div>
+          Adding a reviewer/CC will make this private change visible to them
+        </div>
+      </div>
+    `;
+  }
+
+  private renderNotificationForWipChangesInfo() {
+    const addedAccounts = [
+      ...(this.reviewersList?.additions() ?? []),
+      ...(this.ccsList?.additions() ?? []),
+    ];
+    if (!this.change?.work_in_progress || !addedAccounts.length) return nothing;
+    return html`
+      <div class="infoMessage">
+        <gr-icon icon="info"></gr-icon>
+        <div>Reviewers will not be notified for WIP changes</div>
+      </div>
+    `;
+  }
+
   private renderLabels() {
     if (!this.change || !this.account || !this.permittedLabels) return;
     return html`
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.ts b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.ts
index 633c5b0..8582fd8 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.ts
@@ -211,10 +211,7 @@
               <div class="peopleListLabel">CC</div>
               <gr-account-list allow-any-input="" id="ccs"> </gr-account-list>
             </div>
-            <dialog
-              tabindex="-1"
-              id="reviewerConfirmationModal"
-            >
+            <dialog tabindex="-1" id="reviewerConfirmationModal">
               <div class="reviewerConfirmation">
                 Group
                 <span class="groupName"> </span>
@@ -232,7 +229,7 @@
                   No
                 </gr-button>
               </div>
-            </gr-overlay>
+            </dialog>
           </section>
           <section class="labelsContainer">
             <gr-endpoint-decorator name="reply-label-scores">
@@ -330,6 +327,240 @@
     );
   });
 
+  test('renders private change info when reviewer is added', async () => {
+    element.change!.is_private = true;
+    element.requestUpdate();
+    await element.updateComplete;
+    const peopleContainer = queryAndAssert<HTMLDivElement>(
+      element,
+      '.peopleContainer'
+    );
+
+    // Info is rendered only if reviewer is added
+    assert.dom.equal(
+      peopleContainer,
+      `
+      <section class="peopleContainer">
+        <gr-endpoint-decorator name="reply-reviewers">
+          <gr-endpoint-param name="change"> </gr-endpoint-param>
+          <gr-endpoint-param name="reviewers"> </gr-endpoint-param>
+          <div class="peopleList">
+            <div class="peopleListLabel">Reviewers</div>
+            <gr-account-list id="reviewers"> </gr-account-list>
+            <gr-endpoint-slot name="right"> </gr-endpoint-slot>
+          </div>
+          <gr-endpoint-slot name="below"> </gr-endpoint-slot>
+        </gr-endpoint-decorator>
+        <div class="peopleList">
+          <div class="peopleListLabel">CC</div>
+          <gr-account-list allow-any-input="" id="ccs"> </gr-account-list>
+        </div>
+        <dialog
+          tabindex="-1"
+          id="reviewerConfirmationModal"
+        >
+          <div class="reviewerConfirmation">
+            Group
+            <span class="groupName"> </span>
+            has
+            <span class="groupSize"> </span>
+            members.
+            <br />
+            Are you sure you want to add them all?
+          </div>
+          <div class="reviewerConfirmationButtons">
+            <gr-button aria-disabled="false" role="button" tabindex="0">
+              Yes
+            </gr-button>
+            <gr-button aria-disabled="false" role="button" tabindex="0">
+              No
+            </gr-button>
+          </div>
+        </dialog>
+      </section>
+    `
+    );
+
+    const account = createAccountWithId(22);
+    element.reviewersList!.accounts = [];
+    element.reviewersList!.addAccountItem({account, count: 1});
+    element.reviewersList!.dispatchEvent(
+      new CustomEvent('account-added', {
+        detail: {account},
+      })
+    );
+    element.requestUpdate();
+    await element.updateComplete;
+
+    assert.dom.equal(
+      peopleContainer,
+      `
+      <section class="peopleContainer">
+        <gr-endpoint-decorator name="reply-reviewers">
+          <gr-endpoint-param name="change"> </gr-endpoint-param>
+          <gr-endpoint-param name="reviewers"> </gr-endpoint-param>
+          <div class="peopleList">
+            <div class="peopleListLabel">Reviewers</div>
+            <gr-account-list id="reviewers"> </gr-account-list>
+            <gr-endpoint-slot name="right"> </gr-endpoint-slot>
+          </div>
+          <gr-endpoint-slot name="below"> </gr-endpoint-slot>
+        </gr-endpoint-decorator>
+        <div class="peopleList">
+          <div class="peopleListLabel">CC</div>
+          <gr-account-list allow-any-input="" id="ccs"> </gr-account-list>
+        </div>
+        <dialog
+          tabindex="-1"
+          id="reviewerConfirmationModal"
+        >
+          <div class="reviewerConfirmation">
+            Group
+            <span class="groupName"> </span>
+            has
+            <span class="groupSize"> </span>
+            members.
+            <br />
+            Are you sure you want to add them all?
+          </div>
+          <div class="reviewerConfirmationButtons">
+            <gr-button aria-disabled="false" role="button" tabindex="0">
+              Yes
+            </gr-button>
+            <gr-button aria-disabled="false" role="button" tabindex="0">
+              No
+            </gr-button>
+          </div>
+        </dialog>
+        <div class="infoMessage">
+          <gr-icon icon="info">
+          </gr-icon>
+          <div>
+            Adding a reviewer/CC will make this private change visible to them
+          </div>
+        </div>
+      </section>
+    `
+    );
+  });
+
+  test('renders wip change info when reviewer is added', async () => {
+    element.change!.work_in_progress = true;
+    element.requestUpdate();
+    await element.updateComplete;
+    const peopleContainer = queryAndAssert<HTMLDivElement>(
+      element,
+      '.peopleContainer'
+    );
+
+    // Info is rendered only if reviewer is added
+    assert.dom.equal(
+      peopleContainer,
+      `
+      <section class="peopleContainer">
+        <gr-endpoint-decorator name="reply-reviewers">
+          <gr-endpoint-param name="change"> </gr-endpoint-param>
+          <gr-endpoint-param name="reviewers"> </gr-endpoint-param>
+          <div class="peopleList">
+            <div class="peopleListLabel">Reviewers</div>
+            <gr-account-list id="reviewers"> </gr-account-list>
+            <gr-endpoint-slot name="right"> </gr-endpoint-slot>
+          </div>
+          <gr-endpoint-slot name="below"> </gr-endpoint-slot>
+        </gr-endpoint-decorator>
+        <div class="peopleList">
+          <div class="peopleListLabel">CC</div>
+          <gr-account-list allow-any-input="" id="ccs"> </gr-account-list>
+        </div>
+        <dialog
+          tabindex="-1"
+          id="reviewerConfirmationModal"
+        >
+          <div class="reviewerConfirmation">
+            Group
+            <span class="groupName"> </span>
+            has
+            <span class="groupSize"> </span>
+            members.
+            <br />
+            Are you sure you want to add them all?
+          </div>
+          <div class="reviewerConfirmationButtons">
+            <gr-button aria-disabled="false" role="button" tabindex="0">
+              Yes
+            </gr-button>
+            <gr-button aria-disabled="false" role="button" tabindex="0">
+              No
+            </gr-button>
+          </div>
+        </dialog>
+      </section>
+    `
+    );
+
+    const account = createAccountWithId(22);
+    element.reviewersList!.accounts = [];
+    element.reviewersList!.addAccountItem({account, count: 1});
+    element.reviewersList!.dispatchEvent(
+      new CustomEvent('account-added', {
+        detail: {account},
+      })
+    );
+    element.requestUpdate();
+    await element.updateComplete;
+
+    assert.dom.equal(
+      peopleContainer,
+      `
+      <section class="peopleContainer">
+        <gr-endpoint-decorator name="reply-reviewers">
+          <gr-endpoint-param name="change"> </gr-endpoint-param>
+          <gr-endpoint-param name="reviewers"> </gr-endpoint-param>
+          <div class="peopleList">
+            <div class="peopleListLabel">Reviewers</div>
+            <gr-account-list id="reviewers"> </gr-account-list>
+            <gr-endpoint-slot name="right"> </gr-endpoint-slot>
+          </div>
+          <gr-endpoint-slot name="below"> </gr-endpoint-slot>
+        </gr-endpoint-decorator>
+        <div class="peopleList">
+          <div class="peopleListLabel">CC</div>
+          <gr-account-list allow-any-input="" id="ccs"> </gr-account-list>
+        </div>
+        <dialog
+          tabindex="-1"
+          id="reviewerConfirmationModal"
+        >
+          <div class="reviewerConfirmation">
+            Group
+            <span class="groupName"> </span>
+            has
+            <span class="groupSize"> </span>
+            members.
+            <br />
+            Are you sure you want to add them all?
+          </div>
+          <div class="reviewerConfirmationButtons">
+            <gr-button aria-disabled="false" role="button" tabindex="0">
+              Yes
+            </gr-button>
+            <gr-button aria-disabled="false" role="button" tabindex="0">
+              No
+            </gr-button>
+          </div>
+        </dialog>
+        <div class="infoMessage">
+          <gr-icon icon="info">
+          </gr-icon>
+          <div>
+            Reviewers will not be notified for WIP changes
+          </div>
+        </div>
+      </section>
+    `
+    );
+  });
+
   test('default to publishing draft comments with reply', async () => {
     // Async tick is needed because iron-selector content is distributed and
     // distributed content requires an observer to be set up.
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-results.ts b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
index f7e3542..3fdb1c0 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-results.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
@@ -323,10 +323,18 @@
 
   override updated(changedProperties: PropertyValues) {
     if (changedProperties.has('result')) {
-      this.isExpandable = !!this.result?.summary && !!this.result?.message;
+      this.isExpandable = this.computeIsExpandable();
     }
   }
 
+  private computeIsExpandable() {
+    const hasSummary = !!this.result?.summary;
+    const hasMessage = !!this.result?.message;
+    const hasLinks = (this.result?.links ?? []).length > 0;
+    const hasPointers = (this.result?.codePointers ?? []).length > 0;
+    return hasSummary && (hasMessage || hasLinks || hasPointers);
+  }
+
   override focus() {
     if (this.nameEl) this.nameEl.focus();
   }
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-results_test.ts b/polygerrit-ui/app/elements/checks/gr-checks-results_test.ts
index 934e958..113470c 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-results_test.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-results_test.ts
@@ -117,7 +117,6 @@
           aria-checked="false"
           aria-label="Expand result row"
           class="show-hide"
-          hidden=""
           role="switch"
           tabindex="0"
         >
@@ -262,6 +261,7 @@
             </h3>
             <gr-result-row
               class="FAKEErrorFinderFinderFinderFinderFinderFinderFinder"
+              isexpandable
             >
             </gr-result-row>
             <gr-result-row
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
index f547cc7..8f1b7a2 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
@@ -521,6 +521,20 @@
     this.redirect(url);
   }
 
+  private dispatchLocationChangeEvent() {
+    const detail: LocationChangeEventDetail = {
+      hash: window.location.hash,
+      pathname: window.location.pathname,
+    };
+    document.dispatchEvent(
+      new CustomEvent('location-change', {
+        detail,
+        composed: true,
+        bubbles: true,
+      })
+    );
+  }
+
   startRouter() {
     const base = getBaseUrl();
     if (base) {
@@ -568,17 +582,7 @@
       // Fire asynchronously so that the URL is changed by the time the event
       // is processed.
       setTimeout(() => {
-        const detail: LocationChangeEventDetail = {
-          hash: window.location.hash,
-          pathname: window.location.pathname,
-        };
-        document.dispatchEvent(
-          new CustomEvent('location-change', {
-            detail,
-            composed: true,
-            bubbles: true,
-          })
-        );
+        this.dispatchLocationChangeEvent();
       }, 1);
       next();
     });
diff --git a/polygerrit-ui/app/elements/gr-app-element.ts b/polygerrit-ui/app/elements/gr-app-element.ts
index 2a5bcf5..c4533de 100644
--- a/polygerrit-ui/app/elements/gr-app-element.ts
+++ b/polygerrit-ui/app/elements/gr-app-element.ts
@@ -181,7 +181,7 @@
     this.addEventListener(EventType.DIALOG_CHANGE, e => {
       this.handleDialogChange(e as CustomEvent<DialogChangeEventDetail>);
     });
-    this.addEventListener(EventType.LOCATION_CHANGE, () =>
+    document.addEventListener(EventType.LOCATION_CHANGE, () =>
       this.handleLocationChange()
     );
     this.addEventListener(EventType.RECREATE_CHANGE_VIEW, () =>
diff --git a/polygerrit-ui/app/elements/gr-app_test.ts b/polygerrit-ui/app/elements/gr-app_test.ts
index 730548c..05ee9ed 100644
--- a/polygerrit-ui/app/elements/gr-app_test.ts
+++ b/polygerrit-ui/app/elements/gr-app_test.ts
@@ -20,6 +20,26 @@
 import {resolve} from '../models/dependency';
 import {removeRequestDependencyListener} from '../test/common-test-setup';
 
+suite('gr-app callback tests', () => {
+  const handleLocationChangeSpy = sinon.spy(
+    GrAppElement.prototype,
+    <any>'handleLocationChange'
+  );
+  const dispatchLocationChangeEventSpy = sinon.spy(
+    GrRouter.prototype,
+    <any>'dispatchLocationChangeEvent'
+  );
+
+  setup(async () => {
+    await fixture<GrApp>(html`<gr-app id="app"></gr-app>`);
+  });
+
+  test("handleLocationChange in gr-app-element is called after dispatching 'location-change' event in gr-router", () => {
+    dispatchLocationChangeEventSpy();
+    assert.isTrue(handleLocationChangeSpy.calledOnce);
+  });
+});
+
 suite('gr-app tests', () => {
   let grApp: GrApp;
   const config = createServerInfo();