| <!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> |