diff options
author | Filipa Lacerda <filipa@gitlab.com> | 2017-02-13 16:11:11 +0000 |
---|---|---|
committer | Filipa Lacerda <filipa@gitlab.com> | 2017-02-15 19:57:48 +0000 |
commit | 1285d629064abce3aee8faafaa57492da6f8f163 (patch) | |
tree | 29f6e73839bd7fadfd7f98a0223b955b2c0165c6 | |
parent | 73accafe430f56cd3065774c6118de3db0a45734 (diff) | |
download | gitlab-ce-1285d629064abce3aee8faafaa57492da6f8f163.tar.gz |
Move change page param to utility function
Add tests
Adds folder name in the top of the table
9 files changed, 328 insertions, 72 deletions
diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 2cbfbcad023..e9ea6d91adb 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -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; diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index e40c97130ad..8401459977b 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -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}`; }, }, diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 b/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 index d2ca465351a..29f704c1a37 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 +++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 @@ -1,4 +1,5 @@ const EnvironmentsFolderComponent = require('./environments_folder_view'); +require('../../vue_shared/vue_resource_interceptor'); $(() => { window.gl = window.gl || {}; diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 b/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 index c5d1e2d4d9f..f67e5eab64a 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 +++ b/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 @@ -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}} diff --git a/app/assets/javascripts/lib/utils/common_utils.js.es6 b/app/assets/javascripts/lib/utils/common_utils.js.es6 index bcb3a706b51..276ff01ab89 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js.es6 +++ b/app/assets/javascripts/lib/utils/common_utils.js.es6 @@ -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); diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 2f4a3c80aeb..fed63158f41 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -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%; diff --git a/spec/javascripts/environments/environment_table_spec.js.es6 b/spec/javascripts/environments/environment_table_spec.js.es6 new file mode 100644 index 00000000000..be4330b5012 --- /dev/null +++ b/spec/javascripts/environments/environment_table_spec.js.es6 @@ -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'); + }); +}); diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js.es6 b/spec/javascripts/environments/folder/environments_folder_view_spec.js.es6 new file mode 100644 index 00000000000..6a95fabcea5 --- /dev/null +++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js.es6 @@ -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); + }); + }); +}); diff --git a/spec/javascripts/fixtures/environments/environments_folder_view.html.haml b/spec/javascripts/fixtures/environments/environments_folder_view.html.haml new file mode 100644 index 00000000000..aceec139730 --- /dev/null +++ b/spec/javascripts/fixtures/environments/environments_folder_view.html.haml @@ -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") } } |