diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-02 00:07:58 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-02 00:07:58 +0300 |
commit | 3ddb72a5ab59d56cb9e9cb27a5abb92bc5074544 (patch) | |
tree | 222afa7cff12916d6de0b8e10cc7cd12be886082 /spec | |
parent | 1bafcd6a59a26557e5752dd3ec6fa9e793986036 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
12 files changed, 248 insertions, 51 deletions
diff --git a/spec/factories/merge_requests_diff_llm_summary.rb b/spec/factories/merge_requests_diff_llm_summary.rb index c72ce97efcb..fc67f8442ca 100644 --- a/spec/factories/merge_requests_diff_llm_summary.rb +++ b/spec/factories/merge_requests_diff_llm_summary.rb @@ -5,6 +5,6 @@ FactoryBot.define do association :user, factory: :user association :merge_request_diff, factory: :merge_request_diff provider { 0 } - content { 'test' } + content { FFaker::Lorem.sentence } end end diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/user_autocomplete_item_spec.js.snap b/spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/user_autocomplete_item_spec.js.snap new file mode 100644 index 00000000000..431cdce2955 --- /dev/null +++ b/spec/frontend/super_sidebar/components/global_search/command_palette/__snapshots__/user_autocomplete_item_spec.js.snap @@ -0,0 +1,34 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UserAutocompleteItem should render user item 1`] = ` +<div + class="gl-display-flex gl-align-items-center" +> + <gl-avatar-stub + alt="avatar" + aria-hidden="true" + class="gl-mr-3" + entityid="37" + entityname="Cole Dickinson" + shape="rect" + size="16" + src="https://www.gravatar.com/avatar/a9638f4ec70148d51e56bf05ad41e993?s=80&d=identicon" + /> + + <span + class="gl-display-flex gl-flex-direction-column" + > + <span + class="gl-text-gray-900" + > + Cole Dickinson + </span> + + <span + class="gl-font-sm gl-text-gray-500" + > + reported_user_14 + </span> + </span> +</div> +`; diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js b/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js index a079188190a..df5b7de78f7 100644 --- a/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js +++ b/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js @@ -1,14 +1,21 @@ import fuzzaldrinPlus from 'fuzzaldrin-plus'; -import { GlDisclosureDropdownGroup, GlDisclosureDropdownItem } from '@gitlab/ui'; +import { GlDisclosureDropdownGroup, GlDisclosureDropdownItem, GlLoadingIcon } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; +import MockAdapter from 'axios-mock-adapter'; import CommandPaletteItems from '~/super_sidebar/components/global_search/command_palette/command_palette_items.vue'; import { COMMAND_HANDLE, COMMANDS_GROUP_TITLE, + USERS_GROUP_TITLE, + USER_HANDLE, } from '~/super_sidebar/components/global_search/command_palette/constants'; -import { COMMAND_PALETTE_COMMANDS } from './mock_data'; +import { userMapper } from '~/super_sidebar/components/global_search/command_palette/utils'; +import axios from '~/lib/utils/axios_utils'; +import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; +import waitForPromises from 'helpers/wait_for_promises'; +import { COMMANDS, USERS } from './mock_data'; -const commands = COMMAND_PALETTE_COMMANDS.map(({ text, href, keywords }) => ({ +const commands = COMMANDS.map(({ text, href, keywords }) => ({ text, href, keywords: keywords.join(''), @@ -29,41 +36,101 @@ describe('CommandPaletteItems', () => { GlDisclosureDropdownItem, }, provide: { - commandPaletteData: COMMAND_PALETTE_COMMANDS, + commandPaletteData: COMMANDS, }, }); }; const findItems = () => wrapper.findAllComponents(GlDisclosureDropdownItem); const findGroup = () => wrapper.findComponent(GlDisclosureDropdownGroup); + const findLoader = () => wrapper.findComponent(GlLoadingIcon); - it('renders all commands initially', () => { - createComponent(); - expect(findItems()).toHaveLength(COMMAND_PALETTE_COMMANDS.length); - expect(findGroup().props('group')).toEqual({ - name: COMMANDS_GROUP_TITLE, - items: commands, + describe('COMMANDS', () => { + it('renders all commands initially', () => { + createComponent(); + expect(findItems()).toHaveLength(COMMANDS.length); + expect(findGroup().props('group')).toEqual({ + name: COMMANDS_GROUP_TITLE, + items: commands, + }); + }); + + describe('with search query', () => { + it('should filter by the search query', async () => { + jest.spyOn(fuzzaldrinPlus, 'filter'); + createComponent({ searchQuery: 'mr' }); + const searchQuery = 'todo'; + await wrapper.setProps({ searchQuery }); + expect(fuzzaldrinPlus.filter).toHaveBeenCalledWith( + commands, + searchQuery, + expect.objectContaining({ key: 'keywords' }), + ); + }); + + it('should display no results message when no command matched the search query', async () => { + jest.spyOn(fuzzaldrinPlus, 'filter').mockReturnValue([]); + createComponent({ searchQuery: 'mr' }); + const searchQuery = 'todo'; + await wrapper.setProps({ searchQuery }); + expect(wrapper.text()).toBe('No results found'); + }); }); }); - describe('with search query', () => { - it('should filter by the search query', async () => { - jest.spyOn(fuzzaldrinPlus, 'filter'); - createComponent({ searchQuery: 'mr' }); - const searchQuery = 'todo'; - await wrapper.setProps({ searchQuery }); - expect(fuzzaldrinPlus.filter).toHaveBeenCalledWith( - commands, - searchQuery, - expect.objectContaining({ key: 'keywords' }), + describe('USERS', () => { + let mockAxios; + + beforeEach(() => { + mockAxios = new MockAdapter(axios); + }); + + it('should NOT start search for users by the search query which is less than 3 chars', () => { + jest.spyOn(axios, 'get'); + const searchQuery = 'us'; + createComponent({ handle: USER_HANDLE, searchQuery }); + + expect(axios.get).not.toHaveBeenCalled(); + + expect(findLoader().exists()).toBe(false); + }); + + it('should start search for users by the search query with 3+ chars and display a loader', () => { + jest.spyOn(axios, 'get'); + const searchQuery = 'user'; + createComponent({ handle: USER_HANDLE, searchQuery }); + + expect(axios.get).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + params: { + search: searchQuery, + }, + }), ); + + expect(findLoader().exists()).toBe(true); + }); + + it('should render returned users', async () => { + mockAxios.onGet().replyOnce(HTTP_STATUS_OK, USERS); + + const searchQuery = 'user'; + createComponent({ handle: USER_HANDLE, searchQuery }); + + await waitForPromises(); + expect(findItems()).toHaveLength(USERS.length); + expect(findGroup().props('group')).toEqual({ + name: USERS_GROUP_TITLE, + items: USERS.map(userMapper), + }); }); - it('should display no results message when no command matched the search qery', async () => { - jest.spyOn(fuzzaldrinPlus, 'filter').mockReturnValue([]); - createComponent({ searchQuery: 'mr' }); - const searchQuery = 'todo'; - await wrapper.setProps({ searchQuery }); + it('should display no results message when no users matched the search query', async () => { + mockAxios.onGet().replyOnce(HTTP_STATUS_OK, []); + const searchQuery = 'user'; + createComponent({ handle: USER_HANDLE, searchQuery }); + await waitForPromises(); expect(wrapper.text()).toBe('No results found'); }); }); diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/mock_data.js b/spec/frontend/super_sidebar/components/global_search/command_palette/mock_data.js index 7469154e363..e924efd56af 100644 --- a/spec/frontend/super_sidebar/components/global_search/command_palette/mock_data.js +++ b/spec/frontend/super_sidebar/components/global_search/command_palette/mock_data.js @@ -1,4 +1,4 @@ -export const COMMAND_PALETTE_COMMANDS = [ +export const COMMANDS = [ { text: 'New project/repository', href: '/projects/new', @@ -15,3 +15,26 @@ export const COMMAND_PALETTE_COMMANDS = [ keywords: ['new', 'snippet'], }, ]; + +export const USERS = [ + { + id: 37, + username: 'reported_user_14', + name: 'Cole Dickinson', + web_url: 'http://127.0.0.1:3000/reported_user_14', + avatar_url: + 'https://www.gravatar.com/avatar/a9638f4ec70148d51e56bf05ad41e993?s=80\u0026d=identicon', + }, + { + id: 47, + username: 'sharlatenok', + name: 'Olena Horal-Koretska', + web_url: 'http://127.0.0.1:3000/sharlatenok', + }, + { + id: 30, + username: 'reported_user_7', + name: 'Violeta Feeney', + web_url: 'http://127.0.0.1:3000/reported_user_7', + }, +]; diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/user_autocomplete_item_spec.js b/spec/frontend/super_sidebar/components/global_search/command_palette/user_autocomplete_item_spec.js new file mode 100644 index 00000000000..5abb56228a4 --- /dev/null +++ b/spec/frontend/super_sidebar/components/global_search/command_palette/user_autocomplete_item_spec.js @@ -0,0 +1,25 @@ +import { shallowMount } from '@vue/test-utils'; +import UserAutocompleteItem from '~/super_sidebar/components/global_search/command_palette/user_autocomplete_item.vue'; +import { userMapper } from '~/super_sidebar/components/global_search/command_palette/utils'; +import { USERS } from './mock_data'; + +describe('UserAutocompleteItem', () => { + let wrapper; + + const createComponent = () => { + wrapper = shallowMount(UserAutocompleteItem, { + propsData: { + user: USERS.map(userMapper)[0], + searchQuery: 'root', + }, + }); + }; + + beforeEach(() => { + createComponent(); + }); + + it('should render user item', () => { + expect(wrapper.element).toMatchSnapshot(); + }); +}); diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/utils_spec.js b/spec/frontend/super_sidebar/components/global_search/command_palette/utils_spec.js new file mode 100644 index 00000000000..5bcf5e95793 --- /dev/null +++ b/spec/frontend/super_sidebar/components/global_search/command_palette/utils_spec.js @@ -0,0 +1,29 @@ +import { + userMapper, + commandMapper, +} from '~/super_sidebar/components/global_search/command_palette/utils'; +import { COMMANDS, USERS } from './mock_data'; + +describe('userMapper', () => { + it('should transform users response', () => { + const user = USERS[0]; + expect(userMapper(user)).toEqual({ + id: user.id, + username: user.username, + text: user.name, + href: user.web_url, + avatar_url: user.avatar_url, + }); + }); +}); + +describe('commandMapper', () => { + it('should transform commands response', () => { + const command = COMMANDS[0]; + expect(commandMapper(command)).toEqual({ + href: command.href, + text: command.text, + keywords: command.keywords.join(''), + }); + }); +}); diff --git a/spec/frontend/super_sidebar/components/global_search/components/global_search_spec.js b/spec/frontend/super_sidebar/components/global_search/components/global_search_spec.js index fd79e274ff6..0a6d5919207 100644 --- a/spec/frontend/super_sidebar/components/global_search/components/global_search_spec.js +++ b/spec/frontend/super_sidebar/components/global_search/components/global_search_spec.js @@ -12,6 +12,7 @@ import CommandPaletteItems from '~/super_sidebar/components/global_search/comman import { SEARCH_OR_COMMAND_MODE_PLACEHOLDER, COMMAND_HANDLE, + USER_HANDLE, } from '~/super_sidebar/components/global_search/command_palette/constants'; import { SEARCH_INPUT_DESCRIPTION, @@ -319,24 +320,27 @@ describe('GlobalSearchModal', () => { }); }); - describe('when FF `command_palette` is enabled', () => { - beforeEach(() => { - createComponent({ search: COMMAND_HANDLE }, undefined, undefined, { - commandPalette: true, + describe.each([COMMAND_HANDLE, USER_HANDLE])( + 'when FF `command_palette` is enabled', + (handle) => { + beforeEach(() => { + createComponent({ search: handle }, undefined, undefined, { + commandPalette: true, + }); }); - }); - it('should render command mode components', () => { - expect(findCommandPaletteItems().exists()).toBe(true); - expect(findFakeSearchInput().exists()).toBe(true); - }); + it('should render command mode components', () => { + expect(findCommandPaletteItems().exists()).toBe(true); + expect(findFakeSearchInput().exists()).toBe(true); + }); - it('should provide an alternative placeholder to the search input', () => { - expect(findGlobalSearchInput().attributes('placeholder')).toBe( - SEARCH_OR_COMMAND_MODE_PLACEHOLDER, - ); - }); - }); + it('should provide an alternative placeholder to the search input', () => { + expect(findGlobalSearchInput().attributes('placeholder')).toBe( + SEARCH_OR_COMMAND_MODE_PLACEHOLDER, + ); + }); + }, + ); }); }); diff --git a/spec/graphql/types/ci/job_type_spec.rb b/spec/graphql/types/ci/job_type_spec.rb index e927bac431c..f31c0d5255c 100644 --- a/spec/graphql/types/ci/job_type_spec.rb +++ b/spec/graphql/types/ci/job_type_spec.rb @@ -60,6 +60,12 @@ RSpec.describe Types::Ci::JobType, feature_category: :continuous_integration do failure_message ] + if Gitlab.ee? + expected_fields += %i[ + aiFailureAnalysis + ] + end + expect(described_class).to have_graphql_fields(*expected_fields) end diff --git a/spec/requests/api/graphql/ci/jobs_spec.rb b/spec/requests/api/graphql/ci/jobs_spec.rb index 0d5ac725edd..f237516021d 100644 --- a/spec/requests/api/graphql/ci/jobs_spec.rb +++ b/spec/requests/api/graphql/ci/jobs_spec.rb @@ -183,7 +183,7 @@ RSpec.describe 'Query.project.pipeline', feature_category: :continuous_integrati #{all_graphql_fields_for('CiBuildNeed')} } ... on CiJob { - #{all_graphql_fields_for('CiJob')} + #{all_graphql_fields_for('CiJob', excluded: %w[aiFailureAnalysis])} } } } diff --git a/spec/requests/api/graphql/ci/stages_spec.rb b/spec/requests/api/graphql/ci/stages_spec.rb index f4e1a69d455..2d646a0e1c3 100644 --- a/spec/requests/api/graphql/ci/stages_spec.rb +++ b/spec/requests/api/graphql/ci/stages_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'Query.project.pipeline.stages', feature_category: :continuous_in let(:fields) do <<~QUERY nodes { - #{all_graphql_fields_for('CiStage')} + #{all_graphql_fields_for('CiStage', max_depth: 2)} } QUERY end @@ -37,7 +37,7 @@ RSpec.describe 'Query.project.pipeline.stages', feature_category: :continuous_in before_all do create(:ci_stage, pipeline: pipeline, name: 'deploy') - create_list(:ci_build, 2, pipeline: pipeline, stage: 'deploy') + create(:ci_build, pipeline: pipeline, stage: 'deploy') end it_behaves_like 'a working graphql query' do @@ -58,7 +58,7 @@ RSpec.describe 'Query.project.pipeline.stages', feature_category: :continuous_in it 'returns up to default limit jobs per stage' do post_query - expect(job_nodes.count).to eq(2) + expect(job_nodes.count).to eq(1) end context 'when the limit is manually set' do diff --git a/spec/requests/api/graphql/jobs_query_spec.rb b/spec/requests/api/graphql/jobs_query_spec.rb index 7607aeac6e0..4248a03fa74 100644 --- a/spec/requests/api/graphql/jobs_query_spec.rb +++ b/spec/requests/api/graphql/jobs_query_spec.rb @@ -10,7 +10,7 @@ RSpec.describe 'getting job information', feature_category: :continuous_integrat :jobs, {}, %( count nodes { - #{all_graphql_fields_for(::Types::Ci::JobType, max_depth: 1)} + #{all_graphql_fields_for(::Types::Ci::JobType, max_depth: 1, excluded: %w[aiFailureAnalysis])} }) ) end diff --git a/spec/requests/api/graphql/project/pipeline_spec.rb b/spec/requests/api/graphql/project/pipeline_spec.rb index abfdf07c288..fb1489372fc 100644 --- a/spec/requests/api/graphql/project/pipeline_spec.rb +++ b/spec/requests/api/graphql/project/pipeline_spec.rb @@ -15,7 +15,7 @@ RSpec.describe 'getting pipeline information nested in a project', feature_categ let(:path) { %i[project pipeline] } let(:pipeline_graphql_data) { graphql_data_at(*path) } let(:depth) { 3 } - let(:excluded) { %w[job project] } # Project is very expensive, due to the number of fields + let(:excluded) { %w[job project jobs] } # Project is very expensive, due to the number of fields let(:fields) { all_graphql_fields_for('Pipeline', excluded: excluded, max_depth: depth) } let(:query) do @@ -82,7 +82,11 @@ RSpec.describe 'getting pipeline information nested in a project', feature_categ context 'when enough data is requested' do let(:fields) do query_graphql_field(:jobs, nil, - query_graphql_field(:nodes, {}, all_graphql_fields_for('CiJob', max_depth: 3))) + query_graphql_field( + :nodes, {}, + all_graphql_fields_for('CiJob', excluded: %w[aiFailureAnalysis], max_depth: 3) + ) + ) end it 'contains jobs' do @@ -116,7 +120,12 @@ RSpec.describe 'getting pipeline information nested in a project', feature_categ let(:fields) do query_graphql_field(:jobs, { retried: retried_argument }, - query_graphql_field(:nodes, {}, all_graphql_fields_for('CiJob', max_depth: 3))) + query_graphql_field( + :nodes, + {}, + all_graphql_fields_for('CiJob', excluded: %w[aiFailureAnalysis], max_depth: 3) + ) + ) end context 'when we filter out retried jobs' do @@ -177,7 +186,7 @@ RSpec.describe 'getting pipeline information nested in a project', feature_categ pipeline(iid: $pipelineIID) { jobs(statuses: [$status]) { nodes { - #{all_graphql_fields_for('CiJob', max_depth: 1)} + #{all_graphql_fields_for('CiJob', excluded: %w[aiFailureAnalysis], max_depth: 3)} } } } |