BigW Consortium Gitlab

Commit 956ed72e by James Lopez

Merge branch…

Merge branch '29539-fix-pipelines-container-width-with-parallel-diff-for-9-0-stable-merge' into '9-0-stable' Reset container width when switching to pipelines MR tab -- `9-0-stable` merge edition See merge request !10424
parents 4eb27a39 93b08c8c
......@@ -2,7 +2,8 @@
/* global Vue, CommitsPipelineStore, PipelinesService, Flash */
window.Vue = require('vue');
require('./pipelines_table');
const PipelinesTable = require('./pipelines_table');
/**
* Commits View > Pipelines Tab > Pipelines Table.
* Merge Request View > Pipelines Tab > Pipelines Table.
......@@ -21,7 +22,7 @@ $(() => {
}
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
gl.commits.pipelines.PipelinesTableBundle = new gl.commits.pipelines.PipelinesTableView();
gl.commits.pipelines.PipelinesTableBundle = new PipelinesTable();
if (pipelineTableViewEl && pipelineTableViewEl.dataset.disableInitialization === undefined) {
gl.commits.pipelines.PipelinesTableBundle.$mount(pipelineTableViewEl);
......
......@@ -5,9 +5,9 @@ window.Vue = require('vue');
window.Vue.use(require('vue-resource'));
require('../../lib/utils/common_utils');
require('../../vue_shared/vue_resource_interceptor');
require('../../vue_shared/components/pipelines_table');
require('./pipelines_service');
const PipelineStore = require('./pipelines_store');
const PipelinesTableComponent = require('../../vue_shared/components/pipelines_table');
/**
*
......@@ -19,86 +19,78 @@ const PipelineStore = require('./pipelines_store');
* Necessary SVG in the table are provided as props. This should be refactored
* as soon as we have Webpack and can load them directly into JS files.
*/
module.exports = Vue.component('pipelines-table', {
components: {
'pipelines-table-component': PipelinesTableComponent,
},
(() => {
window.gl = window.gl || {};
gl.commits = gl.commits || {};
gl.commits.pipelines = gl.commits.pipelines || {};
/**
* 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();
gl.commits.pipelines.PipelinesTableView = Vue.component('pipelines-table', {
return {
endpoint: null,
store,
state: store.state,
isLoading: false,
};
},
components: {
'pipelines-table-component': gl.pipelines.PipelinesTableComponent,
},
/**
* 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() {
this.endpoint = this.$el.dataset.endpoint;
const pipelinesService = new gl.commits.pipelines.PipelinesService(this.endpoint);
/**
* Accesses the DOM to provide the needed data.
* Returns the necessary props to render `pipelines-table-component` component.
*
* @return {Object}
*/
data() {
const pipelinesTableData = document.querySelector('#commit-pipeline-table-view').dataset;
const store = new PipelineStore();
this.isLoading = true;
return pipelinesService.all()
.then(response => response.json())
.then((json) => {
// depending of the endpoint the response can either bring a `pipelines` key or not.
const pipelines = json.pipelines || json;
this.store.storePipelines(pipelines);
this.isLoading = false;
})
.catch(() => {
this.isLoading = false;
new Flash('An error occurred while fetching the pipelines, please reload the page again.', 'alert');
});
},
return {
endpoint: pipelinesTableData.endpoint,
store,
state: store.state,
isLoading: false,
};
},
beforeUpdate() {
if (this.state.pipelines.length && this.$children) {
PipelineStore.startTimeAgoLoops.call(this, Vue);
}
},
/**
* 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 pipelinesService = new gl.commits.pipelines.PipelinesService(this.endpoint);
this.isLoading = true;
return pipelinesService.all()
.then(response => response.json())
.then((json) => {
// depending of the endpoint the response can either bring a `pipelines` key or not.
const pipelines = json.pipelines || json;
this.store.storePipelines(pipelines);
this.isLoading = false;
})
.catch(() => {
this.isLoading = false;
new Flash('An error occurred while fetching the pipelines, please reload the page again.', 'alert');
});
},
beforeUpdate() {
if (this.state.pipelines.length && this.$children) {
PipelineStore.startTimeAgoLoops.call(this, Vue);
}
},
template: `
<div class="pipelines">
<div class="realtime-loading" v-if="isLoading">
<i class="fa fa-spinner fa-spin"></i>
</div>
template: `
<div class="pipelines">
<div class="realtime-loading" v-if="isLoading">
<i class="fa fa-spinner fa-spin"></i>
</div>
<div class="blank-state blank-state-no-icon"
v-if="!isLoading && state.pipelines.length === 0">
<h2 class="blank-state-title js-blank-state-title">
No pipelines to show
</h2>
</div>
<div class="blank-state blank-state-no-icon"
v-if="!isLoading && state.pipelines.length === 0">
<h2 class="blank-state-title js-blank-state-title">
No pipelines to show
</h2>
</div>
<div class="table-holder pipelines"
v-if="!isLoading && state.pipelines.length > 0">
<pipelines-table-component :pipelines="state.pipelines"/>
</div>
<div class="table-holder pipelines"
v-if="!isLoading && state.pipelines.length > 0">
<pipelines-table-component :pipelines="state.pipelines"/>
</div>
`,
});
})();
</div>
`,
});
......@@ -3,9 +3,12 @@
/* global Cookies */
/* global Flash */
require('./breakpoints');
window.Cookies = require('js-cookie');
require('./flash');
import Cookies from 'js-cookie';
import './breakpoints';
import './flash';
const PipelinesTable = require('./commit/pipelines/pipelines_table');
/* eslint-disable max-len */
// MergeRequestTabs
......@@ -97,6 +100,13 @@ require('./flash');
.off('click', this.clickTab);
}
destroy() {
this.unbindEvents();
if (this.commitPipelinesTable) {
this.commitPipelinesTable.$destroy();
}
}
showTab(e) {
e.preventDefault();
this.activateTab($(e.target).data('action'));
......@@ -131,12 +141,8 @@ require('./flash');
offset: 0,
});
} else if (action === 'pipelines') {
if (this.pipelinesLoaded) {
return;
}
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
gl.commits.pipelines.PipelinesTableBundle.$mount(pipelineTableViewEl);
this.pipelinesLoaded = true;
this.resetViewContainer();
this.loadPipelines();
} else {
this.expandView();
this.resetViewContainer();
......@@ -225,6 +231,18 @@ require('./flash');
});
}
loadPipelines() {
if (this.pipelinesLoaded) {
return;
}
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
// Could already be mounted from the `pipelines_bundle`
if (pipelineTableViewEl) {
this.commitPipelinesTable = new PipelinesTable().$mount(pipelineTableViewEl);
}
this.pipelinesLoaded = true;
}
loadDiff(source) {
if (this.diffsLoaded) {
return;
......
......@@ -2,17 +2,18 @@
/* eslint-disable no-param-reassign */
window.Vue = require('vue');
require('../vue_shared/components/table_pagination');
require('./store');
require('../vue_shared/components/pipelines_table');
const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_store');
const PipelinesTableComponent = require('../vue_shared/components/pipelines_table');
((gl) => {
gl.VuePipelines = Vue.extend({
components: {
'gl-pagination': gl.VueGlPagination,
'pipelines-table-component': gl.pipelines.PipelinesTableComponent,
'pipelines-table-component': PipelinesTableComponent,
},
data() {
......
......@@ -8,45 +8,39 @@ require('./pipelines_table_row');
* Given an array of objects, renders a table.
*/
(() => {
window.gl = window.gl || {};
gl.pipelines = gl.pipelines || {};
gl.pipelines.PipelinesTableComponent = Vue.component('pipelines-table-component', {
props: {
pipelines: {
type: Array,
required: true,
default: () => ([]),
},
module.exports = {
props: {
pipelines: {
type: Array,
required: true,
default: () => ([]),
},
components: {
'pipelines-table-row-component': gl.pipelines.PipelinesTableRowComponent,
},
},
components: {
'pipelines-table-row-component': gl.pipelines.PipelinesTableRowComponent,
},
template: `
<table class="table ci-table">
<thead>
<tr>
<th class="js-pipeline-status pipeline-status">Status</th>
<th class="js-pipeline-info pipeline-info">Pipeline</th>
<th class="js-pipeline-commit pipeline-commit">Commit</th>
<th class="js-pipeline-stages pipeline-stages">Stages</th>
<th class="js-pipeline-date pipeline-date"></th>
<th class="js-pipeline-actions pipeline-actions"></th>
</tr>
</thead>
<tbody>
<template v-for="model in pipelines"
v-bind:model="model">
<tr is="pipelines-table-row-component"
:pipeline="model"></tr>
</template>
</tbody>
</table>
`,
});
})();
template: `
<table class="table ci-table">
<thead>
<tr>
<th class="js-pipeline-status pipeline-status">Status</th>
<th class="js-pipeline-info pipeline-info">Pipeline</th>
<th class="js-pipeline-commit pipeline-commit">Commit</th>
<th class="js-pipeline-stages pipeline-stages">Stages</th>
<th class="js-pipeline-date pipeline-date"></th>
<th class="js-pipeline-actions pipeline-actions"></th>
</tr>
</thead>
<tbody>
<template v-for="model in pipelines"
v-bind:model="model">
<tr is="pipelines-table-row-component"
:pipeline="model"></tr>
</template>
</tbody>
</table>
`,
};
unless Rails.env.production?
namespace :karma do
desc 'GitLab | Karma | Generate fixtures for JavaScript tests'
RSpec::Core::RakeTask.new(:fixtures) do |t|
RSpec::Core::RakeTask.new(:fixtures, [:pattern]) do |t, args|
args.with_defaults(pattern: 'spec/javascripts/fixtures/*.rb')
ENV['NO_KNAPSACK'] = 'true'
t.pattern = 'spec/javascripts/fixtures/*.rb'
t.pattern = args[:pattern]
t.rspec_opts = '--format documentation'
end
......
/* global pipeline, Vue */
const PipelinesTable = require('~/commit/pipelines/pipelines_table');
require('~/flash');
require('~/commit/pipelines/pipelines_store');
require('~/commit/pipelines/pipelines_service');
require('~/commit/pipelines/pipelines_table');
require('~/vue_shared/vue_resource_interceptor');
const pipeline = require('./mock_data');
describe('Pipelines table in Commits and Merge requests', () => {
preloadFixtures('static/pipelines_table.html.raw');
let component;
beforeEach(() => {
loadFixtures('static/pipelines_table.html.raw');
});
......@@ -24,19 +27,20 @@ describe('Pipelines table in Commits and Merge requests', () => {
beforeEach(() => {
Vue.http.interceptors.push(pipelinesEmptyResponse);
component = new PipelinesTable({
el: document.querySelector('#commit-pipeline-table-view'),
});
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, pipelinesEmptyResponse,
);
component.$destroy();
});
it('should render the empty state', (done) => {
const component = new gl.commits.pipelines.PipelinesTableView({
el: document.querySelector('#commit-pipeline-table-view'),
});
setTimeout(() => {
expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain('No pipelines to show');
done();
......@@ -53,19 +57,20 @@ describe('Pipelines table in Commits and Merge requests', () => {
beforeEach(() => {
Vue.http.interceptors.push(pipelinesResponse);
component = new PipelinesTable({
el: document.querySelector('#commit-pipeline-table-view'),
});
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, pipelinesResponse,
);
component.$destroy();
});
it('should render a table with the received pipelines', (done) => {
const component = new gl.commits.pipelines.PipelinesTableView({
el: document.querySelector('#commit-pipeline-table-view'),
});
setTimeout(() => {
expect(component.$el.querySelectorAll('table > tbody > tr').length).toEqual(1);
done();
......@@ -83,19 +88,20 @@ describe('Pipelines table in Commits and Merge requests', () => {
beforeEach(() => {
Vue.http.interceptors.push(pipelinesErrorResponse);
component = new PipelinesTable({
el: document.querySelector('#commit-pipeline-table-view'),
});
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, pipelinesErrorResponse,
);
component.$destroy();
});
it('should render empty state', (done) => {
const component = new gl.commits.pipelines.PipelinesTableView({
el: document.querySelector('#commit-pipeline-table-view'),
});
setTimeout(() => {
expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain('No pipelines to show');
done();
......
......@@ -6,6 +6,15 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont
let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, namespace: namespace, path: 'merge-requests-project') }
let(:merge_request) { create(:merge_request, :with_diffs, source_project: project, target_project: project, description: '- [ ] Task List Item') }
let(:pipeline) do
create(
:ci_pipeline,
project: merge_request.source_project,
ref: merge_request.source_branch,
sha: merge_request.diff_head_sha
)
end
render_views
......@@ -18,7 +27,8 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont
end
it 'merge_requests/merge_request_with_task_list.html.raw' do |example|
merge_request = create(:merge_request, :with_diffs, source_project: project, target_project: project, description: '- [ ] Task List Item')
create(:ci_build, :pending, pipeline: pipeline)
render_merge_request(example.description, merge_request)
end
......
......@@ -38,6 +38,10 @@ require('vendor/jquery.scrollTo');
}
});
afterEach(function () {
this.class.destroy();
});
describe('#activateTab', function () {
beforeEach(function () {
spyOn($, 'ajax').and.callFake(function () {});
......@@ -200,6 +204,42 @@ require('vendor/jquery.scrollTo');
expect(this.subject('show')).toBe('/foo/bar/merge_requests/1');
});
});
describe('#tabShown', () => {
beforeEach(function () {
loadFixtures('merge_requests/merge_request_with_task_list.html.raw');
});
describe('with "Side-by-side"/parallel diff view', () => {
beforeEach(function () {
this.class.diffViewType = () => 'parallel';
});
it('maintains `container-limited` for pipelines tab', function (done) {
const asyncClick = function (selector) {
return new Promise((resolve) => {
setTimeout(() => {
document.querySelector(selector).click();
resolve();
});
});
};
asyncClick('.merge-request-tabs .pipelines-tab a')
.then(() => asyncClick('.merge-request-tabs .diffs-tab a'))
.then(() => asyncClick('.merge-request-tabs .pipelines-tab a'))
.then(() => {
const hasContainerLimitedClass = document.querySelector('.content-wrapper .container-fluid').classList.contains('container-limited');
expect(hasContainerLimitedClass).toBe(true);
})
.then(done)
.catch((err) => {
done.fail(`Something went wrong clicking MR tabs: ${err.message}\n${err.stack}`);
});
});
});
});
describe('#loadDiff', function () {
it('requires an absolute pathname', function () {
spyOn($, 'ajax').and.callFake(function (options) {
......
require('~/vue_shared/components/pipelines_table');
require('~/lib/utils/datetime_utility');
const Vue = require('vue');
const pipeline = require('../../commit/pipelines/mock_data');
const PipelinesTable = require('~/vue_shared/components/pipelines_table');
const PipelinesTableComponent = Vue.extend(PipelinesTable);
describe('Pipelines Table', () => {
preloadFixtures('static/environments/element.html.raw');
......@@ -12,7 +15,7 @@ describe('Pipelines Table', () => {
describe('table', () => {
let component;
beforeEach(() => {
component = new gl.pipelines.PipelinesTableComponent({
component = new PipelinesTableComponent({
el: document.querySelector('.test-dom-element'),
propsData: {
pipelines: [],
......@@ -21,6 +24,10 @@ describe('Pipelines Table', () => {
});
});
afterEach(() => {
component.$destroy();
});
it('should render a table', () => {
expect(component.$el).toEqual('TABLE');
});
......@@ -37,7 +44,7 @@ describe('Pipelines Table', () => {
describe('without data', () => {
it('should render an empty table', () => {
const component = new gl.pipelines.PipelinesTableComponent({
const component = new PipelinesTableComponent({
el: document.querySelector('.test-dom-element'),
propsData: {
pipelines: [],
......@@ -50,7 +57,7 @@ describe('Pipelines Table', () => {
describe('with data', () => {
it('should render rows', () => {
const component = new gl.pipelines.PipelinesTableComponent({
const component = new PipelinesTableComponent({
el: document.querySelector('.test-dom-element'),
propsData: {
pipelines: [pipeline],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment