blob: 9884241d31f7c82a47ad20baf38a153b943379c5 [file] [log] [blame]
/**
* @license
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '../../../test/common-test-setup';
import './gr-confirm-rebase-dialog';
import {GrConfirmRebaseDialog} from './gr-confirm-rebase-dialog';
import {query, queryAndAssert} from '../../../test/test-utils';
import {
AccountId,
BranchName,
EmailAddress,
EmailInfo,
Timestamp,
} from '../../../types/common';
import {
createAccountWithEmail,
createChangeViewChange,
} from '../../../test/test-data-generators';
import {assert, fixture, html} from '@open-wc/testing';
import {GrDialog} from '../../shared/gr-dialog/gr-dialog';
import {testResolver} from '../../../test/common-test-setup';
import {userModelToken} from '../../../models/user/user-model';
import {changeModelToken} from '../../../models/change/change-model';
import {GrChangeAutocomplete} from '../gr-change-autocomplete/gr-change-autocomplete';
import {GrAutocomplete} from '../../shared/gr-autocomplete/gr-autocomplete';
import {LoadingStatus} from '../../../types/types';
import {GrAccountChip} from '../../shared/gr-account-chip/gr-account-chip';
import {
DropdownItem,
GrDropdownList,
} from '../../shared/gr-dropdown-list/gr-dropdown-list';
suite('gr-confirm-rebase-dialog tests', () => {
let element: GrConfirmRebaseDialog;
setup(async () => {
const userModel = testResolver(userModelToken);
userModel.setAccount({
...createAccountWithEmail('abc@def.com'),
registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
});
element = await fixture(
html`<gr-confirm-rebase-dialog></gr-confirm-rebase-dialog>`
);
});
test('render', async () => {
element.branch = 'test' as BranchName;
element.hasParent = false;
await element.updateComplete;
assert.shadowDom.equal(
element,
/* HTML */ `<gr-dialog
confirm-label="Rebase"
id="confirmDialog"
role="dialog"
>
<div class="header" slot="header">Confirm rebase</div>
<div class="main" slot="main">
<div class="loading rebaseOption" hidden="" id="rebaseOnParent">
<md-radio
id="rebaseOnParentInput"
name="rebaseOptions"
tabindex="0"
touch-target="wrapper"
>
</md-radio>
<label for="rebaseOnParentInput" id="rebaseOnParentLabel">
Rebase on parent change
</label>
</div>
<div class="message" hidden="">
Still loading parent information ...
</div>
<div class="message" hidden="" id="parentUpToDateMsg">
This change is up to date with its parent.
</div>
<div class="rebaseOption" hidden="" id="rebaseOnTip">
<md-radio
disabled=""
id="rebaseOnTipInput"
name="rebaseOptions"
tabindex="0"
touch-target="wrapper"
>
</md-radio>
<label for="rebaseOnTipInput" id="rebaseOnTipLabel">
Rebase on top of the test branch
<span hidden=""> (breaks relation chain) </span>
</label>
</div>
<div class="message" id="tipUpToDateMsg">
Change is up to date with the target branch already (test)
</div>
<div class="rebaseOption" id="rebaseOnOther">
<md-radio
id="rebaseOnOtherInput"
name="rebaseOptions"
tabindex="0"
touch-target="wrapper"
>
</md-radio>
<label for="rebaseOnOtherInput" id="rebaseOnOtherLabel">
Rebase on a specific change, ref, or commit
<span hidden=""> (breaks relation chain) </span>
</label>
</div>
<div class="parentRevisionContainer">
<gr-change-autocomplete> </gr-change-autocomplete>
</div>
<div class="rebaseCheckbox">
<md-checkbox id="rebaseAllowConflicts" touch-target="wrapper">
</md-checkbox>
<label for="rebaseAllowConflicts">
Allow rebase with conflicts
</label>
<gr-validation-options> </gr-validation-options>
</div>
</div>
</gr-dialog> `
);
});
suite('on behalf of uploader', () => {
let changeModel;
const change = {
...createChangeViewChange(),
};
setup(async () => {
element.branch = 'test' as BranchName;
await element.updateComplete;
changeModel = testResolver(changeModelToken);
changeModel.setState({
loadingStatus: LoadingStatus.LOADED,
change,
});
});
test('for reviewer it shows message about on behalf', () => {
const rebaseOnBehalfMsg = queryAndAssert(element, '.rebaseOnBehalfMsg');
assert.dom.equal(
rebaseOnBehalfMsg,
/* HTML */ `<div class="rebaseOnBehalfMsg">
Rebase will be done on behalf of the uploader:
<gr-account-chip> </gr-account-chip> <span> </span>
</div>`
);
const accountChip: GrAccountChip = queryAndAssert(
rebaseOnBehalfMsg,
'gr-account-chip'
);
assert.equal(
accountChip.account!,
change?.revisions[change.current_revision]?.uploader
);
});
test('allowConflicts', async () => {
element.allowConflicts = true;
await element.updateComplete;
const rebaseOnBehalfMsg = queryAndAssert(element, '.rebaseOnBehalfMsg');
assert.dom.equal(
rebaseOnBehalfMsg,
/* HTML */ `<div class="rebaseOnBehalfMsg">
Rebase will be done on behalf of
<gr-account-chip> </gr-account-chip> <span> </span>
</div>`
);
const accountChip: GrAccountChip = queryAndAssert(
rebaseOnBehalfMsg,
'gr-account-chip'
);
assert.equal(accountChip.account, element.account);
});
});
suite('rebase with committer email', () => {
setup(async () => {
element.branch = 'test' as BranchName;
await element.updateComplete;
});
test('hide rebaseWithCommitterEmail dialog when committer has single email', async () => {
element.committerEmailDropdownItems = [
{
email: 'test1@example.com' as EmailAddress,
preferred: true,
pending_confirmation: true,
},
];
await element.updateComplete;
assert.isNotOk(query(element, '.rebaseWithCommitterEmail'));
});
test('show rebaseWithCommitterEmail dialog when committer has more than one email', async () => {
element.committerEmailDropdownItems = [
{
email: 'test1@example.com' as EmailAddress,
preferred: true,
pending_confirmation: true,
},
{
email: 'test2@example.com' as EmailAddress,
pending_confirmation: true,
},
];
await element.updateComplete;
const committerEmail = queryAndAssert(
element,
'.rebaseWithCommitterEmail'
);
assert.dom.equal(
committerEmail,
/* HTML */ `<div class="rebaseWithCommitterEmail"
>Rebase with committer email
<gr-dropdown-list>
</gr-dropdown-list>
<span></div>`
);
const dropdownList: GrDropdownList = queryAndAssert(
committerEmail,
'gr-dropdown-list'
);
assert.strictEqual(dropdownList.items!.length, 2);
});
test('hide rebaseWithCommitterEmail dialog when RebaseChain is set', async () => {
element.shouldRebaseChain = true;
await element.updateComplete;
assert.isNotOk(query(element, '.rebaseWithCommitterEmail'));
});
test('show current user emails in the dropdown list when rebase with conflicts is allowed', async () => {
element.allowConflicts = true;
element.latestCommitter = {
email: 'commit@example.com' as EmailAddress,
name: 'committer',
date: '2023-06-12 18:32:08.000000000' as Timestamp,
};
element.committerEmailDropdownItems = [
{
email: 'currentuser1@example.com' as EmailAddress,
preferred: true,
pending_confirmation: true,
},
{
email: 'currentuser2@example.com' as EmailAddress,
pending_confirmation: true,
},
];
await element.updateComplete;
const committerEmail = queryAndAssert(
element,
'.rebaseWithCommitterEmail'
);
const dropdownList: GrDropdownList = queryAndAssert(
committerEmail,
'gr-dropdown-list'
);
assert.deepStrictEqual(
dropdownList.items!.map((e: DropdownItem) => e.value),
element.committerEmailDropdownItems.map((e: EmailInfo) => e.email)
);
});
test('show uploader emails in the dropdown list when rebase with conflicts is not allowed', async () => {
element.allowConflicts = false;
element.uploader = {_account_id: 2 as AccountId, name: '2'};
element.latestCommitter = {
email: 'commit@example.com' as EmailAddress,
name: 'committer',
date: '2023-06-12 18:32:08.000000000' as Timestamp,
};
element.committerEmailDropdownItems = [
{
email: 'uploader1@example.com' as EmailAddress,
preferred: true,
pending_confirmation: true,
},
{
email: 'uploader2@example.com' as EmailAddress,
preferred: false,
pending_confirmation: true,
},
];
await element.updateComplete;
const committerEmail = queryAndAssert(
element,
'.rebaseWithCommitterEmail'
);
const dropdownList: GrDropdownList = queryAndAssert(
committerEmail,
'gr-dropdown-list'
);
assert.deepStrictEqual(
dropdownList.items!.map((e: DropdownItem) => e.value),
element.committerEmailDropdownItems.map((e: EmailInfo) => e.email)
);
});
});
test('disableActions property disables dialog confirm', async () => {
element.disableActions = false;
await element.updateComplete;
const dialog = queryAndAssert<GrDialog>(element, 'gr-dialog');
assert.isFalse(dialog.disabled);
element.disableActions = true;
await element.updateComplete;
assert.isTrue(dialog.disabled);
});
test('controls with parent and rebase on current available', async () => {
element.rebaseOnCurrent = true;
element.hasParent = true;
await element.updateComplete;
assert.isTrue(
queryAndAssert<HTMLInputElement>(element, '#rebaseOnParentInput').checked
);
assert.isFalse(
queryAndAssert(element, '#rebaseOnParent').hasAttribute('hidden')
);
assert.isTrue(
queryAndAssert(element, '#parentUpToDateMsg').hasAttribute('hidden')
);
assert.isFalse(
queryAndAssert(element, '#rebaseOnTip').hasAttribute('hidden')
);
assert.isTrue(
queryAndAssert(element, '#tipUpToDateMsg').hasAttribute('hidden')
);
});
test('controls with parent rebase on current not available', async () => {
element.rebaseOnCurrent = false;
element.hasParent = true;
await element.updateComplete;
assert.isTrue(
queryAndAssert<HTMLInputElement>(element, '#rebaseOnTipInput').checked
);
assert.isTrue(
queryAndAssert(element, '#rebaseOnParent').hasAttribute('hidden')
);
assert.isFalse(
queryAndAssert(element, '#parentUpToDateMsg').hasAttribute('hidden')
);
assert.isFalse(
queryAndAssert(element, '#rebaseOnTip').hasAttribute('hidden')
);
assert.isTrue(
queryAndAssert(element, '#tipUpToDateMsg').hasAttribute('hidden')
);
});
test('controls without parent and rebase on current available', async () => {
element.rebaseOnCurrent = true;
element.hasParent = false;
await element.updateComplete;
assert.isTrue(
queryAndAssert<HTMLInputElement>(element, '#rebaseOnTipInput').checked
);
assert.isTrue(
queryAndAssert(element, '#rebaseOnParent').hasAttribute('hidden')
);
assert.isTrue(
queryAndAssert(element, '#parentUpToDateMsg').hasAttribute('hidden')
);
assert.isFalse(
queryAndAssert(element, '#rebaseOnTip').hasAttribute('hidden')
);
assert.isTrue(
queryAndAssert(element, '#tipUpToDateMsg').hasAttribute('hidden')
);
});
test('controls without parent rebase on current not available', async () => {
element.rebaseOnCurrent = false;
element.hasParent = false;
await element.updateComplete;
assert.isTrue(element.rebaseOnOtherInput?.checked);
assert.isTrue(
queryAndAssert(element, '#rebaseOnParent').hasAttribute('hidden')
);
assert.isTrue(
queryAndAssert(element, '#parentUpToDateMsg').hasAttribute('hidden')
);
assert.isTrue(
queryAndAssert(element, '#rebaseOnTip').hasAttribute('hidden')
);
assert.isFalse(
queryAndAssert(element, '#tipUpToDateMsg').hasAttribute('hidden')
);
});
test('committer email is sent when chain is not rebased', async () => {
const fireStub = sinon.stub(element, 'dispatchEvent');
element.text = '123';
element.selectedEmailForRebase = 'abc@def.com';
await element.updateComplete;
queryAndAssert(element, '#confirmDialog').dispatchEvent(
new CustomEvent('confirm', {
composed: true,
bubbles: true,
})
);
assert.deepEqual((fireStub.lastCall.args[0] as CustomEvent).detail, {
allowConflicts: false,
base: '123',
rebaseChain: false,
onBehalfOfUploader: true,
committerEmail: 'abc@def.com',
});
});
test('committer email is not sent when chain is rebased', async () => {
const fireStub = sinon.stub(element, 'dispatchEvent');
element.text = '123';
element.selectedEmailForRebase = 'abc@def.com';
element.hasParent = true;
element.shouldRebaseChain = true;
await element.updateComplete;
queryAndAssert<HTMLInputElement>(element, '#rebaseChain').checked = true;
queryAndAssert(element, '#confirmDialog').dispatchEvent(
new CustomEvent('confirm', {
composed: true,
bubbles: true,
})
);
assert.deepEqual((fireStub.lastCall.args[0] as CustomEvent).detail, {
allowConflicts: false,
base: '123',
rebaseChain: true,
onBehalfOfUploader: true,
committerEmail: null,
});
});
test('input cleared on cancel or submit', async () => {
element.text = '123';
await element.updateComplete;
queryAndAssert(element, '#confirmDialog').dispatchEvent(
new CustomEvent('confirm', {
composed: true,
bubbles: true,
})
);
assert.equal(element.text, '');
element.text = '123';
await element.updateComplete;
queryAndAssert(element, '#confirmDialog').dispatchEvent(
new CustomEvent('cancel', {
composed: true,
bubbles: true,
})
);
assert.equal(element.text, '');
});
test('getSelectedBase', async () => {
const autocomplete = queryAndAssert<GrChangeAutocomplete>(
element,
'gr-change-autocomplete'
);
const innerAutocomplete = queryAndAssert<GrAutocomplete>(
autocomplete,
'gr-autocomplete'
);
innerAutocomplete.dispatchEvent(
new CustomEvent('text-changed', {detail: {value: '5fab321c'}})
);
await innerAutocomplete.updateComplete;
await element.updateComplete;
queryAndAssert<HTMLInputElement>(element, '#rebaseOnParentInput').checked =
true;
assert.equal(element.getSelectedBase(), null);
queryAndAssert<HTMLInputElement>(element, '#rebaseOnParentInput').checked =
false;
queryAndAssert<HTMLInputElement>(element, '#rebaseOnTipInput').checked =
true;
assert.equal(element.getSelectedBase(), '');
queryAndAssert<HTMLInputElement>(element, '#rebaseOnTipInput').checked =
false;
assert.equal(element.getSelectedBase(), '5fab321c');
});
});