diff --git a/awx/ui/client/src/job-results/job-results.controller.js b/awx/ui/client/src/job-results/job-results.controller.js index 9b60a7fbd7..396debbcc1 100644 --- a/awx/ui/client/src/job-results/job-results.controller.js +++ b/awx/ui/client/src/job-results/job-results.controller.js @@ -1,7 +1,8 @@ -export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', 'eventQueue', '$compile', '$log', 'Dataset', '$q', 'Rest', '$state', 'QuerySet', '$rootScope', 'moment', '$stateParams', 'i18n', 'fieldChoices', 'fieldLabels', -function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, eventQueue, $compile, $log, Dataset, $q, Rest, $state, QuerySet, $rootScope, moment, $stateParams, i18n, fieldChoices, fieldLabels) { +export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', 'eventQueue', '$compile', '$log', 'Dataset', '$q', 'Rest', '$state', 'QuerySet', '$rootScope', 'moment', '$stateParams', 'i18n', 'fieldChoices', 'fieldLabels', 'workflowResultsService', +function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, eventQueue, $compile, $log, Dataset, $q, Rest, $state, QuerySet, $rootScope, moment, $stateParams, i18n, fieldChoices, fieldLabels, workflowResultsService) { var toDestroy = []; var cancelRequests = false; + var runTimeElapsedTimer = null; // download stdout tooltip text $scope.standardOutTooltip = i18n._('Download Output'); @@ -251,15 +252,15 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy $scope.events = {}; + function updateJobElapsedTimer(time) { + $scope.job.elapsed = time; + } + // For elapsed time while a job is running, compute the differnce in seconds, // from the time the job started until now. Moment() returns the current // time as a moment object. - var start = ($scope.job.started === null) ? moment() : moment($scope.job.started); - if(jobFinished === false){ - var elapsedInterval = setInterval(function(){ - let now = moment(); - $scope.job.elapsed = now.diff(start, 'seconds'); - }, 1000); + if ($scope.job.started !== null && $scope.job.status === 'running') { + runTimeElapsedTimer = workflowResultsService.createOneSecondTimer($scope.job.started, updateJobElapsedTimer); } // EVENT STUFF BELOW @@ -637,11 +638,13 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy parseInt($scope.job.id,10)) { // controller is defined, so set the job_status $scope.job_status = data.status; - if (data.status === "successful" || + if (data.status === "running") { + runTimeElapsedTimer = workflowResultsService.createOneSecondTimer(moment(), updateJobElapsedTimer); + } else if (data.status === "successful" || data.status === "failed" || data.status === "error" || data.status === "canceled") { - clearInterval(elapsedInterval); + workflowResultsService.destroyTimer(runTimeElapsedTimer); if (bufferInterval) { clearInterval(bufferInterval); } @@ -675,7 +678,7 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy $scope.events[v] = null; }); $scope.events = {}; - clearInterval(elapsedInterval); + workflowResultsService.destroyTimer(runTimeElapsedTimer); if (bufferInterval) { clearInterval(bufferInterval); } diff --git a/awx/ui/tests/spec/job-results/job-results.controller-test.js b/awx/ui/tests/spec/job-results/job-results.controller-test.js index 585d7d75e8..f0b7f3ecea 100644 --- a/awx/ui/tests/spec/job-results/job-results.controller-test.js +++ b/awx/ui/tests/spec/job-results/job-results.controller-test.js @@ -1,10 +1,11 @@ 'use strict'; +import moment from 'moment'; describe('Controller: jobResultsController', () => { // Setup let jobResultsController; - let jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, eventQueue, $compile, eventResolve, populateResolve, $rScope, q, $log, Dataset, Rest, $state, QuerySet, i18n,fieldChoices, fieldLabels; + let jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, eventQueue, $compile, eventResolve, populateResolve, $rScope, q, $log, Dataset, Rest, $state, QuerySet, i18n,fieldChoices, fieldLabels, $interval, workflowResultsService; jobData = { related: {} @@ -37,7 +38,8 @@ describe('Controller: jobResultsController', () => { 'deleteJob', 'cancelJob', 'relaunchJob', - 'getEvents' + 'getEvents', + 'getJobData', ]); eventQueue = jasmine.createSpyObj('eventQueue', [ 'populate', @@ -64,6 +66,10 @@ describe('Controller: jobResultsController', () => { } }; + $provide.service('workflowResultsService', () => { + return jasmine.createSpyObj('workflowResultsService', ['createOneSecondTimer', 'destroyTimer']); + }); + $provide.value('jobData', jobData); $provide.value('jobDataOptions', jobDataOptions); $provide.value('jobLabels', jobLabels); @@ -84,7 +90,7 @@ describe('Controller: jobResultsController', () => { }; let injectVals = () => { - angular.mock.inject((_jobData_, _jobDataOptions_, _jobLabels_, _jobFinished_, _count_, _ParseTypeChange_, _ParseVariableString_, _jobResultsService_, _eventQueue_, _$compile_, $rootScope, $controller, $q, $httpBackend, _$log_, _Dataset_, _Rest_, _$state_, _QuerySet_) => { + angular.mock.inject((_jobData_, _jobDataOptions_, _jobLabels_, _jobFinished_, _count_, _ParseTypeChange_, _ParseVariableString_, _jobResultsService_, _eventQueue_, _$compile_, $rootScope, $controller, $q, $httpBackend, _$log_, _Dataset_, _Rest_, _$state_, _QuerySet_, _$interval_, _workflowResultsService_) => { // when you call $scope.$apply() (which you need to do to // to get inside of .then blocks to test), something is // causing a request for all static files. @@ -119,12 +125,20 @@ describe('Controller: jobResultsController', () => { Rest = _Rest_; $state = _$state_; QuerySet = _QuerySet_; + $interval = _$interval_; + workflowResultsService = _workflowResultsService_; jobResultsService.getEvents.and .returnValue(eventResolve); eventQueue.populate.and .returnValue(populateResolve); + jobResultsService.getJobData = function(blah) { + var deferred = $q.defer(); + deferred.resolve({}); + return deferred.promise; + }; + $compile = _$compile_; jobResultsController = $controller('jobResultsController', { @@ -230,6 +244,57 @@ describe('Controller: jobResultsController', () => { }); }); + describe('elapsed timer', () => { + describe('job running', () => { + beforeEach(() => { + jobData.started = moment(); + jobData.status = 'running'; + + bootstrapTest(); + }); + + it('should start timer', () => { + expect(workflowResultsService.createOneSecondTimer).toHaveBeenCalled(); + }); + }); + + describe('job waiting', () => { + beforeEach(() => { + jobData.started = null; + jobData.status = 'waiting'; + + bootstrapTest(); + }); + + it('should not start timer', () => { + expect(workflowResultsService.createOneSecondTimer).not.toHaveBeenCalled(); + }); + }); + + describe('job transitions to running', () => { + beforeEach(() => { + jobData.started = null; + jobData.status = 'waiting'; + jobData.id = 13; + + bootstrapTest(); + + $rScope.$broadcast('ws-jobs', { unified_job_id: jobData.id, status: 'running' }); + }); + + it('should start timer', () => { + expect(workflowResultsService.createOneSecondTimer).toHaveBeenCalled(); + }); + + describe('job transitions from running to finished', () => { + it('should cleanup timer', () => { + $rScope.$broadcast('ws-jobs', { unified_job_id: jobData.id, status: 'successful' }); + expect(workflowResultsService.destroyTimer).toHaveBeenCalled(); + }); + }); + }); + }); + describe('extra vars stuff', () => { let extraVars = "foo";