[FIX JENKINS-36055] Update branches list when new branch jobs are created (#313)

* [FIX JENKINS-36055] Update branches list when new branch jobs are created

* [FIX JENKINS-36055] Move latestRuns data module to "data/runs"

* [FIX JENKINS-36055] A few unit tests for actions.updateBranchList
This commit is contained in:
Tom Fennelly 2016-07-05 12:30:04 +01:00 committed by GitHub
parent 02665e161c
commit eb9334a63f
12 changed files with 949 additions and 217 deletions

View File

@ -48,7 +48,7 @@ class OrganizationPipelines extends Component {
case 'job_crud_created':
case 'job_crud_deleted':
case 'job_crud_renamed':
// Just refetch and update the pipelines list.
// Just refetch and update the pipelines and branches list.
// Yes, in some of these cases it would be possible to
// update the redux store state without making a REST call.
// Trading off for simplicity and view consistency here.
@ -59,6 +59,7 @@ class OrganizationPipelines extends Component {
// benefit to be got from optimizing things here.
// TODO: fix https://issues.jenkins-ci.org/browse/JENKINS-35153 for delete
_this.props.fetchPipelines(_this.context.config, _this.props.organization);
_this.props.updateBranchList(eventCopy, _this.context.config);
break;
case 'job_run_queue_buildable':
case 'job_run_queue_enter':
@ -129,6 +130,7 @@ OrganizationPipelines.propTypes = {
processJobQueuedEvent: func.isRequired,
updateRunState: func.isRequired,
updateBranchState: func.isRequired,
updateBranchList: func.isRequired,
organization: string,
params: object, // From react-router
children: node, // From react-router

View File

@ -522,6 +522,39 @@ export const actions = {
};
},
updateBranchList(event, config) {
return (dispatch, getState) => {
if (event.job_ismultibranch) {
const multibranchPipelines = getState().adminStore.branches || {};
const pipelineName = event.blueocean_job_pipeline_name;
// We're only interested in this event if we're already managing branch state
// associated with this multi-branch job.
if (!multibranchPipelines[pipelineName]) {
return;
}
// Fetch/refetch the latest set of branches for the pipeline.
const url = `${config.getAppURLBase()}/rest/organizations/${event.jenkins_org}` +
`/pipelines/${pipelineName}/branches`;
exports.fetchJson(url, (latestPipelineBranches) => {
if (event.blueocean_is_for_current_job) {
dispatch({
id: pipelineName,
payload: latestPipelineBranches,
type: ACTION_TYPES.SET_CURRENT_BRANCHES_DATA,
});
}
dispatch({
id: pipelineName,
payload: latestPipelineBranches,
type: ACTION_TYPES.SET_BRANCHES_DATA,
});
});
}
};
},
fetchRunsIfNeeded(config) {
return (dispatch) => {
const baseUrl = `${config.getAppURLBase()}/rest/organizations/jenkins` +

View File

@ -3,7 +3,7 @@ import {createRenderer} from 'react-addons-test-utils';
import { assert} from 'chai';
import sd from 'skin-deep';
import Immutable from 'immutable';
import { latestRuns as data } from './latestRuns';
import { latestRuns as data } from './data/runs/latestRuns';
import { RunsRecord } from '../../main/js/components/records.jsx';
import Branches from '../../main/js/components/Branches.jsx';

View File

@ -0,0 +1,605 @@
export default [{
"_class": "io.jenkins.blueocean.service.embedded.rest.BranchImpl",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/"
},
"actions": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/actions/"
},
"runs": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/"
},
"queue": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/queue/"
}
},
"actions": [{
"_class": "com.cloudbees.plugins.credentials.ViewCredentialsAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/credentials/"
}
},
"stores": {},
"urlName": "credentials"
}],
"displayName": "master",
"estimatedDurationInMillis": 39110,
"fullName": "tfprdemo/master",
"lastSuccessfulRun": "http://localhost:8080/jenkins/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/",
"latestRun": {
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineRunImpl",
"_links": {
"nodes": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/nodes/"
},
"log": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/log/"
},
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/"
},
"actions": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/actions/"
},
"steps": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/steps/"
}
},
"actions": [{
"_class": "hudson.model.CauseAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/cause/"
}
},
"causes": [{"_class": "jenkins.branch.BranchIndexingCause", "shortDescription": "Branch indexing"}],
"urlName": "cause"
}, {
"_class": "jenkins.metrics.impl.TimeInQueueAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/timings/"
}
},
"queuingDurationMillis": 2,
"totalDurationMillis": 42108,
"urlName": "timings"
}, {
"_class": "hudson.plugins.git.util.BuildData",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/git/"
}
},
"buildsByBranchName": {
"master": {
"_class": "hudson.plugins.git.util.Build",
"buildNumber": 3,
"buildResult": null,
"marked": {
"SHA1": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64",
"branch": [{"SHA1": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64", "name": "master"}]
},
"revision": {
"SHA1": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64",
"branch": [{"SHA1": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64", "name": "master"}]
}
}
},
"lastBuiltRevision": {
"SHA1": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64",
"branch": [{"SHA1": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64", "name": "master"}]
},
"remoteUrls": ["https://github.com/TomTestOrg/PR-demo.git"],
"scmName": "",
"urlName": "git"
}, {
"_class": "hudson.plugins.git.GitTagAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/tagBuild/"
}
},
"tags": [],
"urlName": "tagBuild"
}, {
"_class": "hudson.plugins.git.util.BuildData",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/git/"
}
},
"buildsByBranchName": {
"master": {
"_class": "hudson.plugins.git.util.Build",
"buildNumber": 3,
"buildResult": null,
"marked": {
"SHA1": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64",
"branch": [{"SHA1": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64", "name": "master"}]
},
"revision": {
"SHA1": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64",
"branch": [{"SHA1": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64", "name": "master"}]
}
}
},
"lastBuiltRevision": {
"SHA1": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64",
"branch": [{"SHA1": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64", "name": "master"}]
},
"remoteUrls": ["https://github.com/TomTestOrg/PR-demo.git"],
"scmName": "",
"urlName": "git"
}, {
"_class": "hudson.plugins.git.GitTagAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/tagBuild/"
}
},
"tags": [],
"urlName": "tagBuild"
}, {
"_class": "org.jenkinsci.plugins.workflow.job.views.FlowGraphAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/flowGraph/"
}
},
"nodes": [{"_class": "org.jenkinsci.plugins.workflow.graph.FlowStartNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode"}, {"_class": "org.jenkinsci.plugins.workflow.graph.FlowEndNode"}],
"urlName": "flowGraph"
}],
"artifacts": [],
"changeSet": [{
"_class": "io.jenkins.blueocean.service.embedded.rest.ChangeSetResource",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/changeset/7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64/"
}
},
"author": {
"_class": "io.jenkins.blueocean.service.embedded.rest.UserImpl",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/users/noreply/"
}
},
"email": "noreply@github.com",
"fullName": "noreply",
"id": "noreply"
},
"affectedPaths": ["Jenkinsfile"],
"commitId": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64",
"comment": "Update Jenkinsfile\n",
"date": "2016-07-02 18:24:38 +0100",
"id": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64",
"msg": "Update Jenkinsfile",
"paths": [{"editType": "edit", "file": "Jenkinsfile"}],
"timestamp": "2016-07-02T18:24:38.000+0100"
}],
"durationInMillis": 42106,
"enQueueTime": "2016-07-02T18:25:06.061+0100",
"endTime": "2016-07-02T18:25:48.169+0100",
"estimatedDurationInMillis": 39110,
"id": "3",
"organization": "jenkins",
"pipeline": "master",
"result": "SUCCESS",
"runSummary": "stable",
"startTime": "2016-07-02T18:25:06.063+0100",
"state": "FINISHED",
"steps": [{
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineStepImpl",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/steps/3/"
}
}
}, {
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineStepImpl",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/steps/6/"
}
}
}, {
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineStepImpl",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/steps/7/"
}
}
}, {
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineStepImpl",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/steps/10/"
}
}
}, {
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineStepImpl",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/master/runs/3/steps/11/"
}
}
}],
"type": "WorkflowRun",
"commitId": "7d7e6b1589bf8be0ffbdd99bb3fd79581f0e7c64"
},
"name": "master",
"organization": "jenkins",
"weatherScore": 100,
"pullRequest": null
}, {
"_class": "io.jenkins.blueocean.service.embedded.rest.BranchImpl",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/"
},
"actions": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/actions/"
},
"runs": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/"
},
"queue": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/queue/"
}
},
"actions": [{
"_class": "com.cloudbees.plugins.credentials.ViewCredentialsAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/credentials/"
}
},
"stores": {},
"urlName": "credentials"
}],
"displayName": "quicker",
"estimatedDurationInMillis": 18710,
"fullName": "tfprdemo/quicker",
"lastSuccessfulRun": "http://localhost:8080/jenkins/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/",
"latestRun": {
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineRunImpl",
"_links": {
"nodes": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/nodes/"
},
"log": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/log/"
},
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/"
},
"actions": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/actions/"
},
"steps": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/steps/"
}
},
"actions": [{
"_class": "hudson.model.CauseAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/cause/"
}
},
"causes": [{"_class": "jenkins.branch.BranchIndexingCause", "shortDescription": "Branch indexing"}],
"urlName": "cause"
}, {
"_class": "jenkins.metrics.impl.TimeInQueueAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/timings/"
}
},
"queuingDurationMillis": 2,
"totalDurationMillis": 18712,
"urlName": "timings"
}, {
"_class": "hudson.plugins.git.util.BuildData",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/git/"
}
},
"buildsByBranchName": {
"quicker": {
"_class": "hudson.plugins.git.util.Build",
"buildNumber": 1,
"buildResult": null,
"marked": {
"SHA1": "ffd80d68b6c3bc7552feff5a7b77525625500a7c",
"branch": [{"SHA1": "ffd80d68b6c3bc7552feff5a7b77525625500a7c", "name": "quicker"}]
},
"revision": {
"SHA1": "ffd80d68b6c3bc7552feff5a7b77525625500a7c",
"branch": [{"SHA1": "ffd80d68b6c3bc7552feff5a7b77525625500a7c", "name": "quicker"}]
}
}
},
"lastBuiltRevision": {
"SHA1": "ffd80d68b6c3bc7552feff5a7b77525625500a7c",
"branch": [{"SHA1": "ffd80d68b6c3bc7552feff5a7b77525625500a7c", "name": "quicker"}]
},
"remoteUrls": ["https://github.com/TomTestOrg/PR-demo.git"],
"scmName": "",
"urlName": "git"
}, {
"_class": "hudson.plugins.git.GitTagAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/tagBuild/"
}
},
"tags": [],
"urlName": "tagBuild"
}, {
"_class": "hudson.plugins.git.util.BuildData",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/git/"
}
},
"buildsByBranchName": {
"quicker": {
"_class": "hudson.plugins.git.util.Build",
"buildNumber": 1,
"buildResult": null,
"marked": {
"SHA1": "ffd80d68b6c3bc7552feff5a7b77525625500a7c",
"branch": [{"SHA1": "ffd80d68b6c3bc7552feff5a7b77525625500a7c", "name": "quicker"}]
},
"revision": {
"SHA1": "ffd80d68b6c3bc7552feff5a7b77525625500a7c",
"branch": [{"SHA1": "ffd80d68b6c3bc7552feff5a7b77525625500a7c", "name": "quicker"}]
}
}
},
"lastBuiltRevision": {
"SHA1": "ffd80d68b6c3bc7552feff5a7b77525625500a7c",
"branch": [{"SHA1": "ffd80d68b6c3bc7552feff5a7b77525625500a7c", "name": "quicker"}]
},
"remoteUrls": ["https://github.com/TomTestOrg/PR-demo.git"],
"scmName": "",
"urlName": "git"
}, {
"_class": "hudson.plugins.git.GitTagAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/tagBuild/"
}
},
"tags": [],
"urlName": "tagBuild"
}, {
"_class": "org.jenkinsci.plugins.workflow.job.views.FlowGraphAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/flowGraph/"
}
},
"nodes": [{"_class": "org.jenkinsci.plugins.workflow.graph.FlowStartNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode"}, {"_class": "org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode"}, {"_class": "org.jenkinsci.plugins.workflow.graph.FlowEndNode"}],
"urlName": "flowGraph"
}],
"artifacts": [],
"changeSet": [],
"durationInMillis": 18710,
"enQueueTime": "2016-07-01T17:20:23.071+0100",
"endTime": "2016-07-01T17:20:41.781+0100",
"estimatedDurationInMillis": 18710,
"id": "1",
"organization": "jenkins",
"pipeline": "quicker",
"result": "SUCCESS",
"runSummary": "stable",
"startTime": "2016-07-01T17:20:23.071+0100",
"state": "FINISHED",
"steps": [{
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineStepImpl",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/steps/3/"
}
}
}, {
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineStepImpl",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/steps/6/"
}
}
}, {
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineStepImpl",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/steps/7/"
}
}
}, {
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineStepImpl",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/steps/10/"
}
}
}, {
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineStepImpl",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/quicker/runs/1/steps/11/"
}
}
}],
"type": "WorkflowRun",
"commitId": "ffd80d68b6c3bc7552feff5a7b77525625500a7c"
},
"name": "quicker",
"organization": "jenkins",
"weatherScore": 100,
"pullRequest": null
}, {
"_class": "io.jenkins.blueocean.service.embedded.rest.BranchImpl",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/tfennelly-patch-1/"
},
"actions": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/tfennelly-patch-1/actions/"
},
"runs": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/tfennelly-patch-1/runs/"
},
"queue": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/tfennelly-patch-1/queue/"
}
},
"actions": [{
"_class": "com.cloudbees.plugins.credentials.ViewCredentialsAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/tfennelly-patch-1/credentials/"
}
},
"stores": {},
"urlName": "credentials"
}],
"displayName": "tfennelly-patch-1",
"estimatedDurationInMillis": -1,
"fullName": "tfprdemo/tfennelly-patch-1",
"lastSuccessfulRun": null,
"latestRun": {
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineRunImpl",
"_links": {
"nodes": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/tfennelly-patch-1/runs/1/nodes/"
},
"log": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/tfennelly-patch-1/runs/1/log/"
},
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/tfennelly-patch-1/runs/1/"
},
"actions": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/tfennelly-patch-1/runs/1/actions/"
},
"steps": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/tfennelly-patch-1/runs/1/steps/"
}
},
"actions": [{
"_class": "hudson.model.CauseAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/tfennelly-patch-1/runs/1/cause/"
}
},
"causes": [{"_class": "jenkins.branch.BranchIndexingCause", "shortDescription": "Branch indexing"}],
"urlName": "cause"
}, {
"_class": "jenkins.metrics.impl.TimeInQueueAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/tfennelly-patch-1/runs/1/timings/"
}
},
"queuingDurationMillis": 2,
"totalDurationMillis": 2,
"urlName": "timings"
}, {
"_class": "org.jenkinsci.plugins.workflow.job.views.FlowGraphAction",
"_links": {
"self": {
"_class": "io.jenkins.blueocean.rest.hal.Link",
"href": "/blue/rest/organizations/jenkins/pipelines/tfprdemo/branches/tfennelly-patch-1/runs/1/flowGraph/"
}
},
"nodes": [],
"urlName": "flowGraph"
}],
"artifacts": [],
"changeSet": [],
"durationInMillis": 0,
"enQueueTime": "2016-07-04T16:13:59.226+0100",
"endTime": null,
"estimatedDurationInMillis": -1,
"id": "1",
"organization": "jenkins",
"pipeline": "tfennelly-patch-1",
"result": "UNKNOWN",
"runSummary": "?",
"startTime": "2016-07-04T16:13:59.227+0100",
"state": "QUEUED",
"steps": [],
"type": "WorkflowRun",
"commitId": null
},
"name": "tfennelly-patch-1",
"organization": "jenkins",
"weatherScore": 100,
"pullRequest": null
}]

