blob: 1736b81ab708ed715fe9ed7d2a96b34b9b8251b5 [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-tryjob-picker 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-tryjob-picker></cr-tryjob-picker>
</template>
</test-fixture>
<script type="module">
import './common-test-setup.js';
import {assertRejects} from './test-util.js';
import {
BuildbucketV2Client,
makeBuildRequests,
} from '../src/main/resources/static/buildbucket-client.js';
import {
getNewOperationId,
} from '../src/main/resources/static/buildbucket-utils.js';
import '../src/main/resources/static/cr-tryjob-picker.js';
suite('cr-tryjob-picker basic tests', () => {
let sandbox;
let element;
let closeFired = false;
setup(() => {
element = fixture('basic');
Object.assign(element, {
change: {
owner: {email: 'jane@example.com'},
_number: 1,
},
revision: {_number: 1},
pluginConfig: {
gitHost: 'example.googlesource.com',
},
});
closeFired = false;
element.addEventListener('close', (e) => {
closeFired = true;
});
sandbox = sandbox = sinon.sandbox.create();
sandbox.stub(window, 'alert');
sandbox.stub(BuildbucketV2Client.prototype, 'listBuilders',
({project, bucket, pageToken}) => {
assert.equal(bucket, 'try');
const response = {};
if (project == 'internal' && !pageToken) {
return Promise.reject('404');
} else if (project === 'v8' && !pageToken) {
response.builders = [{id: {project, bucket, builder: 'linux'}}];
} else if (project === 'chromium' && !pageToken) {
response.builders = [{id: {project, bucket, builder: 'linux'}}];
response.nextPageToken = 'next';
} else if (project === 'chromium' && pageToken === 'next') {
response.builders = [{id: {project, bucket, builder: 'windows'}}];
} else {
assert.fail(`Unexpected request: ${{project, bucket, pageToken}}`);
}
return Promise.resolve(response);
});
});
teardown(() => {
sandbox.restore();
});
test('bucket filter function filters builders', () => {
let fn = element._computeBucketFilterFn(s => true);
assert.isTrue(fn({
builders: ['blerp', 'GERP'],
}));
fn = element._computeBucketFilterFn(s => /gerp/.test(s));
assert.isTrue(fn({
builders: ['blerp', 'gerp'],
}));
assert.isTrue(fn({
builders: ['gerp'],
}));
assert.isFalse(fn({
builders: ['blerp'],
}));
});
test('filter compilation with literal string', () => {
element._filter = 'abc';
assert.isTrue(element._matchesFilter('abc'));
assert.isFalse(element._matchesFilter('a'));
assert.equal(element._filterClass, '');
});
test('filter compilation with wildcard dot', () => {
element._filter = 'a.c';
assert.isTrue(element._matchesFilter('abc'));
assert.isTrue(element._matchesFilter('adc'));
assert.isFalse(element._matchesFilter('a'));
assert.equal(element._filterClass, '');
});
test('filter compilation with optional part, parens', () => {
element._filter = 'a(b)c';
assert.isTrue(element._matchesFilter('abc'));
assert.isFalse(element._matchesFilter('adc'));
assert.isFalse(element._matchesFilter('a'));
assert.equal(element._filterClass, '');
});
test('filter compilation with an invalid expression', () => {
element._filter = 'abc)';
assert.isFalse(element._matchesFilter('abc'));
assert.isFalse(element._matchesFilter('a'));
assert.equal(element._filterClass, 'error');
});
test('_selectedBuilders updated on checkbox change', () => {
const addButton = element.shadowRoot.querySelector('gr-button[primary]');
const el = document.createElement('input');
el.type = 'checkbox';
el.setAttribute('data-project', 'proj.foo');
el.setAttribute('data-bucket', 'bucket.bar');
el.value = 'builder.baz';
el.checked = true;
assert.equal(Object.keys(element._selectedBuilders).length, 0);
element._handleCheckboxChange({target: el});
assert.deepEqual(
element._selectedBuilders,
{
'proj##foo/bucket##bar/builder##baz': {
project: 'proj.foo',
bucket: 'bucket.bar',
builder: 'builder.baz',
},
});
assert.equal(Object.keys(element._selectedBuilders).length, 1);
assert.isTrue(
element._computeChecked('proj.foo', 'bucket.bar', 'builder.baz'));
assert.isFalse(addButton.hasAttribute('disabled'));
});
test('_computeBuckets merges builders', async () => {
// The two groups of builders that are merged are from the
// config (passed to _computeBuckets) and from getBuilders.
// In this test, the return value of getBuilders is set in
// the setup method above.
const buckets = await element._computeBuckets([
{
name: 'luci.chromium.try',
builders: ['extra'],
},
]);
assert.deepEqual(buckets, [
{
project: 'chromium',
bucket: 'try',
builders: ['extra', 'linux', 'windows'],
}
]);
});
test('_computeBuckets ignores errors when listing builders', async () => {
sandbox.stub(window.console, 'error');
await element._bucketsUpdating;
element._pluginConfigChanged({
buckets: [
{name: 'luci.v8.try'},
{name: 'luci.internal.try'},
],
});
await element._bucketsUpdating;
assert.deepEqual(
element._buckets,
[
{project: 'v8', bucket: 'try', builders: ['linux']},
{project: 'internal', bucket: 'try', builders: []},
]);
});
test('_pluginConfigChanged called twice, second config wins', async () => {
await element._bucketsUpdating;
element._pluginConfigChanged({
buckets: [{
name: 'luci.chromium.try',
}],
});
element._pluginConfigChanged({
buckets: [{
name: 'luci.v8.try',
}],
});
await element._bucketsUpdating;
assert.deepEqual(
element._buckets,
[{project: 'v8', bucket: 'try', builders: ['linux']}]);
});
test('add button is disabled when no builds are selected', async () => {
element.pluginConfig = {
buckets: [{
name: 'luci.chromium.try',
builders: ['builder'],
}],
};
await element._bucketsUpdating;
flushAsynchronousOperations();
const addButton = element.shadowRoot.querySelector('gr-button[primary]');
assert.isTrue(addButton.hasAttribute('disabled'));
const checkbox = element.shadowRoot.querySelector('input[type="checkbox"]');
assert.ok(checkbox);
const changePromise = new Promise((resolve) => {
element.addEventListener('change', resolve);
});
checkbox.checked = true;
checkbox.dispatchEvent(
new CustomEvent('change', {bubbles: true, composed: true}));
await changePromise;
assert.isFalse(addButton.hasAttribute('disabled'));
});
test('_includeTrybots updated when bots selected', async () => {
let fn = (project, bucket, builder, isChecked) => {
const el = document.createElement('input');
el.type = 'checkbox';
el.setAttribute('data-project', project);
el.setAttribute('data-bucket', bucket);
el.value = builder;
el.checked = isChecked;
return el;
};
// Check one builder.
const el1 = fn('proj.foo', 'bucket.bar', 'builder.baz', true);
assert.equal(element._includeTrybots, '');
element._handleCheckboxChange({target: el1});
assert.equal(
element._includeTrybots, 'luci.proj.foo.bucket.bar:builder.baz');
// Uncheck the builder.
el1.checked = false;
element._handleCheckboxChange({target: el1});
assert.equal(element._includeTrybots, '');
// Check two builders.
el1.checked = true;
const el2 = fn('proj.foo2', 'bucket.bar2', 'builder.baz2', true);
element._handleCheckboxChange({target: el1});
element._handleCheckboxChange({target: el2});
assert.equal(
element._includeTrybots, 'luci.proj.foo.bucket.bar:builder.baz;' +
'luci.proj.foo2.bucket.bar2:builder.baz2');
// Uncheck one builder.
el1.checked = false;
element._handleCheckboxChange({target: el1});
assert.equal(
element._includeTrybots, 'luci.proj.foo2.bucket.bar2:builder.baz2');
// Uncheck the second builder.
el2.checked = false;
element._handleCheckboxChange({target: el2});
assert.equal(element._includeTrybots, '');
});
test('on successful scheduling', async () => {
element._clientOperationId = 'opid';
element._selectedBuilders = {
'chromium/try/linux-rel': {
project: 'chromium',
bucket: 'try',
builder: 'linux-rel',
},
};
sandbox.stub(BuildbucketV2Client.prototype, 'batch').returns({});
await element._onAddTap();
assert.isTrue(closeFired);
assert.isFalse(element.disabled);
assert.deepEqual(element._selectedBuilders, {});
assert.isNull(element._clientOperationId);
});
test('on HTTP request error', async () => {
sandbox.stub(BuildbucketV2Client.prototype, 'batch')
.returns(Promise.reject(new Error('no')));
const selectedBuilders = {
'chromium/try/linux-rel': {
project: 'chromium',
bucket: 'try',
builder: 'linux-rel',
},
};
element._selectedBuilders = Object.assign({}, selectedBuilders);
element._clientOperationId = 'opid';
await assertRejects(element._onAddTap(), 'no');
assert.isFalse(closeFired);
assert.isFalse(element.disabled);
assert.deepEqual(element._selectedBuilders, selectedBuilders);
assert.equal(element._clientOperationId, 'opid');
});
test('_onAddTap rejects on build request error', async () => {
const result = {responses: [{error: {message: 'no'}}]};
sandbox.stub(BuildbucketV2Client.prototype, 'batch')
.returns(Promise.resolve(result));
const selectedBuilders = {
'chromium/try/linux-rel': {
project: 'chromium',
bucket: 'try',
builder: 'linux-rel',
},
};
element._selectedBuilders = Object.assign({}, selectedBuilders);
element._clientOperationId = 'opid';
// _onTapAdd rejects, which is handled by the caller.
// The picker remains open, and the client operation ID is unchanged
// in case the caller wants to try again.
await assertRejects(element._onAddTap(), 'no');
assert.isFalse(closeFired);
assert.isFalse(element.disabled);
assert.deepEqual(element._selectedBuilders, selectedBuilders);
assert.equal(element._clientOperationId, 'opid');
});
});
</script>