Do not reload page after clicking Cancel in apply-fix dialog
Change-Id: I5830caac655df9d3dc7058dc9674ba727685012b (cherry picked from commit 131bf7a080f0f2772a846591979b7d3dcab964bf)
This commit is contained in:
parent
5c0c08071e
commit
b2d7c2aadb
|
@ -155,6 +155,7 @@ import {
|
|||
ThreadListModifiedEvent,
|
||||
TabState,
|
||||
EventType,
|
||||
CloseFixPreviewEvent,
|
||||
} from '../../../types/events';
|
||||
import {GrButton} from '../../shared/gr-button/gr-button';
|
||||
import {GrMessagesList} from '../gr-messages-list/gr-messages-list';
|
||||
|
@ -676,7 +677,7 @@ export class GrChangeView extends KeyboardShortcutMixin(PolymerElement) {
|
|||
this._handleCommitMessageCancel()
|
||||
);
|
||||
this.addEventListener('open-fix-preview', e => this._onOpenFixPreview(e));
|
||||
this.addEventListener('close-fix-preview', () => this._onCloseFixPreview());
|
||||
this.addEventListener('close-fix-preview', e => this._onCloseFixPreview(e));
|
||||
window.addEventListener('scroll', this.handleScroll);
|
||||
document.addEventListener('visibilitychange', this.handleVisibilityChange);
|
||||
|
||||
|
@ -742,8 +743,8 @@ export class GrChangeView extends KeyboardShortcutMixin(PolymerElement) {
|
|||
this.$.applyFixDialog.open(e);
|
||||
}
|
||||
|
||||
_onCloseFixPreview() {
|
||||
this._reload();
|
||||
_onCloseFixPreview(e: CloseFixPreviewEvent) {
|
||||
if (e.detail.fixApplied) this._reload();
|
||||
}
|
||||
|
||||
_handleToggleDiffMode(e: CustomKeyboardEvent) {
|
||||
|
|
|
@ -37,12 +37,14 @@ import {GrOverlay} from '../../shared/gr-overlay/gr-overlay';
|
|||
import {isRobot} from '../../../utils/comment-util';
|
||||
import {OpenFixPreviewEvent} from '../../../types/events';
|
||||
import {appContext} from '../../../services/app-context';
|
||||
import {fireEvent} from '../../../utils/event-util';
|
||||
import {fireCloseFixPreview, fireEvent} from '../../../utils/event-util';
|
||||
import {ParsedChangeInfo} from '../../../types/types';
|
||||
import {GrButton} from '../../shared/gr-button/gr-button';
|
||||
|
||||
export interface GrApplyFixDialog {
|
||||
$: {
|
||||
applyFixOverlay: GrOverlay;
|
||||
nextFix: GrButton;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -168,7 +170,7 @@ export class GrApplyFixDialog extends PolymerElement {
|
|||
}
|
||||
})
|
||||
.catch(err => {
|
||||
this._close();
|
||||
this._close(false);
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
@ -186,7 +188,7 @@ export class GrApplyFixDialog extends PolymerElement {
|
|||
if (e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
this._close();
|
||||
this._close(false);
|
||||
}
|
||||
|
||||
addOneTo(_selectedFixIdx: number) {
|
||||
|
@ -225,12 +227,12 @@ export class GrApplyFixDialog extends PolymerElement {
|
|||
return _selectedFixIdx === fixSuggestions.length - 1;
|
||||
}
|
||||
|
||||
_close() {
|
||||
_close(fixApplied: boolean) {
|
||||
this._currentFix = undefined;
|
||||
this._currentPreviews = [];
|
||||
this._isApplyFixLoading = false;
|
||||
|
||||
fireEvent(this, 'close-fix-preview');
|
||||
fireCloseFixPreview(this, fixApplied);
|
||||
this.$.applyFixOverlay.close();
|
||||
}
|
||||
|
||||
|
@ -282,7 +284,7 @@ export class GrApplyFixDialog extends PolymerElement {
|
|||
EditPatchSetNum,
|
||||
patchNum as BasePatchSetNum
|
||||
);
|
||||
this._close();
|
||||
this._close(true);
|
||||
}
|
||||
this._isApplyFixLoading = false;
|
||||
});
|
||||
|
|
|
@ -1,272 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import '../../../test/common-test-setup-karma.js';
|
||||
import './gr-apply-fix-dialog.js';
|
||||
import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
|
||||
import {stubRestApi} from '../../../test/test-utils.js';
|
||||
|
||||
const basicFixture = fixtureFromElement('gr-apply-fix-dialog');
|
||||
|
||||
suite('gr-apply-fix-dialog tests', () => {
|
||||
let element;
|
||||
|
||||
const ROBOT_COMMENT_WITH_TWO_FIXES = {
|
||||
robot_id: 'robot_1',
|
||||
fix_suggestions: [{fix_id: 'fix_1'}, {fix_id: 'fix_2'}],
|
||||
};
|
||||
|
||||
const ROBOT_COMMENT_WITH_ONE_FIX = {
|
||||
robot_id: 'robot_1',
|
||||
fix_suggestions: [{fix_id: 'fix_1'}],
|
||||
};
|
||||
|
||||
setup(() => {
|
||||
element = basicFixture.instantiate();
|
||||
element.changeNum = '1';
|
||||
element._patchNum = 2;
|
||||
element.change = {
|
||||
_number: '1',
|
||||
project: 'project',
|
||||
revisions: {
|
||||
abcd: {_number: 1},
|
||||
efgh: {_number: 2},
|
||||
},
|
||||
current_revision: 'efgh',
|
||||
};
|
||||
element.prefs = {
|
||||
font_size: 12,
|
||||
line_length: 100,
|
||||
tab_size: 4,
|
||||
};
|
||||
});
|
||||
|
||||
suite('dialog open', () => {
|
||||
setup(() => {
|
||||
stubRestApi('getRobotCommentFixPreview')
|
||||
.returns(Promise.resolve({
|
||||
f1: {
|
||||
meta_a: {},
|
||||
meta_b: {},
|
||||
content: [
|
||||
{
|
||||
ab: ['loqlwkqll'],
|
||||
},
|
||||
{
|
||||
b: ['qwqqsqw'],
|
||||
},
|
||||
{
|
||||
ab: ['qwqqsqw', 'qweqeqweqeq', 'qweqweq'],
|
||||
},
|
||||
],
|
||||
},
|
||||
f2: {
|
||||
meta_a: {},
|
||||
meta_b: {},
|
||||
content: [
|
||||
{
|
||||
ab: ['eqweqweqwex'],
|
||||
},
|
||||
{
|
||||
b: ['zassdasd'],
|
||||
},
|
||||
{
|
||||
ab: ['zassdasd', 'dasdasda', 'asdasdad'],
|
||||
},
|
||||
],
|
||||
},
|
||||
}));
|
||||
sinon.stub(element.$.applyFixOverlay, 'open')
|
||||
.returns(Promise.resolve());
|
||||
});
|
||||
|
||||
test('dialog opens fetch and sets previews', done => {
|
||||
element.open({detail: {patchNum: 2,
|
||||
comment: ROBOT_COMMENT_WITH_TWO_FIXES}})
|
||||
.then(() => {
|
||||
assert.equal(element._currentFix.fix_id, 'fix_1');
|
||||
assert.equal(element._currentPreviews.length, 2);
|
||||
assert.equal(element._robotId, 'robot_1');
|
||||
const button = element.shadowRoot.querySelector(
|
||||
'#applyFixDialog').shadowRoot.querySelector('#confirm');
|
||||
assert.isFalse(button.hasAttribute('disabled'));
|
||||
assert.equal(button.getAttribute('title'), '');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('tooltip is hidden if apply fix is loading', done => {
|
||||
element.open({detail: {patchNum: 2,
|
||||
comment: ROBOT_COMMENT_WITH_TWO_FIXES}})
|
||||
.then(() => {
|
||||
element._isApplyFixLoading = true;
|
||||
const button = element.shadowRoot.querySelector(
|
||||
'#applyFixDialog').shadowRoot.querySelector('#confirm');
|
||||
assert.isTrue(button.hasAttribute('disabled'));
|
||||
assert.equal(button.getAttribute('title'), '');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('apply fix button is disabled on older patchset', done => {
|
||||
element.change = {
|
||||
_number: '1',
|
||||
project: 'project',
|
||||
revisions: {
|
||||
abcd: {_number: 1},
|
||||
efgh: {_number: 2},
|
||||
},
|
||||
current_revision: 'abcd',
|
||||
};
|
||||
element.open({detail: {patchNum: 2,
|
||||
comment: ROBOT_COMMENT_WITH_ONE_FIX}})
|
||||
.then(() => {
|
||||
flush(() => {
|
||||
const button = element.shadowRoot.querySelector(
|
||||
'#applyFixDialog').shadowRoot.querySelector('#confirm');
|
||||
assert.isTrue(button.hasAttribute('disabled'));
|
||||
assert.equal(button.getAttribute('title'),
|
||||
'Fix can only be applied to the latest patchset');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('next button state updated when suggestions changed', done => {
|
||||
stubRestApi('getRobotCommentFixPreview').returns(Promise.resolve({}));
|
||||
sinon.stub(element.$.applyFixOverlay, 'open').returns(Promise.resolve());
|
||||
|
||||
element.open({detail: {patchNum: 2, comment: ROBOT_COMMENT_WITH_ONE_FIX}})
|
||||
.then(() => assert.isTrue(element.$.nextFix.disabled))
|
||||
.then(() =>
|
||||
element.open({detail: {patchNum: 2,
|
||||
comment: ROBOT_COMMENT_WITH_TWO_FIXES}}))
|
||||
.then(() => {
|
||||
assert.isFalse(element.$.nextFix.disabled);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('preview endpoint throws error should reset dialog', async () => {
|
||||
stubRestApi('getRobotCommentFixPreview').returns(
|
||||
Promise.reject(new Error('backend error')));
|
||||
element.open({detail: {patchNum: 2,
|
||||
comment: ROBOT_COMMENT_WITH_TWO_FIXES}});
|
||||
await flush();
|
||||
assert.equal(element._currentFix, undefined);
|
||||
});
|
||||
|
||||
test('apply fix button should call apply ' +
|
||||
'and navigate to change view', () => {
|
||||
const stub = stubRestApi('applyFixSuggestion').returns(
|
||||
Promise.resolve({ok: true}));
|
||||
sinon.stub(GerritNav, 'navigateToChange');
|
||||
element._currentFix = {fix_id: '123'};
|
||||
|
||||
return element._handleApplyFix().then(() => {
|
||||
assert.isTrue(stub.calledWithExactly('1', 2, '123'));
|
||||
assert.isTrue(GerritNav.navigateToChange.calledWithExactly({
|
||||
_number: '1',
|
||||
project: 'project',
|
||||
revisions: {
|
||||
abcd: {_number: 1},
|
||||
efgh: {_number: 2},
|
||||
},
|
||||
current_revision: 'efgh',
|
||||
}, 'edit', 2));
|
||||
|
||||
// reset gr-apply-fix-dialog and close
|
||||
assert.equal(element._currentFix, undefined);
|
||||
assert.equal(element._currentPreviews.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
test('should not navigate to change view if incorect reponse', done => {
|
||||
const stub = stubRestApi('applyFixSuggestion').returns(Promise.resolve({}));
|
||||
sinon.stub(GerritNav, 'navigateToChange');
|
||||
element._currentFix = {fix_id: '123'};
|
||||
|
||||
element._handleApplyFix().then(() => {
|
||||
assert.isTrue(stub.calledWithExactly('1', 2, '123'));
|
||||
assert.isTrue(GerritNav.navigateToChange.notCalled);
|
||||
|
||||
assert.equal(element._isApplyFixLoading, false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('select fix forward and back of multiple suggested fixes', done => {
|
||||
stubRestApi('getRobotCommentFixPreview')
|
||||
.returns(Promise.resolve({
|
||||
f1: {
|
||||
meta_a: {},
|
||||
meta_b: {},
|
||||
content: [
|
||||
{
|
||||
ab: ['loqlwkqll'],
|
||||
},
|
||||
{
|
||||
b: ['qwqqsqw'],
|
||||
},
|
||||
{
|
||||
ab: ['qwqqsqw', 'qweqeqweqeq', 'qweqweq'],
|
||||
},
|
||||
],
|
||||
},
|
||||
f2: {
|
||||
meta_a: {},
|
||||
meta_b: {},
|
||||
content: [
|
||||
{
|
||||
ab: ['eqweqweqwex'],
|
||||
},
|
||||
{
|
||||
b: ['zassdasd'],
|
||||
},
|
||||
{
|
||||
ab: ['zassdasd', 'dasdasda', 'asdasdad'],
|
||||
},
|
||||
],
|
||||
},
|
||||
}));
|
||||
sinon.stub(element.$.applyFixOverlay, 'open').returns(Promise.resolve());
|
||||
|
||||
element.open({detail: {patchNum: 2, comment: ROBOT_COMMENT_WITH_TWO_FIXES}})
|
||||
.then(() => {
|
||||
element._onNextFixClick();
|
||||
assert.equal(element._currentFix.fix_id, 'fix_2');
|
||||
element._onPrevFixClick();
|
||||
assert.equal(element._currentFix.fix_id, 'fix_1');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('server-error should throw for failed apply call', async () => {
|
||||
stubRestApi('applyFixSuggestion').returns(
|
||||
Promise.reject(new Error('backend error')));
|
||||
sinon.stub(GerritNav, 'navigateToChange');
|
||||
element._currentFix = {fix_id: '123'};
|
||||
let expectedError;
|
||||
await element._handleApplyFix().catch(e => {
|
||||
expectedError = e;
|
||||
});
|
||||
assert.isOk(expectedError);
|
||||
assert.isFalse(GerritNav.navigateToChange.called);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,376 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import '../../../test/common-test-setup-karma';
|
||||
import './gr-apply-fix-dialog';
|
||||
import {GerritNav} from '../../core/gr-navigation/gr-navigation';
|
||||
import {queryAndAssert, stubRestApi} from '../../../test/test-utils';
|
||||
import {GrApplyFixDialog} from './gr-apply-fix-dialog';
|
||||
import {
|
||||
BasePatchSetNum,
|
||||
EditPatchSetNum,
|
||||
PatchSetNum,
|
||||
RobotId,
|
||||
RobotRunId,
|
||||
Timestamp,
|
||||
UrlEncodedCommentId,
|
||||
} from '../../../types/common';
|
||||
import {
|
||||
createFixSuggestionInfo,
|
||||
createParsedChange,
|
||||
createRevisions,
|
||||
getCurrentRevision,
|
||||
} from '../../../test/test-data-generators';
|
||||
import {createDefaultDiffPrefs} from '../../../constants/constants';
|
||||
import {DiffInfo} from '../../../types/diff';
|
||||
import {UIRobot} from '../../../utils/comment-util';
|
||||
import {
|
||||
CloseFixPreviewEventDetail,
|
||||
EventType,
|
||||
OpenFixPreviewEventDetail,
|
||||
} from '../../../types/events';
|
||||
import {GrButton} from '../../shared/gr-button/gr-button';
|
||||
|
||||
const basicFixture = fixtureFromElement('gr-apply-fix-dialog');
|
||||
|
||||
suite('gr-apply-fix-dialog tests', () => {
|
||||
let element: GrApplyFixDialog;
|
||||
|
||||
const ROBOT_COMMENT_WITH_TWO_FIXES: UIRobot = {
|
||||
id: '1' as UrlEncodedCommentId,
|
||||
updated: '2018-02-08 18:49:18.000000000' as Timestamp,
|
||||
robot_id: 'robot_1' as RobotId,
|
||||
robot_run_id: 'run_1' as RobotRunId,
|
||||
properties: {},
|
||||
fix_suggestions: [
|
||||
createFixSuggestionInfo('fix_1'),
|
||||
createFixSuggestionInfo('fix_2'),
|
||||
],
|
||||
};
|
||||
|
||||
const ROBOT_COMMENT_WITH_ONE_FIX: UIRobot = {
|
||||
id: '2' as UrlEncodedCommentId,
|
||||
updated: '2018-02-08 18:49:18.000000000' as Timestamp,
|
||||
robot_id: 'robot_1' as RobotId,
|
||||
robot_run_id: 'run_1' as RobotRunId,
|
||||
properties: {},
|
||||
fix_suggestions: [createFixSuggestionInfo('fix_1')],
|
||||
};
|
||||
|
||||
function getConfirmButton(): GrButton {
|
||||
return queryAndAssert(
|
||||
queryAndAssert(element, '#applyFixDialog'),
|
||||
'#confirm'
|
||||
);
|
||||
}
|
||||
|
||||
setup(() => {
|
||||
element = basicFixture.instantiate();
|
||||
const change = {
|
||||
...createParsedChange(),
|
||||
revisions: createRevisions(2),
|
||||
current_revision: getCurrentRevision(1),
|
||||
};
|
||||
element.changeNum = change._number;
|
||||
element._patchNum = change.revisions[change.current_revision]._number;
|
||||
element.change = change;
|
||||
element.prefs = {
|
||||
...createDefaultDiffPrefs(),
|
||||
font_size: 12,
|
||||
line_length: 100,
|
||||
tab_size: 4,
|
||||
};
|
||||
});
|
||||
|
||||
suite('dialog open', () => {
|
||||
setup(() => {
|
||||
const diffInfo1: DiffInfo = {
|
||||
meta_a: {
|
||||
name: 'f1',
|
||||
content_type: 'text',
|
||||
lines: 10,
|
||||
},
|
||||
meta_b: {
|
||||
name: 'f1',
|
||||
content_type: 'text',
|
||||
lines: 12,
|
||||
},
|
||||
content: [
|
||||
{
|
||||
ab: ['loqlwkqll'],
|
||||
},
|
||||
{
|
||||
b: ['qwqqsqw'],
|
||||
},
|
||||
{
|
||||
ab: ['qwqqsqw', 'qweqeqweqeq', 'qweqweq'],
|
||||
},
|
||||
],
|
||||
change_type: 'MODIFIED',
|
||||
intraline_status: 'OK',
|
||||
};
|
||||
|
||||
const diffInfo2: DiffInfo = {
|
||||
meta_a: {
|
||||
name: 'f2',
|
||||
content_type: 'text',
|
||||
lines: 10,
|
||||
},
|
||||
meta_b: {
|
||||
name: 'f2',
|
||||
content_type: 'text',
|
||||
lines: 12,
|
||||
},
|
||||
content: [
|
||||
{
|
||||
ab: ['eqweqweqwex'],
|
||||
},
|
||||
{
|
||||
b: ['zassdasd'],
|
||||
},
|
||||
{
|
||||
ab: ['zassdasd', 'dasdasda', 'asdasdad'],
|
||||
},
|
||||
],
|
||||
change_type: 'MODIFIED',
|
||||
intraline_status: 'OK',
|
||||
};
|
||||
|
||||
stubRestApi('getRobotCommentFixPreview').returns(
|
||||
Promise.resolve({
|
||||
f1: diffInfo1,
|
||||
f2: diffInfo2,
|
||||
})
|
||||
);
|
||||
sinon.stub(element.$.applyFixOverlay, 'open').returns(Promise.resolve());
|
||||
});
|
||||
|
||||
test('dialog opens fetch and sets previews', async () => {
|
||||
await element.open(
|
||||
new CustomEvent<OpenFixPreviewEventDetail>(EventType.OPEN_FIX_PREVIEW, {
|
||||
detail: {
|
||||
patchNum: 2 as PatchSetNum,
|
||||
comment: ROBOT_COMMENT_WITH_TWO_FIXES,
|
||||
},
|
||||
})
|
||||
);
|
||||
assert.equal(element._currentFix!.fix_id, 'fix_1');
|
||||
assert.equal(element._currentPreviews.length, 2);
|
||||
assert.equal(element._robotId, 'robot_1' as RobotId);
|
||||
const button = getConfirmButton();
|
||||
assert.isFalse(button.hasAttribute('disabled'));
|
||||
assert.equal(button.getAttribute('title'), '');
|
||||
});
|
||||
|
||||
test('tooltip is hidden if apply fix is loading', async () => {
|
||||
await element.open(
|
||||
new CustomEvent<OpenFixPreviewEventDetail>(EventType.OPEN_FIX_PREVIEW, {
|
||||
detail: {
|
||||
patchNum: 2 as PatchSetNum,
|
||||
comment: ROBOT_COMMENT_WITH_TWO_FIXES,
|
||||
},
|
||||
})
|
||||
);
|
||||
element._isApplyFixLoading = true;
|
||||
const button = getConfirmButton();
|
||||
assert.isTrue(button.hasAttribute('disabled'));
|
||||
assert.equal(button.getAttribute('title'), '');
|
||||
});
|
||||
|
||||
test('apply fix button is disabled on older patchset', async () => {
|
||||
element.change = element.change = {
|
||||
...createParsedChange(),
|
||||
revisions: createRevisions(2),
|
||||
current_revision: getCurrentRevision(0),
|
||||
};
|
||||
await element.open(
|
||||
new CustomEvent<OpenFixPreviewEventDetail>(EventType.OPEN_FIX_PREVIEW, {
|
||||
detail: {
|
||||
patchNum: 2 as PatchSetNum,
|
||||
comment: ROBOT_COMMENT_WITH_ONE_FIX,
|
||||
},
|
||||
})
|
||||
);
|
||||
await flush();
|
||||
const button = getConfirmButton();
|
||||
assert.isTrue(button.hasAttribute('disabled'));
|
||||
assert.equal(
|
||||
button.getAttribute('title'),
|
||||
'Fix can only be applied to the latest patchset'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('next button state updated when suggestions changed', async () => {
|
||||
stubRestApi('getRobotCommentFixPreview').returns(Promise.resolve({}));
|
||||
sinon.stub(element.$.applyFixOverlay, 'open').returns(Promise.resolve());
|
||||
|
||||
await element.open(
|
||||
new CustomEvent<OpenFixPreviewEventDetail>(EventType.OPEN_FIX_PREVIEW, {
|
||||
detail: {
|
||||
patchNum: 2 as PatchSetNum,
|
||||
comment: ROBOT_COMMENT_WITH_ONE_FIX,
|
||||
},
|
||||
})
|
||||
);
|
||||
assert.isTrue(element.$.nextFix.disabled);
|
||||
await element.open(
|
||||
new CustomEvent<OpenFixPreviewEventDetail>(EventType.OPEN_FIX_PREVIEW, {
|
||||
detail: {
|
||||
patchNum: 2 as PatchSetNum,
|
||||
comment: ROBOT_COMMENT_WITH_TWO_FIXES,
|
||||
},
|
||||
})
|
||||
);
|
||||
assert.isFalse(element.$.nextFix.disabled);
|
||||
});
|
||||
|
||||
test('preview endpoint throws error should reset dialog', async () => {
|
||||
stubRestApi('getRobotCommentFixPreview').returns(
|
||||
Promise.reject(new Error('backend error'))
|
||||
);
|
||||
element.open(
|
||||
new CustomEvent<OpenFixPreviewEventDetail>(EventType.OPEN_FIX_PREVIEW, {
|
||||
detail: {
|
||||
patchNum: 2 as PatchSetNum,
|
||||
comment: ROBOT_COMMENT_WITH_TWO_FIXES,
|
||||
},
|
||||
})
|
||||
);
|
||||
await flush();
|
||||
assert.equal(element._currentFix, undefined);
|
||||
});
|
||||
|
||||
test('apply fix button should call apply, navigate to change view and fire close', async () => {
|
||||
const applyFixSuggestionStub = stubRestApi('applyFixSuggestion').returns(
|
||||
Promise.resolve(new Response(null, {status: 200}))
|
||||
);
|
||||
const navigateToChangeStub = sinon.stub(GerritNav, 'navigateToChange');
|
||||
element._currentFix = createFixSuggestionInfo('123');
|
||||
|
||||
const closeFixPreviewEventSpy = sinon.spy();
|
||||
// Element is recreated after each test, removeEventListener isn't required
|
||||
element.addEventListener(
|
||||
EventType.CLOSE_FIX_PREVIEW,
|
||||
closeFixPreviewEventSpy
|
||||
);
|
||||
await element._handleApplyFix(new CustomEvent('confirm'));
|
||||
|
||||
sinon.assert.calledOnceWithExactly(
|
||||
applyFixSuggestionStub,
|
||||
element.change!._number,
|
||||
2 as PatchSetNum,
|
||||
'123'
|
||||
);
|
||||
sinon.assert.calledWithExactly(
|
||||
navigateToChangeStub,
|
||||
element.change!,
|
||||
EditPatchSetNum,
|
||||
element.change!.revisions[2]._number as BasePatchSetNum
|
||||
);
|
||||
|
||||
sinon.assert.calledOnceWithExactly(
|
||||
closeFixPreviewEventSpy,
|
||||
new CustomEvent<CloseFixPreviewEventDetail>(EventType.CLOSE_FIX_PREVIEW, {
|
||||
detail: {
|
||||
fixApplied: true,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
// reset gr-apply-fix-dialog and close
|
||||
assert.equal(element._currentFix, undefined);
|
||||
assert.equal(element._currentPreviews.length, 0);
|
||||
});
|
||||
|
||||
test('should not navigate to change view if incorect reponse', async () => {
|
||||
const applyFixSuggestionStub = stubRestApi('applyFixSuggestion').returns(
|
||||
Promise.resolve(new Response(null, {status: 500}))
|
||||
);
|
||||
const navigateToChangeStub = sinon.stub(GerritNav, 'navigateToChange');
|
||||
element._currentFix = createFixSuggestionInfo('fix_123');
|
||||
|
||||
await element._handleApplyFix(new CustomEvent('confirm'));
|
||||
sinon.assert.calledWithExactly(
|
||||
applyFixSuggestionStub,
|
||||
element.change!._number,
|
||||
2 as PatchSetNum,
|
||||
'fix_123'
|
||||
);
|
||||
assert.isTrue(navigateToChangeStub.notCalled);
|
||||
|
||||
assert.equal(element._isApplyFixLoading, false);
|
||||
});
|
||||
|
||||
test('select fix forward and back of multiple suggested fixes', async () => {
|
||||
sinon.stub(element.$.applyFixOverlay, 'open').returns(Promise.resolve());
|
||||
|
||||
await element.open(
|
||||
new CustomEvent<OpenFixPreviewEventDetail>(EventType.OPEN_FIX_PREVIEW, {
|
||||
detail: {
|
||||
patchNum: 2 as PatchSetNum,
|
||||
comment: ROBOT_COMMENT_WITH_TWO_FIXES,
|
||||
},
|
||||
})
|
||||
);
|
||||
element._onNextFixClick(new CustomEvent('click'));
|
||||
assert.equal(element._currentFix!.fix_id, 'fix_2');
|
||||
element._onPrevFixClick(new CustomEvent('click'));
|
||||
assert.equal(element._currentFix!.fix_id, 'fix_1');
|
||||
});
|
||||
|
||||
test('server-error should throw for failed apply call', async () => {
|
||||
stubRestApi('applyFixSuggestion').returns(
|
||||
Promise.reject(new Error('backend error'))
|
||||
);
|
||||
const navigateToChangeStub = sinon.stub(GerritNav, 'navigateToChange');
|
||||
element._currentFix = createFixSuggestionInfo('fix_123');
|
||||
|
||||
const closeFixPreviewEventSpy = sinon.spy();
|
||||
// Element is recreated after each test, removeEventListener isn't required
|
||||
element.addEventListener(
|
||||
EventType.CLOSE_FIX_PREVIEW,
|
||||
closeFixPreviewEventSpy
|
||||
);
|
||||
|
||||
let expectedError;
|
||||
await element._handleApplyFix(new CustomEvent('click')).catch(e => {
|
||||
expectedError = e;
|
||||
});
|
||||
assert.isOk(expectedError);
|
||||
assert.isFalse(navigateToChangeStub.called);
|
||||
sinon.assert.notCalled(closeFixPreviewEventSpy);
|
||||
});
|
||||
|
||||
test('onCancel fires close with correct parameters', () => {
|
||||
const closeFixPreviewEventSpy = sinon.spy();
|
||||
// Element is recreated after each test, removeEventListener isn't required
|
||||
element.addEventListener(
|
||||
EventType.CLOSE_FIX_PREVIEW,
|
||||
closeFixPreviewEventSpy
|
||||
);
|
||||
element.onCancel(new CustomEvent('cancel'));
|
||||
sinon.assert.calledOnceWithExactly(
|
||||
closeFixPreviewEventSpy,
|
||||
new CustomEvent<CloseFixPreviewEventDetail>(EventType.CLOSE_FIX_PREVIEW, {
|
||||
detail: {
|
||||
fixApplied: false,
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
|
@ -72,6 +72,7 @@ export interface DiffContextExpandedEventDetail {
|
|||
declare global {
|
||||
interface HTMLElementEventMap {
|
||||
'diff-context-expanded': CustomEvent<DiffContextExpandedEventDetail>;
|
||||
'content-load-needed': CustomEvent<ContentLoadNeededEventDetail>;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -511,14 +512,14 @@ export abstract class GrDiffBuilder {
|
|||
end_line: lastRange.right.end_line,
|
||||
},
|
||||
};
|
||||
fire<ContentLoadNeededEventDetail>(button, 'content-load-needed', {
|
||||
fire(button, 'content-load-needed', {
|
||||
lineRange,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
button.addEventListener('click', e => {
|
||||
e.stopPropagation();
|
||||
fire<DiffContextExpandedEventDetail>(button, 'diff-context-expanded', {
|
||||
fire(button, 'diff-context-expanded', {
|
||||
groups,
|
||||
section,
|
||||
numLines,
|
||||
|
|
|
@ -65,6 +65,8 @@ import {
|
|||
RelatedChangeAndCommitInfo,
|
||||
SubmittedTogetherInfo,
|
||||
RelatedChangesInfo,
|
||||
FixSuggestionInfo,
|
||||
FixId,
|
||||
} from '../types/common';
|
||||
import {
|
||||
AccountsVisibility,
|
||||
|
@ -614,3 +616,11 @@ export function createSubmittedTogetherInfo(): SubmittedTogetherInfo {
|
|||
non_visible_changes: 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function createFixSuggestionInfo(fixId = 'fix_1'): FixSuggestionInfo {
|
||||
return {
|
||||
fix_id: fixId as FixId,
|
||||
description: `Fix ${fixId}`,
|
||||
replacements: [],
|
||||
};
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ export enum EventType {
|
|||
MOVED_LINK_CLICKED = 'moved-link-clicked',
|
||||
NETWORK_ERROR = 'network-error',
|
||||
OPEN_FIX_PREVIEW = 'open-fix-preview',
|
||||
CLOSE_FIX_PREVIEW = 'close-fix-preview',
|
||||
PAGE_ERROR = 'page-error',
|
||||
RELOAD = 'reload',
|
||||
REPLY = 'reply',
|
||||
|
@ -54,6 +55,7 @@ declare global {
|
|||
'iron-announce': IronAnnounceEvent;
|
||||
'moved-link-clicked': MovedLinkClickedEvent;
|
||||
'open-fix-preview': OpenFixPreviewEvent;
|
||||
'close-fix-preview': CloseFixPreviewEvent;
|
||||
/* prettier-ignore */
|
||||
'reload': ReloadEvent;
|
||||
/* prettier-ignore */
|
||||
|
@ -133,6 +135,11 @@ export interface OpenFixPreviewEventDetail {
|
|||
}
|
||||
export type OpenFixPreviewEvent = CustomEvent<OpenFixPreviewEventDetail>;
|
||||
|
||||
export interface CloseFixPreviewEventDetail {
|
||||
fixApplied: boolean;
|
||||
}
|
||||
export type CloseFixPreviewEvent = CustomEvent<CloseFixPreviewEventDetail>;
|
||||
|
||||
export interface PageErrorEventDetail {
|
||||
response?: Response;
|
||||
}
|
||||
|
|
|
@ -20,15 +20,8 @@ import {FetchRequest} from '../types/types';
|
|||
import {
|
||||
DialogChangeEventDetail,
|
||||
EventType,
|
||||
IronAnnounceEventDetail,
|
||||
NetworkErrorEventDetail,
|
||||
PageErrorEventDetail,
|
||||
ServerErrorEventDetail,
|
||||
ShowAlertEventDetail,
|
||||
SwitchTabEventDetail,
|
||||
TabState,
|
||||
ThreadListModifiedDetail,
|
||||
TitleChangeEventDetail,
|
||||
} from '../types/events';
|
||||
|
||||
export function fireEvent(target: EventTarget, type: string) {
|
||||
|
@ -40,6 +33,34 @@ export function fireEvent(target: EventTarget, type: string) {
|
|||
);
|
||||
}
|
||||
|
||||
type HTMLElementEventDetailType<
|
||||
K extends keyof HTMLElementEventMap
|
||||
> = HTMLElementEventMap[K] extends CustomEvent<infer DT>
|
||||
? unknown extends DT
|
||||
? never
|
||||
: DT
|
||||
: never;
|
||||
|
||||
type DocumentEventDetailType<
|
||||
K extends keyof DocumentEventMap
|
||||
> = DocumentEventMap[K] extends CustomEvent<infer DT>
|
||||
? unknown extends DT
|
||||
? never
|
||||
: DT
|
||||
: never;
|
||||
|
||||
export function fire<K extends keyof DocumentEventMap>(
|
||||
target: Document,
|
||||
type: K,
|
||||
detail: DocumentEventDetailType<K>
|
||||
): void;
|
||||
|
||||
export function fire<K extends keyof HTMLElementEventMap>(
|
||||
target: EventTarget,
|
||||
type: K,
|
||||
detail: HTMLElementEventDetailType<K>
|
||||
): void;
|
||||
|
||||
export function fire<T>(target: EventTarget, type: string, detail: T) {
|
||||
target.dispatchEvent(
|
||||
new CustomEvent<T>(type, {
|
||||
|
@ -51,27 +72,27 @@ export function fire<T>(target: EventTarget, type: string, detail: T) {
|
|||
}
|
||||
|
||||
export function fireAlert(target: EventTarget, message: string) {
|
||||
fire<ShowAlertEventDetail>(target, EventType.SHOW_ALERT, {message});
|
||||
fire(target, EventType.SHOW_ALERT, {message});
|
||||
}
|
||||
|
||||
export function firePageError(response?: Response | null) {
|
||||
if (response === null) response = undefined;
|
||||
fire<PageErrorEventDetail>(document, EventType.PAGE_ERROR, {response});
|
||||
fire(document, EventType.PAGE_ERROR, {response});
|
||||
}
|
||||
|
||||
export function fireServerError(response: Response, request?: FetchRequest) {
|
||||
fire<ServerErrorEventDetail>(document, EventType.SERVER_ERROR, {
|
||||
fire(document, EventType.SERVER_ERROR, {
|
||||
response,
|
||||
request,
|
||||
});
|
||||
}
|
||||
|
||||
export function fireNetworkError(error: Error) {
|
||||
fire<NetworkErrorEventDetail>(document, EventType.NETWORK_ERROR, {error});
|
||||
fire(document, EventType.NETWORK_ERROR, {error});
|
||||
}
|
||||
|
||||
export function fireTitleChange(target: EventTarget, title: string) {
|
||||
fire<TitleChangeEventDetail>(target, EventType.TITLE_CHANGE, {title});
|
||||
fire(target, EventType.TITLE_CHANGE, {title});
|
||||
}
|
||||
|
||||
// TODO(milutin) - remove once new gr-dialog will do it out of the box
|
||||
|
@ -80,11 +101,11 @@ export function fireDialogChange(
|
|||
target: EventTarget,
|
||||
detail: DialogChangeEventDetail
|
||||
) {
|
||||
fire<DialogChangeEventDetail>(target, EventType.DIALOG_CHANGE, detail);
|
||||
fire(target, EventType.DIALOG_CHANGE, detail);
|
||||
}
|
||||
|
||||
export function fireIronAnnounce(target: EventTarget, text: string) {
|
||||
fire<IronAnnounceEventDetail>(target, EventType.IRON_ANNOUNCE, {text});
|
||||
fire(target, EventType.IRON_ANNOUNCE, {text});
|
||||
}
|
||||
|
||||
export function fireThreadListModifiedEvent(
|
||||
|
@ -92,7 +113,7 @@ export function fireThreadListModifiedEvent(
|
|||
rootId: UrlEncodedCommentId,
|
||||
path: string
|
||||
) {
|
||||
fire<ThreadListModifiedDetail>(target, EventType.THREAD_LIST_MODIFIED, {
|
||||
fire(target, EventType.THREAD_LIST_MODIFIED, {
|
||||
rootId,
|
||||
path,
|
||||
});
|
||||
|
@ -105,7 +126,11 @@ export function fireShowPrimaryTab(
|
|||
tabState?: TabState
|
||||
) {
|
||||
const detail: SwitchTabEventDetail = {tab, scrollIntoView, tabState};
|
||||
fire<SwitchTabEventDetail>(target, EventType.SHOW_PRIMARY_TAB, detail);
|
||||
fire(target, EventType.SHOW_PRIMARY_TAB, detail);
|
||||
}
|
||||
|
||||
export function fireCloseFixPreview(target: EventTarget, fixApplied: boolean) {
|
||||
fire(target, EventType.CLOSE_FIX_PREVIEW, {fixApplied});
|
||||
}
|
||||
|
||||
export function waitForEventOnce<K extends keyof HTMLElementEventMap>(
|
||||
|
|
Loading…
Reference in New Issue