diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-08 18:09:34 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-08 18:09:34 +0300 |
commit | b5bdf6e5219b3b57107aee49ba7c103affb65dd9 (patch) | |
tree | 54c1ea8b3140d60af9a6c64867edc0a484ef7735 /spec | |
parent | 81f062b841f6062601662061850934a51e77ceea (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
20 files changed, 501 insertions, 409 deletions
diff --git a/spec/controllers/groups/settings/repository_controller_spec.rb b/spec/controllers/groups/settings/repository_controller_spec.rb index cbf55218b94..73a205069f5 100644 --- a/spec/controllers/groups/settings/repository_controller_spec.rb +++ b/spec/controllers/groups/settings/repository_controller_spec.rb @@ -13,88 +13,73 @@ RSpec.describe Groups::Settings::RepositoryController do end describe 'POST create_deploy_token' do - context 'when ajax_new_deploy_token feature flag is disabled for the project' do - before do - stub_feature_flags(ajax_new_deploy_token: false) - entity.add_owner(user) - end + let(:good_deploy_token_params) do + { + name: 'name', + expires_at: 1.day.from_now.to_s, + username: 'deployer', + read_repository: '1', + deploy_token_type: DeployToken.deploy_token_types[:group_type] + } + end - it_behaves_like 'a created deploy token' do - let(:entity) { group } - let(:create_entity_params) { { group_id: group } } - let(:deploy_token_type) { DeployToken.deploy_token_types[:group_type] } - end + let(:request_params) do + { + group_id: group.to_param, + deploy_token: deploy_token_params + } end - context 'when ajax_new_deploy_token feature flag is enabled for the project' do - let(:good_deploy_token_params) do - { - name: 'name', - expires_at: 1.day.from_now.to_s, - username: 'deployer', - read_repository: '1', - deploy_token_type: DeployToken.deploy_token_types[:group_type] - } - end + before do + group.add_owner(user) + end + + subject { post :create_deploy_token, params: request_params, format: :json } - let(:request_params) do + context('a good request') do + let(:deploy_token_params) { good_deploy_token_params } + let(:expected_response) do { - group_id: group.to_param, - deploy_token: deploy_token_params + 'id' => be_a(Integer), + 'name' => deploy_token_params[:name], + 'username' => deploy_token_params[:username], + 'expires_at' => Time.zone.parse(deploy_token_params[:expires_at]), + 'token' => be_a(String), + 'expired' => false, + 'revoked' => false, + 'scopes' => deploy_token_params.inject([]) do |scopes, kv| + key, value = kv + key.to_s.start_with?('read_') && value.to_i != 0 ? scopes << key.to_s : scopes + end } end - before do - group.add_owner(user) - end + it 'creates the deploy token' do + subject - subject { post :create_deploy_token, params: request_params, format: :json } - - context('a good request') do - let(:deploy_token_params) { good_deploy_token_params } - let(:expected_response) do - { - 'id' => be_a(Integer), - 'name' => deploy_token_params[:name], - 'username' => deploy_token_params[:username], - 'expires_at' => Time.zone.parse(deploy_token_params[:expires_at]), - 'token' => be_a(String), - 'expired' => false, - 'revoked' => false, - 'scopes' => deploy_token_params.inject([]) do |scopes, kv| - key, value = kv - key.to_s.start_with?('read_') && value.to_i != 0 ? scopes << key.to_s : scopes - end - } - end - - it 'creates the deploy token' do - subject - - expect(response).to have_gitlab_http_status(:created) - expect(response).to match_response_schema('public_api/v4/deploy_token') - expect(json_response).to match(expected_response) - end + expect(response).to have_gitlab_http_status(:created) + expect(response).to match_response_schema('public_api/v4/deploy_token') + expect(json_response).to match(expected_response) end + end - context('a bad request') do - let(:deploy_token_params) { good_deploy_token_params.except(:read_repository) } - let(:expected_response) { { 'message' => "Scopes can't be blank" } } + context('a bad request') do + let(:deploy_token_params) { good_deploy_token_params.except(:read_repository) } + let(:expected_response) { { 'message' => "Scopes can't be blank" } } - it 'does not create the deploy token' do - subject + it 'does not create the deploy token' do + subject - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response).to match(expected_response) - end + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response).to match(expected_response) end + end - context('an invalid request') do - let(:deploy_token_params) { good_deploy_token_params.except(:name) } + context('an invalid request') do + let(:deploy_token_params) { good_deploy_token_params.except(:name) } - it 'raises a validation error' do - expect { subject }.to raise_error(ActiveRecord::StatementInvalid) - end + it 'raises a validation error' do + expect { subject }.to raise_error(ActiveRecord::StatementInvalid) end end end diff --git a/spec/controllers/projects/artifacts_controller_spec.rb b/spec/controllers/projects/artifacts_controller_spec.rb index 2d145b6ff1d..81c1d4acd36 100644 --- a/spec/controllers/projects/artifacts_controller_spec.rb +++ b/spec/controllers/projects/artifacts_controller_spec.rb @@ -30,28 +30,10 @@ RSpec.describe Projects::ArtifactsController do stub_feature_flags(artifacts_management_page: true) end - it 'sets the artifacts variable' do + it 'renders the page' do subject - expect(assigns(:artifacts)).to contain_exactly(*project.job_artifacts) - end - - it 'sets the total size variable' do - subject - - expect(assigns(:total_size)).to eq(project.job_artifacts.total_size) - end - - describe 'pagination' do - before do - stub_const("#{described_class}::MAX_PER_PAGE", 1) - end - - it 'paginates artifacts' do - subject - - expect(assigns(:artifacts)).to contain_exactly(project.reload.job_artifacts.last) - end + expect(response).to have_gitlab_http_status(:ok) end end @@ -65,18 +47,6 @@ RSpec.describe Projects::ArtifactsController do expect(response).to have_gitlab_http_status(:no_content) end - - it 'does not set the artifacts variable' do - subject - - expect(assigns(:artifacts)).to eq(nil) - end - - it 'does not set the total size variable' do - subject - - expect(assigns(:total_size)).to eq(nil) - end end end diff --git a/spec/controllers/projects/settings/repository_controller_spec.rb b/spec/controllers/projects/settings/repository_controller_spec.rb index 6e04e3991ab..ea50ff6caa0 100644 --- a/spec/controllers/projects/settings/repository_controller_spec.rb +++ b/spec/controllers/projects/settings/repository_controller_spec.rb @@ -51,78 +51,64 @@ RSpec.describe Projects::Settings::RepositoryController do end describe 'POST create_deploy_token' do - context 'when ajax_new_deploy_token feature flag is disabled for the project' do - before do - stub_feature_flags(ajax_new_deploy_token: false) - end - - it_behaves_like 'a created deploy token' do - let(:entity) { project } - let(:create_entity_params) { base_params } - let(:deploy_token_type) { DeployToken.deploy_token_types[:project_type] } - end + let(:good_deploy_token_params) do + { + name: 'name', + expires_at: 1.day.from_now.to_s, + username: 'deployer', + read_repository: '1', + deploy_token_type: DeployToken.deploy_token_types[:project_type] + } end - context 'when ajax_new_deploy_token feature flag is enabled for the project' do - let(:good_deploy_token_params) do + let(:request_params) { base_params.merge({ deploy_token: deploy_token_params }) } + + subject { post :create_deploy_token, params: request_params, format: :json } + + context('a good request') do + let(:deploy_token_params) { good_deploy_token_params } + let(:expected_response) do { - name: 'name', - expires_at: 1.day.from_now.to_s, - username: 'deployer', - read_repository: '1', - deploy_token_type: DeployToken.deploy_token_types[:project_type] + 'id' => be_a(Integer), + 'name' => deploy_token_params[:name], + 'username' => deploy_token_params[:username], + 'expires_at' => Time.zone.parse(deploy_token_params[:expires_at]), + 'token' => be_a(String), + 'expired' => false, + 'revoked' => false, + 'scopes' => deploy_token_params.inject([]) do |scopes, kv| + key, value = kv + key.to_s.start_with?('read_') && value.to_i != 0 ? scopes << key.to_s : scopes + end } end - let(:request_params) { base_params.merge({ deploy_token: deploy_token_params }) } - - subject { post :create_deploy_token, params: request_params, format: :json } - - context('a good request') do - let(:deploy_token_params) { good_deploy_token_params } - let(:expected_response) do - { - 'id' => be_a(Integer), - 'name' => deploy_token_params[:name], - 'username' => deploy_token_params[:username], - 'expires_at' => Time.zone.parse(deploy_token_params[:expires_at]), - 'token' => be_a(String), - 'expired' => false, - 'revoked' => false, - 'scopes' => deploy_token_params.inject([]) do |scopes, kv| - key, value = kv - key.to_s.start_with?('read_') && value.to_i != 0 ? scopes << key.to_s : scopes - end - } - end + it 'creates the deploy token' do + subject - it 'creates the deploy token' do - subject - - expect(response).to have_gitlab_http_status(:created) - expect(response).to match_response_schema('public_api/v4/deploy_token') - expect(json_response).to match(expected_response) - end + expect(response).to have_gitlab_http_status(:created) + expect(response).to match_response_schema('public_api/v4/deploy_token') + expect(json_response).to match(expected_response) end + end - context('a bad request') do - let(:deploy_token_params) { good_deploy_token_params.except(:read_repository) } - let(:expected_response) { { 'message' => "Scopes can't be blank" } } + context('a bad request') do + let(:deploy_token_params) { good_deploy_token_params.except(:read_repository) } + let(:expected_response) { { 'message' => "Scopes can't be blank" } } - it 'does not create the deploy token' do - subject + it 'does not create the deploy token' do + subject - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response).to match(expected_response) - end + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response).to match(expected_response) end + end - context('an invalid request') do - let(:deploy_token_params) { good_deploy_token_params.except(:name) } + context('an invalid request') do + let(:deploy_token_params) { good_deploy_token_params.except(:name) } - it 'raises a validation error' do - expect { subject }.to raise_error(ActiveRecord::StatementInvalid) - end + it 'raises a validation error' do + expect { subject }.to raise_error(ActiveRecord::StatementInvalid) end end end diff --git a/spec/features/groups/settings/repository_spec.rb b/spec/features/groups/settings/repository_spec.rb index f6b8bbdd35f..cd7dcbdb28d 100644 --- a/spec/features/groups/settings/repository_spec.rb +++ b/spec/features/groups/settings/repository_spec.rb @@ -23,26 +23,9 @@ RSpec.describe 'Group Repository settings', :js do stub_container_registry_config(enabled: true) end - context 'when ajax deploy tokens is enabled' do - before do - stub_feature_flags(ajax_new_deploy_token: true) - end - - it_behaves_like 'a deploy token in settings' do - let(:entity_type) { 'group' } - let(:page_path) { group_settings_repository_path(group) } - end - end - - context 'when ajax deploy tokens is disabled' do - before do - stub_feature_flags(ajax_new_deploy_token: false) - end - - it_behaves_like 'a deploy token in settings' do - let(:entity_type) { 'group' } - let(:page_path) { group_settings_repository_path(group) } - end + it_behaves_like 'a deploy token in settings' do + let(:entity_type) { 'group' } + let(:page_path) { group_settings_repository_path(group) } end end diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb index d9bdbf7aa1a..b25ae80b3c3 100644 --- a/spec/features/projects/settings/repository_settings_spec.rb +++ b/spec/features/projects/settings/repository_settings_spec.rb @@ -30,7 +30,6 @@ RSpec.describe 'Projects > Settings > Repository settings' do before do stub_container_registry_config(enabled: true) - stub_feature_flags(ajax_new_deploy_token: project) end it_behaves_like 'a deploy token in settings' do diff --git a/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb b/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb index eed3494ef5b..47383be1ba1 100644 --- a/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb +++ b/spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb @@ -11,7 +11,6 @@ RSpec.describe 'Repository Settings > User sees revoke deploy token modal', :js before do project.add_role(user, role) sign_in(user) - stub_feature_flags(ajax_new_deploy_token: project) visit(project_settings_repository_path(project)) click_button('Revoke') end diff --git a/spec/frontend/boards/board_card_inner_spec.js b/spec/frontend/boards/board_card_inner_spec.js index 3ebc51c4bcb..3b77c999a9e 100644 --- a/spec/frontend/boards/board_card_inner_spec.js +++ b/spec/frontend/boards/board_card_inner_spec.js @@ -7,7 +7,6 @@ import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import IssuableBlockedIcon from '~/vue_shared/components/issuable_blocked_icon/issuable_blocked_icon.vue'; import BoardCardInner from '~/boards/components/board_card_inner.vue'; -import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue'; import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue'; import { issuableTypes } from '~/boards/constants'; import eventHub from '~/boards/eventhub'; @@ -49,7 +48,6 @@ describe('Board card component', () => { const findEpicCountablesTotalWeight = () => wrapper.findByTestId('epic-countables-total-weight'); const findEpicProgressTooltip = () => wrapper.findByTestId('epic-progress-tooltip-content'); const findHiddenIssueIcon = () => wrapper.findByTestId('hidden-icon'); - const findMoveToPositionComponent = () => wrapper.findComponent(BoardCardMoveToPosition); const findWorkItemIcon = () => wrapper.findComponent(WorkItemTypeIcon); const performSearchMock = jest.fn(); @@ -143,10 +141,6 @@ describe('Board card component', () => { expect(findHiddenIssueIcon().exists()).toBe(false); }); - it('renders the move to position icon', () => { - expect(findMoveToPositionComponent().exists()).toBe(true); - }); - it('does not render the work type icon by default', () => { expect(findWorkItemIcon().exists()).toBe(false); }); @@ -595,10 +589,5 @@ describe('Board card component', () => { expect(findEpicCountablesTotalWeight().text()).toBe('15'); expect(findEpicProgressTooltip().text()).toBe('10 of 15 weight completed'); }); - - it('does not render the move to position icon', () => { - createWrapper(); - expect(findMoveToPositionComponent().exists()).toBe(false); - }); }); }); diff --git a/spec/frontend/boards/board_list_spec.js b/spec/frontend/boards/board_list_spec.js index 9b0c0b93ffb..3a2beb714e9 100644 --- a/spec/frontend/boards/board_list_spec.js +++ b/spec/frontend/boards/board_list_spec.js @@ -6,6 +6,7 @@ import waitForPromises from 'helpers/wait_for_promises'; import createComponent from 'jest/boards/board_list_helper'; import BoardCard from '~/boards/components/board_card.vue'; import eventHub from '~/boards/eventhub'; +import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue'; import { mockIssues } from './mock_data'; @@ -15,6 +16,7 @@ describe('Board list component', () => { const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`); const findIssueCountLoadingIcon = () => wrapper.find('[data-testid="count-loading-icon"]'); const findDraggable = () => wrapper.findComponent(Draggable); + const findMoveToPositionComponent = () => wrapper.findComponent(BoardCardMoveToPosition); const startDrag = ( params = { @@ -99,6 +101,10 @@ describe('Board list component', () => { await nextTick(); expect(wrapper.find('.board-list-count').attributes('data-issue-id')).toBe('-1'); }); + + it('renders the move to position icon', () => { + expect(findMoveToPositionComponent().exists()).toBe(true); + }); }); describe('load more issues', () => { diff --git a/spec/frontend/boards/components/board_card_move_to_position_spec.js b/spec/frontend/boards/components/board_card_move_to_position_spec.js index 7254b9486ef..8dee3c77787 100644 --- a/spec/frontend/boards/components/board_card_move_to_position_spec.js +++ b/spec/frontend/boards/components/board_card_move_to_position_spec.js @@ -48,6 +48,7 @@ describe('Board Card Move to position', () => { propsData: { item: mockIssue2, list: mockList, + listItemsLength: 3, index: 0, ...propsData, }, diff --git a/spec/frontend/search/mock_data.js b/spec/frontend/search/mock_data.js index 0542e96c77c..fa5ccfeb478 100644 --- a/spec/frontend/search/mock_data.js +++ b/spec/frontend/search/mock_data.js @@ -107,3 +107,87 @@ export const PROMISE_ALL_EXPECTED_MUTATIONS = { payload: { key: PROJECTS_LOCAL_STORAGE_KEY, data: [MOCK_FRESH_DATA_RES, MOCK_FRESH_DATA_RES] }, }, }; + +export const MOCK_NAVIGATION = { + projects: { + label: 'Projects', + scope: 'projects', + link: '/search?scope=projects&search=et', + count_link: '/search/count?scope=projects&search=et', + }, + blobs: { + label: 'Code', + scope: 'blobs', + link: '/search?scope=blobs&search=et', + count_link: '/search/count?scope=blobs&search=et', + }, + issues: { + label: 'Issues', + scope: 'issues', + link: '/search?scope=issues&search=et', + active: true, + count: '2,430', + }, + merge_requests: { + label: 'Merge requests', + scope: 'merge_requests', + link: '/search?scope=merge_requests&search=et', + count_link: '/search/count?scope=merge_requests&search=et', + }, + wiki_blobs: { + label: 'Wiki', + scope: 'wiki_blobs', + link: '/search?scope=wiki_blobs&search=et', + count_link: '/search/count?scope=wiki_blobs&search=et', + }, + commits: { + label: 'Commits', + scope: 'commits', + link: '/search?scope=commits&search=et', + count_link: '/search/count?scope=commits&search=et', + }, + notes: { + label: 'Comments', + scope: 'notes', + link: '/search?scope=notes&search=et', + count_link: '/search/count?scope=notes&search=et', + }, + milestones: { + label: 'Milestones', + scope: 'milestones', + link: '/search?scope=milestones&search=et', + count_link: '/search/count?scope=milestones&search=et', + }, + users: { + label: 'Users', + scope: 'users', + link: '/search?scope=users&search=et', + count_link: '/search/count?scope=users&search=et', + }, +}; + +export const MOCK_NAVIGATION_DATA = { + projects: { + label: 'Projects', + scope: 'projects', + link: '/search?scope=projects&search=et', + count_link: '/search/count?scope=projects&search=et', + }, +}; + +export const MOCK_ENDPOINT_RESPONSE = { count: '13' }; + +export const MOCK_DATA_FOR_NAVIGATION_ACTION_MUTATION = { + projects: { + count: '13', + label: 'Projects', + scope: 'projects', + link: '/search?scope=projects&search=et', + count_link: '/search/count?scope=projects&search=et', + }, +}; + +export const MOCK_NAVIGATION_ACTION_MUTATION = { + type: types.RECEIVE_NAVIGATION_COUNT, + payload: { key: 'projects', count: '13' }, +}; diff --git a/spec/frontend/search/sidebar/components/app_spec.js b/spec/frontend/search/sidebar/components/app_spec.js index 89959feec39..b4e3388f067 100644 --- a/spec/frontend/search/sidebar/components/app_spec.js +++ b/spec/frontend/search/sidebar/components/app_spec.js @@ -1,11 +1,9 @@ -import { GlButton, GlLink } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import Vue from 'vue'; import Vuex from 'vuex'; import { MOCK_QUERY } from 'jest/search/mock_data'; import GlobalSearchSidebar from '~/search/sidebar/components/app.vue'; -import ConfidentialityFilter from '~/search/sidebar/components/confidentiality_filter.vue'; -import StatusFilter from '~/search/sidebar/components/status_filter.vue'; +import ResultsFilters from '~/search/sidebar/components/results_filters.vue'; Vue.use(Vuex); @@ -35,118 +33,49 @@ describe('GlobalSearchSidebar', () => { wrapper.destroy(); }); - const findSidebarForm = () => wrapper.find('form'); - const findStatusFilter = () => wrapper.findComponent(StatusFilter); - const findConfidentialityFilter = () => wrapper.findComponent(ConfidentialityFilter); - const findApplyButton = () => wrapper.findComponent(GlButton); - const findResetLinkButton = () => wrapper.findComponent(GlLink); + const findSidebarSection = () => wrapper.find('section'); + const findFilters = () => wrapper.findComponent(ResultsFilters); - describe('template', () => { + describe('renders properly', () => { describe('scope=projects', () => { beforeEach(() => { createComponent({ urlQuery: { ...MOCK_QUERY, scope: 'projects' } }); }); - it("doesn't render StatusFilter", () => { - expect(findStatusFilter().exists()).toBe(false); + it('shows section', () => { + expect(findSidebarSection().exists()).toBe(true); }); - it("doesn't render ConfidentialityFilter", () => { - expect(findConfidentialityFilter().exists()).toBe(false); - }); - - it("doesn't render ApplyButton", () => { - expect(findApplyButton().exists()).toBe(false); - }); - }); - - describe('scope=issues', () => { - beforeEach(() => { - createComponent({ urlQuery: MOCK_QUERY }); - }); - it('renders StatusFilter', () => { - expect(findStatusFilter().exists()).toBe(true); - }); - - it('renders ConfidentialityFilter', () => { - expect(findConfidentialityFilter().exists()).toBe(true); - }); - - it('renders ApplyButton', () => { - expect(findApplyButton().exists()).toBe(true); - }); - }); - }); - - describe('ApplyButton', () => { - describe('when sidebarDirty is false', () => { - beforeEach(() => { - createComponent({ sidebarDirty: false }); - }); - - it('disables the button', () => { - expect(findApplyButton().attributes('disabled')).toBe('true'); + it("doesn't shows filters", () => { + expect(findFilters().exists()).toBe(false); }); }); - describe('when sidebarDirty is true', () => { + describe('scope=merge_requests', () => { beforeEach(() => { - createComponent({ sidebarDirty: true }); + createComponent({ urlQuery: { ...MOCK_QUERY, scope: 'merge_requests' } }); }); - it('enables the button', () => { - expect(findApplyButton().attributes('disabled')).toBe(undefined); - }); - }); - }); - - describe('ResetLinkButton', () => { - describe('with no filter selected', () => { - beforeEach(() => { - createComponent({ urlQuery: {} }); + it('shows section', () => { + expect(findSidebarSection().exists()).toBe(true); }); - it('does not render', () => { - expect(findResetLinkButton().exists()).toBe(false); + it('shows filters', () => { + expect(findFilters().exists()).toBe(true); }); }); - describe('with filter selected', () => { + describe('scope=issues', () => { beforeEach(() => { createComponent({ urlQuery: MOCK_QUERY }); }); - - it('does render', () => { - expect(findResetLinkButton().exists()).toBe(true); - }); - }); - - describe('with filter selected and user updated query back to default', () => { - beforeEach(() => { - createComponent({ urlQuery: MOCK_QUERY, query: {} }); + it('shows section', () => { + expect(findSidebarSection().exists()).toBe(true); }); - it('does render', () => { - expect(findResetLinkButton().exists()).toBe(true); + it('shows filters', () => { + expect(findFilters().exists()).toBe(true); }); }); }); - - describe('actions', () => { - beforeEach(() => { - createComponent({}); - }); - - it('clicking ApplyButton calls applyQuery', () => { - findSidebarForm().trigger('submit'); - - expect(actionSpies.applyQuery).toHaveBeenCalled(); - }); - - it('clicking ResetLinkButton calls resetQuery', () => { - findResetLinkButton().vm.$emit('click'); - - expect(actionSpies.resetQuery).toHaveBeenCalled(); - }); - }); }); diff --git a/spec/frontend/search/sidebar/components/filters_spec.js b/spec/frontend/search/sidebar/components/filters_spec.js new file mode 100644 index 00000000000..4f217709297 --- /dev/null +++ b/spec/frontend/search/sidebar/components/filters_spec.js @@ -0,0 +1,132 @@ +import { GlButton, GlLink } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import Vue from 'vue'; +import Vuex from 'vuex'; +import { MOCK_QUERY } from 'jest/search/mock_data'; +import ResultsFilters from '~/search/sidebar/components/results_filters.vue'; +import ConfidentialityFilter from '~/search/sidebar/components/confidentiality_filter.vue'; +import StatusFilter from '~/search/sidebar/components/status_filter.vue'; + +Vue.use(Vuex); + +describe('GlobalSearchSidebarFilters', () => { + let wrapper; + + const actionSpies = { + applyQuery: jest.fn(), + resetQuery: jest.fn(), + }; + + const createComponent = (initialState) => { + const store = new Vuex.Store({ + state: { + urlQuery: MOCK_QUERY, + ...initialState, + }, + actions: actionSpies, + }); + + wrapper = shallowMount(ResultsFilters, { + store, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + const findSidebarForm = () => wrapper.find('form'); + const findStatusFilter = () => wrapper.findComponent(StatusFilter); + const findConfidentialityFilter = () => wrapper.findComponent(ConfidentialityFilter); + const findApplyButton = () => wrapper.findComponent(GlButton); + const findResetLinkButton = () => wrapper.findComponent(GlLink); + + describe('Renders correctly', () => { + beforeEach(() => { + createComponent({ urlQuery: MOCK_QUERY }); + }); + it('renders StatusFilter', () => { + expect(findStatusFilter().exists()).toBe(true); + }); + + it('renders ConfidentialityFilter', () => { + expect(findConfidentialityFilter().exists()).toBe(true); + }); + + it('renders ApplyButton', () => { + expect(findApplyButton().exists()).toBe(true); + }); + }); + + describe('ApplyButton', () => { + describe('when sidebarDirty is false', () => { + beforeEach(() => { + createComponent({ sidebarDirty: false }); + }); + + it('disables the button', () => { + expect(findApplyButton().attributes('disabled')).toBe('true'); + }); + }); + + describe('when sidebarDirty is true', () => { + beforeEach(() => { + createComponent({ sidebarDirty: true }); + }); + + it('enables the button', () => { + expect(findApplyButton().attributes('disabled')).toBe(undefined); + }); + }); + }); + + describe('ResetLinkButton', () => { + describe('with no filter selected', () => { + beforeEach(() => { + createComponent({ urlQuery: {} }); + }); + + it('does not render', () => { + expect(findResetLinkButton().exists()).toBe(false); + }); + }); + + describe('with filter selected', () => { + beforeEach(() => { + createComponent({ urlQuery: MOCK_QUERY }); + }); + + it('does render', () => { + expect(findResetLinkButton().exists()).toBe(true); + }); + }); + + describe('with filter selected and user updated query back to default', () => { + beforeEach(() => { + createComponent({ urlQuery: MOCK_QUERY, query: {} }); + }); + + it('does render', () => { + expect(findResetLinkButton().exists()).toBe(true); + }); + }); + }); + + describe('actions', () => { + beforeEach(() => { + createComponent({}); + }); + + it('clicking ApplyButton calls applyQuery', () => { + findSidebarForm().trigger('submit'); + + expect(actionSpies.applyQuery).toHaveBeenCalled(); + }); + + it('clicking ResetLinkButton calls resetQuery', () => { + findResetLinkButton().vm.$emit('click'); + + expect(actionSpies.resetQuery).toHaveBeenCalled(); + }); + }); +}); diff --git a/spec/frontend/search/sidebar/components/scope_navigation_spec.js b/spec/frontend/search/sidebar/components/scope_navigation_spec.js new file mode 100644 index 00000000000..6262a52e01a --- /dev/null +++ b/spec/frontend/search/sidebar/components/scope_navigation_spec.js @@ -0,0 +1,80 @@ +import { GlNav, GlNavItem } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import Vue from 'vue'; +import Vuex from 'vuex'; +import { MOCK_QUERY, MOCK_NAVIGATION } from 'jest/search/mock_data'; +import ScopeNavigation from '~/search/sidebar/components/scope_navigation.vue'; + +Vue.use(Vuex); + +describe('ScopeNavigation', () => { + let wrapper; + + const actionSpies = { + fetchSidebarCount: jest.fn(), + }; + + const createComponent = (initialState) => { + const store = new Vuex.Store({ + state: { + urlQuery: MOCK_QUERY, + navigation: MOCK_NAVIGATION, + ...initialState, + }, + actions: actionSpies, + }); + + wrapper = shallowMount(ScopeNavigation, { + store, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + const findNavElement = () => wrapper.find('nav'); + const findGlNav = () => wrapper.findComponent(GlNav); + const findGlNavItems = () => wrapper.findAllComponents(GlNavItem); + const findGlNavItemActive = () => findGlNavItems().wrappers.filter((w) => w.attributes('active')); + const findGlNavItemActiveCount = () => findGlNavItemActive().at(0).findAll('span').at(1); + + describe('scope navigation', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders section', () => { + expect(findNavElement().exists()).toBe(true); + }); + + it('renders nav component', () => { + expect(findGlNav().exists()).toBe(true); + }); + + it('renders all nav item components', () => { + expect(findGlNavItems()).toHaveLength(9); + }); + + it('nav items have proper links', () => { + const linkAtPosition = 3; + const { link } = MOCK_NAVIGATION[Object.keys(MOCK_NAVIGATION)[linkAtPosition]]; + + expect(findGlNavItems().at(linkAtPosition).attributes('href')).toBe(link); + }); + }); + + describe('scope navigation sets proper state', () => { + beforeEach(() => { + createComponent(); + }); + + it('sets proper class to active item', () => { + expect(findGlNavItemActive()).toHaveLength(1); + }); + + it('active item', () => { + expect(findGlNavItemActiveCount().text()).toBe('2.4K'); + }); + }); +}); diff --git a/spec/frontend/search/store/actions_spec.js b/spec/frontend/search/store/actions_spec.js index c442ffa521d..3d19b27ff86 100644 --- a/spec/frontend/search/store/actions_spec.js +++ b/spec/frontend/search/store/actions_spec.js @@ -2,6 +2,7 @@ import MockAdapter from 'axios-mock-adapter'; import testAction from 'helpers/vuex_action_helper'; import Api from '~/api'; import { createAlert } from '~/flash'; +import * as logger from '~/lib/logger'; import axios from '~/lib/utils/axios_utils'; import * as urlUtils from '~/lib/utils/url_utility'; import * as actions from '~/search/store/actions'; @@ -23,6 +24,9 @@ import { MOCK_FRESH_DATA_RES, PRELOAD_EXPECTED_MUTATIONS, PROMISE_ALL_EXPECTED_MUTATIONS, + MOCK_NAVIGATION_DATA, + MOCK_NAVIGATION_ACTION_MUTATION, + MOCK_ENDPOINT_RESPONSE, } from '../mock_data'; jest.mock('~/flash'); @@ -31,6 +35,9 @@ jest.mock('~/lib/utils/url_utility', () => ({ joinPaths: jest.fn().mockReturnValue(''), visitUrl: jest.fn(), })); +jest.mock('~/lib/logger', () => ({ + logError: jest.fn(), +})); describe('Global Search Store Actions', () => { let mock; @@ -260,4 +267,32 @@ describe('Global Search Store Actions', () => { ); }); }); + + describe.each` + action | axiosMock | type | scope | expectedMutations | errorLogs + ${actions.fetchSidebarCount} | ${{ method: 'onGet', code: 200 }} | ${'success'} | ${'issues'} | ${[MOCK_NAVIGATION_ACTION_MUTATION]} | ${0} + ${actions.fetchSidebarCount} | ${{ method: null, code: 0 }} | ${'success'} | ${'projects'} | ${[]} | ${0} + ${actions.fetchSidebarCount} | ${{ method: 'onGet', code: 500 }} | ${'error'} | ${'issues'} | ${[]} | ${1} + `('fetchSidebarCount', ({ action, axiosMock, type, expectedMutations, scope, errorLogs }) => { + describe(`on ${type}`, () => { + beforeEach(() => { + state.navigation = MOCK_NAVIGATION_DATA; + state.urlQuery = { + scope, + }; + + if (axiosMock.method) { + mock[axiosMock.method]().reply(axiosMock.code, MOCK_ENDPOINT_RESPONSE); + } + }); + + it(`should ${expectedMutations.length === 0 ? 'NOT ' : ''}dispatch ${ + expectedMutations.length === 0 ? '' : 'the correct ' + }mutations for ${scope}`, () => { + return testAction({ action, state, expectedMutations }).then(() => { + expect(logger.logError).toHaveBeenCalledTimes(errorLogs); + }); + }); + }); + }); }); diff --git a/spec/frontend/search/store/mutations_spec.js b/spec/frontend/search/store/mutations_spec.js index 25f9b692955..a79ec8f70b0 100644 --- a/spec/frontend/search/store/mutations_spec.js +++ b/spec/frontend/search/store/mutations_spec.js @@ -1,13 +1,20 @@ import * as types from '~/search/store/mutation_types'; import mutations from '~/search/store/mutations'; import createState from '~/search/store/state'; -import { MOCK_QUERY, MOCK_GROUPS, MOCK_PROJECTS } from '../mock_data'; +import { + MOCK_QUERY, + MOCK_GROUPS, + MOCK_PROJECTS, + MOCK_NAVIGATION_DATA, + MOCK_NAVIGATION_ACTION_MUTATION, + MOCK_DATA_FOR_NAVIGATION_ACTION_MUTATION, +} from '../mock_data'; describe('Global Search Store Mutations', () => { let state; beforeEach(() => { - state = createState({ query: MOCK_QUERY }); + state = createState({ query: MOCK_QUERY, navigation: MOCK_NAVIGATION_DATA }); }); describe('REQUEST_GROUPS', () => { @@ -90,4 +97,15 @@ describe('Global Search Store Mutations', () => { expect(state.frequentItems[payload.key]).toStrictEqual(payload.data); }); }); + + describe('RECEIVE_NAVIGATION_COUNT', () => { + it('sets frequentItems[key] to data', () => { + const { payload } = MOCK_NAVIGATION_ACTION_MUTATION; + mutations[types.RECEIVE_NAVIGATION_COUNT](state, payload); + + expect(state.navigation[payload.key]).toStrictEqual( + MOCK_DATA_FOR_NAVIGATION_ACTION_MUTATION[payload.key], + ); + }); + }); }); diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index 4348a57d055..a844d65157d 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -1023,6 +1023,7 @@ RSpec.describe SearchHelper do describe '.search_navigation_json' do using RSpec::Parameterized::TableSyntax + context 'with data' do example_data_1 = { projects: { label: _("Projects"), condition: true }, @@ -1041,13 +1042,13 @@ RSpec.describe SearchHelper do } where(:data, :matcher) do - example_data_1 | -> { include("projects") } - example_data_2 | -> { eq("{}") } - example_data_3 | -> { include("projects", "blobs", "epics") } + example_data_1 | -> { include("projects") } + example_data_2 | -> { eq("{}") } + example_data_3 | -> { include("projects", "blobs", "epics") } end with_them do - it 'converts correctly' do + it 'renders data correctly' do allow(self).to receive(:search_navigation).with(no_args).and_return(data) expect(search_navigation_json).to instance_exec(&matcher) @@ -1056,6 +1057,23 @@ RSpec.describe SearchHelper do end end + describe '.search_navigation_json with .search_navigation' do + before do + allow(self).to receive(:current_user).and_return(build(:user)) + allow(self).to receive(:can?).and_return(true) + allow(self).to receive(:project_search_tabs?).and_return(true) + allow(self).to receive(:feature_flag_tab_enabled?).and_return(true) + allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(true) + allow(self).to receive(:feature_flag_tab_enabled?).and_return(true) + @show_snippets = true + @project = nil + end + + it 'test search navigation item order for CE all options enabled' do + expect(Gitlab::Json.parse(search_navigation_json).keys).to eq(%w[projects blobs issues merge_requests wiki_blobs commits notes milestones users snippet_titles]) + end + end + describe '.search_filter_link_json' do using RSpec::Parameterized::TableSyntax diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb index 3df278a49dd..fce5a3bc7bb 100644 --- a/spec/models/ci/bridge_spec.rb +++ b/spec/models/ci/bridge_spec.rb @@ -37,18 +37,8 @@ RSpec.describe Ci::Bridge do describe '#retryable?' do let(:bridge) { create(:ci_bridge, :success) } - it 'returns true' do - expect(bridge.retryable?).to eq(true) - end - - context 'without ci_recreate_downstream_pipeline ff' do - before do - stub_feature_flags(ci_recreate_downstream_pipeline: false) - end - - it 'returns false' do - expect(bridge.retryable?).to eq(false) - end + it 'returns false' do + expect(bridge.retryable?).to eq(false) end end diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb index 261eba73424..535999f211d 100644 --- a/spec/models/ci/processable_spec.rb +++ b/spec/models/ci/processable_spec.rb @@ -286,12 +286,6 @@ RSpec.describe Ci::Processable do end end - context 'when the processable is a bridge' do - subject(:processable) { create(:ci_bridge, pipeline: pipeline) } - - it_behaves_like 'retryable processable' - end - context 'when the processable is a build' do subject(:processable) { create(:ci_build, pipeline: pipeline) } diff --git a/spec/services/ci/retry_job_service_spec.rb b/spec/services/ci/retry_job_service_spec.rb index 5737303aaf8..540e700efa6 100644 --- a/spec/services/ci/retry_job_service_spec.rb +++ b/spec/services/ci/retry_job_service_spec.rb @@ -27,22 +27,6 @@ RSpec.describe Ci::RetryJobService do project.add_reporter(reporter) end - shared_context 'retryable bridge' do - let_it_be(:downstream_project) { create(:project, :repository) } - - let_it_be_with_refind(:job) do - create(:ci_bridge, :success, - pipeline: pipeline, downstream: downstream_project, description: 'a trigger job', ci_stage: stage - ) - end - - let_it_be(:job_to_clone) { job } - - before do - job.update!(retried: false) - end - end - shared_context 'retryable build' do let_it_be_with_reload(:job) do create(:ci_build, :success, pipeline: pipeline, ci_stage: stage) @@ -211,20 +195,6 @@ RSpec.describe Ci::RetryJobService do expect { service.clone!(create(:ci_build).present) }.to raise_error(TypeError) end - context 'when the job to be cloned is a bridge' do - include_context 'retryable bridge' - - it_behaves_like 'clones the job' - - context 'when given variables' do - let(:new_job) { service.clone!(job, variables: job_variables_attributes) } - - it 'does not give variables to the new bridge' do - expect { new_job }.not_to raise_error - end - end - end - context 'when the job to be cloned is a build' do include_context 'retryable build' @@ -331,20 +301,6 @@ RSpec.describe Ci::RetryJobService do subject { service.execute(job) } - context 'when the job to be retried is a bridge' do - include_context 'retryable bridge' - - it_behaves_like 'retries the job' - - context 'when given variables' do - let(:new_job) { service.clone!(job, variables: job_variables_attributes) } - - it 'does not give variables to the new bridge' do - expect { new_job }.not_to raise_error - end - end - end - context 'when the job to be retried is a build' do include_context 'retryable build' diff --git a/spec/views/shared/deploy_tokens/_form.html.haml_spec.rb b/spec/views/shared/deploy_tokens/_form.html.haml_spec.rb deleted file mode 100644 index 74ad0ccb77a..00000000000 --- a/spec/views/shared/deploy_tokens/_form.html.haml_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'shared/deploy_tokens/_form.html.haml' do - using RSpec::Parameterized::TableSyntax - - let_it_be(:user) { create(:user) } - let_it_be(:token) { build(:deploy_token) } - - RSpec.shared_examples "display deploy token settings" do |role, shows_package_registry_permissions| - before do - subject.add_member(user, role) - allow(view).to receive(:current_user).and_return(user) - stub_config(packages: { enabled: packages_enabled }) - end - - it "correctly renders the form" do - render 'shared/deploy_tokens/form', token: token, group_or_project: subject - - if shows_package_registry_permissions - expect(rendered).to have_content('Allows read-only access to the package registry.') - else - expect(rendered).not_to have_content('Allows read-only access to the package registry.') - end - end - end - - context "when the subject is a project" do - let_it_be(:subject, refind: true) { create(:project, :private) } - - where(:packages_enabled, :feature_enabled, :role, :shows_package_registry_permissions) do - true | true | :maintainer | true - false | true | :maintainer | false - true | false | :maintainer | false - false | false | :maintainer | false - end - - with_them do - before do - subject.update!(packages_enabled: feature_enabled) - end - - it_behaves_like 'display deploy token settings', params[:role], params[:shows_package_registry_permissions] - end - end - - context "when the subject is a group" do - let_it_be(:subject, refind: true) { create(:group, :private) } - - where(:packages_enabled, :role, :shows_package_registry_permissions) do - true | :owner | true - false | :owner | false - true | :maintainer | true - false | :maintainer | false - end - - with_them do - it_behaves_like 'display deploy token settings', params[:role], params[:shows_package_registry_permissions] - end - end -end |