diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue index 6e95e3d16f8e81a9c8933cade0e88040db3c4579..716bfa6d5fe8d6e9f8f814ba98860197ff31fe9f 100644 --- a/app/assets/javascripts/jobs/components/job_app.vue +++ b/app/assets/javascripts/jobs/components/job_app.vue @@ -14,6 +14,8 @@ import Log from './job_log.vue'; import LogTopBar from './job_log_controllers.vue'; import StuckBlock from './stuck_block.vue'; import Sidebar from './sidebar.vue'; +import { sprintf } from '~/locale'; +import delayedJobMixin from '../mixins/delayed_job_mixin'; export default { name: 'JobPageApp', @@ -30,6 +32,7 @@ export default { StuckBlock, Sidebar, }, + mixins: [delayedJobMixin], props: { runnerSettingsUrl: { type: String, @@ -89,6 +92,17 @@ export default { shouldRenderContent() { return !this.isLoading && !this.hasError; }, + + emptyStateTitle() { + const { emptyStateIllustration, remainingTime } = this; + const { title } = emptyStateIllustration; + + if (this.isDelayedJob) { + return sprintf(title, { remainingTime }); + } + + return title; + }, }, watch: { // Once the job log is loaded, @@ -250,7 +264,7 @@ export default { class="js-job-empty-state" :illustration-path="emptyStateIllustration.image" :illustration-size-class="emptyStateIllustration.size" - :title="emptyStateIllustration.title" + :title="emptyStateTitle" :content="emptyStateIllustration.content" :action="emptyStateAction" /> diff --git a/spec/javascripts/jobs/components/job_app_spec.js b/spec/javascripts/jobs/components/job_app_spec.js index ba1889c4dcdc93965aa69964e65414d0e0ca6cd1..ba69554012ec86626fc0c316e19dcce8e854939a 100644 --- a/spec/javascripts/jobs/components/job_app_spec.js +++ b/spec/javascripts/jobs/components/job_app_spec.js @@ -8,6 +8,7 @@ import { resetStore } from '../store/helpers'; import job from '../mock_data'; describe('Job App ', () => { + const delayedJobFixture = getJSONFixture('jobs/delayed.json'); const Component = Vue.extend(jobApp); let store; let vm; @@ -420,6 +421,34 @@ describe('Job App ', () => { done(); }, 0); }); + + it('displays remaining time for a delayed job', (done) => { + const oneHourInMilliseconds = 3600000; + spyOn(Date, 'now').and.callFake(() => new Date(delayedJobFixture.scheduled_at).getTime() - oneHourInMilliseconds); + mock.onGet(props.endpoint).replyOnce(200, { ... delayedJobFixture }); + + vm = mountComponentWithStore(Component, { + props, + store, + }); + + store.subscribeAction((action) => { + if (action.type !== 'receiveJobSuccess') { + return; + } + + Vue.nextTick() + .then(() => { + expect(vm.$el.querySelector('.js-job-empty-state')).not.toBeNull(); + + const title = vm.$el.querySelector('.js-job-empty-state-title'); + + expect(title).toContainText('01:00:00'); + done(); + }) + .catch(done.fail); + }); + }); }); });