blob: 69754b98a131878d4aab23f0015b3dd04cb4a042 [file] [log] [blame]
/**
* @license
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '../../../test/common-test-setup';
import './gr-account-info';
import {
query,
queryAll,
queryAndAssert,
stubRestApi,
} from '../../../test/test-utils';
import {GrAccountInfo} from './gr-account-info';
import {AccountDetailInfo, ServerInfo} from '../../../types/common';
import {
createAccountDetailWithId,
createAccountWithIdNameAndEmail,
createAuth,
createPreferences,
createServerInfo,
} from '../../../test/test-data-generators';
import {SinonStubbedMember} from 'sinon';
import {RestApiService} from '../../../services/gr-rest-api/gr-rest-api';
import {EditableAccountField} from '../../../api/rest-api';
import {assert, fixture, html} from '@open-wc/testing';
import {GrAutogrowTextarea} from '../../shared/gr-autogrow-textarea/gr-autogrow-textarea';
import {MdOutlinedTextField} from '@material/web/textfield/outlined-text-field';
suite('gr-account-info tests', () => {
let element!: GrAccountInfo;
let account: AccountDetailInfo;
let config: ServerInfo;
function queryInput(selector: string): MdOutlinedTextField {
const input = query<MdOutlinedTextField>(element, selector);
if (!input)
assert.fail(`<md-outlined-text-field> with id ${selector} not found.`);
return input;
}
function valueOf(title: string): Element {
const sections = queryAll<HTMLElement>(element, 'section') ?? [];
let titleEl;
for (let i = 0; i < sections.length; i++) {
titleEl = sections[i].querySelector('.title');
if (titleEl?.textContent === title) {
const el = sections[i].querySelector('.value');
if (el) return el;
}
}
assert.fail(`element with title ${title} not found`);
}
setup(async () => {
account = createAccountWithIdNameAndEmail(123) as AccountDetailInfo;
config = createServerInfo();
stubRestApi('getAccount').resolves(account);
stubRestApi('getConfig').resolves(config);
stubRestApi('getPreferences').resolves(createPreferences());
element = await fixture(html`<gr-account-info></gr-account-info>`);
await element.loadData();
await element.updateComplete;
});
test('renders', () => {
assert.shadowDom.equal(
element,
/* HTML */ `
<div class="gr-form-styles">
<p>
All profile fields below may be publicly displayed to others,
including on changes you are associated with, as well as in search
and autocompletion.
<a
href="https://gerrit-review.googlesource.com/Documentation/user-privacy.html"
>Learn more</a
>
</p>
<gr-endpoint-decorator name="profile"></gr-endpoint-decorator>
<section>
<span class="title"></span>
<span class="value">
<gr-avatar hidden="" imagesize="120"></gr-avatar>
</span>
</section>
<section>
<span class="title">ID</span>
<span class="value">123</span>
</section>
<section>
<span class="title">Email</span>
<span class="value">user-123@</span>
</section>
<section>
<span class="title">Registered</span>
<span class="value">
<gr-date-formatter withtooltip=""></gr-date-formatter>
</span>
</section>
<section id="usernameSection">
<span class="title">Username</span>
<span class="value"></span>
</section>
<section id="nameSection">
<label class="title" for="nameInput">Full name</label>
<span class="value">User-123</span>
</section>
<section>
<label class="title" for="displayNameInput">Display name</label>
<span class="value">
<md-outlined-text-field
autocomplete=""
class="showBlueFocusBorder"
id="displayNameInput"
inputmode=""
type="text"
>
</md-outlined-text-field>
</span>
</section>
<section>
<span class="title">
<label for="statusInput">About me (e.g. employer)</label>
<div class="lengthCounter">0/140</div>
</span>
<span class="value">
<gr-autogrow-textarea id="statusInput" maxlength="140" />
</span>
</section>
<section>
<span class="title">
<gr-tooltip-content
has-tooltip=""
show-icon=""
title="This is how you appear to others"
>
Account preview
</gr-tooltip-content>
</span>
<span class="value"
><gr-account-chip></gr-account-chip>
<gr-hovercard-account-contents></gr-hovercard-account-contents>
</span>
</section>
</div>
`,
{ignoreChildren: ['p']}
);
});
test('basic account info render', () => {
assert.isFalse(element.loading);
assert.equal(valueOf('ID').textContent, `${account._account_id}`);
assert.equal(valueOf('Email').textContent, account.email);
assert.equal(valueOf('Username').textContent, account.username);
});
test('full name render (immutable)', () => {
const section = query<HTMLElement>(element, '#nameSection')!;
const displaySpan = section.querySelectorAll('.value')[0];
const inputSpan = section.querySelectorAll('.value')[1];
assert.isFalse(element.nameMutable);
assert.isFalse(displaySpan.hasAttribute('hidden'));
assert.equal(displaySpan.textContent, account.name);
assert.isUndefined(inputSpan);
});
test('full name render (mutable)', async () => {
element.serverConfig = {
...createServerInfo(),
auth: {
...createAuth(),
editable_account_fields: [EditableAccountField.FULL_NAME],
},
};
await element.updateComplete;
const section = query<HTMLElement>(element, '#nameSection')!;
const inputSpan = section.querySelectorAll('.value')[0];
assert.isTrue(element.nameMutable);
assert.equal(queryInput('#nameInput').value, account.name);
assert.isFalse(inputSpan.hasAttribute('hidden'));
});
test('username render (immutable)', () => {
const section = query<HTMLElement>(element, '#usernameSection')!;
const displaySpan = section.querySelectorAll('.value')[0];
const inputSpan = section.querySelectorAll('.value')[1];
assert.isFalse(element.computeUsernameEditable());
assert.isFalse(displaySpan.hasAttribute('hidden'));
assert.equal(displaySpan.textContent, account.username);
assert.isUndefined(inputSpan);
});
test('username render (mutable)', async () => {
element.serverConfig = {
...createServerInfo(),
auth: {
...createAuth(),
editable_account_fields: [EditableAccountField.USER_NAME],
},
};
element.account!.username = '';
element.username = '';
await element.updateComplete;
const section = query<HTMLElement>(element, '#usernameSection')!;
const inputSpan = section.querySelectorAll('.value')[0];
assert.isTrue(element.computeUsernameEditable());
assert.equal(queryInput('#usernameInput').value, account.username);
assert.isFalse(inputSpan.hasAttribute('hidden'));
});
suite('account info edit', () => {
let nameStub: SinonStubbedMember<RestApiService['setAccountName']>;
let usernameStub: SinonStubbedMember<RestApiService['setAccountUsername']>;
let statusStub: SinonStubbedMember<RestApiService['setAccountStatus']>;
setup(async () => {
element.serverConfig = {
...createServerInfo(),
auth: {
...createAuth(),
editable_account_fields: [
EditableAccountField.FULL_NAME,
EditableAccountField.USER_NAME,
],
},
};
await element.updateComplete;
nameStub = stubRestApi('setAccountName').resolves();
usernameStub = stubRestApi('setAccountUsername').resolves();
statusStub = stubRestApi('setAccountStatus').resolves();
});
test('name', async () => {
assert.isTrue(element.nameMutable);
assert.isFalse(element.hasUnsavedChanges);
const statusInputEl = queryInput('#nameInput');
statusInputEl.value = 'new name';
statusInputEl.dispatchEvent(
new Event('input', {bubbles: true, composed: true})
);
await element.updateComplete;
assert.isTrue(element.hasNameChange);
assert.isFalse(element.hasStatusChange);
assert.isTrue(element.hasUnsavedChanges);
await element.save();
assert.isFalse(usernameStub.called);
assert.isTrue(nameStub.called);
assert.isFalse(statusStub.called);
await nameStub.lastCall.returnValue;
assert.equal(nameStub.lastCall.args[0], 'new name');
});
test('username', async () => {
element.account!.username = '';
element.username = 't';
element.hasUsernameChange = false;
element.serverConfig = {
...createServerInfo(),
auth: {
...createAuth(),
editable_account_fields: [EditableAccountField.USER_NAME],
},
};
await element.updateComplete;
assert.isTrue(element.computeUsernameEditable());
const statusInputEl = queryInput('#usernameInput');
statusInputEl.value = 'new username';
statusInputEl.dispatchEvent(
new Event('input', {bubbles: true, composed: true})
);
await element.updateComplete;
assert.isTrue(element.hasUsernameChange);
assert.isFalse(element.hasNameChange);
assert.isFalse(element.hasStatusChange);
assert.isTrue(element.hasUnsavedChanges);
await element.save();
assert.isTrue(usernameStub.called);
assert.isFalse(nameStub.called);
assert.isFalse(statusStub.called);
await usernameStub.lastCall.returnValue;
assert.equal(usernameStub.lastCall.args[0], 'new username');
});
test('status', async () => {
assert.isFalse(element.hasUnsavedChanges);
const statusTextarea = queryAndAssert<GrAutogrowTextarea>(
element,
'#statusInput'
);
statusTextarea.value = 'new status';
statusTextarea.dispatchEvent(
new Event('input', {bubbles: true, composed: true})
);
await element.updateComplete;
assert.isFalse(element.hasNameChange);
assert.isTrue(element.hasStatusChange);
assert.isTrue(element.hasUnsavedChanges);
await element.save();
assert.isFalse(usernameStub.called);
assert.isTrue(statusStub.called);
assert.isFalse(nameStub.called);
await statusStub.lastCall.returnValue;
assert.equal(statusStub.lastCall.args[0], 'new status');
});
});
suite('edit name and status', () => {
let nameStub: SinonStubbedMember<RestApiService['setAccountName']>;
let statusStub: SinonStubbedMember<RestApiService['setAccountStatus']>;
setup(async () => {
element.serverConfig = {
...createServerInfo(),
auth: {
...createAuth(),
editable_account_fields: [EditableAccountField.FULL_NAME],
},
};
await element.updateComplete;
nameStub = stubRestApi('setAccountName').resolves();
statusStub = stubRestApi('setAccountStatus').resolves();
stubRestApi('setAccountUsername').resolves();
});
test('set name and status', async () => {
assert.isTrue(element.nameMutable);
assert.isFalse(element.hasUnsavedChanges);
const inputEl = queryInput('#nameInput');
inputEl.value = 'new name';
inputEl.dispatchEvent(
new Event('input', {bubbles: true, composed: true})
);
await element.updateComplete;
assert.isTrue(element.hasNameChange);
const statusTextarea = queryAndAssert<GrAutogrowTextarea>(
element,
'#statusInput'
);
statusTextarea.value = 'new status';
statusTextarea.dispatchEvent(
new Event('input', {bubbles: true, composed: true})
);
await element.updateComplete;
assert.isTrue(element.hasStatusChange);
assert.isTrue(element.hasUnsavedChanges);
await element.save();
assert.isTrue(statusStub.called);
assert.isTrue(nameStub.called);
assert.equal(nameStub.lastCall.args[0], 'new name');
assert.equal(statusStub.lastCall.args[0], 'new status');
});
});
suite('set status but read name', () => {
let statusStub: SinonStubbedMember<RestApiService['setAccountStatus']>;
setup(async () => {
element.serverConfig = {
...createServerInfo(),
auth: {
...createAuth(),
editable_account_fields: [],
},
};
await element.updateComplete;
statusStub = stubRestApi('setAccountStatus').resolves();
});
test('read full name but set status', async () => {
const section = query<HTMLElement>(element, '#nameSection')!;
const displaySpan = section.querySelectorAll('.value')[0];
const inputSpan = section.querySelectorAll('.value')[1];
assert.isFalse(element.nameMutable);
assert.isFalse(element.hasUnsavedChanges);
assert.isFalse(displaySpan.hasAttribute('hidden'));
assert.equal(displaySpan.textContent, account.name);
assert.isUndefined(inputSpan);
const statusTextarea = queryAndAssert<GrAutogrowTextarea>(
element,
'#statusInput'
);
statusTextarea.value = 'new status';
statusTextarea.dispatchEvent(
new Event('input', {bubbles: true, composed: true})
);
await element.updateComplete;
assert.isTrue(element.hasStatusChange);
assert.isTrue(element.hasUnsavedChanges);
await element.save();
assert.isTrue(statusStub.called);
await statusStub.lastCall.returnValue;
assert.equal(statusStub.lastCall.args[0], 'new status');
});
});
test('_usernameChanged compares usernames with loose equality', async () => {
element.serverConfig = {
...createServerInfo(),
auth: {
...createAuth(),
editable_account_fields: [EditableAccountField.USER_NAME],
},
};
element.account = createAccountDetailWithId();
element.username = 't';
element.hasUsernameChange = false;
element.loading = false;
// usernameChanged is an observer, but call it here after setting
// hasUsernameChange in the test to force recomputation.
await element.updateComplete;
assert.isFalse(element.hasUsernameChange);
const inputEl = queryInput('#usernameInput');
inputEl.value = 'test';
inputEl.dispatchEvent(new Event('input', {bubbles: true, composed: true}));
await element.updateComplete;
assert.isTrue(element.hasUsernameChange);
});
});