Uniformize code between both pipelines tables

This commit is contained in:
Filipa Lacerda 2017-06-20 14:54:55 +00:00 committed by Phil Hughes
parent 1d15ad02cb
commit 05cfba6f6d
16 changed files with 271 additions and 443 deletions

View File

@ -1,29 +1,25 @@
/* eslint-disable no-param-reassign */
import Vue from 'vue'; import Vue from 'vue';
import VueResource from 'vue-resource'; import commitPipelinesTable from './pipelines_table.vue';
import CommitPipelinesTable from './pipelines_table';
Vue.use(VueResource);
/** /**
* Commits View > Pipelines Tab > Pipelines Table. * Used in:
* * - Commit details View > Pipelines Tab > Pipelines Table.
* Renders Pipelines table in pipelines tab in the commits show view. * - 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) const CommitPipelinesTable = Vue.extend(commitPipelinesTable);
window.gl = window.gl || {};
window.gl.CommitPipelinesTable = CommitPipelinesTable;
$(() => {
gl.commits = gl.commits || {};
gl.commits.pipelines = gl.commits.pipelines || {};
document.addEventListener('DOMContentLoaded', () => {
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view'); const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
if (pipelineTableViewEl && pipelineTableViewEl.dataset.disableInitialization === undefined) { if (pipelineTableViewEl && pipelineTableViewEl.dataset.disableInitialization === undefined) {
gl.commits.pipelines.PipelinesTableBundle = new CommitPipelinesTable().$mount(); const table = new CommitPipelinesTable({
pipelineTableViewEl.appendChild(gl.commits.pipelines.PipelinesTableBundle.$el); propsData: {
endpoint: pipelineTableViewEl.dataset.endpoint,
helpPagePath: pipelineTableViewEl.dataset.helpPagePath,
},
}).$mount();
pipelineTableViewEl.appendChild(table.$el);
} }
}); });

View File

@ -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>
`,
});

View File

@ -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>

View File

@ -3,10 +3,12 @@
/* global Flash */ /* global Flash */
/* global notes */ /* global notes */
import Vue from 'vue';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import './breakpoints'; import './breakpoints';
import './flash'; import './flash';
import BlobForkSuggestion from './blob/blob_fork_suggestion'; import BlobForkSuggestion from './blob/blob_fork_suggestion';
import commitPipelinesTable from './commit/pipelines/pipelines_table.vue';
/* eslint-disable max-len */ /* eslint-disable max-len */
// MergeRequestTabs // MergeRequestTabs
@ -233,11 +235,18 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
} }
mountPipelinesView() { 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 // $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 // it everytime this tab is clicked - https://vuejs.org/v2/api/#vm-mount
document.querySelector('#commit-pipeline-table-view') pipelineTableViewEl.appendChild(this.commitPipelinesTable.$el);
.appendChild(this.commitPipelinesTable.$el);
} }
loadDiff(source) { loadDiff(source) {

View File

@ -1,9 +1,9 @@
<script> <script>
/* eslint-disable no-new, no-alert */ /* eslint-disable no-new, no-alert */
/* global Flash */
import '~/flash';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue'; import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltipMixin from '../../vue_shared/mixins/tooltip';
export default { export default {
props: { props: {
@ -11,53 +11,42 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
service: {
type: Object,
required: true,
},
title: { title: {
type: String, type: String,
required: true, required: true,
}, },
icon: { icon: {
type: String, type: String,
required: true, required: true,
}, },
cssClass: { cssClass: {
type: String, type: String,
required: true, required: true,
}, },
confirmActionMessage: { confirmActionMessage: {
type: String, type: String,
required: false, required: false,
}, },
}, },
components: { components: {
loadingIcon, loadingIcon,
}, },
mixins: [
tooltipMixin,
],
data() { data() {
return { return {
isLoading: false, isLoading: false,
}; };
}, },
computed: { computed: {
iconClass() { iconClass() {
return `fa fa-${this.icon}`; return `fa fa-${this.icon}`;
}, },
buttonClass() { buttonClass() {
return `btn has-tooltip ${this.cssClass}`; return `btn ${this.cssClass}`;
}, },
}, },
methods: { methods: {
onClick() { onClick() {
if (this.confirmActionMessage && confirm(this.confirmActionMessage)) { if (this.confirmActionMessage && confirm(this.confirmActionMessage)) {
@ -66,21 +55,11 @@ export default {
this.makeRequest(); this.makeRequest();
} }
}, },
makeRequest() { makeRequest() {
this.isLoading = true; this.isLoading = true;
$(this.$el).tooltip('destroy'); $(this.$refs.tooltip).tooltip('destroy');
eventHub.$emit('postAction', this.endpoint);
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.');
});
}, },
}, },
}; };
@ -95,10 +74,12 @@ export default {
:aria-label="title" :aria-label="title"
data-container="body" data-container="body"
data-placement="top" data-placement="top"
ref="tooltip"
:disabled="isLoading"> :disabled="isLoading">
<i <i
:class="iconClass" :class="iconClass"
aria-hidden="true" /> aria-hidden="true">
</i>
<loading-icon v-if="isLoading" /> <loading-icon v-if="isLoading" />
</button> </button>
</template> </template>

View File

@ -1,15 +1,9 @@
<script> <script>
import Visibility from 'visibilityjs';
import PipelinesService from '../services/pipelines_service'; import PipelinesService from '../services/pipelines_service';
import eventHub from '../event_hub'; import pipelinesMixin from '../mixins/pipelines';
import pipelinesTableComponent from '../../vue_shared/components/pipelines_table.vue';
import tablePagination from '../../vue_shared/components/table_pagination.vue'; 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 navigationTabs from './navigation_tabs.vue';
import navigationControls from './nav_controls.vue'; import navigationControls from './nav_controls.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import Poll from '../../lib/utils/poll';
export default { export default {
props: { props: {
@ -20,13 +14,12 @@
}, },
components: { components: {
tablePagination, tablePagination,
pipelinesTableComponent,
emptyState,
errorState,
navigationTabs, navigationTabs,
navigationControls, navigationControls,
loadingIcon,
}, },
mixins: [
pipelinesMixin,
],
data() { data() {
const pipelinesData = document.querySelector('#pipelines-list-vue').dataset; const pipelinesData = document.querySelector('#pipelines-list-vue').dataset;
@ -47,11 +40,6 @@
state: this.store.state, state: this.store.state,
apiScope: 'all', apiScope: 'all',
pagenum: 1, pagenum: 1,
isLoading: false,
hasError: false,
isMakingRequest: false,
updateGraphDropdown: false,
hasMadeRequest: false,
}; };
}, },
computed: { computed: {
@ -62,9 +50,6 @@
const scope = gl.utils.getParameterByName('scope'); const scope = gl.utils.getParameterByName('scope');
return scope === null ? 'all' : 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 * 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.pipelines.length &&
this.state.pageInfo.total > this.state.pageInfo.perPage; this.state.pageInfo.total > this.state.pageInfo.perPage;
}, },
hasCiEnabled() { hasCiEnabled() {
return this.hasCi !== undefined; return this.hasCi !== undefined;
}, },
@ -129,37 +113,7 @@
}, },
created() { created() {
this.service = new PipelinesService(this.endpoint); this.service = new PipelinesService(this.endpoint);
this.requestData = { page: this.pageParameter, scope: this.scopeParameter };
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');
}, },
methods: { methods: {
/** /**
@ -174,15 +128,6 @@
return param; 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) { successCallback(resp) {
const response = { const response = {
headers: resp.headers, headers: resp.headers,
@ -190,33 +135,14 @@
}; };
this.store.storeCount(response.body.count); this.store.storeCount(response.body.count);
this.store.storePipelines(response.body.pipelines);
this.store.storePagination(response.headers); this.store.storePagination(response.headers);
this.setCommonData(response.body.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;
}
}, },
}, },
}; };
</script> </script>
<template> <template>
<div :class="cssClass"> <div :class="cssClass">
<div <div
class="top-area scrolling-tabs-container inner-page-scroll-tabs" class="top-area scrolling-tabs-container inner-page-scroll-tabs"
v-if="!isLoading && !shouldRenderEmptyState"> v-if="!isLoading && !shouldRenderEmptyState">
@ -274,7 +200,6 @@
<pipelines-table-component <pipelines-table-component
:pipelines="state.pipelines" :pipelines="state.pipelines"
:service="service"
:update-graph-dropdown="updateGraphDropdown" :update-graph-dropdown="updateGraphDropdown"
/> />
</div> </div>

View File

@ -11,10 +11,6 @@
type: Array, type: Array,
required: true, required: true,
}, },
service: {
type: Object,
required: true,
},
}, },
components: { components: {
loadingIcon, loadingIcon,
@ -31,17 +27,9 @@
$(this.$refs.tooltip).tooltip('destroy'); $(this.$refs.tooltip).tooltip('destroy');
this.service.postAction(endpoint) eventHub.$emit('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.');
});
}, },
isActionDisabled(action) { isActionDisabled(action) {
if (action.playable === undefined) { if (action.playable === undefined) {
return false; return false;

View File

@ -12,10 +12,6 @@
type: Array, type: Array,
required: true, required: true,
}, },
service: {
type: Object,
required: true,
},
updateGraphDropdown: { updateGraphDropdown: {
type: Boolean, type: Boolean,
required: false, required: false,
@ -57,7 +53,6 @@
v-for="model in pipelines" v-for="model in pipelines"
:key="model.id" :key="model.id"
:pipeline="model" :pipeline="model"
:service="service"
:update-graph-dropdown="updateGraphDropdown" :update-graph-dropdown="updateGraphDropdown"
/> />
</div> </div>

View File

@ -1,13 +1,13 @@
<script> <script>
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import asyncButtonComponent from '../../pipelines/components/async_button.vue'; import asyncButtonComponent from './async_button.vue';
import pipelinesActionsComponent from '../../pipelines/components/pipelines_actions.vue'; import pipelinesActionsComponent from './pipelines_actions.vue';
import pipelinesArtifactsComponent from '../../pipelines/components/pipelines_artifacts.vue'; import pipelinesArtifactsComponent from './pipelines_artifacts.vue';
import ciBadge from './ci_badge_link.vue'; import ciBadge from '../../vue_shared/components/ci_badge_link.vue';
import pipelineStage from '../../pipelines/components/stage.vue'; import pipelineStage from './stage.vue';
import pipelineUrl from '../../pipelines/components/pipeline_url.vue'; import pipelineUrl from './pipeline_url.vue';
import pipelinesTimeago from '../../pipelines/components/time_ago.vue'; import pipelinesTimeago from './time_ago.vue';
import commitComponent from './commit.vue'; import commitComponent from '../../vue_shared/components/commit.vue';
/** /**
* Pipeline table row. * Pipeline table row.
@ -20,10 +20,6 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
service: {
type: Object,
required: true,
},
updateGraphDropdown: { updateGraphDropdown: {
type: Boolean, type: Boolean,
required: false, required: false,
@ -271,7 +267,6 @@ export default {
<pipelines-actions-component <pipelines-actions-component
v-if="pipeline.details.manual_actions.length" v-if="pipeline.details.manual_actions.length"
:actions="pipeline.details.manual_actions" :actions="pipeline.details.manual_actions"
:service="service"
/> />
<pipelines-artifacts-component <pipelines-artifacts-component
@ -282,7 +277,6 @@ export default {
<async-button-component <async-button-component
v-if="pipeline.flags.retryable" v-if="pipeline.flags.retryable"
:service="service"
:endpoint="pipeline.retry_path" :endpoint="pipeline.retry_path"
css-class="js-pipelines-retry-button btn-default btn-retry" css-class="js-pipelines-retry-button btn-default btn-retry"
title="Retry" title="Retry"
@ -291,7 +285,6 @@ export default {
<async-button-component <async-button-component
v-if="pipeline.flags.cancelable" v-if="pipeline.flags.cancelable"
:service="service"
:endpoint="pipeline.cancel_path" :endpoint="pipeline.cancel_path"
css-class="js-pipelines-cancel-button btn-remove" css-class="js-pipelines-cancel-button btn-remove"
title="Cancel" title="Cancel"

View File

@ -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.'));
},
},
};

View File

@ -1,15 +1,15 @@
import Vue from 'vue'; 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', () => { describe('Pipelines table in Commits and Merge requests', () => {
const jsonFixtureName = 'pipelines/pipelines.json'; const jsonFixtureName = 'pipelines/pipelines.json';
let pipeline; let pipeline;
let PipelinesTable;
preloadFixtures('static/pipelines_table.html.raw');
preloadFixtures(jsonFixtureName); preloadFixtures(jsonFixtureName);
beforeEach(() => { beforeEach(() => {
loadFixtures('static/pipelines_table.html.raw'); PipelinesTable = Vue.extend(pipelinesTable);
const pipelines = getJSONFixture(jsonFixtureName).pipelines; const pipelines = getJSONFixture(jsonFixtureName).pipelines;
pipeline = pipelines.find(p => p.id === 1); pipeline = pipelines.find(p => p.id === 1);
}); });
@ -26,8 +26,11 @@ describe('Pipelines table in Commits and Merge requests', () => {
Vue.http.interceptors.push(pipelinesEmptyResponse); Vue.http.interceptors.push(pipelinesEmptyResponse);
this.component = new PipelinesTable({ this.component = new PipelinesTable({
el: document.querySelector('#commit-pipeline-table-view'), propsData: {
}); endpoint: 'endpoint',
helpPagePath: 'foo',
},
}).$mount();
}); });
afterEach(function () { afterEach(function () {
@ -58,8 +61,11 @@ describe('Pipelines table in Commits and Merge requests', () => {
Vue.http.interceptors.push(pipelinesResponse); Vue.http.interceptors.push(pipelinesResponse);
this.component = new PipelinesTable({ this.component = new PipelinesTable({
el: document.querySelector('#commit-pipeline-table-view'), propsData: {
}); endpoint: 'endpoint',
helpPagePath: 'foo',
},
}).$mount();
}); });
afterEach(() => { afterEach(() => {
@ -92,8 +98,11 @@ describe('Pipelines table in Commits and Merge requests', () => {
Vue.http.interceptors.push(pipelinesErrorResponse); Vue.http.interceptors.push(pipelinesErrorResponse);
this.component = new PipelinesTable({ this.component = new PipelinesTable({
el: document.querySelector('#commit-pipeline-table-view'), propsData: {
}); endpoint: 'endpoint',
helpPagePath: 'foo',
},
}).$mount();
}); });
afterEach(function () { afterEach(function () {

View File

@ -1 +0,0 @@
#commit-pipeline-table-view{ data: { endpoint: "endpoint", "help-page-path": "foo" } }

View File

@ -1,25 +1,20 @@
import Vue from 'vue'; import Vue from 'vue';
import asyncButtonComp from '~/pipelines/components/async_button.vue'; import asyncButtonComp from '~/pipelines/components/async_button.vue';
import eventHub from '~/pipelines/event_hub';
describe('Pipelines Async Button', () => { describe('Pipelines Async Button', () => {
let component; let component;
let spy;
let AsyncButtonComponent; let AsyncButtonComponent;
beforeEach(() => { beforeEach(() => {
AsyncButtonComponent = Vue.extend(asyncButtonComp); AsyncButtonComponent = Vue.extend(asyncButtonComp);
spy = jasmine.createSpy('spy').and.returnValue(Promise.resolve());
component = new AsyncButtonComponent({ component = new AsyncButtonComponent({
propsData: { propsData: {
endpoint: '/foo', endpoint: '/foo',
title: 'Foo', title: 'Foo',
icon: 'fa fa-foo', icon: 'fa fa-foo',
cssClass: 'bar', cssClass: 'bar',
service: {
postAction: spy,
},
}, },
}).$mount(); }).$mount();
}); });
@ -33,7 +28,7 @@ describe('Pipelines Async Button', () => {
}); });
it('should render the provided title', () => { 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'); expect(component.$el.getAttribute('aria-label')).toContain('Foo');
}); });
@ -41,37 +36,12 @@ describe('Pipelines Async Button', () => {
expect(component.$el.getAttribute('class')).toContain('bar'); 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', () => { describe('With confirm dialog', () => {
it('should call the service when confimation is positive', () => { it('should call the service when confimation is positive', () => {
spyOn(window, 'confirm').and.returnValue(true); 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({ component = new AsyncButtonComponent({
propsData: { propsData: {
@ -79,15 +49,11 @@ describe('Pipelines Async Button', () => {
title: 'Foo', title: 'Foo',
icon: 'fa fa-foo', icon: 'fa fa-foo',
cssClass: 'bar', cssClass: 'bar',
service: {
postAction: spy,
},
confirmActionMessage: 'bar', confirmActionMessage: 'bar',
}, },
}).$mount(); }).$mount();
component.$el.click(); component.$el.click();
expect(spy).toHaveBeenCalledWith('/foo');
}); });
}); });
}); });

View File

@ -3,7 +3,6 @@ import pipelinesActionsComp from '~/pipelines/components/pipelines_actions.vue';
describe('Pipelines Actions dropdown', () => { describe('Pipelines Actions dropdown', () => {
let component; let component;
let spy;
let actions; let actions;
let ActionsComponent; let ActionsComponent;
@ -22,14 +21,9 @@ describe('Pipelines Actions dropdown', () => {
}, },
]; ];
spy = jasmine.createSpy('spy').and.returnValue(Promise.resolve());
component = new ActionsComponent({ component = new ActionsComponent({
propsData: { propsData: {
actions, actions,
service: {
postAction: spy,
},
}, },
}).$mount(); }).$mount();
}); });
@ -40,31 +34,6 @@ describe('Pipelines Actions dropdown', () => {
).toEqual(actions.length); ).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', () => { it('should render a disabled action when it\'s not playable', () => {
expect( expect(
component.$el.querySelector('.dropdown-menu li:last-child button').getAttribute('disabled'), component.$el.querySelector('.dropdown-menu li:last-child button').getAttribute('disabled'),

View File

@ -1,5 +1,5 @@
import Vue from 'vue'; 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', () => { describe('Pipelines Table Row', () => {
const jsonFixtureName = 'pipelines/pipelines.json'; const jsonFixtureName = 'pipelines/pipelines.json';

View File

@ -1,5 +1,5 @@
import Vue from 'vue'; 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'; import '~/lib/utils/datetime_utility';
describe('Pipelines Table', () => { describe('Pipelines Table', () => {
@ -22,7 +22,6 @@ describe('Pipelines Table', () => {
component = new PipelinesTableComponent({ component = new PipelinesTableComponent({
propsData: { propsData: {
pipelines: [], pipelines: [],
service: {},
}, },
}).$mount(); }).$mount();
}); });
@ -48,7 +47,6 @@ describe('Pipelines Table', () => {
const component = new PipelinesTableComponent({ const component = new PipelinesTableComponent({
propsData: { propsData: {
pipelines: [], pipelines: [],
service: {},
}, },
}).$mount(); }).$mount();
expect(component.$el.querySelectorAll('.commit.gl-responsive-table-row').length).toEqual(0); expect(component.$el.querySelectorAll('.commit.gl-responsive-table-row').length).toEqual(0);
@ -58,10 +56,8 @@ describe('Pipelines Table', () => {
describe('with data', () => { describe('with data', () => {
it('should render rows', () => { it('should render rows', () => {
const component = new PipelinesTableComponent({ const component = new PipelinesTableComponent({
el: document.querySelector('.test-dom-element'),
propsData: { propsData: {
pipelines: [pipeline], pipelines: [pipeline],
service: {},
}, },
}).$mount(); }).$mount();