Merge "Fix for..of loop inside gr-reply-dialog"
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 2737f18..168e5b3 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
@@ -402,7 +402,8 @@
       return jsonPromise.then(result => {
         const errors = [];
         for (const state of ['reviewers', 'ccs']) {
-          for (const reviewer of result[state]) {
+          if (!result.hasOwnProperty(state)) { continue; }
+          for (const reviewer of Object.values(result[state])) {
             if (reviewer.error) {
               errors.push(reviewer.error);
             }
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 86219e7..8e5444a 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
@@ -406,12 +406,13 @@
         }
         event.detail.response.text().then(body => {
           assert.equal(body, 'first error, second error');
+          done();
         });
       });
 
       // Async tick is needed because iron-selector content is distributed and
       // distributed content requires an observer to be set up.
-      flush(() => { element.send().then(done); });
+      flush(() => { element.send(); });
     });
 
     test('ccs are displayed if NoteDb is enabled', () => {
@@ -692,5 +693,65 @@
           element._computeSendButtonLabel(true),
           'Start review');
     });
+
+    test('_handle400Error reviewrs and CCs', done => {
+      const error1 = 'error 1';
+      const error2 = 'error 2';
+      const error3 = 'error 3';
+      const response = {
+        status: 400,
+        text() {
+          return Promise.resolve(')]}\'' + JSON.stringify({
+            reviewers: {
+              username1: {
+                input: 'user 1',
+                error: error1,
+              },
+              username2: {
+                input: 'user 2',
+                error: error2,
+              },
+            },
+            ccs: {
+              username3: {
+                input: 'user 3',
+                error: error3,
+              }
+            },
+          }));
+        }
+      };
+      element.addEventListener('server-error', e => {
+        e.detail.response.text().then(text => {
+          assert.equal(text, [error1, error2, error3].join(', '));
+          done();
+        })
+      });
+      element._handle400Error(response);
+    });
+
+    test('_handle400Error CCs only', done => {
+      const error1 = 'error 1';
+      const response = {
+        status: 400,
+        text() {
+          return Promise.resolve(')]}\'' + JSON.stringify({
+            ccs: {
+              username1: {
+                input: 'user 1',
+                error: error1,
+              }
+            },
+          }));
+        }
+      };
+      element.addEventListener('server-error', e => {
+        e.detail.response.text().then(text => {
+          assert.equal(text, error1);
+          done();
+        })
+      });
+      element._handle400Error(response);
+    });
   });
 </script>