View File

@ -0,0 +1,14 @@
export default {
"jenkins_object_type": "org.jenkinsci.plugins.workflow.job.WorkflowJob",
"job_name": "tfprdemo/tfennelly-patch-1",
"jenkins_org": "jenkins",
"job_ismultibranch": "true",
"jenkins_object_name": "tfprdemo/tfennelly-patch-1",
"blueocean_job_rest_url": "/rest/organizations/jenkins/pipelines/tfprdemo/branches/tfennelly-patch-1/",
"blueocean_job_branch_name": "tfennelly-patch-1",
"jenkins_event": "job_crud_created",
"blueocean_job_pipeline_name": "tfprdemo",
"jenkins_object_url": "job/tfprdemo/job/tfennelly-patch-1/",
"jenkins_channel": "job",
"blueocean_is_for_current_job": true
};

View File

@ -5,7 +5,7 @@ import sd from 'skin-deep';
import PullRequest from '../../main/js/components/PullRequest.jsx';
import { RunsRecord } from '../../main/js/components/records.jsx';
import { latestRuns as data } from './latestRuns';
import { latestRuns as data } from './data/runs/latestRuns';
const pr = data.filter((run) => run.pullRequest);

View File

@ -1,7 +1,7 @@
import React from 'react';
import { assert} from 'chai';
import { shallow } from 'enzyme';
import { latestRuns as branches } from './latestRuns';
import { latestRuns as branches } from './data/runs/latestRuns';
import {PullRequests} from '../../main/js/components/PullRequests.jsx';

