Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
6653aab95d
commit
24fe7aa2aa
6
Gemfile
6
Gemfile
|
@ -455,9 +455,9 @@ gem 'google-protobuf', '~> 3.8.0'
|
|||
gem 'toml-rb', '~> 1.0.0', require: false
|
||||
|
||||
# Feature toggles
|
||||
gem 'flipper', '~> 0.13.0'
|
||||
gem 'flipper-active_record', '~> 0.13.0'
|
||||
gem 'flipper-active_support_cache_store', '~> 0.13.0'
|
||||
gem 'flipper', '~> 0.17.1'
|
||||
gem 'flipper-active_record', '~> 0.17.1'
|
||||
gem 'flipper-active_support_cache_store', '~> 0.17.1'
|
||||
gem 'unleash', '~> 0.1.5'
|
||||
|
||||
# Structured logging
|
||||
|
|
20
Gemfile.lock
20
Gemfile.lock
|
@ -285,13 +285,13 @@ GEM
|
|||
fast_gettext (1.6.0)
|
||||
ffaker (2.10.0)
|
||||
ffi (1.11.1)
|
||||
flipper (0.13.0)
|
||||
flipper-active_record (0.13.0)
|
||||
activerecord (>= 3.2, < 6)
|
||||
flipper (~> 0.13.0)
|
||||
flipper-active_support_cache_store (0.13.0)
|
||||
activesupport (>= 3.2, < 6)
|
||||
flipper (~> 0.13.0)
|
||||
flipper (0.17.1)
|
||||
flipper-active_record (0.17.1)
|
||||
activerecord (>= 4.2, < 7)
|
||||
flipper (~> 0.17.1)
|
||||
flipper-active_support_cache_store (0.17.1)
|
||||
activesupport (>= 4.2, < 7)
|
||||
flipper (~> 0.17.1)
|
||||
flowdock (0.7.1)
|
||||
httparty (~> 0.7)
|
||||
multi_json
|
||||
|
@ -1149,9 +1149,9 @@ DEPENDENCIES
|
|||
faraday_middleware-aws-signers-v4
|
||||
fast_blank
|
||||
ffaker (~> 2.10)
|
||||
flipper (~> 0.13.0)
|
||||
flipper-active_record (~> 0.13.0)
|
||||
flipper-active_support_cache_store (~> 0.13.0)
|
||||
flipper (~> 0.17.1)
|
||||
flipper-active_record (~> 0.17.1)
|
||||
flipper-active_support_cache_store (~> 0.17.1)
|
||||
flowdock (~> 0.7)
|
||||
fog-aliyun (~> 0.3)
|
||||
fog-aws (~> 3.5)
|
||||
|
|
|
@ -76,6 +76,10 @@ class Project < ApplicationRecord
|
|||
|
||||
delegate :no_import?, to: :import_state, allow_nil: true
|
||||
|
||||
# TODO: remove once GitLab 12.5 is released
|
||||
# https://gitlab.com/gitlab-org/gitlab/issues/34638
|
||||
self.ignored_columns += %i[merge_requests_require_code_owner_approval]
|
||||
|
||||
default_value_for :archived, false
|
||||
default_value_for :resolve_outdated_diff_discussions, false
|
||||
default_value_for :container_registry_enabled, gitlab_config_features.container_registry
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Ignore deprecated column and remove references to it
|
||||
merge_request: 18911
|
||||
author:
|
||||
type: deprecated
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Apply correctly the limit of 10 designs per upload
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -47,6 +47,13 @@ A database **reviewer**'s role is to:
|
|||
reassign MR to the database **maintainer** suggested by Reviewer
|
||||
Roulette.
|
||||
|
||||
#### When there are no database maintainers available
|
||||
|
||||
Currently we have a [critical shortage of database maintainers](https://gitlab.com/gitlab-org/gitlab/issues/29717). Until we are able to increase the number of database maintainers to support the volume of reviews, we have implemented this temporary solution. If the database **reviewer** cannot find an available database **maintainer** then:
|
||||
|
||||
1. Assign the MR for a second review by a **database trainee maintainer** for further review.
|
||||
1. Once satisfied with the review process, and if the database **maintainer** is still not available, skip the database maintainer approval step and assign the merge request to a backend maintainer for final review and approval.
|
||||
|
||||
A database **maintainer**'s role is to:
|
||||
|
||||
- Perform the final database review on the MR.
|
||||
|
|
|
@ -37,8 +37,8 @@
|
|||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/plugin-syntax-import-meta": "^7.2.0",
|
||||
"@babel/preset-env": "^7.6.2",
|
||||
"@gitlab/svgs": "^1.78.0",
|
||||
"@gitlab/ui": "5.36.0",
|
||||
"@gitlab/svgs": "^1.79.0",
|
||||
"@gitlab/ui": "6.0.0",
|
||||
"@gitlab/visual-review-tools": "1.0.3",
|
||||
"apollo-cache-inmemory": "^1.5.1",
|
||||
"apollo-client": "^2.5.1",
|
||||
|
|
|
@ -1,26 +1,37 @@
|
|||
import Vue from 'vue';
|
||||
import paginationComp from '~/vue_shared/components/pagination/table_pagination.vue';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
|
||||
|
||||
describe('Pagination component', () => {
|
||||
let component;
|
||||
let PaginationComponent;
|
||||
let wrapper;
|
||||
let spy;
|
||||
let mountComponent;
|
||||
|
||||
const mountComponent = props => {
|
||||
wrapper = shallowMount(TablePagination, {
|
||||
sync: false,
|
||||
propsData: props,
|
||||
});
|
||||
};
|
||||
|
||||
const findFirstButtonLink = () => wrapper.find('.js-first-button .page-link');
|
||||
const findPreviousButton = () => wrapper.find('.js-previous-button');
|
||||
const findPreviousButtonLink = () => wrapper.find('.js-previous-button .page-link');
|
||||
const findNextButton = () => wrapper.find('.js-next-button');
|
||||
const findNextButtonLink = () => wrapper.find('.js-next-button .page-link');
|
||||
const findLastButtonLink = () => wrapper.find('.js-last-button .page-link');
|
||||
const findPages = () => wrapper.findAll('.page');
|
||||
const findSeparator = () => wrapper.find('.separator');
|
||||
|
||||
beforeEach(() => {
|
||||
spy = jasmine.createSpy('spy');
|
||||
PaginationComponent = Vue.extend(paginationComp);
|
||||
spy = jest.fn();
|
||||
});
|
||||
|
||||
mountComponent = function(props) {
|
||||
return new PaginationComponent({
|
||||
propsData: props,
|
||||
}).$mount();
|
||||
};
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('render', () => {
|
||||
it('should not render anything', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: NaN,
|
||||
page: 1,
|
||||
|
@ -32,12 +43,12 @@ describe('Pagination component', () => {
|
|||
change: spy,
|
||||
});
|
||||
|
||||
expect(component.$el.childNodes.length).toEqual(0);
|
||||
expect(wrapper.isEmpty()).toBe(true);
|
||||
});
|
||||
|
||||
describe('prev button', () => {
|
||||
it('should be disabled and non clickable', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: 2,
|
||||
page: 1,
|
||||
|
@ -49,17 +60,13 @@ describe('Pagination component', () => {
|
|||
change: spy,
|
||||
});
|
||||
|
||||
expect(
|
||||
component.$el.querySelector('.js-previous-button').classList.contains('disabled'),
|
||||
).toEqual(true);
|
||||
|
||||
component.$el.querySelector('.js-previous-button .page-link').click();
|
||||
|
||||
expect(findPreviousButton().classes()).toContain('disabled');
|
||||
findPreviousButtonLink().trigger('click');
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should be disabled and non clickable when total and totalPages are NaN', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: 2,
|
||||
page: 1,
|
||||
|
@ -70,18 +77,13 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
expect(
|
||||
component.$el.querySelector('.js-previous-button').classList.contains('disabled'),
|
||||
).toEqual(true);
|
||||
|
||||
component.$el.querySelector('.js-previous-button .page-link').click();
|
||||
|
||||
expect(findPreviousButton().classes()).toContain('disabled');
|
||||
findPreviousButtonLink().trigger('click');
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should be enabled and clickable', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: 3,
|
||||
page: 2,
|
||||
|
@ -92,14 +94,12 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
component.$el.querySelector('.js-previous-button .page-link').click();
|
||||
|
||||
findPreviousButtonLink().trigger('click');
|
||||
expect(spy).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it('should be enabled and clickable when total and totalPages are NaN', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: 3,
|
||||
page: 2,
|
||||
|
@ -110,16 +110,14 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
component.$el.querySelector('.js-previous-button .page-link').click();
|
||||
|
||||
findPreviousButtonLink().trigger('click');
|
||||
expect(spy).toHaveBeenCalledWith(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('first button', () => {
|
||||
it('should call the change callback with the first page', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: 3,
|
||||
page: 2,
|
||||
|
@ -130,18 +128,14 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
const button = component.$el.querySelector('.js-first-button .page-link');
|
||||
|
||||
expect(button.textContent.trim()).toEqual('« First');
|
||||
|
||||
button.click();
|
||||
|
||||
const button = findFirstButtonLink();
|
||||
expect(button.text().trim()).toEqual('« First');
|
||||
button.trigger('click');
|
||||
expect(spy).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it('should call the change callback with the first page when total and totalPages are NaN', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: 3,
|
||||
page: 2,
|
||||
|
@ -152,20 +146,16 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
const button = component.$el.querySelector('.js-first-button .page-link');
|
||||
|
||||
expect(button.textContent.trim()).toEqual('« First');
|
||||
|
||||
button.click();
|
||||
|
||||
const button = findFirstButtonLink();
|
||||
expect(button.text().trim()).toEqual('« First');
|
||||
button.trigger('click');
|
||||
expect(spy).toHaveBeenCalledWith(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('last button', () => {
|
||||
it('should call the change callback with the last page', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: 3,
|
||||
page: 2,
|
||||
|
@ -176,18 +166,14 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
const button = component.$el.querySelector('.js-last-button .page-link');
|
||||
|
||||
expect(button.textContent.trim()).toEqual('Last »');
|
||||
|
||||
button.click();
|
||||
|
||||
const button = findLastButtonLink();
|
||||
expect(button.text().trim()).toEqual('Last »');
|
||||
button.trigger('click');
|
||||
expect(spy).toHaveBeenCalledWith(5);
|
||||
});
|
||||
|
||||
it('should not render', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: 3,
|
||||
page: 2,
|
||||
|
@ -198,14 +184,13 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
expect(component.$el.querySelector('.js-last-button .page-link')).toBeNull();
|
||||
expect(findLastButtonLink().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('next button', () => {
|
||||
it('should be disabled and non clickable', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: NaN,
|
||||
page: 5,
|
||||
|
@ -216,16 +201,17 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
expect(component.$el.querySelector('.js-next-button').textContent.trim()).toEqual('Next ›');
|
||||
|
||||
component.$el.querySelector('.js-next-button .page-link').click();
|
||||
|
||||
expect(
|
||||
findNextButton()
|
||||
.text()
|
||||
.trim(),
|
||||
).toEqual('Next ›');
|
||||
findNextButtonLink().trigger('click');
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should be disabled and non clickable when total and totalPages are NaN', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: NaN,
|
||||
page: 5,
|
||||
|
@ -236,16 +222,17 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
expect(component.$el.querySelector('.js-next-button').textContent.trim()).toEqual('Next ›');
|
||||
|
||||
component.$el.querySelector('.js-next-button .page-link').click();
|
||||
|
||||
expect(
|
||||
findNextButton()
|
||||
.text()
|
||||
.trim(),
|
||||
).toEqual('Next ›');
|
||||
findNextButtonLink().trigger('click');
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should be enabled and clickable', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: 4,
|
||||
page: 3,
|
||||
|
@ -256,14 +243,12 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
component.$el.querySelector('.js-next-button .page-link').click();
|
||||
|
||||
findNextButtonLink().trigger('click');
|
||||
expect(spy).toHaveBeenCalledWith(4);
|
||||
});
|
||||
|
||||
it('should be enabled and clickable when total and totalPages are NaN', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: 4,
|
||||
page: 3,
|
||||
|
@ -274,16 +259,14 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
component.$el.querySelector('.js-next-button .page-link').click();
|
||||
|
||||
findNextButtonLink().trigger('click');
|
||||
expect(spy).toHaveBeenCalledWith(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('numbered buttons', () => {
|
||||
it('should render 5 pages', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: 4,
|
||||
page: 3,
|
||||
|
@ -294,12 +277,11 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
expect(component.$el.querySelectorAll('.page').length).toEqual(5);
|
||||
expect(findPages().length).toEqual(5);
|
||||
});
|
||||
|
||||
it('should not render any page', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: 4,
|
||||
page: 3,
|
||||
|
@ -310,14 +292,13 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
expect(component.$el.querySelectorAll('.page').length).toEqual(0);
|
||||
expect(findPages().length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('spread operator', () => {
|
||||
it('should render', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: 4,
|
||||
page: 3,
|
||||
|
@ -328,12 +309,15 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
expect(component.$el.querySelector('.separator').textContent.trim()).toEqual('...');
|
||||
expect(
|
||||
findSeparator()
|
||||
.text()
|
||||
.trim(),
|
||||
).toEqual('...');
|
||||
});
|
||||
|
||||
it('should not render', () => {
|
||||
component = mountComponent({
|
||||
mountComponent({
|
||||
pageInfo: {
|
||||
nextPage: 4,
|
||||
page: 3,
|
||||
|
@ -344,8 +328,7 @@ describe('Pagination component', () => {
|
|||
},
|
||||
change: spy,
|
||||
});
|
||||
|
||||
expect(component.$el.querySelector('.separator')).toBeNull();
|
||||
expect(findSeparator().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -537,7 +537,6 @@ Project:
|
|||
- external_webhook_token
|
||||
- pages_https_only
|
||||
- merge_requests_disable_committers_approval
|
||||
- merge_requests_require_code_owner_approval
|
||||
- require_password_to_approve
|
||||
ProjectTracingSetting:
|
||||
- external_url
|
||||
|
|
|
@ -130,5 +130,13 @@ describe DeploymentPlatform do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when instance has configured kubernetes cluster' do
|
||||
let!(:instance_cluster) { create(:cluster, :provided_by_user, :instance) }
|
||||
|
||||
it 'returns the Kubernetes platform' do
|
||||
is_expected.to eq(instance_cluster.platform_kubernetes)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -118,14 +118,13 @@ describe API::Features do
|
|||
post api("/features/#{feature_name}", admin), params: { value: 'true', user: user.username, feature_group: 'perf_team' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(201)
|
||||
expect(json_response).to eq(
|
||||
'name' => 'my_feature',
|
||||
'state' => 'conditional',
|
||||
'gates' => [
|
||||
{ 'key' => 'boolean', 'value' => false },
|
||||
{ 'key' => 'groups', 'value' => ['perf_team'] },
|
||||
{ 'key' => 'actors', 'value' => ["User:#{user.id}"] }
|
||||
])
|
||||
expect(json_response['name']).to eq('my_feature')
|
||||
expect(json_response['state']).to eq('conditional')
|
||||
expect(json_response['gates']).to contain_exactly(
|
||||
{ 'key' => 'boolean', 'value' => false },
|
||||
{ 'key' => 'groups', 'value' => ['perf_team'] },
|
||||
{ 'key' => 'actors', 'value' => ["User:#{user.id}"] }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -990,15 +990,15 @@
|
|||
dependencies:
|
||||
vue-eslint-parser "^6.0.4"
|
||||
|
||||
"@gitlab/svgs@^1.78.0":
|
||||
version "1.78.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.78.0.tgz#469493bd6cdd254eb5d1271edeab22bbbee2f4c4"
|
||||
integrity sha512-dBgEB/Q4FRD0NapmNrD86DF1FsV0uSgTx0UOJloHnGE2DNR2P1HQrCmLW2fX+QgN4P9CDAzdi2buVHuholofWw==
|
||||
"@gitlab/svgs@^1.79.0":
|
||||
version "1.79.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.79.0.tgz#7e79666118d6adc0247bdb3b3b6b2b299aa5a439"
|
||||
integrity sha512-0pTUviQqwyaKBOB6OL7Mmr2dQn/dGB03XslBMtL9lFZz1baB7d6xf+zxFU0GBAJUJan397IbBddE1jjUAQT8Fw==
|
||||
|
||||
"@gitlab/ui@5.36.0":
|
||||
version "5.36.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.36.0.tgz#3087b23c138ad1c222f6b047e533f253371bc618"
|
||||
integrity sha512-XXWUYZbRItKh9N92Vxql04BJ05uW5HlOuTCkD+lMbUgneqYTgVoKGH8d9kD++Jy7q8l5+AfzjboUn2n9sbQMZA==
|
||||
"@gitlab/ui@6.0.0":
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-6.0.0.tgz#1d347fca1752732226f9b61b9fcbd8b60982b7cf"
|
||||
integrity sha512-d37M+4MJen2dLp/svPDBcPVYZi4mgl5Gj01SPM7TeqtBl6gnps9KSjRiYd4P0FBPTbt3QQ8k2qkQ8uTi2q/o3w==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
"@gitlab/vue-toasted" "^1.2.1"
|
||||
|
|
Loading…
Reference in New Issue