From 0c4e163dce59a84ae2e0ee60f2ebb645da10c3c9 Mon Sep 17 00:00:00 2001 From: Kasper Nilsson Date: Thu, 9 Nov 2017 16:08:06 -0800 Subject: [PATCH 1/3] Allow username editing if permitted by project cfg Change-Id: I57986af606833c20535c719a1f7540d22d4602bb --- .../gr-account-info/gr-account-info.html | 19 +++- .../gr-account-info/gr-account-info.js | 45 +++++++-- .../gr-account-info/gr-account-info_test.html | 94 +++++++++++++++---- .../gr-rest-api-interface.js | 58 ++++++------ 4 files changed, 156 insertions(+), 60 deletions(-) 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 494b9e87d6..5dbd466c1f 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 @@ -44,17 +44,28 @@ limitations under the License. date-str="[[_account.registered_on]]"> -
+
Username - [[_account.username]] + [[_account.username]] + +
Full name [[_account.name]] { this._hasNameChange = false; @@ -98,9 +110,15 @@ }, _maybeSetName() { - return this._hasNameChange && this.mutable ? - this.$.restAPI.setAccountName(this._account.name) : - Promise.resolve(); + return this._hasNameChange && this.nameMutable ? + this.$.restAPI.setAccountName(this._account.name) : + Promise.resolve(); + }, + + _maybeSetUsername() { + return this._hasUsernameChange && this.usernameMutable ? + this.$.restAPI.setAccountUsername(this._account.username) : + Promise.resolve(); }, _maybeSetStatus() { @@ -109,11 +127,15 @@ Promise.resolve(); }, - _computeHasUnsavedChanges(name, status) { - return name || status; + _computeHasUnsavedChanges(nameChanged, usernameChanged, statusChanged) { + return nameChanged || usernameChanged || statusChanged; }, - _computeMutable(config) { + _computeUsernameMutable(config) { + return config.auth.editable_account_fields.includes('USER_NAME'); + }, + + _computeNameMutable(config) { return config.auth.editable_account_fields.includes('FULL_NAME'); }, @@ -122,6 +144,11 @@ this._hasStatusChange = true; }, + _usernameChanged() { + if (this._loading) { return; } + this._hasUsernameChange = true; + }, + _nameChanged() { if (this._loading) { return; } this._hasNameChange = true; 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 84fbc09f64..e46be5b1ef 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 @@ -84,18 +84,18 @@ limitations under the License. assert.equal(valueOf('Username').textContent, account.username); }); - test('user name render (immutable)', () => { + test('full name render (immutable)', () => { const section = element.$.nameSection; const displaySpan = section.querySelectorAll('.value')[0]; const inputSpan = section.querySelectorAll('.value')[1]; - assert.isFalse(element.mutable); + assert.isFalse(element.nameMutable); assert.isFalse(displaySpan.hasAttribute('hidden')); assert.equal(displaySpan.textContent, account.name); assert.isTrue(inputSpan.hasAttribute('hidden')); }); - test('user name render (mutable)', () => { + test('full name render (mutable)', () => { element.set('_serverConfig', {auth: {editable_account_fields: ['FULL_NAME']}}); @@ -103,32 +103,62 @@ limitations under the License. const displaySpan = section.querySelectorAll('.value')[0]; const inputSpan = section.querySelectorAll('.value')[1]; - assert.isTrue(element.mutable); + assert.isTrue(element.nameMutable); assert.isTrue(displaySpan.hasAttribute('hidden')); assert.equal(element.$.nameInput.bindValue, account.name); assert.isFalse(inputSpan.hasAttribute('hidden')); }); + test('username render (immutable)', () => { + const section = element.$.usernameSection; + const displaySpan = section.querySelectorAll('.value')[0]; + const inputSpan = section.querySelectorAll('.value')[1]; + + assert.isFalse(element.usernameMutable); + assert.isFalse(displaySpan.hasAttribute('hidden')); + assert.equal(displaySpan.textContent, account.username); + assert.isTrue(inputSpan.hasAttribute('hidden')); + }); + + test('username render (mutable)', () => { + element.set('_serverConfig', + {auth: {editable_account_fields: ['USER_NAME']}}); + + const section = element.$.usernameSection; + const displaySpan = section.querySelectorAll('.value')[0]; + const inputSpan = section.querySelectorAll('.value')[1]; + + assert.isTrue(element.usernameMutable); + assert.isTrue(displaySpan.hasAttribute('hidden')); + assert.equal(element.$.usernameInput.bindValue, account.username); + assert.isFalse(inputSpan.hasAttribute('hidden')); + }); + suite('account info edit', () => { let nameChangedSpy; + let usernameChangedSpy; let statusChangedSpy; let nameStub; + let usernameStub; let statusStub; setup(() => { nameChangedSpy = sandbox.spy(element, '_nameChanged'); + usernameChangedSpy = sandbox.spy(element, '_usernameChanged'); statusChangedSpy = sandbox.spy(element, '_statusChanged'); element.set('_serverConfig', - {auth: {editable_account_fields: ['FULL_NAME']}}); + {auth: {editable_account_fields: ['FULL_NAME', 'USER_NAME']}}); - nameStub = sandbox.stub(element.$.restAPI, 'setAccountName', name => - Promise.resolve()); + nameStub = sandbox.stub(element.$.restAPI, 'setAccountName', + name => Promise.resolve()); + usernameStub = sandbox.stub(element.$.restAPI, 'setAccountUsername', + username => Promise.resolve()); statusStub = sandbox.stub(element.$.restAPI, 'setAccountStatus', status => Promise.resolve()); }); test('name', done => { - assert.isTrue(element.mutable); + assert.isTrue(element.nameMutable); assert.isFalse(element.hasUnsavedChanges); element.set('_account.name', 'new name'); @@ -137,18 +167,39 @@ limitations under the License. assert.isFalse(statusChangedSpy.called); assert.isTrue(element.hasUnsavedChanges); - MockInteractions.pressAndReleaseKeyOn(element.$.nameInput, 13); + element.save().then(() => { + assert.isFalse(usernameStub.called); + assert.isTrue(nameStub.called); + assert.isFalse(statusStub.called); + nameStub.lastCall.returnValue.then(() => { + assert.equal(nameStub.lastCall.args[0], 'new name'); + done(); + }); + }); + }); - assert.isTrue(nameStub.called); - assert.isFalse(statusStub.called); - nameStub.lastCall.returnValue.then(() => { - assert.equal(nameStub.lastCall.args[0], 'new name'); - done(); + test('username', done => { + assert.isTrue(element.usernameMutable); + assert.isFalse(element.hasUnsavedChanges); + + element.set('_account.username', 'new username'); + + assert.isTrue(usernameChangedSpy.called); + assert.isFalse(statusChangedSpy.called); + assert.isTrue(element.hasUnsavedChanges); + + element.save().then(() => { + assert.isTrue(usernameStub.called); + assert.isFalse(nameStub.called); + assert.isFalse(statusStub.called); + usernameStub.lastCall.returnValue.then(() => { + assert.equal(usernameStub.lastCall.args[0], 'new username'); + done(); + }); }); }); test('status', done => { - assert.isTrue(element.mutable); assert.isFalse(element.hasUnsavedChanges); element.set('_account.status', 'new status'); @@ -158,6 +209,7 @@ limitations under the License. assert.isTrue(element.hasUnsavedChanges); element.save().then(() => { + assert.isFalse(usernameStub.called); assert.isTrue(statusStub.called); assert.isFalse(nameStub.called); statusStub.lastCall.returnValue.then(() => { @@ -178,16 +230,18 @@ limitations under the License. nameChangedSpy = sandbox.spy(element, '_nameChanged'); statusChangedSpy = sandbox.spy(element, '_statusChanged'); element.set('_serverConfig', - {auth: {editable_account_fields: ['FULL_NAME']}}); + {auth: {editable_account_fields: ['FULL_NAME']}}); - nameStub = sandbox.stub(element.$.restAPI, 'setAccountName', name => - Promise.resolve()); + nameStub = sandbox.stub(element.$.restAPI, 'setAccountName', + name => Promise.resolve()); statusStub = sandbox.stub(element.$.restAPI, 'setAccountStatus', status => Promise.resolve()); + sandbox.stub(element.$.restAPI, 'setAccountUsername', + username => Promise.resolve()); }); test('set name and status', done => { - assert.isTrue(element.mutable); + assert.isTrue(element.nameMutable); assert.isFalse(element.hasUnsavedChanges); element.set('_account.name', 'new name'); @@ -231,7 +285,7 @@ limitations under the License. const displaySpan = section.querySelectorAll('.value')[0]; const inputSpan = section.querySelectorAll('.value')[1]; - assert.isFalse(element.mutable); + assert.isFalse(element.nameMutable); assert.isFalse(element.hasUnsavedChanges); 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 b8074acaaa..de05f80f95 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 @@ -543,26 +543,40 @@ }); }, + /** + * @param {?Object} obj + */ + _updateCachedAccount(obj) { + // If result of getAccount is in cache, update it in the cache + // so we don't have to invalidate it. + const cachedAccount = this._cache['/accounts/self/detail']; + if (cachedAccount) { + // Replace object in cache with new object to force UI updates. + this._cache['/accounts/self/detail'] = + Object.assign({}, cachedAccount, obj); + } + }, + /** * @param {string} name * @param {function(?Response, string=)=} opt_errFn * @param {?=} opt_ctx */ setAccountName(name, opt_errFn, opt_ctx) { - return this.send('PUT', '/accounts/self/name', {name}, opt_errFn, - opt_ctx).then(response => { - // If result of getAccount is in cache, update it in the cache - // so we don't have to invalidate it. - const cachedAccount = this._cache['/accounts/self/detail']; - if (cachedAccount) { - return this.getResponseObject(response).then(newName => { - // 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, {name: newName}); - }); - } - }); + return this.send('PUT', '/accounts/self/name', {name}, opt_errFn, opt_ctx) + .then(response => this.getResponseObject(response) + .then(newName => this._updateCachedAccount({name: newName}))); + }, + + /** + * @param {string} username + * @param {function(?Response, string=)=} opt_errFn + * @param {?=} opt_ctx + */ + setAccountUsername(username, opt_errFn, opt_ctx) { + return this.send('PUT', '/accounts/self/username', {username}, opt_errFn, + opt_ctx).then(response => this.getResponseObject(response) + .then(newName => this._updateCachedAccount({username: newName}))); }, /** @@ -572,19 +586,9 @@ */ setAccountStatus(status, opt_errFn, opt_ctx) { return this.send('PUT', '/accounts/self/status', {status}, - opt_errFn, opt_ctx).then(response => { - // If result of getAccount is in cache, update it in the cache - // so we don't have to invalidate it. - const cachedAccount = this._cache['/accounts/self/detail']; - if (cachedAccount) { - return this.getResponseObject(response).then(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}); - }); - } - }); + opt_errFn, opt_ctx).then(response => this.getResponseObject(response) + .then(newStatus => this._updateCachedAccount( + {status: newStatus}))); }, getAccountStatus(userId) { From f99f01473da3741c5062fd64753744ccf157768a Mon Sep 17 00:00:00 2001 From: Kasper Nilsson Date: Tue, 14 Nov 2017 15:12:50 -0800 Subject: [PATCH 2/3] Only allow username editing if username is unset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Once set, the username cannot be changed or deleted. If attempted this fails with “405 Method Not Allowed”. Change-Id: I15711ce9c6b350becd2a10fccc9655234e88bc8a --- .../gr-account-info/gr-account-info.html | 4 ++-- .../gr-account-info/gr-account-info.js | 19 ++++++++++++++----- .../gr-account-info/gr-account-info_test.html | 7 +++++-- 3 files changed, 21 insertions(+), 9 deletions(-) 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 5dbd466c1f..55164e0b1f 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 @@ -48,7 +48,7 @@ limitations under the License. Username [[_account.username]] + class="value">[[_username]] @@ -57,7 +57,7 @@ limitations under the License. id="usernameInput" disabled="[[_saving]]" on-keydown="_handleKeydown" - bind-value="{{_account.username}}"> + bind-value="{{_username}}">
Full name 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 03795f669d..a698c71499 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 @@ -27,7 +27,7 @@ usernameMutable: { type: Boolean, notify: true, - computed: '_computeUsernameMutable(_serverConfig)', + computed: '_computeUsernameMutable(_serverConfig, _account.username)', }, nameMutable: { type: Boolean, @@ -64,11 +64,14 @@ /** @type {?} */ _account: Object, _serverConfig: Object, + _username: { + type: String, + observer: '_usernameChanged', + }, }, observers: [ '_nameChanged(_account.name)', - '_usernameChanged(_account.username)', '_statusChanged(_account.status)', ], @@ -82,7 +85,11 @@ })); promises.push(this.$.restAPI.getAccount().then(account => { + // Provide predefined value for username to trigger computation of + // username mutability. + account.username = account.username || ''; this._account = account; + this._username = account.username; })); return Promise.all(promises).then(() => { @@ -117,7 +124,7 @@ _maybeSetUsername() { return this._hasUsernameChange && this.usernameMutable ? - this.$.restAPI.setAccountUsername(this._account.username) : + this.$.restAPI.setAccountUsername(this._username) : Promise.resolve(); }, @@ -131,8 +138,10 @@ return nameChanged || usernameChanged || statusChanged; }, - _computeUsernameMutable(config) { - return config.auth.editable_account_fields.includes('USER_NAME'); + _computeUsernameMutable(config, username) { + // Username may not be changed once it is set. + return config.auth.editable_account_fields.includes('USER_NAME') && + !username; }, _computeNameMutable(config) { 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 e46be5b1ef..d27d15393a 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 @@ -123,6 +123,8 @@ limitations under the License. test('username render (mutable)', () => { element.set('_serverConfig', {auth: {editable_account_fields: ['USER_NAME']}}); + element.set('_account.username', ''); + element.set('_username', ''); const section = element.$.usernameSection; const displaySpan = section.querySelectorAll('.value')[0]; @@ -179,10 +181,11 @@ limitations under the License. }); test('username', done => { + element.set('_account.username', ''); + element._hasUsernameChange = false; assert.isTrue(element.usernameMutable); - assert.isFalse(element.hasUnsavedChanges); - element.set('_account.username', 'new username'); + element.set('_username', 'new username'); assert.isTrue(usernameChangedSpy.called); assert.isFalse(statusChangedSpy.called); From 88c0b9d3d418dbf27de3042ba3d2a1f09df83b54 Mon Sep 17 00:00:00 2001 From: Kasper Nilsson Date: Thu, 9 Nov 2017 15:45:09 -0800 Subject: [PATCH 3/3] Add username to registration dialog Also adds a recommendation to go to the settings page for more advanced configuration, and a link. Bug: Issue 7303 Change-Id: Ib6d03e0411d1db7c55c412d352cb84bf5724b814 --- .../core/gr-navigation/gr-navigation.html | 4 ++ .../app/elements/core/gr-router/gr-router.js | 10 ++++ .../gr-registration-dialog.html | 46 +++++++++++++------ .../gr-registration-dialog.js | 45 ++++++++++++------ .../gr-registration-dialog_test.html | 32 +++++++++---- 5 files changed, 100 insertions(+), 37 deletions(-) diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html index b03f2e5dcb..bd69007280 100644 --- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html +++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html @@ -313,6 +313,10 @@ limitations under the License. } this._navigate(relativeUrl); }, + + getUrlForSettings() { + return this._getUrlFor({view: Gerrit.Nav.View.SETTINGS}); + }, }; })(window); diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js index 88183dabe5..ca35bcc96c 100644 --- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js +++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js @@ -262,6 +262,8 @@ if (params.edit) { url += ',edit'; } + } else if (params.view === Gerrit.Nav.View.SETTINGS) { + url = this._generateSettingsUrl(params); } else { throw new Error('Can\'t generate'); } @@ -269,6 +271,14 @@ return base + url; }, + /** + * @param {!Object} params + * @return {string} + */ + _generateSettingsUrl(params) { + return '/settings'; + }, + /** * Given an object of parameters, potentially including a `patchNum` or a * `basePatchNum` or both, return a string representation of that range. If diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html index de6783de99..0cbd1f6301 100644 --- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html +++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html @@ -16,6 +16,7 @@ limitations under the License. + @@ -37,18 +38,23 @@ limitations under the License. header { border-bottom: 1px solid #cdcdcd; font-family: var(--font-family-bold); + margin-bottom: 1em; } - header, - main, - footer { + .container { padding: .5em 1.5em; } footer { display: flex; - justify-content: space-between; + justify-content: flex-end; + } + footer gr-button { + margin-left: 1em; + } + input { + width: 20em; } -
+
Please confirm your contact information

@@ -64,8 +70,15 @@ limitations under the License. is="iron-input" id="name" bind-value="{{_account.name}}" - disabled="[[_saving]]" - on-keydown="_handleNameKeydown"> + disabled="[[_saving]]"> +

+
+
Username
+
Preferred Email
@@ -78,19 +91,26 @@ limitations under the License.
+
+

+ More configuration options for Gerrit may be found in the + settings. +

- Save Close + Save
- + diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js index 71d80eb377..406d16c104 100644 --- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js +++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js @@ -31,8 +31,18 @@ properties: { /** @type {?} */ - _account: Object, - _saving: Boolean, + _account: { + type: Object, + value: () => { + // Prepopulate possibly undefined fields with values to trigger + // computed bindings. + return {email: null, name: null, username: null}; + }, + }, + _saving: { + type: Boolean, + value: false, + }, }, hostAttributes: { @@ -41,22 +51,19 @@ attached() { this.$.restAPI.getAccount().then(account => { - this._account = account; + // Using Object.assign here allows preservation of the default values + // supplied in the value generating function of this._account, unless + // they are overridden by properties in the account from the response. + this._account = Object.assign({}, this._account, account); }); }, - _handleNameKeydown(e) { - if (e.keyCode === 13) { // Enter - e.stopPropagation(); - this._save(); - } - }, - _save() { this._saving = true; const promises = [ this.$.restAPI.setAccountName(this.$.name.value), - this.$.restAPI.setPreferredAccountEmail(this.$.email.value), + this.$.restAPI.setAccountUsername(this.$.username.value), + this.$.restAPI.setPreferredAccountEmail(this.$.email.value || ''), ]; return Promise.all(promises).then(() => { this._saving = false; @@ -66,15 +73,25 @@ _handleSave(e) { e.preventDefault(); - this._save().then(() => { - this.fire('close'); - }); + this._save().then(this.close.bind(this)); }, _handleClose(e) { e.preventDefault(); + this.close(); + }, + + close() { this._saving = true; // disable buttons indefinitely this.fire('close'); }, + + _computeSaveDisabled(name, username, email, saving) { + return !name || !username || !email || saving; + }, + + _computeSettingsUrl() { + return Gerrit.Nav.getUrlForSettings(); + }, }); })(); diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html index 858c3ae3b0..15b4fa2f2a 100644 --- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html +++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html @@ -41,13 +41,16 @@ limitations under the License. suite('gr-registration-dialog tests', () => { let element; let account; + let sandbox; let _listeners; setup(done => { + sandbox = sinon.sandbox.create(); _listeners = {}; account = { name: 'name', + username: 'username', email: 'email', secondary_emails: [ 'email2', @@ -65,6 +68,10 @@ limitations under the License. account.name = name; return Promise.resolve(); }, + setAccountUsername(username) { + account.username = username; + return Promise.resolve(); + }, setPreferredAccountEmail(email) { account.email = email; return Promise.resolve(); @@ -75,6 +82,7 @@ limitations under the License. }); teardown(() => { + sandbox.restore(); for (const eventType in _listeners) { if (_listeners.hasOwnProperty(eventType)) { element.removeEventListener(eventType, _listeners[eventType]); @@ -119,32 +127,26 @@ limitations under the License. }).then(done); }); - test('saves name and preferred email', done => { + test('saves account details', done => { flush(() => { element.$.name.value = 'new name'; + element.$.username.value = 'new username'; element.$.email.value = 'email3'; // Nothing should be committed yet. assert.equal(account.name, 'name'); + assert.equal(account.username, 'username'); assert.equal(account.email, 'email'); // Save and verify new values are committed. save().then(() => { assert.equal(account.name, 'new name'); + assert.equal(account.username, 'new username'); assert.equal(account.email, 'email3'); }).then(done); }); }); - test('pressing enter saves name', done => { - element.$.name.value = 'entered name'; - save(() => { - MockInteractions.pressAndReleaseKeyOn(element.$.name, 13); // 'enter' - }).then(() => { - assert.equal(account.name, 'entered name'); - }).then(done); - }); - test('email select properly populated', done => { element._account = {email: 'foo', secondary_emails: ['bar', 'baz']}; flush(() => { @@ -152,5 +154,15 @@ limitations under the License. done(); }); }); + + test('save btn disabled', () => { + const compute = element._computeSaveDisabled; + assert.isTrue(compute('', '', '', false)); + assert.isTrue(compute('', 'test', 'test', false)); + assert.isTrue(compute('test', '', 'test', false)); + assert.isTrue(compute('test', 'test', '', false)); + assert.isTrue(compute('test', 'test', 'test', true)); + assert.isFalse(compute('test', 'test', 'test', false)); + }); });