diff --git a/app/scripts/modules/core/delivery/executionGroup/executionGroup.directive.js b/app/scripts/modules/core/delivery/executionGroup/executionGroup.directive.js index b15386765a9..1235802c2fb 100644 --- a/app/scripts/modules/core/delivery/executionGroup/executionGroup.directive.js +++ b/app/scripts/modules/core/delivery/executionGroup/executionGroup.directive.js @@ -95,7 +95,7 @@ module.exports = angular this.triggerPipeline = () => { $uibModal.open({ templateUrl: require('../manualExecution/manualPipelineExecution.html'), - controller: 'ManualPipelineExecutionCtrl as ctrl', + controller: 'ManualPipelineExecutionCtrl as vm', resolve: { pipeline: () => this.pipelineConfig, application: () => this.application, diff --git a/app/scripts/modules/core/delivery/executions/executions.controller.js b/app/scripts/modules/core/delivery/executions/executions.controller.js index 6b6d7998a03..35f15edf11d 100644 --- a/app/scripts/modules/core/delivery/executions/executions.controller.js +++ b/app/scripts/modules/core/delivery/executions/executions.controller.js @@ -128,7 +128,7 @@ module.exports = angular.module('spinnaker.core.delivery.executions.controller', this.triggerPipeline = () => { $uibModal.open({ templateUrl: require('../manualExecution/manualPipelineExecution.html'), - controller: 'ManualPipelineExecutionCtrl as ctrl', + controller: 'ManualPipelineExecutionCtrl as vm', resolve: { pipeline: () => null, application: () => this.application, diff --git a/app/scripts/modules/core/delivery/manualExecution/manualPipelineExecution.controller.js b/app/scripts/modules/core/delivery/manualExecution/manualPipelineExecution.controller.js index 8a7072da163..ee6293ccfb8 100644 --- a/app/scripts/modules/core/delivery/manualExecution/manualPipelineExecution.controller.js +++ b/app/scripts/modules/core/delivery/manualExecution/manualPipelineExecution.controller.js @@ -3,35 +3,34 @@ let angular = require('angular'); module.exports = angular.module('spinnaker.core.delivery.manualPipelineExecution.controller', [ + require('angular-ui-bootstrap'), require('../../utils/lodash.js'), require('../../pipeline/config/triggers/jenkins/jenkinsTrigger.module.js'), + require('../../ci/jenkins/igor.service.js'), ]) - .controller('ManualPipelineExecutionCtrl', function($scope, $filter, _, igorService, $modalInstance, pipeline, application) { + .controller('ManualPipelineExecutionCtrl', function (_, igorService, $modalInstance, pipeline, application) { - $scope.pipeline = pipeline; - - $scope.application = application; - - if (!pipeline) { - $scope.pipelineOptions = application.pipelineConfigs; - } - - $scope.command = { + this.command = { pipeline: pipeline, trigger: null, selectedBuild: null, }; + this.viewState = { + buildsLoading: true, + }; + let addTriggers = () => { - if (!$scope.command.pipeline) { - $scope.command.trigger = null; + if (!this.command.pipeline) { + this.command.trigger = null; return; } - $scope.triggers = _.chain($scope.command.pipeline.triggers) + this.triggers = _.chain(this.command.pipeline.triggers) .filter('type', 'jenkins') .sortBy('enabled') - .map(function (trigger) { + .reverse() + .map((trigger) => { var copy = _.clone(trigger); copy.buildNumber = null; copy.type = 'manual'; @@ -40,81 +39,90 @@ module.exports = angular.module('spinnaker.core.delivery.manualPipelineExecution }) .value(); - $scope.command.trigger = _.first($scope.triggers); - $scope.builds = []; + this.command.trigger = _.first(this.triggers); + this.builds = []; }; - $scope.viewState = { - triggering: false, - buildsLoading: true, - }; - $scope.triggerUpdated = function(trigger) { - $scope.viewState.buildsLoading = true; - let command = $scope.command; + /** + * Controller API + */ + + this.triggerUpdated = (trigger) => { + this.viewState.buildsLoading = true; + let command = this.command; if( trigger !== undefined ) { command.trigger = trigger; } if (command.trigger) { - $scope.viewState.buildsLoading = true; - igorService.listBuildsForJob(command.trigger.master, command.trigger.job).then(function(builds) { - $scope.builds = _.filter(builds, {building: false, result: 'SUCCESS'}); + this.viewState.buildsLoading = true; + igorService.listBuildsForJob(command.trigger.master, command.trigger.job).then((builds) => { + this.builds = _.filter(builds, {building: false, result: 'SUCCESS'}); if (!angular.isDefined(command.trigger.build)) { - command.selectedBuild = $scope.builds[0]; + command.selectedBuild = this.builds[0]; } - $scope.viewState.buildsLoading = false; + this.viewState.buildsLoading = false; }); } else { - $scope.builds = []; - $scope.viewState.buildsLoading = false; + this.builds = []; + this.viewState.buildsLoading = false; } }; - $scope.pipelineSelected = () => { - let pipeline = $scope.command.pipeline, + this.pipelineSelected = () => { + let pipeline = this.command.pipeline, executions = application.executions || []; - $scope.currentlyRunningExecutions = executions + this.currentlyRunningExecutions = executions .filter((execution) => execution.pipelineConfigId === pipeline.id && execution.isActive); addTriggers(); - $scope.triggerUpdated(); - if (pipeline.parameterConfig !== undefined && pipeline.parameterConfig.length){ - $scope.parameters = {}; - _.each(pipeline.parameterConfig, function(parameter) { - $scope.parameters[parameter.name] = parameter.default; - }); - } + this.triggerUpdated(); - }; + this.showRebakeOption = pipeline.stages.some((stage) => stage.type === 'bake'); + if (pipeline.parameterConfig !== undefined && pipeline.parameterConfig.length) { + this.parameters = {}; + pipeline.parameterConfig.forEach((parameter) => { + this.parameters[parameter.name] = parameter.default; + }); + } - $scope.updateSelectedBuild = function(item) { - $scope.command.selectedBuild = item; }; - this.cancel = function() { - $modalInstance.dismiss(); + this.updateSelectedBuild = (item) => { + this.command.selectedBuild = item; }; - this.execute = function() { - let selectedTrigger = $scope.command.trigger || {}, + this.execute = () => { + let selectedTrigger = this.command.trigger || {}, command = { trigger: selectedTrigger }, - pipeline = $scope.command.pipeline; + pipeline = this.command.pipeline; command.pipelineName = pipeline.name; - if (selectedTrigger && $scope.command.selectedBuild) { - selectedTrigger.buildNumber = $scope.command.selectedBuild.number; + if (selectedTrigger && this.command.selectedBuild) { + selectedTrigger.buildNumber = this.command.selectedBuild.number; } if (pipeline.parameterConfig !== undefined && pipeline.parameterConfig.length) { - selectedTrigger.parameters = $scope.parameters; + selectedTrigger.parameters = this.parameters; } $modalInstance.close(command); }; + this.cancel = $modalInstance.dismiss; + + + /** + * Initialization + */ + if (pipeline) { - $scope.pipelineSelected(); + this.pipelineSelected(); + } + + if (!pipeline) { + this.pipelineOptions = application.pipelineConfigs; } }).name; diff --git a/app/scripts/modules/core/delivery/manualExecution/manualPipelineExecution.controller.spec.js b/app/scripts/modules/core/delivery/manualExecution/manualPipelineExecution.controller.spec.js new file mode 100644 index 00000000000..9e4ca0628a9 --- /dev/null +++ b/app/scripts/modules/core/delivery/manualExecution/manualPipelineExecution.controller.spec.js @@ -0,0 +1,249 @@ +'use strict'; + + +describe('Controller: ManualPipelineExecution', function () { + + beforeEach( + window.module( + require('./manualPipelineExecution.controller') + ) + ); + + beforeEach(window.inject(function ($controller, $rootScope, igorService, _, $q) { + this.scope = $rootScope.$new(); + this.igorService = igorService; + this.$q = $q; + + this.initializeController = function(application, pipeline, modalInstance) { + this.ctrl = $controller('ManualPipelineExecutionCtrl', { + $scope: this.scope, + application: application, + pipeline: pipeline, + igorService: igorService, + _: _, + $modalInstance: modalInstance || {} + }); + }; + })); + + describe('Initialization', function () { + describe('No pipeline provided', function () { + it('sets pipeline options on controller from application', function () { + let application = { + pipelineConfigs: [ + { id: 'a' }, + { id: 'b' } + ] + }; + this.initializeController(application); + + expect(this.ctrl.pipelineOptions).toBe(application.pipelineConfigs); + }); + }); + + describe('Pipeline provided', function () { + it ('sets running executions on ctrl', function () { + let application = { + pipelineConfigs: [ + { id: 'a', triggers: [], stages: [] }, + { id: 'b' } + ], + executions: [ + { pipelineConfigId: 'a', isActive: false }, + { pipelineConfigId: 'a', isActive: true }, + { pipelineConfigId: 'b', isActive: true }, + ] + }; + + this.initializeController(application, application.pipelineConfigs[0]); + expect(this.ctrl.currentlyRunningExecutions).toEqual([application.executions[1]]); + }); + + it('adds jenkins trigger options to ctrl, setting description, overriding type, preferring enabled', function () { + let application = { + pipelineConfigs: [ + { + id: 'a', + triggers: [ + { type: 'jenkins', enabled: false, master: 'spinnaker', job: 'package'}, + { type: 'jenkins', enabled: true, master: 'spinnaker', job: 'test'}, + { type: 'other'} + ], + stages: [] + }, + ], + executions: [] + }; + let expected = [ + { type: 'manual', master: 'spinnaker', job: 'test', description: 'spinnaker: test', buildNumber: null, enabled: true }, + { type: 'manual', master: 'spinnaker', job: 'package', description: 'spinnaker: package', buildNumber: null, enabled: false } + ]; + + this.initializeController(application, application.pipelineConfigs[0]); + expect(this.ctrl.triggers).toEqual(expected); + expect(this.ctrl.command.trigger).toEqual(expected[0]); + }); + + it('includes completed, successful jobs for selected trigger', function () { + let builds = [ + { building: true, number: 5 }, + { building: false, result: 'FAILURE', number: 4 }, + { building: false, result: 'SUCCESS', number: 3 }, + { building: false, result: 'SUCCESS', number: 2 }, + ]; + spyOn(this.igorService, 'listBuildsForJob').and.returnValue(this.$q.when(builds)); + + let application = { + pipelineConfigs: [ + { + id: 'a', + triggers: [ { type: 'jenkins', enabled: true, master: 'spinnaker', job: 'test'} ], + stages: [] + }, + ], + executions: [] + }; + + this.initializeController(application, application.pipelineConfigs[0]); + expect(this.igorService.listBuildsForJob.calls.count()).toBe(1); + expect(this.ctrl.viewState.buildsLoading).toBe(true); + + this.scope.$digest(); + expect(this.ctrl.builds).toEqual([builds[2], builds[3]]); + expect(this.ctrl.command.selectedBuild).toEqual(builds[2]); + expect(this.ctrl.viewState.buildsLoading).toBe(false); + }); + + it('clears builds when trigger is unselected', function () { + spyOn(this.igorService, 'listBuildsForJob').and.returnValue(this.$q.when([ + { building: false, result: 'SUCCESS', number: 3 } + ])); + let application = { + pipelineConfigs: [ + { + id: 'a', + triggers: [ { type: 'jenkins', enabled: true, master: 'spinnaker', job: 'test'} ], + stages: [] + }, + ], + executions: [] + }; + + this.initializeController(application, application.pipelineConfigs[0]); + this.scope.$digest(); + expect(this.ctrl.builds.length).toBe(1); + this.ctrl.triggerUpdated(null); + expect(this.ctrl.builds.length).toBe(0); + }); + + it('sets showRebakeOption if any stage is a bake stage', function () { + let application = { + pipelineConfigs: [ + { + id: 'a', + triggers: [], + stages: [ {type: 'not-a-bake'}, {type: 'also-not-a-bake'}] + }, + { + id: 'b', + triggers: [], + stages: [ {type: 'not-a-bake'}, {type: 'bake'}] + }, + ], + executions: [] + }; + + this.initializeController(application, application.pipelineConfigs[0]); + expect(this.ctrl.showRebakeOption).toBe(false); + this.initializeController(application, application.pipelineConfigs[1]); + expect(this.ctrl.showRebakeOption).toBe(true); + }); + + it('sets parameters if present', function () { + let application = { + pipelineConfigs: [ + { + id: 'a', + triggers: [], + stages: [], + parameterConfig: [ + { name: 'foo' }, + { name: 'bar', 'default': 'mr. peanutbutter' }, + { name: 'baz', 'default': '' }, + { name: 'bojack', 'default': null } + ] + }, + ], + executions: [] + }; + + this.initializeController(application, application.pipelineConfigs[0]); + expect(this.ctrl.parameters).toEqual({foo: undefined, bar: 'mr. peanutbutter', baz: '', bojack: null}); + }); + }); + }); + + describe('execution', function () { + beforeEach(function () { + this.command = null; + this.modalInstance = { + close: (cmd) => { + this.command = cmd; + } + }; + }); + it('adds a placeholder trigger if none present', function () { + let application = { + pipelineConfigs: [ + { id: 'a', name: 'aa', triggers: [], stages: []}, + ], + executions: [] + }; + this.initializeController(application, application.pipelineConfigs[0], this.modalInstance); + + this.ctrl.execute(); + expect(this.command.trigger).toEqual({}); + }); + + it('adds parameters if configured', function () { + let application = { + pipelineConfigs: [ + { + id: 'a', + triggers: [], + stages: [], + parameterConfig: [ + { name: 'bar', 'default': 'mr. peanutbutter' }, + ] + }, + ], + executions: [] + }; + + this.initializeController(application, application.pipelineConfigs[0], this.modalInstance); + this.ctrl.execute(); + expect(this.command.trigger.parameters).toEqual({bar: 'mr. peanutbutter'}); + }); + + it('adds build number if selected', function () { + spyOn(this.igorService, 'listBuildsForJob').and.returnValue(this.$q.when([ + { building: false, result: 'SUCCESS', number: 3 } + ])); + let application = { + pipelineConfigs: [ + { + id: 'a', + triggers: [ { type: 'jenkins', enabled: true, master: 'spinnaker', job: 'test'} ], + stages: [] + }, + ], + executions: [] + }; + + this.initializeController(application, application.pipelineConfigs[0], this.modalInstance); + this.scope.$digest(); + this.ctrl.execute(); + expect(this.command.trigger.buildNumber).toBe(3); + }); + }); +}); diff --git a/app/scripts/modules/core/delivery/manualExecution/manualPipelineExecution.html b/app/scripts/modules/core/delivery/manualExecution/manualPipelineExecution.html index b11bd6bb799..da4010c07d7 100644 --- a/app/scripts/modules/core/delivery/manualExecution/manualPipelineExecution.html +++ b/app/scripts/modules/core/delivery/manualExecution/manualPipelineExecution.html @@ -2,40 +2,40 @@