From efa0502386a1868f7120ffd4291175291f0094ed Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 3 Feb 2017 14:28:44 +0100 Subject: [PATCH 01/73] Enable grouping and pagination in environmnets API --- .../projects/environments_controller.rb | 4 ++- .../projects/environments_controller_spec.rb | 26 +++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 0ec8f5bd64a..0d1095da6c2 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -16,7 +16,9 @@ class Projects::EnvironmentsController < Projects::ApplicationController format.html format.json do render json: EnvironmentSerializer - .new(project: @project, user: current_user) + .new(project: @project, user: @current_user) + .with_pagination(request, response) + .within_folders .represent(@environments) end end diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index 7ac1d62d1b1..4ec91738b9b 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -3,9 +3,13 @@ require 'spec_helper' describe Projects::EnvironmentsController do include ApiHelpers - let(:environment) { create(:environment) } - let(:project) { environment.project } - let(:user) { create(:user) } + let(:user) { create(:user) } + let(:project) { create(:empty_project) } + + let(:environment) do + create(:environment, name: 'production', + project: project) + end before do project.team << [user, :master] @@ -22,14 +26,20 @@ describe Projects::EnvironmentsController do end end - context 'when requesting JSON response' do + context 'when requesting JSON response for folders' do + before do + create(:environment, project: project, name: 'staging/review-1') + create(:environment, project: project, name: 'staging/review-2') + end + it 'responds with correct JSON' do get :index, environment_params(format: :json) - first_environment = json_response.first - - expect(first_environment).not_to be_empty - expect(first_environment['name']). to eq environment.name + expect(json_response.count).to eq 2 + expect(json_response.first['name']).to eq 'production' + expect(json_response.second['name']).to eq 'staging' + expect(json_response.second['size']).to eq 2 + expect(json_response.second['latest']['name']).to eq 'staging/review-2' end end end From b3309bb2fad36372b1e4821410691fa9f720bbe4 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 3 Feb 2017 19:47:56 +0000 Subject: [PATCH 02/73] Adjustments to receive new data schema --- .../components/environment.js.es6 | 35 +-- .../components/environment_item.js.es6 | 211 +++++++----------- .../stores/environments_store.js.es6 | 168 ++------------ .../stylesheets/pages/environments.scss | 16 +- .../environments/environment_item_spec.js.es6 | 137 +++++------- .../environments_store_spec.js.es6 | 48 +--- .../javascripts/environments/mock_data.js.es6 | 180 ++++----------- 7 files changed, 219 insertions(+), 576 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 91553bda4dc..93f65ba0ea8 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -69,12 +69,10 @@ require('./environment_item'); * Toggles loading property. */ created() { - gl.environmentsService = new EnvironmentsService(this.endpoint); + const scope = this.$options.getQueryParameter('scope') || this.visibility; + const endpoint = `${this.endpoint}?scope=${scope}`; - const scope = this.$options.getQueryParameter('scope'); - if (scope) { - this.store.storeVisibility(scope); - } + gl.environmentsService = new EnvironmentsService(endpoint); this.isLoading = true; @@ -82,6 +80,8 @@ require('./environment_item'); .then(resp => resp.json()) .then((json) => { this.store.storeEnvironments(json); + }) + .then(() => { this.isLoading = false; }) .catch(() => { @@ -165,8 +165,7 @@ require('./environment_item');

- New Environment @@ -174,7 +173,7 @@ require('./environment_item');
+ v-if="!isLoading && state.environments.length > 0"> @@ -187,31 +186,15 @@ require('./environment_item'); -
diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 33a99231315..ae37bc24396 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -15,12 +15,7 @@ require('./environment_terminal_button'); /** * Envrionment Item Component * - * Used in a hierarchical structure to show folders with children - * in a table. - * Recursive component based on [Tree View](https://vuejs.org/examples/tree-view.html) - * - * See this [issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/22539) - * for more information.15 + * Renders a table row for each environment. */ window.gl = window.gl || {}; @@ -45,11 +40,6 @@ require('./environment_terminal_button'); default: () => ({}), }, - toggleRow: { - type: Function, - required: false, - }, - canCreateDeployment: { type: Boolean, required: false, @@ -76,50 +66,9 @@ require('./environment_terminal_button'); type: String, required: false, }, - - }, - - data() { - return { - rowClass: { - 'children-row': this.model['vue-isChildren'], - }, - }; }, computed: { - - /** - * If an item has a `children` entry it means it is a folder. - * Folder items have different behaviours - it is possible to toggle - * them and show their children. - * - * @returns {Boolean|Undefined} - */ - isFolder() { - return this.model.children && this.model.children.length > 0; - }, - - /** - * If an item is inside a folder structure will return true. - * Used for css purposes. - * - * @returns {Boolean|undefined} - */ - isChildren() { - return this.model['vue-isChildren']; - }, - - /** - * Counts the number of environments in each folder. - * Used to show a badge with the counter. - * - * @returns {Number|Undefined} The number of environments for the current folder. - */ - childrenCounter() { - return this.model.children && this.model.children.length; - }, - /** * Verifies if `last_deployment` key exists in the current Envrionment. * This key is required to render most of the html - this method works has @@ -128,8 +77,8 @@ require('./environment_terminal_button'); * @returns {Boolean} */ hasLastDeploymentKey() { - if (this.model.last_deployment && - !this.$options.isObjectEmpty(this.model.last_deployment)) { + if (this.model.latest.last_deployment && + !this.$options.isObjectEmpty(this.model.latest.last_deployment)) { return true; } return false; @@ -142,8 +91,9 @@ require('./environment_terminal_button'); * @returns {Boolean|Undefined} */ hasManualActions() { - return this.model.last_deployment && this.model.last_deployment.manual_actions && - this.model.last_deployment.manual_actions.length > 0; + return this.model.latest.last_deployment && + this.model.latest.last_deployment.manual_actions && + this.model.latest.last_deployment.manual_actions.length > 0; }, /** @@ -163,8 +113,8 @@ require('./environment_terminal_button'); */ canRetry() { return this.hasLastDeploymentKey && - this.model.last_deployment && - this.model.last_deployment.deployable; + this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable; }, /** @@ -173,9 +123,9 @@ require('./environment_terminal_button'); * @returns {Boolean|Undefined} */ canShowDate() { - return this.model.last_deployment && - this.model.last_deployment.deployable && - this.model.last_deployment.deployable !== undefined; + return this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable && + this.model.latest.last_deployment.deployable !== undefined; }, /** @@ -185,7 +135,7 @@ require('./environment_terminal_button'); */ createdDate() { return gl.environmentsList.timeagoInstance.format( - this.model.last_deployment.deployable.created_at, + this.model.latest.last_deployment.deployable.created_at, ); }, @@ -196,7 +146,7 @@ require('./environment_terminal_button'); */ manualActions() { if (this.hasManualActions) { - return this.model.last_deployment.manual_actions.map((action) => { + return this.model.latest.last_deployment.manual_actions.map((action) => { const parsedAction = { name: gl.text.humanize(action.name), play_path: action.play_path, @@ -213,10 +163,10 @@ require('./environment_terminal_button'); * @returns {String} */ userImageAltDescription() { - if (this.model.last_deployment && - this.model.last_deployment.user && - this.model.last_deployment.user.username) { - return `${this.model.last_deployment.user.username}'s avatar'`; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.user && + this.model.latest.last_deployment.user.username) { + return `${this.model.latest.last_deployment.user.username}'s avatar'`; } return ''; }, @@ -227,9 +177,9 @@ require('./environment_terminal_button'); * @returns {String|Undefined} */ commitTag() { - if (this.model.last_deployment && - this.model.last_deployment.tag) { - return this.model.last_deployment.tag; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.tag) { + return this.model.latest.last_deployment.tag; } return undefined; }, @@ -240,8 +190,9 @@ require('./environment_terminal_button'); * @returns {Object|Undefined} */ commitRef() { - if (this.model.last_deployment && this.model.last_deployment.ref) { - return this.model.last_deployment.ref; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.ref) { + return this.model.latest.last_deployment.ref; } return undefined; }, @@ -252,10 +203,10 @@ require('./environment_terminal_button'); * @returns {String|Undefined} */ commitUrl() { - if (this.model.last_deployment && - this.model.last_deployment.commit && - this.model.last_deployment.commit.commit_path) { - return this.model.last_deployment.commit.commit_path; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.commit_path) { + return this.model.latest.last_deployment.commit.commit_path; } return undefined; }, @@ -266,10 +217,10 @@ require('./environment_terminal_button'); * @returns {String|Undefined} */ commitShortSha() { - if (this.model.last_deployment && - this.model.last_deployment.commit && - this.model.last_deployment.commit.short_id) { - return this.model.last_deployment.commit.short_id; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.short_id) { + return this.model.latest.last_deployment.commit.short_id; } return undefined; }, @@ -280,10 +231,10 @@ require('./environment_terminal_button'); * @returns {String|Undefined} */ commitTitle() { - if (this.model.last_deployment && - this.model.last_deployment.commit && - this.model.last_deployment.commit.title) { - return this.model.last_deployment.commit.title; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.title) { + return this.model.latest.last_deployment.commit.title; } return undefined; }, @@ -294,10 +245,10 @@ require('./environment_terminal_button'); * @returns {Object|Undefined} */ commitAuthor() { - if (this.model.last_deployment && - this.model.last_deployment.commit && - this.model.last_deployment.commit.author) { - return this.model.last_deployment.commit.author; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.author) { + return this.model.latest.last_deployment.commit.author; } return undefined; @@ -309,10 +260,10 @@ require('./environment_terminal_button'); * @returns {String|Undefined} */ retryUrl() { - if (this.model.last_deployment && - this.model.last_deployment.deployable && - this.model.last_deployment.deployable.retry_path) { - return this.model.last_deployment.deployable.retry_path; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable && + this.model.latest.last_deployment.deployable.retry_path) { + return this.model.latest.last_deployment.deployable.retry_path; } return undefined; }, @@ -323,7 +274,8 @@ require('./environment_terminal_button'); * @returns {Boolean|Undefined} */ isLastDeployment() { - return this.model.last_deployment && this.model.last_deployment['last?']; + return this.model.latest.last_deployment && + this.model.latest.last_deployment['last?']; }, /** @@ -332,9 +284,9 @@ require('./environment_terminal_button'); * @returns {String} */ buildName() { - if (this.model.last_deployment && - this.model.last_deployment.deployable) { - return `${this.model.last_deployment.deployable.name} #${this.model.last_deployment.deployable.id}`; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable) { + return `${this.model.latest.last_deployment.deployable.name} #${this.model.latest.last_deployment.deployable.id}`; } return ''; }, @@ -345,9 +297,9 @@ require('./environment_terminal_button'); * @returns {String} */ deploymentInternalId() { - if (this.model.last_deployment && - this.model.last_deployment.iid) { - return `#${this.model.last_deployment.iid}`; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.iid) { + return `#${this.model.latest.last_deployment.iid}`; } return ''; }, @@ -358,8 +310,8 @@ require('./environment_terminal_button'); * @returns {Boolean} */ deploymentHasUser() { - return !this.$options.isObjectEmpty(this.model.last_deployment) && - !this.$options.isObjectEmpty(this.model.last_deployment.user); + return !this.$options.isObjectEmpty(this.model.latest.last_deployment) && + !this.$options.isObjectEmpty(this.model.latest.last_deployment.user); }, /** @@ -369,9 +321,9 @@ require('./environment_terminal_button'); * @returns {Object} */ deploymentUser() { - if (!this.$options.isObjectEmpty(this.model.last_deployment) && - !this.$options.isObjectEmpty(this.model.last_deployment.user)) { - return this.model.last_deployment.user; + if (!this.$options.isObjectEmpty(this.model.latest.last_deployment) && + !this.$options.isObjectEmpty(this.model.latest.last_deployment.user)) { + return this.model.latest.last_deployment.user; } return {}; }, @@ -384,9 +336,9 @@ require('./environment_terminal_button'); * @returns {Boolean} */ shouldRenderBuildName() { - return !this.isFolder && - !this.$options.isObjectEmpty(this.model.last_deployment) && - !this.$options.isObjectEmpty(this.model.last_deployment.deployable); + return !this.model.isFolder && + !this.$options.isObjectEmpty(this.model.latest.last_deployment) && + !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); }, /** @@ -397,9 +349,9 @@ require('./environment_terminal_button'); * @returns {Boolean} */ shouldRenderDeploymentID() { - return !this.isFolder && - !this.$options.isObjectEmpty(this.model.last_deployment) && - this.model.last_deployment.iid !== undefined; + return !this.model.isFolder && + !this.$options.isObjectEmpty(this.model.latest.last_deployment) && + this.model.latest.last_deployment.iid !== undefined; }, }, @@ -420,16 +372,16 @@ require('./environment_terminal_button'); template: ` - -
+ + :href="model.latest.environment_path"> {{model.name}} - + - - + + @@ -437,9 +389,9 @@ require('./environment_terminal_button'); - {{childrenCounter}} + {{model.size}} - + @@ -447,7 +399,7 @@ require('./environment_terminal_button'); {{deploymentInternalId}} - + by + :href="model.latest.last_deployment.deployable.build_path"> {{buildName}} -
+
-

+

No deployments yet

- {{createdDate}} -
+
-
+ :external-url="model.latest.external_url">
+ :stop-url="model.latest.stop_path">
-
+ :terminal-path="model.latest.terminal_path">
diff --git a/app/assets/javascripts/environments/stores/environments_store.js.es6 b/app/assets/javascripts/environments/stores/environments_store.js.es6 index 9b4090100da..a533b8b61d6 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js.es6 +++ b/app/assets/javascripts/environments/stores/environments_store.js.es6 @@ -10,181 +10,41 @@ this.state.environments = []; this.state.stoppedCounter = 0; this.state.availableCounter = 0; - this.state.visibility = 'available'; this.state.filteredEnvironments = []; return this; }, /** - * In order to display a tree view we need to modify the received - * data in to a tree structure based on `environment_type` - * sorted alphabetically. - * In each children a `vue-` property will be added. This property will be - * used to know if an item is a children mostly for css purposes. This is - * needed because the children row is a fragment instance and therfore does - * not accept non-prop attributes. * + * Stores the received environments. * - * @example - * it will transform this: - * [ - * { name: "environment", environment_type: "review" }, - * { name: "environment_1", environment_type: null } - * { name: "environment_2, environment_type: "review" } - * ] - * into this: - * [ - * { name: "review", children: - * [ - * { name: "environment", environment_type: "review", vue-isChildren: true}, - * { name: "environment_2", environment_type: "review", vue-isChildren: true} - * ] - * }, - * {name: "environment_1", environment_type: null} - * ] + * Each environment has the following schema + * { name: String, size: Number, latest: Object } * + * If the `size` is bigger than 1, it means it should be rendered as a folder. + * In those cases we add `isFolder` key in order to render it properly. * - * @param {Array} environments List of environments. - * @returns {Array} Tree structured array with the received environments. + * @param {Array} environments + * @returns {Array} */ storeEnvironments(environments = []) { - this.state.stoppedCounter = this.countByState(environments, 'stopped'); - this.state.availableCounter = this.countByState(environments, 'available'); - - const environmentsTree = environments.reduce((acc, environment) => { - if (environment.environment_type !== null) { - const occurs = acc.filter(element => element.children && - element.name === environment.environment_type); - - environment['vue-isChildren'] = true; - - if (occurs.length) { - acc[acc.indexOf(occurs[0])].children.push(environment); - acc[acc.indexOf(occurs[0])].children.slice().sort(this.sortByName); - } else { - acc.push({ - name: environment.environment_type, - children: [environment], - isOpen: false, - 'vue-isChildren': environment['vue-isChildren'], - }); - } - } else { - acc.push(environment); - } - - return acc; - }, []).slice().sort(this.sortByName); - - this.state.environments = environmentsTree; - - this.filterEnvironmentsByVisibility(this.state.environments); - - return environmentsTree; - }, - - storeVisibility(visibility) { - this.state.visibility = visibility; - }, - /** - * Given the visibility prop provided by the url query parameter and which - * changes according to the active tab we need to filter which environments - * should be visible. - * - * The environments array is a recursive tree structure and we need to filter - * both root level environments and children environments. - * - * In order to acomplish that, both `filterState` and `filterEnvironmentsByVisibility` - * functions work together. - * The first one works as the filter that verifies if the given environment matches - * the given state. - * The second guarantees both root level and children elements are filtered as well. - * - * Given array of environments will return only - * the environments that match the state stored. - * - * @param {Array} array - * @return {Array} - */ - filterEnvironmentsByVisibility(arr) { - const filteredEnvironments = arr.map((item) => { - if (item.children) { - const filteredChildren = this.filterEnvironmentsByVisibility( - item.children, - ).filter(Boolean); - - if (filteredChildren.length) { - item.children = filteredChildren; - return item; - } - } - - return this.filterState(this.state.visibility, item); - }).filter(Boolean); - - this.state.filteredEnvironments = filteredEnvironments; - return filteredEnvironments; - }, - - /** - * Given the state and the environment, - * returns only if the environment state matches the one provided. - * - * @param {String} state - * @param {Object} environment - * @return {Object} - */ - filterState(state, environment) { - return environment.state === state && environment; - }, - - /** - * Toggles folder open property given the environment type. - * - * @param {String} envType - * @return {Array} - */ - toggleFolder(envType) { - const environments = this.state.environments; - - const environmentsCopy = environments.map((env) => { - if (env['vue-isChildren'] && env.name === envType) { - env.isOpen = !env.isOpen; + const filteredEnvironments = environments.map((env) => { + if (env.size > 1) { + return Object.assign({}, env, { isFolder: true }); } return env; }); - this.state.environments = environmentsCopy; + this.state.environments = filteredEnvironments; - return environmentsCopy; + return filteredEnvironments; }, - /** - * Given an array of environments, returns the number of environments - * that have the given state. - * - * @param {Array} environments - * @param {String} state - * @returns {Number} - */ - countByState(environments, state) { - return environments.filter(env => env.state === state).length; + storeCounts() { + //TODO }, - /** - * Sorts the two objects provided by their name. - * - * @param {Object} a - * @param {Object} b - * @returns {Number} - */ - sortByName(a, b) { - const nameA = a.name.toUpperCase(); - const nameB = b.name.toUpperCase(); - - return nameA < nameB ? -1 : nameA > nameB ? 1 : 0; // eslint-disable-line - }, }; })(); diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 778ef01430e..1d4d85ba6de 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -110,17 +110,19 @@ } } - .children-row .environment-name { - margin-left: 17px; - margin-right: -17px; - } - .folder-icon { - padding: 0 5px 0 0; + margin-right: 3px; + color: $gl-text-color-secondary; + + .fa:nth-child(1) { + margin-right: 3px; + } } .folder-name { cursor: pointer; + text-decoration: none; + color: $gl-text-color-secondary; } } @@ -135,4 +137,4 @@ margin-right: 0; } } -} \ No newline at end of file +} diff --git a/spec/javascripts/environments/environment_item_spec.js.es6 b/spec/javascripts/environments/environment_item_spec.js.es6 index d87cc0996c9..14478f1401d 100644 --- a/spec/javascripts/environments/environment_item_spec.js.es6 +++ b/spec/javascripts/environments/environment_item_spec.js.es6 @@ -14,33 +14,13 @@ describe('Environment item', () => { beforeEach(() => { mockItem = { name: 'review', - children: [ - { - name: 'review-app', - id: 1, - state: 'available', - external_url: '', - last_deployment: {}, - created_at: '2016-11-07T11:11:16.525Z', - updated_at: '2016-11-10T15:55:58.778Z', - }, - { - name: 'production', - id: 2, - state: 'available', - external_url: '', - last_deployment: {}, - created_at: '2016-11-07T11:11:16.525Z', - updated_at: '2016-11-10T15:55:58.778Z', - }, - ], + size: 3 }; component = new window.gl.environmentsList.EnvironmentItem({ el: document.querySelector('tr#environment-row'), propsData: { model: mockItem, - toggleRow: () => {}, canCreateDeployment: false, canReadEnvironment: true, }, @@ -53,7 +33,7 @@ describe('Environment item', () => { }); it('Should render the number of children in a badge', () => { - expect(component.$el.querySelector('.folder-name .badge').textContent).toContain(mockItem.children.length); + expect(component.$el.querySelector('.folder-name .badge').textContent).toContain(mockItem.size); }); }); @@ -63,38 +43,23 @@ describe('Environment item', () => { beforeEach(() => { environment = { - id: 31, name: 'production', - state: 'stopped', - external_url: 'http://external.com', - environment_type: null, - last_deployment: { - id: 66, - iid: 6, - sha: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - ref: { - name: 'master', - ref_path: 'root/ci-folders/tree/master', - }, - tag: true, - 'last?': true, - user: { - name: 'Administrator', - username: 'root', - id: 1, - state: 'active', - avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', - web_url: 'http://localhost:3000/root', - }, - commit: { - id: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - short_id: '500aabcb', - title: 'Update .gitlab-ci.yml', - author_name: 'Administrator', - author_email: 'admin@example.com', - created_at: '2016-11-07T18:28:13.000+00:00', - message: 'Update .gitlab-ci.yml', - author: { + size: 1, + latest: { + state: 'stopped', + external_url: 'http://external.com', + environment_type: null, + last_deployment: { + id: 66, + iid: 6, + sha: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', + ref: { + name: 'master', + ref_path: 'root/ci-folders/tree/master', + }, + tag: true, + 'last?': true, + user: { name: 'Administrator', username: 'root', id: 1, @@ -102,34 +67,50 @@ describe('Environment item', () => { avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', web_url: 'http://localhost:3000/root', }, - commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - }, - deployable: { - id: 1279, - name: 'deploy', - build_path: '/root/ci-folders/builds/1279', - retry_path: '/root/ci-folders/builds/1279/retry', - created_at: '2016-11-29T18:11:58.430Z', - updated_at: '2016-11-29T18:11:58.430Z', - }, - manual_actions: [ - { - name: 'action', - play_path: '/play', + commit: { + id: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', + short_id: '500aabcb', + title: 'Update .gitlab-ci.yml', + author_name: 'Administrator', + author_email: 'admin@example.com', + created_at: '2016-11-07T18:28:13.000+00:00', + message: 'Update .gitlab-ci.yml', + author: { + name: 'Administrator', + username: 'root', + id: 1, + state: 'active', + avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', + web_url: 'http://localhost:3000/root', + }, + commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd', }, - ], + deployable: { + id: 1279, + name: 'deploy', + build_path: '/root/ci-folders/builds/1279', + retry_path: '/root/ci-folders/builds/1279/retry', + created_at: '2016-11-29T18:11:58.430Z', + updated_at: '2016-11-29T18:11:58.430Z', + }, + manual_actions: [ + { + name: 'action', + play_path: '/play', + }, + ], + }, + 'stop_action?': true, + environment_path: 'root/ci-folders/environments/31', + created_at: '2016-11-07T11:11:16.525Z', + updated_at: '2016-11-10T15:55:58.778Z', }, - 'stop_action?': true, - environment_path: 'root/ci-folders/environments/31', - created_at: '2016-11-07T11:11:16.525Z', - updated_at: '2016-11-10T15:55:58.778Z', }; component = new window.gl.environmentsList.EnvironmentItem({ el: document.querySelector('tr#environment-row'), propsData: { model: environment, - toggleRow: () => {}, canCreateDeployment: true, canReadEnvironment: true, }, @@ -144,7 +125,7 @@ describe('Environment item', () => { it('should render deployment internal id', () => { expect( component.$el.querySelector('.deployment-column span').textContent, - ).toContain(environment.last_deployment.iid); + ).toContain(environment.latest.last_deployment.iid); expect( component.$el.querySelector('.deployment-column span').textContent, @@ -154,7 +135,7 @@ describe('Environment item', () => { it('should render last deployment date', () => { const timeagoInstance = new timeago(); // eslint-disable-line const formatedDate = timeagoInstance.format( - environment.last_deployment.deployable.created_at, + environment.latest.last_deployment.deployable.created_at, ); expect( @@ -166,7 +147,7 @@ describe('Environment item', () => { it('should render user avatar with link to profile', () => { expect( component.$el.querySelector('.js-deploy-user-container').getAttribute('href'), - ).toEqual(environment.last_deployment.user.web_url); + ).toEqual(environment.latest.last_deployment.user.web_url); }); }); @@ -174,13 +155,13 @@ describe('Environment item', () => { it('Should link to build url provided', () => { expect( component.$el.querySelector('.build-link').getAttribute('href'), - ).toEqual(environment.last_deployment.deployable.build_path); + ).toEqual(environment.latest.last_deployment.deployable.build_path); }); it('Should render deployable name and id', () => { expect( component.$el.querySelector('.build-link').getAttribute('href'), - ).toEqual(environment.last_deployment.deployable.build_path); + ).toEqual(environment.latest.last_deployment.deployable.build_path); }); }); diff --git a/spec/javascripts/environments/environments_store_spec.js.es6 b/spec/javascripts/environments/environments_store_spec.js.es6 index 9a8300d3832..d073e120290 100644 --- a/spec/javascripts/environments/environments_store_spec.js.es6 +++ b/spec/javascripts/environments/environments_store_spec.js.es6 @@ -20,50 +20,10 @@ require('./mock_data'); gl.environmentsList.EnvironmentsStore.storeEnvironments(environmentsList); }); - it('should count stopped environments and save the count in the state', () => { - expect(gl.environmentsList.EnvironmentsStore.state.stoppedCounter).toBe(1); - }); - - it('should count available environments and save the count in the state', () => { - expect(gl.environmentsList.EnvironmentsStore.state.availableCounter).toBe(3); - }); - - it('should store environments with same environment_type as sibilings', () => { - expect(gl.environmentsList.EnvironmentsStore.state.environments.length).toBe(3); - - const parentFolder = gl.environmentsList.EnvironmentsStore.state.environments - .filter(env => env.children && env.children.length > 0); - - expect(parentFolder[0].children.length).toBe(2); - expect(parentFolder[0].children[0].environment_type).toBe('review'); - expect(parentFolder[0].children[1].environment_type).toBe('review'); - expect(parentFolder[0].children[0].name).toBe('test-environment'); - expect(parentFolder[0].children[1].name).toBe('test-environment-1'); - }); - - it('should sort the environments alphabetically', () => { - const { environments } = gl.environmentsList.EnvironmentsStore.state; - - expect(environments[0].name).toBe('production'); - expect(environments[1].name).toBe('review'); - expect(environments[1].children[0].name).toBe('test-environment'); - expect(environments[1].children[1].name).toBe('test-environment-1'); - expect(environments[2].name).toBe('review_app'); - }); - }); - - describe('toggleFolder', () => { - beforeEach(() => { - gl.environmentsList.EnvironmentsStore.storeEnvironments(environmentsList); - }); - - it('should toggle the open property for the given environment', () => { - gl.environmentsList.EnvironmentsStore.toggleFolder('review'); - - const { environments } = gl.environmentsList.EnvironmentsStore.state; - const environment = environments.filter(env => env['vue-isChildren'] === true && env.name === 'review'); - - expect(environment[0].isOpen).toBe(true); + it('should store environments', () => { + expect( + gl.environmentsList.EnvironmentsStore.state.environments.length + ).toBe(environmentsList.length); }); }); }); diff --git a/spec/javascripts/environments/mock_data.js.es6 b/spec/javascripts/environments/mock_data.js.es6 index 80e1cbc6f4d..91595c049b0 100644 --- a/spec/javascripts/environments/mock_data.js.es6 +++ b/spec/javascripts/environments/mock_data.js.es6 @@ -1,153 +1,59 @@ const environmentsList = [ { - id: 31, - name: 'production', - state: 'available', - external_url: 'https://www.gitlab.com', - environment_type: null, - last_deployment: { - id: 64, - iid: 5, - sha: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - ref: { - name: 'master', - ref_url: 'http://localhost:3000/root/ci-folders/tree/master', - }, - tag: false, - 'last?': true, - user: { - name: 'Administrator', - username: 'root', - id: 1, - state: 'active', - avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', - web_url: 'http://localhost:3000/root', - }, - commit: { - id: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - short_id: '500aabcb', - title: 'Update .gitlab-ci.yml', - author_name: 'Administrator', - author_email: 'admin@example.com', - created_at: '2016-11-07T18:28:13.000+00:00', - message: 'Update .gitlab-ci.yml', - author: { - name: 'Administrator', - username: 'root', - id: 1, - state: 'active', - avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', - web_url: 'http://localhost:3000/root', - }, - commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - }, - deployable: { - id: 1278, - name: 'build', - build_path: '/root/ci-folders/builds/1278', - retry_path: '/root/ci-folders/builds/1278/retry', - }, - manual_actions: [], + name: 'DEV', + size: 1, + latest: { + id: 7, + name: 'DEV', + state: 'available', + external_url: null, + environment_type: null, + last_deployment: null, + 'stop_action?': false, + environment_path: '/root/review-app/environments/7', + stop_path: '/root/review-app/environments/7/stop', + created_at: '2017-01-31T10:53:46.894Z', + updated_at: '2017-01-31T10:53:46.894Z', }, - 'stop_action?': true, - environment_path: '/root/ci-folders/environments/31', - created_at: '2016-11-07T11:11:16.525Z', - updated_at: '2016-11-07T11:11:16.525Z', }, { - id: 32, - name: 'review_app', - state: 'stopped', - external_url: 'https://www.gitlab.com', - environment_type: null, - last_deployment: { - id: 64, - iid: 5, - sha: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - ref: { - name: 'master', - ref_url: 'http://localhost:3000/root/ci-folders/tree/master', - }, - tag: false, - 'last?': true, - user: { - name: 'Administrator', - username: 'root', - id: 1, - state: 'active', - avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', - web_url: 'http://localhost:3000/root', - }, - commit: { - id: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - short_id: '500aabcb', - title: 'Update .gitlab-ci.yml', - author_name: 'Administrator', - author_email: 'admin@example.com', - created_at: '2016-11-07T18:28:13.000+00:00', - message: 'Update .gitlab-ci.yml', - author: { - name: 'Administrator', - username: 'root', - id: 1, - state: 'active', - avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', - web_url: 'http://localhost:3000/root', - }, - commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - }, - deployable: { - id: 1278, - name: 'build', - build_path: '/root/ci-folders/builds/1278', - retry_path: '/root/ci-folders/builds/1278/retry', - }, - manual_actions: [], + name: 'build', + size: 5, + latest: { + id: 12, + name: 'build/update-README', + state: 'available', + external_url: null, + environment_type: 'build', + last_deployment: null, + 'stop_action?': false, + environment_path: '/root/review-app/environments/12', + stop_path: '/root/review-app/environments/12/stop', + created_at: '2017-02-01T19:42:18.400Z', + updated_at: '2017-02-01T19:42:18.400Z', }, - 'stop_action?': false, - environment_path: '/root/ci-folders/environments/31', - created_at: '2016-11-07T11:11:16.525Z', - updated_at: '2016-11-07T11:11:16.525Z', - }, - { - id: 33, - name: 'test-environment', - state: 'available', - environment_type: 'review', - last_deployment: null, - 'stop_action?': true, - environment_path: '/root/ci-folders/environments/31', - created_at: '2016-11-07T11:11:16.525Z', - updated_at: '2016-11-07T11:11:16.525Z', - }, - { - id: 34, - name: 'test-environment-1', - state: 'available', - environment_type: 'review', - last_deployment: null, - 'stop_action?': true, - environment_path: '/root/ci-folders/environments/31', - created_at: '2016-11-07T11:11:16.525Z', - updated_at: '2016-11-07T11:11:16.525Z', }, ]; window.environmentsList = environmentsList; const environment = { - id: 4, - name: 'production', - state: 'available', - external_url: 'http://production.', - environment_type: null, - last_deployment: {}, - 'stop_action?': false, - environment_path: '/root/review-app/environments/4', - stop_path: '/root/review-app/environments/4/stop', - created_at: '2016-12-16T11:51:04.690Z', - updated_at: '2016-12-16T12:04:51.133Z', + name: 'DEV', + size: 1, + latest: { + id: 7, + name: 'DEV', + state: 'available', + external_url: null, + environment_type: null, + last_deployment: null, + 'stop_action?': false, + environment_path: '/root/review-app/environments/7', + stop_path: '/root/review-app/environments/7/stop', + created_at: '2017-01-31T10:53:46.894Z', + updated_at: '2017-01-31T10:53:46.894Z', + }, }; window.environment = environment; From 2aeb45bdb55a634a490bf535242a6f8c10aaa938 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 4 Feb 2017 10:38:16 +0100 Subject: [PATCH 03/73] Add support for environment scopes in controller --- .../projects/environments_controller.rb | 19 +++--- .../projects/environments_controller_spec.rb | 59 +++++++++++++++---- 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 0d1095da6c2..34b081b3e41 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -9,17 +9,22 @@ class Projects::EnvironmentsController < Projects::ApplicationController before_action :verify_api_request!, only: :terminal_websocket_authorize def index - @scope = params[:scope] - @environments = project.environments.includes(:last_deployment) + @environments = project.environments + .includes(:last_deployment) + .with_state(params[:scope] || :available) respond_to do |format| format.html format.json do - render json: EnvironmentSerializer - .new(project: @project, user: @current_user) - .with_pagination(request, response) - .within_folders - .represent(@environments) + render json: { + environments: EnvironmentSerializer + .new(project: @project, user: @current_user) + .with_pagination(request, response) + .within_folders + .represent(@environments), + available_count: project.environments.available.count, + stopped_count: project.environments.stopped.count + } end end end diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index 4ec91738b9b..84d119f1867 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -7,8 +7,7 @@ describe Projects::EnvironmentsController do let(:project) { create(:empty_project) } let(:environment) do - create(:environment, name: 'production', - project: project) + create(:environment, name: 'production', project: project) end before do @@ -28,18 +27,56 @@ describe Projects::EnvironmentsController do context 'when requesting JSON response for folders' do before do - create(:environment, project: project, name: 'staging/review-1') - create(:environment, project: project, name: 'staging/review-2') + create(:environment, project: project, + name: 'staging/review-1', + state: :available) + + create(:environment, project: project, + name: 'staging/review-2', + state: :available) + + create(:environment, project: project, + name: 'staging/review-3', + state: :stopped) end - it 'responds with correct JSON' do - get :index, environment_params(format: :json) + let(:environments) { json_response['environments'] } - expect(json_response.count).to eq 2 - expect(json_response.first['name']).to eq 'production' - expect(json_response.second['name']).to eq 'staging' - expect(json_response.second['size']).to eq 2 - expect(json_response.second['latest']['name']).to eq 'staging/review-2' + context 'when requesting available environments scope' do + before do + get :index, environment_params(format: :json, scope: :available) + end + + it 'responds with a payload describing available environments' do + expect(environments.count).to eq 2 + expect(environments.first['name']).to eq 'production' + expect(environments.second['name']).to eq 'staging' + expect(environments.second['size']).to eq 2 + expect(environments.second['latest']['name']).to eq 'staging/review-2' + end + + it 'contains values describing environment scopes sizes' do + expect(json_response['available_count']).to eq 3 + expect(json_response['stopped_count']).to eq 1 + end + end + + context 'when requesting stopped environments scope' do + before do + get :index, environment_params(format: :json, scope: :stopped) + end + + it 'responds with a payload describing stopped environments' do + expect(environments.count).to eq 1 + expect(environments.first['name']).to eq 'staging' + expect(environments.first['size']).to eq 1 + expect(environments.first['latest']['name']).to eq 'staging/review-3' + end + + it 'contains values describing environment scopes sizes' do + expect(json_response['available_count']).to eq 3 + expect(json_response['stopped_count']).to eq 1 + end end end end From 71899e10878455277b7e2ed120d9424489a9d72b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 3 Feb 2017 21:10:50 +0000 Subject: [PATCH 04/73] Adjustments for the new response with counters a --- .../components/environment.js.es6 | 8 +++++-- .../stores/environments_store.js.es6 | 22 +++++++++++++++++-- .../environments_store_spec.js.es6 | 2 +- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 93f65ba0ea8..3e899e5895b 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -7,6 +7,7 @@ window.Vue = require('vue'); window.Vue.use(require('vue-resource')); require('../services/environments_service'); require('./environment_item'); +require('../../vue_pagination/index'); (() => { window.gl = window.gl || {}; @@ -79,7 +80,9 @@ require('./environment_item'); return gl.environmentsService.all() .then(resp => resp.json()) .then((json) => { - this.store.storeEnvironments(json); + this.store.storeAvailableCount(json.available_count); + this.store.storeStoppedCount(json.stopped_count); + this.store.storeEnvironments(json.environments); }) .then(() => { this.isLoading = false; @@ -131,7 +134,8 @@ require('./environment_item'); {{state.availableCounter}} -
  • +
  • +
  • Stopped diff --git a/app/assets/javascripts/environments/stores/environments_store.js.es6 b/app/assets/javascripts/environments/stores/environments_store.js.es6 index a533b8b61d6..c05f353647c 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js.es6 +++ b/app/assets/javascripts/environments/stores/environments_store.js.es6 @@ -42,8 +42,26 @@ return filteredEnvironments; }, - storeCounts() { - //TODO + /** + * Stores the number of available environments. + * + * @param {Number} count = 0 + * @return {Number} + */ + storeAvailableCount(count = 0) { + this.state.availableCounter = count; + return count; + }, + + /** + * Stores the number of closed environments. + * + * @param {Number} count = 0 + * @return {Number} + */ + storeStoppedCount(count = 0) { + this.state.stoppedCounter = count; + return count; }, }; diff --git a/spec/javascripts/environments/environments_store_spec.js.es6 b/spec/javascripts/environments/environments_store_spec.js.es6 index d073e120290..ef4b06dea40 100644 --- a/spec/javascripts/environments/environments_store_spec.js.es6 +++ b/spec/javascripts/environments/environments_store_spec.js.es6 @@ -22,7 +22,7 @@ require('./mock_data'); it('should store environments', () => { expect( - gl.environmentsList.EnvironmentsStore.state.environments.length + gl.environmentsList.EnvironmentsStore.state.environments.length, ).toBe(environmentsList.length); }); }); From 3c39cea60cede6399e7fae517b9ab3c5c86143a7 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 6 Feb 2017 15:32:40 +0000 Subject: [PATCH 05/73] Environments folder should be underlined when on hover and on focus like a regular link --- app/assets/stylesheets/pages/environments.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 1d4d85ba6de..606cf501b82 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -121,7 +121,6 @@ .folder-name { cursor: pointer; - text-decoration: none; color: $gl-text-color-secondary; } } From 72f76c4d4981b14d1cc721b1f379832f74a509f8 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 11:08:44 +0000 Subject: [PATCH 06/73] Remove pagination from this MR --- .../javascripts/environments/components/environment.js.es6 | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 3e899e5895b..073fbdffe21 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -7,7 +7,6 @@ window.Vue = require('vue'); window.Vue.use(require('vue-resource')); require('../services/environments_service'); require('./environment_item'); -require('../../vue_pagination/index'); (() => { window.gl = window.gl || {}; From acb68ae69e382a7bd949386f14d2d27f6cada086 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 11:16:11 +0000 Subject: [PATCH 07/73] Resolve 500 error --- .../javascripts/vue_shared/vue_resource_interceptor.js.es6 | 2 +- app/controllers/projects/environments_controller.rb | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 index d3229f9f730..a575ae3e441 100644 --- a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 +++ b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 @@ -6,7 +6,7 @@ Vue.http.interceptors.push((request, next) => { Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1; next((response) => { - if (typeof response.data === 'string') { + if (typeof response.data === 'string' && response.status !== 500) { response.data = JSON.parse(response.data); } diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 34b081b3e41..2252ece68ce 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -10,7 +10,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController def index @environments = project.environments - .includes(:last_deployment) .with_state(params[:scope] || :available) respond_to do |format| From 6077dea7b1f6fb857f786d86cd7cf9e5204ec9a9 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 11:18:21 +0000 Subject: [PATCH 08/73] Remove store from global namespace, use CSJ instead --- .../components/environment.js.es6 | 12 +- .../environments/environments_bundle.js.es6 | 7 - .../stores/environments_store.js.es6 | 120 +++++++++--------- .../vue_resource_interceptor.js.es6 | 2 +- 4 files changed, 65 insertions(+), 76 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 073fbdffe21..f07b650ca9f 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -7,18 +7,12 @@ window.Vue = require('vue'); window.Vue.use(require('vue-resource')); require('../services/environments_service'); require('./environment_item'); +const Store = require('../stores/environments_store'); (() => { window.gl = window.gl || {}; gl.environmentsList.EnvironmentsComponent = Vue.component('environment-component', { - props: { - store: { - type: Object, - required: true, - default: () => ({}), - }, - }, components: { 'environment-item': gl.environmentsList.EnvironmentItem, @@ -26,9 +20,11 @@ require('./environment_item'); data() { const environmentsData = document.querySelector('#environments-list-view').dataset; + const store = new Store(); return { - state: this.store.state, + store, + state: store.state, visibility: 'available', isLoading: false, cssContainerClass: environmentsData.cssClass, diff --git a/app/assets/javascripts/environments/environments_bundle.js.es6 b/app/assets/javascripts/environments/environments_bundle.js.es6 index 05c59d92fd4..912f50aeec1 100644 --- a/app/assets/javascripts/environments/environments_bundle.js.es6 +++ b/app/assets/javascripts/environments/environments_bundle.js.es6 @@ -1,5 +1,4 @@ window.Vue = require('vue'); -require('./stores/environments_store'); require('./components/environment'); require('../vue_shared/vue_resource_interceptor'); @@ -9,14 +8,8 @@ $(() => { if (gl.EnvironmentsListApp) { gl.EnvironmentsListApp.$destroy(true); } - const Store = gl.environmentsList.EnvironmentsStore; gl.EnvironmentsListApp = new gl.environmentsList.EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), - - propsData: { - store: Store.create(), - }, - }); }); diff --git a/app/assets/javascripts/environments/stores/environments_store.js.es6 b/app/assets/javascripts/environments/stores/environments_store.js.es6 index c05f353647c..52bd6b94551 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js.es6 +++ b/app/assets/javascripts/environments/stores/environments_store.js.es6 @@ -1,68 +1,68 @@ -/* eslint-disable no-param-reassign */ -(() => { - window.gl = window.gl || {}; - window.gl.environmentsList = window.gl.environmentsList || {}; +/** + * Environments Store. + * + * Stores received environments, count of stopped environments and count of + * available environments. + */ +class EnvironmentsStore { + constructor() { + this.state = {}; + this.state.environments = []; + this.state.stoppedCounter = 0; + this.state.availableCounter = 0; + this.state.filteredEnvironments = []; - gl.environmentsList.EnvironmentsStore = { - state: {}, + return this; + } - create() { - this.state.environments = []; - this.state.stoppedCounter = 0; - this.state.availableCounter = 0; - this.state.filteredEnvironments = []; + /** + * + * Stores the received environments. + * + * Each environment has the following schema + * { name: String, size: Number, latest: Object } + * + * If the `size` is bigger than 1, it means it should be rendered as a folder. + * In those cases we add `isFolder` key in order to render it properly. + * + * @param {Array} environments + * @returns {Array} + */ + storeEnvironments(environments = []) { + const filteredEnvironments = environments.map((env) => { + if (env.size > 1) { + return Object.assign({}, env, { isFolder: true }); + } - return this; - }, + return env; + }); - /** - * - * Stores the received environments. - * - * Each environment has the following schema - * { name: String, size: Number, latest: Object } - * - * If the `size` is bigger than 1, it means it should be rendered as a folder. - * In those cases we add `isFolder` key in order to render it properly. - * - * @param {Array} environments - * @returns {Array} - */ - storeEnvironments(environments = []) { - const filteredEnvironments = environments.map((env) => { - if (env.size > 1) { - return Object.assign({}, env, { isFolder: true }); - } + this.state.environments = filteredEnvironments; - return env; - }); + return filteredEnvironments; + } - this.state.environments = filteredEnvironments; + /** + * Stores the number of available environments. + * + * @param {Number} count = 0 + * @return {Number} + */ + storeAvailableCount(count = 0) { + this.state.availableCounter = count; + return count; + } - return filteredEnvironments; - }, + /** + * Stores the number of closed environments. + * + * @param {Number} count = 0 + * @return {Number} + */ + storeStoppedCount(count = 0) { + this.state.stoppedCounter = count; + return count; + } +} - /** - * Stores the number of available environments. - * - * @param {Number} count = 0 - * @return {Number} - */ - storeAvailableCount(count = 0) { - this.state.availableCounter = count; - return count; - }, - - /** - * Stores the number of closed environments. - * - * @param {Number} count = 0 - * @return {Number} - */ - storeStoppedCount(count = 0) { - this.state.stoppedCounter = count; - return count; - }, - - }; -})(); +module.exports = EnvironmentsStore; diff --git a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 index a575ae3e441..d3229f9f730 100644 --- a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 +++ b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 @@ -6,7 +6,7 @@ Vue.http.interceptors.push((request, next) => { Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1; next((response) => { - if (typeof response.data === 'string' && response.status !== 500) { + if (typeof response.data === 'string') { response.data = JSON.parse(response.data); } From 26a951b7ab272b80df1281464aaf656570fe214e Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 11:35:53 +0000 Subject: [PATCH 09/73] Use CJS for tests. Updates expected model in tests --- .../stores/environments_store.js.es6 | 1 - .../environments/environment_item_spec.js.es6 | 8 +++- .../environments/environment_spec.js.es6 | 18 +++------ .../environments_store_spec.js.es6 | 37 ++++++++++--------- .../javascripts/environments/mock_data.js.es6 | 8 ++-- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/app/assets/javascripts/environments/stores/environments_store.js.es6 b/app/assets/javascripts/environments/stores/environments_store.js.es6 index 52bd6b94551..749dd882188 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js.es6 +++ b/app/assets/javascripts/environments/stores/environments_store.js.es6 @@ -10,7 +10,6 @@ class EnvironmentsStore { this.state.environments = []; this.state.stoppedCounter = 0; this.state.availableCounter = 0; - this.state.filteredEnvironments = []; return this; } diff --git a/spec/javascripts/environments/environment_item_spec.js.es6 b/spec/javascripts/environments/environment_item_spec.js.es6 index 14478f1401d..5dc7ef5ad76 100644 --- a/spec/javascripts/environments/environment_item_spec.js.es6 +++ b/spec/javascripts/environments/environment_item_spec.js.es6 @@ -1,7 +1,7 @@ window.timeago = require('vendor/timeago'); require('~/environments/components/environment_item'); -describe('Environment item', () => { +fdescribe('Environment item', () => { preloadFixtures('static/environments/table.html.raw'); beforeEach(() => { loadFixtures('static/environments/table.html.raw'); @@ -14,7 +14,11 @@ describe('Environment item', () => { beforeEach(() => { mockItem = { name: 'review', - size: 3 + size: 3, + isFolder: true, + latest: { + environment_path: 'url', + }, }; component = new window.gl.environmentsList.EnvironmentItem({ diff --git a/spec/javascripts/environments/environment_spec.js.es6 b/spec/javascripts/environments/environment_spec.js.es6 index 87eda136122..8b96f4b09db 100644 --- a/spec/javascripts/environments/environment_spec.js.es6 +++ b/spec/javascripts/environments/environment_spec.js.es6 @@ -1,9 +1,8 @@ /* global Vue, environment */ require('~/flash'); -require('~/environments/stores/environments_store'); require('~/environments/components/environment'); -require('./mock_data'); +const { environment } = require('./mock_data'); describe('Environment', () => { preloadFixtures('static/environments/environments.html.raw'); @@ -35,9 +34,6 @@ describe('Environment', () => { it('should render the empty state', (done) => { component = new gl.environmentsList.EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), - propsData: { - store: gl.environmentsList.EnvironmentsStore.create(), - }, }); setTimeout(() => { @@ -56,7 +52,11 @@ describe('Environment', () => { describe('with environments', () => { const environmentsResponseInterceptor = (request, next) => { - next(request.respondWith(JSON.stringify([environment]), { + next(request.respondWith(JSON.stringify({ + environments: [environment], + stopped_count: 1, + available_count: 0, + }), { status: 200, })); }; @@ -74,9 +74,6 @@ describe('Environment', () => { it('should render a table with environments', (done) => { component = new gl.environmentsList.EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), - propsData: { - store: gl.environmentsList.EnvironmentsStore.create(), - }, }); setTimeout(() => { @@ -109,9 +106,6 @@ describe('Environment', () => { it('should render empty state', (done) => { component = new gl.environmentsList.EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), - propsData: { - store: gl.environmentsList.EnvironmentsStore.create(), - }, }); setTimeout(() => { diff --git a/spec/javascripts/environments/environments_store_spec.js.es6 b/spec/javascripts/environments/environments_store_spec.js.es6 index ef4b06dea40..861136c621f 100644 --- a/spec/javascripts/environments/environments_store_spec.js.es6 +++ b/spec/javascripts/environments/environments_store_spec.js.es6 @@ -1,30 +1,33 @@ -/* global environmentsList */ - -require('~/environments/stores/environments_store'); -require('./mock_data'); +const Store = require('~/environments/stores/environments_store'); +const { environmentsList } = require('./mock_data'); (() => { describe('Store', () => { + let store; + beforeEach(() => { - gl.environmentsList.EnvironmentsStore.create(); + store = new Store(); }); it('should start with a blank state', () => { - expect(gl.environmentsList.EnvironmentsStore.state.environments.length).toBe(0); - expect(gl.environmentsList.EnvironmentsStore.state.stoppedCounter).toBe(0); - expect(gl.environmentsList.EnvironmentsStore.state.availableCounter).toBe(0); + expect(store.state.environments.length).toBe(0); + expect(store.state.stoppedCounter).toBe(0); + expect(store.state.availableCounter).toBe(0); }); - describe('store environments', () => { - beforeEach(() => { - gl.environmentsList.EnvironmentsStore.storeEnvironments(environmentsList); - }); + it('should store environments', () => { + store.storeEnvironments(environmentsList); + expect(store.state.environments.length).toBe(environmentsList.length); + }); - it('should store environments', () => { - expect( - gl.environmentsList.EnvironmentsStore.state.environments.length, - ).toBe(environmentsList.length); - }); + it('should store available count', () => { + store.storeAvailableCount(2); + expect(store.state.availableCounter).toBe(2); + }); + + it('should store stopped count', () => { + store.storeStoppedCount(2); + expect(store.state.stoppedCounter).toBe(2); }); }); })(); diff --git a/spec/javascripts/environments/mock_data.js.es6 b/spec/javascripts/environments/mock_data.js.es6 index 91595c049b0..bdecc95d219 100644 --- a/spec/javascripts/environments/mock_data.js.es6 +++ b/spec/javascripts/environments/mock_data.js.es6 @@ -1,4 +1,3 @@ - const environmentsList = [ { name: 'DEV', @@ -36,8 +35,6 @@ const environmentsList = [ }, ]; -window.environmentsList = environmentsList; - const environment = { name: 'DEV', size: 1, @@ -56,4 +53,7 @@ const environment = { }, }; -window.environment = environment; +module.exports = { + environmentsList, + environment +}; From 8f3678f1dea1134ebc41f0d519424db673b5f764 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 11:38:59 +0000 Subject: [PATCH 10/73] Use CJS in environments service --- .../components/environment.js.es6 | 6 +++--- .../services/environments_service.js.es6 | 20 ++++--------------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index f07b650ca9f..6f84cc625ac 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -5,7 +5,7 @@ window.Vue = require('vue'); window.Vue.use(require('vue-resource')); -require('../services/environments_service'); +const EnvironmentsService = require('../services/environments_service'); require('./environment_item'); const Store = require('../stores/environments_store'); @@ -68,11 +68,11 @@ const Store = require('../stores/environments_store'); const scope = this.$options.getQueryParameter('scope') || this.visibility; const endpoint = `${this.endpoint}?scope=${scope}`; - gl.environmentsService = new EnvironmentsService(endpoint); + const service = new EnvironmentsService(endpoint); this.isLoading = true; - return gl.environmentsService.all() + return service.all() .then(resp => resp.json()) .then((json) => { this.store.storeAvailableCount(json.available_count); diff --git a/app/assets/javascripts/environments/services/environments_service.js.es6 b/app/assets/javascripts/environments/services/environments_service.js.es6 index fab8d977f58..9b33fd02c53 100644 --- a/app/assets/javascripts/environments/services/environments_service.js.es6 +++ b/app/assets/javascripts/environments/services/environments_service.js.es6 @@ -1,20 +1,8 @@ -/* globals Vue */ -/* eslint-disable no-unused-vars, no-param-reassign */ +const Vue = window.Vue = require('vue'); class EnvironmentsService { - - constructor(root) { - Vue.http.options.root = root; - - this.environments = Vue.resource(root); - - Vue.http.interceptors.push((request, next) => { - // needed in order to not break the tests. - if ($.rails) { - request.headers['X-CSRF-Token'] = $.rails.csrfToken(); - } - next(); - }); + constructor(endpoint) { + this.environments = Vue.resource(endpoint); } all() { @@ -22,4 +10,4 @@ class EnvironmentsService { } } -window.EnvironmentsService = EnvironmentsService; +module.exports = EnvironmentsService; From d6ae01da55bc8e0903c9ff13211d2e5dd29bff1f Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 11:52:22 +0000 Subject: [PATCH 11/73] Use CJS in all environments components --- .../components/environment.js.es6 | 360 ++++--- .../components/environment_actions.js.es6 | 79 +- .../environment_external_url.js.es6 | 36 +- .../components/environment_item.js.es6 | 908 +++++++++--------- .../components/environment_rollback.js.es6 | 57 +- .../components/environment_stop.js.es6 | 45 +- .../environment_terminal_button.js.es6 | 47 +- .../environments/environments_bundle.js.es6 | 5 +- .../services/environments_service.js.es6 | 2 +- .../vue_shared/components/commit.js.es6 | 1 + .../environment_actions_spec.js.es6 | 6 +- .../environment_external_url_spec.js.es6 | 4 +- .../environments/environment_item_spec.js.es6 | 8 +- .../environment_rollback_spec.js.es6 | 8 +- .../environments/environment_spec.js.es6 | 11 +- .../environments/environment_stop_spec.js.es6 | 4 +- 16 files changed, 773 insertions(+), 808 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 6f84cc625ac..11a965fcddf 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -1,205 +1,199 @@ /* eslint-disable no-param-reassign, no-new */ -/* global Vue */ -/* global EnvironmentsService */ /* global Flash */ -window.Vue = require('vue'); -window.Vue.use(require('vue-resource')); +const Vue = require('vue'); +Vue.use(require('vue-resource')); const EnvironmentsService = require('../services/environments_service'); -require('./environment_item'); +const EnvironmentItem = require('./environment_item'); const Store = require('../stores/environments_store'); -(() => { - window.gl = window.gl || {}; +module.exports = Vue.component('environment-component', { - gl.environmentsList.EnvironmentsComponent = Vue.component('environment-component', { + components: { + 'environment-item': EnvironmentItem, + }, - components: { - 'environment-item': gl.environmentsList.EnvironmentItem, + data() { + const environmentsData = document.querySelector('#environments-list-view').dataset; + const store = new Store(); + + return { + store, + state: store.state, + visibility: 'available', + isLoading: false, + cssContainerClass: environmentsData.cssClass, + endpoint: environmentsData.environmentsDataEndpoint, + canCreateDeployment: environmentsData.canCreateDeployment, + canReadEnvironment: environmentsData.canReadEnvironment, + canCreateEnvironment: environmentsData.canCreateEnvironment, + projectEnvironmentsPath: environmentsData.projectEnvironmentsPath, + projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath, + newEnvironmentPath: environmentsData.newEnvironmentPath, + helpPagePath: environmentsData.helpPagePath, + commitIconSvg: environmentsData.commitIconSvg, + playIconSvg: environmentsData.playIconSvg, + terminalIconSvg: environmentsData.terminalIconSvg, + }; + }, + + computed: { + scope() { + return this.$options.getQueryParameter('scope'); }, - data() { - const environmentsData = document.querySelector('#environments-list-view').dataset; - const store = new Store(); - - return { - store, - state: store.state, - visibility: 'available', - isLoading: false, - cssContainerClass: environmentsData.cssClass, - endpoint: environmentsData.environmentsDataEndpoint, - canCreateDeployment: environmentsData.canCreateDeployment, - canReadEnvironment: environmentsData.canReadEnvironment, - canCreateEnvironment: environmentsData.canCreateEnvironment, - projectEnvironmentsPath: environmentsData.projectEnvironmentsPath, - projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath, - newEnvironmentPath: environmentsData.newEnvironmentPath, - helpPagePath: environmentsData.helpPagePath, - commitIconSvg: environmentsData.commitIconSvg, - playIconSvg: environmentsData.playIconSvg, - terminalIconSvg: environmentsData.terminalIconSvg, - }; + canReadEnvironmentParsed() { + return this.$options.convertPermissionToBoolean(this.canReadEnvironment); }, - computed: { - scope() { - return this.$options.getQueryParameter('scope'); - }, - - canReadEnvironmentParsed() { - return this.$options.convertPermissionToBoolean(this.canReadEnvironment); - }, - - canCreateDeploymentParsed() { - return this.$options.convertPermissionToBoolean(this.canCreateDeployment); - }, - - canCreateEnvironmentParsed() { - return this.$options.convertPermissionToBoolean(this.canCreateEnvironment); - }, + canCreateDeploymentParsed() { + return this.$options.convertPermissionToBoolean(this.canCreateDeployment); }, - /** - * Fetches all the environments and stores them. - * Toggles loading property. - */ - created() { - const scope = this.$options.getQueryParameter('scope') || this.visibility; - const endpoint = `${this.endpoint}?scope=${scope}`; - - const service = new EnvironmentsService(endpoint); - - this.isLoading = true; - - return service.all() - .then(resp => resp.json()) - .then((json) => { - this.store.storeAvailableCount(json.available_count); - this.store.storeStoppedCount(json.stopped_count); - this.store.storeEnvironments(json.environments); - }) - .then(() => { - this.isLoading = false; - }) - .catch(() => { - this.isLoading = false; - new Flash('An error occurred while fetching the environments.', 'alert'); - }); + canCreateEnvironmentParsed() { + return this.$options.convertPermissionToBoolean(this.canCreateEnvironment); }, + }, - /** - * 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]; + /** + * Fetches all the environments and stores them. + * Toggles loading property. + */ + created() { + const scope = this.$options.getQueryParameter('scope') || this.visibility; + const endpoint = `${this.endpoint}?scope=${scope}`; + + const service = new EnvironmentsService(endpoint); + + this.isLoading = true; + + return service.all() + .then(resp => resp.json()) + .then((json) => { + this.store.storeAvailableCount(json.available_count); + this.store.storeStoppedCount(json.stopped_count); + this.store.storeEnvironments(json.environments); + }) + .then(() => { + this.isLoading = false; + }) + .catch(() => { + this.isLoading = false; + new Flash('An error occurred while fetching the environments.', 'alert'); + }); + }, + + /** + * 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]; + }, + + /** + * 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); }, + }, - /** - * 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); - }, - }, - - template: ` -
  • +
  • + + Stopped + + {{state.stoppedCounter}} + -
  • - -
    - - - - - - - - - - - - - - -
    EnvironmentLast deploymentJobCommitUpdated
    -
    + + +
    - `, - }); -})(); + +
    +
    + +
    + +
    +

    + You don't have any environments right now. +

    +

    + Environments are places where code gets deployed, such as staging or production. +
    + + Read more about environments + +

    + + + New Environment + +
    + +
    + + + + + + + + + + + + + + +
    EnvironmentLast deploymentJobCommitUpdated
    +
    +
    +
    + `, +}); diff --git a/app/assets/javascripts/environments/components/environment_actions.js.es6 b/app/assets/javascripts/environments/components/environment_actions.js.es6 index ed1c78945db..c5a714d9673 100644 --- a/app/assets/javascripts/environments/components/environment_actions.js.es6 +++ b/app/assets/javascripts/environments/components/environment_actions.js.es6 @@ -1,50 +1,43 @@ -/* global Vue */ +const Vue = require('vue'); -window.Vue = require('vue'); - -(() => { - window.gl = window.gl || {}; - window.gl.environmentsList = window.gl.environmentsList || {}; - - gl.environmentsList.ActionsComponent = Vue.component('actions-component', { - props: { - actions: { - type: Array, - required: false, - default: () => [], - }, - - playIconSvg: { - type: String, - required: false, - }, +module.exports = Vue.component('actions-component', { + props: { + actions: { + type: Array, + required: false, + default: () => [], }, - template: ` -
    - - `, - }); -})(); +
    + `, +}); diff --git a/app/assets/javascripts/environments/components/environment_external_url.js.es6 b/app/assets/javascripts/environments/components/environment_external_url.js.es6 index 28cc0022d17..2599bba3c59 100644 --- a/app/assets/javascripts/environments/components/environment_external_url.js.es6 +++ b/app/assets/javascripts/environments/components/environment_external_url.js.es6 @@ -1,23 +1,19 @@ -/* global Vue */ +/** + * Renders the external url link in environments table. + */ +const Vue = require('vue'); -window.Vue = require('vue'); - -(() => { - window.gl = window.gl || {}; - window.gl.environmentsList = window.gl.environmentsList || {}; - - gl.environmentsList.ExternalUrlComponent = Vue.component('external-url-component', { - props: { - externalUrl: { - type: String, - default: '', - }, +module.exports = Vue.component('external-url-component', { + props: { + externalUrl: { + type: String, + default: '', }, + }, - template: ` - - - - `, - }); -})(); + template: ` + + + + `, +}); diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index ae37bc24396..cf79969471e 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -1,489 +1,481 @@ -/* global Vue */ -/* global timeago */ -window.Vue = require('vue'); -window.timeago = require('vendor/timeago'); +const Vue = require('vue'); +const Timeago = require('vendor/timeago'); require('../../lib/utils/text_utility'); require('../../vue_shared/components/commit'); -require('./environment_actions'); -require('./environment_external_url'); -require('./environment_stop'); -require('./environment_rollback'); -require('./environment_terminal_button'); +const ActionsComponent = require('./environment_actions'); +const ExternalUrlComponent = require('./environment_external_url'); +const StopComponent = require('./environment_stop'); +const RollbackComponent = require('./environment_rollback'); +const TerminalButtonComponent = require('./environment_terminal_button'); -(() => { - /** - * Envrionment Item Component - * - * Renders a table row for each environment. - */ +/** + * Envrionment Item Component + * + * Renders a table row for each environment. + */ - window.gl = window.gl || {}; - window.gl.environmentsList = window.gl.environmentsList || {}; - window.gl.environmentsList.timeagoInstance = new timeago(); // eslint-disable-line +const timeagoInstance = new Timeago(); - gl.environmentsList.EnvironmentItem = Vue.component('environment-item', { +module.exports = Vue.component('environment-item', { - components: { - 'commit-component': gl.CommitComponent, - 'actions-component': gl.environmentsList.ActionsComponent, - 'external-url-component': gl.environmentsList.ExternalUrlComponent, - 'stop-component': gl.environmentsList.StopComponent, - 'rollback-component': gl.environmentsList.RollbackComponent, - 'terminal-button-component': gl.environmentsList.TerminalButtonComponent, + components: { + 'commit-component': gl.CommitComponent, + 'actions-component': ActionsComponent, + 'external-url-component': ExternalUrlComponent, + 'stop-component': StopComponent, + 'rollback-component': RollbackComponent, + 'terminal-button-component': TerminalButtonComponent, + }, + + props: { + model: { + type: Object, + required: true, + default: () => ({}), }, - props: { - model: { - type: Object, - required: true, - default: () => ({}), - }, - - canCreateDeployment: { - type: Boolean, - required: false, - default: false, - }, - - canReadEnvironment: { - type: Boolean, - required: false, - default: false, - }, - - commitIconSvg: { - type: String, - required: false, - }, - - playIconSvg: { - type: String, - required: false, - }, - - terminalIconSvg: { - type: String, - required: false, - }, + canCreateDeployment: { + type: Boolean, + required: false, + default: false, }, - computed: { - /** - * Verifies if `last_deployment` key exists in the current Envrionment. - * This key is required to render most of the html - this method works has - * an helper. - * - * @returns {Boolean} - */ - hasLastDeploymentKey() { - if (this.model.latest.last_deployment && - !this.$options.isObjectEmpty(this.model.latest.last_deployment)) { - return true; - } - return false; - }, + canReadEnvironment: { + type: Boolean, + required: false, + default: false, + }, - /** - * Verifies is the given environment has manual actions. - * Used to verify if we should render them or nor. - * - * @returns {Boolean|Undefined} - */ - hasManualActions() { - return this.model.latest.last_deployment && - this.model.latest.last_deployment.manual_actions && - this.model.latest.last_deployment.manual_actions.length > 0; - }, + commitIconSvg: { + type: String, + required: false, + }, - /** - * Returns the value of the `stop_action?` key provided in the response. - * - * @returns {Boolean} - */ - hasStopAction() { - return this.model['stop_action?']; - }, + playIconSvg: { + type: String, + required: false, + }, - /** - * Verifies if the `deployable` key is present in `last_deployment` key. - * Used to verify whether we should or not render the rollback partial. - * - * @returns {Boolean|Undefined} - */ - canRetry() { - return this.hasLastDeploymentKey && - this.model.latest.last_deployment && - this.model.latest.last_deployment.deployable; - }, + terminalIconSvg: { + type: String, + required: false, + }, + }, - /** - * Verifies if the date to be shown is present. - * - * @returns {Boolean|Undefined} - */ - canShowDate() { - return this.model.latest.last_deployment && - this.model.latest.last_deployment.deployable && - this.model.latest.last_deployment.deployable !== undefined; - }, - - /** - * Human readable date. - * - * @returns {String} - */ - createdDate() { - return gl.environmentsList.timeagoInstance.format( - this.model.latest.last_deployment.deployable.created_at, - ); - }, - - /** - * Returns the manual actions with the name parsed. - * - * @returns {Array.|Undefined} - */ - manualActions() { - if (this.hasManualActions) { - return this.model.latest.last_deployment.manual_actions.map((action) => { - const parsedAction = { - name: gl.text.humanize(action.name), - play_path: action.play_path, - }; - return parsedAction; - }); - } - return []; - }, - - /** - * Builds the string used in the user image alt attribute. - * - * @returns {String} - */ - userImageAltDescription() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.user && - this.model.latest.last_deployment.user.username) { - return `${this.model.latest.last_deployment.user.username}'s avatar'`; - } - return ''; - }, - - /** - * If provided, returns the commit tag. - * - * @returns {String|Undefined} - */ - commitTag() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.tag) { - return this.model.latest.last_deployment.tag; - } - return undefined; - }, - - /** - * If provided, returns the commit ref. - * - * @returns {Object|Undefined} - */ - commitRef() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.ref) { - return this.model.latest.last_deployment.ref; - } - return undefined; - }, - - /** - * If provided, returns the commit url. - * - * @returns {String|Undefined} - */ - commitUrl() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.commit && - this.model.latest.last_deployment.commit.commit_path) { - return this.model.latest.last_deployment.commit.commit_path; - } - return undefined; - }, - - /** - * If provided, returns the commit short sha. - * - * @returns {String|Undefined} - */ - commitShortSha() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.commit && - this.model.latest.last_deployment.commit.short_id) { - return this.model.latest.last_deployment.commit.short_id; - } - return undefined; - }, - - /** - * If provided, returns the commit title. - * - * @returns {String|Undefined} - */ - commitTitle() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.commit && - this.model.latest.last_deployment.commit.title) { - return this.model.latest.last_deployment.commit.title; - } - return undefined; - }, - - /** - * If provided, returns the commit tag. - * - * @returns {Object|Undefined} - */ - commitAuthor() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.commit && - this.model.latest.last_deployment.commit.author) { - return this.model.latest.last_deployment.commit.author; - } - - return undefined; - }, - - /** - * Verifies if the `retry_path` key is present and returns its value. - * - * @returns {String|Undefined} - */ - retryUrl() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.deployable && - this.model.latest.last_deployment.deployable.retry_path) { - return this.model.latest.last_deployment.deployable.retry_path; - } - return undefined; - }, - - /** - * Verifies if the `last?` key is present and returns its value. - * - * @returns {Boolean|Undefined} - */ - isLastDeployment() { - return this.model.latest.last_deployment && - this.model.latest.last_deployment['last?']; - }, - - /** - * Builds the name of the builds needed to display both the name and the id. - * - * @returns {String} - */ - buildName() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.deployable) { - return `${this.model.latest.last_deployment.deployable.name} #${this.model.latest.last_deployment.deployable.id}`; - } - return ''; - }, - - /** - * Builds the needed string to show the internal id. - * - * @returns {String} - */ - deploymentInternalId() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.iid) { - return `#${this.model.latest.last_deployment.iid}`; - } - return ''; - }, - - /** - * Verifies if the user object is present under last_deployment object. - * - * @returns {Boolean} - */ - deploymentHasUser() { - return !this.$options.isObjectEmpty(this.model.latest.last_deployment) && - !this.$options.isObjectEmpty(this.model.latest.last_deployment.user); - }, - - /** - * Returns the user object nested with the last_deployment object. - * Used to render the template. - * - * @returns {Object} - */ - deploymentUser() { - if (!this.$options.isObjectEmpty(this.model.latest.last_deployment) && - !this.$options.isObjectEmpty(this.model.latest.last_deployment.user)) { - return this.model.latest.last_deployment.user; - } - return {}; - }, - - /** - * Verifies if the build name column should be rendered by verifing - * if all the information needed is present - * and if the environment is not a folder. - * - * @returns {Boolean} - */ - shouldRenderBuildName() { - return !this.model.isFolder && - !this.$options.isObjectEmpty(this.model.latest.last_deployment) && - !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); - }, - - /** - * Verifies if deplyment internal ID should be rendered by verifing - * if all the information needed is present - * and if the environment is not a folder. - * - * @returns {Boolean} - */ - shouldRenderDeploymentID() { - return !this.model.isFolder && - !this.$options.isObjectEmpty(this.model.latest.last_deployment) && - this.model.latest.last_deployment.iid !== undefined; - }, + computed: { + /** + * Verifies if `last_deployment` key exists in the current Envrionment. + * This key is required to render most of the html - this method works has + * an helper. + * + * @returns {Boolean} + */ + hasLastDeploymentKey() { + if (this.model.latest.last_deployment && + !this.$options.isObjectEmpty(this.model.latest.last_deployment)) { + return true; + } + return false; }, /** - * Helper to verify if certain given object are empty. - * Should be replaced by lodash _.isEmpty - https://lodash.com/docs/4.17.2#isEmpty - * @param {Object} object - * @returns {Bollean} + * Verifies is the given environment has manual actions. + * Used to verify if we should render them or nor. + * + * @returns {Boolean|Undefined} */ - isObjectEmpty(object) { - for (const key in object) { // eslint-disable-line - if (hasOwnProperty.call(object, key)) { - return false; - } - } - return true; + hasManualActions() { + return this.model.latest.last_deployment && + this.model.latest.last_deployment.manual_actions && + this.model.latest.last_deployment.manual_actions.length > 0; }, - template: ` - - - + /** + * Returns the value of the `stop_action?` key provided in the response. + * + * @returns {Boolean} + */ + hasStopAction() { + return this.model['stop_action?']; + }, + + /** + * Verifies if the `deployable` key is present in `last_deployment` key. + * Used to verify whether we should or not render the rollback partial. + * + * @returns {Boolean|Undefined} + */ + canRetry() { + return this.hasLastDeploymentKey && + this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable; + }, + + /** + * Verifies if the date to be shown is present. + * + * @returns {Boolean|Undefined} + */ + canShowDate() { + return this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable && + this.model.latest.last_deployment.deployable !== undefined; + }, + + /** + * Human readable date. + * + * @returns {String} + */ + createdDate() { + return timeagoInstance.format(this.model.latest.last_deployment.deployable.created_at); + }, + + /** + * Returns the manual actions with the name parsed. + * + * @returns {Array.|Undefined} + */ + manualActions() { + if (this.hasManualActions) { + return this.model.latest.last_deployment.manual_actions.map((action) => { + const parsedAction = { + name: gl.text.humanize(action.name), + play_path: action.play_path, + }; + return parsedAction; + }); + } + return []; + }, + + /** + * Builds the string used in the user image alt attribute. + * + * @returns {String} + */ + userImageAltDescription() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.user && + this.model.latest.last_deployment.user.username) { + return `${this.model.latest.last_deployment.user.username}'s avatar'`; + } + return ''; + }, + + /** + * If provided, returns the commit tag. + * + * @returns {String|Undefined} + */ + commitTag() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.tag) { + return this.model.latest.last_deployment.tag; + } + return undefined; + }, + + /** + * If provided, returns the commit ref. + * + * @returns {Object|Undefined} + */ + commitRef() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.ref) { + return this.model.latest.last_deployment.ref; + } + return undefined; + }, + + /** + * If provided, returns the commit url. + * + * @returns {String|Undefined} + */ + commitUrl() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.commit_path) { + return this.model.latest.last_deployment.commit.commit_path; + } + return undefined; + }, + + /** + * If provided, returns the commit short sha. + * + * @returns {String|Undefined} + */ + commitShortSha() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.short_id) { + return this.model.latest.last_deployment.commit.short_id; + } + return undefined; + }, + + /** + * If provided, returns the commit title. + * + * @returns {String|Undefined} + */ + commitTitle() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.title) { + return this.model.latest.last_deployment.commit.title; + } + return undefined; + }, + + /** + * If provided, returns the commit tag. + * + * @returns {Object|Undefined} + */ + commitAuthor() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.author) { + return this.model.latest.last_deployment.commit.author; + } + + return undefined; + }, + + /** + * Verifies if the `retry_path` key is present and returns its value. + * + * @returns {String|Undefined} + */ + retryUrl() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable && + this.model.latest.last_deployment.deployable.retry_path) { + return this.model.latest.last_deployment.deployable.retry_path; + } + return undefined; + }, + + /** + * Verifies if the `last?` key is present and returns its value. + * + * @returns {Boolean|Undefined} + */ + isLastDeployment() { + return this.model.latest.last_deployment && + this.model.latest.last_deployment['last?']; + }, + + /** + * Builds the name of the builds needed to display both the name and the id. + * + * @returns {String} + */ + buildName() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable) { + return `${this.model.latest.last_deployment.deployable.name} #${this.model.latest.last_deployment.deployable.id}`; + } + return ''; + }, + + /** + * Builds the needed string to show the internal id. + * + * @returns {String} + */ + deploymentInternalId() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.iid) { + return `#${this.model.latest.last_deployment.iid}`; + } + return ''; + }, + + /** + * Verifies if the user object is present under last_deployment object. + * + * @returns {Boolean} + */ + deploymentHasUser() { + return !this.$options.isObjectEmpty(this.model.latest.last_deployment) && + !this.$options.isObjectEmpty(this.model.latest.last_deployment.user); + }, + + /** + * Returns the user object nested with the last_deployment object. + * Used to render the template. + * + * @returns {Object} + */ + deploymentUser() { + if (!this.$options.isObjectEmpty(this.model.latest.last_deployment) && + !this.$options.isObjectEmpty(this.model.latest.last_deployment.user)) { + return this.model.latest.last_deployment.user; + } + return {}; + }, + + /** + * Verifies if the build name column should be rendered by verifing + * if all the information needed is present + * and if the environment is not a folder. + * + * @returns {Boolean} + */ + shouldRenderBuildName() { + return !this.model.isFolder && + !this.$options.isObjectEmpty(this.model.latest.last_deployment) && + !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); + }, + + /** + * Verifies if deplyment internal ID should be rendered by verifing + * if all the information needed is present + * and if the environment is not a folder. + * + * @returns {Boolean} + */ + shouldRenderDeploymentID() { + return !this.model.isFolder && + !this.$options.isObjectEmpty(this.model.latest.last_deployment) && + this.model.latest.last_deployment.iid !== undefined; + }, + }, + + /** + * Helper to verify if certain given object are empty. + * Should be replaced by lodash _.isEmpty - https://lodash.com/docs/4.17.2#isEmpty + * @param {Object} object + * @returns {Bollean} + */ + isObjectEmpty(object) { + for (const key in object) { // eslint-disable-line + if (hasOwnProperty.call(object, key)) { + return false; + } + } + return true; + }, + + template: ` + + + + {{model.name}} + + + + + + + + {{model.name}} - - - - - - - - - {{model.name}} - - - - {{model.size}} - - - - - - - {{deploymentInternalId}} - - by - - - + + {{model.size}} - + + - - - {{buildName}} + + + {{deploymentInternalId}} + + + + by + + - + + - -
    - - + + + {{buildName}} + + + + +
    + + +
    +

    + No deployments yet +

    + + + + + {{createdDate}} + + + + +
    +
    + +
    -

    - No deployments yet -

    - - - - {{createdDate}} - - - - -
    -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    +
    + +
    - - - `, - }); -})(); + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + + + `, +}); diff --git a/app/assets/javascripts/environments/components/environment_rollback.js.es6 b/app/assets/javascripts/environments/components/environment_rollback.js.es6 index 5938340a128..daf126eb4e8 100644 --- a/app/assets/javascripts/environments/components/environment_rollback.js.es6 +++ b/app/assets/javascripts/environments/components/environment_rollback.js.es6 @@ -1,33 +1,30 @@ -/* global Vue */ +/** + * Renders Rollback or Re deploy button in environments table depending + * of the provided property `isLastDeployment` + */ +const Vue = require('vue'); -window.Vue = require('vue'); - -(() => { - window.gl = window.gl || {}; - window.gl.environmentsList = window.gl.environmentsList || {}; - - gl.environmentsList.RollbackComponent = Vue.component('rollback-component', { - props: { - retryUrl: { - type: String, - default: '', - }, - - isLastDeployment: { - type: Boolean, - default: true, - }, +module.exports = Vue.component('rollback-component', { + props: { + retryUrl: { + type: String, + default: '', }, - template: ` - - - Re-deploy - - - Rollback - - - `, - }); -})(); + isLastDeployment: { + type: Boolean, + default: true, + }, + }, + + template: ` + + + Re-deploy + + + Rollback + + + `, +}); diff --git a/app/assets/javascripts/environments/components/environment_stop.js.es6 b/app/assets/javascripts/environments/components/environment_stop.js.es6 index be9526989a0..96983a19568 100644 --- a/app/assets/javascripts/environments/components/environment_stop.js.es6 +++ b/app/assets/javascripts/environments/components/environment_stop.js.es6 @@ -1,27 +1,24 @@ -/* global Vue */ +/** + * Renders the stop "button" that allows stop an environment. + * Used in environments table. + */ +const Vue = require('vue'); -window.Vue = require('vue'); - -(() => { - window.gl = window.gl || {}; - window.gl.environmentsList = window.gl.environmentsList || {}; - - gl.environmentsList.StopComponent = Vue.component('stop-component', { - props: { - stopUrl: { - type: String, - default: '', - }, +module.exports = Vue.component('stop-component', { + props: { + stopUrl: { + type: String, + default: '', }, + }, - template: ` - - - - `, - }); -})(); + template: ` + + + + `, +}); diff --git a/app/assets/javascripts/environments/components/environment_terminal_button.js.es6 b/app/assets/javascripts/environments/components/environment_terminal_button.js.es6 index a3ad063f7cb..481e0d15e7a 100644 --- a/app/assets/javascripts/environments/components/environment_terminal_button.js.es6 +++ b/app/assets/javascripts/environments/components/environment_terminal_button.js.es6 @@ -1,28 +1,25 @@ -/* global Vue */ +/** + * Renders a terminal button to open a web terminal. + * Used in environments table. + */ +const Vue = require('vue'); -window.Vue = require('vue'); - -(() => { - window.gl = window.gl || {}; - window.gl.environmentsList = window.gl.environmentsList || {}; - - gl.environmentsList.TerminalButtonComponent = Vue.component('terminal-button-component', { - props: { - terminalPath: { - type: String, - default: '', - }, - terminalIconSvg: { - type: String, - default: '', - }, +module.exports = Vue.component('terminal-button-component', { + props: { + terminalPath: { + type: String, + default: '', }, + terminalIconSvg: { + type: String, + default: '', + }, + }, - template: ` - - - - `, - }); -})(); + template: ` + + + + `, +}); diff --git a/app/assets/javascripts/environments/environments_bundle.js.es6 b/app/assets/javascripts/environments/environments_bundle.js.es6 index 912f50aeec1..867eba1d384 100644 --- a/app/assets/javascripts/environments/environments_bundle.js.es6 +++ b/app/assets/javascripts/environments/environments_bundle.js.es6 @@ -1,5 +1,4 @@ -window.Vue = require('vue'); -require('./components/environment'); +const EnvironmentsComponent = require('./components/environment'); require('../vue_shared/vue_resource_interceptor'); $(() => { @@ -9,7 +8,7 @@ $(() => { gl.EnvironmentsListApp.$destroy(true); } - gl.EnvironmentsListApp = new gl.environmentsList.EnvironmentsComponent({ + gl.EnvironmentsListApp = new EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), }); }); diff --git a/app/assets/javascripts/environments/services/environments_service.js.es6 b/app/assets/javascripts/environments/services/environments_service.js.es6 index 9b33fd02c53..9cef335868e 100644 --- a/app/assets/javascripts/environments/services/environments_service.js.es6 +++ b/app/assets/javascripts/environments/services/environments_service.js.es6 @@ -1,4 +1,4 @@ -const Vue = window.Vue = require('vue'); +const Vue = require('vue'); class EnvironmentsService { constructor(endpoint) { diff --git a/app/assets/javascripts/vue_shared/components/commit.js.es6 b/app/assets/javascripts/vue_shared/components/commit.js.es6 index 7f7c18ddeb1..ff88e236829 100644 --- a/app/assets/javascripts/vue_shared/components/commit.js.es6 +++ b/app/assets/javascripts/vue_shared/components/commit.js.es6 @@ -1,4 +1,5 @@ /* global Vue */ +window.Vue = require('vue'); (() => { window.gl = window.gl || {}; diff --git a/spec/javascripts/environments/environment_actions_spec.js.es6 b/spec/javascripts/environments/environment_actions_spec.js.es6 index b1838045a06..850586f9f3a 100644 --- a/spec/javascripts/environments/environment_actions_spec.js.es6 +++ b/spec/javascripts/environments/environment_actions_spec.js.es6 @@ -1,4 +1,4 @@ -require('~/environments/components/environment_actions'); +const ActionsComponent = require('~/environments/components/environment_actions'); describe('Actions Component', () => { preloadFixtures('static/environments/element.html.raw'); @@ -19,7 +19,7 @@ describe('Actions Component', () => { }, ]; - const component = new window.gl.environmentsList.ActionsComponent({ + const component = new ActionsComponent({ el: document.querySelector('.test-dom-element'), propsData: { actions: actionsMock, @@ -47,7 +47,7 @@ describe('Actions Component', () => { }, ]; - const component = new window.gl.environmentsList.ActionsComponent({ + const component = new ActionsComponent({ el: document.querySelector('.test-dom-element'), propsData: { actions: actionsMock, diff --git a/spec/javascripts/environments/environment_external_url_spec.js.es6 b/spec/javascripts/environments/environment_external_url_spec.js.es6 index a6a587e69f5..393dbb5aae0 100644 --- a/spec/javascripts/environments/environment_external_url_spec.js.es6 +++ b/spec/javascripts/environments/environment_external_url_spec.js.es6 @@ -1,4 +1,4 @@ -require('~/environments/components/environment_external_url'); +const ExternalUrlComponent = require('~/environments/components/environment_external_url'); describe('External URL Component', () => { preloadFixtures('static/environments/element.html.raw'); @@ -8,7 +8,7 @@ describe('External URL Component', () => { it('should link to the provided externalUrl prop', () => { const externalURL = 'https://gitlab.com'; - const component = new window.gl.environmentsList.ExternalUrlComponent({ + const component = new ExternalUrlComponent({ el: document.querySelector('.test-dom-element'), propsData: { externalUrl: externalURL, diff --git a/spec/javascripts/environments/environment_item_spec.js.es6 b/spec/javascripts/environments/environment_item_spec.js.es6 index 5dc7ef5ad76..dd614e50d7f 100644 --- a/spec/javascripts/environments/environment_item_spec.js.es6 +++ b/spec/javascripts/environments/environment_item_spec.js.es6 @@ -1,7 +1,7 @@ window.timeago = require('vendor/timeago'); -require('~/environments/components/environment_item'); +const EnvironmentItem = require('~/environments/components/environment_item'); -fdescribe('Environment item', () => { +describe('Environment item', () => { preloadFixtures('static/environments/table.html.raw'); beforeEach(() => { loadFixtures('static/environments/table.html.raw'); @@ -21,7 +21,7 @@ fdescribe('Environment item', () => { }, }; - component = new window.gl.environmentsList.EnvironmentItem({ + component = new EnvironmentItem({ el: document.querySelector('tr#environment-row'), propsData: { model: mockItem, @@ -111,7 +111,7 @@ fdescribe('Environment item', () => { }, }; - component = new window.gl.environmentsList.EnvironmentItem({ + component = new EnvironmentItem({ el: document.querySelector('tr#environment-row'), propsData: { model: environment, diff --git a/spec/javascripts/environments/environment_rollback_spec.js.es6 b/spec/javascripts/environments/environment_rollback_spec.js.es6 index 043b8708a6e..4a596baad09 100644 --- a/spec/javascripts/environments/environment_rollback_spec.js.es6 +++ b/spec/javascripts/environments/environment_rollback_spec.js.es6 @@ -1,4 +1,4 @@ -require('~/environments/components/environment_rollback'); +const RollbackComponent = require('~/environments/components/environment_rollback'); describe('Rollback Component', () => { preloadFixtures('static/environments/element.html.raw'); @@ -10,7 +10,7 @@ describe('Rollback Component', () => { }); it('Should link to the provided retryUrl', () => { - const component = new window.gl.environmentsList.RollbackComponent({ + const component = new RollbackComponent({ el: document.querySelector('.test-dom-element'), propsData: { retryUrl: retryURL, @@ -22,7 +22,7 @@ describe('Rollback Component', () => { }); it('Should render Re-deploy label when isLastDeployment is true', () => { - const component = new window.gl.environmentsList.RollbackComponent({ + const component = new RollbackComponent({ el: document.querySelector('.test-dom-element'), propsData: { retryUrl: retryURL, @@ -34,7 +34,7 @@ describe('Rollback Component', () => { }); it('Should render Rollback label when isLastDeployment is false', () => { - const component = new window.gl.environmentsList.RollbackComponent({ + const component = new RollbackComponent({ el: document.querySelector('.test-dom-element'), propsData: { retryUrl: retryURL, diff --git a/spec/javascripts/environments/environment_spec.js.es6 b/spec/javascripts/environments/environment_spec.js.es6 index 8b96f4b09db..1d1a688a4a5 100644 --- a/spec/javascripts/environments/environment_spec.js.es6 +++ b/spec/javascripts/environments/environment_spec.js.es6 @@ -1,7 +1,6 @@ -/* global Vue, environment */ - +const Vue = require('vue'); require('~/flash'); -require('~/environments/components/environment'); +const EnvironmentsComponent = require('~/environments/components/environment'); const { environment } = require('./mock_data'); describe('Environment', () => { @@ -32,7 +31,7 @@ describe('Environment', () => { }); it('should render the empty state', (done) => { - component = new gl.environmentsList.EnvironmentsComponent({ + component = new EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), }); @@ -72,7 +71,7 @@ describe('Environment', () => { }); it('should render a table with environments', (done) => { - component = new gl.environmentsList.EnvironmentsComponent({ + component = new EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), }); @@ -104,7 +103,7 @@ describe('Environment', () => { }); it('should render empty state', (done) => { - component = new gl.environmentsList.EnvironmentsComponent({ + component = new EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), }); diff --git a/spec/javascripts/environments/environment_stop_spec.js.es6 b/spec/javascripts/environments/environment_stop_spec.js.es6 index 2dfce5ba824..5ca65b1debc 100644 --- a/spec/javascripts/environments/environment_stop_spec.js.es6 +++ b/spec/javascripts/environments/environment_stop_spec.js.es6 @@ -1,4 +1,4 @@ -require('~/environments/components/environment_stop'); +const StopComponent = require('~/environments/components/environment_stop'); describe('Stop Component', () => { preloadFixtures('static/environments/element.html.raw'); @@ -10,7 +10,7 @@ describe('Stop Component', () => { loadFixtures('static/environments/element.html.raw'); stopURL = '/stop'; - component = new window.gl.environmentsList.StopComponent({ + component = new StopComponent({ el: document.querySelector('.test-dom-element'), propsData: { stopUrl: stopURL, From 19bac884c67acebcc0e6dda2a3df824049b1f726 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 7 Feb 2017 15:06:31 +0100 Subject: [PATCH 12/73] Add route for environment folder and expose an API --- .../projects/environments_controller.rb | 18 ++++++++++++++++++ config/routes/project.rb | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 2252ece68ce..3b7240d8469 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -28,6 +28,24 @@ class Projects::EnvironmentsController < Projects::ApplicationController end end + def folder + @environments = project.environments + .where(environment_type: params[:id]) + .with_state(params[:scope] || :available) + + respond_to do |format| + format.html + format.json do + render json: { + environments: EnvironmentSerializer + .new(project: @project, user: @current_user) + .with_pagination(request, response) + .represent(@environments), + } + end + end + end + def show @deployments = environment.deployments.order(id: :desc).page(params[:page]) end diff --git a/config/routes/project.rb b/config/routes/project.rb index 2ac98cf3842..84f123ff717 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -156,6 +156,10 @@ constraints(ProjectUrlConstrainer.new) do get :terminal get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', constraints: { format: nil } end + + collection do + get :folder, path: 'folders/:id' + end end resource :cycle_analytics, only: [:show] From 2bd1a229060c959a177171ddbb32d1f2202b4801 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 9 Feb 2017 15:51:49 +0000 Subject: [PATCH 13/73] Disables add issue button Previously was disabled until any list was present (except for done) this now takes into account the welcome blank state Closes #27931 --- app/assets/javascripts/boards/boards_bundle.js.es6 | 2 +- spec/features/boards/boards_spec.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6 index 8f30900198e..878ad1b6031 100644 --- a/app/assets/javascripts/boards/boards_bundle.js.es6 +++ b/app/assets/javascripts/boards/boards_bundle.js.es6 @@ -95,7 +95,7 @@ $(() => { }, computed: { disabled() { - return Store.shouldAddBlankState(); + return !this.store.lists.filter(list => list.type !== 'blank' && list.type !== 'done').length; }, }, template: ` diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index 7225f38b7e5..1b25b51cfb2 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -28,6 +28,12 @@ describe 'Issue Boards', feature: true, js: true do expect(page).to have_content('Welcome to your Issue Board!') end + it 'disables add issues button by default' do + button = page.find('.issue-boards-search button', text: 'Add issues') + + expect(button[:disabled]).to eq true + end + it 'hides the blank state when clicking nevermind button' do page.within(find('.board-blank-state')) do click_button("Nevermind, I'll use my own") From 482e7ff01201dba89a13f5e9979ea17c202c87c7 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 17:32:08 +0000 Subject: [PATCH 14/73] Fix broken test and linter error --- .../javascripts/environments/components/environment_item.js.es6 | 2 +- spec/javascripts/environments/mock_data.js.es6 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index cf79969471e..d0c5a343d71 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -97,7 +97,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean} */ hasStopAction() { - return this.model['stop_action?']; + return this.model.latest['stop_action?']; }, /** diff --git a/spec/javascripts/environments/mock_data.js.es6 b/spec/javascripts/environments/mock_data.js.es6 index bdecc95d219..081897f5456 100644 --- a/spec/javascripts/environments/mock_data.js.es6 +++ b/spec/javascripts/environments/mock_data.js.es6 @@ -55,5 +55,5 @@ const environment = { module.exports = { environmentsList, - environment + environment, }; From d0d94e4f104c276ee4095a76d1204daae384c708 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 10 Feb 2017 09:36:52 +0100 Subject: [PATCH 15/73] Fix pagination headers in grouped environments API --- app/serializers/environment_serializer.rb | 23 +++++++++++-------- .../environment_serializer_spec.rb | 11 +++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/app/serializers/environment_serializer.rb b/app/serializers/environment_serializer.rb index fe16a3784c4..d0a60f134da 100644 --- a/app/serializers/environment_serializer.rb +++ b/app/serializers/environment_serializer.rb @@ -20,8 +20,6 @@ class EnvironmentSerializer < BaseSerializer end def represent(resource, opts = {}) - resource = @paginator.paginate(resource) if paginated? - if itemized? itemize(resource).map do |item| { name: item.name, @@ -29,6 +27,8 @@ class EnvironmentSerializer < BaseSerializer latest: super(item.latest, opts) } end else + resource = @paginator.paginate(resource) if paginated? + super(resource, opts) end end @@ -36,15 +36,20 @@ class EnvironmentSerializer < BaseSerializer private def itemize(resource) - items = resource.group(:item_name).order('item_name ASC') - .pluck('COALESCE(environment_type, name) AS item_name', - 'COUNT(*) AS environments_count', - 'MAX(id) AS last_environment_id') + items = resource.order('folder_name ASC') + .group('COALESCE(environment_type, name)') + .select('COALESCE(environment_type, name) AS folder_name', + 'COUNT(*) AS size', 'MAX(id) AS last_id') - environments = resource.where(id: items.map(&:last)).index_by(&:id) + # It makes a difference when you call `paginate` method, because + # although `page` is effective at the end, it calls counting methods + # immediately. + items = @paginator.paginate(items) if paginated? - items.map do |name, size, id| - Item.new(name, size, environments[id]) + environments = resource.where(id: items.map(&:last_id)).index_by(&:id) + + items.map do |item| + Item.new(item.folder_name, item.size, environments[item.last_id]) end end end diff --git a/spec/serializers/environment_serializer_spec.rb b/spec/serializers/environment_serializer_spec.rb index 1b95f1ff198..6a6df377b35 100644 --- a/spec/serializers/environment_serializer_spec.rb +++ b/spec/serializers/environment_serializer_spec.rb @@ -181,6 +181,17 @@ describe EnvironmentSerializer do expect(subject.first[:name]).to eq 'production' expect(subject.second[:name]).to eq 'staging' end + + it 'appends correct total page count header' do + expect(subject).not_to be_empty + expect(response).to have_received(:[]=).with('X-Total', '3') + end + + it 'appends correct page count headers' do + expect(subject).not_to be_empty + expect(response).to have_received(:[]=).with('X-Total-Pages', '2') + expect(response).to have_received(:[]=).with('X-Per-Page', '2') + end end end end From f7d8c29e71dc3a5aed4cf7252beaee6fe634de6b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Sun, 12 Feb 2017 12:10:29 +0000 Subject: [PATCH 16/73] Fix broken path --- .../components/environment_item.js.es6 | 108 ++++++++++++++---- 1 file changed, 83 insertions(+), 25 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 399473bec65..391539232b1 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -1,5 +1,5 @@ const Vue = require('vue'); -const Timeago = require('timeago.j'); +const Timeago = require('timeago.js'); require('../../lib/utils/text_utility'); require('../../vue_shared/components/commit'); @@ -72,7 +72,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean} */ hasLastDeploymentKey() { - if (this.model.latest.last_deployment && + if (this.model.lastest && this.model.latest.last_deployment && !this.$options.isObjectEmpty(this.model.latest.last_deployment)) { return true; } @@ -86,7 +86,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ hasManualActions() { - return this.model.latest.last_deployment && + return this.model.lastest && this.model.latest.last_deployment && this.model.latest.last_deployment.manual_actions && this.model.latest.last_deployment.manual_actions.length > 0; }, @@ -107,7 +107,8 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ canRetry() { - return this.hasLastDeploymentKey && + return this.model.lastest && + this.hasLastDeploymentKey && this.model.latest.last_deployment && this.model.latest.last_deployment.deployable; }, @@ -118,7 +119,8 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ canShowDate() { - return this.model.latest.last_deployment && + return this.model.lastest && + this.model.latest.last_deployment && this.model.latest.last_deployment.deployable && this.model.latest.last_deployment.deployable !== undefined; }, @@ -129,7 +131,13 @@ module.exports = Vue.component('environment-item', { * @returns {String} */ createdDate() { - return timeagoInstance.format(this.model.latest.last_deployment.deployable.created_at); + if (this.model.lastest && + this.model.latest.last_deployment && + this.model.lastest.last_deployment.deployable && + this.model.latest.last_deployment.deployable.created_at) { + return timeagoInstance.format(this.model.latest.last_deployment.deployable.created_at); + } + return ''; }, /** @@ -156,7 +164,8 @@ module.exports = Vue.component('environment-item', { * @returns {String} */ userImageAltDescription() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.user && this.model.latest.last_deployment.user.username) { return `${this.model.latest.last_deployment.user.username}'s avatar'`; @@ -170,7 +179,8 @@ module.exports = Vue.component('environment-item', { * @returns {String|Undefined} */ commitTag() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.tag) { return this.model.latest.last_deployment.tag; } @@ -183,7 +193,8 @@ module.exports = Vue.component('environment-item', { * @returns {Object|Undefined} */ commitRef() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.ref) { return this.model.latest.last_deployment.ref; } @@ -196,7 +207,8 @@ module.exports = Vue.component('environment-item', { * @returns {String|Undefined} */ commitUrl() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.commit && this.model.latest.last_deployment.commit.commit_path) { return this.model.latest.last_deployment.commit.commit_path; @@ -210,7 +222,8 @@ module.exports = Vue.component('environment-item', { * @returns {String|Undefined} */ commitShortSha() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.commit && this.model.latest.last_deployment.commit.short_id) { return this.model.latest.last_deployment.commit.short_id; @@ -224,7 +237,8 @@ module.exports = Vue.component('environment-item', { * @returns {String|Undefined} */ commitTitle() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.commit && this.model.latest.last_deployment.commit.title) { return this.model.latest.last_deployment.commit.title; @@ -238,7 +252,8 @@ module.exports = Vue.component('environment-item', { * @returns {Object|Undefined} */ commitAuthor() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.commit && this.model.latest.last_deployment.commit.author) { return this.model.latest.last_deployment.commit.author; @@ -253,7 +268,8 @@ module.exports = Vue.component('environment-item', { * @returns {String|Undefined} */ retryUrl() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.deployable && this.model.latest.last_deployment.deployable.retry_path) { return this.model.latest.last_deployment.deployable.retry_path; @@ -267,7 +283,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ isLastDeployment() { - return this.model.latest.last_deployment && + return this.model.latest && this.model.latest.last_deployment && this.model.latest.last_deployment['last?']; }, @@ -277,7 +293,8 @@ module.exports = Vue.component('environment-item', { * @returns {String} */ buildName() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.deployable) { return `${this.model.latest.last_deployment.deployable.name} #${this.model.latest.last_deployment.deployable.id}`; } @@ -290,7 +307,8 @@ module.exports = Vue.component('environment-item', { * @returns {String} */ deploymentInternalId() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.iid) { return `#${this.model.latest.last_deployment.iid}`; } @@ -303,7 +321,8 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean} */ deploymentHasUser() { - return !this.$options.isObjectEmpty(this.model.latest.last_deployment) && + return this.model.latest && + !this.$options.isObjectEmpty(this.model.latest.last_deployment) && !this.$options.isObjectEmpty(this.model.latest.last_deployment.user); }, @@ -314,7 +333,8 @@ module.exports = Vue.component('environment-item', { * @returns {Object} */ deploymentUser() { - if (!this.$options.isObjectEmpty(this.model.latest.last_deployment) && + if (this.model.latest && + !this.$options.isObjectEmpty(this.model.latest.last_deployment) && !this.$options.isObjectEmpty(this.model.latest.last_deployment.user)) { return this.model.latest.last_deployment.user; } @@ -330,10 +350,39 @@ module.exports = Vue.component('environment-item', { */ shouldRenderBuildName() { return !this.model.isFolder && + this.model.lastest && !this.$options.isObjectEmpty(this.model.latest.last_deployment) && !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); }, + /** + * Verifies the presence of all the keys needed to render the buil_path. + * + * @return {String} + */ + buildPath() { + if (this.model.latest && + this.model.lastest.last_deployment && + this.model.latest.last_deployment.deployable && + this.model.lastest.last_deployment.deployable.build_path) { + return this.model.lastest.last_deployment.deployable.build_path; + } + + return ''; + }, + /** + * Verifies the presence of all the keys needed to render the external_url. + * + * @return {String} + */ + externalURL() { + if (this.model.latest && this.model.latest.external_url) { + return this.model.latest.external_url; + } + + return ''; + }, + /** * Verifies if deplyment internal ID should be rendered by verifing * if all the information needed is present @@ -343,9 +392,18 @@ module.exports = Vue.component('environment-item', { */ shouldRenderDeploymentID() { return !this.model.isFolder && + this.model.lastest && !this.$options.isObjectEmpty(this.model.latest.last_deployment) && this.model.latest.last_deployment.iid !== undefined; }, + + environmentPath() { + if (this.model && this.model.lastest && this.model.latest.environment_path) { + return this.model.latest.environment_path; + } + + return ''; + }, }, /** @@ -368,7 +426,7 @@ module.exports = Vue.component('environment-item', { + :href="environmentPath"> {{model.name}} @@ -406,7 +464,7 @@ module.exports = Vue.component('environment-item', { + :href="buildPath"> {{buildName}} @@ -445,21 +503,21 @@ module.exports = Vue.component('environment-item', {
    -
    + :external-url="externalURL">
    -
    -
    Date: Sun, 12 Feb 2017 13:43:46 +0000 Subject: [PATCH 17/73] Fix typo --- .../components/environment_item.js.es6 | 24 +++++++++---------- spec/features/projects/builds_spec.rb | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 391539232b1..7805152b173 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -72,7 +72,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean} */ hasLastDeploymentKey() { - if (this.model.lastest && this.model.latest.last_deployment && + if (this.model.latest && this.model.latest.last_deployment && !this.$options.isObjectEmpty(this.model.latest.last_deployment)) { return true; } @@ -86,7 +86,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ hasManualActions() { - return this.model.lastest && this.model.latest.last_deployment && + return this.model.latest && this.model.latest.last_deployment && this.model.latest.last_deployment.manual_actions && this.model.latest.last_deployment.manual_actions.length > 0; }, @@ -107,7 +107,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ canRetry() { - return this.model.lastest && + return this.model.latest && this.hasLastDeploymentKey && this.model.latest.last_deployment && this.model.latest.last_deployment.deployable; @@ -119,7 +119,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ canShowDate() { - return this.model.lastest && + return this.model.latest && this.model.latest.last_deployment && this.model.latest.last_deployment.deployable && this.model.latest.last_deployment.deployable !== undefined; @@ -131,9 +131,9 @@ module.exports = Vue.component('environment-item', { * @returns {String} */ createdDate() { - if (this.model.lastest && + if (this.model.latest && this.model.latest.last_deployment && - this.model.lastest.last_deployment.deployable && + this.model.latest.last_deployment.deployable && this.model.latest.last_deployment.deployable.created_at) { return timeagoInstance.format(this.model.latest.last_deployment.deployable.created_at); } @@ -350,7 +350,7 @@ module.exports = Vue.component('environment-item', { */ shouldRenderBuildName() { return !this.model.isFolder && - this.model.lastest && + this.model.latest && !this.$options.isObjectEmpty(this.model.latest.last_deployment) && !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); }, @@ -362,10 +362,10 @@ module.exports = Vue.component('environment-item', { */ buildPath() { if (this.model.latest && - this.model.lastest.last_deployment && + this.model.latest.last_deployment && this.model.latest.last_deployment.deployable && - this.model.lastest.last_deployment.deployable.build_path) { - return this.model.lastest.last_deployment.deployable.build_path; + this.model.latest.last_deployment.deployable.build_path) { + return this.model.latest.last_deployment.deployable.build_path; } return ''; @@ -392,13 +392,13 @@ module.exports = Vue.component('environment-item', { */ shouldRenderDeploymentID() { return !this.model.isFolder && - this.model.lastest && + this.model.latest && !this.$options.isObjectEmpty(this.model.latest.last_deployment) && this.model.latest.last_deployment.iid !== undefined; }, environmentPath() { - if (this.model && this.model.lastest && this.model.latest.environment_path) { + if (this.model && this.model.latest && this.model.latest.environment_path) { return this.model.latest.environment_path; } diff --git a/spec/features/projects/builds_spec.rb b/spec/features/projects/builds_spec.rb index f7e0115643e..80ba5ff1a63 100644 --- a/spec/features/projects/builds_spec.rb +++ b/spec/features/projects/builds_spec.rb @@ -271,7 +271,7 @@ feature 'Builds', :feature do let!(:deployment) { create(:deployment, environment: environment, sha: project.commit.id) } let(:build) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) } - it 'shows a link to lastest deployment' do + it 'shows a link to latest deployment' do visit namespace_project_build_path(project.namespace, project, build) expect(page).to have_link('latest deployment') From 991a7ae86055df2d0eb619e1d29a1e24e322c741 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 12:18:47 +0000 Subject: [PATCH 18/73] Adds pagination component --- .../components/environment.js.es6 | 25 +++++++++++++++++-- .../stores/environments_store.js.es6 | 13 ++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 11a965fcddf..f89dbdbda1d 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -6,11 +6,13 @@ Vue.use(require('vue-resource')); const EnvironmentsService = require('../services/environments_service'); const EnvironmentItem = require('./environment_item'); const Store = require('../stores/environments_store'); +require('../../vue_shared/components/table_pagination'); module.exports = Vue.component('environment-component', { components: { 'environment-item': EnvironmentItem, + 'table-pagination': gl.VueGlPagination, }, data() { @@ -34,6 +36,10 @@ module.exports = Vue.component('environment-component', { commitIconSvg: environmentsData.commitIconSvg, playIconSvg: environmentsData.playIconSvg, terminalIconSvg: environmentsData.terminalIconSvg, + + // Pagination Properties, + paginationInformation: {}, + pageNumber: 1, }; }, @@ -61,14 +67,18 @@ module.exports = Vue.component('environment-component', { */ created() { const scope = this.$options.getQueryParameter('scope') || this.visibility; - const endpoint = `${this.endpoint}?scope=${scope}`; + const pageNumber = this.$options.getQueryParameter('page') || this.pageNumber; + const endpoint = `${this.endpoint}?scope=${scope}&page=${pageNumber}`; const service = new EnvironmentsService(endpoint); this.isLoading = true; return service.all() - .then(resp => resp.json()) + .then((resp) => { + debugger; + return resp.json(); + }) .then((json) => { this.store.storeAvailableCount(json.available_count); this.store.storeStoppedCount(json.stopped_count); @@ -111,6 +121,10 @@ module.exports = Vue.component('environment-component', { toggleRow(model) { return this.store.toggleFolder(model.name); }, + + changePage(pageNumber, scope) { + gl.utils.visitUrl(`?scope=${scope}&p=${pageNumber}`); + }, }, template: ` @@ -192,6 +206,13 @@ module.exports = Vue.component('environment-component', { + + +
    diff --git a/app/assets/javascripts/environments/stores/environments_store.js.es6 b/app/assets/javascripts/environments/stores/environments_store.js.es6 index 749dd882188..e55b8624ac8 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js.es6 +++ b/app/assets/javascripts/environments/stores/environments_store.js.es6 @@ -1,3 +1,4 @@ +require('~/lib/utils/common_utils'); /** * Environments Store. * @@ -10,6 +11,7 @@ class EnvironmentsStore { this.state.environments = []; this.state.stoppedCounter = 0; this.state.availableCounter = 0; + this.state.paginationInformation = {}; return this; } @@ -41,6 +43,17 @@ class EnvironmentsStore { return filteredEnvironments; } + storePagination(pagination = {}) { + const normalizedHeaders = gl.utils.normalizedHeaders(pagination); + + const paginationInformation = { + + }; + + this.paginationInformation = paginationInformation; + return paginationInformation; + } + /** * Stores the number of available environments. * From fb35980893f918f7dbad0f433447c6df13a1c757 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 14:49:13 +0000 Subject: [PATCH 19/73] Verify `latest` key is present when needed --- .../environments/components/environment_item.js.es6 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 7805152b173..d3ca4cb8dde 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -355,6 +355,18 @@ module.exports = Vue.component('environment-item', { !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); }, + buildPath(){ + return this.model.latest && + this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable && + this.model.latest.last_deployment.deployable.build_path || + ''; + }, + + externalURL() { + return this.model.latest && this.model.latest.external_url || ''; + }, + /** * Verifies the presence of all the keys needed to render the buil_path. * From 595afed2e3de93d1685b2f77dd8e72df2781a57b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 15:10:16 +0000 Subject: [PATCH 20/73] Integrates pagination component with API Adds pagination tests Remove misplaced comment Fix broken store test --- .../components/environment.js.es6 | 37 +++++++++++-------- .../components/environment_item.js.es6 | 16 +++++++- .../stores/environments_store.js.es6 | 12 ++++-- .../vue_pipelines_index/pipelines.js.es6 | 1 + .../components/table_pagination.js.es6 | 4 +- .../environments/environment_spec.js.es6 | 28 +++++++++++--- .../environments_store_spec.js.es6 | 36 +++++++++++++++--- 7 files changed, 100 insertions(+), 34 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index f89dbdbda1d..d5bb9f91e7f 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -59,6 +59,7 @@ module.exports = Vue.component('environment-component', { canCreateEnvironmentParsed() { return this.$options.convertPermissionToBoolean(this.canCreateEnvironment); }, + }, /** @@ -68,6 +69,7 @@ module.exports = Vue.component('environment-component', { created() { const scope = this.$options.getQueryParameter('scope') || this.visibility; const pageNumber = this.$options.getQueryParameter('page') || this.pageNumber; + const endpoint = `${this.endpoint}?scope=${scope}&page=${pageNumber}`; const service = new EnvironmentsService(endpoint); @@ -75,14 +77,15 @@ module.exports = Vue.component('environment-component', { this.isLoading = true; return service.all() - .then((resp) => { - debugger; - return resp.json(); - }) - .then((json) => { - this.store.storeAvailableCount(json.available_count); - this.store.storeStoppedCount(json.stopped_count); - this.store.storeEnvironments(json.environments); + .then(resp => ({ + headers: resp.headers, + 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); }) .then(() => { this.isLoading = false; @@ -122,8 +125,14 @@ module.exports = Vue.component('environment-component', { return this.store.toggleFolder(model.name); }, - changePage(pageNumber, scope) { - gl.utils.visitUrl(`?scope=${scope}&p=${pageNumber}`); + /** + * Will change the page number and update the URL. + * + * @param {Number} pageNumber desired page to go to. + */ + changePage(pageNumber) { + const param = window.location.search.replace(/page=\d/g, `page=${pageNumber}`); + gl.utils.visitUrl(param); }, }, @@ -131,7 +140,7 @@ module.exports = Vue.component('environment-component', { diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index d3ca4cb8dde..1648f06a991 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -355,6 +355,11 @@ module.exports = Vue.component('environment-item', { !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); }, + /** + * Verifies the presence of all the keys needed to render the buil_path. + * + * @return {String} + */ buildPath(){ return this.model.latest && this.model.latest.last_deployment && @@ -363,8 +368,17 @@ module.exports = Vue.component('environment-item', { ''; }, + /** + * Verifies the presence of all the keys needed to render the external_url. + * + * @return {String} + */ externalURL() { - return this.model.latest && this.model.latest.external_url || ''; + if (this.model.latest && this.model.latest.external_url) { + return this.model.latest.external_url; + } + + return ''; }, /** diff --git a/app/assets/javascripts/environments/stores/environments_store.js.es6 b/app/assets/javascripts/environments/stores/environments_store.js.es6 index e55b8624ac8..252e349962e 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js.es6 +++ b/app/assets/javascripts/environments/stores/environments_store.js.es6 @@ -44,13 +44,17 @@ class EnvironmentsStore { } storePagination(pagination = {}) { - const normalizedHeaders = gl.utils.normalizedHeaders(pagination); - + const normalizedHeaders = gl.utils.normalizeHeaders(pagination); const paginationInformation = { - + perPage: parseInt(normalizedHeaders['X-PER-PAGE'], 10), + page: parseInt(normalizedHeaders['X-PAGE'], 10), + total: parseInt(normalizedHeaders['X-TOTAL'], 10), + totalPages: parseInt(normalizedHeaders['X-TOTAL-PAGES'], 10), + nextPage: parseInt(normalizedHeaders['X-NEXT-PAGE'], 10), + previousPage: parseInt(normalizedHeaders['X-PREV-PAGE'], 10), }; - this.paginationInformation = paginationInformation; + this.state.paginationInformation = paginationInformation; return paginationInformation; } diff --git a/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 b/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 index e47dc6935d6..9e816a285e4 100644 --- a/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 +++ b/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 @@ -36,6 +36,7 @@ require('../vue_shared/components/pipelines_table'); }, methods: { change(pagenum, apiScope) { + if (!apiScope) apiScope = 'all'; gl.utils.visitUrl(`?scope=${apiScope}&p=${pagenum}`); }, }, diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.js.es6 b/app/assets/javascripts/vue_shared/components/table_pagination.js.es6 index 67c6cb73761..d8042a9b7fc 100644 --- a/app/assets/javascripts/vue_shared/components/table_pagination.js.es6 +++ b/app/assets/javascripts/vue_shared/components/table_pagination.js.es6 @@ -57,9 +57,7 @@ window.Vue = require('vue'); }, methods: { changePage(e) { - let apiScope = gl.utils.getParameterByName('scope'); - - if (!apiScope) apiScope = 'all'; + const apiScope = gl.utils.getParameterByName('scope'); const text = e.target.innerText; const { totalPages, nextPage, previousPage } = this.pageInfo; diff --git a/spec/javascripts/environments/environment_spec.js.es6 b/spec/javascripts/environments/environment_spec.js.es6 index 1d1a688a4a5..f557dcee91f 100644 --- a/spec/javascripts/environments/environment_spec.js.es6 +++ b/spec/javascripts/environments/environment_spec.js.es6 @@ -49,7 +49,7 @@ describe('Environment', () => { }); }); - describe('with environments', () => { + describe('with paginated environments', () => { const environmentsResponseInterceptor = (request, next) => { next(request.respondWith(JSON.stringify({ environments: [environment], @@ -57,11 +57,22 @@ describe('Environment', () => { 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 EnvironmentsComponent({ + el: document.querySelector('#environments-list-view'), + }); }); afterEach(() => { @@ -71,10 +82,6 @@ describe('Environment', () => { }); it('should render a table with environments', (done) => { - component = new EnvironmentsComponent({ - el: document.querySelector('#environments-list-view'), - }); - setTimeout(() => { expect( component.$el.querySelectorAll('table tbody tr').length, @@ -82,6 +89,17 @@ describe('Environment', () => { done(); }, 0); }); + + describe('pagination', () => { + it('should render pagination', (done) => { + setTimeout(() => { + expect( + component.$el.querySelectorAll('.gl-pagination li').length, + ).toEqual(5); + done(); + }, 0); + }); + }); }); }); diff --git a/spec/javascripts/environments/environments_store_spec.js.es6 b/spec/javascripts/environments/environments_store_spec.js.es6 index 861136c621f..8fd660c3edb 100644 --- a/spec/javascripts/environments/environments_store_spec.js.es6 +++ b/spec/javascripts/environments/environments_store_spec.js.es6 @@ -10,24 +10,48 @@ const { environmentsList } = require('./mock_data'); }); it('should start with a blank state', () => { - expect(store.state.environments.length).toBe(0); - expect(store.state.stoppedCounter).toBe(0); - expect(store.state.availableCounter).toBe(0); + expect(store.state.environments.length).toEqual(0); + expect(store.state.stoppedCounter).toEqual(0); + expect(store.state.availableCounter).toEqual(0); + expect(store.state.paginationInformation).toEqual({}); }); it('should store environments', () => { store.storeEnvironments(environmentsList); - expect(store.state.environments.length).toBe(environmentsList.length); + expect(store.state.environments.length).toEqual(environmentsList.length); }); it('should store available count', () => { store.storeAvailableCount(2); - expect(store.state.availableCounter).toBe(2); + expect(store.state.availableCounter).toEqual(2); }); it('should store stopped count', () => { store.storeStoppedCount(2); - expect(store.state.stoppedCounter).toBe(2); + expect(store.state.stoppedCounter).toEqual(2); + }); + + it('should store pagination information', () => { + const pagination = { + 'X-nExt-pAge': '2', + 'X-page': '1', + 'X-Per-Page': '1', + 'X-Prev-Page': '2', + 'X-TOTAL': '37', + 'X-Total-Pages': '2', + }; + + const expectedResult = { + perPage: 1, + page: 1, + total: 37, + totalPages: 2, + nextPage: 2, + previousPage: 2, + }; + + store.storePagination(pagination); + expect(store.state.paginationInformation).toEqual(expectedResult); }); }); })(); From 27d7ec70b1efcaac73095e6ea3894b0eb9ba8473 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 10 Feb 2017 17:26:48 +0000 Subject: [PATCH 21/73] Remove spec checking for scope 'all' since it's no longer part of component Changes after review Fix typo --- .../environments/components/environment_item.js.es6 | 6 ++++-- .../javascripts/environments/environment_spec.js.es6 | 6 +++--- .../components/table_pagination_spec.js.es6 | 12 ++++++------ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 1648f06a991..871a09977a4 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -72,7 +72,8 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean} */ hasLastDeploymentKey() { - if (this.model.latest && this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && !this.$options.isObjectEmpty(this.model.latest.last_deployment)) { return true; } @@ -86,7 +87,8 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ hasManualActions() { - return this.model.latest && this.model.latest.last_deployment && + return this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.manual_actions && this.model.latest.last_deployment.manual_actions.length > 0; }, diff --git a/spec/javascripts/environments/environment_spec.js.es6 b/spec/javascripts/environments/environment_spec.js.es6 index f557dcee91f..657d8d2ab02 100644 --- a/spec/javascripts/environments/environment_spec.js.es6 +++ b/spec/javascripts/environments/environment_spec.js.es6 @@ -58,11 +58,11 @@ describe('Environment', () => { }), { status: 200, headers: { - 'X-Next-Page': '2', - 'X-Page': '1', + 'X-nExt-pAge': '2', + 'x-page': '1', 'X-Per-Page': '1', 'X-Prev-Page': '', - 'X-Total': '37', + 'X-TOTAL': '37', 'X-Total-Pages': '2', }, })); diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6 b/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6 index e84f0dcfe67..dd495cb43bc 100644 --- a/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6 +++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6 @@ -34,7 +34,7 @@ describe('Pagination component', () => { component.changePage({ target: { innerText: '1' } }); expect(changeChanges.one).toEqual(1); - expect(changeChanges.two).toEqual('all'); + expect(changeChanges.two).toEqual(null); }); it('should go to the previous page', () => { @@ -55,7 +55,7 @@ describe('Pagination component', () => { component.changePage({ target: { innerText: 'Prev' } }); expect(changeChanges.one).toEqual(1); - expect(changeChanges.two).toEqual('all'); + expect(changeChanges.two).toEqual(null); }); it('should go to the next page', () => { @@ -76,7 +76,7 @@ describe('Pagination component', () => { component.changePage({ target: { innerText: 'Next' } }); expect(changeChanges.one).toEqual(5); - expect(changeChanges.two).toEqual('all'); + expect(changeChanges.two).toEqual(null); }); it('should go to the last page', () => { @@ -97,7 +97,7 @@ describe('Pagination component', () => { component.changePage({ target: { innerText: 'Last >>' } }); expect(changeChanges.one).toEqual(10); - expect(changeChanges.two).toEqual('all'); + expect(changeChanges.two).toEqual(null); }); it('should go to the first page', () => { @@ -118,7 +118,7 @@ describe('Pagination component', () => { component.changePage({ target: { innerText: '<< First' } }); expect(changeChanges.one).toEqual(1); - expect(changeChanges.two).toEqual('all'); + expect(changeChanges.two).toEqual(null); }); it('should do nothing', () => { @@ -139,7 +139,7 @@ describe('Pagination component', () => { component.changePage({ target: { innerText: '...' } }); expect(changeChanges.one).toEqual(1); - expect(changeChanges.two).toEqual('all'); + expect(changeChanges.two).toEqual(null); }); }); From c2fe699ac801fd2440cc4b57083a60a334cffa06 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Sun, 12 Feb 2017 13:04:00 +0000 Subject: [PATCH 22/73] Add pagination tests for environments table Remove fdescribe statement Fix conflict --- .../components/environment.js.es6 | 20 ++++++++- .../components/environment_item.js.es6 | 27 +----------- .../environments/environment_spec.js.es6 | 42 +++++++++++++++++++ 3 files changed, 62 insertions(+), 27 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index d5bb9f91e7f..6d9599e7645 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -128,11 +128,29 @@ 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. */ changePage(pageNumber) { - const param = window.location.search.replace(/page=\d/g, `page=${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}`; + } + 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 871a09977a4..fc45c3c5f53 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -357,32 +357,6 @@ module.exports = Vue.component('environment-item', { !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); }, - /** - * Verifies the presence of all the keys needed to render the buil_path. - * - * @return {String} - */ - buildPath(){ - return this.model.latest && - this.model.latest.last_deployment && - this.model.latest.last_deployment.deployable && - this.model.latest.last_deployment.deployable.build_path || - ''; - }, - - /** - * Verifies the presence of all the keys needed to render the external_url. - * - * @return {String} - */ - externalURL() { - if (this.model.latest && this.model.latest.external_url) { - return this.model.latest.external_url; - } - - return ''; - }, - /** * Verifies the presence of all the keys needed to render the buil_path. * @@ -398,6 +372,7 @@ module.exports = Vue.component('environment-item', { return ''; }, + /** * Verifies the presence of all the keys needed to render the external_url. * diff --git a/spec/javascripts/environments/environment_spec.js.es6 b/spec/javascripts/environments/environment_spec.js.es6 index 657d8d2ab02..edd0cad32d0 100644 --- a/spec/javascripts/environments/environment_spec.js.es6 +++ b/spec/javascripts/environments/environment_spec.js.es6 @@ -99,6 +99,48 @@ describe('Environment', () => { 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); + }); }); }); }); From 142432ce5ae78cc873b9748f41e54257adc56dcc Mon Sep 17 00:00:00 2001 From: Simon Knox Date: Tue, 14 Feb 2017 17:27:29 +1100 Subject: [PATCH 23/73] update issue count when closing/reopening an issue --- app/assets/javascripts/issue.js | 3 +++ spec/javascripts/issue_spec.js | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index 6c08b1b8e61..1776b3d61f6 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -54,16 +54,19 @@ require('vendor/task_list'); success: function(data, textStatus, jqXHR) { if ('id' in data) { $(document).trigger('issuable:change'); + const currentTotal = Number($('.issue_counter').text()); if (isClose) { $('a.btn-close').addClass('hidden'); $('a.btn-reopen').removeClass('hidden'); $('div.status-box-closed').removeClass('hidden'); $('div.status-box-open').addClass('hidden'); + $('.issue_counter').text(currentTotal - 1); } else { $('a.btn-reopen').addClass('hidden'); $('a.btn-close').removeClass('hidden'); $('div.status-box-closed').addClass('hidden'); $('div.status-box-open').removeClass('hidden'); + $('.issue_counter').text(currentTotal + 1); } } else { new Flash(issueFailMessage, 'alert'); diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js index 5b0b7aa7903..beb544468ef 100644 --- a/spec/javascripts/issue_spec.js +++ b/spec/javascripts/issue_spec.js @@ -105,6 +105,7 @@ require('~/issue'); expectIssueState(false); expect($btnClose).toHaveProp('disabled', false); + expect($('.issue_counter')).toHaveText(0); }); it('fails to close an issue with success:false', function() { @@ -121,6 +122,7 @@ require('~/issue'); expectIssueState(true); expect($btnClose).toHaveProp('disabled', false); expectErrorMessage(); + expect($('.issue_counter')).toHaveText(1); }); it('fails to closes an issue with HTTP error', function() { @@ -135,6 +137,7 @@ require('~/issue'); expectIssueState(true); expect($btnClose).toHaveProp('disabled', true); expectErrorMessage(); + expect($('.issue_counter')).toHaveText(1); }); }); @@ -159,6 +162,7 @@ require('~/issue'); expectIssueState(true); expect($btnReopen).toHaveProp('disabled', false); + expect($('.issue_counter')).toHaveText(1); }); }); }).call(this); From 0dfccd995aa9bf560e59299f3a4f1705d6981115 Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Sun, 12 Feb 2017 13:02:26 +0500 Subject: [PATCH 24/73] Add active_when helper Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/24036 --- app/helpers/application_helper.rb | 9 +++++++ app/views/admin/logs/show.html.haml | 5 ++-- app/views/admin/projects/index.html.haml | 6 ++--- app/views/admin/users/index.html.haml | 14 +++++----- app/views/dashboard/_activity_head.html.haml | 4 +-- app/views/dashboard/todos/index.html.haml | 6 ++--- app/views/devise/shared/_signin_box.html.haml | 2 +- app/views/devise/shared/_tabs_ldap.html.haml | 2 +- app/views/explore/projects/_filter.html.haml | 4 +-- app/views/kaminari/gitlab/_page.html.haml | 2 +- app/views/projects/pipelines/index.html.haml | 8 +++--- .../wikis/_sidebar_wiki_page.html.haml | 2 +- app/views/search/_category.html.haml | 26 +++++++++---------- app/views/shared/builds/_tabs.html.haml | 8 +++--- app/views/shared/issuable/_nav.html.haml | 10 +++---- .../snippets/_snippets_scope_menu.html.haml | 8 +++--- spec/helpers/application_helper_spec.rb | 5 ++++ 17 files changed, 66 insertions(+), 55 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index bee323993a0..6db813d4a02 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -296,4 +296,13 @@ module ApplicationHelper def page_class "issue-boards-page" if current_controller?(:boards) end + + # Returns active css class when condition returns true + # otherwise returns nil. + # + # Example: + # %li{ class: active_when(params[:filter] == '1') } + def active_when(condition) + 'active' if condition + end end diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index 13d00dd1fcb..5e585ce789b 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -8,15 +8,14 @@ %div{ class: container_class } %ul.nav-links.log-tabs - loggers.each do |klass| - %li{ class: (klass == Gitlab::GitLogger ? 'active' : '') }> + %li{ class: active_when(klass == Gitlab::GitLogger) }> = link_to klass::file_name, "##{klass::file_name_noext}", 'data-toggle' => 'tab' .row-content-block To prevent performance issues admin logs output the last 2000 lines .tab-content - loggers.each do |klass| - .tab-pane{ class: (klass == Gitlab::GitLogger ? 'active' : ''), - id: klass::file_name_noext } + .tab-pane{ class: active_when(klass == Gitlab::GitLogger), id: klass::file_name_noext } .file-holder#README .js-file-title.file-title %i.fa.fa-file diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index cf8d438670b..756782c7d4e 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -48,13 +48,13 @@ = link_to admin_projects_path do All - = nav_link(html_options: { class: params[:visibility_level] == Gitlab::VisibilityLevel::PRIVATE.to_s ? 'active' : '' }) do + = nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::PRIVATE.to_s) }) do = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PRIVATE) do Private - = nav_link(html_options: { class: params[:visibility_level] == Gitlab::VisibilityLevel::INTERNAL.to_s ? 'active' : '' }) do + = nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::INTERNAL.to_s) }) do = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::INTERNAL) do Internal - = nav_link(html_options: { class: params[:visibility_level] == Gitlab::VisibilityLevel::PUBLIC.to_s ? 'active' : '' }) do + = nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::PUBLIC.to_s) }) do = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC) do Public diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 4dc44225d49..298cf0fa950 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -38,31 +38,31 @@ .nav-block %ul.nav-links.wide.scrolling-tabs.white.scrolling-tabs .fade-left - = nav_link(html_options: { class: ('active' unless params[:filter]) }) do + = nav_link(html_options: { class: active_when(params[:filter].nil?) }) do = link_to admin_users_path do Active %small.badge= number_with_delimiter(User.active.count) - = nav_link(html_options: { class: ('active' if params[:filter] == 'admins') }) do + = nav_link(html_options: { class: active_when(params[:filter] == 'admins') }) do = link_to admin_users_path(filter: "admins") do Admins %small.badge= number_with_delimiter(User.admins.count) - = nav_link(html_options: { class: "#{'active' if params[:filter] == 'two_factor_enabled'} filter-two-factor-enabled" }) do + = nav_link(html_options: { class: "#{active_when(params[:filter] == 'two_factor_enabled')} filter-two-factor-enabled" }) do = link_to admin_users_path(filter: 'two_factor_enabled') do 2FA Enabled %small.badge= number_with_delimiter(User.with_two_factor.count) - = nav_link(html_options: { class: "#{'active' if params[:filter] == 'two_factor_disabled'} filter-two-factor-disabled" }) do + = nav_link(html_options: { class: "#{active_when(params[:filter] == 'two_factor_disabled')} filter-two-factor-disabled" }) do = link_to admin_users_path(filter: 'two_factor_disabled') do 2FA Disabled %small.badge= number_with_delimiter(User.without_two_factor.count) - = nav_link(html_options: { class: ('active' if params[:filter] == 'external') }) do + = nav_link(html_options: { class: active_when(params[:filter] == 'external') }) do = link_to admin_users_path(filter: 'external') do External %small.badge= number_with_delimiter(User.external.count) - = nav_link(html_options: { class: ('active' if params[:filter] == 'blocked') }) do + = nav_link(html_options: { class: active_when(params[:filter] == 'blocked') }) do = link_to admin_users_path(filter: "blocked") do Blocked %small.badge= number_with_delimiter(User.blocked.count) - = nav_link(html_options: { class: ('active' if params[:filter] == 'wop') }) do + = nav_link(html_options: { class: active_when(params[:filter] == 'wop') }) do = link_to admin_users_path(filter: "wop") do Without projects %small.badge= number_with_delimiter(User.without_projects.count) diff --git a/app/views/dashboard/_activity_head.html.haml b/app/views/dashboard/_activity_head.html.haml index 68a46f61eb7..ecdf76ef5c5 100644 --- a/app/views/dashboard/_activity_head.html.haml +++ b/app/views/dashboard/_activity_head.html.haml @@ -1,8 +1,8 @@ .top-area %ul.nav-links - %li{ class: ("active" unless params[:filter]) }> + %li{ class: active_when(params[:filter].nil?) }> = link_to activity_dashboard_path, class: 'shortcuts-activity', data: {placement: 'right'} do Your Projects - %li{ class: ("active" if params[:filter] == 'starred') }> + %li{ class: active_when(params[:filter] == 'starred') }> = link_to activity_dashboard_path(filter: 'starred'), data: {placement: 'right'} do Starred Projects diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index c4bf2c90cc2..16a5713948a 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -4,15 +4,13 @@ - if current_user.todos.any? .top-area %ul.nav-links - - todo_pending_active = ('active' if params[:state].blank? || params[:state] == 'pending') - %li{ class: "todos-pending #{todo_pending_active}" }> + %li.todos-pending{ class: active_when(params[:state].blank? || params[:state] == 'pending') }> = link_to todos_filter_path(state: 'pending') do %span To do %span.badge = number_with_delimiter(todos_pending_count) - - todo_done_active = ('active' if params[:state] == 'done') - %li{ class: "todos-done #{todo_done_active}" }> + %li.todos-done{ class: active_when(params[:state] == 'done') }> = link_to todos_filter_path(state: 'done') do %span Done diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml index eddfce363a7..da4769e214e 100644 --- a/app/views/devise/shared/_signin_box.html.haml +++ b/app/views/devise/shared/_signin_box.html.haml @@ -4,7 +4,7 @@ .login-body = render 'devise/sessions/new_crowd' - @ldap_servers.each_with_index do |server, i| - .login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: (:active if i.zero? && !crowd_enabled?) } + .login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: active_when(i.zero? && !crowd_enabled?) } .login-body = render 'devise/sessions/new_ldap', server: server - if signin_enabled? diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml index 8c4ad30c832..dd34600490e 100644 --- a/app/views/devise/shared/_tabs_ldap.html.haml +++ b/app/views/devise/shared/_tabs_ldap.html.haml @@ -3,7 +3,7 @@ %li.active = link_to "Crowd", "#crowd", 'data-toggle' => 'tab' - @ldap_servers.each_with_index do |server, i| - %li{ class: (:active if i.zero? && !crowd_enabled?) } + %li{ class: active_when(i.zero? && !crowd_enabled?) } = link_to server['label'], "##{server['provider_name']}", 'data-toggle' => 'tab' - if signin_enabled? %li diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml index e3088848492..56f463572bb 100644 --- a/app/views/explore/projects/_filter.html.haml +++ b/app/views/explore/projects/_filter.html.haml @@ -13,7 +13,7 @@ = link_to filter_projects_path(visibility_level: nil) do Any - Gitlab::VisibilityLevel.values.each do |level| - %li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' } + %li{ class: active_when(level.to_s == params[:visibility_level]) || 'light' } = link_to filter_projects_path(visibility_level: level) do = visibility_level_icon(level) = visibility_level_label(level) @@ -34,7 +34,7 @@ Any - @tags.each do |tag| - %li{ class: (tag.name == params[:tag]) ? 'active' : 'light' } + %li{ class: active_when(tag.name == params[:tag]) || 'light' } = link_to filter_projects_path(tag: tag.name) do = icon('tag') = tag.name diff --git a/app/views/kaminari/gitlab/_page.html.haml b/app/views/kaminari/gitlab/_page.html.haml index cefe0066a8f..5c5be03a7cd 100644 --- a/app/views/kaminari/gitlab/_page.html.haml +++ b/app/views/kaminari/gitlab/_page.html.haml @@ -6,5 +6,5 @@ -# total_pages: total number of pages -# per_page: number of items to fetch per page -# remote: data-remote -%li{ class: "page#{' active' if page.current?}#{' sibling' if page.next? || page.prev?}" } +%li.page{ class: [active_when(page.current?), ('sibling' if page.next? || page.prev?)] } = link_to page, url, { remote: remote, rel: page.next? ? 'next' : page.prev? ? 'prev' : nil } diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index 81e393d7626..6e0428e2a31 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -5,23 +5,23 @@ %div{ class: container_class } .top-area %ul.nav-links - %li{ class: ('active' if @scope.nil?) }> + %li{ class: active_when(@scope.nil?) }> = link_to project_pipelines_path(@project) do All %span.badge.js-totalbuilds-count = number_with_delimiter(@pipelines_count) - %li{ class: ('active' if @scope == 'running') }> + %li{ class: active_when(@scope == 'running') }> = link_to project_pipelines_path(@project, scope: :running) do Running %span.badge.js-running-count = number_with_delimiter(@running_or_pending_count) - %li{ class: ('active' if @scope == 'branches') }> + %li{ class: active_when(@scope == 'branches') }> = link_to project_pipelines_path(@project, scope: :branches) do Branches - %li{ class: ('active' if @scope == 'tags') }> + %li{ class: active_when(@scope == 'tags') }> = link_to project_pipelines_path(@project, scope: :tags) do Tags diff --git a/app/views/projects/wikis/_sidebar_wiki_page.html.haml b/app/views/projects/wikis/_sidebar_wiki_page.html.haml index eb9bd14920d..0a61d90177b 100644 --- a/app/views/projects/wikis/_sidebar_wiki_page.html.haml +++ b/app/views/projects/wikis/_sidebar_wiki_page.html.haml @@ -1,3 +1,3 @@ -%li{ class: params[:id] == wiki_page.slug ? 'active' : '' } +%li{ class: active_when(params[:id] == wiki_page.slug) } = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_page) do = wiki_page.title.capitalize diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml index 8cbecb725b5..5afb95ac430 100644 --- a/app/views/search/_category.html.haml +++ b/app/views/search/_category.html.haml @@ -1,70 +1,70 @@ %ul.nav-links.search-filter - if @project - %li{ class: ("active" if @scope == 'blobs') } + %li{ class: active_when(@scope == 'blobs') } = link_to search_filter_path(scope: 'blobs') do Code %span.badge = @search_results.blobs_count - %li{ class: ("active" if @scope == 'issues') } + %li{ class: active_when(@scope == 'issues') } = link_to search_filter_path(scope: 'issues') do Issues %span.badge = @search_results.issues_count - %li{ class: ("active" if @scope == 'merge_requests') } + %li{ class: active_when(@scope == 'merge_requests') } = link_to search_filter_path(scope: 'merge_requests') do Merge requests %span.badge = @search_results.merge_requests_count - %li{ class: ("active" if @scope == 'milestones') } + %li{ class: active_when(@scope == 'milestones') } = link_to search_filter_path(scope: 'milestones') do Milestones %span.badge = @search_results.milestones_count - %li{ class: ("active" if @scope == 'notes') } + %li{ class: active_when(@scope == 'notes') } = link_to search_filter_path(scope: 'notes') do Comments %span.badge = @search_results.notes_count - %li{ class: ("active" if @scope == 'wiki_blobs') } + %li{ class: active_when(@scope == 'wiki_blobs') } = link_to search_filter_path(scope: 'wiki_blobs') do Wiki %span.badge = @search_results.wiki_blobs_count - %li{ class: ("active" if @scope == 'commits') } + %li{ class: active_when(@scope == 'commits') } = link_to search_filter_path(scope: 'commits') do Commits %span.badge = @search_results.commits_count - elsif @show_snippets - %li{ class: ("active" if @scope == 'snippet_blobs') } + %li{ class: active_when(@scope == 'snippet_blobs') } = link_to search_filter_path(scope: 'snippet_blobs', snippets: true, group_id: nil, project_id: nil) do Snippet Contents %span.badge = @search_results.snippet_blobs_count - %li{ class: ("active" if @scope == 'snippet_titles') } + %li{ class: active_when(@scope == 'snippet_titles') } = link_to search_filter_path(scope: 'snippet_titles', snippets: true, group_id: nil, project_id: nil) do Titles and Filenames %span.badge = @search_results.snippet_titles_count - else - %li{ class: ("active" if @scope == 'projects') } + %li{ class: active_when(@scope == 'projects') } = link_to search_filter_path(scope: 'projects') do Projects %span.badge = @search_results.projects_count - %li{ class: ("active" if @scope == 'issues') } + %li{ class: active_when(@scope == 'issues') } = link_to search_filter_path(scope: 'issues') do Issues %span.badge = @search_results.issues_count - %li{ class: ("active" if @scope == 'merge_requests') } + %li{ class: active_when(@scope == 'merge_requests') } = link_to search_filter_path(scope: 'merge_requests') do Merge requests %span.badge = @search_results.merge_requests_count - %li{ class: ("active" if @scope == 'milestones') } + %li{ class: active_when(@scope == 'milestones') } = link_to search_filter_path(scope: 'milestones') do Milestones %span.badge diff --git a/app/views/shared/builds/_tabs.html.haml b/app/views/shared/builds/_tabs.html.haml index b6047ece592..3baa956b910 100644 --- a/app/views/shared/builds/_tabs.html.haml +++ b/app/views/shared/builds/_tabs.html.haml @@ -1,23 +1,23 @@ %ul.nav-links - %li{ class: ('active' if scope.nil?) }> + %li{ class: active_when(scope.nil?) }> = link_to build_path_proc.call(nil) do All %span.badge.js-totalbuilds-count = number_with_delimiter(all_builds.count(:id)) - %li{ class: ('active' if scope == 'pending') }> + %li{ class: active_when(scope == 'pending') }> = link_to build_path_proc.call('pending') do Pending %span.badge = number_with_delimiter(all_builds.pending.count(:id)) - %li{ class: ('active' if scope == 'running') }> + %li{ class: active_when(scope == 'running') }> = link_to build_path_proc.call('running') do Running %span.badge = number_with_delimiter(all_builds.running.count(:id)) - %li{ class: ('active' if scope == 'finished') }> + %li{ class: active_when(scope == 'finished') }> = link_to build_path_proc.call('finished') do Finished %span.badge diff --git a/app/views/shared/issuable/_nav.html.haml b/app/views/shared/issuable/_nav.html.haml index 1154316c03f..ad995cbe962 100644 --- a/app/views/shared/issuable/_nav.html.haml +++ b/app/views/shared/issuable/_nav.html.haml @@ -3,23 +3,23 @@ - issuables = @issues || @merge_requests %ul.nav-links.issues-state-filters - %li{ class: ("active" if params[:state] == 'opened') }> + %li{ class: active_when(params[:state] == 'opened') }> = link_to page_filter_path(state: 'opened', label: true), id: 'state-opened', title: "Filter by #{page_context_word} that are currently opened." do #{issuables_state_counter_text(type, :opened)} - if type == :merge_requests - %li{ class: ("active" if params[:state] == 'merged') }> + %li{ class: active_when(params[:state] == 'merged') }> = link_to page_filter_path(state: 'merged', label: true), id: 'state-merged', title: 'Filter by merge requests that are currently merged.' do #{issuables_state_counter_text(type, :merged)} - %li{ class: ("active" if params[:state] == 'closed') }> + %li{ class: active_when(params[:state] == 'closed') }> = link_to page_filter_path(state: 'closed', label: true), id: 'state-closed', title: 'Filter by merge requests that are currently closed and unmerged.' do #{issuables_state_counter_text(type, :closed)} - else - %li{ class: ("active" if params[:state] == 'closed') }> + %li{ class: active_when(params[:state] == 'closed') }> = link_to page_filter_path(state: 'closed', label: true), id: 'state-all', title: 'Filter by issues that are currently closed.' do #{issuables_state_counter_text(type, :closed)} - %li{ class: ("active" if params[:state] == 'all') }> + %li{ class: active_when(params[:state] == 'all') }> = link_to page_filter_path(state: 'all', label: true), id: 'state-all', title: "Show all #{page_context_word}." do #{issuables_state_counter_text(type, :all)} diff --git a/app/views/snippets/_snippets_scope_menu.html.haml b/app/views/snippets/_snippets_scope_menu.html.haml index 2dda5fed647..8b6a98a054a 100644 --- a/app/views/snippets/_snippets_scope_menu.html.haml +++ b/app/views/snippets/_snippets_scope_menu.html.haml @@ -2,7 +2,7 @@ - include_private = local_assigns.fetch(:include_private, false) .nav-links.snippet-scope-menu - %li{ class: ("active" unless params[:scope]) } + %li{ class: active_when(params[:scope].nil?) } = link_to subject_snippets_path(subject) do All %span.badge @@ -12,19 +12,19 @@ = subject.snippets.public_and_internal.count - if include_private - %li{ class: ("active" if params[:scope] == "are_private") } + %li{ class: active_when(params[:scope] == "are_private") } = link_to subject_snippets_path(subject, scope: 'are_private') do Private %span.badge = subject.snippets.are_private.count - %li{ class: ("active" if params[:scope] == "are_internal") } + %li{ class: active_when(params[:scope] == "are_internal") } = link_to subject_snippets_path(subject, scope: 'are_internal') do Internal %span.badge = subject.snippets.are_internal.count - %li{ class: ("active" if params[:scope] == "are_public") } + %li{ class: active_when(params[:scope] == "are_public") } = link_to subject_snippets_path(subject, scope: 'are_public') do Public %span.badge diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 8b201f348f1..fd40fe99941 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -265,4 +265,9 @@ describe ApplicationHelper do expect(helper.render_markup('foo.adoc', content)).to eq('NOEL') end end + + describe '#active_when' do + it { expect(helper.active_when(true)).to eq('active') } + it { expect(helper.active_when(false)).to eq(nil) } + end end From 7a8d0aab61fa5d59a4bde5330948f1adcfbb542c Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Wed, 15 Feb 2017 18:54:18 +0530 Subject: [PATCH 25/73] Ensure only commit comments relevant to target project are returned --- ...-commit-comments-are-shared-across-projects.yml | 4 ++++ lib/api/commits.rb | 2 +- spec/requests/api/commits_spec.rb | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/27873-when-a-commit-appears-in-several-projects-commit-comments-are-shared-across-projects.yml diff --git a/changelogs/unreleased/27873-when-a-commit-appears-in-several-projects-commit-comments-are-shared-across-projects.yml b/changelogs/unreleased/27873-when-a-commit-appears-in-several-projects-commit-comments-are-shared-across-projects.yml new file mode 100644 index 00000000000..89e2bdc69bc --- /dev/null +++ b/changelogs/unreleased/27873-when-a-commit-appears-in-several-projects-commit-comments-are-shared-across-projects.yml @@ -0,0 +1,4 @@ +--- +title: Only return target project's comments for a commit +merge_request: +author: diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 2fefe760d24..173083d0ade 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -114,7 +114,7 @@ module API commit = user_project.commit(params[:sha]) not_found! 'Commit' unless commit - notes = Note.where(commit_id: commit.id).order(:created_at) + notes = user_project.notes.where(commit_id: commit.id).order(:created_at) present paginate(notes), with: Entities::CommitNote end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index af9028a8978..cb11cf98bf4 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -464,6 +464,20 @@ describe API::Commits, api: true do expect(response).to have_http_status(401) end end + + context 'when the commit is present on two projects' do + let(:forked_project) { create(:project, :repository, creator: user2, namespace: user2.namespace) } + let!(:forked_project_note) { create(:note_on_commit, author: user2, project: forked_project, commit_id: forked_project.repository.commit.id, note: 'a comment on a commit for fork') } + + it 'returns the comments for the target project' do + get api("/projects/#{forked_project.id}/repository/commits/#{forked_project.repository.commit.id}/comments", user2) + + expect(response).to have_http_status(200) + expect(json_response.length).to eq(1) + expect(json_response.first['note']).to eq('a comment on a commit for fork') + expect(json_response.first['author']['id']).to eq(user2.id) + end + end end describe 'POST :id/repository/commits/:sha/cherry_pick' do From e15032eded3b35097409817ddb1844164173ce1a Mon Sep 17 00:00:00 2001 From: Simon Knox Date: Thu, 16 Feb 2017 01:07:29 +1100 Subject: [PATCH 26/73] override favicon for development to find tabs more easily --- app/assets/images/favicon-purple.ico | Bin 0 -> 5430 bytes app/helpers/page_layout_helper.rb | 4 ++++ app/views/layouts/_head.html.haml | 2 +- spec/helpers/page_layout_helper_spec.rb | 12 ++++++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 app/assets/images/favicon-purple.ico diff --git a/app/assets/images/favicon-purple.ico b/app/assets/images/favicon-purple.ico new file mode 100644 index 0000000000000000000000000000000000000000..71acdf670ab48827376ef071657da6d7fa9e306f GIT binary patch literal 5430 zcmcIodr(wm6#w?YvOJeXSeEw!%kGMZW;Rojg5(RI!A6PW$I#@IHPauFDUn(_PGb&PgT@e=*$SnEcetH%cMp5{mTi~DcIM3a&N;vHJKw## z_k8CrV+y8ZVPOoki7itzW?_t(Ob)vi{FUIdfe)5|aQH#>>!VdFwAB-%YTn6>s`iI< zf07dd)p6hCCN$c2TSb({+=PaB@cF`#V+hP{o08Hdlvu>J12#!;{p*zEwj4visDI&5 zuCeN0?wp?5C9JfFUE3|~Z*{7+`7vi#f<)3Gp*u9hZ1j%AF0k%^+`B#NVo2`M5@(6hjPfTpgQu z1#?>`C$~tsSi9Jf`v|bVya#)C)^9m%6VWGeAO^7@Et8UOeKk7%tYp9Bj05hkO1O4s zF6js0lk(x(1jIuAm)Y_4Yi;4h!aT6II^&Rk?`_xKDIa}OKJEjtkW*~cuSpNnq&NLO_eRyxVkFs%>`DenN+mX@) zaS69&LDX*YG8nr_6 z!+wkX8VIh*&uf1_h4y-lwHt zd3)AK-TdhzLg$dbyOc2@i?Pl`#_T%A>Q#)DDJbe#qrL5>LoRnPMk0!iK4SuSHVF2k z$NdRVDYOSB8>{zEG*)RQy^h~!ty4=x6i){MkP_wV+^gnMU z#7`M~YDA3ZKD-bFQ1PKp7_T!^F66H_IBAh(BkPFD00-toZqLV+|7#%p3vXIN*E-zWkhW3 znw~23)W)*`&j?@6k)^IZAjg)$1J)y_FZ+n_SA~5xD)w|wUA%5z&Y`zt=TNyb2Xmcr zW@Kz#xJHSikH^n2y?^>oxk*iwBk7sIu>l0nHxN8;TMxO;VCwtmBaEbBHtEF!`}i?8 z>H1K;e_CG~C`byO(~)QXlg|-WxoRVC=MLxm!kyzd({#Ss8;kP}&)1%3ZeL&uok@G} zQ!-7}FvE^VGg95KLILDEzjL}a`#$#>fbPM4h!IZGn2Q{;?*Z3XYu_+K1=Lo5m>F|| zk4yFokhcZIdEZIjhS`tm@mlJI99}+K@pbAlV~*ML%y)RdWWVZOyQk;z_nvI{4y2iq z-ve@S_W=xg@o>ymv>-V6CXV2wo7UU7eFcACp5%kV_>W=6tn)>V zuZ}Tfn ztfDxtD;626=wA7%E0;u9^E_ljEw^`(@xy{AP@LCepXmP?o;y5bGS5SHPKNutOn^qA zZ8~6WCk*MPgVy#yi1~WmgbUFs18*F%J|G^_@SbCb_nltvIFxgEbzB|cNTZM9{nhUm z#h)e~((e|=d3=}3&k0*hIMV3rZ(YJK8lzuIJf!E2(&bZ}*U@^l@#YbmKv-PIgsY7_ z&R07z(k~7;dioaf8FY@G=ZWLf%}I?^|I8*+qbHoN{?E&kE)m}+Wu|l9@`|QMln}mX zMr4UMytnwe*^y<$w`y9%T5tFvN!pf7xjAST*Fpx+`h)k^P{$x%rTVpg%`U^MN!BS z#m+=gwChB%UL}fU3j7N}>}IUAld(iQW4dO>{DA;2!Sz9#q#oM7Bw2#09;CUn+n>ZP OiaL_NMKKZNFv&ls2@&uB literal 0 HcmV?d00001 diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index 7d4d049101a..aee8099aeb2 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -34,6 +34,10 @@ module PageLayoutHelper end end + def favicon + Rails.env.development? ? 'favicon-purple.ico' : 'favicon.ico' + end + def page_image default = image_url('gitlab_logo.png') diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index f2d355587bd..302c1794628 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -23,7 +23,7 @@ %title= page_title(site_name) %meta{ name: "description", content: page_description } - = favicon_link_tag 'favicon.ico' + = favicon_link_tag favicon = stylesheet_link_tag "application", media: "all" = stylesheet_link_tag "print", media: "print" diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index dc07657e101..872679a6ce3 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -40,6 +40,18 @@ describe PageLayoutHelper do end end + describe 'favicon' do + it 'defaults to favicon.ico' do + allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production')) + expect(helper.favicon).to eq 'favicon.ico' + end + + it 'has purple favicon for development' do + allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development')) + expect(helper.favicon).to eq 'favicon-purple.ico' + end + end + describe 'page_image' do it 'defaults to the GitLab logo' do expect(helper.page_image).to end_with 'assets/gitlab_logo.png' From 19cb1fcdf364a31077a70d8cf2af09d674f32eaa Mon Sep 17 00:00:00 2001 From: winniehell Date: Sat, 11 Feb 2017 22:22:22 +0100 Subject: [PATCH 27/73] Make Karma output look nicer for CI (!9165) --- .../unreleased/beautiful-karma-output.yml | 4 + config/karma.config.js | 3 +- package.json | 1 + yarn.lock | 195 +++++++++--------- 4 files changed, 108 insertions(+), 95 deletions(-) create mode 100644 changelogs/unreleased/beautiful-karma-output.yml diff --git a/changelogs/unreleased/beautiful-karma-output.yml b/changelogs/unreleased/beautiful-karma-output.yml new file mode 100644 index 00000000000..6ccddebab68 --- /dev/null +++ b/changelogs/unreleased/beautiful-karma-output.yml @@ -0,0 +1,4 @@ +--- +title: Make Karma output look nicer for CI +merge_request: 9165 +author: winniehell diff --git a/config/karma.config.js b/config/karma.config.js index a1fbeab1f46..5b472780aed 100644 --- a/config/karma.config.js +++ b/config/karma.config.js @@ -4,6 +4,7 @@ var ROOT_PATH = path.resolve(__dirname, '..'); // Karma configuration module.exports = function(config) { + var progressReporter = process.env.CI ? 'mocha' : 'progress'; config.set({ basePath: ROOT_PATH, browsers: ['PhantomJS'], @@ -15,7 +16,7 @@ module.exports = function(config) { preprocessors: { 'spec/javascripts/**/*.js?(.es6)': ['webpack', 'sourcemap'], }, - reporters: ['progress', 'coverage-istanbul'], + reporters: [progressReporter, 'coverage-istanbul'], coverageIstanbulReporter: { reports: ['html', 'text-summary'], dir: 'coverage-javascript/', diff --git a/package.json b/package.json index 0500ba9670e..08bde1bc313 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "jquery-ui": "github:jquery/jquery-ui#1.11.4", "jquery-ujs": "1.2.1", "js-cookie": "^2.1.3", + "karma-mocha-reporter": "^2.2.2", "mousetrap": "1.4.6", "pikaday": "^1.5.1", "select2": "3.5.2-browserify", diff --git a/yarn.lock b/yarn.lock index 42cdc8b3fcd..99db6f61bcd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,10 +1,12 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 + + abbrev@1, abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" -accepts@~1.3.3, accepts@1.3.3: +accepts@1.3.3, accepts@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" dependencies: @@ -23,14 +25,14 @@ acorn-jsx@^3.0.0: dependencies: acorn "^3.0.4" +acorn@4.0.4, acorn@^4.0.3, acorn@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" + acorn@^3.0.4: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" -acorn@^4.0.3, acorn@^4.0.4, acorn@4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" - after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" @@ -178,7 +180,11 @@ async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" -async@^1.4.0, async@^1.4.2, async@^1.5.2, async@1.x: +async@0.2.x, async@~0.2.6: + version "0.2.10" + resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" + +async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -188,10 +194,6 @@ async@^2.1.2, async@^2.1.4: dependencies: lodash "^4.14.0" -async@~0.2.6, async@0.2.x: - version "0.2.10" - resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" - async@~0.9.0: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" @@ -995,7 +997,7 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" -chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: @@ -1140,14 +1142,6 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.4.6: - version "1.6.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" - dependencies: - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - concat-stream@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.0.tgz#53f7d43c51c5e43f81c8fdd03321c631be68d611" @@ -1156,6 +1150,14 @@ concat-stream@1.5.0: readable-stream "~2.0.0" typedarray "~0.0.5" +concat-stream@^1.4.6: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + connect-history-api-fallback@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169" @@ -1263,16 +1265,16 @@ custom-event@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" +d3@3.5.11: + version "3.5.11" + resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.11.tgz#d130750eed0554db70e8432102f920a12407b69c" + d@^0.1.1, d@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" dependencies: es5-ext "~0.10.2" -d3@3.5.11: - version "3.5.11" - resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.11.tgz#d130750eed0554db70e8432102f920a12407b69c" - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1283,28 +1285,28 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -debug@^2.1.1, debug@^2.2.0, debug@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" - dependencies: - ms "0.7.2" +debug@0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" -debug@~2.2.0, debug@2.2.0: +debug@2.2.0, debug@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" dependencies: ms "0.7.1" -debug@0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" - debug@2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-2.3.3.tgz#40c453e67e6e13c901ddec317af8986cda9eff8c" dependencies: ms "0.7.2" +debug@2.6.0, debug@^2.1.1, debug@^2.2.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" + dependencies: + ms "0.7.2" + decamelize@^1.0.0, decamelize@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -1382,7 +1384,7 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" -doctrine@^1.2.2, doctrine@1.5.0: +doctrine@1.5.0, doctrine@^1.2.2: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" dependencies: @@ -1545,7 +1547,7 @@ es6-set@~0.1.3: es6-symbol "3" event-emitter "~0.3.4" -es6-symbol@~3.1, es6-symbol@~3.1.0, es6-symbol@3: +es6-symbol@3, es6-symbol@~3.1, es6-symbol@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.0.tgz#94481c655e7a7cad82eba832d97d5433496d7ffa" dependencies: @@ -1697,7 +1699,7 @@ espree@^3.4.0: acorn "4.0.4" acorn-jsx "^3.0.0" -esprima@^2.7.1, esprima@2.7.x: +esprima@2.7.x, esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -2293,7 +2295,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@2, inherits@2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -2499,14 +2501,14 @@ is-windows@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" -isarray@^1.0.0, isarray@~1.0.0, isarray@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + isbinaryfile@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621" @@ -2632,7 +2634,7 @@ jquery-ujs@1.2.1: dependencies: jquery ">=1.8.0" -jquery@>=1.8.0, jquery@2.2.1: +jquery@2.2.1, jquery@>=1.8.0: version "2.2.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.1.tgz#3c3e16854ad3d2ac44ac65021b17426d22ad803f" @@ -2644,7 +2646,7 @@ js-tokens@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" -js-yaml@^3.5.1, js-yaml@^3.7.0, js-yaml@3.x: +js-yaml@3.x, js-yaml@^3.5.1, js-yaml@^3.7.0: version "3.8.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.1.tgz#782ba50200be7b9e5a8537001b7804db3ad02628" dependencies: @@ -2681,7 +2683,7 @@ json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" -json3@^3.3.2, json3@3.3.2: +json3@3.3.2, json3@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" @@ -2721,6 +2723,12 @@ karma-jasmine@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.0.tgz#22e4c06bf9a182e5294d1f705e3733811b810acf" +karma-mocha-reporter@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/karma-mocha-reporter/-/karma-mocha-reporter-2.2.2.tgz#876de9a287244e54a608591732a98e66611f6abe" + dependencies: + chalk "1.1.3" + karma-phantomjs-launcher@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/karma-phantomjs-launcher/-/karma-phantomjs-launcher-1.0.2.tgz#19e1041498fd75563ed86730a22c1fe579fa8fb1" @@ -2823,7 +2831,7 @@ loader-runner@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" -loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.2.5, loader-utils@0.2.x: +loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.2.5: version "0.2.16" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d" dependencies: @@ -2985,7 +2993,7 @@ mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.13, mime-types@~2.1.7: dependencies: mime-db "~1.26.0" -mime@^1.3.4, mime@1.3.4: +mime@1.3.4, mime@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" @@ -2993,32 +3001,32 @@ minimalistic-assert@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, "minimatch@2 || 3": +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" dependencies: brace-expansion "^1.0.0" +minimist@0.0.8, minimist@~0.0.1: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -minimist@~0.0.1, minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - -mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.0, mkdirp@~0.5.1, mkdirp@0.5.x: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" - mkdirp@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" dependencies: minimist "0.0.8" +mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + moment@2.x: version "2.17.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82" @@ -3130,7 +3138,7 @@ node-zopfli@^2.0.0: nan "^2.0.0" node-pre-gyp "^0.6.4" -nopt@~3.0.6, nopt@3.x: +nopt@3.x, nopt@~3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" dependencies: @@ -3166,14 +3174,14 @@ oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - object-assign@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" +object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + object-component@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" @@ -3199,7 +3207,7 @@ on-headers@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" -once@^1.3.0, once@^1.4.0, once@1.x: +once@1.x, once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: @@ -3480,22 +3488,18 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" -punycode@^1.2.4, punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" +punycode@^1.2.4, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + qjobs@^1.1.4: version "1.1.5" resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73" -qs@~6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" - qs@6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" @@ -3504,6 +3508,10 @@ qs@6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.1.tgz#ce03c5ff0935bc1d9d69a9f14cbd18e568d67625" +qs@~6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -3743,11 +3751,11 @@ resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" -resolve@^1.1.6, resolve@1.1.x: +resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" -resolve@^1.2.0: +resolve@^1.1.6, resolve@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c" @@ -3764,7 +3772,7 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@^2.2.8, rimraf@^2.3.3, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@~2.5.1, rimraf@~2.5.4, rimraf@2: +rimraf@2, rimraf@^2.2.8, rimraf@^2.3.3, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@~2.5.1, rimraf@~2.5.4: version "2.5.4" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" dependencies: @@ -3796,7 +3804,7 @@ select2@3.5.2-browserify: version "3.5.2-browserify" resolved "https://registry.yarnpkg.com/select2/-/select2-3.5.2-browserify.tgz#dc4dafda38d67a734e8a97a46f0d3529ae05391d" -semver@^5.3.0, semver@~5.3.0, "semver@2 || 3 || 4 || 5": +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -3963,7 +3971,7 @@ source-map-support@^0.4.2: dependencies: source-map "^0.5.3" -source-map@^0.1.41, source-map@0.1.x: +source-map@0.1.x, source-map@^0.1.41: version "0.1.43" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" dependencies: @@ -4063,10 +4071,6 @@ stream-http@^2.3.1: to-arraybuffer "^1.0.0" xtend "^4.0.0" -string_decoder@^0.10.25, string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -4082,6 +4086,10 @@ string-width@^2.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^3.0.0" +string_decoder@^0.10.25, string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -4292,17 +4300,10 @@ underscore@1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" -unpipe@~1.0.0, unpipe@1.0.0: +unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" -url-parse@^1.1.1: - version "1.1.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.7.tgz#025cff999653a459ab34232147d89514cc87d74a" - dependencies: - querystringify "0.0.x" - requires-port "1.0.x" - url-parse@1.0.x: version "1.0.5" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" @@ -4310,6 +4311,13 @@ url-parse@1.0.x: querystringify "0.0.x" requires-port "1.0.x" +url-parse@^1.1.1: + version "1.1.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.7.tgz#025cff999653a459ab34232147d89514cc87d74a" + dependencies: + querystringify "0.0.x" + requires-port "1.0.x" + url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -4334,7 +4342,7 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -util@^0.10.3, util@0.10.3: +util@0.10.3, util@^0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" dependencies: @@ -4494,6 +4502,10 @@ window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + wordwrap@^1.0.0, wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" @@ -4502,10 +4514,6 @@ wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" @@ -4588,4 +4596,3 @@ yauzl@2.4.1: yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" - From cec1e3ebc8fe03955c3c1c27f10a7d924f2917ed Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 15 Feb 2017 16:45:41 +0100 Subject: [PATCH 28/73] create lighter version of JSON and reuse initial restore in spec to speed up run --- .../gitlab/import_export/project.light.json | 48 +++++ .../project_tree_restorer_spec.rb | 168 ++++++++---------- 2 files changed, 123 insertions(+), 93 deletions(-) create mode 100644 spec/lib/gitlab/import_export/project.light.json diff --git a/spec/lib/gitlab/import_export/project.light.json b/spec/lib/gitlab/import_export/project.light.json new file mode 100644 index 00000000000..a78836c3c34 --- /dev/null +++ b/spec/lib/gitlab/import_export/project.light.json @@ -0,0 +1,48 @@ +{ + "description": "Nisi et repellendus ut enim quo accusamus vel magnam.", + "visibility_level": 10, + "archived": false, + "labels": [ + { + "id": 2, + "title": "test2", + "color": "#428bca", + "project_id": 8, + "created_at": "2016-07-22T08:55:44.161Z", + "updated_at": "2016-07-22T08:55:44.161Z", + "template": false, + "description": "", + "type": "ProjectLabel", + "priorities": [ + ] + }, + { + "id": 3, + "title": "test3", + "color": "#428bca", + "group_id": 8, + "created_at": "2016-07-22T08:55:44.161Z", + "updated_at": "2016-07-22T08:55:44.161Z", + "template": false, + "description": "", + "project_id": null, + "type": "GroupLabel", + "priorities": [ + { + "id": 1, + "project_id": 5, + "label_id": 1, + "priority": 1, + "created_at": "2016-10-18T09:35:43.338Z", + "updated_at": "2016-10-18T09:35:43.338Z" + } + ] + } + ], + "snippets": [ + + ], + "hooks": [ + + ] +} \ No newline at end of file diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 0af13ba8e47..0eefb450f37 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -3,24 +3,27 @@ include ImportExport::CommonUtil describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do describe 'restore project tree' do - let(:user) { create(:user) } - let(:namespace) { create(:namespace, owner: user) } - let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: "", project_path: 'path') } - let!(:project) { create(:empty_project, :builds_disabled, :issues_disabled, name: 'project', path: 'project') } - let(:project_tree_restorer) { described_class.new(user: user, shared: shared, project: project) } - let(:restored_project_json) { project_tree_restorer.restore } + before(:all) do + user = create(:user) + + RSpec::Mocks.with_temporary_scope do + @shared = Gitlab::ImportExport::Shared.new(relative_path: "", project_path: 'path') + allow(@shared).to receive(:export_path).and_return('spec/lib/gitlab/import_export/') + project = create(:empty_project, :builds_disabled, :issues_disabled, name: 'project', path: 'project') + project_tree_restorer = described_class.new(user: user, shared: @shared, project: project) + @restored_project_json = project_tree_restorer.restore + end + end before do - allow(shared).to receive(:export_path).and_return('spec/lib/gitlab/import_export/') end context 'JSON' do it 'restores models based on JSON' do - expect(restored_project_json).to be true + expect(@restored_project_json).to be true end it 'restore correct project features' do - restored_project_json project = Project.find_by_path('project') expect(project.project_feature.issues_access_level).to eq(ProjectFeature::DISABLED) @@ -31,62 +34,42 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do end it 'has the same label associated to two issues' do - restored_project_json - expect(ProjectLabel.find_by_title('test2').issues.count).to eq(2) end it 'has milestones associated to two separate issues' do - restored_project_json - expect(Milestone.find_by_description('test milestone').issues.count).to eq(2) end it 'creates a valid pipeline note' do - restored_project_json - expect(Ci::Pipeline.first.notes).not_to be_empty end it 'restores pipelines with missing ref' do - restored_project_json - expect(Ci::Pipeline.where(ref: nil)).not_to be_empty end it 'restores the correct event with symbolised data' do - restored_project_json - expect(Event.where.not(data: nil).first.data[:ref]).not_to be_empty end it 'preserves updated_at on issues' do - restored_project_json - issue = Issue.where(description: 'Aliquam enim illo et possimus.').first expect(issue.reload.updated_at.to_s).to eq('2016-06-14 15:02:47 UTC') end it 'contains the merge access levels on a protected branch' do - restored_project_json - expect(ProtectedBranch.first.merge_access_levels).not_to be_empty end it 'contains the push access levels on a protected branch' do - restored_project_json - expect(ProtectedBranch.first.push_access_levels).not_to be_empty end context 'event at forth level of the tree' do let(:event) { Event.where(title: 'test levels').first } - before do - restored_project_json - end - it 'restores the event' do expect(event).not_to be_nil end @@ -99,77 +82,44 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do it 'has the correct data for merge request st_diffs' do # makes sure we are renaming the custom method +utf8_st_diffs+ into +st_diffs+ - expect { restored_project_json }.to change(MergeRequestDiff.where.not(st_diffs: nil), :count).by(9) + expect(MergeRequestDiff.where.not(st_diffs: nil).count).to eq(9) end it 'has labels associated to label links, associated to issues' do - restored_project_json - expect(Label.first.label_links.first.target).not_to be_nil end it 'has project labels' do - restored_project_json - expect(ProjectLabel.count).to eq(2) end it 'has no group labels' do - restored_project_json - expect(GroupLabel.count).to eq(0) end - context 'with group' do - let!(:project) do - create(:empty_project, - :builds_disabled, - :issues_disabled, - name: 'project', - path: 'project', - group: create(:group)) - end - - it 'has group labels' do - restored_project_json - - expect(GroupLabel.count).to eq(1) - end - - it 'has label priorities' do - restored_project_json - - expect(GroupLabel.first.priorities).not_to be_empty - end - end - it 'has a project feature' do - restored_project_json - - expect(project.project_feature).not_to be_nil + expect(Project.first.project_feature).not_to be_nil end it 'restores the correct service' do - restored_project_json - expect(CustomIssueTrackerService.first).not_to be_nil end context 'Merge requests' do before do - restored_project_json + @restored_project_json end it 'always has the new project as a target' do - expect(MergeRequest.find_by_title('MR1').target_project).to eq(project) + expect(MergeRequest.find_by_title('MR1').target_project).to eq(Project.first) end it 'has the same source project as originally if source/target are the same' do - expect(MergeRequest.find_by_title('MR1').source_project).to eq(project) + expect(MergeRequest.find_by_title('MR1').source_project).to eq(Project.first) end it 'has the new project as target if source/target differ' do - expect(MergeRequest.find_by_title('MR2').target_project).to eq(project) + expect(MergeRequest.find_by_title('MR2').target_project).to eq(Project.first) end it 'has no source if source/target differ' do @@ -177,32 +127,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do end end - context 'project.json file access check' do - it 'does not read a symlink' do - Dir.mktmpdir do |tmpdir| - setup_symlink(tmpdir, 'project.json') - allow(shared).to receive(:export_path).and_call_original - - restored_project_json - - expect(shared.errors.first).not_to include('test') - end - end - end - - context 'when there is an existing build with build token' do - it 'restores project json correctly' do - create(:ci_build, token: 'abcd') - - expect(restored_project_json).to be true - end - end - context 'tokens are regenerated' do - before do - restored_project_json - end - it 'has a new CI trigger token' do expect(Ci::Trigger.where(token: 'cdbfasdf44a5958c83654733449e585')).to be_empty end @@ -213,4 +138,61 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do end end end + + context 'Light JSON' do + let(:user) { create(:user) } + let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: "", project_path: 'path') } + let!(:project) { create(:empty_project, :builds_disabled, :issues_disabled, name: 'project', path: 'project') } + let(:project_tree_restorer) { described_class.new(user: user, shared: shared, project: project) } + let(:restored_project_json) { project_tree_restorer.restore } + + before do + allow(ImportExport).to receive(:project_filename).and_return('project.light.json') + allow(shared).to receive(:export_path).and_return('spec/lib/gitlab/import_export/') + end + + context 'project.json file access check' do + it 'does not read a symlink' do + Dir.mktmpdir do |tmpdir| + setup_symlink(tmpdir, 'project.json') + allow(shared).to receive(:export_path).and_call_original + + restored_project_json + + expect(shared.errors.first).not_to include('test') + end + end + end + + context 'when there is an existing build with build token' do + it 'restores project json correctly' do + create(:ci_build, token: 'abcd') + + expect(restored_project_json).to be true + end + end + + context 'with group' do + let!(:project) do + create(:empty_project, + :builds_disabled, + :issues_disabled, + name: 'project', + path: 'project', + group: create(:group)) + end + + before do + restored_project_json + end + + it 'has group labels' do + expect(GroupLabel.count).to eq(1) + end + + it 'has label priorities' do + expect(GroupLabel.first.priorities).not_to be_empty + end + end + end end From a89e9736266cbf9ccde3a49eccd8ab04f72bf38f Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Sat, 11 Feb 2017 22:30:15 +0500 Subject: [PATCH 29/73] Set `Auto-Submitted: auto-generated` header to emails Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/20305 --- changelogs/unreleased/add-auto-submited-header.yml | 4 ++++ .../initializers/additional_headers_interceptor.rb | 1 + lib/additional_email_headers_interceptor.rb | 8 ++++++++ .../lib/additional_email_headers_interceptor_spec.rb | 12 ++++++++++++ 4 files changed, 25 insertions(+) create mode 100644 changelogs/unreleased/add-auto-submited-header.yml create mode 100644 config/initializers/additional_headers_interceptor.rb create mode 100644 lib/additional_email_headers_interceptor.rb create mode 100644 spec/lib/additional_email_headers_interceptor_spec.rb diff --git a/changelogs/unreleased/add-auto-submited-header.yml b/changelogs/unreleased/add-auto-submited-header.yml new file mode 100644 index 00000000000..93481613b39 --- /dev/null +++ b/changelogs/unreleased/add-auto-submited-header.yml @@ -0,0 +1,4 @@ +--- +title: Set Auto-Submitted header to mails +merge_request: +author: Semyon Pupkov diff --git a/config/initializers/additional_headers_interceptor.rb b/config/initializers/additional_headers_interceptor.rb new file mode 100644 index 00000000000..b9159e7c06c --- /dev/null +++ b/config/initializers/additional_headers_interceptor.rb @@ -0,0 +1 @@ +ActionMailer::Base.register_interceptor(AdditionalEmailHeadersInterceptor) diff --git a/lib/additional_email_headers_interceptor.rb b/lib/additional_email_headers_interceptor.rb new file mode 100644 index 00000000000..2358fa6bbfd --- /dev/null +++ b/lib/additional_email_headers_interceptor.rb @@ -0,0 +1,8 @@ +class AdditionalEmailHeadersInterceptor + def self.delivering_email(message) + message.headers( + 'Auto-Submitted' => 'auto-generated', + 'X-Auto-Response-Suppress' => 'All' + ) + end +end diff --git a/spec/lib/additional_email_headers_interceptor_spec.rb b/spec/lib/additional_email_headers_interceptor_spec.rb new file mode 100644 index 00000000000..580450eef1e --- /dev/null +++ b/spec/lib/additional_email_headers_interceptor_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe AdditionalEmailHeadersInterceptor do + it 'adds Auto-Submitted header' do + mail = ActionMailer::Base.mail(to: 'test@mail.com', from: 'info@mail.com', body: 'hello').deliver + + expect(mail.header['To'].value).to eq('test@mail.com') + expect(mail.header['From'].value).to eq('info@mail.com') + expect(mail.header['Auto-Submitted'].value).to eq('auto-generated') + expect(mail.header['X-Auto-Response-Suppress'].value).to eq('All') + end +end From a254dcf0edfb6aa4ea93fd0bfdb992565d6e8422 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Wed, 15 Feb 2017 19:59:30 +0100 Subject: [PATCH 30/73] Add count keys to response JSON --- app/controllers/projects/environments_controller.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 3b7240d8469..fed75396d6e 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -29,9 +29,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController end def folder - @environments = project.environments - .where(environment_type: params[:id]) - .with_state(params[:scope] || :available) + folder_environments = project.environments.where(environment_type: params[:id]) + @environments = folder_environments.with_state(params[:scope] || :available) respond_to do |format| format.html @@ -41,6 +40,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController .new(project: @project, user: @current_user) .with_pagination(request, response) .represent(@environments), + available_count: folder_environments.available.count, + stopped_count: folder_environments.stopped.count } end end From 679ce9dbb35021bec3bc048948e471ba3989e518 Mon Sep 17 00:00:00 2001 From: Simon Knox Date: Thu, 16 Feb 2017 06:55:52 +1100 Subject: [PATCH 31/73] dev favicon is blue, not purple --- .../images/{favicon-purple.ico => favicon-blue.ico} | Bin app/helpers/page_layout_helper.rb | 2 +- spec/helpers/page_layout_helper_spec.rb | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) rename app/assets/images/{favicon-purple.ico => favicon-blue.ico} (100%) diff --git a/app/assets/images/favicon-purple.ico b/app/assets/images/favicon-blue.ico similarity index 100% rename from app/assets/images/favicon-purple.ico rename to app/assets/images/favicon-blue.ico diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index aee8099aeb2..3286a92a8a7 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -35,7 +35,7 @@ module PageLayoutHelper end def favicon - Rails.env.development? ? 'favicon-purple.ico' : 'favicon.ico' + Rails.env.development? ? 'favicon-blue.ico' : 'favicon.ico' end def page_image diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index 872679a6ce3..2cc0b40b2d0 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -46,9 +46,9 @@ describe PageLayoutHelper do expect(helper.favicon).to eq 'favicon.ico' end - it 'has purple favicon for development' do + it 'has blue favicon for development' do allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development')) - expect(helper.favicon).to eq 'favicon-purple.ico' + expect(helper.favicon).to eq 'favicon-blue.ico' end end From 7af6982e5fcf2b76906f33d5046dd9b2298ac32c Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Sun, 12 Feb 2017 14:40:11 +0000 Subject: [PATCH 32/73] Extracts table into a reusable component --- .../components/environment.js.es6 | 37 +++------- .../components/environments_table.js.es6 | 74 +++++++++++++++++++ .../folder/environments_folder_bundle.js.es6 | 0 .../folder/environments_folder_view.js.es6 | 0 .../projects/environments/folder.html.haml | 0 5 files changed, 85 insertions(+), 26 deletions(-) create mode 100644 app/assets/javascripts/environments/components/environments_table.js.es6 create mode 100644 app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 create mode 100644 app/assets/javascripts/environments/folder/environments_folder_view.js.es6 create mode 100644 app/views/projects/environments/folder.html.haml diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 6d9599e7645..42f74e114c9 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -4,14 +4,14 @@ const Vue = require('vue'); Vue.use(require('vue-resource')); const EnvironmentsService = require('../services/environments_service'); -const EnvironmentItem = require('./environment_item'); +const EnvironmentTable = require('./environments_table'); const Store = require('../stores/environments_store'); require('../../vue_shared/components/table_pagination'); module.exports = Vue.component('environment-component', { components: { - 'environment-item': EnvironmentItem, + 'environment-table': EnvironmentTable, 'table-pagination': gl.VueGlPagination, }, @@ -209,30 +209,15 @@ module.exports = Vue.component('environment-component', {
    - - - - - - - - - - - - - - -
    EnvironmentLast deploymentJobCommitUpdated
    + + + ([]), + }, + + canReadEnvironment: { + type: Boolean, + required: false, + default: false, + }, + + canCreateDeployment: { + type: Boolean, + required: false, + default: false, + }, + + commitIconSvg: { + type: String, + required: false, + }, + + playIconSvg: { + type: String, + required: false, + }, + + terminalIconSvg: { + type: String, + required: false, + }, + }, + + template: ` + + + + + + + + + + + + + + +
    EnvironmentLast deploymentJobCommitUpdated
    + `, +}); diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 b/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 b/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml new file mode 100644 index 00000000000..e69de29bb2d From 26d18387dea786ded85df3a429542c5d1e4f1ac1 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Sun, 12 Feb 2017 14:55:18 +0000 Subject: [PATCH 33/73] First iteration --- .../folder/environments_folder_bundle.js.es6 | 14 ++ .../folder/environments_folder_view.js.es6 | 196 ++++++++++++++++++ .../stores/environments_folder_store.js.es6 | 49 +++++ .../projects/environments/folder.html.haml | 8 + config/webpack.config.js | 1 + 5 files changed, 268 insertions(+) create mode 100644 app/assets/javascripts/environments/stores/environments_folder_store.js.es6 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 e69de29bb2d..9cc1c2f4f18 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 +++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 @@ -0,0 +1,14 @@ +const EnvironmentsFolderComponent = require('./environments_folder_view'); +require('../vue_shared/vue_resource_interceptor'); + +$(() => { + window.gl = window.gl || {}; + + if (gl.EnvironmentsListFolderApp) { + gl.EnvironmentsListFolderApp.$destroy(true); + } + + gl.EnvironmentsListFolderApp = new EnvironmentsFolderComponent({ + el: document.querySelector('#environments-folder-list-view'), + }); +}); 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 e69de29bb2d..070bc84bbe3 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 +++ b/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 @@ -0,0 +1,196 @@ +/* eslint-disable no-param-reassign, no-new */ +/* global Flash */ + +const Vue = require('vue'); +Vue.use(require('vue-resource')); +const EnvironmentsService = require('../services/environments_service'); +const EnvironmentTable = require('./environments_table'); +const Store = require('../stores/environments_folder_store'); +require('../../vue_shared/components/table_pagination'); + +module.exports = Vue.component('environment-folder-view', { + + components: { + 'environment-table': EnvironmentTable, + 'table-pagination': gl.VueGlPagination, + }, + + props: { + endpoint: { + type: String, + required: true, + default: '', + }, + + folderName: { + type: String, + required: true, + default: '', + }, + }, + + data() { + const store = new Store(); + + return { + store, + state: store.state, + isLoading: false, + + // Pagination Properties, + paginationInformation: {}, + pageNumber: 1, + }; + }, + + /** + * Fetches all the environments and stores them. + * Toggles loading property. + */ + created() { + const scope = this.$options.getQueryParameter('scope') || this.visibility; + const pageNumber = this.$options.getQueryParameter('page') || this.pageNumber; + + const endpoint = `${this.endpoint}?scope=${scope}&page=${pageNumber}`; + + const service = new EnvironmentsService(endpoint); + + this.isLoading = true; + + return service.all() + .then(resp => ({ + headers: resp.headers, + body: resp.json(), + })) + .then((response) => { + this.store.storeEnvironments(response.body.environments); + this.store.storePagination(response.headers); + }) + .then(() => { + this.isLoading = false; + }) + .catch(() => { + this.isLoading = false; + new Flash('An error occurred while fetching the environments.', 'alert'); + }); + }, + + /** + * 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}`; + } + + gl.utils.visitUrl(param); + return param; + }, + }, + + template: ` +
    + + +
    +
    + +
    + +
    +

    + You don't have any environments right now. +

    +

    + Environments are places where code gets deployed, such as staging or production. +
    + + Read more about environments + +

    + + + New Environment + +
    + +
    + + + + + + +
    +
    +
    + `, +}); diff --git a/app/assets/javascripts/environments/stores/environments_folder_store.js.es6 b/app/assets/javascripts/environments/stores/environments_folder_store.js.es6 new file mode 100644 index 00000000000..005ed52d9a1 --- /dev/null +++ b/app/assets/javascripts/environments/stores/environments_folder_store.js.es6 @@ -0,0 +1,49 @@ +require('~/lib/utils/common_utils'); +/** + * Environments Folder Store. + * + * Stores received environments that belong to a parent store. + */ +class EnvironmentsFolderStore { + constructor() { + this.state = {}; + this.state.environments = []; + this.state.paginationInformation = {}; + + return this; + } + + /** + * + * Stores the received environments. + * + * Each environment has the following schema + * { name: String, size: Number, latest: Object } + * + * + * @param {Array} environments + * @returns {Array} + */ + storeEnvironments(environments = []) { + this.state.environments = environments; + + return environments; + } + + storePagination(pagination = {}) { + const normalizedHeaders = gl.utils.normalizeHeaders(pagination); + const paginationInformation = { + perPage: parseInt(normalizedHeaders['X-PER-PAGE'], 10), + page: parseInt(normalizedHeaders['X-PAGE'], 10), + total: parseInt(normalizedHeaders['X-TOTAL'], 10), + totalPages: parseInt(normalizedHeaders['X-TOTAL-PAGES'], 10), + nextPage: parseInt(normalizedHeaders['X-NEXT-PAGE'], 10), + previousPage: parseInt(normalizedHeaders['X-PREV-PAGE'], 10), + }; + + this.state.paginationInformation = paginationInformation; + return paginationInformation; + } +} + +module.exports = EnvironmentsFolderStore; diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml index e69de29bb2d..452d32fc8b6 100644 --- a/app/views/projects/environments/folder.html.haml +++ b/app/views/projects/environments/folder.html.haml @@ -0,0 +1,8 @@ +- @no_container = true +- page_title "Environments" += render "projects/pipelines/head" + +- content_for :page_specific_javascripts do + = page_specific_javascript_bundle_tag("environments_folder") + +#environments-folder-list-view diff --git a/config/webpack.config.js b/config/webpack.config.js index 00f448c1fbb..7fda5405ea2 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -22,6 +22,7 @@ var config = { commit_pipelines: './commit/pipelines/pipelines_bundle.js', diff_notes: './diff_notes/diff_notes_bundle.js', environments: './environments/environments_bundle.js', + environments_folder: './environments/folder/environments_folder_bundle.js', filtered_search: './filtered_search/filtered_search_bundle.js', graphs: './graphs/graphs_bundle.js', issuable: './issuable/issuable_bundle.js', From 082348491360d51ffaa737e3f266c4d1d6bdd946 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Sun, 12 Feb 2017 16:58:53 +0000 Subject: [PATCH 34/73] Adds url for folder; Creates new subview to show envirnoments that belong to a folder --- .../components/environment_item.js.es6 | 12 ++- .../folder/environments_folder_bundle.js.es6 | 1 - .../folder/environments_folder_view.js.es6 | 86 +++++++++---------- .../stores/environments_folder_store.js.es6 | 49 ----------- .../projects/environments/folder.html.haml | 7 +- 5 files changed, 59 insertions(+), 96 deletions(-) delete mode 100644 app/assets/javascripts/environments/stores/environments_folder_store.js.es6 diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index fc45c3c5f53..e40c97130ad 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -407,6 +407,16 @@ module.exports = Vue.component('environment-item', { return ''; }, + + /** + * Constructs folder URL based on the current location and the folder id. + * + * @return {String} + */ + folderUrl() { + return `${window.location.pathname}/folders/${this.model.latest.id}`; + }, + }, /** @@ -432,7 +442,7 @@ module.exports = Vue.component('environment-item', { :href="environmentPath"> {{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 9cc1c2f4f18..d2ca465351a 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 +++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 @@ -1,5 +1,4 @@ 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 070bc84bbe3..83643161056 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 +++ b/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 @@ -4,9 +4,8 @@ const Vue = require('vue'); Vue.use(require('vue-resource')); const EnvironmentsService = require('../services/environments_service'); -const EnvironmentTable = require('./environments_table'); -const Store = require('../stores/environments_folder_store'); -require('../../vue_shared/components/table_pagination'); +const EnvironmentTable = require('../components/environments_table'); +const Store = require('../stores/environments_store'); module.exports = Vue.component('environment-folder-view', { @@ -15,27 +14,25 @@ module.exports = Vue.component('environment-folder-view', { 'table-pagination': gl.VueGlPagination, }, - props: { - endpoint: { - type: String, - required: true, - default: '', - }, - - folderName: { - type: String, - required: true, - default: '', - }, - }, - data() { + const environmentsData = document.querySelector('#environments-folder-list-view').dataset; const store = new Store(); + const endpoint = `${window.location.pathname}.json`; return { store, + endpoint, state: store.state, + visibility: 'available', isLoading: false, + cssContainerClass: environmentsData.cssClass, + canCreateDeployment: environmentsData.canCreateDeployment, + canReadEnvironment: environmentsData.canReadEnvironment, + + // svgs + commitIconSvg: environmentsData.commitIconSvg, + playIconSvg: environmentsData.playIconSvg, + terminalIconSvg: environmentsData.terminalIconSvg, // Pagination Properties, paginationInformation: {}, @@ -43,6 +40,29 @@ module.exports = Vue.component('environment-folder-view', { }; }, + computed: { + scope() { + return this.$options.getQueryParameter('scope'); + }, + + canReadEnvironmentParsed() { + return this.$options.convertPermissionToBoolean(this.canReadEnvironment); + }, + + canCreateDeploymentParsed() { + return this.$options.convertPermissionToBoolean(this.canCreateDeployment); + }, + + stoppedPath() { + return `${window.location.pathname}?scope=stopped`; + }, + + availablePath() { + return window.location.pathname; + }, + + }, + /** * Fetches all the environments and stores them. * Toggles loading property. @@ -123,9 +143,12 @@ module.exports = Vue.component('environment-folder-view', { template: `
    @@ -153,26 +171,6 @@ module.exports = Vue.component('environment-folder-view', {
    -
    -

    - You don't have any environments right now. -

    -

    - Environments are places where code gets deployed, such as staging or production. -
    - - Read more about environments - -

    - - - New Environment - -
    -
    diff --git a/app/assets/javascripts/environments/stores/environments_folder_store.js.es6 b/app/assets/javascripts/environments/stores/environments_folder_store.js.es6 deleted file mode 100644 index 005ed52d9a1..00000000000 --- a/app/assets/javascripts/environments/stores/environments_folder_store.js.es6 +++ /dev/null @@ -1,49 +0,0 @@ -require('~/lib/utils/common_utils'); -/** - * Environments Folder Store. - * - * Stores received environments that belong to a parent store. - */ -class EnvironmentsFolderStore { - constructor() { - this.state = {}; - this.state.environments = []; - this.state.paginationInformation = {}; - - return this; - } - - /** - * - * Stores the received environments. - * - * Each environment has the following schema - * { name: String, size: Number, latest: Object } - * - * - * @param {Array} environments - * @returns {Array} - */ - storeEnvironments(environments = []) { - this.state.environments = environments; - - return environments; - } - - storePagination(pagination = {}) { - const normalizedHeaders = gl.utils.normalizeHeaders(pagination); - const paginationInformation = { - perPage: parseInt(normalizedHeaders['X-PER-PAGE'], 10), - page: parseInt(normalizedHeaders['X-PAGE'], 10), - total: parseInt(normalizedHeaders['X-TOTAL'], 10), - totalPages: parseInt(normalizedHeaders['X-TOTAL-PAGES'], 10), - nextPage: parseInt(normalizedHeaders['X-NEXT-PAGE'], 10), - previousPage: parseInt(normalizedHeaders['X-PREV-PAGE'], 10), - }; - - this.state.paginationInformation = paginationInformation; - return paginationInformation; - } -} - -module.exports = EnvironmentsFolderStore; diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml index 452d32fc8b6..d9cb7bc0331 100644 --- a/app/views/projects/environments/folder.html.haml +++ b/app/views/projects/environments/folder.html.haml @@ -5,4 +5,9 @@ - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag("environments_folder") -#environments-folder-list-view +#environments-folder-list-view{ data: { "can-create-deployment" => can?(current_user, :create_deployment, @project).to_s, + "can-read-environment" => can?(current_user, :read_environment, @project).to_s, + "css-class" => container_class, + "commit-icon-svg" => custom_icon("icon_commit"), + "terminal-icon-svg" => custom_icon("icon_terminal"), + "play-icon-svg" => custom_icon("icon_play") } } From 17897c37f806e593359239ba09667081b88cb24b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 13 Feb 2017 14:41:50 +0000 Subject: [PATCH 35/73] Fix underline style --- app/assets/stylesheets/pages/environments.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 606cf501b82..2f4a3c80aeb 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -122,6 +122,7 @@ .folder-name { cursor: pointer; color: $gl-text-color-secondary; + display: inline-block; } } From 73accafe430f56cd3065774c6118de3db0a45734 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 13 Feb 2017 14:49:19 +0000 Subject: [PATCH 36/73] Use common util to get parameter name --- .../components/environment.js.es6 | 24 ++++--------------- .../folder/environments_folder_view.js.es6 | 7 +++--- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 42f74e114c9..2cbfbcad023 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -7,6 +7,7 @@ const EnvironmentsService = require('../services/environments_service'); const EnvironmentTable = require('./environments_table'); const Store = require('../stores/environments_store'); require('../../vue_shared/components/table_pagination'); +require('../../lib/utils/common_utils'); module.exports = Vue.component('environment-component', { @@ -45,7 +46,7 @@ module.exports = Vue.component('environment-component', { computed: { scope() { - return this.$options.getQueryParameter('scope'); + return gl.utils.getParameterByName('scope'); }, canReadEnvironmentParsed() { @@ -67,8 +68,8 @@ module.exports = Vue.component('environment-component', { * Toggles loading property. */ created() { - const scope = this.$options.getQueryParameter('scope') || this.visibility; - const pageNumber = this.$options.getQueryParameter('page') || this.pageNumber; + const scope = gl.utils.getParameterByName('scope') || this.visibility; + const pageNumber = gl.utils.getParameterByName('page') || this.pageNumber; const endpoint = `${this.endpoint}?scope=${scope}&page=${pageNumber}`; @@ -96,21 +97,6 @@ module.exports = Vue.component('environment-component', { }); }, - /** - * 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]; - }, - /** * Converts permission provided as strings to booleans. * @param {String} string @@ -158,7 +144,7 @@ module.exports = Vue.component('environment-component', {