diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js
index c02e10128e2bb22c810b891ed8feaf7bb08e8f96..e8b3cf2f7292fd3c0bf521179ac46944bda6a687 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.js
@@ -17,6 +17,9 @@ export default {
 
       return hasCI && !ciStatus;
     },
+    hasPipeline() {
+      return Object.keys(this.mr.pipeline || {}).length > 0;
+    },
     svg() {
       return statusIconEntityMap.icon_status_failed;
     },
@@ -30,7 +33,11 @@ export default {
   template: `
     <div class="mr-widget-heading">
       <div class="ci-widget">
-        <template v-if="hasCIError">
+        <template v-if="!hasPipeline">
+          <i class="fa fa-spinner fa-spin append-right-10" aria-hidden="true"></i>
+          Waiting for pipeline...
+        </template>
+        <template v-else-if="hasCIError">
           <div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error">
             <span class="js-icon-link icon-link">
               <span
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
index 647b59520f8a1779ba35072082dbc03fe7efe0d8..4b6f171c8d6daffccb5abace8f7134e3c80ed785 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
@@ -76,6 +76,28 @@ describe('MRWidgetPipeline', () => {
       el = vm.$el;
     });
 
+    afterEach(() => {
+      vm.$destroy();
+    });
+
+    describe('without a pipeline', () => {
+      beforeEach(() => {
+        vm.mr = { pipeline: null };
+      });
+
+      it('should render message with spinner', (done) => {
+        Vue.nextTick()
+          .then(() => {
+            expect(el.querySelector('.pipeline-id')).toBe(null);
+            expect(el.innerText.trim()).toBe('Waiting for pipeline...');
+            expect(el.querySelectorAll('i.fa.fa-spinner.fa-spin').length).toBe(1);
+            done();
+          })
+          .then(done)
+          .catch(done.fail);
+      });
+    });
+
     it('should render template elements correctly', () => {
       expect(el.classList.contains('mr-widget-heading')).toBeTruthy();
       expect(el.querySelectorAll('.ci-status-icon.ci-status-icon-success').length).toEqual(1);
@@ -93,39 +115,47 @@ describe('MRWidgetPipeline', () => {
     it('should list single stage', (done) => {
       pipeline.details.stages.splice(0, 1);
 
-      Vue.nextTick(() => {
-        expect(el.querySelectorAll('.stage-container button').length).toEqual(1);
-        expect(el.innerText).toContain('with stage');
-        done();
-      });
+      Vue.nextTick()
+        .then(() => {
+          expect(el.querySelectorAll('.stage-container button').length).toEqual(1);
+          expect(el.innerText).toContain('with stage');
+        })
+        .then(done)
+        .catch(done.fail);
     });
 
     it('should not have stages when there is no stage', (done) => {
       vm.mr.pipeline.details.stages = [];
 
-      Vue.nextTick(() => {
-        expect(el.querySelectorAll('.stage-container button').length).toEqual(0);
-        done();
-      });
+      Vue.nextTick()
+        .then(() => {
+          expect(el.querySelectorAll('.stage-container button').length).toEqual(0);
+        })
+        .then(done)
+        .catch(done.fail);
     });
 
     it('should not have coverage text when pipeline has no coverage info', (done) => {
       vm.mr.pipeline.coverage = null;
 
-      Vue.nextTick(() => {
-        expect(el.querySelector('.js-mr-coverage')).toEqual(null);
-        done();
-      });
+      Vue.nextTick()
+        .then(() => {
+          expect(el.querySelector('.js-mr-coverage')).toEqual(null);
+        })
+        .then(done)
+        .catch(done.fail);
     });
 
     it('should show CI error when there is a CI error', (done) => {
       vm.mr.ciStatus = null;
 
-      Vue.nextTick(() => {
-        expect(el.querySelectorAll('.js-ci-error').length).toEqual(1);
-        expect(el.innerText).toContain('Could not connect to the CI server');
-        done();
-      });
+      Vue.nextTick()
+        .then(() => {
+          expect(el.querySelectorAll('.js-ci-error').length).toEqual(1);
+          expect(el.innerText).toContain('Could not connect to the CI server');
+        })
+        .then(done)
+        .catch(done.fail);
     });
   });
 });