Merge changes from topic "username" into stable-2.15
* changes: Add username to registration dialog Only allow username editing if username is unset Allow username editing if permitted by project cfg
This commit is contained in:
@@ -313,6 +313,10 @@ limitations under the License.
|
|||||||
}
|
}
|
||||||
this._navigate(relativeUrl);
|
this._navigate(relativeUrl);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getUrlForSettings() {
|
||||||
|
return this._getUrlFor({view: Gerrit.Nav.View.SETTINGS});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
})(window);
|
})(window);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -262,6 +262,8 @@
|
|||||||
if (params.edit) {
|
if (params.edit) {
|
||||||
url += ',edit';
|
url += ',edit';
|
||||||
}
|
}
|
||||||
|
} else if (params.view === Gerrit.Nav.View.SETTINGS) {
|
||||||
|
url = this._generateSettingsUrl(params);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Can\'t generate');
|
throw new Error('Can\'t generate');
|
||||||
}
|
}
|
||||||
@@ -269,6 +271,14 @@
|
|||||||
return base + url;
|
return base + url;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {!Object} params
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
_generateSettingsUrl(params) {
|
||||||
|
return '/settings';
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an object of parameters, potentially including a `patchNum` or a
|
* Given an object of parameters, potentially including a `patchNum` or a
|
||||||
* `basePatchNum` or both, return a string representation of that range. If
|
* `basePatchNum` or both, return a string representation of that range. If
|
||||||
|
|||||||
@@ -44,17 +44,28 @@ limitations under the License.
|
|||||||
date-str="[[_account.registered_on]]"></gr-date-formatter>
|
date-str="[[_account.registered_on]]"></gr-date-formatter>
|
||||||
</span>
|
</span>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section id="usernameSection">
|
||||||
<span class="title">Username</span>
|
<span class="title">Username</span>
|
||||||
<span class="value">[[_account.username]]</span>
|
<span
|
||||||
|
hidden$="[[usernameMutable]]"
|
||||||
|
class="value">[[_username]]</span>
|
||||||
|
<span
|
||||||
|
hidden$="[[!usernameMutable]]"
|
||||||
|
class="value">
|
||||||
|
<input
|
||||||
|
is="iron-input"
|
||||||
|
id="usernameInput"
|
||||||
|
disabled="[[_saving]]"
|
||||||
|
on-keydown="_handleKeydown"
|
||||||
|
bind-value="{{_username}}">
|
||||||
</section>
|
</section>
|
||||||
<section id="nameSection">
|
<section id="nameSection">
|
||||||
<span class="title">Full name</span>
|
<span class="title">Full name</span>
|
||||||
<span
|
<span
|
||||||
hidden$="[[mutable]]"
|
hidden$="[[nameMutable]]"
|
||||||
class="value">[[_account.name]]</span>
|
class="value">[[_account.name]]</span>
|
||||||
<span
|
<span
|
||||||
hidden$="[[!mutable]]"
|
hidden$="[[!nameMutable]]"
|
||||||
class="value">
|
class="value">
|
||||||
<input
|
<input
|
||||||
is="iron-input"
|
is="iron-input"
|
||||||
|
|||||||
@@ -24,21 +24,31 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
mutable: {
|
usernameMutable: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
notify: true,
|
notify: true,
|
||||||
computed: '_computeMutable(_serverConfig)',
|
computed: '_computeUsernameMutable(_serverConfig, _account.username)',
|
||||||
|
},
|
||||||
|
nameMutable: {
|
||||||
|
type: Boolean,
|
||||||
|
notify: true,
|
||||||
|
computed: '_computeNameMutable(_serverConfig)',
|
||||||
},
|
},
|
||||||
hasUnsavedChanges: {
|
hasUnsavedChanges: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
notify: true,
|
notify: true,
|
||||||
computed: '_computeHasUnsavedChanges(_hasNameChange, _hasStatusChange)',
|
computed: '_computeHasUnsavedChanges(_hasNameChange, ' +
|
||||||
|
'_hasUsernameChange, _hasStatusChange)',
|
||||||
},
|
},
|
||||||
|
|
||||||
_hasNameChange: {
|
_hasNameChange: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
_hasUsernameChange: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
_hasStatusChange: {
|
_hasStatusChange: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
@@ -54,6 +64,10 @@
|
|||||||
/** @type {?} */
|
/** @type {?} */
|
||||||
_account: Object,
|
_account: Object,
|
||||||
_serverConfig: Object,
|
_serverConfig: Object,
|
||||||
|
_username: {
|
||||||
|
type: String,
|
||||||
|
observer: '_usernameChanged',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
observers: [
|
observers: [
|
||||||
@@ -71,7 +85,11 @@
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
promises.push(this.$.restAPI.getAccount().then(account => {
|
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._account = account;
|
||||||
|
this._username = account.username;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return Promise.all(promises).then(() => {
|
return Promise.all(promises).then(() => {
|
||||||
@@ -88,6 +106,7 @@
|
|||||||
// Set only the fields that have changed.
|
// Set only the fields that have changed.
|
||||||
// Must be done in sequence to avoid race conditions (@see Issue 5721)
|
// Must be done in sequence to avoid race conditions (@see Issue 5721)
|
||||||
return this._maybeSetName()
|
return this._maybeSetName()
|
||||||
|
.then(this._maybeSetUsername.bind(this))
|
||||||
.then(this._maybeSetStatus.bind(this))
|
.then(this._maybeSetStatus.bind(this))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this._hasNameChange = false;
|
this._hasNameChange = false;
|
||||||
@@ -98,9 +117,15 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
_maybeSetName() {
|
_maybeSetName() {
|
||||||
return this._hasNameChange && this.mutable ?
|
return this._hasNameChange && this.nameMutable ?
|
||||||
this.$.restAPI.setAccountName(this._account.name) :
|
this.$.restAPI.setAccountName(this._account.name) :
|
||||||
Promise.resolve();
|
Promise.resolve();
|
||||||
|
},
|
||||||
|
|
||||||
|
_maybeSetUsername() {
|
||||||
|
return this._hasUsernameChange && this.usernameMutable ?
|
||||||
|
this.$.restAPI.setAccountUsername(this._username) :
|
||||||
|
Promise.resolve();
|
||||||
},
|
},
|
||||||
|
|
||||||
_maybeSetStatus() {
|
_maybeSetStatus() {
|
||||||
@@ -109,11 +134,17 @@
|
|||||||
Promise.resolve();
|
Promise.resolve();
|
||||||
},
|
},
|
||||||
|
|
||||||
_computeHasUnsavedChanges(name, status) {
|
_computeHasUnsavedChanges(nameChanged, usernameChanged, statusChanged) {
|
||||||
return name || status;
|
return nameChanged || usernameChanged || statusChanged;
|
||||||
},
|
},
|
||||||
|
|
||||||
_computeMutable(config) {
|
_computeUsernameMutable(config, username) {
|
||||||
|
// Username may not be changed once it is set.
|
||||||
|
return config.auth.editable_account_fields.includes('USER_NAME') &&
|
||||||
|
!username;
|
||||||
|
},
|
||||||
|
|
||||||
|
_computeNameMutable(config) {
|
||||||
return config.auth.editable_account_fields.includes('FULL_NAME');
|
return config.auth.editable_account_fields.includes('FULL_NAME');
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -122,6 +153,11 @@
|
|||||||
this._hasStatusChange = true;
|
this._hasStatusChange = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_usernameChanged() {
|
||||||
|
if (this._loading) { return; }
|
||||||
|
this._hasUsernameChange = true;
|
||||||
|
},
|
||||||
|
|
||||||
_nameChanged() {
|
_nameChanged() {
|
||||||
if (this._loading) { return; }
|
if (this._loading) { return; }
|
||||||
this._hasNameChange = true;
|
this._hasNameChange = true;
|
||||||
|
|||||||
@@ -84,18 +84,18 @@ limitations under the License.
|
|||||||
assert.equal(valueOf('Username').textContent, account.username);
|
assert.equal(valueOf('Username').textContent, account.username);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('user name render (immutable)', () => {
|
test('full name render (immutable)', () => {
|
||||||
const section = element.$.nameSection;
|
const section = element.$.nameSection;
|
||||||
const displaySpan = section.querySelectorAll('.value')[0];
|
const displaySpan = section.querySelectorAll('.value')[0];
|
||||||
const inputSpan = section.querySelectorAll('.value')[1];
|
const inputSpan = section.querySelectorAll('.value')[1];
|
||||||
|
|
||||||
assert.isFalse(element.mutable);
|
assert.isFalse(element.nameMutable);
|
||||||
assert.isFalse(displaySpan.hasAttribute('hidden'));
|
assert.isFalse(displaySpan.hasAttribute('hidden'));
|
||||||
assert.equal(displaySpan.textContent, account.name);
|
assert.equal(displaySpan.textContent, account.name);
|
||||||
assert.isTrue(inputSpan.hasAttribute('hidden'));
|
assert.isTrue(inputSpan.hasAttribute('hidden'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('user name render (mutable)', () => {
|
test('full name render (mutable)', () => {
|
||||||
element.set('_serverConfig',
|
element.set('_serverConfig',
|
||||||
{auth: {editable_account_fields: ['FULL_NAME']}});
|
{auth: {editable_account_fields: ['FULL_NAME']}});
|
||||||
|
|
||||||
@@ -103,32 +103,64 @@ limitations under the License.
|
|||||||
const displaySpan = section.querySelectorAll('.value')[0];
|
const displaySpan = section.querySelectorAll('.value')[0];
|
||||||
const inputSpan = section.querySelectorAll('.value')[1];
|
const inputSpan = section.querySelectorAll('.value')[1];
|
||||||
|
|
||||||
assert.isTrue(element.mutable);
|
assert.isTrue(element.nameMutable);
|
||||||
assert.isTrue(displaySpan.hasAttribute('hidden'));
|
assert.isTrue(displaySpan.hasAttribute('hidden'));
|
||||||
assert.equal(element.$.nameInput.bindValue, account.name);
|
assert.equal(element.$.nameInput.bindValue, account.name);
|
||||||
assert.isFalse(inputSpan.hasAttribute('hidden'));
|
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']}});
|
||||||
|
element.set('_account.username', '');
|
||||||
|
element.set('_username', '');
|
||||||
|
|
||||||
|
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', () => {
|
suite('account info edit', () => {
|
||||||
let nameChangedSpy;
|
let nameChangedSpy;
|
||||||
|
let usernameChangedSpy;
|
||||||
let statusChangedSpy;
|
let statusChangedSpy;
|
||||||
let nameStub;
|
let nameStub;
|
||||||
|
let usernameStub;
|
||||||
let statusStub;
|
let statusStub;
|
||||||
|
|
||||||
setup(() => {
|
setup(() => {
|
||||||
nameChangedSpy = sandbox.spy(element, '_nameChanged');
|
nameChangedSpy = sandbox.spy(element, '_nameChanged');
|
||||||
|
usernameChangedSpy = sandbox.spy(element, '_usernameChanged');
|
||||||
statusChangedSpy = sandbox.spy(element, '_statusChanged');
|
statusChangedSpy = sandbox.spy(element, '_statusChanged');
|
||||||
element.set('_serverConfig',
|
element.set('_serverConfig',
|
||||||
{auth: {editable_account_fields: ['FULL_NAME']}});
|
{auth: {editable_account_fields: ['FULL_NAME', 'USER_NAME']}});
|
||||||
|
|
||||||
nameStub = sandbox.stub(element.$.restAPI, 'setAccountName', name =>
|
nameStub = sandbox.stub(element.$.restAPI, 'setAccountName',
|
||||||
Promise.resolve());
|
name => Promise.resolve());
|
||||||
|
usernameStub = sandbox.stub(element.$.restAPI, 'setAccountUsername',
|
||||||
|
username => Promise.resolve());
|
||||||
statusStub = sandbox.stub(element.$.restAPI, 'setAccountStatus',
|
statusStub = sandbox.stub(element.$.restAPI, 'setAccountStatus',
|
||||||
status => Promise.resolve());
|
status => Promise.resolve());
|
||||||
});
|
});
|
||||||
|
|
||||||
test('name', done => {
|
test('name', done => {
|
||||||
assert.isTrue(element.mutable);
|
assert.isTrue(element.nameMutable);
|
||||||
assert.isFalse(element.hasUnsavedChanges);
|
assert.isFalse(element.hasUnsavedChanges);
|
||||||
|
|
||||||
element.set('_account.name', 'new name');
|
element.set('_account.name', 'new name');
|
||||||
@@ -137,18 +169,40 @@ limitations under the License.
|
|||||||
assert.isFalse(statusChangedSpy.called);
|
assert.isFalse(statusChangedSpy.called);
|
||||||
assert.isTrue(element.hasUnsavedChanges);
|
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);
|
test('username', done => {
|
||||||
assert.isFalse(statusStub.called);
|
element.set('_account.username', '');
|
||||||
nameStub.lastCall.returnValue.then(() => {
|
element._hasUsernameChange = false;
|
||||||
assert.equal(nameStub.lastCall.args[0], 'new name');
|
assert.isTrue(element.usernameMutable);
|
||||||
done();
|
|
||||||
|
element.set('_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 => {
|
test('status', done => {
|
||||||
assert.isTrue(element.mutable);
|
|
||||||
assert.isFalse(element.hasUnsavedChanges);
|
assert.isFalse(element.hasUnsavedChanges);
|
||||||
|
|
||||||
element.set('_account.status', 'new status');
|
element.set('_account.status', 'new status');
|
||||||
@@ -158,6 +212,7 @@ limitations under the License.
|
|||||||
assert.isTrue(element.hasUnsavedChanges);
|
assert.isTrue(element.hasUnsavedChanges);
|
||||||
|
|
||||||
element.save().then(() => {
|
element.save().then(() => {
|
||||||
|
assert.isFalse(usernameStub.called);
|
||||||
assert.isTrue(statusStub.called);
|
assert.isTrue(statusStub.called);
|
||||||
assert.isFalse(nameStub.called);
|
assert.isFalse(nameStub.called);
|
||||||
statusStub.lastCall.returnValue.then(() => {
|
statusStub.lastCall.returnValue.then(() => {
|
||||||
@@ -178,16 +233,18 @@ limitations under the License.
|
|||||||
nameChangedSpy = sandbox.spy(element, '_nameChanged');
|
nameChangedSpy = sandbox.spy(element, '_nameChanged');
|
||||||
statusChangedSpy = sandbox.spy(element, '_statusChanged');
|
statusChangedSpy = sandbox.spy(element, '_statusChanged');
|
||||||
element.set('_serverConfig',
|
element.set('_serverConfig',
|
||||||
{auth: {editable_account_fields: ['FULL_NAME']}});
|
{auth: {editable_account_fields: ['FULL_NAME']}});
|
||||||
|
|
||||||
nameStub = sandbox.stub(element.$.restAPI, 'setAccountName', name =>
|
nameStub = sandbox.stub(element.$.restAPI, 'setAccountName',
|
||||||
Promise.resolve());
|
name => Promise.resolve());
|
||||||
statusStub = sandbox.stub(element.$.restAPI, 'setAccountStatus',
|
statusStub = sandbox.stub(element.$.restAPI, 'setAccountStatus',
|
||||||
status => Promise.resolve());
|
status => Promise.resolve());
|
||||||
|
sandbox.stub(element.$.restAPI, 'setAccountUsername',
|
||||||
|
username => Promise.resolve());
|
||||||
});
|
});
|
||||||
|
|
||||||
test('set name and status', done => {
|
test('set name and status', done => {
|
||||||
assert.isTrue(element.mutable);
|
assert.isTrue(element.nameMutable);
|
||||||
assert.isFalse(element.hasUnsavedChanges);
|
assert.isFalse(element.hasUnsavedChanges);
|
||||||
|
|
||||||
element.set('_account.name', 'new name');
|
element.set('_account.name', 'new name');
|
||||||
@@ -231,7 +288,7 @@ limitations under the License.
|
|||||||
const displaySpan = section.querySelectorAll('.value')[0];
|
const displaySpan = section.querySelectorAll('.value')[0];
|
||||||
const inputSpan = section.querySelectorAll('.value')[1];
|
const inputSpan = section.querySelectorAll('.value')[1];
|
||||||
|
|
||||||
assert.isFalse(element.mutable);
|
assert.isFalse(element.nameMutable);
|
||||||
|
|
||||||
assert.isFalse(element.hasUnsavedChanges);
|
assert.isFalse(element.hasUnsavedChanges);
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ limitations under the License.
|
|||||||
|
|
||||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../../../styles/gr-form-styles.html">
|
<link rel="import" href="../../../styles/gr-form-styles.html">
|
||||||
|
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
|
||||||
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
||||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||||
<link rel="import" href="../../../styles/shared-styles.html">
|
<link rel="import" href="../../../styles/shared-styles.html">
|
||||||
@@ -37,18 +38,23 @@ limitations under the License.
|
|||||||
header {
|
header {
|
||||||
border-bottom: 1px solid #cdcdcd;
|
border-bottom: 1px solid #cdcdcd;
|
||||||
font-family: var(--font-family-bold);
|
font-family: var(--font-family-bold);
|
||||||
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
header,
|
.container {
|
||||||
main,
|
|
||||||
footer {
|
|
||||||
padding: .5em 1.5em;
|
padding: .5em 1.5em;
|
||||||
}
|
}
|
||||||
footer {
|
footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
footer gr-button {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
width: 20em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<main class="gr-form-styles">
|
<div class="container gr-form-styles">
|
||||||
<header>Please confirm your contact information</header>
|
<header>Please confirm your contact information</header>
|
||||||
<main>
|
<main>
|
||||||
<p>
|
<p>
|
||||||
@@ -64,8 +70,15 @@ limitations under the License.
|
|||||||
is="iron-input"
|
is="iron-input"
|
||||||
id="name"
|
id="name"
|
||||||
bind-value="{{_account.name}}"
|
bind-value="{{_account.name}}"
|
||||||
disabled="[[_saving]]"
|
disabled="[[_saving]]">
|
||||||
on-keydown="_handleNameKeydown">
|
</section>
|
||||||
|
<section>
|
||||||
|
<div class="title">Username</div>
|
||||||
|
<input
|
||||||
|
is="iron-input"
|
||||||
|
id="username"
|
||||||
|
bind-value="{{_account.username}}"
|
||||||
|
disabled="[[_saving]]">
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<div class="title">Preferred Email</div>
|
<div class="title">Preferred Email</div>
|
||||||
@@ -78,19 +91,26 @@ limitations under the License.
|
|||||||
</template>
|
</template>
|
||||||
</select>
|
</select>
|
||||||
</section>
|
</section>
|
||||||
|
<hr>
|
||||||
|
<p>
|
||||||
|
More configuration options for Gerrit may be found in the
|
||||||
|
<a on-tap="close" href$="[[_computeSettingsUrl(_account)]]">settings</a>.
|
||||||
|
</p>
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
<gr-button
|
|
||||||
id="saveButton"
|
|
||||||
primary
|
|
||||||
disabled="[[_saving]]"
|
|
||||||
on-tap="_handleSave">Save</gr-button>
|
|
||||||
<gr-button
|
<gr-button
|
||||||
id="closeButton"
|
id="closeButton"
|
||||||
|
link
|
||||||
disabled="[[_saving]]"
|
disabled="[[_saving]]"
|
||||||
on-tap="_handleClose">Close</gr-button>
|
on-tap="_handleClose">Close</gr-button>
|
||||||
|
<gr-button
|
||||||
|
id="saveButton"
|
||||||
|
primary
|
||||||
|
link
|
||||||
|
disabled="[[_computeSaveDisabled(_account.name, _account.username, _account.email, _saving)]]"
|
||||||
|
on-tap="_handleSave">Save</gr-button>
|
||||||
</footer>
|
</footer>
|
||||||
</main>
|
</div>
|
||||||
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||||
</template>
|
</template>
|
||||||
<script src="gr-registration-dialog.js"></script>
|
<script src="gr-registration-dialog.js"></script>
|
||||||
|
|||||||
@@ -31,8 +31,18 @@
|
|||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
/** @type {?} */
|
/** @type {?} */
|
||||||
_account: Object,
|
_account: {
|
||||||
_saving: Boolean,
|
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: {
|
hostAttributes: {
|
||||||
@@ -41,22 +51,19 @@
|
|||||||
|
|
||||||
attached() {
|
attached() {
|
||||||
this.$.restAPI.getAccount().then(account => {
|
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() {
|
_save() {
|
||||||
this._saving = true;
|
this._saving = true;
|
||||||
const promises = [
|
const promises = [
|
||||||
this.$.restAPI.setAccountName(this.$.name.value),
|
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(() => {
|
return Promise.all(promises).then(() => {
|
||||||
this._saving = false;
|
this._saving = false;
|
||||||
@@ -66,15 +73,25 @@
|
|||||||
|
|
||||||
_handleSave(e) {
|
_handleSave(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this._save().then(() => {
|
this._save().then(this.close.bind(this));
|
||||||
this.fire('close');
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_handleClose(e) {
|
_handleClose(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
close() {
|
||||||
this._saving = true; // disable buttons indefinitely
|
this._saving = true; // disable buttons indefinitely
|
||||||
this.fire('close');
|
this.fire('close');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_computeSaveDisabled(name, username, email, saving) {
|
||||||
|
return !name || !username || !email || saving;
|
||||||
|
},
|
||||||
|
|
||||||
|
_computeSettingsUrl() {
|
||||||
|
return Gerrit.Nav.getUrlForSettings();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -41,13 +41,16 @@ limitations under the License.
|
|||||||
suite('gr-registration-dialog tests', () => {
|
suite('gr-registration-dialog tests', () => {
|
||||||
let element;
|
let element;
|
||||||
let account;
|
let account;
|
||||||
|
let sandbox;
|
||||||
let _listeners;
|
let _listeners;
|
||||||
|
|
||||||
setup(done => {
|
setup(done => {
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
_listeners = {};
|
_listeners = {};
|
||||||
|
|
||||||
account = {
|
account = {
|
||||||
name: 'name',
|
name: 'name',
|
||||||
|
username: 'username',
|
||||||
email: 'email',
|
email: 'email',
|
||||||
secondary_emails: [
|
secondary_emails: [
|
||||||
'email2',
|
'email2',
|
||||||
@@ -65,6 +68,10 @@ limitations under the License.
|
|||||||
account.name = name;
|
account.name = name;
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
|
setAccountUsername(username) {
|
||||||
|
account.username = username;
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
setPreferredAccountEmail(email) {
|
setPreferredAccountEmail(email) {
|
||||||
account.email = email;
|
account.email = email;
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
@@ -75,6 +82,7 @@ limitations under the License.
|
|||||||
});
|
});
|
||||||
|
|
||||||
teardown(() => {
|
teardown(() => {
|
||||||
|
sandbox.restore();
|
||||||
for (const eventType in _listeners) {
|
for (const eventType in _listeners) {
|
||||||
if (_listeners.hasOwnProperty(eventType)) {
|
if (_listeners.hasOwnProperty(eventType)) {
|
||||||
element.removeEventListener(eventType, _listeners[eventType]);
|
element.removeEventListener(eventType, _listeners[eventType]);
|
||||||
@@ -119,32 +127,26 @@ limitations under the License.
|
|||||||
}).then(done);
|
}).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('saves name and preferred email', done => {
|
test('saves account details', done => {
|
||||||
flush(() => {
|
flush(() => {
|
||||||
element.$.name.value = 'new name';
|
element.$.name.value = 'new name';
|
||||||
|
element.$.username.value = 'new username';
|
||||||
element.$.email.value = 'email3';
|
element.$.email.value = 'email3';
|
||||||
|
|
||||||
// Nothing should be committed yet.
|
// Nothing should be committed yet.
|
||||||
assert.equal(account.name, 'name');
|
assert.equal(account.name, 'name');
|
||||||
|
assert.equal(account.username, 'username');
|
||||||
assert.equal(account.email, 'email');
|
assert.equal(account.email, 'email');
|
||||||
|
|
||||||
// Save and verify new values are committed.
|
// Save and verify new values are committed.
|
||||||
save().then(() => {
|
save().then(() => {
|
||||||
assert.equal(account.name, 'new name');
|
assert.equal(account.name, 'new name');
|
||||||
|
assert.equal(account.username, 'new username');
|
||||||
assert.equal(account.email, 'email3');
|
assert.equal(account.email, 'email3');
|
||||||
}).then(done);
|
}).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 => {
|
test('email select properly populated', done => {
|
||||||
element._account = {email: 'foo', secondary_emails: ['bar', 'baz']};
|
element._account = {email: 'foo', secondary_emails: ['bar', 'baz']};
|
||||||
flush(() => {
|
flush(() => {
|
||||||
@@ -152,5 +154,15 @@ limitations under the License.
|
|||||||
done();
|
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));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -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 {string} name
|
||||||
* @param {function(?Response, string=)=} opt_errFn
|
* @param {function(?Response, string=)=} opt_errFn
|
||||||
* @param {?=} opt_ctx
|
* @param {?=} opt_ctx
|
||||||
*/
|
*/
|
||||||
setAccountName(name, opt_errFn, opt_ctx) {
|
setAccountName(name, opt_errFn, opt_ctx) {
|
||||||
return this.send('PUT', '/accounts/self/name', {name}, opt_errFn,
|
return this.send('PUT', '/accounts/self/name', {name}, opt_errFn, opt_ctx)
|
||||||
opt_ctx).then(response => {
|
.then(response => this.getResponseObject(response)
|
||||||
// If result of getAccount is in cache, update it in the cache
|
.then(newName => this._updateCachedAccount({name: newName})));
|
||||||
// so we don't have to invalidate it.
|
},
|
||||||
const cachedAccount = this._cache['/accounts/self/detail'];
|
|
||||||
if (cachedAccount) {
|
/**
|
||||||
return this.getResponseObject(response).then(newName => {
|
* @param {string} username
|
||||||
// Replace object in cache with new object to force UI updates.
|
* @param {function(?Response, string=)=} opt_errFn
|
||||||
// TODO(logan): Polyfill for Object.assign in IE
|
* @param {?=} opt_ctx
|
||||||
this._cache['/accounts/self/detail'] = Object.assign(
|
*/
|
||||||
{}, cachedAccount, {name: newName});
|
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) {
|
setAccountStatus(status, opt_errFn, opt_ctx) {
|
||||||
return this.send('PUT', '/accounts/self/status', {status},
|
return this.send('PUT', '/accounts/self/status', {status},
|
||||||
opt_errFn, opt_ctx).then(response => {
|
opt_errFn, opt_ctx).then(response => this.getResponseObject(response)
|
||||||
// If result of getAccount is in cache, update it in the cache
|
.then(newStatus => this._updateCachedAccount(
|
||||||
// so we don't have to invalidate it.
|
{status: newStatus})));
|
||||||
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});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getAccountStatus(userId) {
|
getAccountStatus(userId) {
|
||||||
|
|||||||
Reference in New Issue
Block a user