Move change page param to utility function

Add tests

Adds folder name in the top of the table
This commit is contained in:
Filipa Lacerda 2017-02-13 16:11:11 +00:00
parent 73accafe43
commit 1285d62906
9 changed files with 328 additions and 72 deletions

View File

@ -50,15 +50,15 @@ module.exports = Vue.component('environment-component', {
},
canReadEnvironmentParsed() {
return this.$options.convertPermissionToBoolean(this.canReadEnvironment);
return gl.utils.convertPermissionToBoolean(this.canReadEnvironment);
},
canCreateDeploymentParsed() {
return this.$options.convertPermissionToBoolean(this.canCreateDeployment);
return gl.utils.convertPermissionToBoolean(this.canCreateDeployment);
},
canCreateEnvironmentParsed() {
return this.$options.convertPermissionToBoolean(this.canCreateEnvironment);
return gl.utils.convertPermissionToBoolean(this.canCreateEnvironment);
},
},
@ -97,15 +97,6 @@ module.exports = Vue.component('environment-component', {
});
},
/**
* Converts permission provided as strings to booleans.
* @param {String} string
* @returns {Boolean}
*/
convertPermissionToBoolean(string) {
return string === 'true';
},
methods: {
toggleRow(model) {
return this.store.toggleFolder(model.name);
@ -114,26 +105,11 @@ module.exports = Vue.component('environment-component', {
/**
* Will change the page number and update the URL.
*
* If no search params are present, we'll add param for page
* If param for page is already present, we'll update it
* If there are params but none for page, we'll add it at the end.
*
* @param {Number} pageNumber desired page to go to.
* @return {String}
*/
changePage(pageNumber) {
let param;
if (window.location.search.length === 0) {
param = `?page=${pageNumber}`;
}
if (window.location.search.indexOf('page') !== -1) {
param = window.location.search.replace(/page=\d/g, `page=${pageNumber}`);
}
if (window.location.search.length &&
window.location.search.indexOf('page') === -1) {
param = `${window.location.search}&page=${pageNumber}`;
}
const param = gl.utils.setParamInURL('page', pageNumber);
gl.utils.visitUrl(param);
return param;

View File

@ -99,7 +99,7 @@ module.exports = Vue.component('environment-item', {
* @returns {Boolean}
*/
hasStopAction() {
return this.model.latest['stop_action?'];
return this.model.latest && this.model.latest['stop_action?'];
},
/**
@ -414,7 +414,7 @@ module.exports = Vue.component('environment-item', {
* @return {String}
*/
folderUrl() {
return `${window.location.pathname}/folders/${this.model.latest.id}`;
return `${window.location.pathname}/folders/${this.model.name}`;
},
},

View File

@ -1,4 +1,5 @@
const EnvironmentsFolderComponent = require('./environments_folder_view');
require('../../vue_shared/vue_resource_interceptor');
$(() => {
window.gl = window.gl || {};

View File

@ -6,6 +6,7 @@ Vue.use(require('vue-resource'));
const EnvironmentsService = require('../services/environments_service');
const EnvironmentTable = require('../components/environments_table');
const Store = require('../stores/environments_store');
require('../../vue_shared/components/table_pagination');
require('../../lib/utils/common_utils');
module.exports = Vue.component('environment-folder-view', {
@ -19,9 +20,11 @@ module.exports = Vue.component('environment-folder-view', {
const environmentsData = document.querySelector('#environments-folder-list-view').dataset;
const store = new Store();
const endpoint = `${window.location.pathname}.json`;
const folderName = window.location.pathname.substr(window.location.pathname.lastIndexOf('/') + 1);
return {
store,
folderName,
endpoint,
state: store.state,
visibility: 'available',
@ -47,21 +50,30 @@ module.exports = Vue.component('environment-folder-view', {
},
canReadEnvironmentParsed() {
return this.$options.convertPermissionToBoolean(this.canReadEnvironment);
return gl.utils.convertPermissionToBoolean(this.canReadEnvironment);
},
canCreateDeploymentParsed() {
return this.$options.convertPermissionToBoolean(this.canCreateDeployment);
return gl.utils.convertPermissionToBoolean(this.canCreateDeployment);
},
/**
* URL to link in the stopped tab.
*
* @return {String}
*/
stoppedPath() {
return `${window.location.pathname}?scope=stopped`;
},
/**
* URL to link in the available tab.
*
* @return {String}
*/
availablePath() {
return window.location.pathname;
},
},
/**
@ -84,6 +96,8 @@ module.exports = Vue.component('environment-folder-view', {
body: resp.json(),
}))
.then((response) => {
this.store.storeAvailableCount(response.body.available_count);
this.store.storeStoppedCount(response.body.stopped_count);
this.store.storeEnvironments(response.body.environments);
this.store.storePagination(response.headers);
})
@ -96,45 +110,14 @@ module.exports = Vue.component('environment-folder-view', {
});
},
/**
* Transforms the url parameter into an object and
* returns the one requested.
*
* @param {String} param
* @returns {String} The value of the requested parameter.
*/
getQueryParameter(parameter) {
return window.location.search.substring(1).split('&').reduce((acc, param) => {
const paramSplited = param.split('=');
acc[paramSplited[0]] = paramSplited[1];
return acc;
}, {})[parameter];
},
methods: {
/**
* Will change the page number and update the URL.
*
* If no search params are present, we'll add param for page
* If param for page is already present, we'll update it
* If there are params but none for page, we'll add it at the end.
*
* @param {Number} pageNumber desired page to go to.
*/
changePage(pageNumber) {
let param;
if (window.location.search.length === 0) {
param = `?page=${pageNumber}`;
}
if (window.location.search.indexOf('page') !== -1) {
param = window.location.search.replace(/page=\d/g, `page=${pageNumber}`);
}
if (window.location.search.length &&
window.location.search.indexOf('page') === -1) {
param = `${window.location.search}&page=${pageNumber}`;
}
const param = gl.utils.setParamInURL('page', pageNumber);
gl.utils.visitUrl(param);
return param;
@ -143,13 +126,15 @@ module.exports = Vue.component('environment-folder-view', {
template: `
<div :class="cssContainerClass">
<div class="top-area">
<div class="top-area" v-if="!isLoading">
<h3>FOLDER NAME</h3>
<h4 class="js-folder-name environments-folder-name">
Environments / <b>{{folderName}}</b>
</h4>
<ul v-if="!isLoading" class="nav-links">
<li v-bind:class="{ 'active': scope === undefined || scope === 'available' }">
<a :href="availablePath">
<ul class="nav-links">
<li v-bind:class="{ 'active': scope === null || scope === 'available' }">
<a :href="availablePath" class="js-available-environments-folder-tab">
Available
<span class="badge js-available-environments-count">
{{state.availableCounter}}
@ -157,7 +142,7 @@ module.exports = Vue.component('environment-folder-view', {
</a>
</li>
<li v-bind:class="{ 'active' : scope === 'stopped' }">
<a :href="stoppedPath">
<a :href="stoppedPath" class="js-stopped-environments-folder-tab">
Stopped
<span class="badge js-stopped-environments-count">
{{state.stoppedCounter}}

View File

@ -241,5 +241,45 @@
acc[element] = DOMStringMapObject[element];
return acc;
}, {});
/**
* Updates the search parameter of a URL given the parameter and values provided.
*
* If no search params are present we'll add it.
* If param for page is already present, we'll update it
* If there are params but not for the given one, we'll add it at the end.
* Returns the new search parameters.
*
* @param {String} param
* @param {Number|String|Undefined|Null} value
* @return {String}
*/
w.gl.utils.setParamInURL = (param, value) => {
let search;
if (window.location.search.length === 0) {
search = `?${param}=${value}`;
}
if (window.location.search.indexOf(param) !== -1) {
const regex = new RegExp(param + '=\\d');
search = window.location.search.replace(regex, `${param}=${value}`);
}
if (window.location.search.length &&
window.location.search.indexOf(param) === -1) {
search = `${window.location.search}&${param}=${value}`;
}
return search;
};
/**
* Converts permission provided as strings to booleans.
*
* @param {String} string
* @returns {Boolean}
*/
w.gl.utils.convertPermissionToBoolean = permission => permission === 'true';
})(window);
}).call(this);

View File

@ -10,6 +10,11 @@
font-size: 34px;
}
.environments-folder-name {
font-weight: normal;
padding-top: 20px;
}
@media (max-width: $screen-xs-max) {
.environments-container {
width: 100%;

View File

@ -0,0 +1,30 @@
const EnvironmentTable = require('~/environments/components/environments_table');
describe('Environment item', () => {
preloadFixtures('static/environments/element.html.raw');
beforeEach(() => {
loadFixtures('static/environments/element.html.raw');
});
it('Should render a table', () => {
const mockItem = {
name: 'review',
size: 3,
isFolder: true,
latest: {
environment_path: 'url',
},
};
const component = new EnvironmentTable({
el: document.querySelector('.test-dom-element'),
propsData: {
environments: [{ mockItem }],
canCreateDeployment: false,
canReadEnvironment: true,
},
});
expect(component.$el.tagName).toEqual('TABLE');
});
});

View File

@ -0,0 +1,212 @@
const Vue = require('vue');
require('~/flash');
const EnvironmentsFolderViewComponent = require('~/environments/folder/environments_folder_view');
const { environmentsList } = require('../mock_data');
describe('Environments Folder View', () => {
preloadFixtures('static/environments/environments_folder_view.html.raw');
beforeEach(() => {
loadFixtures('static/environments/environments_folder_view.html.raw');
window.history.pushState({}, null, 'environments/folders/51');
});
let component;
describe('successfull request', () => {
const environmentsResponseInterceptor = (request, next) => {
next(request.respondWith(JSON.stringify({
environments: environmentsList,
stopped_count: 1,
available_count: 0,
}), {
status: 200,
headers: {
'X-nExt-pAge': '2',
'x-page': '1',
'X-Per-Page': '1',
'X-Prev-Page': '',
'X-TOTAL': '37',
'X-Total-Pages': '2',
},
}));
};
beforeEach(() => {
Vue.http.interceptors.push(environmentsResponseInterceptor);
component = new EnvironmentsFolderViewComponent({
el: document.querySelector('#environments-folder-list-view'),
});
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, environmentsResponseInterceptor,
);
});
it('should render a table with environments', (done) => {
setTimeout(() => {
expect(
component.$el.querySelectorAll('table tbody tr').length,
).toEqual(2);
done();
}, 0);
});
it('should render available tab with count', (done) => {
setTimeout(() => {
expect(
component.$el.querySelector('.js-available-environments-folder-tab').textContent,
).toContain('Available');
expect(
component.$el.querySelector('.js-available-environments-folder-tab .js-available-environments-count').textContent,
).toContain('0');
done();
}, 0);
});
it('should render stopped tab with count', (done) => {
setTimeout(() => {
expect(
component.$el.querySelector('.js-stopped-environments-folder-tab').textContent,
).toContain('Stopped');
expect(
component.$el.querySelector('.js-stopped-environments-folder-tab .js-stopped-environments-count').textContent,
).toContain('1');
done();
}, 0);
});
// FIX ME:
it('should render parent folder name', (done) => {
setTimeout(() => {
expect(
component.$el.querySelector('.js-folder-name'),
).toBe(null);
done();
}, 0);
});
describe('pagination', () => {
it('should render pagination', (done) => {
setTimeout(() => {
expect(
component.$el.querySelectorAll('.gl-pagination li').length,
).toEqual(5);
done();
}, 0);
});
it('should update url when no search params are present', (done) => {
spyOn(gl.utils, 'visitUrl');
setTimeout(() => {
component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2');
done();
}, 0);
});
it('should update url when page is already present', (done) => {
spyOn(gl.utils, 'visitUrl');
window.history.pushState({}, null, '?page=1');
setTimeout(() => {
component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2');
done();
}, 0);
});
it('should update url when page and scope are already present', (done) => {
spyOn(gl.utils, 'visitUrl');
window.history.pushState({}, null, '?scope=all&page=1');
setTimeout(() => {
component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
expect(gl.utils.visitUrl).toHaveBeenCalledWith('?scope=all&page=2');
done();
}, 0);
});
it('should update url when page and scope are already present and page is first param', (done) => {
spyOn(gl.utils, 'visitUrl');
window.history.pushState({}, null, '?page=1&scope=all');
setTimeout(() => {
component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2&scope=all');
done();
}, 0);
});
});
});
describe('unsuccessfull request', () => {
const environmentsErrorResponseInterceptor = (request, next) => {
next(request.respondWith(JSON.stringify([]), {
status: 500,
}));
};
beforeEach(() => {
Vue.http.interceptors.push(environmentsErrorResponseInterceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, environmentsErrorResponseInterceptor,
);
});
it('should not render a table', (done) => {
component = new EnvironmentsFolderViewComponent({
el: document.querySelector('#environments-folder-list-view'),
});
setTimeout(() => {
expect(
component.$el.querySelector('table'),
).toBe(null);
done();
}, 0);
});
it('should render available tab with count 0', (done) => {
setTimeout(() => {
expect(
component.$el.querySelector('.js-available-environments-folder-tab').textContent,
).toContain('Available');
expect(
component.$el.querySelector('.js-available-environments-folder-tab .js-available-environments-count').textContent,
).toContain('0');
done();
}, 0);
});
it('should render stopped tab with count 0', (done) => {
setTimeout(() => {
expect(
component.$el.querySelector('.js-stopped-environments-folder-tab').textContent,
).toContain('Stopped');
expect(
component.$el.querySelector('.js-stopped-environments-folder-tab .js-stopped-environments-count').textContent,
).toContain('0');
done();
}, 0);
});
it('should not render parent folder name', (done) => {
setTimeout(() => {
expect(
component.$el.querySelector('.js-folder-name'),
).toBe(null);
done();
}, 0);
});
});
});

View File

@ -0,0 +1,7 @@
%div
#environments-folder-list-view{ data: { "can-create-deployment" => "true",
"can-read-environment" => "true",
"css-class" => "",
"commit-icon-svg" => custom_icon("icon_commit"),
"terminal-icon-svg" => custom_icon("icon_terminal"),
"play-icon-svg" => custom_icon("icon_play") } }