From 3feda79a555559174b585f8d55a758650737e5c9 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 9 Mar 2022 06:07:38 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- spec/controllers/application_controller_spec.rb | 17 +++++ .../controllers/projects/issues_controller_spec.rb | 29 --------- .../filtered_search/dropdown_assignee_spec.rb | 73 ++++++++++++++++++++-- .../header_search_autocomplete_items_spec.js | 13 +++- spec/frontend/header_search/store/actions_spec.js | 16 ++--- .../frontend/header_search/store/mutations_spec.js | 4 ++ .../assignees/sidebar_assignees_widget_spec.js | 14 +---- spec/frontend/sidebar/sidebar_assignees_spec.js | 31 +++------ spec/requests/api/users_spec.rb | 12 ++++ spec/services/issues/update_service_spec.rb | 28 ++------- 10 files changed, 131 insertions(+), 106 deletions(-) (limited to 'spec') diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index d49d4947cd5..ddd80b67639 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -1059,15 +1059,25 @@ RSpec.describe ApplicationController do describe 'setting permissions-policy header' do controller do skip_before_action :authenticate_user! + before_action :redirect_to_example, only: [:redirect] def index render html: 'It is a flock of sheep, not a floc of sheep.' end + + def redirect + raise 'Should not be reached' + end + + def redirect_to_example + redirect_to('https://example.com') + end end before do routes.draw do get 'index' => 'anonymous#index' + get 'redirect' => 'anonymous#redirect' end end @@ -1093,6 +1103,13 @@ RSpec.describe ApplicationController do expect(response.headers['Permissions-Policy']).to eq('interest-cohort=()') end + + it 'sets the Permissions-Policy header even when redirected before_action' do + get :redirect + + expect(response).to have_gitlab_http_status(:redirect) + expect(response.headers['Permissions-Policy']).to eq('interest-cohort=()') + end end end end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 00985dbd77f..73c83ed0ed4 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1065,35 +1065,6 @@ RSpec.describe Projects::IssuesController do .not_to exceed_query_limit(control_count + 2 * labels.count) end - context 'real-time sidebar feature flag' do - let_it_be(:project) { create(:project, :public) } - let_it_be(:issue) { create(:issue, project: project) } - - context 'when enabled' do - before do - stub_feature_flags(real_time_issue_sidebar: true) - end - - it 'pushes the correct value to the frontend' do - go(id: issue.to_param) - - expect(Gon.features).to include('realTimeIssueSidebar' => true) - end - end - - context 'when disabled' do - before do - stub_feature_flags(real_time_issue_sidebar: false) - end - - it 'pushes the correct value to the frontend' do - go(id: issue.to_param) - - expect(Gon.features).to include('realTimeIssueSidebar' => false) - end - end - end - it 'logs the view with Gitlab::Search::RecentIssues' do sign_in(user) recent_issues_double = instance_double(::Gitlab::Search::RecentIssues, log_view: nil) diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb index e873ebb21c4..3ba2f7e788d 100644 --- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb @@ -12,14 +12,14 @@ RSpec.describe 'Dropdown assignee', :js do let(:js_dropdown_assignee) { '#js-dropdown-assignee' } let(:filter_dropdown) { find("#{js_dropdown_assignee} .filter-dropdown") } - before do - project.add_maintainer(user) - sign_in(user) + describe 'behavior' do + before do + project.add_maintainer(user) + sign_in(user) - visit project_issues_path(project) - end + visit project_issues_path(project) + end - describe 'behavior' do it 'loads all the assignees when opened' do input_filtered_search('assignee:=', submit: false, extra_space: false) @@ -35,6 +35,11 @@ RSpec.describe 'Dropdown assignee', :js do describe 'selecting from dropdown without Ajax call' do before do + project.add_maintainer(user) + sign_in(user) + + visit project_issues_path(project) + Gitlab::Testing::RequestBlockerMiddleware.block_requests! input_filtered_search('assignee:=', submit: false, extra_space: false) end @@ -51,4 +56,60 @@ RSpec.describe 'Dropdown assignee', :js do expect_filtered_search_input_empty end end + + context 'assignee suggestions' do + let!(:group) { create(:group) } + let!(:group_project) { create(:project, namespace: group) } + let!(:group_user) { create(:user) } + + let!(:subgroup) { create(:group, parent: group) } + let!(:subgroup_project) { create(:project, namespace: subgroup) } + let!(:subgroup_project_issue) { create(:issue, project: subgroup_project) } + let!(:subgroup_user) { create(:user) } + + let!(:subsubgroup) { create(:group, parent: subgroup) } + let!(:subsubgroup_project) { create(:project, namespace: subsubgroup) } + let!(:subsubgroup_user) { create(:user) } + + let!(:invited_to_group_group) { create(:group) } + let!(:invited_to_group_group_user) { create(:user) } + + let!(:invited_to_project_group) { create(:group) } + let!(:invited_to_project_group_user) { create(:user) } + + before do + group.add_developer(group_user) + subgroup.add_developer(subgroup_user) + subsubgroup.add_developer(subsubgroup_user) + invited_to_group_group.add_developer(invited_to_group_group_user) + invited_to_project_group.add_developer(invited_to_project_group_user) + + create(:group_group_link, shared_group: subgroup, shared_with_group: invited_to_group_group) + create(:project_group_link, project: subgroup_project, group: invited_to_project_group) + + sign_in(subgroup_user) + end + + it 'shows inherited, direct, and invited group members but not descendent members', :aggregate_failures do + visit issues_group_path(subgroup) + + input_filtered_search('assignee:=', submit: false, extra_space: false) + + expect(page).to have_text group_user.name + expect(page).to have_text subgroup_user.name + expect(page).to have_text invited_to_group_group_user.name + expect(page).not_to have_text subsubgroup_user.name + expect(page).not_to have_text invited_to_project_group_user.name + + visit project_issues_path(subgroup_project) + + input_filtered_search('assignee:=', submit: false, extra_space: false) + + expect(page).to have_text group_user.name + expect(page).to have_text subgroup_user.name + expect(page).to have_text invited_to_project_group_user.name + expect(page).not_to have_text subsubgroup_user.name + expect(page).not_to have_text invited_to_group_group_user.name + end + end end diff --git a/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js b/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js index 502f10ff771..f427482be46 100644 --- a/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js +++ b/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js @@ -1,4 +1,4 @@ -import { GlDropdownItem, GlLoadingIcon, GlAvatar } from '@gitlab/ui'; +import { GlDropdownItem, GlLoadingIcon, GlAvatar, GlAlert } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; @@ -46,6 +46,7 @@ describe('HeaderSearchAutocompleteItems', () => { const findDropdownItemLinks = () => findDropdownItems().wrappers.map((w) => w.attributes('href')); const findGlLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findGlAvatar = () => wrapper.findComponent(GlAvatar); + const findGlAlert = () => wrapper.findComponent(GlAlert); describe('template', () => { describe('when loading is true', () => { @@ -62,6 +63,15 @@ describe('HeaderSearchAutocompleteItems', () => { }); }); + describe('when api returns error', () => { + beforeEach(() => { + createComponent({ autocompleteError: true }); + }); + + it('renders Alert', () => { + expect(findGlAlert().exists()).toBe(true); + }); + }); describe('when loading is false', () => { beforeEach(() => { createComponent({ loading: false }); @@ -86,6 +96,7 @@ describe('HeaderSearchAutocompleteItems', () => { expect(findDropdownItemLinks()).toStrictEqual(expectedLinks); }); }); + describe.each` item | showAvatar | avatarSize ${{ data: [{ category: PROJECTS_CATEGORY, avatar_url: null }] }} | ${true} | ${String(LARGE_AVATAR_PX)} diff --git a/spec/frontend/header_search/store/actions_spec.js b/spec/frontend/header_search/store/actions_spec.js index 6599115f017..1748d89a6d3 100644 --- a/spec/frontend/header_search/store/actions_spec.js +++ b/spec/frontend/header_search/store/actions_spec.js @@ -1,6 +1,5 @@ import MockAdapter from 'axios-mock-adapter'; import testAction from 'helpers/vuex_action_helper'; -import createFlash from '~/flash'; import * as actions from '~/header_search/store/actions'; import * as types from '~/header_search/store/mutation_types'; import createState from '~/header_search/store/state'; @@ -13,11 +12,6 @@ describe('Header Search Store Actions', () => { let state; let mock; - const flashCallback = (callCount) => { - expect(createFlash).toHaveBeenCalledTimes(callCount); - createFlash.mockClear(); - }; - beforeEach(() => { state = createState({}); mock = new MockAdapter(axios); @@ -29,10 +23,10 @@ describe('Header Search Store Actions', () => { }); describe.each` - axiosMock | type | expectedMutations | flashCallCount - ${{ method: 'onGet', code: 200, res: MOCK_AUTOCOMPLETE_OPTIONS_RES }} | ${'success'} | ${[{ type: types.REQUEST_AUTOCOMPLETE }, { type: types.RECEIVE_AUTOCOMPLETE_SUCCESS, payload: MOCK_AUTOCOMPLETE_OPTIONS_RES }]} | ${0} - ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_AUTOCOMPLETE }, { type: types.RECEIVE_AUTOCOMPLETE_ERROR }]} | ${1} - `('fetchAutocompleteOptions', ({ axiosMock, type, expectedMutations, flashCallCount }) => { + axiosMock | type | expectedMutations + ${{ method: 'onGet', code: 200, res: MOCK_AUTOCOMPLETE_OPTIONS_RES }} | ${'success'} | ${[{ type: types.REQUEST_AUTOCOMPLETE }, { type: types.RECEIVE_AUTOCOMPLETE_SUCCESS, payload: MOCK_AUTOCOMPLETE_OPTIONS_RES }]} + ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_AUTOCOMPLETE }, { type: types.RECEIVE_AUTOCOMPLETE_ERROR }]} + `('fetchAutocompleteOptions', ({ axiosMock, type, expectedMutations }) => { describe(`on ${type}`, () => { beforeEach(() => { mock[axiosMock.method]().replyOnce(axiosMock.code, axiosMock.res); @@ -42,7 +36,7 @@ describe('Header Search Store Actions', () => { action: actions.fetchAutocompleteOptions, state, expectedMutations, - }).then(() => flashCallback(flashCallCount)); + }); }); }); }); diff --git a/spec/frontend/header_search/store/mutations_spec.js b/spec/frontend/header_search/store/mutations_spec.js index 7bcf8e49118..e3c15ded948 100644 --- a/spec/frontend/header_search/store/mutations_spec.js +++ b/spec/frontend/header_search/store/mutations_spec.js @@ -20,6 +20,7 @@ describe('Header Search Store Mutations', () => { expect(state.loading).toBe(true); expect(state.autocompleteOptions).toStrictEqual([]); + expect(state.autocompleteError).toBe(false); }); }); @@ -29,6 +30,7 @@ describe('Header Search Store Mutations', () => { expect(state.loading).toBe(false); expect(state.autocompleteOptions).toStrictEqual(MOCK_AUTOCOMPLETE_OPTIONS); + expect(state.autocompleteError).toBe(false); }); }); @@ -38,6 +40,7 @@ describe('Header Search Store Mutations', () => { expect(state.loading).toBe(false); expect(state.autocompleteOptions).toStrictEqual([]); + expect(state.autocompleteError).toBe(true); }); }); @@ -46,6 +49,7 @@ describe('Header Search Store Mutations', () => { mutations[types.CLEAR_AUTOCOMPLETE](state); expect(state.autocompleteOptions).toStrictEqual([]); + expect(state.autocompleteError).toBe(false); }); }); diff --git a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js index def46255994..db1cffbd2cb 100644 --- a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js +++ b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js @@ -340,21 +340,9 @@ describe('Sidebar assignees widget', () => { }); }); - it('when realtime feature flag is disabled', async () => { + it('includes the real-time assignees component', async () => { createComponent(); await waitForPromises(); - expect(findRealtimeAssignees().exists()).toBe(false); - }); - - it('when realtime feature flag is enabled', async () => { - createComponent({ - provide: { - glFeatures: { - realTimeIssueSidebar: true, - }, - }, - }); - await waitForPromises(); expect(findRealtimeAssignees().exists()).toBe(true); }); diff --git a/spec/frontend/sidebar/sidebar_assignees_spec.js b/spec/frontend/sidebar/sidebar_assignees_spec.js index 5f77e21c1f8..68d20060c37 100644 --- a/spec/frontend/sidebar/sidebar_assignees_spec.js +++ b/spec/frontend/sidebar/sidebar_assignees_spec.js @@ -14,7 +14,7 @@ describe('sidebar assignees', () => { let wrapper; let mediator; let axiosMock; - const createComponent = (realTimeIssueSidebar = false, props) => { + const createComponent = (props) => { wrapper = shallowMount(SidebarAssignees, { propsData: { issuableIid: '1', @@ -25,11 +25,6 @@ describe('sidebar assignees', () => { changing: false, ...props, }, - provide: { - glFeatures: { - realTimeIssueSidebar, - }, - }, // Attaching to document is required because this component emits something from the parent element :/ attachTo: document.body, }); @@ -86,27 +81,17 @@ describe('sidebar assignees', () => { expect(wrapper.find(Assigness).exists()).toBe(true); }); - describe('when realTimeIssueSidebar is turned on', () => { - describe('when issuableType is issue', () => { - it('finds AssigneesRealtime componeont', () => { - createComponent(true); - - expect(wrapper.find(AssigneesRealtime).exists()).toBe(true); - }); - }); - - describe('when issuableType is MR', () => { - it('does not find AssigneesRealtime componeont', () => { - createComponent(true, { issuableType: 'MR' }); + describe('when issuableType is issue', () => { + it('finds AssigneesRealtime component', () => { + createComponent(); - expect(wrapper.find(AssigneesRealtime).exists()).toBe(false); - }); + expect(wrapper.find(AssigneesRealtime).exists()).toBe(true); }); }); - describe('when realTimeIssueSidebar is turned off', () => { - it('does not find AssigneesRealtime', () => { - createComponent(false, { issuableType: 'issue' }); + describe('when issuableType is MR', () => { + it('does not find AssigneesRealtime component', () => { + createComponent({ issuableType: 'MR' }); expect(wrapper.find(AssigneesRealtime).exists()).toBe(false); }); diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 7f5b6661695..2d71674273b 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -3117,6 +3117,18 @@ RSpec.describe API::Users do expect(response.body).to eq('null') end end + + context 'with the API initiating user' do + let(:user_id) { admin.id } + + it 'does not block the API initiating user, returns 403' do + block_user + + expect(response).to have_gitlab_http_status(:forbidden) + expect(json_response['message']).to eq('403 Forbidden - The API initiating user cannot be blocked by the API') + expect(admin.reload.state).to eq('active') + end + end end it 'is not available for non admin users' do diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 92e45194ad1..6d3c3dd4e39 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -1332,32 +1332,14 @@ RSpec.describe Issues::UpdateService, :mailer do context 'broadcasting issue assignee updates' do let(:update_params) { { assignee_ids: [user2.id] } } - context 'when feature flag is enabled' do - before do - stub_feature_flags(broadcast_issue_updates: true) - end - - it 'triggers the GraphQL subscription' do - expect(GraphqlTriggers).to receive(:issuable_assignees_updated).with(issue) - - update_issue(update_params) - end + it 'triggers the GraphQL subscription' do + expect(GraphqlTriggers).to receive(:issuable_assignees_updated).with(issue) - context 'when assignee is not updated' do - let(:update_params) { { title: 'Some other title' } } - - it 'does not trigger the GraphQL subscription' do - expect(GraphqlTriggers).not_to receive(:issuable_assignees_updated).with(issue) - - update_issue(update_params) - end - end + update_issue(update_params) end - context 'when feature flag is disabled' do - before do - stub_feature_flags(broadcast_issue_updates: false) - end + context 'when assignee is not updated' do + let(:update_params) { { title: 'Some other title' } } it 'does not trigger the GraphQL subscription' do expect(GraphqlTriggers).not_to receive(:issuable_assignees_updated).with(issue) -- cgit v1.2.3