summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Rose <sam@gitlab.com>2017-04-06 21:02:25 -0400
committerSam Rose <sam@gitlab.com>2017-04-06 21:02:25 -0400
commitbb23534b37f0eff6075dbc1a3337009c9f19c327 (patch)
treec9f465801b7c907ab1160b3d0378e1087bf3507f
parentb47d8a333c9aa554f6b820c6da101b67d85015c8 (diff)
parent3900aa803e574ddbcdb1a899f0e6085a0bb6c457 (diff)
downloadgitlab-ce-mr-shortcuts-counter.tar.gz
Update branch with latest stable commitsmr-shortcuts-counter
-rw-r--r--CHANGELOG.md25
-rw-r--r--VERSION2
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_bundle.js5
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.js142
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js27
-rw-r--r--app/assets/javascripts/merge_request_tabs.js36
-rw-r--r--app/assets/javascripts/vue_pipelines_index/pipelines.js5
-rw-r--r--app/assets/javascripts/vue_shared/components/pipelines_table.js72
-rw-r--r--app/controllers/concerns/continue_params.rb1
-rw-r--r--app/controllers/dashboard/todos_controller.rb2
-rw-r--r--app/controllers/projects/issues_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb2
-rw-r--r--app/controllers/registrations_controller.rb4
-rw-r--r--app/helpers/projects_helper.rb5
-rw-r--r--app/models/user.rb6
-rw-r--r--app/services/merge_requests/build_service.rb4
-rw-r--r--app/services/todo_service.rb2
-rw-r--r--app/services/users/destroy_service.rb4
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml2
-rw-r--r--changelogs/unreleased/26595-fix-issue-preselected-template.yml4
-rw-r--r--changelogs/unreleased/30098-banzai-filter-mergerequestreferencefilter-has-an-n-1-query-problem.yml4
-rw-r--r--changelogs/unreleased/30276-move-top-badges.yml4
-rw-r--r--changelogs/unreleased/30289-allow-users-to-import-github-projects-to-subgroups.yml4
-rw-r--r--changelogs/unreleased/fix-gb-environments-folders-route.yml4
-rw-r--r--changelogs/unreleased/make-ci-build-to-lock-on-status-change.yml4
-rw-r--r--doc/api/notes.md40
-rw-r--r--lib/api/helpers.rb5
-rw-r--r--lib/api/helpers/runner.rb6
-rw-r--r--lib/api/merge_requests.rb35
-rw-r--r--lib/api/notes.rb14
-rw-r--r--lib/api/runner.rb15
-rw-r--r--lib/api/users.rb2
-rw-r--r--lib/banzai/filter/markdown_filter.rb2
-rw-r--r--lib/banzai/filter/sanitization_filter.rb22
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb2
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb2
-rw-r--r--lib/banzai/renderer/html.rb13
-rw-r--r--lib/ci/api/builds.rb15
-rw-r--r--lib/ci/api/helpers.rb6
-rw-r--r--lib/gitlab/github_import/branch_formatter.rb8
-rw-r--r--lib/gitlab/github_import/pull_request_formatter.rb35
-rw-r--r--lib/tasks/karma.rake5
-rw-r--r--spec/controllers/dashboard/todos_controller_spec.rb7
-rw-r--r--spec/controllers/projects/imports_controller_spec.rb9
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb11
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb12
-rw-r--r--spec/controllers/registrations_controller_spec.rb16
-rw-r--r--spec/features/merge_requests/create_new_mr_spec.rb12
-rw-r--r--spec/helpers/events_helper_spec.rb27
-rw-r--r--spec/helpers/projects_helper_spec.rb8
-rw-r--r--spec/javascripts/commit/pipelines/pipelines_spec.js32
-rw-r--r--spec/javascripts/environments/environment_spec.js4
-rw-r--r--spec/javascripts/fixtures/merge_requests.rb12
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js49
-rw-r--r--spec/javascripts/merge_request_tabs_spec.js40
-rw-r--r--spec/javascripts/right_sidebar_spec.js6
-rw-r--r--spec/javascripts/vue_shared/components/pipelines_table_spec.js15
-rw-r--r--spec/javascripts/vue_shared/components/table_pagination_spec.js4
-rw-r--r--spec/lib/banzai/filter/markdown_filter_spec.rb19
-rw-r--r--spec/lib/banzai/filter/sanitization_filter_spec.rb7
-rw-r--r--spec/lib/banzai/filter/syntax_highlight_filter_spec.rb6
-rw-r--r--spec/lib/gitlab/github_import/importer_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/pull_request_formatter_spec.rb69
-rw-r--r--spec/requests/api/merge_requests_spec.rb58
-rw-r--r--spec/requests/api/notes_spec.rb60
-rw-r--r--spec/requests/api/users_spec.rb10
-rw-r--r--spec/services/merge_requests/build_service_spec.rb42
-rw-r--r--spec/services/users/destroy_spec.rb19
-rw-r--r--spec/support/test_env.rb2
69 files changed, 730 insertions, 429 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f914111fc4c..111a7d4f51c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,31 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 9.0.4 (2017-04-05)
+
+- Don’t show source project name when user does not have access.
+- Remove the class attribute from the whitelist for HTML generated from Markdown.
+- Fix path disclosure in project import/export.
+- Fix for open redirect vulnerability using continue[to] in URL when requesting project import status.
+- Fix for open redirect vulnerabilities in todos, issues, and MR controllers.
+
+## 9.0.3 (2017-04-05)
+
+- Fix name colision when importing GitHub pull requests from forked repositories. !9719
+- Fix GitHub Importer for PRs of deleted forked repositories. !9992
+- Fix environment folder route when special chars present in environment name. !10250
+- Improve Markdown rendering when a lot of merge requests are referenced. !10252
+- Allow users to import GitHub projects to subgroups.
+- Backport API changes needed to fix sticking in EE.
+- Remove unnecessary ORDER BY clause from `forked_to_project_id` subquery. (mhasbini)
+- Make CI build to use optimistic locking only on status change.
+- Fix race condition where a namespace would be deleted before a project was deleted.
+- Fix linking to new issue with selected template via url parameter.
+- Remove unnecessary ORDER BY clause when updating todos. (mhasbini)
+- API: Make the /notes endpoint work with noteable iid instead of id.
+- Fixes method not replacing URL parameters correctly and breaking pipelines pagination.
+- Move issue, mr, todos next to profile dropdown in top nav.
+
## 9.0.2 (2017-03-29)
- Correctly update paths when changing a child group.
diff --git a/VERSION b/VERSION
index 3beeadd423d..77063fc9e3a 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-9.0.2
+9.0.4
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
index b5a988df897..87a9cc13a7e 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
+++ b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
@@ -2,7 +2,8 @@
/* global Vue, CommitsPipelineStore, PipelinesService, Flash */
window.Vue = require('vue');
-require('./pipelines_table');
+const PipelinesTable = require('./pipelines_table');
+
/**
* Commits View > Pipelines Tab > Pipelines Table.
* Merge Request View > Pipelines Tab > Pipelines Table.
@@ -21,7 +22,7 @@ $(() => {
}
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
- gl.commits.pipelines.PipelinesTableBundle = new gl.commits.pipelines.PipelinesTableView();
+ gl.commits.pipelines.PipelinesTableBundle = new PipelinesTable();
if (pipelineTableViewEl && pipelineTableViewEl.dataset.disableInitialization === undefined) {
gl.commits.pipelines.PipelinesTableBundle.$mount(pipelineTableViewEl);
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.js b/app/assets/javascripts/commit/pipelines/pipelines_table.js
index 631ed34851c..4e770ab7c31 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.js
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.js
@@ -5,9 +5,9 @@ window.Vue = require('vue');
window.Vue.use(require('vue-resource'));
require('../../lib/utils/common_utils');
require('../../vue_shared/vue_resource_interceptor');
-require('../../vue_shared/components/pipelines_table');
require('./pipelines_service');
const PipelineStore = require('./pipelines_store');
+const PipelinesTableComponent = require('../../vue_shared/components/pipelines_table');
/**
*
@@ -19,86 +19,78 @@ const PipelineStore = require('./pipelines_store');
* Necessary SVG in the table are provided as props. This should be refactored
* as soon as we have Webpack and can load them directly into JS files.
*/
+module.exports = Vue.component('pipelines-table', {
+ components: {
+ 'pipelines-table-component': PipelinesTableComponent,
+ },
-(() => {
- window.gl = window.gl || {};
- gl.commits = gl.commits || {};
- gl.commits.pipelines = gl.commits.pipelines || {};
+ /**
+ * Accesses the DOM to provide the needed data.
+ * Returns the necessary props to render `pipelines-table-component` component.
+ *
+ * @return {Object}
+ */
+ data() {
+ const store = new PipelineStore();
- gl.commits.pipelines.PipelinesTableView = Vue.component('pipelines-table', {
+ return {
+ endpoint: null,
+ store,
+ state: store.state,
+ isLoading: false,
+ };
+ },
- components: {
- 'pipelines-table-component': gl.pipelines.PipelinesTableComponent,
- },
+ /**
+ * When the component is about to be mounted, tell the service to fetch the data
+ *
+ * A request to fetch the pipelines will be made.
+ * In case of a successfull response we will store the data in the provided
+ * store, in case of a failed response we need to warn the user.
+ *
+ */
+ beforeMount() {
+ this.endpoint = this.$el.dataset.endpoint;
+ const pipelinesService = new gl.commits.pipelines.PipelinesService(this.endpoint);
- /**
- * Accesses the DOM to provide the needed data.
- * Returns the necessary props to render `pipelines-table-component` component.
- *
- * @return {Object}
- */
- data() {
- const pipelinesTableData = document.querySelector('#commit-pipeline-table-view').dataset;
- const store = new PipelineStore();
+ this.isLoading = true;
+ return pipelinesService.all()
+ .then(response => response.json())
+ .then((json) => {
+ // depending of the endpoint the response can either bring a `pipelines` key or not.
+ const pipelines = json.pipelines || json;
+ this.store.storePipelines(pipelines);
+ this.isLoading = false;
+ })
+ .catch(() => {
+ this.isLoading = false;
+ new Flash('An error occurred while fetching the pipelines, please reload the page again.', 'alert');
+ });
+ },
- return {
- endpoint: pipelinesTableData.endpoint,
- store,
- state: store.state,
- isLoading: false,
- };
- },
+ beforeUpdate() {
+ if (this.state.pipelines.length && this.$children) {
+ PipelineStore.startTimeAgoLoops.call(this, Vue);
+ }
+ },
- /**
- * When the component is about to be mounted, tell the service to fetch the data
- *
- * A request to fetch the pipelines will be made.
- * In case of a successfull response we will store the data in the provided
- * store, in case of a failed response we need to warn the user.
- *
- */
- beforeMount() {
- const pipelinesService = new gl.commits.pipelines.PipelinesService(this.endpoint);
-
- this.isLoading = true;
- return pipelinesService.all()
- .then(response => response.json())
- .then((json) => {
- // depending of the endpoint the response can either bring a `pipelines` key or not.
- const pipelines = json.pipelines || json;
- this.store.storePipelines(pipelines);
- this.isLoading = false;
- })
- .catch(() => {
- this.isLoading = false;
- new Flash('An error occurred while fetching the pipelines, please reload the page again.', 'alert');
- });
- },
-
- beforeUpdate() {
- if (this.state.pipelines.length && this.$children) {
- PipelineStore.startTimeAgoLoops.call(this, Vue);
- }
- },
-
- template: `
- <div class="pipelines">
- <div class="realtime-loading" v-if="isLoading">
- <i class="fa fa-spinner fa-spin"></i>
- </div>
+ template: `
+ <div class="pipelines">
+ <div class="realtime-loading" v-if="isLoading">
+ <i class="fa fa-spinner fa-spin"></i>
+ </div>
- <div class="blank-state blank-state-no-icon"
- v-if="!isLoading && state.pipelines.length === 0">
- <h2 class="blank-state-title js-blank-state-title">
- No pipelines to show
- </h2>
- </div>
+ <div class="blank-state blank-state-no-icon"
+ v-if="!isLoading && state.pipelines.length === 0">
+ <h2 class="blank-state-title js-blank-state-title">
+ No pipelines to show
+ </h2>
+ </div>
- <div class="table-holder pipelines"
- v-if="!isLoading && state.pipelines.length > 0">
- <pipelines-table-component :pipelines="state.pipelines"/>
- </div>
+ <div class="table-holder pipelines"
+ v-if="!isLoading && state.pipelines.length > 0">
+ <pipelines-table-component :pipelines="state.pipelines"/>
</div>
- `,
- });
-})();
+ </div>
+ `,
+});
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index a1423b6fda5..6e1590e3b0b 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -247,7 +247,7 @@
});
/**
- * Updates the search parameter of a URL given the parameter and values provided.
+ * Updates the search parameter of a URL given the parameter and value provided.
*
* If no search params are present we'll add it.
* If param for page is already present, we'll update it
@@ -262,17 +262,24 @@
let search;
const locationSearch = window.location.search;
- if (locationSearch.length === 0) {
- search = `?${param}=${value}`;
- }
+ if (locationSearch.length) {
+ const parameters = locationSearch.substring(1, locationSearch.length)
+ .split('&')
+ .reduce((acc, element) => {
+ const val = element.split('=');
+ acc[val[0]] = decodeURIComponent(val[1]);
+ return acc;
+ }, {});
- if (locationSearch.indexOf(param) !== -1) {
- const regex = new RegExp(param + '=\\d');
- search = locationSearch.replace(regex, `${param}=${value}`);
- }
+ parameters[param] = value;
- if (locationSearch.length && locationSearch.indexOf(param) === -1) {
- search = `${locationSearch}&${param}=${value}`;
+ const toString = Object.keys(parameters)
+ .map(val => `${val}=${encodeURIComponent(parameters[val])}`)
+ .join('&');
+
+ search = `?${toString}`;
+ } else {
+ search = `?${param}=${value}`;
}
return search;
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 190336dbd20..3f0ad6490b5 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -3,9 +3,12 @@
/* global Cookies */
/* global Flash */
-require('./breakpoints');
-window.Cookies = require('js-cookie');
-require('./flash');
+import Cookies from 'js-cookie';
+
+import './breakpoints';
+import './flash';
+
+const PipelinesTable = require('./commit/pipelines/pipelines_table');
/* eslint-disable max-len */
// MergeRequestTabs
@@ -97,6 +100,13 @@ require('./flash');
.off('click', this.clickTab);
}
+ destroy() {
+ this.unbindEvents();
+ if (this.commitPipelinesTable) {
+ this.commitPipelinesTable.$destroy();
+ }
+ }
+
showTab(e) {
e.preventDefault();
this.activateTab($(e.target).data('action'));
@@ -131,12 +141,8 @@ require('./flash');
offset: 0,
});
} else if (action === 'pipelines') {
- if (this.pipelinesLoaded) {
- return;
- }
- const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
- gl.commits.pipelines.PipelinesTableBundle.$mount(pipelineTableViewEl);
- this.pipelinesLoaded = true;
+ this.resetViewContainer();
+ this.loadPipelines();
} else {
this.expandView();
this.resetViewContainer();
@@ -225,6 +231,18 @@ require('./flash');
});
}
+ loadPipelines() {
+ if (this.pipelinesLoaded) {
+ return;
+ }
+ const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
+ // Could already be mounted from the `pipelines_bundle`
+ if (pipelineTableViewEl) {
+ this.commitPipelinesTable = new PipelinesTable().$mount(pipelineTableViewEl);
+ }
+ this.pipelinesLoaded = true;
+ }
+
loadDiff(source) {
if (this.diffsLoaded) {
return;
diff --git a/app/assets/javascripts/vue_pipelines_index/pipelines.js b/app/assets/javascripts/vue_pipelines_index/pipelines.js
index 601ef41e917..4732092ab14 100644
--- a/app/assets/javascripts/vue_pipelines_index/pipelines.js
+++ b/app/assets/javascripts/vue_pipelines_index/pipelines.js
@@ -2,17 +2,18 @@
/* eslint-disable no-param-reassign */
window.Vue = require('vue');
+
require('../vue_shared/components/table_pagination');
require('./store');
-require('../vue_shared/components/pipelines_table');
const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_store');
+const PipelinesTableComponent = require('../vue_shared/components/pipelines_table');
((gl) => {
gl.VuePipelines = Vue.extend({
components: {
'gl-pagination': gl.VueGlPagination,
- 'pipelines-table-component': gl.pipelines.PipelinesTableComponent,
+ 'pipelines-table-component': PipelinesTableComponent,
},
data() {
diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table.js b/app/assets/javascripts/vue_shared/components/pipelines_table.js
index 0d8f85db965..c534bb85bf1 100644
--- a/app/assets/javascripts/vue_shared/components/pipelines_table.js
+++ b/app/assets/javascripts/vue_shared/components/pipelines_table.js
@@ -8,45 +8,39 @@ require('./pipelines_table_row');
* Given an array of objects, renders a table.
*/
-(() => {
- window.gl = window.gl || {};
- gl.pipelines = gl.pipelines || {};
-
- gl.pipelines.PipelinesTableComponent = Vue.component('pipelines-table-component', {
-
- props: {
- pipelines: {
- type: Array,
- required: true,
- default: () => ([]),
- },
-
+module.exports = {
+ props: {
+ pipelines: {
+ type: Array,
+ required: true,
+ default: () => ([]),
},
- components: {
- 'pipelines-table-row-component': gl.pipelines.PipelinesTableRowComponent,
- },
+ },
+
+ components: {
+ 'pipelines-table-row-component': gl.pipelines.PipelinesTableRowComponent,
+ },
- template: `
- <table class="table ci-table">
- <thead>
- <tr>
- <th class="js-pipeline-status pipeline-status">Status</th>
- <th class="js-pipeline-info pipeline-info">Pipeline</th>
- <th class="js-pipeline-commit pipeline-commit">Commit</th>
- <th class="js-pipeline-stages pipeline-stages">Stages</th>
- <th class="js-pipeline-date pipeline-date"></th>
- <th class="js-pipeline-actions pipeline-actions"></th>
- </tr>
- </thead>
- <tbody>
- <template v-for="model in pipelines"
- v-bind:model="model">
- <tr is="pipelines-table-row-component"
- :pipeline="model"></tr>
- </template>
- </tbody>
- </table>
- `,
- });
-})();
+ template: `
+ <table class="table ci-table">
+ <thead>
+ <tr>
+ <th class="js-pipeline-status pipeline-status">Status</th>
+ <th class="js-pipeline-info pipeline-info">Pipeline</th>
+ <th class="js-pipeline-commit pipeline-commit">Commit</th>
+ <th class="js-pipeline-stages pipeline-stages">Stages</th>
+ <th class="js-pipeline-date pipeline-date"></th>
+ <th class="js-pipeline-actions pipeline-actions"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <template v-for="model in pipelines"
+ v-bind:model="model">
+ <tr is="pipelines-table-row-component"
+ :pipeline="model"></tr>
+ </template>
+ </tbody>
+ </table>
+ `,
+};
diff --git a/app/controllers/concerns/continue_params.rb b/app/controllers/concerns/continue_params.rb
index 0a995c45bdf..eb3a623acdd 100644
--- a/app/controllers/concerns/continue_params.rb
+++ b/app/controllers/concerns/continue_params.rb
@@ -7,6 +7,7 @@ module ContinueParams
continue_params = continue_params.permit(:to, :notice, :notice_now)
return unless continue_params[:to] && continue_params[:to].start_with?('/')
+ return if continue_params[:to].start_with?('//')
continue_params
end
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 5848ca62777..20336349c9a 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -7,7 +7,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
@sort = params[:sort]
@todos = @todos.page(params[:page])
if @todos.out_of_range? && @todos.total_pages != 0
- redirect_to url_for(params.merge(page: @todos.total_pages))
+ redirect_to url_for(params.merge(page: @todos.total_pages, only_path: true))
end
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 857d907c1a6..6bdf089d45c 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -29,7 +29,7 @@ class Projects::IssuesController < Projects::ApplicationController
@issuable_meta_data = issuable_meta_data(@issues, @collection_type)
if @issues.out_of_range? && @issues.total_pages != 0
- return redirect_to url_for(params.merge(page: @issues.total_pages))
+ return redirect_to url_for(params.merge(page: @issues.total_pages, only_path: true))
end
if params[:label_name].present?
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 677a8a1a73a..943cc7a688f 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -42,7 +42,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type)
if @merge_requests.out_of_range? && @merge_requests.total_pages != 0
- return redirect_to url_for(params.merge(page: @merge_requests.total_pages))
+ return redirect_to url_for(params.merge(page: @merge_requests.total_pages, only_path: true))
end
if params[:label_name].present?
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index b44f38d4a0c..dff58338b94 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -24,12 +24,12 @@ class RegistrationsController < Devise::RegistrationsController
end
def destroy
- Users::DestroyService.new(current_user).execute(current_user)
+ DeleteUserWorker.perform_async(current_user.id, current_user.id)
respond_to do |format|
format.html do
session.try(:destroy)
- redirect_to new_user_session_path, notice: "Account successfully removed."
+ redirect_to new_user_session_path, notice: "Account scheduled for removal."
end
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index bd0c2cd661e..6b9e4267281 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -407,7 +407,10 @@ module ProjectsHelper
def sanitize_repo_path(project, message)
return '' unless message.present?
- message.strip.gsub(project.repository_storage_path.chomp('/'), "[REPOS PATH]")
+ exports_path = File.join(Settings.shared['path'], 'tmp/project_exports')
+ filtered_message = message.strip.gsub(exports_path, "[REPO EXPORT PATH]")
+
+ filtered_message.gsub(project.repository_storage_path.chomp('/'), "[REPOS PATH]")
end
def project_feature_options
diff --git a/app/models/user.rb b/app/models/user.rb
index 0432b2aa6a1..e786abb98e1 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -631,8 +631,10 @@ class User < ActiveRecord::Base
end
def fork_of(project)
- links = ForkedProjectLink.where(forked_from_project_id: project, forked_to_project_id: personal_projects)
-
+ links = ForkedProjectLink.where(
+ forked_from_project_id: project,
+ forked_to_project_id: personal_projects.unscope(:order)
+ )
if links.any?
links.first.forked_to_project
else
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index fdce542bd9e..d45da5180e1 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -21,7 +21,9 @@ module MergeRequests
delegate :target_branch, :source_branch, :source_project, :target_project, :compare_commits, :wip_title, :description, :errors, to: :merge_request
def find_source_project
- source_project || project
+ return source_project if source_project.present? && can?(current_user, :read_project, source_project)
+
+ project
end
def find_target_project
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index 8787a1c93a9..336f55ed161 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -202,7 +202,7 @@ class TodoService
todos = current_user.todos.where(id: ids)
# Only return those that are not really on that state
- marked_todos = todos.where.not(state: state).update_all(state: state)
+ marked_todos = todos.where.not(state: state).unscope(:order).update_all(state: state)
current_user.update_todos_count_cache
marked_todos
end
diff --git a/app/services/users/destroy_service.rb b/app/services/users/destroy_service.rb
index 833da5bc5d1..a3b32a71a64 100644
--- a/app/services/users/destroy_service.rb
+++ b/app/services/users/destroy_service.rb
@@ -20,10 +20,10 @@ module Users
Groups::DestroyService.new(group, current_user).execute
end
- user.personal_projects.each do |project|
+ user.personal_projects.with_deleted.each do |project|
# Skip repository removal because we remove directory with namespace
# that contain all this repositories
- ::Projects::DestroyService.new(project, current_user, skip_repo: true).async_execute
+ ::Projects::DestroyService.new(project, current_user, skip_repo: true).execute
end
move_issues_to_ghost_user(user)
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 25a4aec0a38..6696b988511 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -121,7 +121,7 @@
- selected_labels = issuable.labels
.block.labels
.sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(issuable.labels_array), data: { placement: "left", container: "body" } }
- = icon('tags', class: 'hidden', 'aria-hidden': 'true')
+ = icon('tags', 'aria-hidden': 'true')
%span
= selected_labels.size
.title.hide-collapsed
diff --git a/changelogs/unreleased/26595-fix-issue-preselected-template.yml b/changelogs/unreleased/26595-fix-issue-preselected-template.yml
deleted file mode 100644
index a94765f8f2a..00000000000
--- a/changelogs/unreleased/26595-fix-issue-preselected-template.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix linking to new issue with selected template via url parameter
-merge_request:
-author:
diff --git a/changelogs/unreleased/30098-banzai-filter-mergerequestreferencefilter-has-an-n-1-query-problem.yml b/changelogs/unreleased/30098-banzai-filter-mergerequestreferencefilter-has-an-n-1-query-problem.yml
deleted file mode 100644
index f3f4e065aef..00000000000
--- a/changelogs/unreleased/30098-banzai-filter-mergerequestreferencefilter-has-an-n-1-query-problem.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Improve Markdown rendering when a lot of merge requests are referenced
-merge_request: 10252
-author:
diff --git a/changelogs/unreleased/30276-move-top-badges.yml b/changelogs/unreleased/30276-move-top-badges.yml
deleted file mode 100644
index 68a8ab359fd..00000000000
--- a/changelogs/unreleased/30276-move-top-badges.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Move issue, mr, todos next to profile dropdown in top nav
-merge_request:
-author:
diff --git a/changelogs/unreleased/30289-allow-users-to-import-github-projects-to-subgroups.yml b/changelogs/unreleased/30289-allow-users-to-import-github-projects-to-subgroups.yml
deleted file mode 100644
index a33382a85e3..00000000000
--- a/changelogs/unreleased/30289-allow-users-to-import-github-projects-to-subgroups.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Allow users to import GitHub projects to subgroups
-merge_request:
-author:
diff --git a/changelogs/unreleased/fix-gb-environments-folders-route.yml b/changelogs/unreleased/fix-gb-environments-folders-route.yml
deleted file mode 100644
index fd9d9e6f168..00000000000
--- a/changelogs/unreleased/fix-gb-environments-folders-route.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix environment folder route when special chars present in environment name
-merge_request: 10250
-author:
diff --git a/changelogs/unreleased/make-ci-build-to-lock-on-status-change.yml b/changelogs/unreleased/make-ci-build-to-lock-on-status-change.yml
deleted file mode 100644
index b0c5eb4ed17..00000000000
--- a/changelogs/unreleased/make-ci-build-to-lock-on-status-change.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make CI build to use optimistic locking only on status change
-merge_request:
-author:
diff --git a/doc/api/notes.md b/doc/api/notes.md
index 6ef06b2c2e9..5e927143714 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -9,13 +9,13 @@ Notes are comments on snippets, issues or merge requests.
Gets a list of all notes for a single issue.
```
-GET /projects/:id/issues/:issue_id/notes
+GET /projects/:id/issues/:issue_iid/notes
```
Parameters:
- `id` (required) - The ID of a project
-- `issue_id` (required) - The ID of an issue
+- `issue_iid` (required) - The IID of an issue
```json
[
@@ -63,13 +63,13 @@ Parameters:
Returns a single note for a specific project issue
```
-GET /projects/:id/issues/:issue_id/notes/:note_id
+GET /projects/:id/issues/:issue_iid/notes/:note_id
```
Parameters:
- `id` (required) - The ID of a project
-- `issue_id` (required) - The ID of a project issue
+- `issue_iid` (required) - The IID of a project issue
- `note_id` (required) - The ID of an issue note
### Create new issue note
@@ -78,13 +78,13 @@ Creates a new note to a single project issue. If you create a note where the bod
only contains an Award Emoji, you'll receive this object back.
```
-POST /projects/:id/issues/:issue_id/notes
+POST /projects/:id/issues/:issue_iid/notes
```
Parameters:
- `id` (required) - The ID of a project
-- `issue_id` (required) - The ID of an issue
+- `issue_id` (required) - The IID of an issue
- `body` (required) - The content of a note
- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z
@@ -93,13 +93,13 @@ Parameters:
Modify existing note of an issue.
```
-PUT /projects/:id/issues/:issue_id/notes/:note_id
+PUT /projects/:id/issues/:issue_iid/notes/:note_id
```
Parameters:
- `id` (required) - The ID of a project
-- `issue_id` (required) - The ID of an issue
+- `issue_iid` (required) - The IID of an issue
- `note_id` (required) - The ID of a note
- `body` (required) - The content of a note
@@ -108,7 +108,7 @@ Parameters:
Deletes an existing note of an issue.
```
-DELETE /projects/:id/issues/:issue_id/notes/:note_id
+DELETE /projects/:id/issues/:issue_iid/notes/:note_id
```
Parameters:
@@ -116,7 +116,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project |
-| `issue_id` | integer | yes | The ID of an issue |
+| `issue_iid` | integer | yes | The IID of an issue |
| `note_id` | integer | yes | The ID of a note |
```bash
@@ -228,26 +228,26 @@ curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://git
Gets a list of all notes for a single merge request.
```
-GET /projects/:id/merge_requests/:merge_request_id/notes
+GET /projects/:id/merge_requests/:merge_request_iid/notes
```
Parameters:
- `id` (required) - The ID of a project
-- `merge_request_id` (required) - The ID of a project merge request
+- `merge_request_iid` (required) - The IID of a project merge request
### Get single merge request note
Returns a single note for a given merge request.
```
-GET /projects/:id/merge_requests/:merge_request_id/notes/:note_id
+GET /projects/:id/merge_requests/:merge_request_iid/notes/:note_id
```
Parameters:
- `id` (required) - The ID of a project
-- `merge_request_id` (required) - The ID of a project merge request
+- `merge_request_iid` (required) - The IID of a project merge request
- `note_id` (required) - The ID of a merge request note
```json
@@ -278,13 +278,13 @@ If you create a note where the body only contains an Award Emoji, you'll receive
this object back.
```
-POST /projects/:id/merge_requests/:merge_request_id/notes
+POST /projects/:id/merge_requests/:merge_request_iid/notes
```
Parameters:
- `id` (required) - The ID of a project
-- `merge_request_id` (required) - The ID of a merge request
+- `merge_request_iid` (required) - The IID of a merge request
- `body` (required) - The content of a note
### Modify existing merge request note
@@ -292,13 +292,13 @@ Parameters:
Modify existing note of a merge request.
```
-PUT /projects/:id/merge_requests/:merge_request_id/notes/:note_id
+PUT /projects/:id/merge_requests/:merge_request_iid/notes/:note_id
```
Parameters:
- `id` (required) - The ID of a project
-- `merge_request_id` (required) - The ID of a merge request
+- `merge_request_iid` (required) - The IID of a merge request
- `note_id` (required) - The ID of a note
- `body` (required) - The content of a note
@@ -307,7 +307,7 @@ Parameters:
Deletes an existing note of a merge request.
```
-DELETE /projects/:id/merge_requests/:merge_request_id/notes/:note_id
+DELETE /projects/:id/merge_requests/:merge_request_iid/notes/:note_id
```
Parameters:
@@ -315,7 +315,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project |
-| `merge_request_id` | integer | yes | The ID of a merge request |
+| `merge_request_iid` | integer | yes | The IID of a merge request |
| `note_id` | integer | yes | The ID of a note |
```bash
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index a9b364da9e1..b680c43a269 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -90,6 +90,11 @@ module API
MergeRequestsFinder.new(current_user, project_id: user_project.id).find_by!(iid: iid)
end
+ def find_project_snippet(id)
+ finder_params = { filter: :by_project, project: user_project }
+ SnippetsFinder.new.execute(current_user, finder_params).find(id)
+ end
+
def find_merge_request_with_access(iid, access_level = :read_merge_request)
merge_request = user_project.merge_requests.find_by!(iid: iid)
authorize! access_level, merge_request
diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb
index 74848a6e144..1369b021ea4 100644
--- a/lib/api/helpers/runner.rb
+++ b/lib/api/helpers/runner.rb
@@ -50,10 +50,14 @@ module API
forbidden!('Job has been erased!') if job.erased?
end
- def authenticate_job!(job)
+ def authenticate_job!
+ job = Ci::Build.find_by_id(params[:id])
+
validate_job!(job) do
forbidden! unless job_token_valid?(job)
end
+
+ job
end
def job_token_valid?(job)
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 7a03955a045..09821945053 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -226,41 +226,6 @@ module API
.cancel(merge_request)
end
- desc 'Get the comments of a merge request' do
- success Entities::MRNote
- end
- params do
- use :pagination
- end
- get ':id/merge_requests/:merge_request_iid/comments' do
- merge_request = find_merge_request_with_access(params[:merge_request_iid])
- present paginate(merge_request.notes.fresh), with: Entities::MRNote
- end
-
- desc 'Post a comment to a merge request' do
- success Entities::MRNote
- end
- params do
- requires :note, type: String, desc: 'The text of the comment'
- end
- post ':id/merge_requests/:merge_request_iid/comments' do
- merge_request = find_merge_request_with_access(params[:merge_request_iid], :create_note)
-
- opts = {
- note: params[:note],
- noteable_type: 'MergeRequest',
- noteable_id: merge_request.id
- }
-
- note = ::Notes::CreateService.new(user_project, current_user, opts).execute
-
- if note.save
- present note, with: Entities::MRNote
- else
- render_api_error!("Failed to save note #{note.errors.messages}", 400)
- end
- end
-
desc 'List issues that will be closed on merge' do
success Entities::MRNote
end
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 3b3e45cbd06..3dee8938179 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -21,7 +21,7 @@ module API
use :pagination
end
get ":id/#{noteables_str}/:noteable_id/notes" do
- noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id])
+ noteable = find_project_noteable(noteables_str, params[:noteable_id])
if can?(current_user, noteable_read_ability_name(noteable), noteable)
# We exclude notes that are cross-references and that cannot be viewed
@@ -49,7 +49,7 @@ module API
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
end
get ":id/#{noteables_str}/:noteable_id/notes/:note_id" do
- noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id])
+ noteable = find_project_noteable(noteables_str, params[:noteable_id])
note = noteable.notes.find(params[:note_id])
can_read_note = can?(current_user, noteable_read_ability_name(noteable), noteable) && !note.cross_reference_not_visible_for?(current_user)
@@ -69,14 +69,14 @@ module API
optional :created_at, type: String, desc: 'The creation date of the note'
end
post ":id/#{noteables_str}/:noteable_id/notes" do
+ noteable = find_project_noteable(noteables_str, params[:noteable_id])
+
opts = {
note: params[:body],
noteable_type: noteables_str.classify,
- noteable_id: params[:noteable_id]
+ noteable_id: noteable.id
}
- noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id])
-
if can?(current_user, noteable_read_ability_name(noteable), noteable)
if params[:created_at] && (current_user.is_admin? || user_project.owner == current_user)
opts[:created_at] = params[:created_at]
@@ -137,6 +137,10 @@ module API
end
helpers do
+ def find_project_noteable(noteables_str, noteable_id)
+ public_send("find_project_#{noteables_str.singularize}", noteable_id)
+ end
+
def noteable_read_ability_name(noteable)
"read_#{noteable.class.to_s.underscore}".to_sym
end
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index 4c9db2c8716..d288369e362 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -113,8 +113,7 @@ module API
optional :state, type: String, desc: %q(Job's status: success, failed)
end
put '/:id' do
- job = Ci::Build.find_by_id(params[:id])
- authenticate_job!(job)
+ job = authenticate_job!
job.update_attributes(trace: params[:trace]) if params[:trace]
@@ -140,8 +139,7 @@ module API
optional :token, type: String, desc: %q(Job's authentication token)
end
patch '/:id/trace' do
- job = Ci::Build.find_by_id(params[:id])
- authenticate_job!(job)
+ job = authenticate_job!
error!('400 Missing header Content-Range', 400) unless request.headers.has_key?('Content-Range')
content_range = request.headers['Content-Range']
@@ -175,8 +173,7 @@ module API
require_gitlab_workhorse!
Gitlab::Workhorse.verify_api_request!(headers)
- job = Ci::Build.find_by_id(params[:id])
- authenticate_job!(job)
+ job = authenticate_job!
forbidden!('Job is not running') unless job.running?
if params[:filesize]
@@ -212,8 +209,7 @@ module API
not_allowed! unless Gitlab.config.artifacts.enabled
require_gitlab_workhorse!
- job = Ci::Build.find_by_id(params[:id])
- authenticate_job!(job)
+ job = authenticate_job!
forbidden!('Job is not running!') unless job.running?
artifacts_upload_path = ArtifactUploader.artifacts_upload_path
@@ -245,8 +241,7 @@ module API
optional :token, type: String, desc: %q(Job's authentication token)
end
get '/:id/artifacts' do
- job = Ci::Build.find_by_id(params[:id])
- authenticate_job!(job)
+ job = authenticate_job!
artifacts_file = job.artifacts_file
unless artifacts_file.file_storage?
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 549003f576a..a309be9d0fa 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -312,7 +312,7 @@ module API
user = User.find_by(id: params[:id])
not_found!('User') unless user
- ::Users::DestroyService.new(current_user).execute(user)
+ DeleteUserWorker.perform_async(current_user.id, user.id)
end
desc 'Block a user. Available only for admins.'
diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb
index ff580ec68f8..ee73fa91589 100644
--- a/lib/banzai/filter/markdown_filter.rb
+++ b/lib/banzai/filter/markdown_filter.rb
@@ -14,7 +14,7 @@ module Banzai
def self.renderer
@renderer ||= begin
- renderer = Redcarpet::Render::HTML.new
+ renderer = Banzai::Renderer::HTML.new
Redcarpet::Markdown.new(renderer, redcarpet_options)
end
end
diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index d5f9e252f62..522217deae4 100644
--- a/lib/banzai/filter/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -24,10 +24,6 @@ module Banzai
# Only push these customizations once
return if customized?(whitelist[:transformers])
- # Allow code highlighting
- whitelist[:attributes]['pre'] = %w(class v-pre)
- whitelist[:attributes]['span'] = %w(class)
-
# Allow table alignment
whitelist[:attributes]['th'] = %w(style)
whitelist[:attributes]['td'] = %w(style)
@@ -52,9 +48,6 @@ module Banzai
# Remove `rel` attribute from `a` elements
whitelist[:transformers].push(self.class.remove_rel)
- # Remove `class` attribute from non-highlight spans
- whitelist[:transformers].push(self.class.clean_spans)
-
whitelist
end
@@ -84,21 +77,6 @@ module Banzai
end
end
end
-
- def clean_spans
- lambda do |env|
- node = env[:node]
-
- return unless node.name == 'span'
- return unless node.has_attribute?('class')
-
- unless node.ancestors.any? { |n| n.name.casecmp('pre').zero? }
- node.remove_attribute('class')
- end
-
- { node_whitelist: [node] }
- end
- end
end
end
end
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index a447e2b8bff..d40d1f063bb 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -16,7 +16,7 @@ module Banzai
end
def highlight_node(node)
- language = node.attr('class')
+ language = node.attr('lang')
code = node.text
css_classes = "code highlight"
lexer = lexer_for(language)
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index b25d6f18d59..639773539ad 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -9,9 +9,9 @@ module Banzai
# The GFM-to-HTML-to-GFM cycle is tested in spec/features/copy_as_gfm_spec.rb.
def self.filters
@filters ||= FilterArray[
- Filter::SyntaxHighlightFilter,
Filter::PlantumlFilter,
Filter::SanitizationFilter,
+ Filter::SyntaxHighlightFilter,
Filter::MathFilter,
Filter::UploadLinkFilter,
diff --git a/lib/banzai/renderer/html.rb b/lib/banzai/renderer/html.rb
new file mode 100644
index 00000000000..252caa35947
--- /dev/null
+++ b/lib/banzai/renderer/html.rb
@@ -0,0 +1,13 @@
+module Banzai
+ module Renderer
+ class HTML < Redcarpet::Render::HTML
+ def block_code(code, lang)
+ lang_attr = lang ? %Q{ lang="#{lang}"} : ''
+
+ "\n<pre>" \
+ "<code#{lang_attr}>#{html_escape(code)}</code>" \
+ "</pre>"
+ end
+ end
+ end
+end
diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb
index 746e76a1b1f..95cc6308c3b 100644
--- a/lib/ci/api/builds.rb
+++ b/lib/ci/api/builds.rb
@@ -86,8 +86,7 @@ module Ci
# Example Request:
# PATCH /builds/:id/trace.txt
patch ":id/trace.txt" do
- build = Ci::Build.find_by_id(params[:id])
- authenticate_build!(build)
+ build = authenticate_build!
error!('400 Missing header Content-Range', 400) unless request.headers.has_key?('Content-Range')
content_range = request.headers['Content-Range']
@@ -117,8 +116,7 @@ module Ci
require_gitlab_workhorse!
Gitlab::Workhorse.verify_api_request!(headers)
not_allowed! unless Gitlab.config.artifacts.enabled
- build = Ci::Build.find_by_id(params[:id])
- authenticate_build!(build)
+ build = authenticate_build!
forbidden!('build is not running') unless build.running?
if params[:filesize]
@@ -154,8 +152,7 @@ module Ci
post ":id/artifacts" do
require_gitlab_workhorse!
not_allowed! unless Gitlab.config.artifacts.enabled
- build = Ci::Build.find_by_id(params[:id])
- authenticate_build!(build)
+ build = authenticate_build!
forbidden!('Build is not running!') unless build.running?
artifacts_upload_path = ArtifactUploader.artifacts_upload_path
@@ -189,8 +186,7 @@ module Ci
# Example Request:
# GET /builds/:id/artifacts
get ":id/artifacts" do
- build = Ci::Build.find_by_id(params[:id])
- authenticate_build!(build)
+ build = authenticate_build!
artifacts_file = build.artifacts_file
unless artifacts_file.file_storage?
@@ -214,8 +210,7 @@ module Ci
# Example Request:
# DELETE /builds/:id/artifacts
delete ":id/artifacts" do
- build = Ci::Build.find_by_id(params[:id])
- authenticate_build!(build)
+ build = authenticate_build!
status(200)
build.erase_artifacts!
diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb
index 996990b464f..5109dc9670f 100644
--- a/lib/ci/api/helpers.rb
+++ b/lib/ci/api/helpers.rb
@@ -13,10 +13,14 @@ module Ci
forbidden! unless current_runner
end
- def authenticate_build!(build)
+ def authenticate_build!
+ build = Ci::Build.find_by_id(params[:id])
+
validate_build!(build) do
forbidden! unless build_token_valid?(build)
end
+
+ build
end
def validate_build!(build)
diff --git a/lib/gitlab/github_import/branch_formatter.rb b/lib/gitlab/github_import/branch_formatter.rb
index 0a8d05b5fe1..e7c6d2d5346 100644
--- a/lib/gitlab/github_import/branch_formatter.rb
+++ b/lib/gitlab/github_import/branch_formatter.rb
@@ -11,6 +11,14 @@ module Gitlab
sha.present? && ref.present?
end
+ def user
+ raw_data.user&.login || 'unknown'
+ end
+
+ def short_sha
+ Commit.truncate_sha(sha)
+ end
+
private
def branch_exists?
diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb
index 4ea0200e89b..6d57ac335eb 100644
--- a/lib/gitlab/github_import/pull_request_formatter.rb
+++ b/lib/gitlab/github_import/pull_request_formatter.rb
@@ -1,8 +1,8 @@
module Gitlab
module GithubImport
class PullRequestFormatter < IssuableFormatter
- delegate :exists?, :project, :ref, :repo, :sha, to: :source_branch, prefix: true
- delegate :exists?, :project, :ref, :repo, :sha, to: :target_branch, prefix: true
+ delegate :user, :project, :ref, :repo, :sha, to: :source_branch, prefix: true
+ delegate :user, :exists?, :project, :ref, :repo, :sha, :short_sha, to: :target_branch, prefix: true
def attributes
{
@@ -37,9 +37,20 @@ module Gitlab
end
def source_branch_name
- @source_branch_name ||= begin
- source_branch_exists? ? source_branch_ref : "pull/#{number}/#{source_branch_ref}"
- end
+ @source_branch_name ||=
+ if cross_project? || !source_branch_exists?
+ source_branch_name_prefixed
+ else
+ source_branch_ref
+ end
+ end
+
+ def source_branch_name_prefixed
+ "gh-#{target_branch_short_sha}/#{number}/#{source_branch_user}/#{source_branch_ref}"
+ end
+
+ def source_branch_exists?
+ !cross_project? && source_branch.exists?
end
def target_branch
@@ -47,9 +58,17 @@ module Gitlab
end
def target_branch_name
- @target_branch_name ||= begin
- target_branch_exists? ? target_branch_ref : "pull/#{number}/#{target_branch_ref}"
- end
+ @target_branch_name ||= target_branch_exists? ? target_branch_ref : target_branch_name_prefixed
+ end
+
+ def target_branch_name_prefixed
+ "gl-#{target_branch_short_sha}/#{number}/#{target_branch_user}/#{target_branch_ref}"
+ end
+
+ def cross_project?
+ return true if source_branch_repo.nil?
+
+ source_branch_repo.id != target_branch_repo.id
end
private
diff --git a/lib/tasks/karma.rake b/lib/tasks/karma.rake
index 40465ea3bf0..62a12174efa 100644
--- a/lib/tasks/karma.rake
+++ b/lib/tasks/karma.rake
@@ -1,9 +1,10 @@
unless Rails.env.production?
namespace :karma do
desc 'GitLab | Karma | Generate fixtures for JavaScript tests'
- RSpec::Core::RakeTask.new(:fixtures) do |t|
+ RSpec::Core::RakeTask.new(:fixtures, [:pattern]) do |t, args|
+ args.with_defaults(pattern: 'spec/javascripts/fixtures/*.rb')
ENV['NO_KNAPSACK'] = 'true'
- t.pattern = 'spec/javascripts/fixtures/*.rb'
+ t.pattern = args[:pattern]
t.rspec_opts = '--format documentation'
end
diff --git a/spec/controllers/dashboard/todos_controller_spec.rb b/spec/controllers/dashboard/todos_controller_spec.rb
index 7072bd5e87c..f0395fccfca 100644
--- a/spec/controllers/dashboard/todos_controller_spec.rb
+++ b/spec/controllers/dashboard/todos_controller_spec.rb
@@ -35,6 +35,13 @@ describe Dashboard::TodosController do
expect(assigns(:todos).current_page).to eq(last_page)
expect(response).to have_http_status(200)
end
+
+ it 'does not redirect to external sites when provided a host field' do
+ external_host = "www.example.com"
+ get :index, page: (last_page + 1).to_param, host: external_host
+
+ expect(response).to redirect_to(dashboard_todos_path(page: last_page))
+ end
end
end
diff --git a/spec/controllers/projects/imports_controller_spec.rb b/spec/controllers/projects/imports_controller_spec.rb
index 7c75815f3c4..6724b474179 100644
--- a/spec/controllers/projects/imports_controller_spec.rb
+++ b/spec/controllers/projects/imports_controller_spec.rb
@@ -96,12 +96,19 @@ describe Projects::ImportsController do
}
end
- it 'redirects to params[:to]' do
+ it 'redirects to internal params[:to]' do
get :show, namespace_id: project.namespace.to_param, project_id: project, continue: params
expect(flash[:notice]).to eq params[:notice]
expect(response).to redirect_to params[:to]
end
+
+ it 'does not redirect to external params[:to]' do
+ params[:to] = "//google.com"
+
+ get :show, namespace_id: project.namespace.to_param, project_id: project, continue: params
+ expect(response).not_to redirect_to params[:to]
+ end
end
end
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 972005ede3a..b16a8db01c4 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -83,6 +83,17 @@ describe Projects::IssuesController do
expect(assigns(:issues).current_page).to eq(last_page)
expect(response).to have_http_status(200)
end
+
+ it 'does not redirect to external sites when provided a host field' do
+ external_host = "www.example.com"
+ get :index,
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ page: (last_page + 1).to_param,
+ host: external_host
+
+ expect(response).to redirect_to(namespace_project_issues_path(page: last_page, state: controller.params[:state], scope: controller.params[:scope]))
+ end
end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index c310d830e81..62f48f9d590 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -176,6 +176,18 @@ describe Projects::MergeRequestsController do
expect(assigns(:merge_requests).current_page).to eq(last_page)
expect(response).to have_http_status(200)
end
+
+ it 'does not redirect to external sites when provided a host field' do
+ external_host = "www.example.com"
+ get :index,
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ state: 'opened',
+ page: (last_page + 1).to_param,
+ host: external_host
+
+ expect(response).to redirect_to(namespace_project_merge_requests_path(page: last_page, state: controller.params[:state], scope: controller.params[:scope]))
+ end
end
context 'when filtering by opened state' do
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index 8cc216445eb..5afd3132da6 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -59,4 +59,20 @@ describe RegistrationsController do
end
end
end
+
+ describe '#destroy' do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'schedules the user for destruction' do
+ expect(DeleteUserWorker).to receive(:perform_async).with(user.id, user.id)
+
+ post(:destroy)
+
+ expect(response.status).to eq(302)
+ end
+ end
end
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
index 8cc0996acab..82f1dea99a0 100644
--- a/spec/features/merge_requests/create_new_mr_spec.rb
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -43,6 +43,18 @@ feature 'Create New Merge Request', feature: true, js: true do
visit new_namespace_project_merge_request_path(project.namespace, project, merge_request: { target_project_id: private_project.id })
expect(page).not_to have_content private_project.path_with_namespace
+ expect(page).to have_content project.path_with_namespace
+ end
+ end
+
+ context 'when source project cannot be viewed by the current user' do
+ it 'does not leak the private project name & namespace' do
+ private_project = create(:project, :private)
+
+ visit new_namespace_project_merge_request_path(project.namespace, project, merge_request: { source_project_id: private_project.id })
+
+ expect(page).not_to have_content private_project.path_with_namespace
+ expect(page).to have_content project.path_with_namespace
end
end
diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb
index 81ba693f2f3..0acbd584f08 100644
--- a/spec/helpers/events_helper_spec.rb
+++ b/spec/helpers/events_helper_spec.rb
@@ -2,8 +2,10 @@ require 'spec_helper'
describe EventsHelper do
describe '#event_note' do
+ let(:user) { build(:user) }
+
before do
- allow(helper).to receive(:current_user).and_return(double)
+ allow(helper).to receive(:current_user).and_return(user)
end
it 'displays one line of plain text without alteration' do
@@ -62,11 +64,26 @@ describe EventsHelper do
expect(helper.event_note(input)).to eq(expected)
end
- it 'preserves style attribute within a tag' do
- input = '<span class="" style="background-color: #44ad8e; color: #FFFFFF;"></span>'
- expected = '<p><span style="background-color: #44ad8e; color: #FFFFFF;"></span></p>'
+ context 'labels formatting' do
+ let(:input) { 'this should be ~label_1' }
- expect(helper.event_note(input)).to eq(expected)
+ def format_event_note(project)
+ create(:label, title: 'label_1', project: project)
+
+ helper.event_note(input, { project: project })
+ end
+
+ it 'preserves style attribute for a label that can be accessed by current_user' do
+ project = create(:empty_project, :public)
+
+ expect(format_event_note(project)).to match(/span class=.*style=.*/)
+ end
+
+ it 'does not style a label that can not be accessed by current_user' do
+ project = create(:empty_project, :private)
+
+ expect(format_event_note(project)).to eq("<p>#{input}</p>")
+ end
end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index fc6ad6419ac..44312ada438 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -167,6 +167,7 @@ describe ProjectsHelper do
before do
allow(project).to receive(:repository_storage_path).and_return('/base/repo/path')
+ allow(Settings.shared).to receive(:[]).with('path').and_return('/base/repo/export/path')
end
it 'removes the repo path' do
@@ -175,6 +176,13 @@ describe ProjectsHelper do
expect(sanitize_repo_path(project, import_error)).to eq('Could not clone [REPOS PATH]/namespace/test.git')
end
+
+ it 'removes the temporary repo path used for uploads/exports' do
+ repo = '/base/repo/export/path/tmp/project_exports/uploads/test.tar.gz'
+ import_error = "Unable to decompress #{repo}\n"
+
+ expect(sanitize_repo_path(project, import_error)).to eq('Unable to decompress [REPO EXPORT PATH]/uploads/test.tar.gz')
+ end
end
describe '#last_push_event' do
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index f09c57978a1..5b76e2303ab 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -1,15 +1,18 @@
/* global pipeline, Vue */
+const PipelinesTable = require('~/commit/pipelines/pipelines_table');
+
require('~/flash');
require('~/commit/pipelines/pipelines_store');
require('~/commit/pipelines/pipelines_service');
-require('~/commit/pipelines/pipelines_table');
require('~/vue_shared/vue_resource_interceptor');
const pipeline = require('./mock_data');
describe('Pipelines table in Commits and Merge requests', () => {
preloadFixtures('static/pipelines_table.html.raw');
+ let component;
+
beforeEach(() => {
loadFixtures('static/pipelines_table.html.raw');
});
@@ -24,19 +27,20 @@ describe('Pipelines table in Commits and Merge requests', () => {
beforeEach(() => {
Vue.http.interceptors.push(pipelinesEmptyResponse);
+
+ component = new PipelinesTable({
+ el: document.querySelector('#commit-pipeline-table-view'),
+ });
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, pipelinesEmptyResponse,
);
+ component.$destroy();
});
it('should render the empty state', (done) => {
- const component = new gl.commits.pipelines.PipelinesTableView({
- el: document.querySelector('#commit-pipeline-table-view'),
- });
-
setTimeout(() => {
expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain('No pipelines to show');
done();
@@ -53,19 +57,20 @@ describe('Pipelines table in Commits and Merge requests', () => {
beforeEach(() => {
Vue.http.interceptors.push(pipelinesResponse);
+
+ component = new PipelinesTable({
+ el: document.querySelector('#commit-pipeline-table-view'),
+ });
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, pipelinesResponse,
);
+ component.$destroy();
});
it('should render a table with the received pipelines', (done) => {
- const component = new gl.commits.pipelines.PipelinesTableView({
- el: document.querySelector('#commit-pipeline-table-view'),
- });
-
setTimeout(() => {
expect(component.$el.querySelectorAll('table > tbody > tr').length).toEqual(1);
done();
@@ -83,19 +88,20 @@ describe('Pipelines table in Commits and Merge requests', () => {
beforeEach(() => {
Vue.http.interceptors.push(pipelinesErrorResponse);
+
+ component = new PipelinesTable({
+ el: document.querySelector('#commit-pipeline-table-view'),
+ });
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, pipelinesErrorResponse,
);
+ component.$destroy();
});
it('should render empty state', (done) => {
- const component = new gl.commits.pipelines.PipelinesTableView({
- el: document.querySelector('#commit-pipeline-table-view'),
- });
-
setTimeout(() => {
expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain('No pipelines to show');
done();
diff --git a/spec/javascripts/environments/environment_spec.js b/spec/javascripts/environments/environment_spec.js
index edd0cad32d0..eb8e5e50434 100644
--- a/spec/javascripts/environments/environment_spec.js
+++ b/spec/javascripts/environments/environment_spec.js
@@ -91,6 +91,10 @@ describe('Environment', () => {
});
describe('pagination', () => {
+ afterEach(() => {
+ window.history.pushState({}, null, '');
+ });
+
it('should render pagination', (done) => {
setTimeout(() => {
expect(
diff --git a/spec/javascripts/fixtures/merge_requests.rb b/spec/javascripts/fixtures/merge_requests.rb
index ee893b76c84..fddeaaf504d 100644
--- a/spec/javascripts/fixtures/merge_requests.rb
+++ b/spec/javascripts/fixtures/merge_requests.rb
@@ -6,6 +6,15 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont
let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, namespace: namespace, path: 'merge-requests-project') }
+ let(:merge_request) { create(:merge_request, :with_diffs, source_project: project, target_project: project, description: '- [ ] Task List Item') }
+ let(:pipeline) do
+ create(
+ :ci_pipeline,
+ project: merge_request.source_project,
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha
+ )
+ end
render_views
@@ -18,7 +27,8 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont
end
it 'merge_requests/merge_request_with_task_list.html.raw' do |example|
- merge_request = create(:merge_request, :with_diffs, source_project: project, target_project: project, description: '- [ ] Task List Item')
+ create(:ci_build, :pending, pipeline: pipeline)
+
render_merge_request(example.description, merge_request)
end
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index f4d3e77e515..7c7b334c5d8 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -46,6 +46,10 @@ require('~/lib/utils/common_utils');
spyOn(window.document, 'getElementById').and.callThrough();
});
+ afterEach(() => {
+ window.history.pushState({}, null, '');
+ });
+
function expectGetElementIdToHaveBeenCalledWith(elementId) {
expect(window.document.getElementById).toHaveBeenCalledWith(elementId);
}
@@ -75,11 +79,56 @@ require('~/lib/utils/common_utils');
});
});
+ describe('gl.utils.setParamInURL', () => {
+ afterEach(() => {
+ window.history.pushState({}, null, '');
+ });
+
+ it('should return the parameter', () => {
+ window.history.replaceState({}, null, '');
+
+ expect(gl.utils.setParamInURL('page', 156)).toBe('?page=156');
+ expect(gl.utils.setParamInURL('page', '156')).toBe('?page=156');
+ });
+
+ it('should update the existing parameter when its a number', () => {
+ window.history.pushState({}, null, '?page=15');
+
+ expect(gl.utils.setParamInURL('page', 16)).toBe('?page=16');
+ expect(gl.utils.setParamInURL('page', '16')).toBe('?page=16');
+ expect(gl.utils.setParamInURL('page', true)).toBe('?page=true');
+ });
+
+ it('should update the existing parameter when its a string', () => {
+ window.history.pushState({}, null, '?scope=all');
+
+ expect(gl.utils.setParamInURL('scope', 'finished')).toBe('?scope=finished');
+ });
+
+ it('should update the existing parameter when more than one parameter exists', () => {
+ window.history.pushState({}, null, '?scope=all&page=15');
+
+ expect(gl.utils.setParamInURL('scope', 'finished')).toBe('?scope=finished&page=15');
+ });
+
+ it('should add a new parameter to the end of the existing ones', () => {
+ window.history.pushState({}, null, '?scope=all');
+
+ expect(gl.utils.setParamInURL('page', 16)).toBe('?scope=all&page=16');
+ expect(gl.utils.setParamInURL('page', '16')).toBe('?scope=all&page=16');
+ expect(gl.utils.setParamInURL('page', true)).toBe('?scope=all&page=true');
+ });
+ });
+
describe('gl.utils.getParameterByName', () => {
beforeEach(() => {
window.history.pushState({}, null, '?scope=all&p=2');
});
+ afterEach(() => {
+ window.history.replaceState({}, null, null);
+ });
+
it('should return valid parameter', () => {
const value = gl.utils.getParameterByName('scope');
expect(value).toBe('all');
diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js
index 7506e6ab49e..7b9632be84e 100644
--- a/spec/javascripts/merge_request_tabs_spec.js
+++ b/spec/javascripts/merge_request_tabs_spec.js
@@ -38,6 +38,10 @@ require('vendor/jquery.scrollTo');
}
});
+ afterEach(function () {
+ this.class.destroy();
+ });
+
describe('#activateTab', function () {
beforeEach(function () {
spyOn($, 'ajax').and.callFake(function () {});
@@ -200,6 +204,42 @@ require('vendor/jquery.scrollTo');
expect(this.subject('show')).toBe('/foo/bar/merge_requests/1');
});
});
+
+ describe('#tabShown', () => {
+ beforeEach(function () {
+ loadFixtures('merge_requests/merge_request_with_task_list.html.raw');
+ });
+
+ describe('with "Side-by-side"/parallel diff view', () => {
+ beforeEach(function () {
+ this.class.diffViewType = () => 'parallel';
+ });
+
+ it('maintains `container-limited` for pipelines tab', function (done) {
+ const asyncClick = function (selector) {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ document.querySelector(selector).click();
+ resolve();
+ });
+ });
+ };
+
+ asyncClick('.merge-request-tabs .pipelines-tab a')
+ .then(() => asyncClick('.merge-request-tabs .diffs-tab a'))
+ .then(() => asyncClick('.merge-request-tabs .pipelines-tab a'))
+ .then(() => {
+ const hasContainerLimitedClass = document.querySelector('.content-wrapper .container-fluid').classList.contains('container-limited');
+ expect(hasContainerLimitedClass).toBe(true);
+ })
+ .then(done)
+ .catch((err) => {
+ done.fail(`Something went wrong clicking MR tabs: ${err.message}\n${err.stack}`);
+ });
+ });
+ });
+ });
+
describe('#loadDiff', function () {
it('requires an absolute pathname', function () {
spyOn($, 'ajax').and.callFake(function (options) {
diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js
index 4ac7e911740..c5c30fdda90 100644
--- a/spec/javascripts/right_sidebar_spec.js
+++ b/spec/javascripts/right_sidebar_spec.js
@@ -78,5 +78,11 @@ require('~/extensions/jquery.js');
expect(todoToggleSpy.calls.count()).toEqual(1);
});
+
+ it('should not hide collapsed icons', () => {
+ [].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => {
+ expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy();
+ });
+ });
});
}).call(window);
diff --git a/spec/javascripts/vue_shared/components/pipelines_table_spec.js b/spec/javascripts/vue_shared/components/pipelines_table_spec.js
index 54d81e2ea7d..905ac0ec723 100644
--- a/spec/javascripts/vue_shared/components/pipelines_table_spec.js
+++ b/spec/javascripts/vue_shared/components/pipelines_table_spec.js
@@ -1,6 +1,9 @@
-require('~/vue_shared/components/pipelines_table');
require('~/lib/utils/datetime_utility');
+const Vue = require('vue');
const pipeline = require('../../commit/pipelines/mock_data');
+const PipelinesTable = require('~/vue_shared/components/pipelines_table');
+
+const PipelinesTableComponent = Vue.extend(PipelinesTable);
describe('Pipelines Table', () => {
preloadFixtures('static/environments/element.html.raw');
@@ -12,7 +15,7 @@ describe('Pipelines Table', () => {
describe('table', () => {
let component;
beforeEach(() => {
- component = new gl.pipelines.PipelinesTableComponent({
+ component = new PipelinesTableComponent({
el: document.querySelector('.test-dom-element'),
propsData: {
pipelines: [],
@@ -21,6 +24,10 @@ describe('Pipelines Table', () => {
});
});
+ afterEach(() => {
+ component.$destroy();
+ });
+
it('should render a table', () => {
expect(component.$el).toEqual('TABLE');
});
@@ -37,7 +44,7 @@ describe('Pipelines Table', () => {
describe('without data', () => {
it('should render an empty table', () => {
- const component = new gl.pipelines.PipelinesTableComponent({
+ const component = new PipelinesTableComponent({
el: document.querySelector('.test-dom-element'),
propsData: {
pipelines: [],
@@ -50,7 +57,7 @@ describe('Pipelines Table', () => {
describe('with data', () => {
it('should render rows', () => {
- const component = new gl.pipelines.PipelinesTableComponent({
+ const component = new PipelinesTableComponent({
el: document.querySelector('.test-dom-element'),
propsData: {
pipelines: [pipeline],
diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js b/spec/javascripts/vue_shared/components/table_pagination_spec.js
index 9cb067921a7..1e3e60f249a 100644
--- a/spec/javascripts/vue_shared/components/table_pagination_spec.js
+++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js
@@ -136,6 +136,10 @@ describe('Pagination component', () => {
});
describe('paramHelper', () => {
+ afterEach(() => {
+ window.history.pushState({}, null, '');
+ });
+
it('can parse url parameters correctly', () => {
window.history.pushState({}, null, '?scope=all&p=2');
diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb
new file mode 100644
index 00000000000..897288b8ad5
--- /dev/null
+++ b/spec/lib/banzai/filter/markdown_filter_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe Banzai::Filter::MarkdownFilter, lib: true do
+ include FilterSpecHelper
+
+ context 'code block' do
+ it 'adds language to lang attribute when specified' do
+ result = filter("```html\nsome code\n```")
+
+ expect(result).to start_with("\n<pre><code lang=\"html\">")
+ end
+
+ it 'does not add language to lang attribute when not specified' do
+ result = filter("```\nsome code\n```")
+
+ expect(result).to start_with("\n<pre><code>")
+ end
+ end
+end
diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb
index b4cd5f63a15..fdbc65b5e00 100644
--- a/spec/lib/banzai/filter/sanitization_filter_spec.rb
+++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb
@@ -49,11 +49,12 @@ describe Banzai::Filter::SanitizationFilter, lib: true do
instance = described_class.new('Foo')
3.times { instance.whitelist }
- expect(instance.whitelist[:transformers].size).to eq 5
+ expect(instance.whitelist[:transformers].size).to eq 4
end
- it 'allows syntax highlighting' do
- exp = act = %q{<pre class="code highlight white c"><code><span class="k">def</span></code></pre>}
+ it 'sanitizes `class` attribute from all elements' do
+ act = %q{<pre class="code highlight white c"><code>&lt;span class="k"&gt;def&lt;/span&gt;</code></pre>}
+ exp = %q{<pre><code>&lt;span class="k"&gt;def&lt;/span&gt;</code></pre>}
expect(filter(act).to_html).to eq exp
end
diff --git a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
index 69e3c52b35a..bfd11941867 100644
--- a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
+++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
@@ -12,14 +12,14 @@ describe Banzai::Filter::SyntaxHighlightFilter, lib: true do
context "when a valid language is specified" do
it "highlights as that language" do
- result = filter('<pre><code class="ruby">def fun end</code></pre>')
+ result = filter('<pre><code lang="ruby">def fun end</code></pre>')
expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight ruby" lang="ruby" v-pre="true"><code><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></code></pre>')
end
end
context "when an invalid language is specified" do
it "highlights as plaintext" do
- result = filter('<pre><code class="gnuplot">This is a test</code></pre>')
+ result = filter('<pre><code lang="gnuplot">This is a test</code></pre>')
expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext" lang="plaintext" v-pre="true"><code>This is a test</code></pre>')
end
end
@@ -30,7 +30,7 @@ describe Banzai::Filter::SyntaxHighlightFilter, lib: true do
end
it "highlights as plaintext" do
- result = filter('<pre><code class="ruby">This is a test</code></pre>')
+ result = filter('<pre><code lang="ruby">This is a test</code></pre>')
expect(result.to_html).to eq('<pre class="code highlight" lang="" v-pre="true"><code>This is a test</code></pre>')
end
end
diff --git a/spec/lib/gitlab/github_import/importer_spec.rb b/spec/lib/gitlab/github_import/importer_spec.rb
index 33d83d6d2f1..c131f325c0c 100644
--- a/spec/lib/gitlab/github_import/importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer_spec.rb
@@ -130,9 +130,9 @@ describe Gitlab::GithubImport::Importer, lib: true do
let!(:user) { create(:user, email: octocat.email) }
let(:repository) { double(id: 1, fork: false) }
let(:source_sha) { create(:commit, project: project).id }
- let(:source_branch) { double(ref: 'feature', repo: repository, sha: source_sha) }
+ let(:source_branch) { double(ref: 'feature', repo: repository, sha: source_sha, user: octocat) }
let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id }
- let(:target_branch) { double(ref: 'master', repo: repository, sha: target_sha) }
+ let(:target_branch) { double(ref: 'master', repo: repository, sha: target_sha, user: octocat) }
let(:pull_request) do
double(
number: 1347,
diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
index e46be18aa99..736e3bcad63 100644
--- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
@@ -4,13 +4,18 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
let(:client) { double }
let(:project) { create(:project, :repository) }
let(:source_sha) { create(:commit, project: project).id }
- let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id }
+ let(:target_commit) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit) }
+ let(:target_sha) { target_commit.id }
+ let(:target_short_sha) { target_commit.id.to_s[0..7] }
let(:repository) { double(id: 1, fork: false) }
let(:source_repo) { repository }
let(:source_branch) { double(ref: 'feature', repo: source_repo, sha: source_sha) }
+ let(:forked_source_repo) { double(id: 2, fork: true, name: 'otherproject', full_name: 'company/otherproject') }
let(:target_repo) { repository }
- let(:target_branch) { double(ref: 'master', repo: target_repo, sha: target_sha) }
- let(:removed_branch) { double(ref: 'removed-branch', repo: source_repo, sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b') }
+ let(:target_branch) { double(ref: 'master', repo: target_repo, sha: target_sha, user: octocat) }
+ let(:removed_branch) { double(ref: 'removed-branch', repo: source_repo, sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b', user: octocat) }
+ let(:forked_branch) { double(ref: 'master', repo: forked_source_repo, sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b', user: octocat) }
+ let(:branch_deleted_repo) { double(ref: 'master', repo: nil, sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b', user: octocat) }
let(:octocat) { double(id: 123456, login: 'octocat', email: 'octocat@example.com') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
@@ -201,8 +206,24 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
context 'when source branch does not exist' do
let(:raw_data) { double(base_data.merge(head: removed_branch)) }
- it 'prefixes branch name with pull request number' do
- expect(pull_request.source_branch_name).to eq 'pull/1347/removed-branch'
+ it 'prefixes branch name with gh-:short_sha/:number/:user pattern to avoid collision' do
+ expect(pull_request.source_branch_name).to eq "gh-#{target_short_sha}/1347/octocat/removed-branch"
+ end
+ end
+
+ context 'when source branch is from a fork' do
+ let(:raw_data) { double(base_data.merge(head: forked_branch)) }
+
+ it 'prefixes branch name with gh-:short_sha/:number/:user pattern to avoid collision' do
+ expect(pull_request.source_branch_name).to eq "gh-#{target_short_sha}/1347/octocat/master"
+ end
+ end
+
+ context 'when source branch is from a deleted fork' do
+ let(:raw_data) { double(base_data.merge(head: branch_deleted_repo)) }
+
+ it 'prefixes branch name with gh-:short_sha/:number/:user pattern to avoid collision' do
+ expect(pull_request.source_branch_name).to eq "gh-#{target_short_sha}/1347/octocat/master"
end
end
end
@@ -219,8 +240,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
context 'when target branch does not exist' do
let(:raw_data) { double(base_data.merge(base: removed_branch)) }
- it 'prefixes branch name with pull request number' do
- expect(pull_request.target_branch_name).to eq 'pull/1347/removed-branch'
+ it 'prefixes branch name with gh-:short_sha/:number/:user pattern to avoid collision' do
+ expect(pull_request.target_branch_name).to eq 'gl-2e5d3239/1347/octocat/removed-branch'
end
end
end
@@ -271,6 +292,40 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end
end
+ describe '#cross_project?' do
+ context 'when source and target repositories are different' do
+ let(:raw_data) { double(base_data.merge(head: forked_branch)) }
+
+ it 'returns true' do
+ expect(pull_request.cross_project?).to eq true
+ end
+ end
+
+ context 'when source repository does not exist anymore' do
+ let(:raw_data) { double(base_data.merge(head: branch_deleted_repo)) }
+
+ it 'returns true' do
+ expect(pull_request.cross_project?).to eq true
+ end
+ end
+
+ context 'when source and target repositories are the same' do
+ let(:raw_data) { double(base_data.merge(head: source_branch)) }
+
+ it 'returns false' do
+ expect(pull_request.cross_project?).to eq false
+ end
+ end
+ end
+
+ describe '#source_branch_exists?' do
+ let(:raw_data) { double(base_data.merge(head: forked_branch)) }
+
+ it 'returns false when is a cross_project' do
+ expect(pull_request.source_branch_exists?).to eq false
+ end
+ end
+
describe '#url' do
let(:raw_data) { double(base_data) }
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 9aba1d75612..61d965e8974 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -623,64 +623,6 @@ describe API::MergeRequests, api: true do
end
end
- describe "POST /projects/:id/merge_requests/:merge_request_iid/comments" do
- it "returns comment" do
- original_count = merge_request.notes.size
-
- post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/comments", user), note: "My comment"
-
- expect(response).to have_http_status(201)
- expect(json_response['note']).to eq('My comment')
- expect(json_response['author']['name']).to eq(user.name)
- expect(json_response['author']['username']).to eq(user.username)
- expect(merge_request.reload.notes.size).to eq(original_count + 1)
- end
-
- it "returns 400 if note is missing" do
- post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/comments", user)
- expect(response).to have_http_status(400)
- end
-
- it "returns 404 if merge request iid is invalid" do
- post api("/projects/#{project.id}/merge_requests/404/comments", user),
- note: 'My comment'
- expect(response).to have_http_status(404)
- end
-
- it "returns 404 if merge request id is used instead of iid" do
- post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user),
- note: 'My comment'
- expect(response).to have_http_status(404)
- end
- end
-
- describe "GET :id/merge_requests/:merge_request_iid/comments" do
- let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
- let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") }
-
- it "returns merge_request comments ordered by created_at" do
- get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/comments", user)
-
- expect(response).to have_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response.length).to eq(2)
- expect(json_response.first['note']).to eq("a comment on a MR")
- expect(json_response.first['author']['id']).to eq(user.id)
- expect(json_response.last['note']).to eq("another comment on a MR")
- end
-
- it "returns a 404 error if merge_request_iid is invalid" do
- get api("/projects/#{project.id}/merge_requests/999/comments", user)
- expect(response).to have_http_status(404)
- end
-
- it "returns a 404 error if merge_request id is used instead of iid" do
- get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user)
- expect(response).to have_http_status(404)
- end
- end
-
describe 'GET :id/merge_requests/:merge_request_iid/closes_issues' do
it 'returns the issue that will be closed on merge' do
issue = create(:issue, project: project)
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 347f8f6fa3b..d8eb8ce921e 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -34,7 +34,7 @@ describe API::Notes, api: true do
describe "GET /projects/:id/noteable/:noteable_id/notes" do
context "when noteable is an Issue" do
it "returns an array of issue notes" do
- get api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
+ get api("/projects/#{project.id}/issues/#{issue.iid}/notes", user)
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
@@ -50,7 +50,7 @@ describe API::Notes, api: true do
context "and current user cannot view the notes" do
it "returns an empty array" do
- get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user)
+ get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", user)
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
@@ -62,7 +62,7 @@ describe API::Notes, api: true do
before { ext_issue.update_attributes(confidential: true) }
it "returns 404" do
- get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user)
+ get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", user)
expect(response).to have_http_status(404)
end
@@ -70,7 +70,7 @@ describe API::Notes, api: true do
context "and current user can view the note" do
it "returns an empty array" do
- get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", private_user)
+ get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", private_user)
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
@@ -106,7 +106,7 @@ describe API::Notes, api: true do
context "when noteable is a Merge Request" do
it "returns an array of merge_requests notes" do
- get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes", user)
+ get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/notes", user)
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
@@ -131,21 +131,21 @@ describe API::Notes, api: true do
describe "GET /projects/:id/noteable/:noteable_id/notes/:note_id" do
context "when noteable is an Issue" do
it "returns an issue note by id" do
- get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", user)
+ get api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{issue_note.id}", user)
expect(response).to have_http_status(200)
expect(json_response['body']).to eq(issue_note.note)
end
it "returns a 404 error if issue note not found" do
- get api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user)
+ get api("/projects/#{project.id}/issues/#{issue.iid}/notes/12345", user)
expect(response).to have_http_status(404)
end
context "and current user cannot view the note" do
it "returns a 404 error" do
- get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", user)
+ get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes/#{cross_reference_note.id}", user)
expect(response).to have_http_status(404)
end
@@ -154,7 +154,7 @@ describe API::Notes, api: true do
before { issue.update_attributes(confidential: true) }
it "returns 404" do
- get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", private_user)
+ get api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{issue_note.id}", private_user)
expect(response).to have_http_status(404)
end
@@ -162,7 +162,7 @@ describe API::Notes, api: true do
context "and current user can view the note" do
it "returns an issue note by id" do
- get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", private_user)
+ get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes/#{cross_reference_note.id}", private_user)
expect(response).to have_http_status(200)
expect(json_response['body']).to eq(cross_reference_note.note)
@@ -190,7 +190,7 @@ describe API::Notes, api: true do
describe "POST /projects/:id/noteable/:noteable_id/notes" do
context "when noteable is an Issue" do
it "creates a new issue note" do
- post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!'
+ post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user), body: 'hi!'
expect(response).to have_http_status(201)
expect(json_response['body']).to eq('hi!')
@@ -198,13 +198,13 @@ describe API::Notes, api: true do
end
it "returns a 400 bad request error if body not given" do
- post api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
+ post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user)
expect(response).to have_http_status(400)
end
it "returns a 401 unauthorized error if user not authenticated" do
- post api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!'
+ post api("/projects/#{project.id}/issues/#{issue.iid}/notes"), body: 'hi!'
expect(response).to have_http_status(401)
end
@@ -212,7 +212,7 @@ describe API::Notes, api: true do
context 'when an admin or owner makes the request' do
it 'accepts the creation date to be set' do
creation_time = 2.weeks.ago
- post api("/projects/#{project.id}/issues/#{issue.id}/notes", user),
+ post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user),
body: 'hi!', created_at: creation_time
expect(response).to have_http_status(201)
@@ -226,7 +226,7 @@ describe API::Notes, api: true do
let(:issue2) { create(:issue, project: project) }
it 'creates a new issue note' do
- post api("/projects/#{project.id}/issues/#{issue2.id}/notes", user), body: ':+1:'
+ post api("/projects/#{project.id}/issues/#{issue2.iid}/notes", user), body: ':+1:'
expect(response).to have_http_status(201)
expect(json_response['body']).to eq(':+1:')
@@ -235,7 +235,7 @@ describe API::Notes, api: true do
context 'when the user is posting an award emoji on his/her own issue' do
it 'creates a new issue note' do
- post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: ':+1:'
+ post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user), body: ':+1:'
expect(response).to have_http_status(201)
expect(json_response['body']).to eq(':+1:')
@@ -270,7 +270,7 @@ describe API::Notes, api: true do
project = create(:empty_project, :private) { |p| p.add_guest(user) }
issue = create(:issue, :confidential, project: project)
- post api("/projects/#{project.id}/issues/#{issue.id}/notes", user),
+ post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user),
body: 'Foo'
expect(response).to have_http_status(404)
@@ -285,7 +285,7 @@ describe API::Notes, api: true do
# from a different project, see #15577
#
before do
- post api("/projects/#{project.id}/issues/#{private_issue.id}/notes", user),
+ post api("/projects/#{private_issue.project.id}/issues/#{private_issue.iid}/notes", user),
body: 'Hi!'
end
@@ -303,14 +303,14 @@ describe API::Notes, api: true do
it "creates an activity event when an issue note is created" do
expect(Event).to receive(:create)
- post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!'
+ post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user), body: 'hi!'
end
end
describe 'PUT /projects/:id/noteable/:noteable_id/notes/:note_id' do
context 'when noteable is an Issue' do
it 'returns modified note' do
- put api("/projects/#{project.id}/issues/#{issue.id}/"\
+ put api("/projects/#{project.id}/issues/#{issue.iid}/"\
"notes/#{issue_note.id}", user), body: 'Hello!'
expect(response).to have_http_status(200)
@@ -318,14 +318,14 @@ describe API::Notes, api: true do
end
it 'returns a 404 error when note id not found' do
- put api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user),
+ put api("/projects/#{project.id}/issues/#{issue.iid}/notes/12345", user),
body: 'Hello!'
expect(response).to have_http_status(404)
end
it 'returns a 400 bad request error if body not given' do
- put api("/projects/#{project.id}/issues/#{issue.id}/"\
+ put api("/projects/#{project.id}/issues/#{issue.iid}/"\
"notes/#{issue_note.id}", user)
expect(response).to have_http_status(400)
@@ -351,7 +351,7 @@ describe API::Notes, api: true do
context 'when noteable is a Merge Request' do
it 'returns modified note' do
- put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/"\
"notes/#{merge_request_note.id}", user), body: 'Hello!'
expect(response).to have_http_status(200)
@@ -359,7 +359,7 @@ describe API::Notes, api: true do
end
it 'returns a 404 error when note id not found' do
- put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/"\
"notes/12345", user), body: "Hello!"
expect(response).to have_http_status(404)
@@ -370,18 +370,18 @@ describe API::Notes, api: true do
describe 'DELETE /projects/:id/noteable/:noteable_id/notes/:note_id' do
context 'when noteable is an Issue' do
it 'deletes a note' do
- delete api("/projects/#{project.id}/issues/#{issue.id}/"\
+ delete api("/projects/#{project.id}/issues/#{issue.iid}/"\
"notes/#{issue_note.id}", user)
expect(response).to have_http_status(204)
# Check if note is really deleted
- delete api("/projects/#{project.id}/issues/#{issue.id}/"\
+ delete api("/projects/#{project.id}/issues/#{issue.iid}/"\
"notes/#{issue_note.id}", user)
expect(response).to have_http_status(404)
end
it 'returns a 404 error when note id not found' do
- delete api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user)
+ delete api("/projects/#{project.id}/issues/#{issue.iid}/notes/12345", user)
expect(response).to have_http_status(404)
end
@@ -410,18 +410,18 @@ describe API::Notes, api: true do
context 'when noteable is a Merge Request' do
it 'deletes a note' do
delete api("/projects/#{project.id}/merge_requests/"\
- "#{merge_request.id}/notes/#{merge_request_note.id}", user)
+ "#{merge_request.iid}/notes/#{merge_request_note.id}", user)
expect(response).to have_http_status(204)
# Check if note is really deleted
delete api("/projects/#{project.id}/merge_requests/"\
- "#{merge_request.id}/notes/#{merge_request_note.id}", user)
+ "#{merge_request.iid}/notes/#{merge_request_note.id}", user)
expect(response).to have_http_status(404)
end
it 'returns a 404 error when note id not found' do
delete api("/projects/#{project.id}/merge_requests/"\
- "#{merge_request.id}/notes/12345", user)
+ "#{merge_request.iid}/notes/12345", user)
expect(response).to have_http_status(404)
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 04e7837fd7a..f793c0db2f3 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -676,7 +676,7 @@ describe API::Users, api: true do
before { admin }
it "deletes user" do
- delete api("/users/#{user.id}", admin)
+ Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) }
expect(response).to have_http_status(204)
expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound
@@ -684,23 +684,23 @@ describe API::Users, api: true do
end
it "does not delete for unauthenticated user" do
- delete api("/users/#{user.id}")
+ Sidekiq::Testing.inline! { delete api("/users/#{user.id}") }
expect(response).to have_http_status(401)
end
it "is not available for non admin users" do
- delete api("/users/#{user.id}", user)
+ Sidekiq::Testing.inline! { delete api("/users/#{user.id}", user) }
expect(response).to have_http_status(403)
end
it "returns 404 for non-existing user" do
- delete api("/users/999999", admin)
+ Sidekiq::Testing.inline! { delete api("/users/999999", admin) }
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
end
it "returns a 404 for invalid ID" do
- delete api("/users/ASDF", admin)
+ Sidekiq::Testing.inline! { delete api("/users/ASDF", admin) }
expect(response).to have_http_status(404)
end
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index adfa75a524f..f78b4928dc3 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -4,6 +4,8 @@ describe MergeRequests::BuildService, services: true do
include RepoHelpers
let(:project) { create(:project) }
+ let(:source_project) { nil }
+ let(:target_project) { nil }
let(:user) { create(:user) }
let(:issue_confidential) { false }
let(:issue) { create(:issue, project: project, title: 'A bug', confidential: issue_confidential) }
@@ -20,7 +22,9 @@ describe MergeRequests::BuildService, services: true do
MergeRequests::BuildService.new(project, user,
description: description,
source_branch: source_branch,
- target_branch: target_branch)
+ target_branch: target_branch,
+ source_project: source_project,
+ target_project: target_project)
end
before do
@@ -256,5 +260,41 @@ describe MergeRequests::BuildService, services: true do
)
end
end
+
+ context 'target_project is set and accessible by current_user' do
+ let(:target_project) { create(:project, :public, :repository)}
+ let(:commits) { Commit.decorate([commit_1], project) }
+
+ it 'sets target project correctly' do
+ expect(merge_request.target_project).to eq(target_project)
+ end
+ end
+
+ context 'target_project is set but not accessible by current_user' do
+ let(:target_project) { create(:project, :private, :repository)}
+ let(:commits) { Commit.decorate([commit_1], project) }
+
+ it 'sets target project correctly' do
+ expect(merge_request.target_project).to eq(project)
+ end
+ end
+
+ context 'source_project is set and accessible by current_user' do
+ let(:source_project) { create(:project, :public, :repository)}
+ let(:commits) { Commit.decorate([commit_1], project) }
+
+ it 'sets target project correctly' do
+ expect(merge_request.source_project).to eq(source_project)
+ end
+ end
+
+ context 'source_project is set but not accessible by current_user' do
+ let(:source_project) { create(:project, :private, :repository)}
+ let(:commits) { Commit.decorate([commit_1], project) }
+
+ it 'sets target project correctly' do
+ expect(merge_request.source_project).to eq(project)
+ end
+ end
end
end
diff --git a/spec/services/users/destroy_spec.rb b/spec/services/users/destroy_spec.rb
index 922e82445d0..e65109e6746 100644
--- a/spec/services/users/destroy_spec.rb
+++ b/spec/services/users/destroy_spec.rb
@@ -17,13 +17,28 @@ describe Users::DestroyService, services: true do
expect { Namespace.with_deleted.find(user.namespace.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
- it 'will delete the project in the near future' do
- expect_any_instance_of(Projects::DestroyService).to receive(:async_execute).once
+ it 'will delete the project' do
+ expect_any_instance_of(Projects::DestroyService).to receive(:execute).once
service.execute(user)
end
end
+ context 'projects in pending_delete' do
+ before do
+ project.pending_delete = true
+ project.save
+ end
+
+ it 'destroys a project in pending_delete' do
+ expect_any_instance_of(Projects::DestroyService).to receive(:execute).once
+
+ service.execute(user)
+
+ expect { Project.find(project.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
context "a deleted user's issues" do
let(:project) { create :project }
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 9da54cb886d..aad8ec38a9c 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -91,7 +91,7 @@ module TestEnv
tmp_test_path = Rails.root.join('tmp', 'tests', '**')
Dir[tmp_test_path].each do |entry|
- unless File.basename(entry) =~ /\Agitlab-(shell|test|test_bare|test-fork)\z/
+ unless File.basename(entry) =~ /\Agitlab-(shell|test|test_bare|test-fork|test-fork_bare)\z/
FileUtils.rm_rf(entry)
end
end