summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-05-20 21:09:09 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-05-20 21:09:09 +0000
commit1902e256266822bc94e1a69debd79fb256de2d79 (patch)
tree2a6a431d24bc7c293312cb84bdfad6a438fa4d80
parentf781b0b69368ea3181cf892305c60a22886c0d7e (diff)
downloadgitlab-ce-1902e256266822bc94e1a69debd79fb256de2d79.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.eslintignore1
-rw-r--r--.rubocop.yml4
-rw-r--r--app/assets/javascripts/pages/projects/pipelines/index/index.js1
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines.vue14
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_filtered_search.vue42
-rw-r--r--app/assets/javascripts/pipelines/components/tokens/pipeline_branch_name_token.vue15
-rw-r--r--app/assets/javascripts/pipelines/components/tokens/pipeline_trigger_author_token.vue13
-rw-r--r--app/assets/javascripts/pipelines/constants.js1
-rw-r--r--app/controllers/projects_controller.rb7
-rw-r--r--app/views/projects/pipelines/index.html.haml1
-rw-r--r--changelogs/unreleased/filter-from-url-query-params-pipeline.yml5
-rw-r--r--changelogs/unreleased/improve_issue_labels_api.yml5
-rw-r--r--changelogs/unreleased/jh-rate_limit_project_export.yml5
-rw-r--r--changelogs/unreleased/leaky-constant-fix-28.yml5
-rw-r--r--changelogs/unreleased/leaky-constant-fix-32.yml5
-rw-r--r--changelogs/unreleased/leaky-constant-fix-38.yml5
-rw-r--r--doc/api/issues.md2
-rw-r--r--doc/user/project/code_owners.md17
-rw-r--r--jest.config.js (renamed from jest.config.unit.js)0
-rw-r--r--lib/api/helpers/issues_helpers.rb2
-rw-r--r--lib/api/issues.rb2
-rw-r--r--lib/api/project_export.rb2
-rw-r--r--package.json2
-rw-r--r--spec/controllers/projects_controller_spec.rb30
-rw-r--r--spec/frontend/.eslintrc.yml2
-rw-r--r--spec/frontend/pipelines/components/pipelines_filtered_search_spec.js59
-rw-r--r--spec/frontend/pipelines/pipelines_spec.js1
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js12
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js10
-rw-r--r--spec/lib/gitlab/config/entry/factory_spec.rb11
-rw-r--r--spec/lib/gitlab/jira_import/issues_importer_spec.rb4
-rw-r--r--spec/lib/gitlab/no_cache_headers_spec.rb7
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb8
-rw-r--r--spec/requests/api/issues/put_projects_issues_spec.rb29
-rw-r--r--spec/requests/api/project_export_spec.rb37
35 files changed, 254 insertions, 112 deletions
diff --git a/.eslintignore b/.eslintignore
index f364771e54f..c41556f6aae 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -9,6 +9,7 @@
/scripts/
/tmp/
/vendor/
+jest.config.js
jest.config.*.js
karma.config.js
webpack.config.js
diff --git a/.rubocop.yml b/.rubocop.yml
index 782a9629672..60808f72e24 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -354,7 +354,6 @@ RSpec/LeakyConstantDeclaration:
- 'spec/lib/gitlab/ci/config/entry/retry_spec.rb'
- 'spec/lib/gitlab/cluster/mixins/puma_cluster_spec.rb'
- 'spec/lib/gitlab/cluster/mixins/unicorn_http_server_spec.rb'
- - 'spec/lib/gitlab/config/entry/factory_spec.rb'
- 'spec/lib/gitlab/config/entry/simplifiable_spec.rb'
- 'spec/lib/gitlab/database/migration_helpers_spec.rb'
- 'spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb'
@@ -362,11 +361,8 @@ RSpec/LeakyConstantDeclaration:
- 'spec/lib/gitlab/git/diff_collection_spec.rb'
- 'spec/lib/gitlab/import_export/import_test_coverage_spec.rb'
- 'spec/lib/gitlab/import_export/project/relation_factory_spec.rb'
- - 'spec/lib/gitlab/jira_import/issues_importer_spec.rb'
- - 'spec/lib/gitlab/no_cache_headers_spec.rb'
- 'spec/lib/gitlab/path_regex_spec.rb'
- 'spec/lib/gitlab/quick_actions/dsl_spec.rb'
- - 'spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb'
- 'spec/lib/marginalia_spec.rb'
- 'spec/mailers/notify_spec.rb'
- 'spec/migrations/20191125114345_add_admin_mode_protected_path_spec.rb'
diff --git a/app/assets/javascripts/pages/projects/pipelines/index/index.js b/app/assets/javascripts/pages/projects/pipelines/index/index.js
index bbad3238ec4..2c37d7da4a7 100644
--- a/app/assets/javascripts/pages/projects/pipelines/index/index.js
+++ b/app/assets/javascripts/pages/projects/pipelines/index/index.js
@@ -51,6 +51,7 @@ document.addEventListener(
ciLintPath: this.dataset.ciLintPath,
resetCachePath: this.dataset.resetCachePath,
projectId: this.dataset.projectId,
+ params: JSON.parse(this.dataset.params),
},
});
},
diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue
index fc93635bdb5..2f0a59ef3fa 100644
--- a/app/assets/javascripts/pipelines/components/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines.vue
@@ -1,5 +1,5 @@
<script>
-import { isEqual } from 'lodash';
+import { isEqual, pickBy } from 'lodash';
import { __, sprintf, s__ } from '../../locale';
import createFlash from '../../flash';
import PipelinesService from '../services/pipelines_service';
@@ -10,7 +10,7 @@ import NavigationControls from './nav_controls.vue';
import { getParameterByName } from '../../lib/utils/common_utils';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
import PipelinesFilteredSearch from './pipelines_filtered_search.vue';
-import { ANY_TRIGGER_AUTHOR, RAW_TEXT_WARNING } from '../constants';
+import { ANY_TRIGGER_AUTHOR, RAW_TEXT_WARNING, SUPPORTED_FILTER_PARAMETERS } from '../constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
@@ -86,6 +86,10 @@ export default {
type: String,
required: true,
},
+ params: {
+ type: Object,
+ required: true,
+ },
},
data() {
return {
@@ -220,10 +224,15 @@ export default {
canFilterPipelines() {
return this.glFeatures.filterPipelinesSearch;
},
+ validatedParams() {
+ return pickBy(this.params, (val, key) => SUPPORTED_FILTER_PARAMETERS.includes(key) && val);
+ },
},
created() {
this.service = new PipelinesService(this.endpoint);
this.requestData = { page: this.page, scope: this.scope };
+
+ Object.assign(this.requestData, this.validatedParams);
},
methods: {
successCallback(resp) {
@@ -306,6 +315,7 @@ export default {
v-if="canFilterPipelines"
:pipelines="state.pipelines"
:project-id="projectId"
+ :params="validatedParams"
@filterPipelines="filterPipelines"
/>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_filtered_search.vue b/app/assets/javascripts/pipelines/components/pipelines_filtered_search.vue
index 8f9c3eb70a2..c557a010f2e 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_filtered_search.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_filtered_search.vue
@@ -3,9 +3,7 @@ import { GlFilteredSearch } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import PipelineTriggerAuthorToken from './tokens/pipeline_trigger_author_token.vue';
import PipelineBranchNameToken from './tokens/pipeline_branch_name_token.vue';
-import Api from '~/api';
-import createFlash from '~/flash';
-import { FETCH_AUTHOR_ERROR_MESSAGE, FETCH_BRANCH_ERROR_MESSAGE } from '../constants';
+import { map } from 'lodash';
export default {
components: {
@@ -20,12 +18,10 @@ export default {
type: String,
required: true,
},
- },
- data() {
- return {
- projectUsers: null,
- projectBranches: null,
- };
+ params: {
+ type: Object,
+ required: true,
+ },
},
computed: {
tokens() {
@@ -37,7 +33,6 @@ export default {
unique: true,
token: PipelineTriggerAuthorToken,
operators: [{ value: '=', description: __('is'), default: 'true' }],
- triggerAuthors: this.projectUsers,
projectId: this.projectId,
},
{
@@ -47,30 +42,16 @@ export default {
unique: true,
token: PipelineBranchNameToken,
operators: [{ value: '=', description: __('is'), default: 'true' }],
- branches: this.projectBranches,
projectId: this.projectId,
},
];
},
- },
- created() {
- Api.projectUsers(this.projectId)
- .then(users => {
- this.projectUsers = users;
- })
- .catch(err => {
- createFlash(FETCH_AUTHOR_ERROR_MESSAGE);
- throw err;
- });
-
- Api.branches(this.projectId)
- .then(({ data }) => {
- this.projectBranches = data.map(branch => branch.name);
- })
- .catch(err => {
- createFlash(FETCH_BRANCH_ERROR_MESSAGE);
- throw err;
- });
+ paramsValue() {
+ return map(this.params, (val, key) => ({
+ type: key,
+ value: { data: val, operator: '=' },
+ }));
+ },
},
methods: {
onSubmit(filters) {
@@ -85,6 +66,7 @@ export default {
<gl-filtered-search
:placeholder="__('Filter pipelines')"
:available-tokens="tokens"
+ :value="paramsValue"
@submit="onSubmit"
/>
</div>
diff --git a/app/assets/javascripts/pipelines/components/tokens/pipeline_branch_name_token.vue b/app/assets/javascripts/pipelines/components/tokens/pipeline_branch_name_token.vue
index a7a3f986255..da14bb2d308 100644
--- a/app/assets/javascripts/pipelines/components/tokens/pipeline_branch_name_token.vue
+++ b/app/assets/javascripts/pipelines/components/tokens/pipeline_branch_name_token.vue
@@ -23,15 +23,18 @@ export default {
},
data() {
return {
- branches: this.config.branches,
+ branches: null,
loading: true,
};
},
+ created() {
+ this.fetchBranches();
+ },
methods: {
- fetchBranchBySearchTerm(searchTerm) {
- Api.branches(this.config.projectId, searchTerm)
- .then(res => {
- this.branches = res.data.map(branch => branch.name);
+ fetchBranches(searchterm) {
+ Api.branches(this.config.projectId, searchterm)
+ .then(({ data }) => {
+ this.branches = data.map(branch => branch.name);
this.loading = false;
})
.catch(err => {
@@ -41,7 +44,7 @@ export default {
});
},
searchBranches: debounce(function debounceSearch({ data }) {
- this.fetchBranchBySearchTerm(data);
+ this.fetchBranches(data);
}, FILTER_PIPELINES_SEARCH_DELAY),
},
};
diff --git a/app/assets/javascripts/pipelines/components/tokens/pipeline_trigger_author_token.vue b/app/assets/javascripts/pipelines/components/tokens/pipeline_trigger_author_token.vue
index 83e3558e1a1..4062a3b11bb 100644
--- a/app/assets/javascripts/pipelines/components/tokens/pipeline_trigger_author_token.vue
+++ b/app/assets/javascripts/pipelines/components/tokens/pipeline_trigger_author_token.vue
@@ -36,7 +36,7 @@ export default {
},
data() {
return {
- users: this.config.triggerAuthors,
+ users: [],
loading: true,
};
},
@@ -50,11 +50,14 @@ export default {
});
},
},
+ created() {
+ this.fetchProjectUsers();
+ },
methods: {
- fetchAuthorBySearchTerm(searchTerm) {
+ fetchProjectUsers(searchTerm) {
Api.projectUsers(this.config.projectId, searchTerm)
- .then(res => {
- this.users = res;
+ .then(users => {
+ this.users = users;
this.loading = false;
})
.catch(err => {
@@ -64,7 +67,7 @@ export default {
});
},
searchAuthors: debounce(function debounceSearch({ data }) {
- this.fetchAuthorBySearchTerm(data);
+ this.fetchProjectUsers(data);
}, FILTER_PIPELINES_SEARCH_DELAY),
},
};
diff --git a/app/assets/javascripts/pipelines/constants.js b/app/assets/javascripts/pipelines/constants.js
index d694430830b..b8d4cdcaa21 100644
--- a/app/assets/javascripts/pipelines/constants.js
+++ b/app/assets/javascripts/pipelines/constants.js
@@ -5,6 +5,7 @@ export const PIPELINES_TABLE = 'PIPELINES_TABLE';
export const LAYOUT_CHANGE_DELAY = 300;
export const FILTER_PIPELINES_SEARCH_DELAY = 200;
export const ANY_TRIGGER_AUTHOR = 'Any';
+export const SUPPORTED_FILTER_PARAMETERS = ['username', 'ref'];
export const TestStatus = {
FAILED: 'failed',
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 2f86b945b06..ff292973546 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -483,11 +483,12 @@ class ProjectsController < Projects::ApplicationController
def export_rate_limit
prefixed_action = "project_#{params[:action]}".to_sym
- if rate_limiter.throttled?(prefixed_action, scope: [current_user, prefixed_action, @project])
+ project_scope = params[:action] == :download_export ? @project : nil
+
+ if rate_limiter.throttled?(prefixed_action, scope: [current_user, prefixed_action, project_scope].compact)
rate_limiter.log_request(request, "#{prefixed_action}_request_limit".to_sym, current_user)
- flash[:alert] = _('This endpoint has been requested too many times. Try again later.')
- redirect_to edit_project_path(@project)
+ render plain: _('This endpoint has been requested too many times. Try again later.'), status: :too_many_requests
end
end
diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml
index 64789c7c263..fa4a77a692a 100644
--- a/app/views/projects/pipelines/index.html.haml
+++ b/app/views/projects/pipelines/index.html.haml
@@ -4,6 +4,7 @@
#pipelines-list-vue{ data: { endpoint: project_pipelines_path(@project, format: :json),
project_id: @project.id,
+ params: params.to_json,
"help-page-path" => help_page_path('ci/quick_start/README'),
"help-auto-devops-path" => help_page_path('topics/autodevops/index.md'),
"empty-state-svg-path" => image_path('illustrations/pipelines_empty.svg'),
diff --git a/changelogs/unreleased/filter-from-url-query-params-pipeline.yml b/changelogs/unreleased/filter-from-url-query-params-pipeline.yml
new file mode 100644
index 00000000000..e1de53cb4ef
--- /dev/null
+++ b/changelogs/unreleased/filter-from-url-query-params-pipeline.yml
@@ -0,0 +1,5 @@
+---
+title: Filter pipelines based on url query params
+merge_request: 32230
+author:
+type: added
diff --git a/changelogs/unreleased/improve_issue_labels_api.yml b/changelogs/unreleased/improve_issue_labels_api.yml
new file mode 100644
index 00000000000..e57aba6ab59
--- /dev/null
+++ b/changelogs/unreleased/improve_issue_labels_api.yml
@@ -0,0 +1,5 @@
+---
+title: Improve Add/Remove Issue Labels API
+merge_request: 31864
+author: Lee Tickett
+type: added
diff --git a/changelogs/unreleased/jh-rate_limit_project_export.yml b/changelogs/unreleased/jh-rate_limit_project_export.yml
new file mode 100644
index 00000000000..743755b1c49
--- /dev/null
+++ b/changelogs/unreleased/jh-rate_limit_project_export.yml
@@ -0,0 +1,5 @@
+---
+title: Rate limit project export by user
+merge_request: 31719
+author:
+type: changed
diff --git a/changelogs/unreleased/leaky-constant-fix-28.yml b/changelogs/unreleased/leaky-constant-fix-28.yml
new file mode 100644
index 00000000000..d1a04a25caa
--- /dev/null
+++ b/changelogs/unreleased/leaky-constant-fix-28.yml
@@ -0,0 +1,5 @@
+---
+title: Fix leaky constant issue in sidekiq middleware server metric spec
+merge_request: 32104
+author: Rajendra Kadam
+type: fixed
diff --git a/changelogs/unreleased/leaky-constant-fix-32.yml b/changelogs/unreleased/leaky-constant-fix-32.yml
new file mode 100644
index 00000000000..a5c5674d5da
--- /dev/null
+++ b/changelogs/unreleased/leaky-constant-fix-32.yml
@@ -0,0 +1,5 @@
+---
+title: Fix leaky constant issue importer and cache headers spec
+merge_request: 32122
+author: Rajendra Kadam
+type: fixed
diff --git a/changelogs/unreleased/leaky-constant-fix-38.yml b/changelogs/unreleased/leaky-constant-fix-38.yml
new file mode 100644
index 00000000000..7ed7fc71da6
--- /dev/null
+++ b/changelogs/unreleased/leaky-constant-fix-38.yml
@@ -0,0 +1,5 @@
+---
+title: Fix leaky constant issue in factory spec
+merge_request: 32174
+author: Rajendra Kadam
+type: fixed
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 8e5882c4d4e..f408cc3b2d3 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -768,6 +768,8 @@ PUT /projects/:id/issues/:issue_iid
| `assignee_ids` | integer array | no | The ID of the user(s) to assign the issue to. Set to `0` or provide an empty value to unassign all assignees. |
| `milestone_id` | integer | no | The global ID of a milestone to assign the issue to. Set to `0` or provide an empty value to unassign a milestone.|
| `labels` | string | no | Comma-separated label names for an issue. Set to an empty string to unassign all labels. |
+| `add_labels` | string | no | Comma-separated label names to add to an issue. |
+| `remove_labels`| string | no | Comma-separated label names to remove from an issue. |
| `state_event` | string | no | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it |
| `updated_at` | string | no | Date time string, ISO 8601 formatted, for example `2016-03-11T03:45:40Z` (requires admin or project owner rights). Empty string or null values are not accepted.|
| `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, for example `2016-03-11` |
diff --git a/doc/user/project/code_owners.md b/doc/user/project/code_owners.md
index 45d9e8f04e0..40ea1833fa3 100644
--- a/doc/user/project/code_owners.md
+++ b/doc/user/project/code_owners.md
@@ -88,6 +88,23 @@ or more users or by the `@name` of one or more groups that should
be owners of the file. Groups must be added as [members of the project](members/index.md),
or they will be ignored.
+Starting in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/32432), you can now specify
+groups or subgroups from the project's group hierarchy as potential code owners.
+
+For example, consider the following hierarchy for a given project:
+
+```text
+group >> sub-group >> sub-subgroup >> myproject >> file.md
+```
+
+Any of the following groups would be eligible to be specified as code owners:
+
+- `@group`
+- `@group/sub-group`
+- `@group/sub-group/sub-subgroup`
+
+In addition, any groups that have been invited to the project using the **Settings > Members** tool will also be recognized as eligible code owners.
+
The order in which the paths are defined is significant: the last
pattern that matches a given path will be used to find the code
owners.
diff --git a/jest.config.unit.js b/jest.config.js
index 4627462c730..4627462c730 100644
--- a/jest.config.unit.js
+++ b/jest.config.js
diff --git a/lib/api/helpers/issues_helpers.rb b/lib/api/helpers/issues_helpers.rb
index e272b13f3ae..638b31cc7ba 100644
--- a/lib/api/helpers/issues_helpers.rb
+++ b/lib/api/helpers/issues_helpers.rb
@@ -24,6 +24,8 @@ module API
:discussion_locked,
:due_date,
:labels,
+ :add_labels,
+ :remove_labels,
:milestone_id,
:state_event,
:title
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index be50c3e0381..de2d0b01a64 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -67,6 +67,8 @@ module API
optional :assignee_id, type: Integer, desc: '[Deprecated] The ID of a user to assign issue'
optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue'
optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
+ optional :add_labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
+ optional :remove_labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
optional :due_date, type: String, desc: 'Date string in the format YEAR-MONTH-DAY'
optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential'
optional :discussion_locked, type: Boolean, desc: " Boolean parameter indicating if the issue's discussion is locked"
diff --git a/lib/api/project_export.rb b/lib/api/project_export.rb
index 9fd9d13a20c..61e394048e3 100644
--- a/lib/api/project_export.rb
+++ b/lib/api/project_export.rb
@@ -45,7 +45,7 @@ module API
end
end
post ':id/export' do
- check_rate_limit! :project_export, [current_user, :project_export, user_project]
+ check_rate_limit! :project_export, [current_user, :project_export]
project_export_params = declared_params(include_missing: false)
after_export_params = project_export_params.delete(:upload) || {}
diff --git a/package.json b/package.json
index 82976399171..e35a2eaeb71 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
"eslint-report": "eslint --max-warnings 0 --ext .js,.vue --format html --output-file ./eslint-report.html --no-inline-config .",
"file-coverage": "scripts/frontend/file_test_coverage.js",
"prejest": "yarn check-dependencies",
- "jest": "jest --config jest.config.unit.js",
+ "jest": "jest --config jest.config.js",
"jest-debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
"jest:integration": "jest --config jest.config.integration.js",
"jsdoc": "jsdoc -c config/jsdocs.config.js",
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 6c00dad8bb7..4f8100d7bae 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -1159,17 +1159,18 @@ describe ProjectsController do
end
shared_examples 'rate limits project export endpoint' do
- before do
- allow(::Gitlab::ApplicationRateLimiter)
- .to receive(:throttled?)
- .and_return(true)
- end
-
it 'prevents requesting project export' do
- post action, params: { namespace_id: project.namespace, id: project }
+ exportable_project = create(:project)
+ exportable_project.add_maintainer(user)
+
+ post action, params: { namespace_id: exportable_project.namespace, id: exportable_project }
- expect(flash[:alert]).to eq('This endpoint has been requested too many times. Try again later.')
expect(response).to have_gitlab_http_status(:found)
+
+ post action, params: { namespace_id: project.namespace, id: project }
+
+ expect(response.body).to eq('This endpoint has been requested too many times. Try again later.')
+ expect(response).to have_gitlab_http_status(:too_many_requests)
end
end
@@ -1226,7 +1227,18 @@ describe ProjectsController do
end
context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_cache do
- include_examples 'rate limits project export endpoint'
+ before do
+ allow(::Gitlab::ApplicationRateLimiter)
+ .to receive(:throttled?)
+ .and_return(true)
+ end
+
+ it 'prevents requesting project export' do
+ post action, params: { namespace_id: project.namespace, id: project }
+
+ expect(response.body).to eq('This endpoint has been requested too many times. Try again later.')
+ expect(response).to have_gitlab_http_status(:too_many_requests)
+ end
end
end
end
diff --git a/spec/frontend/.eslintrc.yml b/spec/frontend/.eslintrc.yml
index b9159191114..8e6faa90c58 100644
--- a/spec/frontend/.eslintrc.yml
+++ b/spec/frontend/.eslintrc.yml
@@ -10,7 +10,7 @@ settings:
- path
import/resolver:
jest:
- jestConfigFile: 'jest.config.unit.js'
+ jestConfigFile: 'jest.config.js'
globals:
getJSONFixture: false
loadFixtures: false
diff --git a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js
index 12c6fab9c41..c213b99c97b 100644
--- a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js
+++ b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js
@@ -3,13 +3,7 @@ import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import PipelinesFilteredSearch from '~/pipelines/components/pipelines_filtered_search.vue';
-import {
- users,
- mockSearch,
- pipelineWithStages,
- branches,
- mockBranchesAfterMap,
-} from '../mock_data';
+import { users, mockSearch, pipelineWithStages, branches } from '../mock_data';
import { GlFilteredSearch } from '@gitlab/ui';
describe('Pipelines filtered search', () => {
@@ -22,11 +16,12 @@ describe('Pipelines filtered search', () => {
.props('availableTokens')
.find(token => token.type === type);
- const createComponent = () => {
+ const createComponent = (params = {}) => {
wrapper = mount(PipelinesFilteredSearch, {
propsData: {
pipelines: [pipelineWithStages],
projectId: '21',
+ params,
},
attachToDocument: true,
});
@@ -60,7 +55,6 @@ describe('Pipelines filtered search', () => {
icon: 'user',
title: 'Trigger author',
unique: true,
- triggerAuthors: users,
projectId: '21',
operators: [expect.objectContaining({ value: '=' })],
});
@@ -70,28 +64,49 @@ describe('Pipelines filtered search', () => {
icon: 'branch',
title: 'Branch name',
unique: true,
- branches: mockBranchesAfterMap,
projectId: '21',
operators: [expect.objectContaining({ value: '=' })],
});
});
- it('fetches and sets project users', () => {
- expect(Api.projectUsers).toHaveBeenCalled();
-
- expect(wrapper.vm.projectUsers).toEqual(users);
- });
-
- it('fetches and sets branches', () => {
- expect(Api.branches).toHaveBeenCalled();
-
- expect(wrapper.vm.projectBranches).toEqual(mockBranchesAfterMap);
- });
-
it('emits filterPipelines on submit with correct filter', () => {
findFilteredSearch().vm.$emit('submit', mockSearch);
expect(wrapper.emitted('filterPipelines')).toBeTruthy();
expect(wrapper.emitted('filterPipelines')[0]).toEqual([mockSearch]);
});
+
+ describe('Url query params', () => {
+ const params = {
+ username: 'deja.green',
+ ref: 'master',
+ };
+
+ beforeEach(() => {
+ createComponent(params);
+ });
+
+ it('sets default value if url query params', () => {
+ const expectedValueProp = [
+ {
+ type: 'username',
+ value: {
+ data: params.username,
+ operator: '=',
+ },
+ },
+ {
+ type: 'ref',
+ value: {
+ data: params.ref,
+ operator: '=',
+ },
+ },
+ { type: 'filtered-search-term', value: { data: '' } },
+ ];
+
+ expect(findFilteredSearch().props('value')).toEqual(expectedValueProp);
+ expect(findFilteredSearch().props('value')).toHaveLength(expectedValueProp.length);
+ });
+ });
});
diff --git a/spec/frontend/pipelines/pipelines_spec.js b/spec/frontend/pipelines/pipelines_spec.js
index 2ddd2116e2c..65ee578262e 100644
--- a/spec/frontend/pipelines/pipelines_spec.js
+++ b/spec/frontend/pipelines/pipelines_spec.js
@@ -56,6 +56,7 @@ describe('Pipelines', () => {
propsData: {
store: new Store(),
projectId: '21',
+ params: {},
...props,
},
methods: {
diff --git a/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js
index a6753600792..dc35feaf1ae 100644
--- a/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js
+++ b/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js
@@ -1,7 +1,8 @@
+import Api from '~/api';
import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import PipelineBranchNameToken from '~/pipelines/components/tokens/pipeline_branch_name_token.vue';
-import { branches } from '../mock_data';
+import { branches, mockBranchesAfterMap } from '../mock_data';
describe('Pipeline Branch Name Token', () => {
let wrapper;
@@ -46,6 +47,8 @@ describe('Pipeline Branch Name Token', () => {
};
beforeEach(() => {
+ jest.spyOn(Api, 'branches').mockResolvedValue({ data: branches });
+
createComponent();
});
@@ -58,6 +61,13 @@ describe('Pipeline Branch Name Token', () => {
expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config);
});
+ it('fetches and sets project branches', () => {
+ expect(Api.branches).toHaveBeenCalled();
+
+ expect(wrapper.vm.branches).toEqual(mockBranchesAfterMap);
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+
describe('displays loading icon correctly', () => {
it('shows loading icon', () => {
createComponent({ stubs }, { loading: true });
diff --git a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
index 00a9ff04e75..98de4f40c51 100644
--- a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
+++ b/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
@@ -1,3 +1,4 @@
+import Api from '~/api';
import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import PipelineTriggerAuthorToken from '~/pipelines/components/tokens/pipeline_trigger_author_token.vue';
@@ -45,6 +46,8 @@ describe('Pipeline Trigger Author Token', () => {
};
beforeEach(() => {
+ jest.spyOn(Api, 'projectUsers').mockResolvedValue(users);
+
createComponent();
});
@@ -57,6 +60,13 @@ describe('Pipeline Trigger Author Token', () => {
expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config);
});
+ it('fetches and sets project users', () => {
+ expect(Api.projectUsers).toHaveBeenCalled();
+
+ expect(wrapper.vm.users).toEqual(users);
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+
describe('displays loading icon correctly', () => {
it('shows loading icon', () => {
createComponent({ stubs }, { loading: true });
diff --git a/spec/lib/gitlab/config/entry/factory_spec.rb b/spec/lib/gitlab/config/entry/factory_spec.rb
index a614ef56a78..81ca5f2cba1 100644
--- a/spec/lib/gitlab/config/entry/factory_spec.rb
+++ b/spec/lib/gitlab/config/entry/factory_spec.rb
@@ -4,11 +4,14 @@ require 'spec_helper'
describe Gitlab::Config::Entry::Factory do
describe '#create!' do
- class Script < Gitlab::Config::Entry::Node
- include Gitlab::Config::Entry::Validatable
+ before do
+ stub_const('Script', Class.new(Gitlab::Config::Entry::Node))
+ Script.class_eval do
+ include Gitlab::Config::Entry::Validatable
- validations do
- validates :config, array_of_strings: true
+ validations do
+ validates :config, array_of_strings: true
+ end
end
end
diff --git a/spec/lib/gitlab/jira_import/issues_importer_spec.rb b/spec/lib/gitlab/jira_import/issues_importer_spec.rb
index 6cf06c20e19..ebe224bdc4b 100644
--- a/spec/lib/gitlab/jira_import/issues_importer_spec.rb
+++ b/spec/lib/gitlab/jira_import/issues_importer_spec.rb
@@ -39,8 +39,8 @@ describe Gitlab::JiraImport::IssuesImporter do
end
context 'with results returned' do
- JiraIssue = Struct.new(:id)
- let_it_be(:jira_issues) { [JiraIssue.new(1), JiraIssue.new(2)] }
+ jira_issue = Struct.new(:id)
+ let_it_be(:jira_issues) { [jira_issue.new(1), jira_issue.new(2)] }
def mock_issue_serializer(count)
serializer = instance_double(Gitlab::JiraImport::IssueSerializer, execute: { key: 'data' })
diff --git a/spec/lib/gitlab/no_cache_headers_spec.rb b/spec/lib/gitlab/no_cache_headers_spec.rb
index f011b55006e..c7a73f0e2dc 100644
--- a/spec/lib/gitlab/no_cache_headers_spec.rb
+++ b/spec/lib/gitlab/no_cache_headers_spec.rb
@@ -3,8 +3,11 @@
require 'spec_helper'
describe Gitlab::NoCacheHeaders do
- class NoCacheTester
- include Gitlab::NoCacheHeaders
+ before do
+ stub_const('NoCacheTester', Class.new)
+ NoCacheTester.class_eval do
+ include Gitlab::NoCacheHeaders
+ end
end
describe "#no_cache_headers" do
diff --git a/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb b/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
index da8d17b1272..7000a52bb5d 100644
--- a/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
@@ -164,9 +164,13 @@ describe Gitlab::SidekiqMiddleware::ServerMetrics do
end
context "when workers are not attributed" do
- class TestNonAttributedWorker
- include Sidekiq::Worker
+ before do
+ stub_const('TestNonAttributedWorker', Class.new)
+ TestNonAttributedWorker.class_eval do
+ include Sidekiq::Worker
+ end
end
+
let(:worker) { TestNonAttributedWorker.new }
let(:labels) { default_labels.merge(urgency: "") }
diff --git a/spec/requests/api/issues/put_projects_issues_spec.rb b/spec/requests/api/issues/put_projects_issues_spec.rb
index 2ab8b9d7877..db8932ab63a 100644
--- a/spec/requests/api/issues/put_projects_issues_spec.rb
+++ b/spec/requests/api/issues/put_projects_issues_spec.rb
@@ -301,6 +301,35 @@ describe API::Issues do
let!(:label) { create(:label, title: 'dummy', project: project) }
let!(:label_link) { create(:label_link, label: label, target: issue) }
+ it 'adds relevant labels' do
+ put api("/projects/#{project.id}/issues/#{issue.iid}", user),
+ params: { add_labels: '1, 2' }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['labels']).to contain_exactly(label.title, '1', '2')
+ end
+
+ context 'removes' do
+ let!(:label2) { create(:label, title: 'a-label', project: project) }
+ let!(:label_link2) { create(:label_link, label: label2, target: issue) }
+
+ it 'removes relevant labels' do
+ put api("/projects/#{project.id}/issues/#{issue.iid}", user),
+ params: { remove_labels: label2.title }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['labels']).to eq([label.title])
+ end
+
+ it 'removes all labels' do
+ put api("/projects/#{project.id}/issues/#{issue.iid}", user),
+ params: { remove_labels: "#{label.title}, #{label2.title}" }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['labels']).to be_empty
+ end
+ end
+
it 'does not update labels if not present' do
put api("/projects/#{project.id}/issues/#{issue.iid}", user),
params: { title: 'updated title' }
diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb
index ad872b88664..622f2f8a4c9 100644
--- a/spec/requests/api/project_export_spec.rb
+++ b/spec/requests/api/project_export_spec.rb
@@ -44,19 +44,6 @@ describe API::ProjectExport, :clean_gitlab_redis_cache do
it_behaves_like '404 response'
end
- shared_examples_for 'when rate limit is exceeded' do
- before do
- allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true)
- end
-
- it 'prevents requesting project export' do
- request
-
- expect(response).to have_gitlab_http_status(:too_many_requests)
- expect(json_response['message']['error']).to eq('This endpoint has been requested too many times. Try again later.')
- end
- end
-
describe 'GET /projects/:project_id/export' do
shared_examples_for 'get project export status not found' do
it_behaves_like '404 response' do
@@ -247,7 +234,16 @@ describe API::ProjectExport, :clean_gitlab_redis_cache do
context 'when rate limit is exceeded' do
let(:request) { get api(download_path, admin) }
- include_examples 'when rate limit is exceeded'
+ before do
+ allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true)
+ end
+
+ it 'prevents requesting project export' do
+ request
+
+ expect(response).to have_gitlab_http_status(:too_many_requests)
+ expect(json_response['message']['error']).to eq('This endpoint has been requested too many times. Try again later.')
+ end
end
end
@@ -360,10 +356,17 @@ describe API::ProjectExport, :clean_gitlab_redis_cache do
it_behaves_like 'post project export start'
- context 'when rate limit is exceeded' do
- let(:request) { post api(path, admin) }
+ context 'when rate limit is exceeded across projects' do
+ it 'prevents requesting project export' do
+ post api(path_none, admin)
- include_examples 'when rate limit is exceeded'
+ expect(response).not_to have_gitlab_http_status(:too_many_requests)
+
+ post api(path, admin)
+
+ expect(response).to have_gitlab_http_status(:too_many_requests)
+ expect(json_response['message']['error']).to eq('This endpoint has been requested too many times. Try again later.')
+ end
end
end