blob: 9392cb9d11cf8618b4637c7629b2683a430771b6 [file] [log] [blame]
/**
* @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,
RobotCommentInfo,
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 {
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: RobotCommentInfo = {
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: RobotCommentInfo = {
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;
await flush();
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!, {
patchNum: EditPatchSetNum,
basePatchNum: 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,
},
})
);
});
});