summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2017-06-20 14:54:55 +0000
committerPhil Hughes <me@iamphill.com>2017-06-20 14:54:55 +0000
commit05cfba6f6d18dc04e1fda6177b3dfa36d9cdc9f2 (patch)
treeeae8ee66efdbff4cf12a2355bc9c9d8a4eaf464c
parent1d15ad02cb5c5a2b256e06537eea93974cab8f8c (diff)
downloadgitlab-ce-05cfba6f6d18dc04e1fda6177b3dfa36d9cdc9f2.tar.gz
Uniformize code between both pipelines tables
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_bundle.js32
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.js191
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.vue90
-rw-r--r--app/assets/javascripts/merge_request_tabs.js15
-rw-r--r--app/assets/javascripts/pipelines/components/async_button.vue41
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines.vue87
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_actions.vue16
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table.vue (renamed from app/assets/javascripts/vue_shared/components/pipelines_table.vue)5
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table_row.vue (renamed from app/assets/javascripts/vue_shared/components/pipelines_table_row.vue)23
-rw-r--r--app/assets/javascripts/pipelines/mixins/pipelines.js103
-rw-r--r--spec/javascripts/commit/pipelines/pipelines_spec.js27
-rw-r--r--spec/javascripts/fixtures/pipelines_table.html.haml1
-rw-r--r--spec/javascripts/pipelines/async_button_spec.js44
-rw-r--r--spec/javascripts/pipelines/pipelines_actions_spec.js31
-rw-r--r--spec/javascripts/pipelines/pipelines_table_row_spec.js (renamed from spec/javascripts/vue_shared/components/pipelines_table_row_spec.js)2
-rw-r--r--spec/javascripts/pipelines/pipelines_table_spec.js (renamed from spec/javascripts/vue_shared/components/pipelines_table_spec.js)6
16 files changed, 271 insertions, 443 deletions
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
index 86d99dd87da..c7dc6863160 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
+++ b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
@@ -1,29 +1,25 @@
-/* eslint-disable no-param-reassign */
-
import Vue from 'vue';
-import VueResource from 'vue-resource';
-import CommitPipelinesTable from './pipelines_table';
-
-Vue.use(VueResource);
+import commitPipelinesTable from './pipelines_table.vue';
/**
- * Commits View > Pipelines Tab > Pipelines Table.
- *
- * Renders Pipelines table in pipelines tab in the commits show view.
+ * Used in:
+ * - Commit details View > Pipelines Tab > Pipelines Table.
+ * - Merge Request details View > Pipelines Tab > Pipelines Table.
+ * - New Merge Request View > Pipelines Tab > Pipelines Table.
*/
-// export for use in merge_request_tabs.js (TODO: remove this hack)
-window.gl = window.gl || {};
-window.gl.CommitPipelinesTable = CommitPipelinesTable;
-
-$(() => {
- gl.commits = gl.commits || {};
- gl.commits.pipelines = gl.commits.pipelines || {};
+const CommitPipelinesTable = Vue.extend(commitPipelinesTable);
+document.addEventListener('DOMContentLoaded', () => {
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
if (pipelineTableViewEl && pipelineTableViewEl.dataset.disableInitialization === undefined) {
- gl.commits.pipelines.PipelinesTableBundle = new CommitPipelinesTable().$mount();
- pipelineTableViewEl.appendChild(gl.commits.pipelines.PipelinesTableBundle.$el);
+ const table = new CommitPipelinesTable({
+ propsData: {
+ endpoint: pipelineTableViewEl.dataset.endpoint,
+ helpPagePath: pipelineTableViewEl.dataset.helpPagePath,
+ },
+ }).$mount();
+ pipelineTableViewEl.appendChild(table.$el);
}
});
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.js b/app/assets/javascripts/commit/pipelines/pipelines_table.js
deleted file mode 100644
index 70ba83ce5b9..00000000000
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.js
+++ /dev/null
@@ -1,191 +0,0 @@
-import Vue from 'vue';
-import Visibility from 'visibilityjs';
-import pipelinesTableComponent from '../../vue_shared/components/pipelines_table.vue';
-import PipelinesService from '../../pipelines/services/pipelines_service';
-import PipelineStore from '../../pipelines/stores/pipelines_store';
-import eventHub from '../../pipelines/event_hub';
-import emptyState from '../../pipelines/components/empty_state.vue';
-import errorState from '../../pipelines/components/error_state.vue';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-import '../../lib/utils/common_utils';
-import '../../vue_shared/vue_resource_interceptor';
-import Poll from '../../lib/utils/poll';
-
-/**
- *
- * Uses `pipelines-table-component` to render Pipelines table with an API call.
- * Endpoint is provided in HTML and passed as `endpoint`.
- * We need a store to store the received environemnts.
- * We need a service to communicate with the server.
- *
- */
-
-export default Vue.component('pipelines-table', {
-
- components: {
- pipelinesTableComponent,
- errorState,
- emptyState,
- loadingIcon,
- },
-
- /**
- * Accesses the DOM to provide the needed data.
- * Returns the necessary props to render `pipelines-table-component` component.
- *
- * @return {Object}
- */
- data() {
- const store = new PipelineStore();
-
- return {
- endpoint: null,
- helpPagePath: null,
- store,
- state: store.state,
- isLoading: false,
- hasError: false,
- isMakingRequest: false,
- updateGraphDropdown: false,
- hasMadeRequest: false,
- };
- },
-
- computed: {
- shouldRenderErrorState() {
- return this.hasError && !this.isLoading;
- },
-
- /**
- * Empty state is only rendered if after the first request we receive no pipelines.
- *
- * @return {Boolean}
- */
- shouldRenderEmptyState() {
- return !this.state.pipelines.length &&
- !this.isLoading &&
- this.hasMadeRequest &&
- !this.hasError;
- },
-
- shouldRenderTable() {
- return !this.isLoading &&
- this.state.pipelines.length > 0 &&
- !this.hasError;
- },
- },
-
- /**
- * When the component is about to be mounted, tell the service to fetch the data
- *
- * A request to fetch the pipelines will be made.
- * In case of a successfull response we will store the data in the provided
- * store, in case of a failed response we need to warn the user.
- *
- */
- beforeMount() {
- const element = document.querySelector('#commit-pipeline-table-view');
-
- this.endpoint = element.dataset.endpoint;
- this.helpPagePath = element.dataset.helpPagePath;
- this.service = new PipelinesService(this.endpoint);
-
- this.poll = new Poll({
- resource: this.service,
- method: 'getPipelines',
- successCallback: this.successCallback,
- errorCallback: this.errorCallback,
- notificationCallback: this.setIsMakingRequest,
- });
-
- if (!Visibility.hidden()) {
- this.isLoading = true;
- this.poll.makeRequest();
- } else {
- // If tab is not visible we need to make the first request so we don't show the empty
- // state without knowing if there are any pipelines
- this.fetchPipelines();
- }
-
- Visibility.change(() => {
- if (!Visibility.hidden()) {
- this.poll.restart();
- } else {
- this.poll.stop();
- }
- });
-
- eventHub.$on('refreshPipelines', this.fetchPipelines);
- },
-
- beforeDestroy() {
- eventHub.$off('refreshPipelines');
- },
-
- destroyed() {
- this.poll.stop();
- },
-
- methods: {
- fetchPipelines() {
- this.isLoading = true;
-
- return this.service.getPipelines()
- .then(response => this.successCallback(response))
- .catch(() => this.errorCallback());
- },
-
- successCallback(resp) {
- const response = resp.json();
-
- this.hasMadeRequest = true;
-
- // depending of the endpoint the response can either bring a `pipelines` key or not.
- const pipelines = response.pipelines || response;
- this.store.storePipelines(pipelines);
- this.isLoading = false;
- this.updateGraphDropdown = true;
- },
-
- errorCallback() {
- this.hasError = true;
- this.isLoading = false;
- this.updateGraphDropdown = false;
- },
-
- setIsMakingRequest(isMakingRequest) {
- this.isMakingRequest = isMakingRequest;
-
- if (isMakingRequest) {
- this.updateGraphDropdown = false;
- }
- },
- },
-
- template: `
- <div class="content-list pipelines">
-
- <loading-icon
- label="Loading pipelines"
- size="3"
- v-if="isLoading"
- />
-
- <empty-state
- v-if="shouldRenderEmptyState"
- :help-page-path="helpPagePath" />
-
- <error-state v-if="shouldRenderErrorState" />
-
- <div
- class="table-holder"
- v-if="shouldRenderTable">
- <pipelines-table-component
- :pipelines="state.pipelines"
- :service="service"
- :update-graph-dropdown="updateGraphDropdown"
- />
- </div>
- </div>
- `,
-});
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
new file mode 100644
index 00000000000..3c77f14d533
--- /dev/null
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
@@ -0,0 +1,90 @@
+<script>
+ import PipelinesService from '../../pipelines/services/pipelines_service';
+ import PipelineStore from '../../pipelines/stores/pipelines_store';
+ import pipelinesMixin from '../../pipelines/mixins/pipelines';
+
+ export default {
+ props: {
+ endpoint: {
+ type: String,
+ required: true,
+ },
+ helpPagePath: {
+ type: String,
+ required: true,
+ },
+ },
+ mixins: [
+ pipelinesMixin,
+ ],
+
+ data() {
+ const store = new PipelineStore();
+
+ return {
+ store,
+ state: store.state,
+ };
+ },
+
+ computed: {
+ /**
+ * Empty state is only rendered if after the first request we receive no pipelines.
+ *
+ * @return {Boolean}
+ */
+ shouldRenderEmptyState() {
+ return !this.state.pipelines.length &&
+ !this.isLoading &&
+ this.hasMadeRequest &&
+ !this.hasError;
+ },
+
+ shouldRenderTable() {
+ return !this.isLoading &&
+ this.state.pipelines.length > 0 &&
+ !this.hasError;
+ },
+ },
+ created() {
+ this.service = new PipelinesService(this.endpoint);
+ },
+ methods: {
+ successCallback(resp) {
+ const response = resp.json();
+
+ // depending of the endpoint the response can either bring a `pipelines` key or not.
+ const pipelines = response.pipelines || response;
+ this.setCommonData(pipelines);
+ },
+ },
+ };
+</script>
+<template>
+ <div class="content-list pipelines">
+
+ <loading-icon
+ label="Loading pipelines"
+ size="3"
+ v-if="isLoading"
+ />
+
+ <empty-state
+ v-if="shouldRenderEmptyState"
+ :help-page-path="helpPagePath"
+ />
+
+ <error-state
+ v-if="shouldRenderErrorState"
+ />
+
+ <div
+ class="table-holder"
+ v-if="shouldRenderTable">
+ <pipelines-table-component
+ :pipelines="state.pipelines"
+ :update-graph-dropdown="updateGraphDropdown"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 894ed81b044..f503cd38c24 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -3,10 +3,12 @@
/* global Flash */
/* global notes */
+import Vue from 'vue';
import Cookies from 'js-cookie';
import './breakpoints';
import './flash';
import BlobForkSuggestion from './blob/blob_fork_suggestion';
+import commitPipelinesTable from './commit/pipelines/pipelines_table.vue';
/* eslint-disable max-len */
// MergeRequestTabs
@@ -233,11 +235,18 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
}
mountPipelinesView() {
- this.commitPipelinesTable = new gl.CommitPipelinesTable().$mount();
+ const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
+ const CommitPipelinesTable = Vue.extend(commitPipelinesTable);
+ this.commitPipelinesTable = new CommitPipelinesTable({
+ propsData: {
+ endpoint: pipelineTableViewEl.dataset.endpoint,
+ helpPagePath: pipelineTableViewEl.dataset.helpPagePath,
+ },
+ }).$mount();
+
// $mount(el) replaces the el with the new rendered component. We need it in order to mount
// it everytime this tab is clicked - https://vuejs.org/v2/api/#vm-mount
- document.querySelector('#commit-pipeline-table-view')
- .appendChild(this.commitPipelinesTable.$el);
+ pipelineTableViewEl.appendChild(this.commitPipelinesTable.$el);
}
loadDiff(source) {
diff --git a/app/assets/javascripts/pipelines/components/async_button.vue b/app/assets/javascripts/pipelines/components/async_button.vue
index 37a6f02d8fd..abcd0c4ecea 100644
--- a/app/assets/javascripts/pipelines/components/async_button.vue
+++ b/app/assets/javascripts/pipelines/components/async_button.vue
@@ -1,9 +1,9 @@
<script>
/* eslint-disable no-new, no-alert */
-/* global Flash */
-import '~/flash';
+
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+import tooltipMixin from '../../vue_shared/mixins/tooltip';
export default {
props: {
@@ -11,53 +11,42 @@ export default {
type: String,
required: true,
},
-
- service: {
- type: Object,
- required: true,
- },
-
title: {
type: String,
required: true,
},
-
icon: {
type: String,
required: true,
},
-
cssClass: {
type: String,
required: true,
},
-
confirmActionMessage: {
type: String,
required: false,
},
},
-
components: {
loadingIcon,
},
-
+ mixins: [
+ tooltipMixin,
+ ],
data() {
return {
isLoading: false,
};
},
-
computed: {
iconClass() {
return `fa fa-${this.icon}`;
},
-
buttonClass() {
- return `btn has-tooltip ${this.cssClass}`;
+ return `btn ${this.cssClass}`;
},
},
-
methods: {
onClick() {
if (this.confirmActionMessage && confirm(this.confirmActionMessage)) {
@@ -66,21 +55,11 @@ export default {
this.makeRequest();
}
},
-
makeRequest() {
this.isLoading = true;
- $(this.$el).tooltip('destroy');
-
- this.service.postAction(this.endpoint)
- .then(() => {
- this.isLoading = false;
- eventHub.$emit('refreshPipelines');
- })
- .catch(() => {
- this.isLoading = false;
- new Flash('An error occured while making the request.');
- });
+ $(this.$refs.tooltip).tooltip('destroy');
+ eventHub.$emit('postAction', this.endpoint);
},
},
};
@@ -95,10 +74,12 @@ export default {
:aria-label="title"
data-container="body"
data-placement="top"
+ ref="tooltip"
:disabled="isLoading">
<i
:class="iconClass"
- aria-hidden="true" />
+ aria-hidden="true">
+ </i>
<loading-icon v-if="isLoading" />
</button>
</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue
index fed42d23112..01ae07aad65 100644
--- a/app/assets/javascripts/pipelines/components/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines.vue
@@ -1,15 +1,9 @@
<script>
- import Visibility from 'visibilityjs';
import PipelinesService from '../services/pipelines_service';
- import eventHub from '../event_hub';
- import pipelinesTableComponent from '../../vue_shared/components/pipelines_table.vue';
+ import pipelinesMixin from '../mixins/pipelines';
import tablePagination from '../../vue_shared/components/table_pagination.vue';
- import emptyState from './empty_state.vue';
- import errorState from './error_state.vue';
import navigationTabs from './navigation_tabs.vue';
import navigationControls from './nav_controls.vue';
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
- import Poll from '../../lib/utils/poll';
export default {
props: {
@@ -20,13 +14,12 @@
},
components: {
tablePagination,
- pipelinesTableComponent,
- emptyState,
- errorState,
navigationTabs,
navigationControls,
- loadingIcon,
},
+ mixins: [
+ pipelinesMixin,
+ ],
data() {
const pipelinesData = document.querySelector('#pipelines-list-vue').dataset;
@@ -47,11 +40,6 @@
state: this.store.state,
apiScope: 'all',
pagenum: 1,
- isLoading: false,
- hasError: false,
- isMakingRequest: false,
- updateGraphDropdown: false,
- hasMadeRequest: false,
};
},
computed: {
@@ -62,9 +50,6 @@
const scope = gl.utils.getParameterByName('scope');
return scope === null ? 'all' : scope;
},
- shouldRenderErrorState() {
- return this.hasError && !this.isLoading;
- },
/**
* The empty state should only be rendered when the request is made to fetch all pipelines
@@ -106,7 +91,6 @@
this.state.pipelines.length &&
this.state.pageInfo.total > this.state.pageInfo.perPage;
},
-
hasCiEnabled() {
return this.hasCi !== undefined;
},
@@ -129,37 +113,7 @@
},
created() {
this.service = new PipelinesService(this.endpoint);
-
- const poll = new Poll({
- resource: this.service,
- method: 'getPipelines',
- data: { page: this.pageParameter, scope: this.scopeParameter },
- successCallback: this.successCallback,
- errorCallback: this.errorCallback,
- notificationCallback: this.setIsMakingRequest,
- });
-
- if (!Visibility.hidden()) {
- this.isLoading = true;
- poll.makeRequest();
- } else {
- // If tab is not visible we need to make the first request so we don't show the empty
- // state without knowing if there are any pipelines
- this.fetchPipelines();
- }
-
- Visibility.change(() => {
- if (!Visibility.hidden()) {
- poll.restart();
- } else {
- poll.stop();
- }
- });
-
- eventHub.$on('refreshPipelines', this.fetchPipelines);
- },
- beforeDestroy() {
- eventHub.$off('refreshPipelines');
+ this.requestData = { page: this.pageParameter, scope: this.scopeParameter };
},
methods: {
/**
@@ -174,15 +128,6 @@
return param;
},
- fetchPipelines() {
- if (!this.isMakingRequest) {
- this.isLoading = true;
-
- this.service.getPipelines({ scope: this.scopeParameter, page: this.pageParameter })
- .then(response => this.successCallback(response))
- .catch(() => this.errorCallback());
- }
- },
successCallback(resp) {
const response = {
headers: resp.headers,
@@ -190,33 +135,14 @@
};
this.store.storeCount(response.body.count);
- this.store.storePipelines(response.body.pipelines);
this.store.storePagination(response.headers);
-
- this.isLoading = false;
- this.updateGraphDropdown = true;
- this.hasMadeRequest = true;
- },
-
- errorCallback() {
- this.hasError = true;
- this.isLoading = false;
- this.updateGraphDropdown = false;
- },
-
- setIsMakingRequest(isMakingRequest) {
- this.isMakingRequest = isMakingRequest;
-
- if (isMakingRequest) {
- this.updateGraphDropdown = false;
- }
+ this.setCommonData(response.body.pipelines);
},
},
};
</script>
<template>
<div :class="cssClass">
-
<div
class="top-area scrolling-tabs-container inner-page-scroll-tabs"
v-if="!isLoading && !shouldRenderEmptyState">
@@ -274,7 +200,6 @@
<pipelines-table-component
:pipelines="state.pipelines"
- :service="service"
:update-graph-dropdown="updateGraphDropdown"
/>
</div>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
index 97b4de26214..a6fc4f04237 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
@@ -11,10 +11,6 @@
type: Array,
required: true,
},
- service: {
- type: Object,
- required: true,
- },
},
components: {
loadingIcon,
@@ -31,17 +27,9 @@
$(this.$refs.tooltip).tooltip('destroy');
- this.service.postAction(endpoint)
- .then(() => {
- this.isLoading = false;
- eventHub.$emit('refreshPipelines');
- })
- .catch(() => {
- this.isLoading = false;
- // eslint-disable-next-line no-new
- new Flash('An error occured while making the request.');
- });
+ eventHub.$emit('postAction', endpoint);
},
+
isActionDisabled(action) {
if (action.playable === undefined) {
return false;
diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue
index 884f1ce9689..5088d92209f 100644
--- a/app/assets/javascripts/vue_shared/components/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue
@@ -12,10 +12,6 @@
type: Array,
required: true,
},
- service: {
- type: Object,
- required: true,
- },
updateGraphDropdown: {
type: Boolean,
required: false,
@@ -57,7 +53,6 @@
v-for="model in pipelines"
:key="model.id"
:pipeline="model"
- :service="service"
:update-graph-dropdown="updateGraphDropdown"
/>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
index 4d5ebe2e9ed..c3f1c426d8a 100644
--- a/app/assets/javascripts/vue_shared/components/pipelines_table_row.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
@@ -1,13 +1,13 @@
<script>
/* eslint-disable no-param-reassign */
-import asyncButtonComponent from '../../pipelines/components/async_button.vue';
-import pipelinesActionsComponent from '../../pipelines/components/pipelines_actions.vue';
-import pipelinesArtifactsComponent from '../../pipelines/components/pipelines_artifacts.vue';
-import ciBadge from './ci_badge_link.vue';
-import pipelineStage from '../../pipelines/components/stage.vue';
-import pipelineUrl from '../../pipelines/components/pipeline_url.vue';
-import pipelinesTimeago from '../../pipelines/components/time_ago.vue';
-import commitComponent from './commit.vue';
+import asyncButtonComponent from './async_button.vue';
+import pipelinesActionsComponent from './pipelines_actions.vue';
+import pipelinesArtifactsComponent from './pipelines_artifacts.vue';
+import ciBadge from '../../vue_shared/components/ci_badge_link.vue';
+import pipelineStage from './stage.vue';
+import pipelineUrl from './pipeline_url.vue';
+import pipelinesTimeago from './time_ago.vue';
+import commitComponent from '../../vue_shared/components/commit.vue';
/**
* Pipeline table row.
@@ -20,10 +20,6 @@ export default {
type: Object,
required: true,
},
- service: {
- type: Object,
- required: true,
- },
updateGraphDropdown: {
type: Boolean,
required: false,
@@ -271,7 +267,6 @@ export default {
<pipelines-actions-component
v-if="pipeline.details.manual_actions.length"
:actions="pipeline.details.manual_actions"
- :service="service"
/>
<pipelines-artifacts-component
@@ -282,7 +277,6 @@ export default {
<async-button-component
v-if="pipeline.flags.retryable"
- :service="service"
:endpoint="pipeline.retry_path"
css-class="js-pipelines-retry-button btn-default btn-retry"
title="Retry"
@@ -291,7 +285,6 @@ export default {
<async-button-component
v-if="pipeline.flags.cancelable"
- :service="service"
:endpoint="pipeline.cancel_path"
css-class="js-pipelines-cancel-button btn-remove"
title="Cancel"
diff --git a/app/assets/javascripts/pipelines/mixins/pipelines.js b/app/assets/javascripts/pipelines/mixins/pipelines.js
new file mode 100644
index 00000000000..9adc15e6266
--- /dev/null
+++ b/app/assets/javascripts/pipelines/mixins/pipelines.js
@@ -0,0 +1,103 @@
+/* global Flash */
+import '~/flash';
+import Visibility from 'visibilityjs';
+import Poll from '../../lib/utils/poll';
+import emptyState from '../components/empty_state.vue';
+import errorState from '../components/error_state.vue';
+import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+import pipelinesTableComponent from '../components/pipelines_table.vue';
+import eventHub from '../event_hub';
+
+export default {
+ components: {
+ pipelinesTableComponent,
+ errorState,
+ emptyState,
+ loadingIcon,
+ },
+ computed: {
+ shouldRenderErrorState() {
+ return this.hasError && !this.isLoading;
+ },
+ },
+ data() {
+ return {
+ isLoading: false,
+ hasError: false,
+ isMakingRequest: false,
+ updateGraphDropdown: false,
+ hasMadeRequest: false,
+ };
+ },
+ beforeMount() {
+ this.poll = new Poll({
+ resource: this.service,
+ method: 'getPipelines',
+ data: this.requestData ? this.requestData : undefined,
+ successCallback: this.successCallback,
+ errorCallback: this.errorCallback,
+ notificationCallback: this.setIsMakingRequest,
+ });
+
+ if (!Visibility.hidden()) {
+ this.isLoading = true;
+ this.poll.makeRequest();
+ } else {
+ // If tab is not visible we need to make the first request so we don't show the empty
+ // state without knowing if there are any pipelines
+ this.fetchPipelines();
+ }
+
+ Visibility.change(() => {
+ if (!Visibility.hidden()) {
+ this.poll.restart();
+ } else {
+ this.poll.stop();
+ }
+ });
+
+ eventHub.$on('refreshPipelines', this.fetchPipelines);
+ eventHub.$on('postAction', this.postAction);
+ },
+ beforeDestroy() {
+ eventHub.$off('refreshPipelines');
+ eventHub.$on('postAction', this.postAction);
+ },
+ destroyed() {
+ this.poll.stop();
+ },
+ methods: {
+ fetchPipelines() {
+ if (!this.isMakingRequest) {
+ this.isLoading = true;
+
+ this.service.getPipelines(this.requestData)
+ .then(response => this.successCallback(response))
+ .catch(() => this.errorCallback());
+ }
+ },
+ setCommonData(pipelines) {
+ this.store.storePipelines(pipelines);
+ this.isLoading = false;
+ this.updateGraphDropdown = true;
+ this.hasMadeRequest = true;
+ },
+ errorCallback() {
+ this.hasError = true;
+ this.isLoading = false;
+ this.updateGraphDropdown = false;
+ },
+ setIsMakingRequest(isMakingRequest) {
+ this.isMakingRequest = isMakingRequest;
+
+ if (isMakingRequest) {
+ this.updateGraphDropdown = false;
+ }
+ },
+ postAction(endpoint) {
+ this.service.postAction(endpoint)
+ .then(() => eventHub.$emit('refreshPipelines'))
+ .catch(() => new Flash('An error occured while making the request.'));
+ },
+ },
+};
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index ebfd60198b2..694f94efcff 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -1,15 +1,15 @@
import Vue from 'vue';
-import PipelinesTable from '~/commit/pipelines/pipelines_table';
+import pipelinesTable from '~/commit/pipelines/pipelines_table.vue';
describe('Pipelines table in Commits and Merge requests', () => {
const jsonFixtureName = 'pipelines/pipelines.json';
let pipeline;
+ let PipelinesTable;
- preloadFixtures('static/pipelines_table.html.raw');
preloadFixtures(jsonFixtureName);
beforeEach(() => {
- loadFixtures('static/pipelines_table.html.raw');
+ PipelinesTable = Vue.extend(pipelinesTable);
const pipelines = getJSONFixture(jsonFixtureName).pipelines;
pipeline = pipelines.find(p => p.id === 1);
});
@@ -26,8 +26,11 @@ describe('Pipelines table in Commits and Merge requests', () => {
Vue.http.interceptors.push(pipelinesEmptyResponse);
this.component = new PipelinesTable({
- el: document.querySelector('#commit-pipeline-table-view'),
- });
+ propsData: {
+ endpoint: 'endpoint',
+ helpPagePath: 'foo',
+ },
+ }).$mount();
});
afterEach(function () {
@@ -58,8 +61,11 @@ describe('Pipelines table in Commits and Merge requests', () => {
Vue.http.interceptors.push(pipelinesResponse);
this.component = new PipelinesTable({
- el: document.querySelector('#commit-pipeline-table-view'),
- });
+ propsData: {
+ endpoint: 'endpoint',
+ helpPagePath: 'foo',
+ },
+ }).$mount();
});
afterEach(() => {
@@ -92,8 +98,11 @@ describe('Pipelines table in Commits and Merge requests', () => {
Vue.http.interceptors.push(pipelinesErrorResponse);
this.component = new PipelinesTable({
- el: document.querySelector('#commit-pipeline-table-view'),
- });
+ propsData: {
+ endpoint: 'endpoint',
+ helpPagePath: 'foo',
+ },
+ }).$mount();
});
afterEach(function () {
diff --git a/spec/javascripts/fixtures/pipelines_table.html.haml b/spec/javascripts/fixtures/pipelines_table.html.haml
deleted file mode 100644
index ad1682704bb..00000000000
--- a/spec/javascripts/fixtures/pipelines_table.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-#commit-pipeline-table-view{ data: { endpoint: "endpoint", "help-page-path": "foo" } }
diff --git a/spec/javascripts/pipelines/async_button_spec.js b/spec/javascripts/pipelines/async_button_spec.js
index 28c9c7ab282..48620898357 100644
--- a/spec/javascripts/pipelines/async_button_spec.js
+++ b/spec/javascripts/pipelines/async_button_spec.js
@@ -1,25 +1,20 @@
import Vue from 'vue';
import asyncButtonComp from '~/pipelines/components/async_button.vue';
+import eventHub from '~/pipelines/event_hub';
describe('Pipelines Async Button', () => {
let component;
- let spy;
let AsyncButtonComponent;
beforeEach(() => {
AsyncButtonComponent = Vue.extend(asyncButtonComp);
- spy = jasmine.createSpy('spy').and.returnValue(Promise.resolve());
-
component = new AsyncButtonComponent({
propsData: {
endpoint: '/foo',
title: 'Foo',
icon: 'fa fa-foo',
cssClass: 'bar',
- service: {
- postAction: spy,
- },
},
}).$mount();
});
@@ -33,7 +28,7 @@ describe('Pipelines Async Button', () => {
});
it('should render the provided title', () => {
- expect(component.$el.getAttribute('title')).toContain('Foo');
+ expect(component.$el.getAttribute('data-original-title')).toContain('Foo');
expect(component.$el.getAttribute('aria-label')).toContain('Foo');
});
@@ -41,37 +36,12 @@ describe('Pipelines Async Button', () => {
expect(component.$el.getAttribute('class')).toContain('bar');
});
- it('should call the service when it is clicked with the provided endpoint', () => {
- component.$el.click();
- expect(spy).toHaveBeenCalledWith('/foo');
- });
-
- it('should hide loading if request fails', () => {
- spy = jasmine.createSpy('spy').and.returnValue(Promise.reject());
-
- component = new AsyncButtonComponent({
- propsData: {
- endpoint: '/foo',
- title: 'Foo',
- icon: 'fa fa-foo',
- cssClass: 'bar',
- dataAttributes: {
- 'data-foo': 'foo',
- },
- service: {
- postAction: spy,
- },
- },
- }).$mount();
-
- component.$el.click();
- expect(component.$el.querySelector('.fa-spinner')).toBe(null);
- });
-
describe('With confirm dialog', () => {
it('should call the service when confimation is positive', () => {
spyOn(window, 'confirm').and.returnValue(true);
- spy = jasmine.createSpy('spy').and.returnValue(Promise.resolve());
+ eventHub.$on('postAction', (endpoint) => {
+ expect(endpoint).toEqual('/foo');
+ });
component = new AsyncButtonComponent({
propsData: {
@@ -79,15 +49,11 @@ describe('Pipelines Async Button', () => {
title: 'Foo',
icon: 'fa fa-foo',
cssClass: 'bar',
- service: {
- postAction: spy,
- },
confirmActionMessage: 'bar',
},
}).$mount();
component.$el.click();
- expect(spy).toHaveBeenCalledWith('/foo');
});
});
});
diff --git a/spec/javascripts/pipelines/pipelines_actions_spec.js b/spec/javascripts/pipelines/pipelines_actions_spec.js
index 8a58b77f1e3..72fb0a8f9ef 100644
--- a/spec/javascripts/pipelines/pipelines_actions_spec.js
+++ b/spec/javascripts/pipelines/pipelines_actions_spec.js
@@ -3,7 +3,6 @@ import pipelinesActionsComp from '~/pipelines/components/pipelines_actions.vue';
describe('Pipelines Actions dropdown', () => {
let component;
- let spy;
let actions;
let ActionsComponent;
@@ -22,14 +21,9 @@ describe('Pipelines Actions dropdown', () => {
},
];
- spy = jasmine.createSpy('spy').and.returnValue(Promise.resolve());
-
component = new ActionsComponent({
propsData: {
actions,
- service: {
- postAction: spy,
- },
},
}).$mount();
});
@@ -40,31 +34,6 @@ describe('Pipelines Actions dropdown', () => {
).toEqual(actions.length);
});
- it('should call the service when an action is clicked', () => {
- component.$el.querySelector('.js-pipeline-dropdown-manual-actions').click();
- component.$el.querySelector('.js-pipeline-action-link').click();
-
- expect(spy).toHaveBeenCalledWith(actions[0].path);
- });
-
- it('should hide loading if request fails', () => {
- spy = jasmine.createSpy('spy').and.returnValue(Promise.reject());
-
- component = new ActionsComponent({
- propsData: {
- actions,
- service: {
- postAction: spy,
- },
- },
- }).$mount();
-
- component.$el.querySelector('.js-pipeline-dropdown-manual-actions').click();
- component.$el.querySelector('.js-pipeline-action-link').click();
-
- expect(component.$el.querySelector('.fa-spinner')).toEqual(null);
- });
-
it('should render a disabled action when it\'s not playable', () => {
expect(
component.$el.querySelector('.dropdown-menu li:last-child button').getAttribute('disabled'),
diff --git a/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js
index 9475ee28a03..7ce39dca112 100644
--- a/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import tableRowComp from '~/vue_shared/components/pipelines_table_row.vue';
+import tableRowComp from '~/pipelines/components/pipelines_table_row.vue';
describe('Pipelines Table Row', () => {
const jsonFixtureName = 'pipelines/pipelines.json';
diff --git a/spec/javascripts/vue_shared/components/pipelines_table_spec.js b/spec/javascripts/pipelines/pipelines_table_spec.js
index 4c35d702004..3afe89c8db4 100644
--- a/spec/javascripts/vue_shared/components/pipelines_table_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import pipelinesTableComp from '~/vue_shared/components/pipelines_table.vue';
+import pipelinesTableComp from '~/pipelines/components/pipelines_table.vue';
import '~/lib/utils/datetime_utility';
describe('Pipelines Table', () => {
@@ -22,7 +22,6 @@ describe('Pipelines Table', () => {
component = new PipelinesTableComponent({
propsData: {
pipelines: [],
- service: {},
},
}).$mount();
});
@@ -48,7 +47,6 @@ describe('Pipelines Table', () => {
const component = new PipelinesTableComponent({
propsData: {
pipelines: [],
- service: {},
},
}).$mount();
expect(component.$el.querySelectorAll('.commit.gl-responsive-table-row').length).toEqual(0);
@@ -58,10 +56,8 @@ describe('Pipelines Table', () => {
describe('with data', () => {
it('should render rows', () => {
const component = new PipelinesTableComponent({
- el: document.querySelector('.test-dom-element'),
propsData: {
pipelines: [pipeline],
- service: {},
},
}).$mount();