View File

@ -26,236 +26,237 @@ const CONFIG = {
};
const originalFetchJson = actions.fetchJson;
try {
describe("push events - queued run tests", () => {
describe("push events - queued run tests", () => {
afterEach(() => {
actions.fetchJson = originalFetchJson;
});
// Test queued event for when the event is for the pipeline that
// the user is actually "currently" looking at.
it("currently displayed pipeline", () => {
const event = newEvent('job_run_queue_enter');
const dispatcher = actions.actions.processJobQueuedEvent(event);
// Test queued event for when the event is for the pipeline that
// the user is actually "currently" looking at.
it("currently displayed pipeline", () => {
const event = newEvent('job_run_queue_enter');
const dispatcher = actions.actions.processJobQueuedEvent(event);
// mimic invocation of this action dispatcher and inspect the
// actualDispatchObj passed to the dispatch function
const dispatchedEvents = [];
dispatcher(function(actualDispatchObj) {
//console.log(actualDispatchObj);
dispatchedEvents.push(actualDispatchObj);
}, function() {
return {
adminStore: {
runs: {
'PR-demo': []
}
// mimic invocation of this action dispatcher and inspect the
// actualDispatchObj passed to the dispatch function
const dispatchedEvents = [];
dispatcher(function(actualDispatchObj) {
//console.log(actualDispatchObj);
dispatchedEvents.push(actualDispatchObj);
}, function() {
return {
adminStore: {
runs: {
'PR-demo': []
}
}
});
// The queued event should get dispatched twice:
// 1. To update the redux store's view of the set of 'currentRuns' because
// the event is associated with the currently active pipeline.
// 2. To update the redux store's view of the set of runs associated with the
// pipeline itself. Every time the user navs to a pipeline, the runs for
// that pipeline are cached in the redux store. This dispatch updates
// that state.
assert.equal(dispatchedEvents.length, 2);
assert.equal(dispatchedEvents[0].type, actions.ACTION_TYPES.SET_CURRENT_RUN_DATA);
assert.equal(dispatchedEvents[0].payload.length, 1);
assert.equal(dispatchedEvents[0].payload[0].pipeline, 'quicker');
assert.equal(dispatchedEvents[0].payload[0].state, 'QUEUED');
assert.equal(dispatchedEvents[1].type, actions.ACTION_TYPES.SET_RUNS_DATA);
assert.equal(dispatchedEvents[1].id, 'PR-demo');
assert.equal(dispatchedEvents[1].payload.length, 1);
assert.equal(dispatchedEvents[1].payload[0].pipeline, 'quicker');
assert.equal(dispatchedEvents[1].payload[0].state, 'QUEUED');
}
});
// Test queued event for when the event is for a different pipeline to
// the one that the user is actually "currently" looking at.
it("not currently displayed pipeline", () => {
const event = newEvent('job_run_queue_enter');
// The queued event should get dispatched twice:
// 1. To update the redux store's view of the set of 'currentRuns' because
// the event is associated with the currently active pipeline.
// 2. To update the redux store's view of the set of runs associated with the
// pipeline itself. Every time the user navs to a pipeline, the runs for
// that pipeline are cached in the redux store. This dispatch updates
// that state.
assert.equal(dispatchedEvents.length, 2);
assert.equal(dispatchedEvents[0].type, actions.ACTION_TYPES.SET_CURRENT_RUN_DATA);
assert.equal(dispatchedEvents[0].payload.length, 1);
assert.equal(dispatchedEvents[0].payload[0].pipeline, 'quicker');
assert.equal(dispatchedEvents[0].payload[0].state, 'QUEUED');
assert.equal(dispatchedEvents[1].type, actions.ACTION_TYPES.SET_RUNS_DATA);
assert.equal(dispatchedEvents[1].id, 'PR-demo');
assert.equal(dispatchedEvents[1].payload.length, 1);
assert.equal(dispatchedEvents[1].payload[0].pipeline, 'quicker');
assert.equal(dispatchedEvents[1].payload[0].state, 'QUEUED');
});
// Test queued event for when the event is for a different pipeline to
// the one that the user is actually "currently" looking at.
it("not currently displayed pipeline", () => {
const event = newEvent('job_run_queue_enter');
// modify the event to turn off the blueocean_is_for_current_job flag.
// This should result in just one dispatch.
event.blueocean_is_for_current_job = false;
const dispatcher = actions.actions.processJobQueuedEvent(event);
// mimic invocation of this action dispatcher and inspect the
// actualDispatchObj passed to the dispatch function
const dispatchedEvents = [];
dispatcher(function(actualDispatchObj) {
// console.log(actualDispatchObj);
dispatchedEvents.push(actualDispatchObj);
}, function() {
return {
adminStore: {
runs: {
'PR-demo': []
}
}
}
});
// The queued event should get dispatched once only i.e. to update the
// "global" run state. See the previous test comments for more details.
assert.equal(dispatchedEvents.length, 1);
assert.equal(dispatchedEvents[0].type, actions.ACTION_TYPES.SET_RUNS_DATA);
assert.equal(dispatchedEvents[0].id, 'PR-demo');
assert.equal(dispatchedEvents[0].payload.length, 1);
assert.equal(dispatchedEvents[0].payload[0].pipeline, 'quicker');
assert.equal(dispatchedEvents[0].payload[0].state, 'QUEUED');
});
// Test queued event is ignored if already received. This can happen because
// there are multiple events relating to the run queue lifecycle.
it("ignore multiple events with same queueId", () => {
// mimic invocation of this action dispatcher and inspect the
// actualDispatchObj passed to the dispatch function
const adminStore = {runs: { 'PR-demo': [] } };
function fireEvent() {
const event = newEvent('job_run_queue_enter');
// modify the event to turn off the blueocean_is_for_current_job flag.
// This should result in just one dispatch.
// This should result in max of one dispatch per call to dispatcher.
event.blueocean_is_for_current_job = false;
const dispatcher = actions.actions.processJobQueuedEvent(event);
dispatcher(function (actualDispatchObj) {
adminStore.runs['PR-demo'] = actualDispatchObj.payload;
}, function () {
return {
adminStore: adminStore
}
});
}
// fire the dispatcher for the first time...
fireEvent();
// Should have been dispatched i.e. count === 1 ...
assert.equal(adminStore.runs['PR-demo'].length, 1);
// fire the dispatcher a second time (same event)...
fireEvent();
// Should not have been dispatched i.e. count
// should still be 1 ...
assert.equal(adminStore.runs['PR-demo'].length, 1);
});
});
describe("push events - started run tests", () => {
afterEach(() => {
actions.fetchJson = originalFetchJson;
});
// Test run started event for when the event is for the pipeline that
// the user is actually "currently" looking at.
it("run fetch ok", () => {
// Mimic the run being in the queued state before the start
const adminStore = {
runs: {
'PR-demo': [{
job_run_queueId: '12',
pipeline: 'quicker',
state: 'QUEUED',
result: 'UNKNOWN'
}]
}
};
function fireEvent() {
const event = newEvent('job_run_started');
event.blueocean_is_for_current_job = false;
const dispatcher = actions.actions.processJobQueuedEvent(event);
// Mock the fetchJson
actions.fetchJson = function(url, onSuccess, onError) {
assert.equal(url, '/jenkins/rest/organizations/jenkins/pipelines/PR-demo/branches/quicker/runs/12');
onSuccess({
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineRunImpl",
"artifacts": [],
"changeSet": [],
"durationInMillis": 0,
"enQueueTime": "2016-05-19T22:05:39.301+0100",
"endTime": null,
"estimatedDurationInMillis": 17882,
"id": "12",
"organization": "jenkins",
"pipeline": "quicker",
"result": "UNKNOWN",
"runSummary": "?",
"startTime": "2016-05-19T22:05:39.303+0100",
"state": "RUNNING",
"type": "WorkflowRun",
"commitId": null
});
};
// mimic invocation of this action dispatcher and inspect the
// actualDispatchObj passed to the dispatch function
const dispatchedEvents = [];
dispatcher(function(actualDispatchObj) {
// console.log(actualDispatchObj);
dispatchedEvents.push(actualDispatchObj);
}, function() {
const dispatcher = actions.actions.updateRunState(event, CONFIG, true);
dispatcher(function (actualDispatchObj) {
adminStore.runs['PR-demo'] = actualDispatchObj.payload;
}, function () {
return {
adminStore: {
runs: {
'PR-demo': []
}
}
adminStore: adminStore
}
});
}
// The queued event should get dispatched once only i.e. to update the
// "global" run state. See the previous test comments for more details.
assert.equal(dispatchedEvents.length, 1);
assert.equal(dispatchedEvents[0].type, actions.ACTION_TYPES.SET_RUNS_DATA);
assert.equal(dispatchedEvents[0].id, 'PR-demo');
assert.equal(dispatchedEvents[0].payload.length, 1);
assert.equal(dispatchedEvents[0].payload[0].pipeline, 'quicker');
assert.equal(dispatchedEvents[0].payload[0].state, 'QUEUED');
});
// Fire the start event and then check that the run state
// has changed as expected.
fireEvent();
// Test queued event is ignored if already received. This can happen because
// there are multiple events relating to the run queue lifecycle.
it("ignore multiple events with same queueId", () => {
// mimic invocation of this action dispatcher and inspect the
// actualDispatchObj passed to the dispatch function
const adminStore = {runs: { 'PR-demo': [] } };
function fireEvent() {
const event = newEvent('job_run_queue_enter');
// modify the event to turn off the blueocean_is_for_current_job flag.
// This should result in max of one dispatch per call to dispatcher.
event.blueocean_is_for_current_job = false;
const dispatcher = actions.actions.processJobQueuedEvent(event);
dispatcher(function (actualDispatchObj) {
adminStore.runs['PR-demo'] = actualDispatchObj.payload;
}, function () {
return {
adminStore: adminStore
}
});
}
// fire the dispatcher for the first time...
fireEvent();
// Should have been dispatched i.e. count === 1 ...
assert.equal(adminStore.runs['PR-demo'].length, 1);
// fire the dispatcher a second time (same event)...
fireEvent();
// Should not have been dispatched i.e. count
// should still be 1 ...
assert.equal(adminStore.runs['PR-demo'].length, 1);
});
var runs = adminStore.runs['PR-demo'];
assert.equal(runs.length, 1);
assert.equal(runs[0].enQueueTime, '2016-05-19T22:05:39.301+0100');
assert.equal(runs[0].state, 'RUNNING');
});
describe("push events - started run tests", () => {
it("run fetch failed", () => {
// Mimic the run being in the queued state before the start
const adminStore = {
runs: {
'PR-demo': [{
job_run_queueId: '12',
pipeline: 'quicker',
state: 'QUEUED',
result: 'UNKNOWN'
}]
}
};
// Test run started event for when the event is for the pipeline that
// the user is actually "currently" looking at.
it("run fetch ok", () => {
// Mimic the run being in the queued state before the start
const adminStore = {
runs: {
'PR-demo': [{
job_run_queueId: '12',
pipeline: 'quicker',
state: 'QUEUED',
result: 'UNKNOWN'
}]
}
function fireEvent() {
const event = newEvent('job_run_started');
event.blueocean_is_for_current_job = false;
// Mock the fetchJson
actions.fetchJson = function(url, onSuccess, onError) {
assert.equal(url, '/jenkins/rest/organizations/jenkins/pipelines/PR-demo/branches/quicker/runs/12');
onError({});
};
function fireEvent() {
const event = newEvent('job_run_started');
event.blueocean_is_for_current_job = false;
const dispatcher = actions.actions.updateRunState(event, CONFIG, true);
// Mock the fetchJson
actions.fetchJson = function(url, onSuccess, onError) {
assert.equal(url, '/jenkins/rest/organizations/jenkins/pipelines/PR-demo/branches/quicker/runs/12');
onSuccess({
"_class": "io.jenkins.blueocean.service.embedded.rest.PipelineRunImpl",
"artifacts": [],
"changeSet": [],
"durationInMillis": 0,
"enQueueTime": "2016-05-19T22:05:39.301+0100",
"endTime": null,
"estimatedDurationInMillis": 17882,
"id": "12",
"organization": "jenkins",
"pipeline": "quicker",
"result": "UNKNOWN",
"runSummary": "?",
"startTime": "2016-05-19T22:05:39.303+0100",
"state": "RUNNING",
"type": "WorkflowRun",
"commitId": null
});
};
const dispatcher = actions.actions.updateRunState(event, CONFIG, true);
dispatcher(function (actualDispatchObj) {
adminStore.runs['PR-demo'] = actualDispatchObj.payload;
}, function () {
return {
adminStore: adminStore
}
});
}
// Fire the start event and then check that the run state
// has changed as expected.
fireEvent();
var runs = adminStore.runs['PR-demo'];
assert.equal(runs.length, 1);
assert.equal(runs[0].enQueueTime, '2016-05-19T22:05:39.301+0100');
assert.equal(runs[0].state, 'RUNNING');
});
it("run fetch failed", () => {
// Mimic the run being in the queued state before the start
const adminStore = {
runs: {
'PR-demo': [{
job_run_queueId: '12',
pipeline: 'quicker',
state: 'QUEUED',
result: 'UNKNOWN'
}]
dispatcher(function (actualDispatchObj) {
adminStore.runs['PR-demo'] = actualDispatchObj.payload;
}, function () {
return {
adminStore: adminStore
}
};
});
}
function fireEvent() {
const event = newEvent('job_run_started');
event.blueocean_is_for_current_job = false;
// Fire the start event and then check that the run state
// has changed as expected .
fireEvent();
// Mock the fetchJson
actions.fetchJson = function(url, onSuccess, onError) {
assert.equal(url, '/jenkins/rest/organizations/jenkins/pipelines/PR-demo/branches/quicker/runs/12');
onError({});
};
const dispatcher = actions.actions.updateRunState(event, CONFIG, true);
dispatcher(function (actualDispatchObj) {
adminStore.runs['PR-demo'] = actualDispatchObj.payload;
}, function () {
return {
adminStore: adminStore
}
});
}
// Fire the start event and then check that the run state
// has changed as expected .
fireEvent();
var runs = adminStore.runs['PR-demo'];
assert.equal(runs.length, 1);
// This time, the run state should have changed as expected
// because we do it manually when the fetch fails, but we don't
// see the time changes etc.
assert.equal(runs[0].enQueueTime, undefined);
assert.equal(runs[0].state, 'RUNNING');
});
var runs = adminStore.runs['PR-demo'];
assert.equal(runs.length, 1);
// This time, the run state should have changed as expected
// because we do it manually when the fetch fails, but we don't
// see the time changes etc.
assert.equal(runs[0].enQueueTime, undefined);
assert.equal(runs[0].state, 'RUNNING');
});
} finally {
actions.fetchJson = originalFetchJson;
}
});

View File

@ -2,7 +2,7 @@ import { assert } from 'chai';
import React from 'react';
import sd from 'skin-deep';
import { latestRuns } from './latestRuns';
import { latestRuns } from './data/runs/latestRuns';
import RunDetailsArtifacts from '../../main/js/components/RunDetailsArtifacts';
describe('RunDetailsArtifacts', () => {

View File

@ -2,7 +2,7 @@ import { assert } from 'chai';
import React from 'react';
import sd from 'skin-deep';
import { latestRuns } from './latestRuns';
import { latestRuns } from './data/runs/latestRuns';
import RunDetailsChanges from '../../main/js/components/RunDetailsChanges';
describe('RunDetailsChanges', () => {

View File

@ -11,15 +11,21 @@ import {
currentRuns as currentRunsSelector,
} from '../../main/js/redux';
import { pipelines } from './pipelines';
import { latestRuns } from './latestRuns';
import { latestRuns } from './data/runs/latestRuns';
import job_crud_created_multibranch from './data/sse/job_crud_created_multibranch';
import fetchedBranches from './data/branches/latestBranches';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe("Store should work", () => {
import * as actionsModule from '../../main/js/redux/actions';
const actionsFetch = actionsModule.fetchJson;
describe("Redux Store - ", () => {
afterEach(() => {
nock.cleanAll()
})
nock.cleanAll();
actionsModule.fetchJson = actionsFetch;
});
it("create store with pipeline data", () => {
var ruleId = '/rest/organizations/jenkins/pipelines/';
nock('http://example.com')
@ -55,6 +61,77 @@ describe("Store should work", () => {
assert.equal(store.getActions()[0].type, 'CLEAR_CURRENT_RUN_DATA');
});
});
const multi_branch_job_crud_job_created = (job_crud_sse_event) => {
// Mock the fetching of the latest branches from the REST API.
actionsModule.fetchJson = function (url, onSuccess) {
onSuccess(fetchedBranches);
};
const actionFunc = actions.updateBranchList(job_crud_sse_event, {
getAppURLBase() {
return 'http://example.com';
}
});
const dispatches = [];
actionFunc((dispatchConfig) => {
dispatches.push(dispatchConfig);
}, () => {
// fetchedBranches is a 3 branch array. First 2 branches
// are older branches (what's in the store) and the 3rd branch is
// the new branch relating to the sse event.
const currentStoreBranches = [
fetchedBranches[0],
fetchedBranches[1]
];
return {
adminStore: {
branches: {
tfprdemo: currentStoreBranches
}
}
};
});
//console.log('------------------');
//console.log(dispatches);
//console.log('------------------');
return dispatches;
};
it("multi-branch job_crud_job_created blueocean_is_for_current_job=true", () => {
const dispatches = multi_branch_job_crud_job_created(job_crud_created_multibranch);
// Should be 2 events dispatched because
// blueocean_is_for_current_job=true
assert.equal(dispatches.length, 2);
assert.equal(dispatches[0].id, 'tfprdemo');
assert.equal(dispatches[0].type, 'SET_CURRENT_BRANCHES_DATA');
assert.equal(dispatches[0].payload.length, 3); // 3 branches as returned by the fetch
assert.equal(dispatches[1].id, 'tfprdemo');
assert.equal(dispatches[1].type, 'SET_BRANCHES_DATA');
assert.equal(dispatches[1].payload.length, 3); // 3 branches as returned by the fetch
});
it("multi-branch job_crud_job_created blueocean_is_for_current_job=false", () => {
// Copy the job_crud_created_multibranch event object and modify the
// blueocean_is_for_current_job property to false. This switch off
// one of the event dispatches.
const sse_event = Object.assign({}, job_crud_created_multibranch);
sse_event.blueocean_is_for_current_job = false;
const dispatches = multi_branch_job_crud_job_created(sse_event);
// Should only be 1 event dispatched because
// blueocean_is_for_current_job=false
assert.equal(dispatches.length, 1);
assert.equal(dispatches[0].id, 'tfprdemo');
assert.equal(dispatches[0].type, 'SET_BRANCHES_DATA');
assert.equal(dispatches[0].payload.length, 3); // 3 branches as returned by the fetch
});
});