blob: e781328d2438287ada40bd987e637985f7c2ef32 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import sinon from 'sinon';
import {assert} from 'chai';
import {prpcClient} from 'prpc-client-instance.js';
import {MrEditIssue, allowRemovedRestrictions} from './mr-edit-issue.js';
import {clientLoggerFake} from 'shared/test/fakes.js';
import {migratedTypes} from 'shared/issue-fields.js';
let element;
let clock;
describe('mr-edit-issue', () => {
beforeEach(() => {
element = document.createElement('mr-edit-issue');
document.body.appendChild(element);
sinon.stub(prpcClient, 'call');
element.clientLogger = clientLoggerFake();
clock = sinon.useFakeTimers();
});
afterEach(() => {
document.body.removeChild(element);
prpcClient.call.restore();
clock.restore();
});
it('initializes', () => {
assert.instanceOf(element, MrEditIssue);
});
it('scrolls into view on #makechanges hash', async () => {
await element.updateComplete;
const header = element.querySelector('#makechanges');
sinon.stub(header, 'scrollIntoView');
element.focusId = 'makechanges';
await element.updateComplete;
assert.isTrue(header.scrollIntoView.calledOnce);
header.scrollIntoView.restore();
});
it('shows snackbar and resets form when editing finishes', async () => {
sinon.stub(element, 'reset');
sinon.stub(element, '_showCommentAddedSnackbar');
element.updatingIssue = true;
await element.updateComplete;
sinon.assert.notCalled(element._showCommentAddedSnackbar);
sinon.assert.notCalled(element.reset);
element.updatingIssue = false;
await element.updateComplete;
sinon.assert.calledOnce(element._showCommentAddedSnackbar);
sinon.assert.calledOnce(element.reset);
});
it('does not show snackbar or reset form on edit error', async () => {
sinon.stub(element, 'reset');
sinon.stub(element, '_showCommentAddedSnackbar');
element.updatingIssue = true;
await element.updateComplete;
element.updateError = 'The save failed';
element.updatingIssue = false;
await element.updateComplete;
sinon.assert.notCalled(element._showCommentAddedSnackbar);
sinon.assert.notCalled(element.reset);
});
it('shows current status even if not defined for project', async () => {
await element.updateComplete;
const editMetadata = element.querySelector('mr-edit-metadata');
assert.deepEqual(editMetadata.statuses, []);
element.projectConfig = {statusDefs: [
{status: 'hello'},
{status: 'world'},
]};
await editMetadata.updateComplete;
assert.deepEqual(editMetadata.statuses, [
{status: 'hello'},
{status: 'world'},
]);
element.issue = {
statusRef: {status: 'hello'},
};
await editMetadata.updateComplete;
assert.deepEqual(editMetadata.statuses, [
{status: 'hello'},
{status: 'world'},
]);
element.issue = {
statusRef: {status: 'weirdStatus'},
};
await editMetadata.updateComplete;
assert.deepEqual(editMetadata.statuses, [
{status: 'weirdStatus'},
{status: 'hello'},
{status: 'world'},
]);
});
it('ignores deprecated statuses, unless used on current issue', async () => {
await element.updateComplete;
const editMetadata = element.querySelector('mr-edit-metadata');
assert.deepEqual(editMetadata.statuses, []);
element.projectConfig = {statusDefs: [
{status: 'new'},
{status: 'accepted', deprecated: false},
{status: 'compiling', deprecated: true},
]};
await editMetadata.updateComplete;
assert.deepEqual(editMetadata.statuses, [
{status: 'new'},
{status: 'accepted', deprecated: false},
]);
element.issue = {
statusRef: {status: 'compiling'},
};
await editMetadata.updateComplete;
assert.deepEqual(editMetadata.statuses, [
{status: 'compiling'},
{status: 'new'},
{status: 'accepted', deprecated: false},
]);
});
it('filter out empty or deleted user owners', () => {
assert.equal(
element._ownerDisplayName({displayName: 'a_deleted_user'}),
'');
assert.equal(
element._ownerDisplayName({
displayName: 'test@example.com',
userId: '1234',
}),
'test@example.com');
});
it('logs issue-update metrics', async () => {
await element.updateComplete;
const editMetadata = element.querySelector('mr-edit-metadata');
sinon.stub(editMetadata, 'delta').get(() => ({summary: 'test'}));
await element.save();
sinon.assert.calledOnce(element.clientLogger.logStart);
sinon.assert.calledWith(element.clientLogger.logStart,
'issue-update', 'computer-time');
// Simulate a response updating the UI.
element.issue = {summary: 'test'};
await element.updateComplete;
await element.updateComplete;
sinon.assert.calledOnce(element.clientLogger.logEnd);
sinon.assert.calledWith(element.clientLogger.logEnd,
'issue-update', 'computer-time', 120 * 1000);
});
it('presubmits issue on metadata change', async () => {
element.issueRef = {};
await element.updateComplete;
const editMetadata = element.querySelector('mr-edit-metadata');
editMetadata.dispatchEvent(new CustomEvent('change', {
detail: {
delta: {
summary: 'Summary',
},
},
}));
// Wait for debouncer.
clock.tick(element.presubmitDebounceTimeOut + 1);
sinon.assert.calledWith(prpcClient.call, 'monorail.Issues',
'PresubmitIssue',
{issueDelta: {summary: 'Summary'}, issueRef: {}});
});
it('presubmits issue on comment change', async () => {
element.issueRef = {};
await element.updateComplete;
const editMetadata = element.querySelector('mr-edit-metadata');
editMetadata.dispatchEvent(new CustomEvent('change', {
detail: {
delta: {},
commentContent: 'test',
},
}));
// Wait for debouncer.
clock.tick(element.presubmitDebounceTimeOut + 1);
sinon.assert.calledWith(prpcClient.call, 'monorail.Issues',
'PresubmitIssue',
{issueDelta: {}, issueRef: {}});
});
it('does not presubmit issue when no changes', () => {
element._presubmitIssue({});
sinon.assert.notCalled(prpcClient.call);
});
it('editing form runs _presubmitIssue debounced', async () => {
sinon.stub(element, '_presubmitIssue');
await element.updateComplete;
// User makes some changes.
const comment = element.querySelector('#commentText');
comment.value = 'Value';
comment.dispatchEvent(new Event('keyup'));
clock.tick(5);
// User makes more changes before debouncer timeout is done.
comment.value = 'more changes';
comment.dispatchEvent(new Event('keyup'));
clock.tick(10);
sinon.assert.notCalled(element._presubmitIssue);
// Wait for debouncer.
clock.tick(element.presubmitDebounceTimeOut + 1);
sinon.assert.calledOnce(element._presubmitIssue);
});
});
describe('allowRemovedRestrictions', () => {
beforeEach(() => {
sinon.stub(window, 'confirm');
});
afterEach(() => {
window.confirm.restore();
});
it('returns true if no restrictions removed', () => {
assert.isTrue(allowRemovedRestrictions([
{label: 'not-restricted'},
{label: 'fine'},
]));
});
it('returns false if restrictions removed and confirmation denied', () => {
window.confirm.returns(false);
assert.isFalse(allowRemovedRestrictions([
{label: 'not-restricted'},
{label: 'restrict-view-people'},
]));
});
it('returns true if restrictions removed and confirmation accepted', () => {
window.confirm.returns(true);
assert.isTrue(allowRemovedRestrictions([
{label: 'not-restricted'},
{label: 'restrict-view-people'},
]));
});
describe('migrated issue', () => {
it('does not show notice if issue not migrated', async () => {
element.migratedId = '';
await element.updateComplete;
assert.isNull(element.querySelector('.migrated-banner'));
assert.isNull(element.querySelector('.legacy-edit'));
});
it('shows notice if issue migrated', async () => {
element.migratedId = '1234';
element.migratedType = migratedTypes.LAUNCH_TYPE
await element.updateComplete;
assert.isNotNull(element.querySelector('.migrated-banner'));
assert.isNotNull(element.querySelector('.legacy-edit'));
});
it('shows buganizer link when migrated to buganizer', async () => {
element.migratedId = '1234';
element.migratedType = migratedTypes.BUGANIZER_TYPE
await element.updateComplete;
const link = element.querySelector('.migrated-banner a');
assert.include(link.textContent, 'b/1234');
});
it('shows launch banner when migrated to launch', async () => {
element.migratedId = '1234';
element.migratedType = migratedTypes.LAUNCH_TYPE
await element.updateComplete;
const link = element.querySelector('.migrated-banner');
assert.include(link.textContent, 'This issue has been migrated to Launch, see link in final comment below');
});
it('hides edit form if issue migrated', async () => {
element.migratedId = '1234';
element.migratedType = migratedTypes.LAUNCH_TYPE
await element.updateComplete;
const editForm = element.querySelector('mr-edit-metadata');
assert.isTrue(editForm.hasAttribute('hidden'));
});
it('unhides edit form on button click', async () => {
element.migratedId = '1234';
element.migratedType = migratedTypes.LAUNCH_TYPE
await element.updateComplete;
const button = element.querySelector('.legacy-edit');
button.click();
await element.updateComplete;
const editForm = element.querySelector('mr-edit-metadata');
assert.isFalse(editForm.hasAttribute('hidden'));
});
});
});