Commit 24fe7aa2 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 6653aab9
...@@ -455,9 +455,9 @@ gem 'google-protobuf', '~> 3.8.0' ...@@ -455,9 +455,9 @@ gem 'google-protobuf', '~> 3.8.0'
gem 'toml-rb', '~> 1.0.0', require: false gem 'toml-rb', '~> 1.0.0', require: false
# Feature toggles # Feature toggles
gem 'flipper', '~> 0.13.0' gem 'flipper', '~> 0.17.1'
gem 'flipper-active_record', '~> 0.13.0' gem 'flipper-active_record', '~> 0.17.1'
gem 'flipper-active_support_cache_store', '~> 0.13.0' gem 'flipper-active_support_cache_store', '~> 0.17.1'
gem 'unleash', '~> 0.1.5' gem 'unleash', '~> 0.1.5'
# Structured logging # Structured logging
......
...@@ -285,13 +285,13 @@ GEM ...@@ -285,13 +285,13 @@ GEM
fast_gettext (1.6.0) fast_gettext (1.6.0)
ffaker (2.10.0) ffaker (2.10.0)
ffi (1.11.1) ffi (1.11.1)
flipper (0.13.0) flipper (0.17.1)
flipper-active_record (0.13.0) flipper-active_record (0.17.1)
activerecord (>= 3.2, < 6) activerecord (>= 4.2, < 7)
flipper (~> 0.13.0) flipper (~> 0.17.1)
flipper-active_support_cache_store (0.13.0) flipper-active_support_cache_store (0.17.1)
activesupport (>= 3.2, < 6) activesupport (>= 4.2, < 7)
flipper (~> 0.13.0) flipper (~> 0.17.1)
flowdock (0.7.1) flowdock (0.7.1)
httparty (~> 0.7) httparty (~> 0.7)
multi_json multi_json
...@@ -1149,9 +1149,9 @@ DEPENDENCIES ...@@ -1149,9 +1149,9 @@ DEPENDENCIES
faraday_middleware-aws-signers-v4 faraday_middleware-aws-signers-v4
fast_blank fast_blank
ffaker (~> 2.10) ffaker (~> 2.10)
flipper (~> 0.13.0) flipper (~> 0.17.1)
flipper-active_record (~> 0.13.0) flipper-active_record (~> 0.17.1)
flipper-active_support_cache_store (~> 0.13.0) flipper-active_support_cache_store (~> 0.17.1)
flowdock (~> 0.7) flowdock (~> 0.7)
fog-aliyun (~> 0.3) fog-aliyun (~> 0.3)
fog-aws (~> 3.5) fog-aws (~> 3.5)
......
...@@ -76,6 +76,10 @@ class Project < ApplicationRecord ...@@ -76,6 +76,10 @@ class Project < ApplicationRecord
delegate :no_import?, to: :import_state, allow_nil: true 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 :archived, false
default_value_for :resolve_outdated_diff_discussions, false default_value_for :resolve_outdated_diff_discussions, false
default_value_for :container_registry_enabled, gitlab_config_features.container_registry default_value_for :container_registry_enabled, gitlab_config_features.container_registry
......
---
title: Ignore deprecated column and remove references to it
merge_request: 18911
author:
type: deprecated
---
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: ...@@ -47,6 +47,13 @@ A database **reviewer**'s role is to:
reassign MR to the database **maintainer** suggested by Reviewer reassign MR to the database **maintainer** suggested by Reviewer
Roulette. 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: A database **maintainer**'s role is to:
- Perform the final database review on the MR. - Perform the final database review on the MR.
......
import Vue from 'vue'; import { shallowMount } from '@vue/test-utils';
import paginationComp from '~/vue_shared/components/pagination/table_pagination.vue'; import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
describe('Pagination component', () => { describe('Pagination component', () => {
let component; let wrapper;
let PaginationComponent;
let spy; let spy;
let mountComponent;
beforeEach(() => { const mountComponent = props => {
spy = jasmine.createSpy('spy'); wrapper = shallowMount(TablePagination, {
PaginationComponent = Vue.extend(paginationComp); sync: false,
mountComponent = function(props) {
return new PaginationComponent({
propsData: props, propsData: props,
}).$mount(); });
}; };
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 = jest.fn();
});
afterEach(() => {
wrapper.destroy();
}); });
describe('render', () => { describe('render', () => {
it('should not render anything', () => { it('should not render anything', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: NaN, nextPage: NaN,
page: 1, page: 1,
...@@ -32,12 +43,12 @@ describe('Pagination component', () => { ...@@ -32,12 +43,12 @@ describe('Pagination component', () => {
change: spy, change: spy,
}); });
expect(component.$el.childNodes.length).toEqual(0); expect(wrapper.isEmpty()).toBe(true);
}); });
describe('prev button', () => { describe('prev button', () => {
it('should be disabled and non clickable', () => { it('should be disabled and non clickable', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: 2, nextPage: 2,
page: 1, page: 1,
...@@ -49,17 +60,13 @@ describe('Pagination component', () => { ...@@ -49,17 +60,13 @@ describe('Pagination component', () => {
change: spy, change: spy,
}); });
expect( expect(findPreviousButton().classes()).toContain('disabled');
component.$el.querySelector('.js-previous-button').classList.contains('disabled'), findPreviousButtonLink().trigger('click');
).toEqual(true);
component.$el.querySelector('.js-previous-button .page-link').click();
expect(spy).not.toHaveBeenCalled(); expect(spy).not.toHaveBeenCalled();
}); });
it('should be disabled and non clickable when total and totalPages are NaN', () => { it('should be disabled and non clickable when total and totalPages are NaN', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: 2, nextPage: 2,
page: 1, page: 1,
...@@ -70,18 +77,13 @@ describe('Pagination component', () => { ...@@ -70,18 +77,13 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
expect(findPreviousButton().classes()).toContain('disabled');
expect( findPreviousButtonLink().trigger('click');
component.$el.querySelector('.js-previous-button').classList.contains('disabled'),
).toEqual(true);
component.$el.querySelector('.js-previous-button .page-link').click();
expect(spy).not.toHaveBeenCalled(); expect(spy).not.toHaveBeenCalled();
}); });
it('should be enabled and clickable', () => { it('should be enabled and clickable', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: 3, nextPage: 3,
page: 2, page: 2,
...@@ -92,14 +94,12 @@ describe('Pagination component', () => { ...@@ -92,14 +94,12 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
findPreviousButtonLink().trigger('click');
component.$el.querySelector('.js-previous-button .page-link').click();
expect(spy).toHaveBeenCalledWith(1); expect(spy).toHaveBeenCalledWith(1);
}); });
it('should be enabled and clickable when total and totalPages are NaN', () => { it('should be enabled and clickable when total and totalPages are NaN', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: 3, nextPage: 3,
page: 2, page: 2,
...@@ -110,16 +110,14 @@ describe('Pagination component', () => { ...@@ -110,16 +110,14 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
findPreviousButtonLink().trigger('click');
component.$el.querySelector('.js-previous-button .page-link').click();
expect(spy).toHaveBeenCalledWith(1); expect(spy).toHaveBeenCalledWith(1);
}); });
}); });
describe('first button', () => { describe('first button', () => {
it('should call the change callback with the first page', () => { it('should call the change callback with the first page', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: 3, nextPage: 3,
page: 2, page: 2,
...@@ -130,18 +128,14 @@ describe('Pagination component', () => { ...@@ -130,18 +128,14 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
const button = findFirstButtonLink();
const button = component.$el.querySelector('.js-first-button .page-link'); expect(button.text().trim()).toEqual('« First');
button.trigger('click');
expect(button.textContent.trim()).toEqual('« First');
button.click();
expect(spy).toHaveBeenCalledWith(1); expect(spy).toHaveBeenCalledWith(1);
}); });
it('should call the change callback with the first page when total and totalPages are NaN', () => { it('should call the change callback with the first page when total and totalPages are NaN', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: 3, nextPage: 3,
page: 2, page: 2,
...@@ -152,20 +146,16 @@ describe('Pagination component', () => { ...@@ -152,20 +146,16 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
const button = findFirstButtonLink();
const button = component.$el.querySelector('.js-first-button .page-link'); expect(button.text().trim()).toEqual('« First');
button.trigger('click');
expect(button.textContent.trim()).toEqual('« First');
button.click();
expect(spy).toHaveBeenCalledWith(1); expect(spy).toHaveBeenCalledWith(1);
}); });
}); });
describe('last button', () => { describe('last button', () => {
it('should call the change callback with the last page', () => { it('should call the change callback with the last page', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: 3, nextPage: 3,
page: 2, page: 2,
...@@ -176,18 +166,14 @@ describe('Pagination component', () => { ...@@ -176,18 +166,14 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
const button = findLastButtonLink();
const button = component.$el.querySelector('.js-last-button .page-link'); expect(button.text().trim()).toEqual('Last »');
button.trigger('click');
expect(button.textContent.trim()).toEqual('Last »');
button.click();
expect(spy).toHaveBeenCalledWith(5); expect(spy).toHaveBeenCalledWith(5);
}); });
it('should not render', () => { it('should not render', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: 3, nextPage: 3,
page: 2, page: 2,
...@@ -198,14 +184,13 @@ describe('Pagination component', () => { ...@@ -198,14 +184,13 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
expect(findLastButtonLink().exists()).toBe(false);
expect(component.$el.querySelector('.js-last-button .page-link')).toBeNull();
}); });
}); });
describe('next button', () => { describe('next button', () => {
it('should be disabled and non clickable', () => { it('should be disabled and non clickable', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: NaN, nextPage: NaN,
page: 5, page: 5,
...@@ -216,16 +201,17 @@ describe('Pagination component', () => { ...@@ -216,16 +201,17 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
expect(
expect(component.$el.querySelector('.js-next-button').textContent.trim()).toEqual('Next ›'); findNextButton()
.text()
component.$el.querySelector('.js-next-button .page-link').click(); .trim(),
).toEqual('Next ›');
findNextButtonLink().trigger('click');
expect(spy).not.toHaveBeenCalled(); expect(spy).not.toHaveBeenCalled();
}); });
it('should be disabled and non clickable when total and totalPages are NaN', () => { it('should be disabled and non clickable when total and totalPages are NaN', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: NaN, nextPage: NaN,
page: 5, page: 5,
...@@ -236,16 +222,17 @@ describe('Pagination component', () => { ...@@ -236,16 +222,17 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
expect(
expect(component.$el.querySelector('.js-next-button').textContent.trim()).toEqual('Next ›'); findNextButton()
.text()
component.$el.querySelector('.js-next-button .page-link').click(); .trim(),
).toEqual('Next ›');
findNextButtonLink().trigger('click');
expect(spy).not.toHaveBeenCalled(); expect(spy).not.toHaveBeenCalled();
}); });
it('should be enabled and clickable', () => { it('should be enabled and clickable', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: 4, nextPage: 4,
page: 3, page: 3,
...@@ -256,14 +243,12 @@ describe('Pagination component', () => { ...@@ -256,14 +243,12 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
findNextButtonLink().trigger('click');
component.$el.querySelector('.js-next-button .page-link').click();
expect(spy).toHaveBeenCalledWith(4); expect(spy).toHaveBeenCalledWith(4);
}); });
it('should be enabled and clickable when total and totalPages are NaN', () => { it('should be enabled and clickable when total and totalPages are NaN', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: 4, nextPage: 4,
page: 3, page: 3,
...@@ -274,16 +259,14 @@ describe('Pagination component', () => { ...@@ -274,16 +259,14 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
findNextButtonLink().trigger('click');
component.$el.querySelector('.js-next-button .page-link').click();
expect(spy).toHaveBeenCalledWith(4); expect(spy).toHaveBeenCalledWith(4);
}); });
}); });
describe('numbered buttons', () => { describe('numbered buttons', () => {
it('should render 5 pages', () => { it('should render 5 pages', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: 4, nextPage: 4,
page: 3, page: 3,
...@@ -294,12 +277,11 @@ describe('Pagination component', () => { ...@@ -294,12 +277,11 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
expect(findPages().length).toEqual(5);
expect(component.$el.querySelectorAll('.page').length).toEqual(5);
}); });
it('should not render any page', () => { it('should not render any page', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: 4, nextPage: 4,
page: 3, page: 3,
...@@ -310,14 +292,13 @@ describe('Pagination component', () => { ...@@ -310,14 +292,13 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
expect(findPages().length).toEqual(0);
expect(component.$el.querySelectorAll('.page').length).toEqual(0);
}); });
}); });
describe('spread operator', () => { describe('spread operator', () => {
it('should render', () => { it('should render', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: 4, nextPage: 4,
page: 3, page: 3,
...@@ -328,12 +309,15 @@ describe('Pagination component', () => { ...@@ -328,12 +309,15 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
expect(
expect(component.$el.querySelector('.separator').textContent.trim()).toEqual('...'); findSeparator()
.text()
.trim(),
).toEqual('...');
}); });
it('should not render', () => { it('should not render', () => {
component = mountComponent({ mountComponent({
pageInfo: { pageInfo: {
nextPage: 4, nextPage: 4,
page: 3, page: 3,
...@@ -344,8 +328,7 @@ describe('Pagination component', () => { ...@@ -344,8 +328,7 @@ describe('Pagination component', () => {
}, },
change: spy, change: spy,
}); });
expect(findSeparator().exists()).toBe(false);
expect(component.$el.querySelector('.separator')).toBeNull();
}); });
}); });
}); });
......
...@@ -537,7 +537,6 @@ Project: ...@@ -537,7 +537,6 @@ Project:
- external_webhook_token - external_webhook_token
- pages_https_only - pages_https_only
- merge_requests_disable_committers_approval - merge_requests_disable_committers_approval
- merge_requests_require_code_owner_approval
- require_password_to_approve - require_password_to_approve
ProjectTracingSetting: ProjectTracingSetting:
- external_url - external_url
......
...@@ -130,5 +130,13 @@ describe DeploymentPlatform do ...@@ -130,5 +130,13 @@ describe DeploymentPlatform do
end end
end 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
end end
...@@ -118,14 +118,13 @@ describe API::Features do ...@@ -118,14 +118,13 @@ describe API::Features do
post api("/features/#{feature_name}", admin), params: { value: 'true', user: user.username, feature_group: 'perf_team' } 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(response).to have_gitlab_http_status(201)
expect(json_response).to eq( expect(json_response['name']).to eq('my_feature')
'name' => 'my_feature', expect(json_response['state']).to eq('conditional')
'state' => 'conditional', expect(json_response['gates']).to contain_exactly(
'gates' => [
{ 'key' => 'boolean', 'value' => false }, { 'key' => 'boolean', 'value' => false },
{ 'key' => 'groups', 'value' => ['perf_team'] }, { 'key' => 'groups', 'value' => ['perf_team'] },
{ 'key' => 'actors', 'value' => ["User:#{user.id}"] } { 'key' => 'actors', 'value' => ["User:#{user.id}"] }
]) )
end end
end end
......
...@@ -990,15 +990,15 @@ ...@@ -990,15 +990,15 @@
dependencies: dependencies:
vue-eslint-parser "^6.0.4" vue-eslint-parser "^6.0.4"
"@gitlab/svgs@^1.78.0": "@gitlab/svgs@^1.79.0":
version "1.78.0" version "1.79.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.78.0.tgz#469493bd6cdd254eb5d1271edeab22bbbee2f4c4" resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.79.0.tgz#7e79666118d6adc0247bdb3b3b6b2b299aa5a439"
integrity sha512-dBgEB/Q4FRD0NapmNrD86DF1FsV0uSgTx0UOJloHnGE2DNR2P1HQrCmLW2fX+QgN4P9CDAzdi2buVHuholofWw== integrity sha512-0pTUviQqwyaKBOB6OL7Mmr2dQn/dGB03XslBMtL9lFZz1baB7d6xf+zxFU0GBAJUJan397IbBddE1jjUAQT8Fw==
"@gitlab/ui@5.36.0": "@gitlab/ui@6.0.0":
version "5.36.0" version "6.0.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.36.0.tgz#3087b23c138ad1c222f6b047e533f253371bc618" resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-6.0.0.tgz#1d347fca1752732226f9b61b9fcbd8b60982b7cf"
integrity sha512-XXWUYZbRItKh9N92Vxql04BJ05uW5HlOuTCkD+lMbUgneqYTgVoKGH8d9kD++Jy7q8l5+AfzjboUn2n9sbQMZA== integrity sha512-d37M+4MJen2dLp/svPDBcPVYZi4mgl5Gj01SPM7TeqtBl6gnps9KSjRiYd4P0FBPTbt3QQ8k2qkQ8uTi2q/o3w==
dependencies: dependencies:
"@babel/standalone" "^7.0.0" "@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.2.1" "@gitlab/vue-toasted" "^1.2.1"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment