diff options
Diffstat (limited to 'spec/frontend')
-rw-r--r-- | spec/frontend/fixtures/issues.rb | 2 | ||||
-rw-r--r-- | spec/frontend/fixtures/snippet.rb | 2 | ||||
-rw-r--r-- | spec/frontend/header_spec.js | 9 | ||||
-rw-r--r-- | spec/frontend/ide/components/repo_editor_spec.js | 9 | ||||
-rw-r--r-- | spec/frontend/issues/issue_spec.js | 12 | ||||
-rw-r--r-- | spec/frontend/lib/utils/common_utils_spec.js | 24 | ||||
-rw-r--r-- | spec/frontend/shortcuts_spec.js | 40 | ||||
-rw-r--r-- | spec/frontend/vue_shared/components/list_selector/index_spec.js | 134 | ||||
-rw-r--r-- | spec/frontend/vue_shared/components/list_selector/mock_data.js | 25 | ||||
-rw-r--r-- | spec/frontend/vue_shared/components/list_selector/user_spec.js | 55 |
10 files changed, 284 insertions, 28 deletions
diff --git a/spec/frontend/fixtures/issues.rb b/spec/frontend/fixtures/issues.rb index 90aa0544526..6d81d9ca1d2 100644 --- a/spec/frontend/fixtures/issues.rb +++ b/spec/frontend/fixtures/issues.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Projects::IssuesController, '(JavaScript fixtures)', :with_license, type: :controller do include JavaScriptFixturesHelpers - let(:user) { create(:user, :no_super_sidebar, feed_token: 'feedtoken:coldfeed') } + let(:user) { create(:user, feed_token: 'feedtoken:coldfeed') } let(:namespace) { create(:namespace, name: 'frontend-fixtures') } let(:project) { create(:project_empty_repo, namespace: namespace, path: 'issues-project') } diff --git a/spec/frontend/fixtures/snippet.rb b/spec/frontend/fixtures/snippet.rb index 23df89a244c..a96b7a57106 100644 --- a/spec/frontend/fixtures/snippet.rb +++ b/spec/frontend/fixtures/snippet.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe SnippetsController, '(JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers - let(:user) { create(:user, :no_super_sidebar) } + let(:user) { create(:user) } let(:namespace) { create(:namespace, name: 'frontend-fixtures', owner: user) } let(:project) { create(:project, :repository, namespace: namespace, path: 'branches-project') } let(:snippet) { create(:personal_snippet, :public, title: 'snippet.md', content: '# snippet', file_name: 'snippet.md', author: user) } diff --git a/spec/frontend/header_spec.js b/spec/frontend/header_spec.js index 4907dc09a3c..13c11863443 100644 --- a/spec/frontend/header_spec.js +++ b/spec/frontend/header_spec.js @@ -3,7 +3,14 @@ import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; import initTodoToggle, { initNavUserDropdownTracking } from '~/header'; import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; -describe('Header', () => { +// TODO: Remove this with the removal of the old navigation. +// See https://gitlab.com/groups/gitlab-org/-/epics/11875. +// +// This and ~/header will be removed. These tests no longer work due to the +// corresponding fixtures changing for +// https://gitlab.com/gitlab-org/gitlab/-/issues/420121. +// eslint-disable-next-line jest/no-disabled-tests +describe.skip('Header', () => { describe('Todos notification', () => { const todosPendingCount = '.js-todos-count'; diff --git a/spec/frontend/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js index 33fa5bc799f..6f53aaed655 100644 --- a/spec/frontend/ide/components/repo_editor_spec.js +++ b/spec/frontend/ide/components/repo_editor_spec.js @@ -8,17 +8,14 @@ import { shallowMount } from '@vue/test-utils'; import waitForPromises from 'helpers/wait_for_promises'; import { stubPerformanceWebAPI } from 'helpers/performance'; import { exampleConfigs, exampleFiles } from 'jest/ide/lib/editorconfig/mock_data'; -import { - EDITOR_CODE_INSTANCE_FN, - EDITOR_DIFF_INSTANCE_FN, - EXTENSION_CI_SCHEMA_FILE_NAME_MATCH, -} from '~/editor/constants'; +import { EDITOR_CODE_INSTANCE_FN, EDITOR_DIFF_INSTANCE_FN } from '~/editor/constants'; import { EditorMarkdownExtension } from '~/editor/extensions/source_editor_markdown_ext'; import { EditorMarkdownPreviewExtension } from '~/editor/extensions/source_editor_markdown_livepreview_ext'; import { CiSchemaExtension } from '~/editor/extensions/source_editor_ci_schema_ext'; import SourceEditor from '~/editor/source_editor'; import RepoEditor from '~/ide/components/repo_editor.vue'; import { leftSidebarViews, FILE_VIEW_MODE_PREVIEW, viewerTypes } from '~/ide/constants'; +import { DEFAULT_CI_CONFIG_PATH } from '~/lib/utils/constants'; import ModelManager from '~/ide/lib/common/model_manager'; import service from '~/ide/services'; import { createStoreOptions } from '~/ide/stores'; @@ -56,7 +53,7 @@ const dummyFile = { active: true, }, ciConfig: { - ...file(EXTENSION_CI_SCHEMA_FILE_NAME_MATCH), + ...file(DEFAULT_CI_CONFIG_PATH), content: '', tempFile: true, active: true, diff --git a/spec/frontend/issues/issue_spec.js b/spec/frontend/issues/issue_spec.js index bf2ca42f71f..b976a051f7a 100644 --- a/spec/frontend/issues/issue_spec.js +++ b/spec/frontend/issues/issue_spec.js @@ -58,7 +58,17 @@ describe('Issue', () => { ); }); - it('updates issueCounter text', () => { + // TODO: Remove this with the removal of the old navigation. + // See https://gitlab.com/groups/gitlab-org/-/epics/11875. + // See also https://gitlab.com/gitlab-org/gitlab/-/issues/429678 about + // reimplementing this in the new navigation. + // + // Since this entire suite only tests the issue count updating, removing + // this test would mean removing the entire suite. But, ~/issues/issue.js + // does more than just that. Tests should be written to cover those other + // features. So we're just skipping this for now. + // eslint-disable-next-line jest/no-disabled-tests + it.skip('updates issueCounter text', () => { expect(testContext.issueCounter).toBeVisible(); expect(testContext.issueCounter).toHaveText(expectedCounterText); }); diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js index 8697249ebf5..6295914b127 100644 --- a/spec/frontend/lib/utils/common_utils_spec.js +++ b/spec/frontend/lib/utils/common_utils_spec.js @@ -1213,4 +1213,28 @@ describe('common_utils', () => { expect(cloned.ref === ref).toBe(false); }); }); + + describe('isDefaultCiConfig', () => { + it('returns true when the path is the default CI config path', () => { + expect(commonUtils.isDefaultCiConfig('.gitlab-ci.yml')).toBe(true); + }); + + it('returns false when the path is not the default CI config path', () => { + expect(commonUtils.isDefaultCiConfig('some/other/path.yml')).toBe(false); + }); + }); + + describe('hasCiConfigExtension', () => { + it('returns true when the path is the default CI config path', () => { + expect(commonUtils.hasCiConfigExtension('.gitlab-ci.yml')).toBe(true); + }); + + it('returns true when the path has a CI config extension', () => { + expect(commonUtils.hasCiConfigExtension('some/path.gitlab-ci.yml')).toBe(true); + }); + + it('returns false when the path does not have a CI config extension', () => { + expect(commonUtils.hasCiConfigExtension('some/other/path.yml')).toBe(false); + }); + }); }); diff --git a/spec/frontend/shortcuts_spec.js b/spec/frontend/shortcuts_spec.js index 88ad9204d08..ca72426cb44 100644 --- a/spec/frontend/shortcuts_spec.js +++ b/spec/frontend/shortcuts_spec.js @@ -7,23 +7,26 @@ import Shortcuts, { LOCAL_MOUSETRAP_DATA_KEY } from '~/behaviors/shortcuts/short import MarkdownPreview from '~/behaviors/preview_markdown'; describe('Shortcuts', () => { - const createEvent = (type, target) => - $.Event(type, { - target, - }); let shortcuts; beforeAll(() => { shortcuts = new Shortcuts(); }); + const mockSuperSidebarSearchButton = () => { + const button = document.createElement('button'); + button.id = 'super-sidebar-search'; + return button; + }; + beforeEach(() => { setHTMLFixture(htmlSnippetsShow); + document.body.appendChild(mockSuperSidebarSearchButton()); new Shortcuts(); // eslint-disable-line no-new new MarkdownPreview(); // eslint-disable-line no-new - jest.spyOn(document.querySelector('#search'), 'focus'); + jest.spyOn(HTMLElement.prototype, 'click'); jest.spyOn(Mousetrap.prototype, 'stopCallback'); jest.spyOn(Mousetrap.prototype, 'bind').mockImplementation(); @@ -100,21 +103,22 @@ describe('Shortcuts', () => { }); describe('focusSearch', () => { - describe('when super sidebar is NOT enabled', () => { - let originalGon; - beforeEach(() => { - originalGon = window.gon; - window.gon = { use_new_navigation: false }; - }); + let event; - afterEach(() => { - window.gon = originalGon; - }); + beforeEach(() => { + window.gon.use_new_navigation = true; + event = new KeyboardEvent('keydown', { cancelable: true }); + Shortcuts.focusSearch(event); + }); - it('focuses the search bar', () => { - Shortcuts.focusSearch(createEvent('KeyboardEvent')); - expect(document.querySelector('#search').focus).toHaveBeenCalled(); - }); + it('clicks the super sidebar search button', () => { + expect(HTMLElement.prototype.click).toHaveBeenCalled(); + const thisArg = HTMLElement.prototype.click.mock.contexts[0]; + expect(thisArg.id).toBe('super-sidebar-search'); + }); + + it('cancels the default behaviour of the event', () => { + expect(event.defaultPrevented).toBe(true); }); }); diff --git a/spec/frontend/vue_shared/components/list_selector/index_spec.js b/spec/frontend/vue_shared/components/list_selector/index_spec.js new file mode 100644 index 00000000000..96074519857 --- /dev/null +++ b/spec/frontend/vue_shared/components/list_selector/index_spec.js @@ -0,0 +1,134 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import { GlCard, GlIcon, GlCollapsibleListbox, GlSearchBoxByType } from '@gitlab/ui'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import ListSelector from '~/vue_shared/components/list_selector/index.vue'; +import User from '~/vue_shared/components/list_selector/user.vue'; +import usersAutocompleteQuery from '~/graphql_shared/queries/users_autocomplete.query.graphql'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import { USERS_RESPONSE_MOCK } from './mock_data'; + +Vue.use(VueApollo); + +describe('List Selector spec', () => { + let wrapper; + let fakeApollo; + + const MOCK_PROPS = { + title: 'Users', + projectPath: 'some/project/path', + type: 'users', + }; + + const usersAutocompleteQuerySuccess = jest.fn().mockResolvedValue(USERS_RESPONSE_MOCK); + + const createComponent = async (props) => { + fakeApollo = createMockApollo([[usersAutocompleteQuery, usersAutocompleteQuerySuccess]]); + + wrapper = mountExtended(ListSelector, { + apolloProvider: fakeApollo, + propsData: { + ...MOCK_PROPS, + ...props, + }, + }); + + await waitForPromises(); + }; + + const findCard = () => wrapper.findComponent(GlCard); + const findTitle = () => wrapper.findByText(MOCK_PROPS.title); + const findIcon = () => wrapper.findComponent(GlIcon); + const findListBox = () => wrapper.findComponent(GlCollapsibleListbox); + const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType); + const findAllUserComponents = () => wrapper.findAllComponents(User); + + describe('Users type', () => { + beforeEach(() => createComponent({ type: 'users' })); + + it('renders a Card component', () => { + expect(findCard().exists()).toBe(true); + }); + + it('renders a title', () => { + expect(findTitle().exists()).toBe(true); + }); + + it('renders the correct icon', () => { + expect(findIcon().props('name')).toBe('user'); + }); + + it('renders a Search box component', () => { + expect(findSearchBox().exists()).toBe(true); + }); + + it('does not call query when search box has not received an input', () => { + expect(usersAutocompleteQuerySuccess).not.toHaveBeenCalled(); + expect(findAllUserComponents().length).toBe(0); + }); + + describe('searching', () => { + const searchResponse = USERS_RESPONSE_MOCK.data.project.autocompleteUsers; + const search = 'foo'; + + const emitSearchInput = async () => { + findSearchBox().vm.$emit('input', search); + await waitForPromises(); + }; + + beforeEach(() => emitSearchInput()); + + it('calls query with correct variables when Search box receives an input', () => { + expect(usersAutocompleteQuerySuccess).toHaveBeenCalledWith({ + fullPath: MOCK_PROPS.projectPath, + isProject: true, + search, + }); + }); + + it('renders a List box component with the correct props', () => { + expect(findListBox().props()).toMatchObject({ multiple: true, items: searchResponse }); + }); + + it('renders a user component for each search result', () => { + expect(findAllUserComponents().length).toBe(searchResponse.length); + }); + + it('emits an event when a search result is selected', () => { + const firstSearchResult = searchResponse[0]; + findAllUserComponents().at(0).vm.$emit('select', firstSearchResult.username); + + expect(wrapper.emitted('select')).toEqual([ + [{ ...firstSearchResult, text: 'Administrator', value: 'root' }], + ]); + }); + }); + + describe('selected items', () => { + const selectedUser = { username: 'root' }; + const selectedItems = [selectedUser]; + beforeEach(() => createComponent({ selectedItems })); + + it('renders a heading with the total selected items', () => { + expect(findCard().text()).toContain('Users'); + expect(findCard().text()).toContain('1'); + }); + + it('renders a user component for each selected item', () => { + expect(findAllUserComponents().length).toBe(selectedItems.length); + expect(findAllUserComponents().at(0).props()).toMatchObject({ + data: selectedUser, + canDelete: true, + }); + }); + + it('emits a delete event when a delete event is emitted from the user component', () => { + const username = 'root'; + findAllUserComponents().at(0).vm.$emit('delete', username); + + expect(wrapper.emitted('delete')).toEqual([[username]]); + }); + }); + }); +}); diff --git a/spec/frontend/vue_shared/components/list_selector/mock_data.js b/spec/frontend/vue_shared/components/list_selector/mock_data.js new file mode 100644 index 00000000000..ce5d209a938 --- /dev/null +++ b/spec/frontend/vue_shared/components/list_selector/mock_data.js @@ -0,0 +1,25 @@ +export const USERS_RESPONSE_MOCK = { + data: { + project: { + id: 'gid://gitlab/Project/20', + autocompleteUsers: [ + { + id: 'gid://gitlab/User/1', + avatarUrl: '/uploads/-/system/user/avatar/1/avatar.png', + name: 'Administrator', + username: 'root', + __typename: 'AutocompletedUser', + }, + { + id: 'gid://gitlab/User/15', + avatarUrl: + 'https://www.gravatar.com/avatar/c4ab964b90c3049c47882b319d3c5cc0?s=80\u0026d=identicon', + name: 'Corrine Rath', + username: 'laronda.graham', + __typename: 'AutocompletedUser', + }, + ], + __typename: 'Project', + }, + }, +}; diff --git a/spec/frontend/vue_shared/components/list_selector/user_spec.js b/spec/frontend/vue_shared/components/list_selector/user_spec.js new file mode 100644 index 00000000000..5ce21f6672b --- /dev/null +++ b/spec/frontend/vue_shared/components/list_selector/user_spec.js @@ -0,0 +1,55 @@ +import { GlAvatar } from '@gitlab/ui'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import User from '~/vue_shared/components/list_selector/user.vue'; + +describe('User spec', () => { + let wrapper; + + const MOCK_USER = { name: 'Admin', username: 'root', avatarUrl: 'some/avatar.jpg' }; + + const createComponent = (props) => { + wrapper = mountExtended(User, { + propsData: { + data: MOCK_USER, + ...props, + }, + }); + }; + + const findAvatar = () => wrapper.findComponent(GlAvatar); + const findDeleteButton = () => wrapper.findByRole('button', { name: 'Delete Admin' }); + + beforeEach(() => createComponent()); + + it('renders an Avatar component', () => { + expect(findAvatar().props('size')).toBe(32); + expect(findAvatar().attributes()).toMatchObject({ + src: MOCK_USER.avatarUrl, + alt: MOCK_USER.name, + }); + }); + + it('renders a name and username', () => { + expect(wrapper.text()).toContain('Admin'); + expect(wrapper.text()).toContain('@root'); + }); + + it('does not render a delete button by default', () => { + expect(findDeleteButton().exists()).toBe(false); + }); + + describe('Delete button', () => { + beforeEach(() => createComponent({ canDelete: true })); + + it('renders a delete button', () => { + expect(findDeleteButton().exists()).toBe(true); + expect(findDeleteButton().props('icon')).toBe('remove'); + }); + + it('emits a delete event if the delete button is clicked', () => { + findDeleteButton().trigger('click'); + + expect(wrapper.emitted('delete')).toEqual([[MOCK_USER.username]]); + }); + }); +}); |