blob: 473d2b9fd489390fcdafce29ab0c9862c1f54038 [file] [log] [blame]
/**
* @license
* Copyright (C) 2017 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-dashboard-view.js';
import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
import {GerritView} from '../../../services/router/router-model.js';
import {changeIsOpen} from '../../../utils/change-util.js';
import {ChangeStatus} from '../../../constants/constants.js';
import {createAccountWithId} from '../../../test/test-data-generators.js';
import {addListenerForTest, stubRestApi, isHidden} from '../../../test/test-utils.js';
const basicFixture = fixtureFromElement('gr-dashboard-view');
suite('gr-dashboard-view tests', () => {
let element;
let paramsChangedPromise;
let getChangesStub;
setup(() => {
stubRestApi('getLoggedIn').returns(Promise.resolve(false));
stubRestApi('getAccountDetails').returns(Promise.resolve({}));
stubRestApi('getAccountStatus').returns(Promise.resolve(false));
getChangesStub= stubRestApi('getChanges').callsFake(
(_, qs) => Promise.resolve(qs.map(() => [])));
element = basicFixture.instantiate();
let resolver;
paramsChangedPromise = new Promise(resolve => {
resolver = resolve;
});
const paramsChanged = element._paramsChanged.bind(element);
sinon.stub(element, '_paramsChanged').callsFake( params => {
paramsChanged(params).then(() => resolver());
});
});
suite('drafts banner functionality', () => {
suite('_maybeShowDraftsBanner', () => {
test('not dashboard/self', () => {
element._maybeShowDraftsBanner({
view: GerritView.DASHBOARD,
user: 'notself',
});
assert.isFalse(element._showDraftsBanner);
});
test('no drafts at all', () => {
element._results = [];
element._maybeShowDraftsBanner({
view: GerritView.DASHBOARD,
user: 'self',
});
assert.isFalse(element._showDraftsBanner);
});
test('no drafts on open changes', () => {
const openChange = {status: ChangeStatus.NEW};
element._results = [{query: 'has:draft', results: [openChange]}];
element._maybeShowDraftsBanner({
view: GerritView.DASHBOARD,
user: 'self',
});
assert.isFalse(element._showDraftsBanner);
});
test('no drafts on not open changes', () => {
const notOpenChange = {status: '_'};
element._results = [{query: 'has:draft', results: [notOpenChange]}];
assert.isFalse(changeIsOpen(element._results[0].results[0]));
element._maybeShowDraftsBanner({
view: GerritView.DASHBOARD,
user: 'self',
});
assert.isTrue(element._showDraftsBanner);
});
});
test('_showDraftsBanner', () => {
element._showDraftsBanner = false;
flush();
assert.isTrue(isHidden(element.shadowRoot
.querySelector('.banner')));
element._showDraftsBanner = true;
flush();
assert.isFalse(isHidden(element.shadowRoot
.querySelector('.banner')));
});
test('delete tap opens dialog', () => {
sinon.stub(element, '_handleOpenDeleteDialog');
element._showDraftsBanner = true;
flush();
MockInteractions.tap(element.shadowRoot
.querySelector('.banner .delete'));
assert.isTrue(element._handleOpenDeleteDialog.called);
});
test('delete comments flow', async () => {
sinon.spy(element, '_handleConfirmDelete');
sinon.stub(element, '_reload');
// Set up control over timing of when RPC resolves.
let deleteDraftCommentsPromiseResolver;
const deleteDraftCommentsPromise = new Promise(resolve => {
deleteDraftCommentsPromiseResolver = resolve;
});
const deleteStub = stubRestApi('deleteDraftComments')
.returns(deleteDraftCommentsPromise);
// Open confirmation dialog and tap confirm button.
await element.$.confirmDeleteOverlay.open();
MockInteractions.tap(element.$.confirmDeleteDialog.$.confirm);
flush();
assert.isTrue(deleteStub.calledWithExactly('-is:open'));
assert.isTrue(element.$.confirmDeleteDialog.disabled);
assert.equal(element._reload.callCount, 0);
// Verify state after RPC resolves.
deleteDraftCommentsPromiseResolver([]);
await deleteDraftCommentsPromise;
assert.equal(element._reload.callCount, 1);
});
});
test('_computeTitle', () => {
assert.equal(element._computeTitle('self'), 'My Reviews');
assert.equal(element._computeTitle('not self'), 'Dashboard for not self');
});
suite('_computeSectionCountLabel', () => {
test('empty changes dont count label', () => {
assert.equal('', element._computeSectionCountLabel([]));
});
test('1 change', () => {
assert.equal('(1)',
element._computeSectionCountLabel(['1']));
});
test('2 changes', () => {
assert.equal('(2)',
element._computeSectionCountLabel(['1', '2']));
});
test('1 change and more', () => {
assert.equal('(1 and more)',
element._computeSectionCountLabel([{_more_changes: true}]));
});
});
suite('_isViewActive', () => {
test('nothing happens when user param is falsy', () => {
element.params = {};
flush();
assert.equal(getChangesStub.callCount, 0);
element.params = {user: ''};
flush();
assert.equal(getChangesStub.callCount, 0);
});
test('content is refreshed when user param is updated', () => {
element.params = {
view: GerritNav.View.DASHBOARD,
user: 'self',
};
return paramsChangedPromise.then(() => {
assert.equal(getChangesStub.callCount, 1);
});
});
});
suite('selfOnly sections', () => {
test('viewing self dashboard includes selfOnly sections', () => {
element.params = {
view: GerritNav.View.DASHBOARD,
sections: [
{query: '1'},
{query: '2', selfOnly: true},
],
user: 'self',
};
return paramsChangedPromise.then(() => {
assert.isTrue(getChangesStub.calledWith(undefined, ['1', '2']));
});
});
test('viewing dashboard when logged in includes owner:self query', () => {
element.account = createAccountWithId(1);
element.params = {
view: GerritNav.View.DASHBOARD,
sections: [
{query: '1'},
{query: '2', selfOnly: true},
],
user: 'self',
};
return paramsChangedPromise.then(() => {
assert.isTrue(getChangesStub.calledWith(undefined,
['1', '2', 'owner:self limit:1']));
});
});
test('viewing another user\'s dashboard omits selfOnly sections', () => {
element.params = {
view: GerritNav.View.DASHBOARD,
sections: [
{query: '1'},
{query: '2', selfOnly: true},
],
user: 'user',
};
return paramsChangedPromise.then(() => {
assert.isTrue(getChangesStub.calledWith(undefined, ['1']));
});
});
});
test('suffixForDashboard is included in getChanges query', () => {
element.params = {
view: GerritNav.View.DASHBOARD,
sections: [
{query: '1'},
{query: '2', suffixForDashboard: 'suffix'},
],
};
return paramsChangedPromise.then(() => {
assert.isTrue(getChangesStub.calledOnce);
assert.deepEqual(
getChangesStub.firstCall.args, [undefined, ['1', '2 suffix']]);
});
});
suite('_getProjectDashboard', () => {
test('dashboard with foreach', () => {
stubRestApi('getDashboard')
.callsFake( () => Promise.resolve({
title: 'title',
foreach: 'foreach for ${project}',
sections: [
{name: 'section 1', query: 'query 1'},
{name: 'section 2', query: '${project} query 2'},
],
}));
return element._getProjectDashboard('project', '').then(dashboard => {
assert.deepEqual(
dashboard,
{
title: 'title',
sections: [
{name: 'section 1', query: 'query 1 foreach for project'},
{
name: 'section 2',
query: 'project query 2 foreach for project',
},
],
});
});
});
test('dashboard without foreach', () => {
stubRestApi('getDashboard').callsFake(
() => Promise.resolve({
title: 'title',
sections: [
{name: 'section 1', query: 'query 1'},
{name: 'section 2', query: '${project} query 2'},
],
}));
return element._getProjectDashboard('project', '').then(dashboard => {
assert.deepEqual(
dashboard,
{
title: 'title',
sections: [
{name: 'section 1', query: 'query 1'},
{name: 'section 2', query: 'project query 2'},
],
});
});
});
});
test('hideIfEmpty sections', () => {
const sections = [
{name: 'test1', query: 'test1', hideIfEmpty: true},
{name: 'test2', query: 'test2', hideIfEmpty: true},
];
getChangesStub.restore();
stubRestApi('getChanges')
.returns(Promise.resolve([[], ['nonempty']]));
return element._fetchDashboardChanges({sections}, false).then(() => {
assert.equal(element._results.length, 1);
assert.equal(element._results[0].name, 'test2');
});
});
test('preserve isOutgoing sections', () => {
const sections = [
{name: 'test1', query: 'test1', isOutgoing: true},
{name: 'test2', query: 'test2'},
];
getChangesStub.restore();
stubRestApi('getChanges')
.returns(Promise.resolve([[], []]));
return element._fetchDashboardChanges({sections}, false).then(() => {
assert.equal(element._results.length, 2);
assert.isTrue(element._results[0].isOutgoing);
assert.isNotOk(element._results[1].isOutgoing);
});
});
test('toggling star will update change everywhere', () => {
// It is important that the same change is represented by multiple objects
// and all are updated.
const change = {id: '5', starred: false};
const sameChange = {id: '5', starred: false};
const differentChange = {id: '4', starred: false};
element._results = [
{query: 'has:draft', results: [change]},
{query: 'is:open', results: [sameChange, differentChange]},
];
element._handleToggleStar(
new CustomEvent('toggle-star', {
detail: {
change,
starred: true,
},
})
);
assert.isTrue(change.starred);
assert.isTrue(sameChange.starred);
assert.isFalse(differentChange.starred);
});
test('_showNewUserHelp', () => {
element._loading = false;
element._showNewUserHelp = false;
flush();
assert.equal(element.$.emptyOutgoing.textContent.trim(), 'No changes');
assert.isNotOk(element.shadowRoot
.querySelector('gr-create-change-help'));
element._showNewUserHelp = true;
flush();
assert.notEqual(element.$.emptyOutgoing.textContent.trim(), 'No changes');
assert.isOk(element.shadowRoot
.querySelector('gr-create-change-help'));
});
test('_computeUserHeaderClass', () => {
assert.equal(element._computeUserHeaderClass(undefined), 'hide');
assert.equal(element._computeUserHeaderClass({}), 'hide');
assert.equal(element._computeUserHeaderClass({user: 'self'}), 'hide');
assert.equal(element._computeUserHeaderClass({user: 'user'}), 'hide');
assert.equal(
element._computeUserHeaderClass({
view: GerritView.DASHBOARD,
user: 'user',
}),
'');
assert.equal(
element._computeUserHeaderClass({project: 'p', user: 'user'}),
'hide');
assert.equal(
element._computeUserHeaderClass({
view: GerritView.DASHBOARD,
project: 'p',
user: 'user',
}),
'hide');
});
test('404 page', done => {
const response = {status: 404};
stubRestApi('getDashboard').callsFake(
async (project, dashboard, errFn) => {
errFn(response);
});
addListenerForTest(document, 'page-error', e => {
assert.strictEqual(e.detail.response, response);
paramsChangedPromise.then(done);
});
element.params = {
view: GerritNav.View.DASHBOARD,
project: 'project',
dashboard: 'dashboard',
};
});
test('params change triggers dashboardDisplayed()', async () => {
stubRestApi('getDashboard').returns(Promise.resolve({
title: 'title',
sections: [],
}));
sinon.stub(element.reporting, 'dashboardDisplayed');
element.params = {
view: GerritNav.View.DASHBOARD,
project: 'project',
dashboard: 'dashboard',
};
await paramsChangedPromise;
assert.isTrue(element.reporting.dashboardDisplayed.calledOnce);
});
test('selectedChangeIndex is derived from the params', () => {
stubRestApi('getDashboard').returns(Promise.resolve({
title: 'title',
sections: [],
}));
element.viewState = {
101001: 23,
};
element.params = {
view: GerritNav.View.DASHBOARD,
project: 'project',
dashboard: 'dashboard',
user: '101001',
};
flush();
sinon.stub(element.reporting, 'dashboardDisplayed');
paramsChangedPromise.then(() => {
assert.equal(element._selectedChangeIndex, 23);
});
});
});