| import {assertRejects} from './test-util'; |
| import {BuildbucketV2Client} from '../buildbucket-client'; |
| import {CrTryjobPicker} from '../cr-tryjob-picker'; |
| |
| suite('cr-tryjob-picker basic tests', () => { |
| let sandbox: sinon.SinonSandbox; |
| let element: CrTryjobPicker; |
| let closeFired = false; |
| |
| setup(() => { |
| element = document.createElement('cr-tryjob-picker') as CrTryjobPicker; |
| |
| Object.assign(element, { |
| buildbucketHost: 'foo.bar', |
| change: { |
| owner: {email: 'jane@example.com'}, |
| _number: 1, |
| }, |
| revision: {_number: 1}, |
| pluginConfig: { |
| gitHost: 'example.googlesource.com', |
| }, |
| }); |
| |
| closeFired = false; |
| element.addEventListener('close', _ => { |
| closeFired = true; |
| }); |
| |
| document.body.appendChild(element); |
| |
| sandbox = sandbox = sinon.createSandbox(); |
| sandbox.stub(window, 'alert'); |
| |
| sandbox |
| .stub(BuildbucketV2Client.prototype, 'listBuilders') |
| .callsFake(({project, bucket, pageToken}) => { |
| assert.equal(bucket, 'try'); |
| const response: any = {}; |
| 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(() => { |
| document.body.removeChild(element); |
| sandbox.restore(); |
| }); |
| |
| test('bucket filter function filters builders', () => { |
| let fn = element.computeBucketFilterFn(_ => true); |
| assert.isTrue( |
| fn({ |
| builders: ['blerp', 'GERP'], |
| project: '', |
| bucket: '', |
| }) |
| ); |
| |
| fn = element.computeBucketFilterFn(s => /gerp/.test(s)); |
| assert.isTrue( |
| fn({ |
| builders: ['blerp', 'gerp'], |
| project: '', |
| bucket: '', |
| }) |
| ); |
| assert.isTrue( |
| fn({ |
| builders: ['gerp'], |
| project: '', |
| bucket: '', |
| }) |
| ); |
| assert.isFalse( |
| fn({ |
| builders: ['blerp'], |
| project: '', |
| bucket: '', |
| }) |
| ); |
| }); |
| |
| 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', async () => { |
| await element.bucketsUpdating; |
| 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} as unknown as Event); |
| 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.equal(addButton?.getAttribute('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; |
| 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 as any).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 () => { |
| const fn = ( |
| project: string, |
| bucket: string, |
| builder: string, |
| isChecked: boolean |
| ) => { |
| 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} as unknown as Event); |
| assert.equal( |
| element.includeTrybots, |
| 'luci.proj.foo.bucket.bar:builder.baz' |
| ); |
| |
| // Uncheck the builder. |
| el1.checked = false; |
| element.handleCheckboxChange({target: el1} as unknown as Event); |
| assert.equal(element.includeTrybots, ''); |
| |
| // Check two builders. |
| el1.checked = true; |
| const el2 = fn('proj.foo2', 'bucket.bar2', 'builder.baz2', true); |
| element.handleCheckboxChange({target: el1} as unknown as Event); |
| element.handleCheckboxChange({target: el2} as unknown as Event); |
| 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} as unknown as Event); |
| assert.equal( |
| element.includeTrybots, |
| 'luci.proj.foo2.bucket.bar2:builder.baz2' |
| ); |
| |
| // Uncheck the second builder. |
| el2.checked = false; |
| element.handleCheckboxChange({target: el2} as unknown as Event); |
| 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(Promise.resolve({})); |
| await element.onAddTap(); |
| assert.isTrue(closeFired); |
| assert.isFalse(element.disabled); |
| assert.deepEqual(element.selectedBuilders, {}); |
| assert.isUndefined(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 = {...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 = {...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'); |
| }); |
| }); |