diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-20 21:09:09 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-20 21:09:09 +0000 |
commit | 1902e256266822bc94e1a69debd79fb256de2d79 (patch) | |
tree | 2a6a431d24bc7c293312cb84bdfad6a438fa4d80 | |
parent | f781b0b69368ea3181cf892305c60a22886c0d7e (diff) | |
download | gitlab-ce-1902e256266822bc94e1a69debd79fb256de2d79.tar.gz |
Add latest changes from gitlab-org/gitlab@master
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 |