Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-12-11 15:08:10 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2019-12-11 15:08:10 +0300
commitb86f474bf51e20d2db4cf0895d0a8e0894e31c08 (patch)
tree061d2a4c749924f5a35fe6199dd1d8982c4b0b27 /spec
parent6b8040dc25fdc5fe614c3796a147517dd50bc7d8 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/admin/sessions_controller_spec.rb74
-rw-r--r--spec/controllers/application_controller_spec.rb1
-rw-r--r--spec/controllers/oauth/applications_controller_spec.rb6
-rw-r--r--spec/controllers/omniauth_callbacks_controller_spec.rb107
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb2
-rw-r--r--spec/features/projects/environments/environments_spec.rb6
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb6
-rw-r--r--spec/frontend/create_cluster/eks_cluster/components/cluster_form_dropdown_spec.js11
-rw-r--r--spec/frontend/vue_shared/components/dropdown/dropdown_search_input_spec.js55
-rw-r--r--spec/frontend/vue_shared/components/table_pagination_spec.js324
-rw-r--r--spec/graphql/types/project_type_spec.rb2
-rw-r--r--spec/helpers/container_expiration_policies_helper_spec.rb47
-rw-r--r--spec/helpers/nav_helper_spec.rb17
-rw-r--r--spec/javascripts/commit/pipelines/pipelines_spec.js2
-rw-r--r--spec/javascripts/environments/environments_app_spec.js4
-rw-r--r--spec/javascripts/environments/folder/environments_folder_view_spec.js4
-rw-r--r--spec/javascripts/pipelines/pipelines_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/dropdown/dropdown_search_input_spec.js51
-rw-r--r--spec/lib/gitlab/auth/auth_finders_spec.rb (renamed from spec/lib/gitlab/auth/user_auth_finders_spec.rb)68
-rw-r--r--spec/lib/gitlab/auth/current_user_mode_spec.rb115
-rw-r--r--spec/lib/gitlab/auth/request_authenticator_spec.rb24
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml10
-rw-r--r--spec/models/container_expiration_policy_spec.rb41
-rw-r--r--spec/models/namespace_spec.rb7
-rw-r--r--spec/models/project_spec.rb8
-rw-r--r--spec/models/user_spec.rb1
-rw-r--r--spec/requests/api/helpers_spec.rb22
-rw-r--r--spec/requests/rack_attack_global_spec.rb12
-rw-r--r--spec/support/helpers/admin_mode_helpers.rb2
-rw-r--r--spec/support/matchers/graphql_matchers.rb13
-rw-r--r--spec/views/admin/sessions/new.html.haml_spec.rb35
-rw-r--r--spec/views/layouts/application.html.haml_spec.rb1
33 files changed, 647 insertions, 434 deletions
diff --git a/spec/controllers/admin/sessions_controller_spec.rb b/spec/controllers/admin/sessions_controller_spec.rb
index c1cb57c0b9d..bd0bb0bd81f 100644
--- a/spec/controllers/admin/sessions_controller_spec.rb
+++ b/spec/controllers/admin/sessions_controller_spec.rb
@@ -17,7 +17,7 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
get :new
expect(response).to have_gitlab_http_status(:not_found)
- expect(controller.send(:current_user_mode).admin_mode?).to be(false)
+ expect(controller.current_user_mode.admin_mode?).to be(false)
end
end
@@ -28,7 +28,21 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
get :new
expect(response).to render_template :new
- expect(controller.send(:current_user_mode).admin_mode?).to be(false)
+ expect(controller.current_user_mode.admin_mode?).to be(false)
+ end
+
+ context 'already in admin mode' do
+ before do
+ controller.current_user_mode.request_admin_mode!
+ controller.current_user_mode.enable_admin_mode!(password: user.password)
+ end
+
+ it 'redirects to original location' do
+ get :new
+
+ expect(response).to redirect_to(admin_root_path)
+ expect(controller.current_user_mode.admin_mode?).to be(true)
+ end
end
end
end
@@ -39,7 +53,7 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
post :create
expect(response).to have_gitlab_http_status(:not_found)
- expect(controller.send(:current_user_mode).admin_mode?).to be(false)
+ expect(controller.current_user_mode.admin_mode?).to be(false)
end
end
@@ -47,24 +61,60 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
let(:user) { create(:admin) }
it 'sets admin mode with a valid password' do
- expect(controller.send(:current_user_mode).admin_mode?).to be(false)
+ expect(controller.current_user_mode.admin_mode?).to be(false)
controller.store_location_for(:redirect, admin_root_path)
+
+ # triggering the auth form will request admin mode
+ get :new
+
post :create, params: { password: user.password }
expect(response).to redirect_to admin_root_path
- expect(controller.send(:current_user_mode).admin_mode?).to be(true)
+ expect(controller.current_user_mode.admin_mode?).to be(true)
end
it 'fails with an invalid password' do
- expect(controller.send(:current_user_mode).admin_mode?).to be(false)
+ expect(controller.current_user_mode.admin_mode?).to be(false)
controller.store_location_for(:redirect, admin_root_path)
+ # triggering the auth form will request admin mode
+ get :new
+
post :create, params: { password: '' }
expect(response).to render_template :new
- expect(controller.send(:current_user_mode).admin_mode?).to be(false)
+ expect(controller.current_user_mode.admin_mode?).to be(false)
+ end
+
+ it 'fails if not requested first' do
+ expect(controller.current_user_mode.admin_mode?).to be(false)
+
+ controller.store_location_for(:redirect, admin_root_path)
+
+ # do not trigger the auth form
+
+ post :create, params: { password: user.password }
+
+ expect(response).to redirect_to(new_admin_session_path)
+ expect(controller.current_user_mode.admin_mode?).to be(false)
+ end
+
+ it 'fails if request period expired' do
+ expect(controller.current_user_mode.admin_mode?).to be(false)
+
+ controller.store_location_for(:redirect, admin_root_path)
+
+ # triggering the auth form will request admin mode
+ get :new
+
+ Timecop.freeze(Gitlab::Auth::CurrentUserMode::ADMIN_MODE_REQUESTED_GRACE_PERIOD.from_now) do
+ post :create, params: { password: user.password }
+
+ expect(response).to redirect_to(new_admin_session_path)
+ expect(controller.current_user_mode.admin_mode?).to be(false)
+ end
end
end
end
@@ -75,7 +125,7 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
get :destroy
expect(response).to have_gitlab_http_status(404)
- expect(controller.send(:current_user_mode).admin_mode?).to be(false)
+ expect(controller.current_user_mode.admin_mode?).to be(false)
end
end
@@ -83,15 +133,17 @@ describe Admin::SessionsController, :do_not_mock_admin_mode do
let(:user) { create(:admin) }
it 'disables admin mode and redirects to main page' do
- expect(controller.send(:current_user_mode).admin_mode?).to be(false)
+ expect(controller.current_user_mode.admin_mode?).to be(false)
+
+ get :new
post :create, params: { password: user.password }
- expect(controller.send(:current_user_mode).admin_mode?).to be(true)
+ expect(controller.current_user_mode.admin_mode?).to be(true)
get :destroy
expect(response).to have_gitlab_http_status(:found)
expect(response).to redirect_to(root_path)
- expect(controller.send(:current_user_mode).admin_mode?).to be(false)
+ expect(controller.current_user_mode.admin_mode?).to be(false)
end
end
end
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 9c6ea13f948..e72ab16f62a 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -814,6 +814,7 @@ describe ApplicationController do
context 'that re-authenticated' do
before do
+ Gitlab::Auth::CurrentUserMode.new(user).request_admin_mode!
Gitlab::Auth::CurrentUserMode.new(user).enable_admin_mode!(password: user.password)
end
diff --git a/spec/controllers/oauth/applications_controller_spec.rb b/spec/controllers/oauth/applications_controller_spec.rb
index df836c2c3e3..270a2fcc1d6 100644
--- a/spec/controllers/oauth/applications_controller_spec.rb
+++ b/spec/controllers/oauth/applications_controller_spec.rb
@@ -62,6 +62,12 @@ describe Oauth::ApplicationsController do
end
end
+ context 'Helpers' do
+ it 'current_user_mode available' do
+ expect(subject.current_user_mode).not_to be_nil
+ end
+ end
+
def disable_user_oauth
allow(Gitlab::CurrentSettings.current_application_settings).to receive(:user_oauth_applications?).and_return(false)
end
diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb
index 521dbe7ee23..6c5f36804e8 100644
--- a/spec/controllers/omniauth_callbacks_controller_spec.rb
+++ b/spec/controllers/omniauth_callbacks_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe OmniauthCallbacksController, type: :controller do
+describe OmniauthCallbacksController, type: :controller, do_not_mock_admin_mode: true do
include LoginHelpers
describe 'omniauth' do
@@ -336,4 +336,109 @@ describe OmniauthCallbacksController, type: :controller do
end
end
end
+
+ describe 'enable admin mode' do
+ include_context 'custom session'
+
+ let(:provider) { :auth0 }
+ let(:extern_uid) { 'my-uid' }
+ let(:user) { create(:omniauth_user, extern_uid: extern_uid, provider: provider) }
+
+ def reauthenticate_and_check_admin_mode(expected_admin_mode:)
+ # Initially admin mode disabled
+ expect(subject.current_user_mode.admin_mode?).to be(false)
+
+ # Trigger OmniAuth admin mode flow and expect admin mode status
+ post provider
+
+ expect(request.env['warden']).to be_authenticated
+ expect(subject.current_user_mode.admin_mode?).to be(expected_admin_mode)
+ end
+
+ context 'user and admin mode requested by the same user' do
+ before do
+ sign_in user
+
+ mock_auth_hash(provider.to_s, extern_uid, user.email, additional_info: {})
+ stub_omniauth_provider(provider, context: request)
+ end
+
+ context 'with a regular user' do
+ it 'cannot be enabled' do
+ reauthenticate_and_check_admin_mode(expected_admin_mode: false)
+
+ expect(response).to redirect_to(root_path)
+ end
+ end
+
+ context 'with an admin user' do
+ let(:user) { create(:omniauth_user, extern_uid: extern_uid, provider: provider, access_level: :admin) }
+
+ context 'when requested first' do
+ before do
+ subject.current_user_mode.request_admin_mode!
+ end
+
+ it 'can be enabled' do
+ reauthenticate_and_check_admin_mode(expected_admin_mode: true)
+
+ expect(response).to redirect_to(admin_root_path)
+ end
+ end
+
+ context 'when not requested first' do
+ it 'cannot be enabled' do
+ reauthenticate_and_check_admin_mode(expected_admin_mode: false)
+
+ expect(response).to redirect_to(root_path)
+ end
+ end
+ end
+ end
+
+ context 'user and admin mode requested by different users' do
+ let(:reauth_extern_uid) { 'another_uid' }
+ let(:reauth_user) { create(:omniauth_user, extern_uid: reauth_extern_uid, provider: provider) }
+
+ before do
+ sign_in user
+
+ mock_auth_hash(provider.to_s, reauth_extern_uid, reauth_user.email, additional_info: {})
+ stub_omniauth_provider(provider, context: request)
+ end
+
+ context 'with a regular user' do
+ it 'cannot be enabled' do
+ reauthenticate_and_check_admin_mode(expected_admin_mode: false)
+
+ expect(response).to redirect_to(profile_account_path)
+ end
+ end
+
+ context 'with an admin user' do
+ let(:user) { create(:omniauth_user, extern_uid: extern_uid, provider: provider, access_level: :admin) }
+ let(:reauth_user) { create(:omniauth_user, extern_uid: reauth_extern_uid, provider: provider, access_level: :admin) }
+
+ context 'when requested first' do
+ before do
+ subject.current_user_mode.request_admin_mode!
+ end
+
+ it 'cannot be enabled' do
+ reauthenticate_and_check_admin_mode(expected_admin_mode: false)
+
+ expect(response).to redirect_to(new_admin_session_path)
+ end
+ end
+
+ context 'when not requested first' do
+ it 'cannot be enabled' do
+ reauthenticate_and_check_admin_mode(expected_admin_mode: false)
+
+ expect(response).to redirect_to(profile_account_path)
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index ba7374d5040..741f46cef45 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe 'Gcp Cluster', :js do
+describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
include GoogleApi::CloudPlatformHelpers
let(:project) { create(:project) }
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index 382b5f3cac0..01687674309 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -49,11 +49,11 @@ describe 'Environments page', :js do
it 'renders second page of pipelines' do
visit_environments(project, scope: 'available')
- find('.js-next-button').click
+ find('.page-link.next-page-item').click
wait_for_requests
- expect(page).to have_selector('.gl-pagination .page', count: 2)
- expect(find('.gl-pagination .page-item.active .page-link').text).to eq("2")
+ expect(page).to have_selector('.gl-pagination .page-link', count: 4)
+ expect(find('.gl-pagination .page-link.active').text).to eq("2")
end
end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index f5558a1f2ec..b4c9eb7ebec 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -592,15 +592,15 @@ describe 'Pipelines', :js do
visit project_pipelines_path(project, page: '2')
wait_for_requests
- expect(page).to have_selector('.gl-pagination .page', count: 2)
+ expect(page).to have_selector('.gl-pagination .page-link', count: 4)
end
it 'shows updated content' do
visit project_pipelines_path(project)
wait_for_requests
- page.find('.js-next-button .page-link').click
+ page.find('.page-link.next-page-item').click
- expect(page).to have_selector('.gl-pagination .page', count: 2)
+ expect(page).to have_selector('.gl-pagination .page-link', count: 4)
end
end
end
diff --git a/spec/frontend/create_cluster/eks_cluster/components/cluster_form_dropdown_spec.js b/spec/frontend/create_cluster/eks_cluster/components/cluster_form_dropdown_spec.js
index 7ba35358442..c9cdd728509 100644
--- a/spec/frontend/create_cluster/eks_cluster/components/cluster_form_dropdown_spec.js
+++ b/spec/frontend/create_cluster/eks_cluster/components/cluster_form_dropdown_spec.js
@@ -1,4 +1,5 @@
import { shallowMount } from '@vue/test-utils';
+import $ from 'jquery';
import { GlIcon } from '@gitlab/ui';
import ClusterFormDropdown from '~/create_cluster/eks_cluster/components/cluster_form_dropdown.vue';
@@ -169,4 +170,14 @@ describe('ClusterFormDropdown', () => {
expect(vm.findAll('.js-dropdown-item').length).toEqual(1);
expect(vm.find('.js-dropdown-item').text()).toEqual(secondItem.name);
});
+
+ it('focuses dropdown search input when dropdown is displayed', () => {
+ const dropdownEl = vm.find('.dropdown').element;
+
+ expect(vm.find(DropdownSearchInput).props('focused')).toBe(false);
+
+ $(dropdownEl).trigger('shown.bs.dropdown');
+
+ expect(vm.find(DropdownSearchInput).props('focused')).toBe(true);
+ });
});
diff --git a/spec/frontend/vue_shared/components/dropdown/dropdown_search_input_spec.js b/spec/frontend/vue_shared/components/dropdown/dropdown_search_input_spec.js
new file mode 100644
index 00000000000..0d0e4ae4349
--- /dev/null
+++ b/spec/frontend/vue_shared/components/dropdown/dropdown_search_input_spec.js
@@ -0,0 +1,55 @@
+import { mount } from '@vue/test-utils';
+import DropdownSearchInputComponent from '~/vue_shared/components/dropdown/dropdown_search_input.vue';
+
+describe('DropdownSearchInputComponent', () => {
+ let wrapper;
+
+ const defaultProps = {
+ placeholderText: 'Search something',
+ };
+ const buildVM = (propsData = defaultProps) => {
+ wrapper = mount(DropdownSearchInputComponent, {
+ propsData,
+ });
+ };
+ const findInputEl = () => wrapper.find('.dropdown-input-field');
+
+ beforeEach(() => {
+ buildVM();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('template', () => {
+ it('renders input element with type `search`', () => {
+ expect(findInputEl().exists()).toBe(true);
+ expect(findInputEl().attributes('type')).toBe('search');
+ });
+
+ it('renders search icon element', () => {
+ expect(wrapper.find('.fa-search.dropdown-input-search').exists()).toBe(true);
+ });
+
+ it('renders clear search icon element', () => {
+ expect(wrapper.find('.fa-times.dropdown-input-clear.js-dropdown-input-clear').exists()).toBe(
+ true,
+ );
+ });
+
+ it('displays custom placeholder text', () => {
+ expect(findInputEl().attributes('placeholder')).toBe(defaultProps.placeholderText);
+ });
+
+ it('focuses input element when focused property equals true', () => {
+ const inputEl = findInputEl().element;
+
+ jest.spyOn(inputEl, 'focus');
+
+ wrapper.setProps({ focused: true });
+
+ expect(inputEl.focus).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/table_pagination_spec.js b/spec/frontend/vue_shared/components/table_pagination_spec.js
index 0a9ff36b2fb..8105d1fcef3 100644
--- a/spec/frontend/vue_shared/components/table_pagination_spec.js
+++ b/spec/frontend/vue_shared/components/table_pagination_spec.js
@@ -1,5 +1,6 @@
import { shallowMount } from '@vue/test-utils';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
+import { GlPagination } from '@gitlab/ui';
describe('Pagination component', () => {
let wrapper;
@@ -12,15 +13,6 @@ describe('Pagination component', () => {
});
};
- 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();
});
@@ -46,290 +38,54 @@ describe('Pagination component', () => {
expect(wrapper.isEmpty()).toBe(true);
});
- describe('prev button', () => {
- it('should be disabled and non clickable', () => {
- mountComponent({
- pageInfo: {
- nextPage: 2,
- page: 1,
- perPage: 20,
- previousPage: NaN,
- total: 84,
- totalPages: 5,
- },
- change: spy,
- });
-
- 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', () => {
- mountComponent({
- pageInfo: {
- nextPage: 2,
- page: 1,
- perPage: 20,
- previousPage: NaN,
- total: NaN,
- totalPages: NaN,
- },
- change: spy,
- });
- expect(findPreviousButton().classes()).toContain('disabled');
- findPreviousButtonLink().trigger('click');
- expect(spy).not.toHaveBeenCalled();
- });
-
- it('should be enabled and clickable', () => {
- mountComponent({
- pageInfo: {
- nextPage: 3,
- page: 2,
- perPage: 20,
- previousPage: 1,
- total: 84,
- totalPages: 5,
- },
- change: spy,
- });
- findPreviousButtonLink().trigger('click');
- expect(spy).toHaveBeenCalledWith(1);
- });
-
- it('should be enabled and clickable when total and totalPages are NaN', () => {
- mountComponent({
- pageInfo: {
- nextPage: 3,
- page: 2,
- perPage: 20,
- previousPage: 1,
- total: NaN,
- totalPages: NaN,
- },
- change: spy,
- });
- findPreviousButtonLink().trigger('click');
- expect(spy).toHaveBeenCalledWith(1);
- });
- });
-
- describe('first button', () => {
- it('should call the change callback with the first page', () => {
- mountComponent({
- pageInfo: {
- nextPage: 3,
- page: 2,
- perPage: 20,
- previousPage: 1,
- total: 84,
- totalPages: 5,
- },
- change: spy,
- });
- 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', () => {
- mountComponent({
- pageInfo: {
- nextPage: 3,
- page: 2,
- perPage: 20,
- previousPage: 1,
- total: NaN,
- totalPages: NaN,
- },
- change: spy,
- });
- 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', () => {
- mountComponent({
- pageInfo: {
- nextPage: 3,
- page: 2,
- perPage: 20,
- previousPage: 1,
- total: 84,
- totalPages: 5,
- },
- change: spy,
- });
- const button = findLastButtonLink();
- expect(button.text().trim()).toEqual('Last »');
- button.trigger('click');
- expect(spy).toHaveBeenCalledWith(5);
- });
-
- it('should not render', () => {
- mountComponent({
- pageInfo: {
- nextPage: 3,
- page: 2,
- perPage: 20,
- previousPage: 1,
- total: NaN,
- totalPages: NaN,
- },
- change: spy,
- });
- expect(findLastButtonLink().exists()).toBe(false);
- });
- });
-
- describe('next button', () => {
- it('should be disabled and non clickable', () => {
- mountComponent({
- pageInfo: {
- nextPage: NaN,
- page: 5,
- perPage: 20,
- previousPage: 4,
- total: 84,
- totalPages: 5,
- },
- change: spy,
- });
- 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', () => {
- mountComponent({
- pageInfo: {
- nextPage: NaN,
- page: 5,
- perPage: 20,
- previousPage: 4,
- total: NaN,
- totalPages: NaN,
- },
- change: spy,
- });
- expect(
- findNextButton()
- .text()
- .trim(),
- ).toEqual('Next ›');
- findNextButtonLink().trigger('click');
- expect(spy).not.toHaveBeenCalled();
- });
-
- it('should be enabled and clickable', () => {
- mountComponent({
- pageInfo: {
- nextPage: 4,
- page: 3,
- perPage: 20,
- previousPage: 2,
- total: 84,
- totalPages: 5,
- },
- change: spy,
- });
- findNextButtonLink().trigger('click');
- expect(spy).toHaveBeenCalledWith(4);
+ it('renders if there is a next page', () => {
+ mountComponent({
+ pageInfo: {
+ nextPage: 2,
+ page: 1,
+ perPage: 20,
+ previousPage: NaN,
+ total: 15,
+ totalPages: 1,
+ },
+ change: spy,
});
- it('should be enabled and clickable when total and totalPages are NaN', () => {
- mountComponent({
- pageInfo: {
- nextPage: 4,
- page: 3,
- perPage: 20,
- previousPage: 2,
- total: NaN,
- totalPages: NaN,
- },
- change: spy,
- });
- findNextButtonLink().trigger('click');
- expect(spy).toHaveBeenCalledWith(4);
- });
+ expect(wrapper.isEmpty()).toBe(false);
});
- describe('numbered buttons', () => {
- it('should render 5 pages', () => {
- mountComponent({
- pageInfo: {
- nextPage: 4,
- page: 3,
- perPage: 20,
- previousPage: 2,
- total: 84,
- totalPages: 5,
- },
- change: spy,
- });
- expect(findPages().length).toEqual(5);
+ it('renders if there is a prev page', () => {
+ mountComponent({
+ pageInfo: {
+ nextPage: NaN,
+ page: 2,
+ perPage: 20,
+ previousPage: 1,
+ total: 15,
+ totalPages: 1,
+ },
+ change: spy,
});
- it('should not render any page', () => {
- mountComponent({
- pageInfo: {
- nextPage: 4,
- page: 3,
- perPage: 20,
- previousPage: 2,
- total: NaN,
- totalPages: NaN,
- },
- change: spy,
- });
- expect(findPages().length).toEqual(0);
- });
+ expect(wrapper.isEmpty()).toBe(false);
});
+ });
- describe('spread operator', () => {
- it('should render', () => {
- mountComponent({
- pageInfo: {
- nextPage: 4,
- page: 3,
- perPage: 20,
- previousPage: 2,
- total: 84,
- totalPages: 10,
- },
- change: spy,
- });
- expect(
- findSeparator()
- .text()
- .trim(),
- ).toEqual('...');
- });
-
- it('should not render', () => {
- mountComponent({
- pageInfo: {
- nextPage: 4,
- page: 3,
- perPage: 20,
- previousPage: 2,
- total: NaN,
- totalPages: NaN,
- },
- change: spy,
- });
- expect(findSeparator().exists()).toBe(false);
+ describe('events', () => {
+ it('calls change method when page changes', () => {
+ mountComponent({
+ pageInfo: {
+ nextPage: NaN,
+ page: 2,
+ perPage: 20,
+ previousPage: 1,
+ total: 15,
+ totalPages: 1,
+ },
+ change: spy,
});
+ wrapper.find(GlPagination).vm.$emit('input', 3);
+ expect(spy).toHaveBeenCalledWith(3);
});
});
});
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index 5d1a5fe1987..a3c51f24307 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -25,7 +25,7 @@ describe GitlabSchema.types['Project'] do
issue pipelines removeSourceBranchAfterMerge sentryDetailedError snippets
]
- is_expected.to have_graphql_fields(*expected_fields)
+ is_expected.to include_graphql_fields(*expected_fields)
end
describe 'issue field' do
diff --git a/spec/helpers/container_expiration_policies_helper_spec.rb b/spec/helpers/container_expiration_policies_helper_spec.rb
new file mode 100644
index 00000000000..3eb1234d82b
--- /dev/null
+++ b/spec/helpers/container_expiration_policies_helper_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ContainerExpirationPoliciesHelper do
+ describe '#keep_n_options' do
+ it 'returns keep_n options formatted for dropdown usage' do
+ expected_result = [
+ { key: 1, label: '1 tag per image name' },
+ { key: 5, label: '5 tags per image name' },
+ { key: 10, label: '10 tags per image name' },
+ { key: 25, label: '25 tags per image name' },
+ { key: 50, label: '50 tags per image name' },
+ { key: 100, label: '100 tags per image name' }
+ ]
+
+ expect(helper.keep_n_options).to eq(expected_result)
+ end
+ end
+
+ describe '#cadence_options' do
+ it 'returns cadence options formatted for dropdown usage' do
+ expected_result = [
+ { key: '1d', label: 'Every day' },
+ { key: '7d', label: 'Every week' },
+ { key: '14d', label: 'Every two weeks' },
+ { key: '1month', label: 'Every month' },
+ { key: '3month', label: 'Every three months' }
+ ]
+
+ expect(helper.cadence_options).to eq(expected_result)
+ end
+ end
+
+ describe '#older_than_options' do
+ it 'returns older_than options formatted for dropdown usage' do
+ expected_result = [
+ { key: '7d', label: '7 days until tags are automatically removed' },
+ { key: '14d', label: '14 days until tags are automatically removed' },
+ { key: '30d', label: '30 days until tags are automatically removed' },
+ { key: '90d', label: '90 days until tags are automatically removed' }
+ ]
+
+ expect(helper.older_than_options).to eq(expected_result)
+ end
+ end
+end
diff --git a/spec/helpers/nav_helper_spec.rb b/spec/helpers/nav_helper_spec.rb
index 882a125a0da..8d7572c5b5f 100644
--- a/spec/helpers/nav_helper_spec.rb
+++ b/spec/helpers/nav_helper_spec.rb
@@ -42,6 +42,7 @@ describe NavHelper, :do_not_mock_admin_mode do
context 'with admin mode enabled' do
before do
+ current_user_mode.request_admin_mode!
current_user_mode.enable_admin_mode!(password: user.password)
end
@@ -62,6 +63,7 @@ describe NavHelper, :do_not_mock_admin_mode do
context 'with admin mode enabled' do
before do
+ current_user_mode.request_admin_mode!
current_user_mode.enable_admin_mode!(password: user.password)
end
@@ -89,11 +91,18 @@ describe NavHelper, :do_not_mock_admin_mode do
end
end
- it 'returns only the sign in and search when the user is not logged in' do
- allow(helper).to receive(:current_user).and_return(nil)
- allow(helper).to receive(:can?).with(nil, :read_cross_project) { true }
+ context 'when the user is not logged in' do
+ let(:current_user_mode) { Gitlab::Auth::CurrentUserMode.new(nil) }
- expect(helper.header_links).to contain_exactly(:sign_in, :search)
+ before do
+ allow(helper).to receive(:current_user).and_return(nil)
+ allow(helper).to receive(:current_user_mode).and_return(current_user_mode)
+ allow(helper).to receive(:can?).with(nil, :read_cross_project) { true }
+ end
+
+ it 'returns only the sign in and search when the user is not logged in' do
+ expect(helper.header_links).to contain_exactly(:sign_in, :search)
+ end
end
end
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index e2bbada3a49..29bdf05b8cf 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -83,7 +83,7 @@ describe('Pipelines table in Commits and Merge requests', function() {
};
vm.$nextTick(() => {
- vm.$el.querySelector('.js-next-button .page-link').click();
+ vm.$el.querySelector('.next-page-item').click();
expect(vm.updateContent).toHaveBeenCalledWith({ page: '2' });
done();
diff --git a/spec/javascripts/environments/environments_app_spec.js b/spec/javascripts/environments/environments_app_spec.js
index 9c8da4970f4..75526c2ba74 100644
--- a/spec/javascripts/environments/environments_app_spec.js
+++ b/spec/javascripts/environments/environments_app_spec.js
@@ -92,13 +92,13 @@ describe('Environment', () => {
describe('pagination', () => {
it('should render pagination', () => {
- expect(component.$el.querySelectorAll('.gl-pagination li').length).toEqual(5);
+ expect(component.$el.querySelectorAll('.gl-pagination li').length).toEqual(9);
});
it('should make an API request when page is clicked', done => {
spyOn(component, 'updateContent');
setTimeout(() => {
- component.$el.querySelector('.gl-pagination li:nth-child(5) .page-link').click();
+ component.$el.querySelector('.gl-pagination li:nth-child(3) .page-link').click();
expect(component.updateContent).toHaveBeenCalledWith({ scope: 'available', page: '2' });
done();
diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js
index d217bbb3078..6530201240f 100644
--- a/spec/javascripts/environments/folder/environments_folder_view_spec.js
+++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js
@@ -115,7 +115,9 @@ describe('Environments Folder View', () => {
it('should make an API request when changing page', done => {
spyOn(component, 'updateContent');
setTimeout(() => {
- component.$el.querySelector('.gl-pagination .js-last-button .page-link').click();
+ component.$el
+ .querySelector('.gl-pagination .page-item:nth-last-of-type(2) .page-link')
+ .click();
expect(component.updateContent).toHaveBeenCalledWith({
scope: component.scope,
diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js
index e1123cc7248..5cd91413c5f 100644
--- a/spec/javascripts/pipelines/pipelines_spec.js
+++ b/spec/javascripts/pipelines/pipelines_spec.js
@@ -446,7 +446,7 @@ describe('Pipelines', () => {
};
vm.$nextTick(() => {
- vm.$el.querySelector('.js-next-button .page-link').click();
+ vm.$el.querySelector('.next-page-item').click();
expect(vm.updateContent).toHaveBeenCalledWith({ scope: 'all', page: '2' });
diff --git a/spec/javascripts/vue_shared/components/dropdown/dropdown_search_input_spec.js b/spec/javascripts/vue_shared/components/dropdown/dropdown_search_input_spec.js
deleted file mode 100644
index 456f310d10c..00000000000
--- a/spec/javascripts/vue_shared/components/dropdown/dropdown_search_input_spec.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import Vue from 'vue';
-
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import dropdownSearchInputComponent from '~/vue_shared/components/dropdown/dropdown_search_input.vue';
-
-const componentConfig = {
- placeholderText: 'Search something',
-};
-
-const createComponent = (config = componentConfig) => {
- const Component = Vue.extend(dropdownSearchInputComponent);
-
- return mountComponent(Component, config);
-};
-
-describe('DropdownSearchInputComponent', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('template', () => {
- it('renders input element with type `search`', () => {
- const inputEl = vm.$el.querySelector('input.dropdown-input-field');
-
- expect(inputEl).not.toBeNull();
- expect(inputEl.getAttribute('type')).toBe('search');
- });
-
- it('renders search icon element', () => {
- expect(vm.$el.querySelector('.fa-search.dropdown-input-search')).not.toBeNull();
- });
-
- it('renders clear search icon element', () => {
- expect(
- vm.$el.querySelector('.fa-times.dropdown-input-clear.js-dropdown-input-clear'),
- ).not.toBeNull();
- });
-
- it('displays custom placeholder text', () => {
- const inputEl = vm.$el.querySelector('input.dropdown-input-field');
-
- expect(inputEl.getAttribute('placeholder')).toBe(componentConfig.placeholderText);
- });
- });
-});
diff --git a/spec/lib/gitlab/auth/user_auth_finders_spec.rb b/spec/lib/gitlab/auth/auth_finders_spec.rb
index 125039edcf8..3d10f411310 100644
--- a/spec/lib/gitlab/auth/user_auth_finders_spec.rb
+++ b/spec/lib/gitlab/auth/auth_finders_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Gitlab::Auth::UserAuthFinders do
+describe Gitlab::Auth::AuthFinders do
include described_class
let(:user) { create(:user) }
@@ -196,13 +196,13 @@ describe Gitlab::Auth::UserAuthFinders do
context 'when validate_access_token! returns valid' do
it 'returns user' do
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect(find_user_from_access_token).to eq user
end
it 'returns exception if token has no user' do
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
allow_any_instance_of(PersonalAccessToken).to receive(:user).and_return(nil)
expect { find_user_from_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
@@ -228,7 +228,7 @@ describe Gitlab::Auth::UserAuthFinders do
let(:personal_access_token) { create(:personal_access_token, user: user) }
before do
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
end
it 'returns exception if token has no user' do
@@ -279,7 +279,7 @@ describe Gitlab::Auth::UserAuthFinders do
context 'passed as header' do
it 'returns token if valid personal_access_token' do
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect(find_personal_access_token).to eq personal_access_token
end
@@ -287,7 +287,7 @@ describe Gitlab::Auth::UserAuthFinders do
context 'passed as param' do
it 'returns token if valid personal_access_token' do
- set_param(Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_PARAM, personal_access_token.token)
+ set_param(described_class::PRIVATE_TOKEN_PARAM, personal_access_token.token)
expect(find_personal_access_token).to eq personal_access_token
end
@@ -298,7 +298,7 @@ describe Gitlab::Auth::UserAuthFinders do
end
it 'returns exception if invalid personal_access_token' do
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = 'invalid_token'
+ env[described_class::PRIVATE_TOKEN_HEADER] = 'invalid_token'
expect { find_personal_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
@@ -379,4 +379,58 @@ describe Gitlab::Auth::UserAuthFinders do
end
end
end
+
+ describe '#find_runner_from_token' do
+ let(:runner) { create(:ci_runner) }
+
+ context 'with API requests' do
+ before do
+ env['SCRIPT_NAME'] = '/api/endpoint'
+ end
+
+ it 'returns the runner if token is valid' do
+ set_param(:token, runner.token)
+
+ expect(find_runner_from_token).to eq(runner)
+ end
+
+ it 'returns nil if token is not present' do
+ expect(find_runner_from_token).to be_nil
+ end
+
+ it 'returns nil if token is blank' do
+ set_param(:token, '')
+
+ expect(find_runner_from_token).to be_nil
+ end
+
+ it 'returns exception if invalid token' do
+ set_param(:token, 'invalid_token')
+
+ expect { find_runner_from_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
+ end
+ end
+
+ context 'without API requests' do
+ before do
+ env['SCRIPT_NAME'] = 'url.ics'
+ end
+
+ it 'returns nil if token is valid' do
+ set_param(:token, runner.token)
+
+ expect(find_runner_from_token).to be_nil
+ end
+
+ it 'returns nil if token is blank' do
+ expect(find_runner_from_token).to be_nil
+ end
+
+ it 'returns nil if invalid token' do
+ set_param(:token, 'invalid_token')
+
+ expect(find_runner_from_token).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/auth/current_user_mode_spec.rb b/spec/lib/gitlab/auth/current_user_mode_spec.rb
index b93d460cf48..3b3db0f7315 100644
--- a/spec/lib/gitlab/auth/current_user_mode_spec.rb
+++ b/spec/lib/gitlab/auth/current_user_mode_spec.rb
@@ -62,69 +62,90 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
context 'when the user is an admin' do
let(:user) { build(:user, :admin) }
- it 'is false by default' do
- expect(subject.admin_mode?).to be(false)
- end
-
- it 'cannot be enabled with an invalid password' do
- subject.enable_admin_mode!(password: nil)
-
- expect(subject.admin_mode?).to be(false)
- end
+ context 'when admin mode not requested' do
+ it 'is false by default' do
+ expect(subject.admin_mode?).to be(false)
+ end
- it 'can be enabled with a valid password' do
- subject.enable_admin_mode!(password: user.password)
+ it 'raises exception if we try to enable it' do
+ expect do
+ subject.enable_admin_mode!(password: user.password)
+ end.to raise_error(::Gitlab::Auth::CurrentUserMode::NotRequestedError)
- expect(subject.admin_mode?).to be(true)
+ expect(subject.admin_mode?).to be(false)
+ end
end
- it 'can be disabled' do
- subject.enable_admin_mode!(password: user.password)
- subject.disable_admin_mode!
-
- expect(subject.admin_mode?).to be(false)
- end
+ context 'when admin mode requested first' do
+ before do
+ subject.request_admin_mode!
+ end
- it 'will expire in the future' do
- subject.enable_admin_mode!(password: user.password)
- expect(subject.admin_mode?).to be(true), 'admin mode is not active in the present'
+ it 'is false by default' do
+ expect(subject.admin_mode?).to be(false)
+ end
- Timecop.freeze(Gitlab::Auth::CurrentUserMode::MAX_ADMIN_MODE_TIME.from_now) do
- # in the future this will be a new request, simulate by clearing the RequestStore
- Gitlab::SafeRequestStore.clear!
+ it 'cannot be enabled with an invalid password' do
+ subject.enable_admin_mode!(password: nil)
- expect(subject.admin_mode?).to be(false), 'admin mode did not expire in the future'
+ expect(subject.admin_mode?).to be(false)
end
- end
- context 'skipping password validation' do
it 'can be enabled with a valid password' do
- subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
+ subject.enable_admin_mode!(password: user.password)
expect(subject.admin_mode?).to be(true)
end
- it 'can be enabled with an invalid password' do
- subject.enable_admin_mode!(skip_password_validation: true)
+ it 'can be disabled' do
+ subject.enable_admin_mode!(password: user.password)
+ subject.disable_admin_mode!
- expect(subject.admin_mode?).to be(true)
+ expect(subject.admin_mode?).to be(false)
end
- end
- context 'with two independent sessions' do
- let(:another_session) { {} }
- let(:another_subject) { described_class.new(user) }
+ it 'will expire in the future' do
+ subject.enable_admin_mode!(password: user.password)
+ expect(subject.admin_mode?).to be(true), 'admin mode is not active in the present'
- before do
- allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session, another_session])
+ Timecop.freeze(Gitlab::Auth::CurrentUserMode::MAX_ADMIN_MODE_TIME.from_now) do
+ # in the future this will be a new request, simulate by clearing the RequestStore
+ Gitlab::SafeRequestStore.clear!
+
+ expect(subject.admin_mode?).to be(false), 'admin mode did not expire in the future'
+ end
end
- it 'can be enabled in one and seen in the other' do
- Gitlab::Session.with_session(another_session) do
- another_subject.enable_admin_mode!(password: user.password)
+ context 'skipping password validation' do
+ it 'can be enabled with a valid password' do
+ subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
+
+ expect(subject.admin_mode?).to be(true)
end
- expect(subject.admin_mode?).to be(true)
+ it 'can be enabled with an invalid password' do
+ subject.enable_admin_mode!(skip_password_validation: true)
+
+ expect(subject.admin_mode?).to be(true)
+ end
+ end
+
+ context 'with two independent sessions' do
+ let(:another_session) { {} }
+ let(:another_subject) { described_class.new(user) }
+
+ before do
+ allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session, another_session])
+ end
+
+ it 'can be enabled in one and seen in the other' do
+ Gitlab::Session.with_session(another_session) do
+ another_subject.request_admin_mode!
+ another_subject.enable_admin_mode!(password: user.password)
+ end
+
+ expect(subject.admin_mode?).to be(true)
+ end
end
end
end
@@ -134,16 +155,28 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
let(:user) { build(:user, :admin) }
it 'creates a timestamp in the session' do
+ subject.request_admin_mode!
subject.enable_admin_mode!(password: user.password)
expect(session).to include(expected_session_entry(be_within(1.second).of Time.now))
end
end
+ describe '#enable_sessionless_admin_mode!' do
+ let(:user) { build(:user, :admin) }
+
+ it 'enabled admin mode without password' do
+ subject.enable_sessionless_admin_mode!
+
+ expect(subject.admin_mode?).to be(true)
+ end
+ end
+
describe '#disable_admin_mode!' do
let(:user) { build(:user, :admin) }
it 'sets the session timestamp to nil' do
+ subject.request_admin_mode!
subject.disable_admin_mode!
expect(session).to include(expected_session_entry(be_nil))
diff --git a/spec/lib/gitlab/auth/request_authenticator_spec.rb b/spec/lib/gitlab/auth/request_authenticator_spec.rb
index f7fff389d88..4dbcd0df302 100644
--- a/spec/lib/gitlab/auth/request_authenticator_spec.rb
+++ b/spec/lib/gitlab/auth/request_authenticator_spec.rb
@@ -66,4 +66,28 @@ describe Gitlab::Auth::RequestAuthenticator do
expect(subject.find_sessionless_user([:api])).to be_blank
end
end
+
+ describe '#runner' do
+ let!(:runner) { build(:ci_runner) }
+
+ it 'returns the runner using #find_runner_from_token' do
+ expect_any_instance_of(described_class)
+ .to receive(:find_runner_from_token)
+ .and_return(runner)
+
+ expect(subject.runner).to eq runner
+ end
+
+ it 'returns nil if no runner is found' do
+ expect(subject.runner).to be_blank
+ end
+
+ it 'rescue Gitlab::Auth::AuthenticationError exceptions' do
+ expect_any_instance_of(described_class)
+ .to receive(:find_runner_from_token)
+ .and_raise(Gitlab::Auth::UnauthorizedError)
+
+ expect(subject.runner).to be_blank
+ end
+ end
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 26793f28bd8..8d436fb28e0 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -443,6 +443,7 @@ project:
- downstream_project_subscriptions
- service_desk_setting
- import_failures
+- container_expiration_policy
award_emoji:
- awardable
- user
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index fa6bf14bf64..bf8c079f027 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -773,3 +773,13 @@ ZoomMeeting:
ServiceDeskSetting:
- project_id
- issue_template_key
+ContainerExpirationPolicy:
+- created_at
+- updated_at
+- next_run_at
+- project_id
+- name_regex
+- cadence
+- older_than
+- keep_n
+- enabled
diff --git a/spec/models/container_expiration_policy_spec.rb b/spec/models/container_expiration_policy_spec.rb
new file mode 100644
index 00000000000..1ce76490448
--- /dev/null
+++ b/spec/models/container_expiration_policy_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ContainerExpirationPolicy, type: :model do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:project) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:project) }
+
+ describe '#enabled' do
+ it { is_expected.to allow_value(true).for(:enabled) }
+ it { is_expected.to allow_value(false).for(:enabled) }
+ it { is_expected.not_to allow_value(nil).for(:enabled) }
+ end
+
+ describe '#cadence' do
+ it { is_expected.to validate_presence_of(:cadence) }
+
+ it { is_expected.to allow_value('1d').for(:cadence) }
+ it { is_expected.to allow_value('1month').for(:cadence) }
+ it { is_expected.not_to allow_value('123asdf').for(:cadence) }
+ it { is_expected.not_to allow_value(nil).for(:cadence) }
+ end
+
+ describe '#older_than' do
+ it { is_expected.to allow_value('7d').for(:older_than) }
+ it { is_expected.to allow_value('14d').for(:older_than) }
+ it { is_expected.to allow_value(nil).for(:older_than) }
+ it { is_expected.not_to allow_value('123asdf').for(:older_than) }
+ end
+
+ describe '#keep_n' do
+ it { is_expected.to allow_value(10).for(:keep_n) }
+ it { is_expected.to allow_value(nil).for(:keep_n) }
+ it { is_expected.not_to allow_value('foo').for(:keep_n) }
+ end
+ end
+end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index c93e6aafd75..3089afd8d8a 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -199,6 +199,13 @@ describe Namespace do
expect(described_class.find_by_pages_host(host)).to eq(namespace)
end
+
+ it "returns no result if the provided host is not subdomain of the Pages host" do
+ create(:namespace, name: 'namespace.io')
+ host = "namespace.io"
+
+ expect(described_class.find_by_pages_host(host)).to eq(nil)
+ end
end
describe '#ancestors_upto' do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index e7abdf847e1..feb06f4ffc9 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -62,6 +62,7 @@ describe Project do
it { is_expected.to have_one(:external_wiki_service) }
it { is_expected.to have_one(:project_feature) }
it { is_expected.to have_one(:project_repository) }
+ it { is_expected.to have_one(:container_expiration_policy) }
it { is_expected.to have_one(:statistics).class_name('ProjectStatistics') }
it { is_expected.to have_one(:import_data).class_name('ProjectImportData') }
it { is_expected.to have_one(:last_event).class_name('Event') }
@@ -137,6 +138,13 @@ describe Project do
expect(project.ci_cd_settings).to be_persisted
end
+ it 'automatically creates a container expiration policy row' do
+ project = create(:project)
+
+ expect(project.container_expiration_policy).to be_an_instance_of(ContainerExpirationPolicy)
+ expect(project.container_expiration_policy).to be_persisted
+ end
+
it 'automatically creates a Pages metadata row' do
project = create(:project)
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 4f9dfbd9103..9e8aa6a95e8 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -2839,6 +2839,7 @@ describe User, :do_not_mock_admin_mode do
context 'when admin mode is enabled' do
before do
+ Gitlab::Auth::CurrentUserMode.new(user).request_admin_mode!
Gitlab::Auth::CurrentUserMode.new(user).enable_admin_mode!(password: user.password)
end
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index bbfe40041a1..0c53c04ba40 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -146,13 +146,13 @@ describe API::Helpers do
let(:personal_access_token) { create(:personal_access_token, user: user) }
it "returns a 401 response for an invalid token" do
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = 'invalid token'
+ env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = 'invalid token'
expect { current_user }.to raise_error /401/
end
it "returns a 403 response for a user without access" do
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
expect { current_user }.to raise_error /403/
@@ -160,7 +160,7 @@ describe API::Helpers do
it 'returns a 403 response for a user who is blocked' do
user.block!
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect { current_user }.to raise_error /403/
end
@@ -168,7 +168,7 @@ describe API::Helpers do
context 'when terms are enforced' do
before do
enforce_terms
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
end
it 'returns a 403 when a user has not accepted the terms' do
@@ -183,27 +183,27 @@ describe API::Helpers do
end
it "sets current_user" do
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect(current_user).to eq(user)
end
it "does not allow tokens without the appropriate scope" do
personal_access_token = create(:personal_access_token, user: user, scopes: ['read_user'])
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect { current_user }.to raise_error Gitlab::Auth::InsufficientScopeError
end
it 'does not allow revoked tokens' do
personal_access_token.revoke!
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect { current_user }.to raise_error Gitlab::Auth::RevokedError
end
it 'does not allow expired tokens' do
personal_access_token.update!(expires_at: 1.day.ago)
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect { current_user }.to raise_error Gitlab::Auth::ExpiredError
end
@@ -213,7 +213,7 @@ describe API::Helpers do
before do
stub_config_setting(impersonation_enabled: false)
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
end
it 'does not allow impersonation tokens' do
@@ -478,7 +478,7 @@ describe API::Helpers do
context 'passed as param' do
before do
- set_param(Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_PARAM, token.token)
+ set_param(Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_PARAM, token.token)
end
it_behaves_like 'sudo'
@@ -486,7 +486,7 @@ describe API::Helpers do
context 'passed as header' do
before do
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = token.token
+ env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = token.token
end
it_behaves_like 'sudo'
diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb
index 59113687d1c..9e62d84d313 100644
--- a/spec/requests/rack_attack_global_spec.rb
+++ b/spec/requests/rack_attack_global_spec.rb
@@ -100,6 +100,18 @@ describe 'Rack Attack global throttles' do
end
end
+ context 'when the request is authenticated by a runner token' do
+ let(:request_jobs_url) { '/api/v4/jobs/request' }
+ let(:runner) { create(:ci_runner) }
+
+ it 'does not cont as unauthenticated' do
+ (1 + requests_per_period).times do
+ post request_jobs_url, params: { token: runner.token }
+ expect(response).to have_http_status 204
+ end
+ end
+ end
+
it 'logs RackAttack info into structured logs' do
requests_per_period.times do
get url_that_does_not_require_authentication
diff --git a/spec/support/helpers/admin_mode_helpers.rb b/spec/support/helpers/admin_mode_helpers.rb
index de8ffe40536..e995a7d4f5e 100644
--- a/spec/support/helpers/admin_mode_helpers.rb
+++ b/spec/support/helpers/admin_mode_helpers.rb
@@ -3,7 +3,7 @@
# Helper for enabling admin mode in tests
module AdminModeHelper
- # Users are logged in by default in user mode and have to switch to admin
+ # Administrators are logged in by default in user mode and have to switch to admin
# mode for accessing any administrative functionality. This helper lets a user
# be in admin mode without requiring a second authentication step (provided
# the user is an admin)
diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb
index ebba5d8a73c..dbf457a9200 100644
--- a/spec/support/matchers/graphql_matchers.rb
+++ b/spec/support/matchers/graphql_matchers.rb
@@ -28,6 +28,19 @@ RSpec::Matchers.define :have_graphql_fields do |*expected|
end
end
+RSpec::Matchers.define :include_graphql_fields do |*expected|
+ expected_field_names = expected.map { |name| GraphqlHelpers.fieldnamerize(name) }
+
+ match do |kls|
+ expect(kls.fields.keys).to include(*expected_field_names)
+ end
+
+ failure_message do |kls|
+ missing = expected_field_names - kls.fields.keys
+ "is missing fields: <#{missing.inspect}>" if missing.any?
+ end
+end
+
RSpec::Matchers.define :have_graphql_field do |field_name, args = {}|
match do |kls|
field = kls.fields[GraphqlHelpers.fieldnamerize(field_name)]
diff --git a/spec/views/admin/sessions/new.html.haml_spec.rb b/spec/views/admin/sessions/new.html.haml_spec.rb
index 57255748988..b3208296c80 100644
--- a/spec/views/admin/sessions/new.html.haml_spec.rb
+++ b/spec/views/admin/sessions/new.html.haml_spec.rb
@@ -3,29 +3,44 @@
require 'spec_helper'
describe 'admin/sessions/new.html.haml' do
- context 'admin has password set' do
- before do
- allow(view).to receive(:password_authentication_enabled_for_web?).and_return(true)
- end
+ let(:user) { create(:admin) }
+
+ before do
+ allow(view).to receive(:current_user).and_return(user)
+ allow(view).to receive(:omniauth_enabled?).and_return(false)
+ end
- it "shows enter password form" do
+ context 'internal admin user' do
+ it 'shows enter password form' do
render
expect(rendered).to have_css('#login-pane.active')
expect(rendered).to have_selector('input[name="password"]')
end
+
+ it 'warns authentication not possible if password not set' do
+ allow(user).to receive(:require_password_creation_for_web?).and_return(true)
+
+ render
+
+ expect(rendered).not_to have_css('#login-pane')
+ expect(rendered).to have_content _('No authentication methods configured.')
+ end
end
- context 'admin has no password set' do
+ context 'omniauth authentication enabled' do
before do
- allow(view).to receive(:password_authentication_enabled_for_web?).and_return(false)
+ allow(view).to receive(:omniauth_enabled?).and_return(true)
+ allow(view).to receive(:button_based_providers_enabled?).and_return(true)
end
- it "warns authentication not possible" do
+ it 'shows omniauth form' do
render
- expect(rendered).not_to have_css('#login-pane')
- expect(rendered).to have_content 'No authentication methods configured'
+ expect(rendered).to have_css('.omniauth-container')
+ expect(rendered).to have_content _('Sign in with')
+
+ expect(rendered).not_to have_content _('No authentication methods configured.')
end
end
end
diff --git a/spec/views/layouts/application.html.haml_spec.rb b/spec/views/layouts/application.html.haml_spec.rb
index bdd4a97a1f5..4270bbf1924 100644
--- a/spec/views/layouts/application.html.haml_spec.rb
+++ b/spec/views/layouts/application.html.haml_spec.rb
@@ -11,6 +11,7 @@ describe 'layouts/application' do
allow(view).to receive(:session).and_return({})
allow(view).to receive(:user_signed_in?).and_return(true)
allow(view).to receive(:current_user).and_return(user)
+ allow(view).to receive(:current_user_mode).and_return(Gitlab::Auth::CurrentUserMode.new(user))
end
context 'body data elements for pageview context' do