Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f781b0b693
commit
1902e25626
|
@ -9,6 +9,7 @@
|
|||
/scripts/
|
||||
/tmp/
|
||||
/vendor/
|
||||
jest.config.js
|
||||
jest.config.*.js
|
||||
karma.config.js
|
||||
webpack.config.js
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -51,6 +51,7 @@ document.addEventListener(
|
|||
ciLintPath: this.dataset.ciLintPath,
|
||||
resetCachePath: this.dataset.resetCachePath,
|
||||
projectId: this.dataset.projectId,
|
||||
params: JSON.parse(this.dataset.params),
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
/>
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
params: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
projectUsers: null,
|
||||
projectBranches: null,
|
||||
};
|
||||
},
|
||||
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,
|
||||
},
|
||||
];
|
||||
},
|
||||
paramsValue() {
|
||||
return map(this.params, (val, key) => ({
|
||||
type: key,
|
||||
value: { data: val, operator: '=' },
|
||||
}));
|
||||
},
|
||||
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;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
onSubmit(filters) {
|
||||
|
@ -85,6 +66,7 @@ export default {
|
|||
<gl-filtered-search
|
||||
:placeholder="__('Filter pipelines')"
|
||||
:available-tokens="tokens"
|
||||
:value="paramsValue"
|
||||
@submit="onSubmit"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Filter pipelines based on url query params
|
||||
merge_request: 32230
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Improve Add/Remove Issue Labels API
|
||||
merge_request: 31864
|
||||
author: Lee Tickett
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Rate limit project export by user
|
||||
merge_request: 31719
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix leaky constant issue in sidekiq middleware server metric spec
|
||||
merge_request: 32104
|
||||
author: Rajendra Kadam
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix leaky constant issue importer and cache headers spec
|
||||
merge_request: 32122
|
||||
author: Rajendra Kadam
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix leaky constant issue in factory spec
|
||||
merge_request: 32174
|
||||
author: Rajendra Kadam
|
||||
type: fixed
|
|
@ -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` |
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -24,6 +24,8 @@ module API
|
|||
:discussion_locked,
|
||||
:due_date,
|
||||
:labels,
|
||||
:add_labels,
|
||||
:remove_labels,
|
||||
:milestone_id,
|
||||
:state_event,
|
||||
:title
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) || {}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
exportable_project = create(:project)
|
||||
exportable_project.add_maintainer(user)
|
||||
|
||||
post action, params: { namespace_id: exportable_project.namespace, id: exportable_project }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
|
||||
post action, params: { namespace_id: project.namespace, id: 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)
|
||||
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
|
||||
|
|
|
@ -10,7 +10,7 @@ settings:
|
|||
- path
|
||||
import/resolver:
|
||||
jest:
|
||||
jestConfigFile: 'jest.config.unit.js'
|
||||
jestConfigFile: 'jest.config.js'
|
||||
globals:
|
||||
getJSONFixture: false
|
||||
loadFixtures: false
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -56,6 +56,7 @@ describe('Pipelines', () => {
|
|||
propsData: {
|
||||
store: new Store(),
|
||||
projectId: '21',
|
||||
params: {},
|
||||
...props,
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -4,13 +4,16 @@ require 'spec_helper'
|
|||
|
||||
describe Gitlab::Config::Entry::Factory do
|
||||
describe '#create!' do
|
||||
class Script < Gitlab::Config::Entry::Node
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
let(:entry) { Script }
|
||||
let(:factory) { described_class.new(entry) }
|
||||
|
|
|
@ -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' })
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::NoCacheHeaders do
|
||||
class NoCacheTester
|
||||
before do
|
||||
stub_const('NoCacheTester', Class.new)
|
||||
NoCacheTester.class_eval do
|
||||
include Gitlab::NoCacheHeaders
|
||||
end
|
||||
end
|
||||
|
||||
describe "#no_cache_headers" do
|
||||
subject { NoCacheTester.new }
|
||||
|
|
|
@ -164,9 +164,13 @@ describe Gitlab::SidekiqMiddleware::ServerMetrics do
|
|||
end
|
||||
|
||||
context "when workers are not attributed" do
|
||||
class TestNonAttributedWorker
|
||||
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: "") }
|
||||
|
||||
|
|
|
@ -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' }
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue