Add status field to settings view

This change allows the user to modify their status via the settings
view.

Feature: Issue 4394
Change-Id: I07f721a13b150968e5fc795afeadbc7322e620b7
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
index 38b1db6..4831000 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
@@ -138,13 +138,13 @@
       return loading || !this._changes || this._changes.length < changesPerPage;
     },
 
-    _handleNextPage() {
+    _handleNextPage: function() {
       if (this._hideNextArrow(this._offset)) { return; }
       page.show(this._computeNavLink(
           this._query, this._offset, 1, this._changesPerPage));
     },
 
-    _handlePreviousPage() {
+    _handlePreviousPage: function() {
       if (this._hidePrevArrow(this._offset)) { return; }
       page.show(this._computeNavLink(
           this._query, this._offset, -1, this._changesPerPage));
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html
index b34925a..1ff1f96 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html
@@ -54,11 +54,23 @@
             class="value">
           <input
               is="iron-input"
+              id="nameInput"
               disabled="[[_saving]]"
-              on-keydown="_handleNameKeydown"
+              on-keydown="_handleKeydown"
               bind-value="{{_account.name}}">
         </span>
       </section>
+      <section>
+        <span class="title">Status</span>
+        <span class="value">
+          <input
+              is="iron-input"
+              id="statusInput"
+              disabled="[[_saving]]"
+              on-keydown="_handleKeydown"
+              bind-value="{{_account.status}}">
+          </span>
+      </section>
     </div>
     <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
   </template>
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js
index 2704ce5..25762d4 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js
@@ -48,7 +48,7 @@
     },
 
     observers: [
-      '_nameChanged(_account.name)',
+      '_accountChanged(_account.*)',
     ],
 
     loadData: function() {
@@ -75,7 +75,10 @@
       }
 
       this._saving = true;
-      return this.$.restAPI.setAccountName(this._account.name).then(function() {
+      return Promise.all([
+        this.$.restAPI.setAccountName(this._account.name),
+        this.$.restAPI.setAccountStatus(this._account.status),
+      ]).then(function() {
         this.hasUnsavedChanges = false;
         this._saving = false;
         this.fire('account-detail-update');
@@ -86,12 +89,12 @@
       return config.auth.editable_account_fields.indexOf('FULL_NAME') !== -1;
     },
 
-    _nameChanged: function() {
+    _accountChanged: function() {
       if (this._loading) { return; }
       this.hasUnsavedChanges = true;
     },
 
-    _handleNameKeydown: function(e) {
+    _handleKeydown: function(e) {
       if (e.keyCode === 13) { // Enter
         e.stopPropagation();
         this.save();
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html
index 9e1472d..5dbbf1f 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html
@@ -35,7 +35,7 @@
     var element;
     var account;
     var config;
-    var nameInput;
+    var sandbox;
 
     function valueOf(title) {
       var sections = Polymer.dom(element.root).querySelectorAll('section');
@@ -49,6 +49,7 @@
     }
 
     setup(function(done) {
+      sandbox = sinon.sandbox.create();
       account = {
         _account_id: 123,
         name: 'user name',
@@ -66,13 +67,14 @@
         },
       });
       element = fixture('basic');
-
-      nameInput = element.$.nameSection.querySelector('.value input');
-
       // Allow the element to render.
       element.loadData().then(function() { flush(done); });
     });
 
+    teardown(function() {
+      sandbox.restore();
+    });
+
     test('basic account info render', function() {
       assert.isFalse(element._loading);
 
@@ -102,33 +104,61 @@
 
       assert.isTrue(element.mutable);
       assert.isTrue(displaySpan.hasAttribute('hidden'));
-      assert.equal(nameInput.bindValue, account.name);
+      assert.equal(element.$.nameInput.bindValue, account.name);
       assert.isFalse(inputSpan.hasAttribute('hidden'));
     });
 
-    test('account info edit', function(done) {
-      element.set('_serverConfig',
+    suite('account info edit', function() {
+      var accountChangedSpy;
+      var nameStub;
+      var statusStub;
+
+      setup(function() {
+        accountChangedSpy = sandbox.spy(element, '_accountChanged');
+        element.set('_serverConfig',
           {auth: {editable_account_fields: ['FULL_NAME']}});
 
-      var setStub = sinon.stub(element.$.restAPI, 'setAccountName',
-          function(name) { return Promise.resolve(); });
+        nameStub = sandbox.stub(element.$.restAPI, 'setAccountName',
+            function(name) { return Promise.resolve(); });
+        statusStub = sandbox.stub(element.$.restAPI, 'setAccountStatus',
+            function(status) { return Promise.resolve(); });
+      });
 
-      var nameChangedSpy = sinon.spy(element, '_nameChanged');
+      test('name', function(done) {
+        assert.isTrue(element.mutable);
+        assert.isFalse(element.hasUnsavedChanges);
 
-      assert.isTrue(element.mutable);
-      assert.isFalse(element.hasUnsavedChanges);
+        element.set('_account.name', 'new name');
 
-      element.set('_account.name', 'new name');
+        assert.isTrue(accountChangedSpy.called);
+        assert.isTrue(element.hasUnsavedChanges);
 
-      assert.isTrue(nameChangedSpy.called);
-      assert.isTrue(element.hasUnsavedChanges);
+        MockInteractions.pressAndReleaseKeyOn(element.$.nameInput, 13);
 
-      MockInteractions.pressAndReleaseKeyOn(nameInput, 13);
+        assert.isTrue(nameStub.called);
+        nameStub.lastCall.returnValue.then(function() {
+          assert.equal(nameStub.lastCall.args[0], 'new name');
+          done();
+        });
+      });
 
-      assert.isTrue(setStub.called);
-      setStub.lastCall.returnValue.then(function() {
-        assert.equal(setStub.lastCall.args[0], 'new name');
-        done();
+      test('status', function(done) {
+        assert.isTrue(element.mutable);
+        assert.isFalse(element.hasUnsavedChanges);
+
+        element.set('_account.status', 'new status');
+
+        assert.isTrue(accountChangedSpy.called);
+        assert.isTrue(element.hasUnsavedChanges);
+
+        MockInteractions.pressAndReleaseKeyOn(element.$.statusInput, 13);
+        flushAsynchronousOperations();
+
+        assert.isTrue(statusStub.called);
+        statusStub.lastCall.returnValue.then(function() {
+          assert.equal(statusStub.lastCall.args[0], 'new status');
+          done();
+        });
       });
     });
   });
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 07f59ae..03e396f 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -288,6 +288,23 @@
       }.bind(this));
     },
 
+    setAccountStatus: function(status, opt_errFn, opt_ctx) {
+      return this.send('PUT', '/accounts/self/status', {status: status},
+        opt_errFn, opt_ctx).then(function(response) {
+        // If result of getAccount is in cache, update it in the cache
+        // so we don't have to invalidate it.
+        var cachedAccount = this._cache['/accounts/self/detail'];
+        if (cachedAccount) {
+          return this.getResponseObject(response).then(function(newStatus) {
+            // Replace object in cache with new object to force UI updates.
+            // TODO(logan): Polyfill for Object.assign in IE
+            this._cache['/accounts/self/detail'] = Object.assign(
+                {}, cachedAccount, {status: newStatus});
+          }.bind(this));
+        }
+      }.bind(this));
+    },
+
     getAccountGroups: function() {
       return this._fetchSharedCacheURL('/accounts/self/groups');
     },
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index 3d65062..0d6d40d 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -436,5 +436,19 @@
         done();
       });
     });
+
+    test('setAccountStatus', function(done) {
+      sandbox.stub(element, 'send').returns(Promise.resolve('OOO'));
+      sandbox.stub(element, 'getResponseObject')
+          .returns(Promise.resolve('OOO'));
+      element._cache['/accounts/self/detail'] = {};
+      element.setAccountStatus('OOO').then(function() {
+        assert.isTrue(element.send.calledWith('PUT', '/accounts/self/status',
+            {status: 'OOO'}));
+        assert.deepEqual(element._cache['/accounts/self/detail'],
+            {status: 'OOO'});
+        done();
+      });
+    });
   });
 </script>