blob: a7fc99e5677b04c888ea9acb6e8e49671667a036 [file]
<!DOCTYPE html>
<!--
Copyright 2017 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.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>cr-buildbucket-view tests</title>
<script src="../node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"></script>
<script src="../node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
<script src="../node_modules/web-component-tester/browser.js"></script>
<test-fixture id="basic">
<template>
<cr-buildbucket-view></cr-buildbucket-view>
</template>
</test-fixture>
<script type="module">
import './common-test-setup.js';
import {deepFreeze} from './test-util.js';
import {BuildStatus} from '../src/main/resources/static/buildbucket-client.js';
import '../src/main/resources/static/cr-buildbucket-view.js';
suite('cr-buildbucket-view basic tests', () => {
const config = deepFreeze({
gerritHost: 'gerrit.example.com',
buckets: [],
});
const exampleBuilds = deepFreeze([
{
id: '1',
status: BuildStatus.SUCCESS,
builder: {project: 'chromium', bucket: 'try', builder: 'linux-rel'},
tags: [{key: 'cq_experimental', value: 'false'}],
},
{
id: '2',
status: BuildStatus.FAILURE,
builder: {project: 'chromium', bucket: 'try', builder: 'mac-rel'},
tags: [{key: 'cq_experimental', value: 'false'}],
},
]);
let sandbox;
let element;
setup(() => {
element = fixture('basic');
Object.assign(element, {
plugin: {
getPluginName() {
return 'buildbucket';
},
restApi() {
return {getLoggedIn: async () => true};
},
},
change: {
project: 'project-foo',
status: 'NEW',
_number: 1,
revisions: {
'ba5eba11': {_number: 1, kind: 'REWORK'},
'cofffefe': {_number: 2, kind: 'REWORK'},
},
},
revision: {_number: 2},
_pluginConfig: config,
_updateTimeoutID: null,
});
sandbox = sinon.sandbox.create();
});
teardown(() => {
sandbox.restore();
});
test('_refreshBuilds uses exponential backoff', async () => {
assert.isNotOk(element._updateTimeoutID);
const fetchBuildsStub = sandbox.stub(
element, '_fetchBuilds', () => Promise.reject(new Error('nope')));
const timeoutMs = element._updateIntervalMs;
await element._refreshBuilds();
// The update interval has been increased to more than two times
// its previous value, and the timer is set.
assert.isAbove(element._updateIntervalMs, 2 * timeoutMs);
assert.isOk(element._updateTimeoutID);
// Also, a request was made to fetch builds for the latest patchset,
// but not the previous patchset.
assert.isTrue(fetchBuildsStub.calledWith([2]));
assert.isFalse(fetchBuildsStub.calledWith([1]));
});
test('_refreshBuilds with a merged change', async () => {
// If the change is merged and the latest patchset is a single
// patchset with no results, the _refreshBuilds will fetch
// builds for the previous patchset(s).
const fetchBuildsStub = sandbox.stub(
element, '_fetchBuilds', () => Promise.resolve([]));
element.change.status = 'MERGED';
await element._refreshBuilds();
assert.isTrue(fetchBuildsStub.calledWith([2]));
// A request was made to fetch builds for the previous patchset.
assert.isTrue(fetchBuildsStub.calledWith([1]));
});
test('_refreshBuilds with a merged change and builds', async () => {
element.change.status = 'MERGED';
const fetchBuildsStub = sandbox.stub(element, '_fetchBuilds');
fetchBuildsStub.withArgs([2]).returns(Promise.resolve(exampleBuilds));
fetchBuildsStub.withArgs([1]).returns(Promise.resolve([]));
await element._refreshBuilds();
assert.isTrue(fetchBuildsStub.calledWith([2]));
// No request was made to fetch builds for the previous patchset,
// since the first request already had a non-empty result.
assert.isFalse(fetchBuildsStub.calledWith([1]));
});
test('_refreshBuilds with merged CL, trivial final patchset', async () => {
element.change = {
project: 'project-foo',
status: 'MERGED',
_number: 1,
revisions: {
'ba5eba11': {_number: 1, kind: 'REWORK'},
'cofffefe': {_number: 2, kind: 'TRIVIAL_REBASE'},
},
};
const fetchBuildsStub = sandbox.stub(element, '_fetchBuilds');
fetchBuildsStub.withArgs([1, 2]).returns(Promise.resolve([]));
await element._refreshBuilds();
// The final patchset is not treated as a special case if it
// is a trivial patchset.
assert.isTrue(fetchBuildsStub.calledWith([1, 2]));
});
test('_refreshBuilds with no builds sets no latest builds', async () => {
const fetchBuildsStub = sandbox.stub(element, '_fetchBuilds');
fetchBuildsStub.returns(Promise.resolve([]));
await element._refreshBuilds();
assert.deepEqual(element._latestBuilds, []);
assert.deepEqual(element._latestBuildsGroup, []);
});
test('_refreshBuilds with current builds sets latest builds', async () => {
const fetchBuildsStub = sandbox.stub(element, '_fetchBuilds');
fetchBuildsStub.withArgs([2]).returns(Promise.resolve(exampleBuilds));
await element._refreshBuilds();
assert.isFalse(fetchBuildsStub.calledWith([1]));
assert.deepEqual(element._latestBuilds, exampleBuilds);
assert.deepEqual(element._latestBuildsGroup, [2]);
});
test('_refreshBuilds sets nothing with earlier displayed ps', async () => {
element.revision = {_number: 1};
await element._refreshBuilds();
assert.deepEqual(element._latestBuilds, []);
assert.deepEqual(element._latestBuildsGroup, []);
});
test('_refreshBuilds sets previous ps builds', async () => {
const fetchBuildsStub = sandbox.stub(element, '_fetchBuilds');
fetchBuildsStub.withArgs([2]).returns(Promise.resolve([]));
fetchBuildsStub.withArgs([1]).returns(Promise.resolve(exampleBuilds));
await element._refreshBuilds();
assert.deepEqual(element._latestBuilds, exampleBuilds);
assert.deepEqual(element._latestBuildsGroup, [1]);
});
test('_updateCurrentBuilds updates experimental builds', () => {
const builds = deepFreeze([
{
id: 123,
status: BuildStatus.SUCCESS,
builder: {project: 'chromium', bucket: 'try', builder: 'linux-rel'},
tags: [{key: 'cq_experimental', value: 'false'}],
},
{
id: 234,
status: BuildStatus.SUCCESS,
builder: {project: 'chromium', bucket: 'try', builder: 'linux-rel'},
tags: [{key: 'cq_experimental', value: 'true'}],
},
]);
element._updateCurrentBuilds(builds);
assert.equal(element._currentBuilds.length, 1);
assert.equal(element._currentExpBuilds.length, 1);
assert.equal(element._currentBuilds[0], builds[0]);
assert.equal(element._currentExpBuilds[0], builds[1]);
});
test('_makeGerritChange creates a buildbucket.v2.GerritChange', () => {
const change = {
project: 'project-foo',
_number: 123,
revisions: {
revA: {_number: 1, kind: 'REWORK'},
revB: {_number: 2, kind: 'TRIVIAL_REBASE'},
},
};
assert.deepEqual(element._makeGerritChange('foo-rev.com', change, 2), {
host: 'foo-rev.com',
project: 'project-foo',
change: 123,
patchset: 2,
});
});
test('_computeMatchingPatchsets with only trivial patchsets', () => {
// The first patchset is always REWORK. In this example, all other
// patchsets are trivial.
const change = {
project: 'project-foo',
_number: 1,
revisions: {
revA: {_number: 1, kind: 'REWORK'},
revB: {_number: 2, kind: 'TRIVIAL_REBASE'},
revC: {_number: 3, kind: 'TRIVIAL_REBASE'},
},
};
assert.deepEqual(
element._computeMatchingPatchsets(change, 3), [1, 2, 3]);
assert.deepEqual(
element._computeMatchingPatchsets(change, 2), [1, 2]);
});
test('_computeMatchingPatchsets with merged change', () => {
// The change being merged doesn't affect the grouping.
const change = {
project: 'project-foo',
_number: 1,
status: 'MERGED',
revisions: {
revA: {_number: 1, kind: 'REWORK'},
revB: {_number: 2, kind: 'TRIVIAL_REBASE'},
revC: {_number: 3, kind: 'TRIVIAL_REBASE'},
revD: {_number: 4, kind: 'REWORK'},
},
};
assert.deepEqual(
element._computeMatchingPatchsets(change, 4), [4]);
assert.deepEqual(
element._computeMatchingPatchsets(change, 3), [1, 2, 3]);
});
test('_computeMatchingPatchsets with non-trivial patchsets', () => {
const change = {
project: 'project-foo',
_number: 1,
revisions: {
revA: {_number: 1, kind: 'REWORK'},
revB: {_number: 2, kind: 'REBASE'},
revC: {_number: 3, kind: 'TRIVIAL_REBASE'},
},
};
assert.deepEqual(
element._computeMatchingPatchsets(change, 3),
[2, 3]);
});
test('_computeMatchingPatchsets with the first patchset', () => {
const change = {
project: 'project-foo',
_number: 1,
revisions: {
revA: {_number: 1, kind: 'REWORK'},
revB: {_number: 2, kind: 'REWORK'},
},
};
assert.deepEqual(
element._computeMatchingPatchsets(change, 1),
[1]);
});
test('_computeMatchingPatchsets with an invalid patchset', () => {
const change = {
project: 'project-foo',
_number: 1,
revisions: {
revA: {_number: 1, kind: 'REWORK'},
revB: {_number: 2, kind: 'REWORK'},
},
};
assert.deepEqual(element._computeMatchingPatchsets(change, 0), []);
assert.deepEqual(element._computeMatchingPatchsets(change, -1), []);
});
test('_displayed returns true when there are buckets', () => {
assert.isTrue(element._displayed([{
name: 'm.b',
builders: ['b'],
}], [], []));
});
test('_displayed returns true when there are builds', () => {
assert.isTrue(element._displayed(null, [{
tags: ['builder:b'],
}], []));
assert.isTrue(element._displayed([], [{
tags: ['builder:b'],
}], []));
assert.isTrue(element._displayed([], [], [{
tags: ['builder:b'],
}]));
});
test('_displayed returns false when there are no builds or buckets', () => {
assert.isFalse(element._displayed([], [], []));
assert.isFalse(element._displayed(null, [], []));
});
test('_computePatchsetDescriptor with no patchsets', () => {
assert.equal(element._computePatchsetDescriptor([]), '');
assert.equal(element._computePatchsetDescriptor(null), '');
});
test('_computePatchsetDescriptor with one patchset', () => {
assert.equal(
element._computePatchsetDescriptor([3]),
'Showing jobs from patchset 3. ');
});
test('_computePatchsetDescriptor with multiple patchsets', () => {
assert.equal(
element._computePatchsetDescriptor([4, 3, 5]),
'Showing jobs from patchsets 3..5. ');
});
test('_computeRetryFailedButtonText with past range', () => {
assert.equal(
element._computeRetryFailedButtonText([2, 3]),
'Retry failed from patchsets 2..3');
});
test('_computeRetryFailedButtonText with current builds', () => {
const builds = [
{
id: 4,
status: BuildStatus.FAILURE,
builder: {project: 'chromium', bucket: 'try', builder: 'linux-rel'},
tags: [{key: 'cq_experimental', value: 'false'}],
},
];
element._updateCurrentBuilds(builds);
assert.equal(
element._computeRetryFailedButtonText([2, 3]),
'Retry failed');
});
test('_computeRetryFailedButtonHidden when logged out', () => {
const builds = [
{
id: 4,
status: BuildStatus.FAILURE,
builder: {project: 'chromium', bucket: 'try', builder: 'linux-rel'},
tags: [{key: 'cq_experimental', value: 'false'}],
},
];
element._loggedIn = false;
element.change.status = 'NEW';
assert.isTrue(element._computeRetryFailedButtonHidden(builds));
});
test('_computeRetryFailedButtonHidden with merged change', () => {
const builds = [
{
id: 4,
status: BuildStatus.FAILURE,
builder: {project: 'chromium', bucket: 'try', builder: 'linux-rel'},
tags: [{key: 'cq_experimental', value: 'false'}],
},
];
element._loggedIn = true;
element.change.status = 'MERGED';
assert.isTrue(element._computeRetryFailedButtonHidden(builds));
});
test('_computeRetryFailedButtonHidden with no builds', () => {
element._loggedIn = true;
element.change.status = 'NEW';
assert.isTrue(element._computeRetryFailedButtonHidden([]));
});
test('_computeRetryFailedButtonHidden when it should be visible', () => {
const builds = [
{
id: 4,
status: BuildStatus.FAILURE,
builder: {project: 'chromium', bucket: 'try', builder: 'linux-rel'},
tags: [{key: 'cq_experimental', value: 'false'}],
},
];
element._loggedIn = true;
element.change.status = 'NEW';
assert.isFalse(element._computeRetryFailedButtonHidden(builds));
});
test('_computeRetryFailedButtonDisabled with running builds', () => {
element._loggedIn = true;
element.change.status = 'NEW';
const builds = [
{
id: '2',
status: BuildStatus.STARTED,
builder: {project: 'chromium', bucket: 'try', builder: 'linux-rel'},
tags: [{key: 'cq_experimental', value: 'false'}],
},
{
id: '1',
status: BuildStatus.FAILURE,
builder: {project: 'chromium', bucket: 'try', builder: 'linux-rel'},
tags: [{key: 'cq_experimental', value: 'false'}],
},
];
assert.isTrue(element._computeRetryFailedButtonDisabled(builds, false));
});
test('_computeRetryFailedButtonDisabled with failed build', () => {
element._loggedIn = true;
element.change.status = 'NEW';
const builds = [
{
id: '1',
status: BuildStatus.FAILURE,
builder: {project: 'chromium', bucket: 'try', builder: 'linux-rel'},
tags: [{key: 'cq_experimental', value: 'false'}],
},
];
assert.isFalse(element._computeRetryFailedButtonDisabled(builds, false));
});
test('_computeRetryFailedButtonDisabled when waiting for trigger', () => {
element._loggedIn = true;
element.change.status = 'NEW';
const builds = [
{
id: '1',
status: BuildStatus.FAILURE,
builder: {project: 'chromium', bucket: 'try', builder: 'linux-rel'},
tags: [{key: 'cq_experimental', value: 'false'}],
},
];
assert.isTrue(element._computeRetryFailedButtonDisabled(builds, true));
});
});
</script>