blob: beeac959ab3c37312e023042398a5d915b950f14 [file] [log] [blame] [edit]
<!DOCTYPE html>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<script src="/node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
<script src="../node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
<script src="../bower_components/web-component-tester/browser.js"></script>
<title>gr-checks-item</title>
<!-- Gr-overlay does not exist in the test framework
It is expected to be provided by Gerrit core -->
<script type="module">
import '../test/common-test-setup.js';
class GrOverlay extends Polymer.Element {
static get is() { return 'gr-overlay'; }
refit() {}
open() {}
}
customElements.define(GrOverlay.is, GrOverlay);
</script>
<test-fixture id="basic">
<template>
<gr-checks-view></gr-checks-view>
</template>
</test-fixture>
<script type="module">
import './gr-checks-view.js';
import {Statuses} from './gr-checks-all-statuses.js';
const CHECK1 = {
checkId: 'test-check-id',
logUrl: 'http://example.com/test-log-url',
startTime: '2019-02-06T22:25:19.269Z',
finishTime: '2019-02-06T22:25:44.574Z',
checker_name: 'test checker',
state: 'RUNNING',
checker_status: 'ENABLED',
blocking: [],
checker_description: 'No-op jobs for testing purposes',
message: '\n\nChange-Id: I8df212a28ae23cc239afd10ee4f506887e03ab70\n',
showCheckMessage: undefined,
checker_uuid: 'codestyle-a6a0e4682515f3521897c5f950d1394f4619d928',
};
const CHECK2 = {
checkId: 'test-check-id2',
logUrl: 'http://example.com/test-log-url',
startTime: '2019-02-06T22:25:19.269Z',
finishTime: '2019-02-06T22:25:44.574Z',
checker_name: 'test checker2',
state: 'RUNNING',
checker_status: 'ENABLED',
blocking: [],
checker_description: 'No-op jobs for testing purposes 2',
message: '\n\nChange-Id: I8df212a28ae23cc239afd10ee4f506887e03ab70\n',
showCheckMessage: undefined,
checker_uuid: 'polygerrit-a6a0e4682515f3521897c5f950d1394f4619d928',
};
const REVISION = {
kind: 'REWORK',
_number: 3,
created: '2018-05-15 21:56:13.000000000',
uploader: {
_account_id: 1000000,
},
ref: 'refs/changes/00/1000/1',
commit: {
parents: [],
subject: '',
message: '\n\nChange-Id: I8df212a28ae23cc239afd10ee4f506887e03ab70\n',
commit: '1c9a1dfd38ea51dc7880f3ddf669100710f0c91b',
},
};
const REVISION2 = {
kind: 'REWORK',
_number: 2,
created: '2018-05-15 21:56:13.000000000',
uploader: {
_account_id: 1000000,
},
ref: 'refs/changes/00/1000/2',
commit: {
parents: [],
subject: '',
message: '\n\nChange-Id: I8df212a28ae23cc239afd10ee4f506887e03ab70\n',
commit: '1c9a1dfd38ea51dc6880f3ddf669100710f0c91b',
},
};
const CHECKS_POLL_INTERVAL_MS = 60 * 1000;
const STATE_ALL = 'ALL';
const CHECKS_LIMIT = 20;
suite('gr-checks-view tests', () => {
let element;
let sandbox;
let getChecksSpy;
let getChecksResolve;
let retryCheckSpy;
let isConfiguredSpy;
let isConfiguredResolve;
let getAccountSpy;
let getAccountResolve;
let fetchJSONSpy;
let getAccountCapabilitiesSpy;
let getAccountCapabilitiesResolve;
let postSpy;
let postReject;
setup(done => {
sandbox = sinon.sandbox.create();
getChecksSpy = sinon.stub();
const getChecksPromise = new Promise((resolve, reject) => {
getChecksResolve = resolve;
});
getChecksSpy.returns(getChecksPromise);
isConfiguredSpy = sinon.stub();
const isConfiguredPromise = new Promise((resolve, reject) => {
isConfiguredResolve = resolve;
});
isConfiguredSpy.returns(isConfiguredPromise);
retryCheckSpy = sinon.stub();
retryCheckSpy.returns(Promise.resolve());
const plugin = {};
getAccountSpy = sinon.stub();
const getAccountPromise = new Promise((resolve, reject) => {
getAccountResolve = resolve;
});
getAccountSpy.returns(getAccountPromise);
postSpy = sinon.stub();
const postPromise = new Promise((resolve, reject) => {
postReject = reject;
});
postSpy.returns(postPromise);
fetchJSONSpy = sinon.stub();
const fetchJSONPromise = new Promise(() => {});
fetchJSONSpy.returns(fetchJSONPromise);
getAccountCapabilitiesSpy = sinon.stub();
const getAccountCapabilitiesPromise = new Promise((resolve, reject) => {
getAccountCapabilitiesResolve = resolve;
});
getAccountCapabilitiesSpy.returns(getAccountCapabilitiesPromise);
plugin.restApi = () => ({
getAccount: getAccountSpy,
fetchJSON: fetchJSONSpy,
getAccountCapabilities: getAccountCapabilitiesSpy,
post: postSpy,
});
element = fixture('basic');
Object.assign(element, {
retryCheck: retryCheckSpy,
getChecks: getChecksSpy,
isConfigured: isConfiguredSpy,
change: {
project: 'test-repository',
_number: 2,
revisions: {
'first-sha': REVISION2,
'second-sha': REVISION,
},
},
plugin,
revision: REVISION,
});
flush(done);
});
teardown(() => { sandbox.restore(); });
test('renders loading', () => {
// Element also contains the hidden gr-overlay hence use includes
assert(element.shadowRoot.textContent.trim().includes('Loading...'));
});
test('queries the checks', () => {
assert.isTrue(getChecksSpy.called);
assert.isTrue(getChecksSpy.calledWith(2, 3));
});
suite('no checks returned', () => {
setup(done => {
getChecksResolve([]);
flush(done);
});
test('it calls to check if the checks are configured', () => {
assert.isTrue(isConfiguredSpy.called);
assert.isTrue(isConfiguredSpy.calledWith('test-repository'));
});
test('no configure button renders', () => {
assert(!element.$$('gr-button'));
});
suite('not configured', () => {
setup(done => {
isConfiguredResolve(false);
flush(done);
});
test('renders checks not configured', () => {
const header = element.$$('h2');
assert.equal(header.textContent.trim(),
'Code review checks not configured');
});
suite('create checker capability false', () => {
setup(done => {
getAccountResolve(true);
getAccountCapabilitiesResolve({'checks-administrateCheckers': false});
flush(done);
});
test('checker button does not render', () => {
assert(!element.$$('gr-button'));
});
});
suite('create checker capability true', () => {
setup(done => {
getAccountResolve(true);
getAccountCapabilitiesResolve({'checks-administrateCheckers': true});
flush(done);
});
test('checker button renders', () => {
assert(element.$$('gr-button'));
});
});
});
suite('no checks ran', () => {
setup(done => {
isConfiguredResolve(true);
flush(done);
});
test('renders checks not configured', () => {
const header = element.shadowRoot.querySelector('h2');
assert.equal(header.textContent.trim(),
'No checks ran for this code review');
});
});
});
suite('checks updated properly', () => {
setup(done => {
element._checks = [CHECK1, CHECK2];
flush(done);
});
test('message is updated', () => {
const NEW_CHECKS = [Object.assign({}, CHECK1),
Object.assign({}, CHECK2)];
NEW_CHECKS[0].message = 'New message 1';
NEW_CHECKS[1].message = 'New message 2';
const EXPECTED_CHECKS = [Object.assign({}, CHECK1),
Object.assign({}, CHECK2)];
EXPECTED_CHECKS[0].message = 'New message 1';
EXPECTED_CHECKS[1].message = 'New message 2';
const UPDATED_CHECKS = element._updateChecks(NEW_CHECKS);
assert.equal(UPDATED_CHECKS[0].message, NEW_CHECKS[0].message);
assert.equal(UPDATED_CHECKS[1].message, NEW_CHECKS[1].message);
});
test('total checks updated if one is deleted', () => {
const NEW_CHECKS = [Object.assign({}, CHECK1)];
const EXPECTED_CHECKS = [Object.assign({}, CHECK1)];
const UPDATED_CHECKS = element._updateChecks(NEW_CHECKS);
assert.equal(UPDATED_CHECKS.length, 1);
assert.deepEqual(UPDATED_CHECKS, EXPECTED_CHECKS);
});
test('status is updated', () => {
const NEW_CHECKS = [Object.assign({}, CHECK1),
Object.assign({}, CHECK2)];
NEW_CHECKS[0].state = 'SUCCESSFUL';
NEW_CHECKS[1].state = 'FAILED';
const UPDATED_CHECKS = element._updateChecks(NEW_CHECKS);
assert.deepEqual(UPDATED_CHECKS, NEW_CHECKS);
});
test('showMessage property is retained', () => {
element._checks[0].showCheckMessage = true;
element._checks[1].showCheckMessage = false;
const NEW_CHECKS = [Object.assign({}, CHECK1),
Object.assign({}, CHECK2)];
const UPDATED_CHECKS = element._updateChecks(NEW_CHECKS);
assert.equal(UPDATED_CHECKS[0].showCheckMessage,
CHECK1.showCheckMessage);
assert.equal(UPDATED_CHECKS[1].showCheckMessage,
CHECK2.showCheckMessage);
element._checks[0].showCheckMessage = undefined;
element._checks[1].showCheckMessage = undefined;
});
test('url is not set if new check has no url', () => {
const NEW_CHECKS = [Object.assign({}, CHECK1),
Object.assign({}, CHECK2)];
delete NEW_CHECKS[0]['url'];
delete NEW_CHECKS[1]['url'];
const UPDATED_CHECKS = element._updateChecks(NEW_CHECKS);
assert.isNotOk(UPDATED_CHECKS[0].url);
assert.isNotOk(UPDATED_CHECKS[1].url);
});
test('url is not set if new check has no url', () => {
const NEW_CHECKS = [Object.assign({}, CHECK1),
Object.assign({}, CHECK2)];
delete NEW_CHECKS[0]['url'];
delete NEW_CHECKS[1]['url'];
const UPDATED_CHECKS = element._updateChecks(NEW_CHECKS);
assert.isNotOk(UPDATED_CHECKS[0].url);
assert.isNotOk(UPDATED_CHECKS[1].url);
});
test('message is not shown if new check has no message', () => {
const NEW_CHECKS = [Object.assign({}, CHECK1),
Object.assign({}, CHECK2)];
NEW_CHECKS[0].message = '';
const UPDATED_CHECKS = element._updateChecks(NEW_CHECKS);
assert.equal(UPDATED_CHECKS[0].message, NEW_CHECKS[0].message);
});
});
suite('with checks', () => {
setup(done => {
const CHECK3 = Object.assign({}, CHECK1, {state: 'FAILED'});
const CHECK4 = Object.assign({}, CHECK1, {state: 'FAILED',
blocking: [1, 2, 3]});
getChecksResolve([CHECK1, CHECK2, CHECK3, CHECK4]);
flush(done);
});
test('it calls to check if the checks are configured', () => {
assert.isFalse(isConfiguredSpy.called);
});
test('renders a table of all the checks', () => {
const tbody = element.$$('table > tbody');
assert.lengthOf(tbody.querySelectorAll('gr-checks-item'), 4);
});
test('retry fires show-error event', done => {
postReject(new Error('random error'));
const fireStub = sandbox.stub(element, 'dispatchEvent');
Polymer.dom.flush();
const checksItem = element.shadowRoot.querySelectorAll(
'table > tbody > gr-checks-item'
)[0];
const reRun = checksItem.shadowRoot.querySelectorAll('td')[7];
const reRunButton = reRun.querySelector('gr-button');
reRunButton.click();
flush(() => {
assert.equal(fireStub.firstCall.args[0].type, ('show-error'));
done();
});
});
suite('message is rendered', () => {
setup(done => {
element._checks = [CHECK1, CHECK2];
flush(done);
});
test('messsage icon is displayed', () => {
const checkItems = element.shadowRoot
.querySelectorAll('gr-checks-item');
const messagesCount = [...checkItems].reduce((count, item) => {
return count += item.shadowRoot
.querySelectorAll('.expand-message').length;
}, 0);
assert.equal(messagesCount,
element._visibleChecks.length);
});
test('message displayed on clicking icon', done => {
const checkItem = element.shadowRoot.querySelector('gr-checks-item');
checkItem.shadowRoot
.querySelector('.expand-message').click();
flush(() => {
const msg = element.shadowRoot
.querySelector('.check-message').innerText
.trim();
assert.equal(msg, element._visibleChecks[0].message.trim());
done();
});
});
});
suite('create checker capability false', () => {
setup(done => {
getAccountResolve(true);
getAccountCapabilitiesResolve({'checks-administrateCheckers': false});
flush(done);
});
test('checker button does not render', () => {
assert(!element.$$('gr-button'));
});
});
test('retry checks', done => {
const checkItems = element.shadowRoot
.querySelectorAll('gr-checks-item');
const retryButton = checkItems[0].shadowRoot
.querySelectorAll('gr-button')[1];
MockInteractions.tap(retryButton);
const expectedUrl = '/changes/2/revisions/3/checks/codestyle-'
+ 'a6a0e4682515f3521897c5f950d1394f4619d928/rerun';
flush(() => {
assert.isTrue(postSpy.calledWith(expectedUrl));
done();
});
});
suite('create checker capability true', () => {
setup(done => {
getAccountResolve(true);
getAccountCapabilitiesResolve({
'checks-administrateCheckers': true,
});
flush(done);
});
test('checker button renders', () => {
assert(element.$$('gr-button'));
});
});
suite('patchset navigation', () => {
test('renders the dropdown', () => {
assert.isNotNull(element.shadowRoot
.querySelector('.patch-set-dropdown'));
});
test('when patchset updated it fetches new checks', done => {
const clock = sinon.useFakeTimers();
const fetchChecksStub = sandbox.stub(element,
'_fetchChecks');
assert.equal(element._currentPatchSet, 3);
element.revision = REVISION2;
flush(() => {
const firstCallArgs = fetchChecksStub.args[0];
assert.equal(firstCallArgs[1], element._currentPatchSet);
clock.tick(CHECKS_POLL_INTERVAL_MS + 1000);
flush(() => {
assert(fetchChecksStub.callCount === 2);
const secondCallArgs = fetchChecksStub.args[1];
assert.equal(secondCallArgs[1], element._currentPatchSet);
done();
});
});
});
test('update to revision updates currentPatchset', done => {
assert.equal(element._currentPatchSet, 3);
element.revision = REVISION2;
flush(() => {
assert.equal(element._currentPatchSet, 2);
done();
});
});
});
suite('check state filter', () => {
test('renders the filter dropdown', () => {
assert.isNotNull(element.shadowRoot
.querySelector('.check-state-filter'));
});
test('default filter is all', () => {
assert.equal(element._currentStatus, STATE_ALL);
});
test('updating filter status filters checks', done => {
assert.equal(element._visibleChecks.length, 4);
element._currentStatus = Statuses.RUNNING;
flush(() => {
assert.equal(element._visibleChecks.length, 2);
done();
});
});
});
suite('blocking checkbox', () => {
test('renders the checkbox', () => {
assert.isNotNull(element.shadowRoot
.querySelector('input[name="blocking"]'));
});
test('default filter is all', () => {
assert.equal(element.shadowRoot
.querySelector('input[name="blocking"]').checked,
false);
});
test('blocking checkbox filters checks', done => {
assert.equal(element._visibleChecks.length, 4);
element.shadowRoot.querySelector('input[name="blocking"]').click();
flush(() => {
assert.equal(element._visibleChecks.length, 1);
done();
});
});
});
});
suite('with large number of checks', () => {
setup(done => {
const CHECK3 = Object.assign({}, CHECK1, {state: 'FAILED'});
const CHECK4 = Object.assign({}, CHECK1, {state: 'FAILED',
blocking: [1, 2, 3]});
const checks = [];
for (let i = 1; i <= 20; i++) {
checks.push(...[CHECK1, CHECK2, CHECK3, CHECK4]);
}
getChecksResolve(checks);
flush(done);
});
test('rendered checks are limited by default', () => {
assert.equal(element._visibleChecks.length, CHECKS_LIMIT);
});
test('show more button expands to show all checks', () => {
const showMoreChecksButton = element.shadowRoot
.querySelector('.show-more-checks');
assert.isOk(showMoreChecksButton);
showMoreChecksButton.click();
flush(() => {
assert.equal(element._visibleChecks.length, element._checks.length);
});
});
test('show more button hides if checks are below limit', () => {
element._currentStatus = Statuses.NOT_STARTED;
flush(() => {
const showMoreChecksButton = element.querySelector(
'.show-more-checks');
assert.equal(showMoreChecksButton.style.display, 'none');
});
});
});
});
</script>