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
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-07-05 15:09:46 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-07-05 15:09:46 +0300
commitf34077e88198da754b4efecd1ce1d996ce982286 (patch)
tree24a176ba93be06eee0ee912215fbeb2611ab7872 /spec/frontend
parent402c915cb58cfc658ecbdad368e89fb7b3993c1e (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/design_management/components/design_sidebar_spec.js61
-rw-r--r--spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap67
-rw-r--r--spec/frontend/diffs/components/diff_code_quality_spec.js66
-rw-r--r--spec/frontend/diffs/components/diff_view_spec.js32
-rw-r--r--spec/frontend/diffs/mock_data/diff_code_quality.js62
-rw-r--r--spec/frontend/groups/components/group_folder_spec.js79
-rw-r--r--spec/frontend/groups/mock_data.js20
-rw-r--r--spec/frontend/header_search/components/app_spec.js263
-rw-r--r--spec/frontend/header_search/components/header_search_scoped_items_spec.js47
-rw-r--r--spec/frontend/header_search/mock_data.js75
-rw-r--r--spec/frontend/header_search/store/getters_spec.js8
-rw-r--r--spec/frontend/ide/components/commit_sidebar/empty_state_spec.js26
-rw-r--r--spec/frontend/ide/components/commit_sidebar/list_spec.js56
-rw-r--r--spec/frontend/ide/components/commit_sidebar/success_message_spec.js30
-rw-r--r--spec/frontend/ide/components/ide_tree_list_spec.js78
-rw-r--r--spec/frontend/ide/components/repo_editor_spec.js9
-rw-r--r--spec/frontend/issues/list/components/issues_list_app_spec.js61
-rw-r--r--spec/frontend/issues/list/utils_spec.js71
-rw-r--r--spec/frontend/issues/show/components/edited_spec.js83
-rw-r--r--spec/frontend/logs/components/environment_logs_spec.js370
-rw-r--r--spec/frontend/logs/components/log_advanced_filters_spec.js175
-rw-r--r--spec/frontend/logs/components/log_simple_filters_spec.js134
-rw-r--r--spec/frontend/logs/stores/actions_spec.js521
-rw-r--r--spec/frontend/logs/stores/getters_spec.js75
-rw-r--r--spec/frontend/notebook/cells/prompt_spec.js42
-rw-r--r--spec/frontend/notes/components/note_signed_out_widget_spec.js37
-rw-r--r--spec/frontend/pdf/index_spec.js39
-rw-r--r--spec/frontend/vue_shared/components/page_size_selector_spec.js44
-rw-r--r--spec/frontend/vue_shared/issuable/list/components/issuable_list_root_spec.js23
29 files changed, 824 insertions, 1830 deletions
diff --git a/spec/frontend/design_management/components/design_sidebar_spec.js b/spec/frontend/design_management/components/design_sidebar_spec.js
index 40968d9204a..f13796138bd 100644
--- a/spec/frontend/design_management/components/design_sidebar_spec.js
+++ b/spec/frontend/design_management/components/design_sidebar_spec.js
@@ -1,7 +1,6 @@
-import { GlCollapse, GlPopover } from '@gitlab/ui';
+import { GlAccordionItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
-import Cookies from '~/lib/utils/cookies';
import DesignDiscussion from '~/design_management/components/design_notes/design_discussion.vue';
import DesignNoteSignedOut from '~/design_management/components/design_notes/design_note_signed_out.vue';
import DesignSidebar from '~/design_management/components/design_sidebar.vue';
@@ -27,8 +26,6 @@ const $route = {
},
};
-const cookieKey = 'hide_design_resolved_comments_popover';
-
const mutate = jest.fn().mockResolvedValue();
describe('Design management design sidebar component', () => {
@@ -40,9 +37,7 @@ describe('Design management design sidebar component', () => {
const findUnresolvedDiscussions = () => wrapper.findAll('[data-testid="unresolved-discussion"]');
const findResolvedDiscussions = () => wrapper.findAll('[data-testid="resolved-discussion"]');
const findParticipants = () => wrapper.find(Participants);
- const findCollapsible = () => wrapper.find(GlCollapse);
- const findToggleResolvedCommentsButton = () => wrapper.find('[data-testid="resolved-comments"]');
- const findPopover = () => wrapper.find(GlPopover);
+ const findResolvedCommentsToggle = () => wrapper.find(GlAccordionItem);
const findNewDiscussionDisclaimer = () =>
wrapper.find('[data-testid="new-discussion-disclaimer"]');
@@ -61,7 +56,6 @@ describe('Design management design sidebar component', () => {
mutate,
},
},
- stubs: { GlPopover },
provide: {
registerPath: '/users/sign_up?redirect_to_referer=yes',
signInPath: '/users/sign_in?redirect_to_referer=yes',
@@ -119,7 +113,6 @@ describe('Design management design sidebar component', () => {
describe('when has discussions', () => {
beforeEach(() => {
- Cookies.set(cookieKey, true);
createComponent();
});
@@ -131,26 +124,23 @@ describe('Design management design sidebar component', () => {
expect(findResolvedDiscussions()).toHaveLength(1);
});
- it('has resolved comments collapsible collapsed', () => {
- expect(findCollapsible().attributes('visible')).toBeUndefined();
+ it('has resolved comments accordion item collapsed', () => {
+ expect(findResolvedCommentsToggle().props('visible')).toBe(false);
});
- it('emits toggleResolveComments event on resolve comments button click', () => {
- findToggleResolvedCommentsButton().vm.$emit('click');
+ it('emits toggleResolveComments event on resolve comments button click', async () => {
+ findResolvedCommentsToggle().vm.$emit('input', true);
+ await nextTick();
expect(wrapper.emitted('toggleResolvedComments')).toHaveLength(1);
});
- it('opens a collapsible when resolvedDiscussionsExpanded prop changes to true', async () => {
- expect(findCollapsible().attributes('visible')).toBeUndefined();
+ it('opens the accordion item when resolvedDiscussionsExpanded prop changes to true', async () => {
+ expect(findResolvedCommentsToggle().props('visible')).toBe(false);
wrapper.setProps({
resolvedDiscussionsExpanded: true,
});
await nextTick();
- expect(findCollapsible().attributes('visible')).toBe('true');
- });
-
- it('does not popover about resolved comments', () => {
- expect(findPopover().exists()).toBe(false);
+ expect(findResolvedCommentsToggle().props('visible')).toBe(true);
});
it('sends a mutation to set an active discussion when clicking on a discussion', () => {
@@ -232,36 +222,6 @@ describe('Design management design sidebar component', () => {
});
});
- describe('when showing resolved discussions for the first time', () => {
- beforeEach(() => {
- Cookies.set(cookieKey, false);
- createComponent();
- });
-
- it('renders a popover if we show resolved comments collapsible for the first time', () => {
- expect(findPopover().exists()).toBe(true);
- });
-
- it('scrolls to resolved threads link', () => {
- expect(scrollIntoViewMock).toHaveBeenCalled();
- });
-
- it('dismisses a popover on the outside click', async () => {
- wrapper.trigger('click');
- await nextTick();
- expect(findPopover().exists()).toBe(false);
- });
-
- it(`sets a ${cookieKey} cookie on clicking outside the popover`, () => {
- jest.spyOn(Cookies, 'set');
- wrapper.trigger('click');
- expect(Cookies.set).toHaveBeenCalledWith(cookieKey, 'true', {
- expires: 365 * 10,
- secure: false,
- });
- });
- });
-
describe('when user is not logged in', () => {
const findDesignNoteSignedOut = () => wrapper.findComponent(DesignNoteSignedOut);
@@ -292,7 +252,6 @@ describe('Design management design sidebar component', () => {
describe('design has discussions', () => {
beforeEach(() => {
- Cookies.set(cookieKey, true);
createComponent();
});
diff --git a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
index 0f2857821ea..3177a5e016c 100644
--- a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
+++ b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
@@ -88,57 +88,26 @@ exports[`Design management design index page renders design index 1`] = `
signinpath=""
/>
- <gl-button-stub
- buttontextclasses=""
- category="primary"
- class="link-inherit-color gl-text-body gl-text-decoration-none gl-font-weight-bold gl-mb-4"
- data-testid="resolved-comments"
- icon="chevron-right"
- id="resolved-comments"
- size="medium"
- variant="link"
+ <gl-accordion-stub
+ class="gl-mb-5"
+ headerlevel="3"
>
- Resolved Comments (1)
-
- </gl-button-stub>
-
- <gl-popover-stub
- container="popovercontainer"
- cssclasses=""
- placement="top"
- show="true"
- target="resolved-comments"
- title="Resolved Comments"
- >
- <p>
-
- Comments you resolve can be viewed and unresolved by going to the "Resolved Comments" section below
-
- </p>
-
- <a
- href="https://docs.gitlab.com/ee/user/project/issues/design_management.html#resolve-design-threads"
- rel="noopener noreferrer"
- target="_blank"
+ <gl-accordion-item-stub
+ headerclass="gl-mb-5!"
+ title="Resolved Comments (1)"
>
- Learn more about resolving comments
- </a>
- </gl-popover-stub>
-
- <gl-collapse-stub
- class="gl-mt-3"
- >
- <design-discussion-stub
- data-testid="resolved-discussion"
- designid="gid::/gitlab/Design/1"
- discussion="[object Object]"
- discussionwithopenform=""
- markdownpreviewpath="/project-path/preview_markdown?target_type=Issue"
- noteableid="gid::/gitlab/Design/1"
- registerpath=""
- signinpath=""
- />
- </gl-collapse-stub>
+ <design-discussion-stub
+ data-testid="resolved-discussion"
+ designid="gid::/gitlab/Design/1"
+ discussion="[object Object]"
+ discussionwithopenform=""
+ markdownpreviewpath="/project-path/preview_markdown?target_type=Issue"
+ noteableid="gid::/gitlab/Design/1"
+ registerpath=""
+ signinpath=""
+ />
+ </gl-accordion-item-stub>
+ </gl-accordion-stub>
</div>
</div>
diff --git a/spec/frontend/diffs/components/diff_code_quality_spec.js b/spec/frontend/diffs/components/diff_code_quality_spec.js
new file mode 100644
index 00000000000..81a817c47dc
--- /dev/null
+++ b/spec/frontend/diffs/components/diff_code_quality_spec.js
@@ -0,0 +1,66 @@
+import { GlIcon } from '@gitlab/ui';
+import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import DiffCodeQuality from '~/diffs/components/diff_code_quality.vue';
+import { SEVERITY_CLASSES, SEVERITY_ICONS } from '~/reports/codequality_report/constants';
+import { multipleFindingsArr } from '../mock_data/diff_code_quality';
+
+let wrapper;
+
+const findIcon = () => wrapper.findComponent(GlIcon);
+
+describe('DiffCodeQuality', () => {
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const createWrapper = (codeQuality, mountFunction = mountExtended) => {
+ return mountFunction(DiffCodeQuality, {
+ propsData: {
+ expandedLines: [],
+ line: 1,
+ codeQuality,
+ },
+ });
+ };
+
+ it('hides details and throws hideCodeQualityFindings event on close click', async () => {
+ wrapper = createWrapper(multipleFindingsArr);
+ expect(wrapper.findByTestId('diff-codequality').exists()).toBe(true);
+
+ await wrapper.findByTestId('diff-codequality-close').trigger('click');
+
+ expect(wrapper.emitted('hideCodeQualityFindings').length).toBe(1);
+ expect(wrapper.emitted().hideCodeQualityFindings[0][0]).toBe(wrapper.props('line'));
+ });
+
+ it('renders correct amount of list items for codequality array and their description', async () => {
+ wrapper = createWrapper(multipleFindingsArr);
+ const listItems = wrapper.findAll('li');
+
+ expect(wrapper.findAll('li').length).toBe(3);
+
+ listItems.wrappers.map((e, i) => {
+ return expect(e.text()).toEqual(multipleFindingsArr[i].description);
+ });
+ });
+
+ it.each`
+ severity
+ ${'info'}
+ ${'minor'}
+ ${'major'}
+ ${'critical'}
+ ${'blocker'}
+ ${'unknown'}
+ `('shows icon for $severity degradation', ({ severity }) => {
+ wrapper = createWrapper([{ severity }], shallowMountExtended);
+
+ expect(findIcon().exists()).toBe(true);
+
+ expect(findIcon().attributes()).toMatchObject({
+ class: `codequality-severity-icon ${SEVERITY_CLASSES[severity]}`,
+ name: SEVERITY_ICONS[severity],
+ size: '12',
+ });
+ });
+});
diff --git a/spec/frontend/diffs/components/diff_view_spec.js b/spec/frontend/diffs/components/diff_view_spec.js
index dfbe30e460b..15923a1c6de 100644
--- a/spec/frontend/diffs/components/diff_view_spec.js
+++ b/spec/frontend/diffs/components/diff_view_spec.js
@@ -1,7 +1,9 @@
import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import DiffView from '~/diffs/components/diff_view.vue';
+import DiffCodeQuality from '~/diffs/components/diff_code_quality.vue';
+import { diffCodeQuality } from '../mock_data/diff_code_quality';
describe('DiffView', () => {
const DiffExpansionCell = { template: `<div/>` };
@@ -12,7 +14,7 @@ describe('DiffView', () => {
const setSelectedCommentPosition = jest.fn();
const getDiffRow = (wrapper) => wrapper.findComponent(DiffRow).vm;
- const createWrapper = (props) => {
+ const createWrapper = (props, provide = {}) => {
Vue.use(Vuex);
const batchComments = {
@@ -46,9 +48,33 @@ describe('DiffView', () => {
...props,
};
const stubs = { DiffExpansionCell, DiffRow, DiffCommentCell, DraftNote };
- return shallowMount(DiffView, { propsData, store, stubs });
+ return shallowMount(DiffView, { propsData, store, stubs, provide });
};
+ it('does not render a codeQuality diff view when there is no finding', () => {
+ const wrapper = createWrapper();
+ expect(wrapper.findComponent(DiffCodeQuality).exists()).toBe(false);
+ });
+
+ it('does render a codeQuality diff view with the correct props when there is a finding & refactorCodeQualityInlineFindings flag is true ', async () => {
+ const wrapper = createWrapper(diffCodeQuality, {
+ glFeatures: { refactorCodeQualityInlineFindings: true },
+ });
+ wrapper.findComponent(DiffRow).vm.$emit('toggleCodeQualityFindings', 2);
+ await nextTick();
+ expect(wrapper.findComponent(DiffCodeQuality).exists()).toBe(true);
+ expect(wrapper.findComponent(DiffCodeQuality).props().codeQuality.length).not.toBe(0);
+ });
+
+ it('does not render a codeQuality diff view when there is a finding & refactorCodeQualityInlineFindings flag is false ', async () => {
+ const wrapper = createWrapper(diffCodeQuality, {
+ glFeatures: { refactorCodeQualityInlineFindings: false },
+ });
+ wrapper.findComponent(DiffRow).vm.$emit('toggleCodeQualityFindings', 2);
+ await nextTick();
+ expect(wrapper.findComponent(DiffCodeQuality).exists()).toBe(false);
+ });
+
it.each`
type | side | container | sides | total
${'parallel'} | ${'left'} | ${'.old'} | ${{ left: { lineDraft: {}, renderDiscussion: true }, right: { lineDraft: {}, renderDiscussion: true } }} | ${2}
diff --git a/spec/frontend/diffs/mock_data/diff_code_quality.js b/spec/frontend/diffs/mock_data/diff_code_quality.js
new file mode 100644
index 00000000000..2ca421a20b4
--- /dev/null
+++ b/spec/frontend/diffs/mock_data/diff_code_quality.js
@@ -0,0 +1,62 @@
+export const multipleFindingsArr = [
+ {
+ severity: 'minor',
+ description: 'Unexpected Debugger Statement.',
+ line: 2,
+ },
+ {
+ severity: 'major',
+ description:
+ 'Function `aVeryLongFunction` has 52 lines of code (exceeds 25 allowed). Consider refactoring.',
+ line: 3,
+ },
+ {
+ severity: 'minor',
+ description: 'Arrow function has too many statements (52). Maximum allowed is 30.',
+ line: 3,
+ },
+];
+
+export const multipleFindings = {
+ filePath: 'index.js',
+ codequality: multipleFindingsArr,
+};
+
+export const singularFinding = {
+ filePath: 'index.js',
+ codequality: [multipleFindingsArr[0]],
+};
+
+export const diffCodeQuality = {
+ diffFile: { file_hash: '123' },
+ diffLines: [
+ {
+ left: {
+ type: 'old',
+ old_line: 1,
+ new_line: null,
+ codequality: [],
+ lineDraft: {},
+ },
+ },
+ {
+ left: {
+ type: null,
+ old_line: 2,
+ new_line: 1,
+ codequality: [],
+ lineDraft: {},
+ },
+ },
+ {
+ left: {
+ type: 'new',
+ old_line: null,
+ new_line: 2,
+
+ codequality: [multipleFindingsArr[0]],
+ lineDraft: {},
+ },
+ },
+ ],
+};
diff --git a/spec/frontend/groups/components/group_folder_spec.js b/spec/frontend/groups/components/group_folder_spec.js
index 98b7c2dd6c6..f223333360d 100644
--- a/spec/frontend/groups/components/group_folder_spec.js
+++ b/spec/frontend/groups/components/group_folder_spec.js
@@ -1,65 +1,50 @@
-import Vue, { nextTick } from 'vue';
-
-import groupFolderComponent from '~/groups/components/group_folder.vue';
-import groupItemComponent from '~/groups/components/group_item.vue';
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import GroupFolder from '~/groups/components/group_folder.vue';
+import GroupItem from '~/groups/components/group_item.vue';
+import { MAX_CHILDREN_COUNT } from '~/groups/constants';
import { mockGroups, mockParentGroupItem } from '../mock_data';
-const createComponent = (groups = mockGroups, parentGroup = mockParentGroupItem) => {
- const Component = Vue.extend(groupFolderComponent);
-
- return new Component({
- propsData: {
- groups,
- parentGroup,
- },
- });
-};
+describe('GroupFolder component', () => {
+ let wrapper;
-describe('GroupFolderComponent', () => {
- let vm;
+ Vue.component('GroupItem', GroupItem);
- beforeEach(async () => {
- Vue.component('GroupItem', groupItemComponent);
+ const findLink = () => wrapper.find('a');
- vm = createComponent();
- vm.$mount();
-
- await nextTick();
- });
+ const createComponent = ({ groups = mockGroups, parentGroup = mockParentGroupItem } = {}) =>
+ shallowMount(GroupFolder, {
+ propsData: {
+ groups,
+ parentGroup,
+ },
+ });
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
- describe('computed', () => {
- describe('hasMoreChildren', () => {
- it('should return false when childrenCount of group is less than MAX_CHILDREN_COUNT', () => {
- expect(vm.hasMoreChildren).toBeFalsy();
- });
- });
+ it('does not render more children stats link when children count of group is under limit', () => {
+ wrapper = createComponent();
- describe('moreChildrenStats', () => {
- it('should return message with count of excess children over MAX_CHILDREN_COUNT limit', () => {
- expect(vm.moreChildrenStats).toBe('3 more items');
- });
- });
+ expect(findLink().exists()).toBe(false);
});
- describe('template', () => {
- it('should render component template correctly', () => {
- expect(vm.$el.classList.contains('group-list-tree')).toBeTruthy();
- expect(vm.$el.querySelectorAll('li.group-row').length).toBe(7);
+ it('renders text of count of excess children when children count of group is over limit', () => {
+ const childrenCount = MAX_CHILDREN_COUNT + 1;
+ wrapper = createComponent({
+ parentGroup: {
+ ...mockParentGroupItem,
+ childrenCount,
+ },
});
- it('should render more children link when groups list has children over MAX_CHILDREN_COUNT limit', () => {
- const parentGroup = { ...mockParentGroupItem };
- parentGroup.childrenCount = 21;
+ expect(findLink().text()).toBe(`${childrenCount} more items`);
+ });
- const newVm = createComponent(mockGroups, parentGroup);
- newVm.$mount();
+ it('renders group items', () => {
+ wrapper = createComponent();
- expect(newVm.$el.querySelector('li.group-row a.has-more-items')).toBeDefined();
- newVm.$destroy();
- });
+ expect(wrapper.findAllComponents(GroupItem)).toHaveLength(7);
});
});
diff --git a/spec/frontend/groups/mock_data.js b/spec/frontend/groups/mock_data.js
index 603cb27deec..65a62876893 100644
--- a/spec/frontend/groups/mock_data.js
+++ b/spec/frontend/groups/mock_data.js
@@ -5,26 +5,6 @@ export const ITEM_TYPE = {
GROUP: 'group',
};
-export const GROUP_VISIBILITY_TYPE = {
- public: 'Public - The group and any public projects can be viewed without any authentication.',
- internal:
- 'Internal - The group and any internal projects can be viewed by any logged in user except external users.',
- private: 'Private - The group and its projects can only be viewed by members.',
-};
-
-export const PROJECT_VISIBILITY_TYPE = {
- public: 'Public - The project can be accessed without any authentication.',
- internal: 'Internal - The project can be accessed by any logged in user except external users.',
- private:
- 'Private - Project access must be granted explicitly to each user. If this project is part of a group, access will be granted to members of the group.',
-};
-
-export const VISIBILITY_TYPE_ICON = {
- public: 'earth',
- internal: 'shield',
- private: 'lock',
-};
-
export const mockParentGroupItem = {
id: 55,
name: 'hardware',
diff --git a/spec/frontend/header_search/components/app_spec.js b/spec/frontend/header_search/components/app_spec.js
index f0de5b083ae..5f2b71a22c5 100644
--- a/spec/frontend/header_search/components/app_spec.js
+++ b/spec/frontend/header_search/components/app_spec.js
@@ -1,22 +1,32 @@
-import { GlSearchBoxByType } from '@gitlab/ui';
+import { GlSearchBoxByType, GlToken, GlIcon } from '@gitlab/ui';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { s__, sprintf } from '~/locale';
import HeaderSearchApp from '~/header_search/components/app.vue';
import HeaderSearchAutocompleteItems from '~/header_search/components/header_search_autocomplete_items.vue';
import HeaderSearchDefaultItems from '~/header_search/components/header_search_default_items.vue';
import HeaderSearchScopedItems from '~/header_search/components/header_search_scoped_items.vue';
-import { SEARCH_INPUT_DESCRIPTION, SEARCH_RESULTS_DESCRIPTION } from '~/header_search/constants';
+import {
+ SEARCH_INPUT_DESCRIPTION,
+ SEARCH_RESULTS_DESCRIPTION,
+ SEARCH_BOX_INDEX,
+ ICON_PROJECT,
+ ICON_GROUP,
+ ICON_SUBGROUP,
+ SCOPE_TOKEN_MAX_LENGTH,
+} from '~/header_search/constants';
import DropdownKeyboardNavigation from '~/vue_shared/components/dropdown_keyboard_navigation.vue';
import { ENTER_KEY } from '~/lib/utils/keys';
import { visitUrl } from '~/lib/utils/url_utility';
+import { truncate } from '~/lib/utils/text_utility';
import {
MOCK_SEARCH,
MOCK_SEARCH_QUERY,
MOCK_USERNAME,
MOCK_DEFAULT_SEARCH_OPTIONS,
MOCK_SCOPED_SEARCH_OPTIONS,
- MOCK_SORTED_AUTOCOMPLETE_OPTIONS,
+ MOCK_SEARCH_CONTEXT_FULL,
} from '../mock_data';
Vue.use(Vuex);
@@ -52,11 +62,26 @@ describe('HeaderSearchApp', () => {
});
};
+ const formatScopeName = (scopeName) => {
+ if (!scopeName) {
+ return false;
+ }
+ const searchResultsScope = s__('GlobalSearch|in %{scope}');
+ return truncate(
+ sprintf(searchResultsScope, {
+ scope: scopeName,
+ }),
+ SCOPE_TOKEN_MAX_LENGTH,
+ );
+ };
+
afterEach(() => {
wrapper.destroy();
});
+ const findHeaderSearchForm = () => wrapper.findByTestId('header-search-form');
const findHeaderSearchInput = () => wrapper.findComponent(GlSearchBoxByType);
+ const findScopeToken = () => wrapper.findComponent(GlToken);
const findHeaderSearchDropdown = () => wrapper.findByTestId('header-search-dropdown-menu');
const findHeaderSearchDefaultItems = () => wrapper.findComponent(HeaderSearchDefaultItems);
const findHeaderSearchScopedItems = () => wrapper.findComponent(HeaderSearchScopedItems);
@@ -106,53 +131,38 @@ describe('HeaderSearchApp', () => {
});
describe.each`
- search | showDefault | showScoped | showAutocomplete | showDropdownNavigation
- ${null} | ${true} | ${false} | ${false} | ${true}
- ${''} | ${true} | ${false} | ${false} | ${true}
- ${'1'} | ${false} | ${false} | ${false} | ${false}
- ${')'} | ${false} | ${false} | ${false} | ${false}
- ${'t'} | ${false} | ${false} | ${true} | ${true}
- ${'te'} | ${false} | ${true} | ${true} | ${true}
- ${'tes'} | ${false} | ${true} | ${true} | ${true}
- ${MOCK_SEARCH} | ${false} | ${true} | ${true} | ${true}
- `(
- 'Header Search Dropdown Items',
- ({ search, showDefault, showScoped, showAutocomplete, showDropdownNavigation }) => {
- describe(`when search is ${search}`, () => {
- beforeEach(() => {
- window.gon.current_username = MOCK_USERNAME;
- createComponent(
- { search },
- {
- autocompleteGroupedSearchOptions: () =>
- search.match(/^[A-Za-z]+$/g) ? MOCK_SORTED_AUTOCOMPLETE_OPTIONS : [],
- },
- );
- findHeaderSearchInput().vm.$emit('click');
- });
+ search | showDefault | showScoped | showAutocomplete
+ ${null} | ${true} | ${false} | ${false}
+ ${''} | ${true} | ${false} | ${false}
+ ${'t'} | ${false} | ${false} | ${true}
+ ${'te'} | ${false} | ${false} | ${true}
+ ${'tes'} | ${false} | ${true} | ${true}
+ ${MOCK_SEARCH} | ${false} | ${true} | ${true}
+ `('Header Search Dropdown Items', ({ search, showDefault, showScoped, showAutocomplete }) => {
+ describe(`when search is ${search}`, () => {
+ beforeEach(() => {
+ window.gon.current_username = MOCK_USERNAME;
+ createComponent({ search }, {});
+ findHeaderSearchInput().vm.$emit('click');
+ });
- it(`should${showDefault ? '' : ' not'} render the Default Dropdown Items`, () => {
- expect(findHeaderSearchDefaultItems().exists()).toBe(showDefault);
- });
+ it(`should${showDefault ? '' : ' not'} render the Default Dropdown Items`, () => {
+ expect(findHeaderSearchDefaultItems().exists()).toBe(showDefault);
+ });
- it(`should${showScoped ? '' : ' not'} render the Scoped Dropdown Items`, () => {
- expect(findHeaderSearchScopedItems().exists()).toBe(showScoped);
- });
+ it(`should${showScoped ? '' : ' not'} render the Scoped Dropdown Items`, () => {
+ expect(findHeaderSearchScopedItems().exists()).toBe(showScoped);
+ });
- it(`should${
- showAutocomplete ? '' : ' not'
- } render the Autocomplete Dropdown Items`, () => {
- expect(findHeaderSearchAutocompleteItems().exists()).toBe(showAutocomplete);
- });
+ it(`should${showAutocomplete ? '' : ' not'} render the Autocomplete Dropdown Items`, () => {
+ expect(findHeaderSearchAutocompleteItems().exists()).toBe(showAutocomplete);
+ });
- it(`should${
- showDropdownNavigation ? '' : ' not'
- } render the Dropdown Navigation Component`, () => {
- expect(findDropdownKeyboardNavigation().exists()).toBe(showDropdownNavigation);
- });
+ it(`should render the Dropdown Navigation Component`, () => {
+ expect(findDropdownKeyboardNavigation().exists()).toBe(true);
});
- },
- );
+ });
+ });
describe.each`
username | showDropdown | expectedDesc
@@ -185,12 +195,18 @@ describe('HeaderSearchApp', () => {
`(
'Search Results Description',
({ username, showDropdown, search, loading, searchOptions, expectedDesc }) => {
- describe(`search is ${search}, loading is ${loading}, and showSearchDropdown is ${
- Boolean(username) && showDropdown
- }`, () => {
+ describe(`search is "${search}", loading is ${loading}, and showSearchDropdown is ${showDropdown}`, () => {
beforeEach(() => {
window.gon.current_username = username;
- createComponent({ search, loading }, { searchOptions: () => searchOptions });
+ createComponent(
+ {
+ search,
+ loading,
+ },
+ {
+ searchOptions: () => searchOptions,
+ },
+ );
findHeaderSearchInput().vm.$emit(showDropdown ? 'click' : '');
});
@@ -200,6 +216,121 @@ describe('HeaderSearchApp', () => {
});
},
);
+
+ describe('input box', () => {
+ describe.each`
+ search | searchOptions | hasToken
+ ${MOCK_SEARCH} | ${[MOCK_SCOPED_SEARCH_OPTIONS[0]]} | ${true}
+ ${MOCK_SEARCH} | ${[MOCK_SCOPED_SEARCH_OPTIONS[1]]} | ${true}
+ ${MOCK_SEARCH} | ${[MOCK_SCOPED_SEARCH_OPTIONS[2]]} | ${true}
+ ${MOCK_SEARCH} | ${[MOCK_SCOPED_SEARCH_OPTIONS[3]]} | ${true}
+ ${MOCK_SEARCH} | ${[MOCK_SCOPED_SEARCH_OPTIONS[4]]} | ${true}
+ ${'te'} | ${[MOCK_SCOPED_SEARCH_OPTIONS[5]]} | ${false}
+ ${'x'} | ${[]} | ${false}
+ `('token', ({ search, searchOptions, hasToken }) => {
+ beforeEach(() => {
+ window.gon.current_username = MOCK_USERNAME;
+ createComponent(
+ { search },
+ {
+ searchOptions: () => searchOptions,
+ },
+ );
+ });
+
+ it(`${hasToken ? 'is' : 'is NOT'} rendered when data set has type "${
+ searchOptions[0]?.html_id
+ }"`, () => {
+ expect(findScopeToken().exists()).toBe(hasToken);
+ });
+
+ it(`text ${hasToken ? 'is correctly' : 'is NOT'} rendered when text is "${
+ searchOptions[0]?.scope || searchOptions[0]?.description
+ }"`, () => {
+ expect(findScopeToken().exists() && findScopeToken().text()).toBe(
+ formatScopeName(searchOptions[0]?.scope || searchOptions[0]?.description),
+ );
+ });
+ });
+ });
+
+ describe('form wrapper', () => {
+ describe.each`
+ searchContext | search | searchOptions
+ ${MOCK_SEARCH_CONTEXT_FULL} | ${null} | ${[]}
+ ${MOCK_SEARCH_CONTEXT_FULL} | ${MOCK_SEARCH} | ${[]}
+ ${MOCK_SEARCH_CONTEXT_FULL} | ${MOCK_SEARCH} | ${MOCK_SCOPED_SEARCH_OPTIONS}
+ ${null} | ${MOCK_SEARCH} | ${MOCK_SCOPED_SEARCH_OPTIONS}
+ ${null} | ${null} | ${MOCK_SCOPED_SEARCH_OPTIONS}
+ ${null} | ${null} | ${[]}
+ `('', ({ searchContext, search, searchOptions }) => {
+ beforeEach(() => {
+ window.gon.current_username = MOCK_USERNAME;
+
+ createComponent({ search, searchContext }, { searchOptions: () => searchOptions });
+
+ findHeaderSearchInput().vm.$emit('click');
+ });
+
+ const hasIcon = Boolean(searchContext?.group);
+ const isSearching = Boolean(search);
+ const isActive = Boolean(searchOptions.length > 0);
+
+ it(`${hasIcon ? 'with' : 'without'} search context classes contain "${
+ hasIcon ? 'has-icon' : 'has-no-icon'
+ }"`, () => {
+ const iconClassRegex = hasIcon ? 'has-icon' : 'has-no-icon';
+ expect(findHeaderSearchForm().classes()).toContain(iconClassRegex);
+ });
+
+ it(`${isSearching ? 'with' : 'without'} search string classes contain "${
+ isSearching ? 'is-searching' : 'is-not-searching'
+ }"`, () => {
+ const iconClassRegex = isSearching ? 'is-searching' : 'is-not-searching';
+ expect(findHeaderSearchForm().classes()).toContain(iconClassRegex);
+ });
+
+ it(`${isActive ? 'with' : 'without'} search results classes contain "${
+ isActive ? 'is-active' : 'is-not-active'
+ }"`, () => {
+ const iconClassRegex = isActive ? 'is-active' : 'is-not-active';
+ expect(findHeaderSearchForm().classes()).toContain(iconClassRegex);
+ });
+ });
+ });
+
+ describe.each`
+ search | searchOptions | hasIcon | iconName
+ ${MOCK_SEARCH} | ${[MOCK_SCOPED_SEARCH_OPTIONS[0]]} | ${true} | ${ICON_PROJECT}
+ ${MOCK_SEARCH} | ${[MOCK_SCOPED_SEARCH_OPTIONS[2]]} | ${true} | ${ICON_GROUP}
+ ${MOCK_SEARCH} | ${[MOCK_SCOPED_SEARCH_OPTIONS[3]]} | ${true} | ${ICON_SUBGROUP}
+ ${MOCK_SEARCH} | ${[MOCK_SCOPED_SEARCH_OPTIONS[4]]} | ${false} | ${false}
+ `('token', ({ search, searchOptions, hasIcon, iconName }) => {
+ beforeEach(() => {
+ window.gon.current_username = MOCK_USERNAME;
+ createComponent(
+ { search },
+ {
+ searchOptions: () => searchOptions,
+ },
+ );
+ });
+
+ it(`icon for data set type "${searchOptions[0]?.html_id}" ${
+ hasIcon ? 'is' : 'is NOT'
+ } rendered`, () => {
+ expect(findScopeToken().findComponent(GlIcon).exists()).toBe(hasIcon);
+ });
+
+ it(`render ${iconName ? `"${iconName}"` : 'NO'} icon for data set type "${
+ searchOptions[0]?.html_id
+ }"`, () => {
+ expect(
+ findScopeToken().findComponent(GlIcon).exists() &&
+ findScopeToken().findComponent(GlIcon).attributes('name'),
+ ).toBe(iconName);
+ });
+ });
});
describe('events', () => {
@@ -285,18 +416,20 @@ describe('HeaderSearchApp', () => {
});
describe('computed', () => {
- describe('currentFocusedOption', () => {
- const MOCK_INDEX = 1;
-
+ describe.each`
+ MOCK_INDEX | search
+ ${1} | ${null}
+ ${SEARCH_BOX_INDEX} | ${'test'}
+ ${2} | ${'test1'}
+ `('currentFocusedOption', ({ MOCK_INDEX, search }) => {
beforeEach(() => {
- createComponent();
+ createComponent({ search });
window.gon.current_username = MOCK_USERNAME;
findHeaderSearchInput().vm.$emit('click');
});
- it(`when currentFocusIndex changes to ${MOCK_INDEX} updates the data to searchOptions[${MOCK_INDEX}]`, async () => {
+ it(`when currentFocusIndex changes to ${MOCK_INDEX} updates the data to searchOptions[${MOCK_INDEX}]`, () => {
findDropdownKeyboardNavigation().vm.$emit('change', MOCK_INDEX);
- await nextTick();
expect(wrapper.vm.currentFocusedOption).toBe(MOCK_DEFAULT_SEARCH_OPTIONS[MOCK_INDEX]);
});
});
@@ -308,15 +441,25 @@ describe('HeaderSearchApp', () => {
createComponent();
});
- it('onKey-enter submits a search', async () => {
+ it('onKey-enter submits a search', () => {
findHeaderSearchInput().vm.$emit('keydown', new KeyboardEvent({ key: ENTER_KEY }));
- await nextTick();
-
expect(visitUrl).toHaveBeenCalledWith(MOCK_SEARCH_QUERY);
});
});
+ describe('with less than min characters and no dropdown results', () => {
+ beforeEach(() => {
+ createComponent({ search: 'x' });
+ });
+
+ it('onKey-enter will NOT submit a search', () => {
+ findHeaderSearchInput().vm.$emit('keydown', new KeyboardEvent({ key: ENTER_KEY }));
+
+ expect(visitUrl).not.toHaveBeenCalledWith(MOCK_SEARCH_QUERY);
+ });
+ });
+
describe('with currentFocusedOption', () => {
const MOCK_INDEX = 1;
@@ -326,9 +469,9 @@ describe('HeaderSearchApp', () => {
findHeaderSearchInput().vm.$emit('click');
});
- it('onKey-enter clicks the selected dropdown item rather than submitting a search', async () => {
+ it('onKey-enter clicks the selected dropdown item rather than submitting a search', () => {
findDropdownKeyboardNavigation().vm.$emit('change', MOCK_INDEX);
- await nextTick();
+
findHeaderSearchInput().vm.$emit('keydown', new KeyboardEvent({ key: ENTER_KEY }));
expect(visitUrl).toHaveBeenCalledWith(MOCK_DEFAULT_SEARCH_OPTIONS[MOCK_INDEX].url);
});
diff --git a/spec/frontend/header_search/components/header_search_scoped_items_spec.js b/spec/frontend/header_search/components/header_search_scoped_items_spec.js
index 8788fb23458..2db9f71d702 100644
--- a/spec/frontend/header_search/components/header_search_scoped_items_spec.js
+++ b/spec/frontend/header_search/components/header_search_scoped_items_spec.js
@@ -1,9 +1,11 @@
-import { GlDropdownItem, GlDropdownDivider } from '@gitlab/ui';
+import { GlDropdownItem, GlToken, GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import { trimText } from 'helpers/text_helper';
import HeaderSearchScopedItems from '~/header_search/components/header_search_scoped_items.vue';
+import { truncate } from '~/lib/utils/text_utility';
+import { MSG_IN_ALL_GITLAB, SCOPE_TOKEN_MAX_LENGTH } from '~/header_search/constants';
import {
MOCK_SEARCH,
MOCK_SCOPED_SEARCH_OPTIONS,
@@ -41,9 +43,12 @@ describe('HeaderSearchScopedItems', () => {
});
const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
- const findGlDropdownDivider = () => wrapper.findComponent(GlDropdownDivider);
const findFirstDropdownItem = () => findDropdownItems().at(0);
const findDropdownItemTitles = () => findDropdownItems().wrappers.map((w) => trimText(w.text()));
+ const findScopeTokens = () => wrapper.findAllComponents(GlToken);
+ const findScopeTokensText = () => findScopeTokens().wrappers.map((w) => trimText(w.text()));
+ const findScopeTokensIcons = () =>
+ findScopeTokens().wrappers.map((w) => w.findAllComponents(GlIcon));
const findDropdownItemAriaLabels = () =>
findDropdownItems().wrappers.map((w) => trimText(w.attributes('aria-label')));
const findDropdownItemLinks = () => findDropdownItems().wrappers.map((w) => w.attributes('href'));
@@ -59,15 +64,31 @@ describe('HeaderSearchScopedItems', () => {
});
it('renders titles correctly', () => {
+ findDropdownItemTitles().forEach((title) => expect(title).toContain(MOCK_SEARCH));
+ });
+
+ it('renders scope names correctly', () => {
const expectedTitles = MOCK_SCOPED_SEARCH_OPTIONS.map((o) =>
- trimText(`"${MOCK_SEARCH}" ${o.description} ${o.scope || ''}`),
+ truncate(trimText(`in ${o.description || o.scope}`), SCOPE_TOKEN_MAX_LENGTH),
);
- expect(findDropdownItemTitles()).toStrictEqual(expectedTitles);
+
+ expect(findScopeTokensText()).toStrictEqual(expectedTitles);
+ });
+
+ it('renders scope icons correctly', () => {
+ findScopeTokensIcons().forEach((icon, i) => {
+ const w = icon.wrappers[0];
+ expect(w?.attributes('name')).toBe(MOCK_SCOPED_SEARCH_OPTIONS[i].icon);
+ });
+ });
+
+ it(`renders scope ${MSG_IN_ALL_GITLAB} correctly`, () => {
+ expect(findScopeTokens().at(-1).findComponent(GlIcon).exists()).toBe(false);
});
it('renders aria-labels correctly', () => {
const expectedLabels = MOCK_SCOPED_SEARCH_OPTIONS.map((o) =>
- trimText(`${MOCK_SEARCH} ${o.description} ${o.scope || ''}`),
+ trimText(`${MOCK_SEARCH} ${o.description || o.icon} ${o.scope || ''}`),
);
expect(findDropdownItemAriaLabels()).toStrictEqual(expectedLabels);
});
@@ -98,21 +119,5 @@ describe('HeaderSearchScopedItems', () => {
});
});
});
-
- describe.each`
- autosuggestResults | showDivider
- ${[]} | ${false}
- ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS} | ${true}
- `('scoped search items', ({ autosuggestResults, showDivider }) => {
- describe(`when when we have ${autosuggestResults.length} auto-sugest results`, () => {
- beforeEach(() => {
- createComponent({}, { autocompleteGroupedSearchOptions: () => autosuggestResults }, {});
- });
-
- it(`divider should${showDivider ? '' : ' not'} be shown`, () => {
- expect(findGlDropdownDivider().exists()).toBe(showDivider);
- });
- });
- });
});
});
diff --git a/spec/frontend/header_search/mock_data.js b/spec/frontend/header_search/mock_data.js
index b6f0fdcc29d..8ccd7fb17e3 100644
--- a/spec/frontend/header_search/mock_data.js
+++ b/spec/frontend/header_search/mock_data.js
@@ -4,9 +4,12 @@ import {
MSG_MR_ASSIGNED_TO_ME,
MSG_MR_IM_REVIEWER,
MSG_MR_IVE_CREATED,
- MSG_IN_PROJECT,
- MSG_IN_GROUP,
MSG_IN_ALL_GITLAB,
+ PROJECTS_CATEGORY,
+ ICON_PROJECT,
+ GROUPS_CATEGORY,
+ ICON_GROUP,
+ ICON_SUBGROUP,
} from '~/header_search/constants';
export const MOCK_USERNAME = 'anyone';
@@ -27,12 +30,24 @@ export const MOCK_PROJECT = {
path: '/mock-project',
};
+export const MOCK_PROJECT_LONG = {
+ id: 124,
+ name: 'Mock Project Name That Is Ridiculously Long And It Goes Forever',
+ path: '/mock-project-name-that-is-ridiculously-long-and-it-goes-forever',
+};
+
export const MOCK_GROUP = {
id: 321,
name: 'MockGroup',
path: '/mock-group',
};
+export const MOCK_SUBGROUP = {
+ id: 322,
+ name: 'MockSubGroup',
+ path: `${MOCK_GROUP}/mock-subgroup`,
+};
+
export const MOCK_SEARCH_QUERY = 'http://gitlab.com/search?search=test';
export const MOCK_SEARCH = 'test';
@@ -44,6 +59,20 @@ export const MOCK_SEARCH_CONTEXT = {
group_metadata: {},
};
+export const MOCK_SEARCH_CONTEXT_FULL = {
+ group: {
+ id: 31,
+ name: 'testGroup',
+ full_name: 'testGroup',
+ },
+ group_metadata: {
+ group_path: 'testGroup',
+ name: 'testGroup',
+ issues_path: '/groups/testGroup/-/issues',
+ mr_path: '/groups/testGroup/-/merge_requests',
+ },
+};
+
export const MOCK_DEFAULT_SEARCH_OPTIONS = [
{
html_id: 'default-issues-assigned',
@@ -76,13 +105,51 @@ export const MOCK_SCOPED_SEARCH_OPTIONS = [
{
html_id: 'scoped-in-project',
scope: MOCK_PROJECT.name,
- description: MSG_IN_PROJECT,
+ scopeCategory: PROJECTS_CATEGORY,
+ icon: ICON_PROJECT,
+ url: MOCK_PROJECT.path,
+ },
+ {
+ html_id: 'scoped-in-project-long',
+ scope: MOCK_PROJECT_LONG.name,
+ scopeCategory: PROJECTS_CATEGORY,
+ icon: ICON_PROJECT,
+ url: MOCK_PROJECT_LONG.path,
+ },
+ {
+ html_id: 'scoped-in-group',
+ scope: MOCK_GROUP.name,
+ scopeCategory: GROUPS_CATEGORY,
+ icon: ICON_GROUP,
+ url: MOCK_GROUP.path,
+ },
+ {
+ html_id: 'scoped-in-subgroup',
+ scope: MOCK_SUBGROUP.name,
+ scopeCategory: GROUPS_CATEGORY,
+ icon: ICON_SUBGROUP,
+ url: MOCK_SUBGROUP.path,
+ },
+ {
+ html_id: 'scoped-in-all',
+ description: MSG_IN_ALL_GITLAB,
+ url: MOCK_ALL_PATH,
+ },
+];
+
+export const MOCK_SCOPED_SEARCH_OPTIONS_DEF = [
+ {
+ html_id: 'scoped-in-project',
+ scope: MOCK_PROJECT.name,
+ scopeCategory: PROJECTS_CATEGORY,
+ icon: ICON_PROJECT,
url: MOCK_PROJECT.path,
},
{
html_id: 'scoped-in-group',
scope: MOCK_GROUP.name,
- description: MSG_IN_GROUP,
+ scopeCategory: GROUPS_CATEGORY,
+ icon: ICON_GROUP,
url: MOCK_GROUP.path,
},
{
diff --git a/spec/frontend/header_search/store/getters_spec.js b/spec/frontend/header_search/store/getters_spec.js
index d3510de1439..c76be3c0360 100644
--- a/spec/frontend/header_search/store/getters_spec.js
+++ b/spec/frontend/header_search/store/getters_spec.js
@@ -9,6 +9,7 @@ import {
MOCK_SEARCH_CONTEXT,
MOCK_DEFAULT_SEARCH_OPTIONS,
MOCK_SCOPED_SEARCH_OPTIONS,
+ MOCK_SCOPED_SEARCH_OPTIONS_DEF,
MOCK_PROJECT,
MOCK_GROUP,
MOCK_ALL_PATH,
@@ -284,7 +285,7 @@ describe('Header Search Store Getters', () => {
it('returns the correct array', () => {
expect(getters.scopedSearchOptions(state, mockGetters)).toStrictEqual(
- MOCK_SCOPED_SEARCH_OPTIONS,
+ MOCK_SCOPED_SEARCH_OPTIONS_DEF,
);
});
});
@@ -308,6 +309,11 @@ describe('Header Search Store Getters', () => {
${MOCK_SEARCH} | ${MOCK_DEFAULT_SEARCH_OPTIONS} | ${MOCK_SCOPED_SEARCH_OPTIONS} | ${[]} | ${MOCK_SCOPED_SEARCH_OPTIONS}
${MOCK_SEARCH} | ${MOCK_DEFAULT_SEARCH_OPTIONS} | ${[]} | ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS} | ${MOCK_SORTED_AUTOCOMPLETE_OPTIONS}
${MOCK_SEARCH} | ${MOCK_DEFAULT_SEARCH_OPTIONS} | ${MOCK_SCOPED_SEARCH_OPTIONS} | ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS} | ${MOCK_SCOPED_SEARCH_OPTIONS.concat(MOCK_SORTED_AUTOCOMPLETE_OPTIONS)}
+ ${1} | ${MOCK_DEFAULT_SEARCH_OPTIONS} | ${[]} | ${[]} | ${[]}
+ ${'('} | ${MOCK_DEFAULT_SEARCH_OPTIONS} | ${[]} | ${[]} | ${[]}
+ ${'t'} | ${MOCK_DEFAULT_SEARCH_OPTIONS} | ${MOCK_SCOPED_SEARCH_OPTIONS} | ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS} | ${MOCK_SORTED_AUTOCOMPLETE_OPTIONS}
+ ${'te'} | ${MOCK_DEFAULT_SEARCH_OPTIONS} | ${MOCK_SCOPED_SEARCH_OPTIONS} | ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS} | ${MOCK_SORTED_AUTOCOMPLETE_OPTIONS}
+ ${'tes'} | ${MOCK_DEFAULT_SEARCH_OPTIONS} | ${MOCK_SCOPED_SEARCH_OPTIONS} | ${MOCK_GROUPED_AUTOCOMPLETE_OPTIONS} | ${MOCK_SCOPED_SEARCH_OPTIONS.concat(MOCK_SORTED_AUTOCOMPLETE_OPTIONS)}
`(
'searchOptions',
({
diff --git a/spec/frontend/ide/components/commit_sidebar/empty_state_spec.js b/spec/frontend/ide/components/commit_sidebar/empty_state_spec.js
index 4f81c0aa5d3..7c48c0e6f95 100644
--- a/spec/frontend/ide/components/commit_sidebar/empty_state_spec.js
+++ b/spec/frontend/ide/components/commit_sidebar/empty_state_spec.js
@@ -1,29 +1,21 @@
-import Vue from 'vue';
-import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
-import emptyState from '~/ide/components/commit_sidebar/empty_state.vue';
+import { shallowMount } from '@vue/test-utils';
+import EmptyState from '~/ide/components/commit_sidebar/empty_state.vue';
import { createStore } from '~/ide/stores';
-describe('IDE commit panel empty state', () => {
- let vm;
- let store;
+describe('IDE commit panel EmptyState component', () => {
+ let wrapper;
beforeEach(() => {
- store = createStore();
-
- const Component = Vue.extend(emptyState);
-
- Vue.set(store.state, 'noChangesStateSvgPath', 'no-changes');
-
- vm = createComponentWithStore(Component, store);
-
- vm.$mount();
+ const store = createStore();
+ store.state.noChangesStateSvgPath = 'no-changes';
+ wrapper = shallowMount(EmptyState, { store });
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
it('renders no changes text when last commit message is empty', () => {
- expect(vm.$el.textContent).toContain('No changes');
+ expect(wrapper.find('h4').text()).toBe('No changes');
});
});
diff --git a/spec/frontend/ide/components/commit_sidebar/list_spec.js b/spec/frontend/ide/components/commit_sidebar/list_spec.js
index 1d42512c9ee..81c81fc0a9f 100644
--- a/spec/frontend/ide/components/commit_sidebar/list_spec.js
+++ b/spec/frontend/ide/components/commit_sidebar/list_spec.js
@@ -1,51 +1,47 @@
-import Vue, { nextTick } from 'vue';
-import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
-import commitSidebarList from '~/ide/components/commit_sidebar/list.vue';
-import { createStore } from '~/ide/stores';
+import { shallowMount } from '@vue/test-utils';
+import CommitSidebarList from '~/ide/components/commit_sidebar/list.vue';
+import ListItem from '~/ide/components/commit_sidebar/list_item.vue';
import { file } from '../../helpers';
describe('Multi-file editor commit sidebar list', () => {
- let store;
- let vm;
-
- beforeEach(() => {
- store = createStore();
-
- const Component = Vue.extend(commitSidebarList);
-
- vm = createComponentWithStore(Component, store, {
- title: 'Staged',
- fileList: [],
- action: 'stageAllChanges',
- actionBtnText: 'stage all',
- actionBtnIcon: 'history',
- activeFileKey: 'staged-testing',
- keyPrefix: 'staged',
+ let wrapper;
+
+ const mountComponent = ({ fileList }) =>
+ shallowMount(CommitSidebarList, {
+ propsData: {
+ title: 'Staged',
+ fileList,
+ action: 'stageAllChanges',
+ actionBtnText: 'stage all',
+ actionBtnIcon: 'history',
+ activeFileKey: 'staged-testing',
+ keyPrefix: 'staged',
+ },
});
- vm.$mount();
- });
-
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
describe('with a list of files', () => {
beforeEach(async () => {
const f = file('file name');
f.changed = true;
- vm.fileList.push(f);
- await nextTick();
+ wrapper = mountComponent({ fileList: [f] });
});
it('renders list', () => {
- expect(vm.$el.querySelectorAll('.multi-file-commit-list > li').length).toBe(1);
+ expect(wrapper.findAllComponents(ListItem)).toHaveLength(1);
});
});
- describe('empty files array', () => {
- it('renders no changes text when empty', () => {
- expect(vm.$el.textContent).toContain('No changes');
+ describe('with empty files array', () => {
+ beforeEach(() => {
+ wrapper = mountComponent({ fileList: [] });
+ });
+
+ it('renders no changes text ', () => {
+ expect(wrapper.text()).toContain('No changes');
});
});
});
diff --git a/spec/frontend/ide/components/commit_sidebar/success_message_spec.js b/spec/frontend/ide/components/commit_sidebar/success_message_spec.js
index 52e35bdbb73..63d51953915 100644
--- a/spec/frontend/ide/components/commit_sidebar/success_message_spec.js
+++ b/spec/frontend/ide/components/commit_sidebar/success_message_spec.js
@@ -1,32 +1,22 @@
-import Vue, { nextTick } from 'vue';
-import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
-import successMessage from '~/ide/components/commit_sidebar/success_message.vue';
+import { shallowMount } from '@vue/test-utils';
+import SuccessMessage from '~/ide/components/commit_sidebar/success_message.vue';
import { createStore } from '~/ide/stores';
describe('IDE commit panel successful commit state', () => {
- let vm;
- let store;
+ let wrapper;
beforeEach(() => {
- store = createStore();
-
- const Component = Vue.extend(successMessage);
-
- vm = createComponentWithStore(Component, store, {
- committedStateSvgPath: 'committed-state',
- });
-
- vm.$mount();
+ const store = createStore();
+ store.state.committedStateSvgPath = 'committed-state';
+ store.state.lastCommitMsg = 'testing commit message';
+ wrapper = shallowMount(SuccessMessage, { store });
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
- it('renders last commit message when it exists', async () => {
- vm.$store.state.lastCommitMsg = 'testing commit message';
-
- await nextTick();
- expect(vm.$el.textContent).toContain('testing commit message');
+ it('renders last commit message when it exists', () => {
+ expect(wrapper.text()).toContain('testing commit message');
});
});
diff --git a/spec/frontend/ide/components/ide_tree_list_spec.js b/spec/frontend/ide/components/ide_tree_list_spec.js
index a85c52f5e86..0f61aa80e53 100644
--- a/spec/frontend/ide/components/ide_tree_list_spec.js
+++ b/spec/frontend/ide/components/ide_tree_list_spec.js
@@ -1,82 +1,72 @@
-import Vue, { nextTick } from 'vue';
-import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
+import { GlSkeletonLoader } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
import IdeTreeList from '~/ide/components/ide_tree_list.vue';
import { createStore } from '~/ide/stores';
+import FileTree from '~/vue_shared/components/file_tree.vue';
import { file } from '../helpers';
import { projectData } from '../mock_data';
-describe('IDE tree list', () => {
- const Component = Vue.extend(IdeTreeList);
- const normalBranchTree = [file('fileName')];
- const emptyBranchTree = [];
- let vm;
- let store;
+describe('IdeTreeList component', () => {
+ let wrapper;
- const bootstrapWithTree = (tree = normalBranchTree) => {
+ const mountComponent = ({ tree, loading = false } = {}) => {
+ const store = createStore();
store.state.currentProjectId = 'abcproject';
store.state.currentBranchId = 'main';
store.state.projects.abcproject = { ...projectData };
- Vue.set(store.state.trees, 'abcproject/main', {
- tree,
- loading: false,
- });
+ Vue.set(store.state.trees, 'abcproject/main', { tree, loading });
- vm = createComponentWithStore(Component, store, {
- viewerType: 'edit',
+ wrapper = shallowMount(IdeTreeList, {
+ propsData: {
+ viewerType: 'edit',
+ },
+ store,
});
};
- beforeEach(() => {
- store = createStore();
- });
-
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
describe('normal branch', () => {
- beforeEach(() => {
- bootstrapWithTree();
-
- jest.spyOn(vm, '$emit').mockImplementation(() => {});
-
- vm.$mount();
- });
+ const tree = [file('fileName')];
it('emits tree-ready event', () => {
- expect(vm.$emit).toHaveBeenCalledTimes(1);
- expect(vm.$emit).toHaveBeenCalledWith('tree-ready');
+ mountComponent({ tree });
+
+ expect(wrapper.emitted('tree-ready')).toEqual([[]]);
});
- it('renders loading indicator', async () => {
- store.state.trees['abcproject/main'].loading = true;
+ it('renders loading indicator', () => {
+ mountComponent({ tree, loading: true });
- await nextTick();
- expect(vm.$el.querySelector('.multi-file-loading-container')).not.toBeNull();
- expect(vm.$el.querySelectorAll('.multi-file-loading-container').length).toBe(3);
+ expect(wrapper.findAllComponents(GlSkeletonLoader)).toHaveLength(3);
});
it('renders list of files', () => {
- expect(vm.$el.textContent).toContain('fileName');
+ mountComponent({ tree });
+
+ expect(wrapper.findAllComponents(FileTree)).toHaveLength(1);
+ expect(wrapper.findComponent(FileTree).props('file')).toEqual(tree[0]);
});
});
describe('empty-branch state', () => {
beforeEach(() => {
- bootstrapWithTree(emptyBranchTree);
-
- jest.spyOn(vm, '$emit').mockImplementation(() => {});
+ mountComponent({ tree: [] });
+ });
- vm.$mount();
+ it('emits tree-ready event', () => {
+ expect(wrapper.emitted('tree-ready')).toEqual([[]]);
});
- it('still emits tree-ready event', () => {
- expect(vm.$emit).toHaveBeenCalledWith('tree-ready');
+ it('does not render files', () => {
+ expect(wrapper.findAllComponents(FileTree)).toHaveLength(0);
});
- it('does not load files if the branch is empty', () => {
- expect(vm.$el.textContent).not.toContain('fileName');
- expect(vm.$el.textContent).toContain('No files');
+ it('renders empty state text', () => {
+ expect(wrapper.text()).toBe('No files');
});
});
});
diff --git a/spec/frontend/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js
index b44651481e9..593fe6bf5a8 100644
--- a/spec/frontend/ide/components/repo_editor_spec.js
+++ b/spec/frontend/ide/components/repo_editor_spec.js
@@ -1,3 +1,4 @@
+import { GlTab } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import { editor as monacoEditor, Range } from 'monaco-editor';
import Vue, { nextTick } from 'vue';
@@ -125,7 +126,7 @@ describe('RepoEditor', () => {
};
const findEditor = () => wrapper.find('[data-testid="editor-container"]');
- const findTabs = () => wrapper.findAll('.ide-mode-tabs .nav-links li');
+ const findTabs = () => wrapper.findAllComponents(GlTab);
const findPreviewTab = () => wrapper.find('[data-testid="preview-tab"]');
beforeEach(() => {
@@ -201,12 +202,12 @@ describe('RepoEditor', () => {
const tabs = findTabs();
expect(tabs).toHaveLength(2);
- expect(tabs.at(0).text()).toBe('Edit');
- expect(tabs.at(1).text()).toBe('Preview Markdown');
+ expect(tabs.at(0).element.dataset.testid).toBe('edit-tab');
+ expect(tabs.at(1).element.dataset.testid).toBe('preview-tab');
});
it('renders markdown for tempFile', async () => {
- findPreviewTab().trigger('click');
+ findPreviewTab().vm.$emit('click');
await waitForPromises();
expect(wrapper.find(ContentViewer).html()).toContain(dummyFile.text.content);
});
diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js
index 3f2c3c3ec5f..3d3dbfa6853 100644
--- a/spec/frontend/issues/list/components/issues_list_app_spec.js
+++ b/spec/frontend/issues/list/components/issues_list_app_spec.js
@@ -29,6 +29,7 @@ import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_ro
import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants';
import IssuesListApp from '~/issues/list/components/issues_list_app.vue';
import NewIssueDropdown from '~/issues/list/components/new_issue_dropdown.vue';
+
import {
CREATED_DESC,
RELATIVE_POSITION,
@@ -98,6 +99,7 @@ describe('CE IssuesListApp component', () => {
};
let defaultQueryResponse = getIssuesQueryResponse;
+ let router;
if (IS_EE) {
defaultQueryResponse = cloneDeep(getIssuesQueryResponse);
defaultQueryResponse.data.project.issues.nodes[0].blockingCount = 1;
@@ -133,9 +135,11 @@ describe('CE IssuesListApp component', () => {
[setSortPreferenceMutation, sortPreferenceMutationResponse],
];
+ router = new VueRouter({ mode: 'history' });
+
return mountFn(IssuesListApp, {
apolloProvider: createMockApollo(requestHandlers),
- router: new VueRouter({ mode: 'history' }),
+ router,
provide: {
...defaultProvide,
...provide,
@@ -736,7 +740,7 @@ describe('CE IssuesListApp component', () => {
describe('when "click-tab" event is emitted by IssuableList', () => {
beforeEach(() => {
wrapper = mountComponent();
- jest.spyOn(wrapper.vm.$router, 'push');
+ router.push = jest.fn();
findIssuableList().vm.$emit('click-tab', IssuableStates.Closed);
});
@@ -746,16 +750,26 @@ describe('CE IssuesListApp component', () => {
});
it('updates url to the new tab', () => {
- expect(wrapper.vm.$router.push).toHaveBeenCalledWith({
+ expect(router.push).toHaveBeenCalledWith({
query: expect.objectContaining({ state: IssuableStates.Closed }),
});
});
});
describe.each`
- event | params
- ${'next-page'} | ${{ page_after: 'endCursor', page_before: undefined, first_page_size: 20, last_page_size: undefined }}
- ${'previous-page'} | ${{ page_after: undefined, page_before: 'startCursor', first_page_size: undefined, last_page_size: 20 }}
+ event | params
+ ${'next-page'} | ${{
+ page_after: 'endCursor',
+ page_before: undefined,
+ first_page_size: 20,
+ last_page_size: undefined,
+}}
+ ${'previous-page'} | ${{
+ page_after: undefined,
+ page_before: 'startCursor',
+ first_page_size: undefined,
+ last_page_size: 20,
+}}
`('when "$event" event is emitted by IssuableList', ({ event, params }) => {
beforeEach(() => {
wrapper = mountComponent({
@@ -766,7 +780,7 @@ describe('CE IssuesListApp component', () => {
},
},
});
- jest.spyOn(wrapper.vm.$router, 'push');
+ router.push = jest.fn();
findIssuableList().vm.$emit(event);
});
@@ -776,7 +790,7 @@ describe('CE IssuesListApp component', () => {
});
it(`updates url`, () => {
- expect(wrapper.vm.$router.push).toHaveBeenCalledWith({
+ expect(router.push).toHaveBeenCalledWith({
query: expect.objectContaining(params),
});
});
@@ -888,13 +902,13 @@ describe('CE IssuesListApp component', () => {
'updates to the new sort when payload is `%s`',
async (sortKey) => {
wrapper = mountComponent();
- jest.spyOn(wrapper.vm.$router, 'push');
+ router.push = jest.fn();
findIssuableList().vm.$emit('sort', sortKey);
jest.runOnlyPendingTimers();
await nextTick();
- expect(wrapper.vm.$router.push).toHaveBeenCalledWith({
+ expect(router.push).toHaveBeenCalledWith({
query: expect.objectContaining({ sort: urlSortParams[sortKey] }),
});
},
@@ -907,13 +921,13 @@ describe('CE IssuesListApp component', () => {
wrapper = mountComponent({
provide: { initialSort, isIssueRepositioningDisabled: true },
});
- jest.spyOn(wrapper.vm.$router, 'push');
+ router.push = jest.fn();
findIssuableList().vm.$emit('sort', RELATIVE_POSITION_ASC);
});
it('does not update the sort to manual', () => {
- expect(wrapper.vm.$router.push).not.toHaveBeenCalled();
+ expect(router.push).not.toHaveBeenCalled();
});
it('shows an alert to tell the user that manual reordering is disabled', () => {
@@ -978,12 +992,12 @@ describe('CE IssuesListApp component', () => {
describe('when "filter" event is emitted by IssuableList', () => {
it('updates IssuableList with url params', async () => {
wrapper = mountComponent();
- jest.spyOn(wrapper.vm.$router, 'push');
+ router.push = jest.fn();
findIssuableList().vm.$emit('filter', filteredTokens);
await nextTick();
- expect(wrapper.vm.$router.push).toHaveBeenCalledWith({
+ expect(router.push).toHaveBeenCalledWith({
query: expect.objectContaining(urlParams),
});
});
@@ -993,13 +1007,13 @@ describe('CE IssuesListApp component', () => {
wrapper = mountComponent({
provide: { isAnonymousSearchDisabled: true, isSignedIn: false },
});
- jest.spyOn(wrapper.vm.$router, 'push');
+ router.push = jest.fn();
findIssuableList().vm.$emit('filter', filteredTokens);
});
it('does not update url params', () => {
- expect(wrapper.vm.$router.push).not.toHaveBeenCalled();
+ expect(router.push).not.toHaveBeenCalled();
});
it('shows an alert to tell the user they must be signed in to search', () => {
@@ -1030,4 +1044,19 @@ describe('CE IssuesListApp component', () => {
expect(mockQuery).toHaveBeenCalledWith(expect.objectContaining({ hideUsers }));
});
});
+
+ describe('when "page-size-change" event is emitted by IssuableList', () => {
+ it('updates url params with new page size', async () => {
+ wrapper = mountComponent();
+ router.push = jest.fn();
+
+ findIssuableList().vm.$emit('page-size-change', 50);
+ await nextTick();
+
+ expect(router.push).toHaveBeenCalledTimes(1);
+ expect(router.push).toHaveBeenCalledWith({
+ query: expect.objectContaining({ first_page_size: 50 }),
+ });
+ });
+ });
});
diff --git a/spec/frontend/issues/list/utils_spec.js b/spec/frontend/issues/list/utils_spec.js
index 90eab1f3754..3c6332d5728 100644
--- a/spec/frontend/issues/list/utils_spec.js
+++ b/spec/frontend/issues/list/utils_spec.js
@@ -10,12 +10,7 @@ import {
urlParams,
urlParamsWithSpecialValues,
} from 'jest/issues/list/mock_data';
-import {
- PAGE_SIZE,
- PAGE_SIZE_MANUAL,
- RELATIVE_POSITION_ASC,
- urlSortParams,
-} from '~/issues/list/constants';
+import { PAGE_SIZE, urlSortParams } from '~/issues/list/constants';
import {
convertToApiParams,
convertToSearchQuery,
@@ -29,52 +24,30 @@ import {
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
describe('getInitialPageParams', () => {
- it.each(Object.keys(urlSortParams))(
- 'returns the correct page params for sort key %s',
- (sortKey) => {
- const firstPageSize = sortKey === RELATIVE_POSITION_ASC ? PAGE_SIZE_MANUAL : PAGE_SIZE;
+ it('returns page params with a default page size when no arguments are given', () => {
+ expect(getInitialPageParams()).toEqual({ firstPageSize: PAGE_SIZE });
+ });
- expect(getInitialPageParams(sortKey)).toEqual({ firstPageSize });
- },
- );
+ it('returns page params with the given page size', () => {
+ const pageSize = 100;
+ expect(getInitialPageParams(pageSize)).toEqual({ firstPageSize: pageSize });
+ });
- it.each(Object.keys(urlSortParams))(
- 'returns the correct page params for sort key %s with afterCursor',
- (sortKey) => {
- const firstPageSize = sortKey === RELATIVE_POSITION_ASC ? PAGE_SIZE_MANUAL : PAGE_SIZE;
- const lastPageSize = undefined;
- const afterCursor = 'randomCursorString';
- const beforeCursor = undefined;
- const pageParams = getInitialPageParams(
- sortKey,
- firstPageSize,
- lastPageSize,
- afterCursor,
- beforeCursor,
- );
-
- expect(pageParams).toEqual({ firstPageSize, afterCursor });
- },
- );
+ it('does not return firstPageSize when lastPageSize is provided', () => {
+ const firstPageSize = 100;
+ const lastPageSize = 50;
+ const afterCursor = undefined;
+ const beforeCursor = 'randomCursorString';
+ const pageParams = getInitialPageParams(
+ 100,
+ firstPageSize,
+ lastPageSize,
+ afterCursor,
+ beforeCursor,
+ );
- it.each(Object.keys(urlSortParams))(
- 'returns the correct page params for sort key %s with beforeCursor',
- (sortKey) => {
- const firstPageSize = undefined;
- const lastPageSize = PAGE_SIZE;
- const afterCursor = undefined;
- const beforeCursor = 'anotherRandomCursorString';
- const pageParams = getInitialPageParams(
- sortKey,
- firstPageSize,
- lastPageSize,
- afterCursor,
- beforeCursor,
- );
-
- expect(pageParams).toEqual({ lastPageSize, beforeCursor });
- },
- );
+ expect(pageParams).toEqual({ lastPageSize, beforeCursor });
+ });
});
describe('getSortKey', () => {
diff --git a/spec/frontend/issues/show/components/edited_spec.js b/spec/frontend/issues/show/components/edited_spec.js
index 8a8fe23230a..8a240c38b5f 100644
--- a/spec/frontend/issues/show/components/edited_spec.js
+++ b/spec/frontend/issues/show/components/edited_spec.js
@@ -1,49 +1,50 @@
-import Vue from 'vue';
-import edited from '~/issues/show/components/edited.vue';
-
-function formatText(text) {
- return text.trim().replace(/\s\s+/g, ' ');
-}
-
-describe('edited', () => {
- const EditedComponent = Vue.extend(edited);
-
- it('should render an edited at+by string', () => {
- const editedComponent = new EditedComponent({
- propsData: {
- updatedAt: '2017-05-15T12:31:04.428Z',
- updatedByName: 'Some User',
- updatedByPath: '/some_user',
- },
- }).$mount();
-
- expect(formatText(editedComponent.$el.innerText)).toMatch(/Edited[\s\S]+?by Some User/);
- expect(editedComponent.$el.querySelector('.author-link').href).toMatch(/\/some_user$/);
- expect(editedComponent.$el.querySelector('time')).toBeTruthy();
+import { shallowMount } from '@vue/test-utils';
+import Edited from '~/issues/show/components/edited.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+
+describe('Edited component', () => {
+ let wrapper;
+
+ const findAuthorLink = () => wrapper.find('a');
+ const findTimeAgoTooltip = () => wrapper.findComponent(TimeAgoTooltip);
+ const formatText = (text) => text.trim().replace(/\s\s+/g, ' ');
+
+ const mountComponent = (propsData) => shallowMount(Edited, { propsData });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders an edited at+by string', () => {
+ wrapper = mountComponent({
+ updatedAt: '2017-05-15T12:31:04.428Z',
+ updatedByName: 'Some User',
+ updatedByPath: '/some_user',
+ });
+
+ expect(formatText(wrapper.text())).toBe('Edited by Some User');
+ expect(findAuthorLink().attributes('href')).toBe('/some_user');
+ expect(findTimeAgoTooltip().exists()).toBe(true);
});
it('if no updatedAt is provided, no time element will be rendered', () => {
- const editedComponent = new EditedComponent({
- propsData: {
- updatedByName: 'Some User',
- updatedByPath: '/some_user',
- },
- }).$mount();
-
- expect(formatText(editedComponent.$el.innerText)).toMatch(/Edited by Some User/);
- expect(editedComponent.$el.querySelector('.author-link').href).toMatch(/\/some_user$/);
- expect(editedComponent.$el.querySelector('time')).toBeFalsy();
+ wrapper = mountComponent({
+ updatedByName: 'Some User',
+ updatedByPath: '/some_user',
+ });
+
+ expect(formatText(wrapper.text())).toBe('Edited by Some User');
+ expect(findAuthorLink().attributes('href')).toBe('/some_user');
+ expect(findTimeAgoTooltip().exists()).toBe(false);
});
it('if no updatedByName and updatedByPath is provided, no user element will be rendered', () => {
- const editedComponent = new EditedComponent({
- propsData: {
- updatedAt: '2017-05-15T12:31:04.428Z',
- },
- }).$mount();
-
- expect(formatText(editedComponent.$el.innerText)).not.toMatch(/by Some User/);
- expect(editedComponent.$el.querySelector('.author-link')).toBeFalsy();
- expect(editedComponent.$el.querySelector('time')).toBeTruthy();
+ wrapper = mountComponent({
+ updatedAt: '2017-05-15T12:31:04.428Z',
+ });
+
+ expect(formatText(wrapper.text())).toBe('Edited');
+ expect(findAuthorLink().exists()).toBe(false);
+ expect(findTimeAgoTooltip().exists()).toBe(true);
});
});
diff --git a/spec/frontend/logs/components/environment_logs_spec.js b/spec/frontend/logs/components/environment_logs_spec.js
deleted file mode 100644
index 84dc0bdf6cd..00000000000
--- a/spec/frontend/logs/components/environment_logs_spec.js
+++ /dev/null
@@ -1,370 +0,0 @@
-import { GlSprintf, GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import { scrollDown } from '~/lib/utils/scroll_utils';
-import EnvironmentLogs from '~/logs/components/environment_logs.vue';
-
-import { createStore } from '~/logs/stores';
-import {
- mockEnvName,
- mockEnvironments,
- mockPods,
- mockLogsResult,
- mockTrace,
- mockEnvironmentsEndpoint,
- mockDocumentationPath,
-} from '../mock_data';
-
-jest.mock('~/lib/utils/scroll_utils');
-
-const module = 'environmentLogs';
-
-jest.mock('lodash/throttle', () =>
- jest.fn((func) => {
- return func;
- }),
-);
-
-describe('EnvironmentLogs', () => {
- let store;
- let dispatch;
- let wrapper;
- let state;
-
- const propsData = {
- environmentName: mockEnvName,
- environmentsPath: mockEnvironmentsEndpoint,
- clusterApplicationsDocumentationPath: mockDocumentationPath,
- clustersPath: '/gitlab-org',
- };
-
- const updateControlBtnsMock = jest.fn();
- const LogControlButtonsStub = {
- template: '<div/>',
- methods: {
- update: updateControlBtnsMock,
- },
- props: {
- scrollDownButtonDisabled: false,
- },
- };
-
- const findEnvironmentsDropdown = () => wrapper.find('.js-environments-dropdown');
-
- const findSimpleFilters = () => wrapper.find({ ref: 'log-simple-filters' });
- const findAdvancedFilters = () => wrapper.find({ ref: 'log-advanced-filters' });
- const findElasticsearchNotice = () => wrapper.find({ ref: 'elasticsearchNotice' });
- const findLogControlButtons = () => wrapper.find(LogControlButtonsStub);
-
- const findInfiniteScroll = () => wrapper.find({ ref: 'infiniteScroll' });
- const findLogTrace = () => wrapper.find({ ref: 'logTrace' });
- const findLogFooter = () => wrapper.find({ ref: 'logFooter' });
- const getInfiniteScrollAttr = (attr) => parseInt(findInfiniteScroll().attributes(attr), 10);
-
- const mockSetInitData = () => {
- state.pods.options = mockPods;
- state.environments.current = mockEnvName;
- [state.pods.current] = state.pods.options;
-
- state.logs.lines = [];
- };
-
- const mockShowPodLogs = () => {
- state.pods.options = mockPods;
- [state.pods.current] = mockPods;
-
- state.logs.lines = mockLogsResult;
- };
-
- const mockFetchEnvs = () => {
- state.environments.options = mockEnvironments;
- };
-
- const initWrapper = () => {
- wrapper = shallowMount(EnvironmentLogs, {
- propsData,
- store,
- stubs: {
- LogControlButtons: LogControlButtonsStub,
- GlInfiniteScroll: {
- name: 'gl-infinite-scroll',
- template: `
- <div>
- <slot name="header"></slot>
- <slot name="items"></slot>
- <slot></slot>
- </div>
- `,
- },
- GlSprintf,
- },
- });
- };
-
- beforeEach(() => {
- store = createStore();
- state = store.state.environmentLogs;
-
- jest.spyOn(store, 'dispatch').mockResolvedValue();
-
- dispatch = store.dispatch;
- });
-
- afterEach(() => {
- store.dispatch.mockReset();
-
- if (wrapper) {
- wrapper.destroy();
- }
- });
-
- it('displays UI elements', () => {
- initWrapper();
-
- expect(findEnvironmentsDropdown().is(GlDropdown)).toBe(true);
- expect(findSimpleFilters().exists()).toBe(true);
- expect(findLogControlButtons().exists()).toBe(true);
-
- expect(findInfiniteScroll().exists()).toBe(true);
- expect(findLogTrace().exists()).toBe(true);
- });
-
- it('mounted inits data', () => {
- initWrapper();
-
- expect(dispatch).toHaveBeenCalledWith(`${module}/setInitData`, {
- timeRange: expect.objectContaining({
- default: true,
- }),
- environmentName: mockEnvName,
- podName: null,
- });
-
- expect(dispatch).toHaveBeenCalledWith(`${module}/fetchEnvironments`, mockEnvironmentsEndpoint);
- });
-
- describe('loading state', () => {
- beforeEach(() => {
- state.pods.options = [];
-
- state.logs.lines = [];
- state.logs.isLoading = true;
-
- state.environments = {
- options: [],
- isLoading: true,
- };
-
- initWrapper();
- });
-
- it('does not display an alert to upgrade to ES', () => {
- expect(findElasticsearchNotice().exists()).toBe(false);
- });
-
- it('displays a disabled environments dropdown', () => {
- expect(findEnvironmentsDropdown().attributes('disabled')).toBe('true');
- expect(findEnvironmentsDropdown().findAll(GlDropdownItem).length).toBe(0);
- });
-
- it('does not update buttons state', () => {
- expect(updateControlBtnsMock).not.toHaveBeenCalled();
- });
-
- it('shows an infinite scroll with no content', () => {
- expect(getInfiniteScrollAttr('fetched-items')).toBe(0);
- });
-
- it('shows an infinite scroll container with no set max-height ', () => {
- expect(findInfiniteScroll().attributes('max-list-height')).toBeUndefined();
- });
-
- it('shows a logs trace', () => {
- expect(findLogTrace().text()).toBe('');
- expect(findLogTrace().find('.js-build-loader-animation').isVisible()).toBe(true);
- });
- });
-
- describe('k8s environment', () => {
- beforeEach(() => {
- state.pods.options = [];
-
- state.logs.lines = [];
- state.logs.isLoading = false;
-
- state.environments = {
- options: mockEnvironments,
- current: 'staging',
- isLoading: false,
- };
-
- initWrapper();
- });
-
- it('displays an alert to upgrade to ES', () => {
- expect(findElasticsearchNotice().exists()).toBe(true);
- });
-
- it('displays simple filters for kubernetes logs API', () => {
- expect(findSimpleFilters().exists()).toBe(true);
- expect(findAdvancedFilters().exists()).toBe(false);
- });
- });
-
- describe('state with data', () => {
- beforeEach(() => {
- dispatch.mockImplementation((actionName) => {
- if (actionName === `${module}/setInitData`) {
- mockSetInitData();
- } else if (actionName === `${module}/showPodLogs`) {
- mockShowPodLogs();
- } else if (actionName === `${module}/fetchEnvironments`) {
- mockFetchEnvs();
- mockShowPodLogs();
- }
- });
-
- initWrapper();
- });
-
- afterEach(() => {
- scrollDown.mockReset();
- updateControlBtnsMock.mockReset();
- });
-
- it('does not display an alert to upgrade to ES', () => {
- expect(findElasticsearchNotice().exists()).toBe(false);
- });
-
- it('populates environments dropdown', () => {
- const items = findEnvironmentsDropdown().findAll(GlDropdownItem);
- expect(findEnvironmentsDropdown().props('text')).toBe(mockEnvName);
- expect(items.length).toBe(mockEnvironments.length);
- mockEnvironments.forEach((env, i) => {
- const item = items.at(i);
- expect(item.text()).toBe(env.name);
- });
- });
-
- it('dropdown has one environment selected', () => {
- const items = findEnvironmentsDropdown().findAll(GlDropdownItem);
- mockEnvironments.forEach((env, i) => {
- const item = items.at(i);
-
- if (item.text() !== mockEnvName) {
- expect(item.find(GlDropdownItem).attributes('ischecked')).toBeFalsy();
- } else {
- expect(item.find(GlDropdownItem).attributes('ischecked')).toBeTruthy();
- }
- });
- });
-
- it('displays advanced filters for elasticsearch logs API', () => {
- expect(findSimpleFilters().exists()).toBe(false);
- expect(findAdvancedFilters().exists()).toBe(true);
- });
-
- it('shows infinite scroll with content', () => {
- expect(getInfiniteScrollAttr('fetched-items')).toBe(mockTrace.length);
- });
-
- it('populates logs trace', () => {
- const trace = findLogTrace();
- expect(trace.text().split('\n').length).toBe(mockTrace.length);
- expect(trace.text().split('\n')).toEqual(mockTrace);
- });
-
- it('populates footer', () => {
- const footer = findLogFooter().text();
-
- expect(footer).toContain(`${mockLogsResult.length} results`);
- });
-
- describe('when user clicks', () => {
- it('environment name, trace is refreshed', () => {
- const items = findEnvironmentsDropdown().findAll(GlDropdownItem);
- const index = 1; // any env
-
- expect(dispatch).not.toHaveBeenCalledWith(`${module}/showEnvironment`, expect.anything());
-
- items.at(index).vm.$emit('click');
-
- expect(dispatch).toHaveBeenCalledWith(
- `${module}/showEnvironment`,
- mockEnvironments[index].name,
- );
- });
-
- it('refresh button, trace is refreshed', () => {
- expect(dispatch).not.toHaveBeenCalledWith(`${module}/refreshPodLogs`, undefined);
-
- findLogControlButtons().vm.$emit('refresh');
-
- expect(dispatch).toHaveBeenCalledWith(`${module}/refreshPodLogs`, undefined);
- });
- });
- });
-
- describe('listeners', () => {
- beforeEach(() => {
- initWrapper();
- });
-
- it('attaches listeners in components', () => {
- expect(findInfiniteScroll().vm.$listeners).toEqual({
- topReached: expect.any(Function),
- scroll: expect.any(Function),
- });
- });
-
- it('`topReached` when not loading', () => {
- expect(store.dispatch).not.toHaveBeenCalledWith(`${module}/fetchMoreLogsPrepend`, undefined);
-
- findInfiniteScroll().vm.$emit('topReached');
-
- expect(store.dispatch).toHaveBeenCalledWith(`${module}/fetchMoreLogsPrepend`, undefined);
- });
-
- it('`topReached` does not fetches more logs when already loading', () => {
- state.logs.isLoading = true;
- findInfiniteScroll().vm.$emit('topReached');
-
- expect(store.dispatch).not.toHaveBeenCalledWith(`${module}/fetchMoreLogsPrepend`, undefined);
- });
-
- it('`topReached` fetches more logs', () => {
- state.logs.isLoading = true;
- findInfiniteScroll().vm.$emit('topReached');
-
- expect(store.dispatch).not.toHaveBeenCalledWith(`${module}/fetchMoreLogsPrepend`, undefined);
- });
-
- it('`scroll` on a scrollable target results in enabled scroll buttons', async () => {
- const target = { scrollTop: 10, clientHeight: 10, scrollHeight: 21 };
-
- state.logs.isLoading = true;
- findInfiniteScroll().vm.$emit('scroll', { target });
-
- await nextTick();
- expect(findLogControlButtons().props('scrollDownButtonDisabled')).toEqual(false);
- });
-
- it('`scroll` on a non-scrollable target in disabled scroll buttons', async () => {
- const target = { scrollTop: 10, clientHeight: 10, scrollHeight: 20 };
-
- state.logs.isLoading = true;
- findInfiniteScroll().vm.$emit('scroll', { target });
-
- await nextTick();
- expect(findLogControlButtons().props('scrollDownButtonDisabled')).toEqual(true);
- });
-
- it('`scroll` on no target results in disabled scroll buttons', async () => {
- state.logs.isLoading = true;
- findInfiniteScroll().vm.$emit('scroll', { target: undefined });
-
- await nextTick();
- expect(findLogControlButtons().props('scrollDownButtonDisabled')).toEqual(true);
- });
- });
-});
diff --git a/spec/frontend/logs/components/log_advanced_filters_spec.js b/spec/frontend/logs/components/log_advanced_filters_spec.js
deleted file mode 100644
index 4e4052eb4d8..00000000000
--- a/spec/frontend/logs/components/log_advanced_filters_spec.js
+++ /dev/null
@@ -1,175 +0,0 @@
-import { GlFilteredSearch } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { convertToFixedRange } from '~/lib/utils/datetime_range';
-import LogAdvancedFilters from '~/logs/components/log_advanced_filters.vue';
-import { TOKEN_TYPE_POD_NAME } from '~/logs/constants';
-import { createStore } from '~/logs/stores';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
-import { defaultTimeRange } from '~/vue_shared/constants';
-import { mockPods, mockSearch } from '../mock_data';
-
-const module = 'environmentLogs';
-
-describe('LogAdvancedFilters', () => {
- let store;
- let dispatch;
- let wrapper;
- let state;
-
- const findFilteredSearch = () => wrapper.find(GlFilteredSearch);
- const findTimeRangePicker = () => wrapper.find({ ref: 'dateTimePicker' });
- const getSearchToken = (type) =>
- findFilteredSearch()
- .props('availableTokens')
- .filter((token) => token.type === type)[0];
-
- const mockStateLoading = () => {
- state.timeRange.selected = defaultTimeRange;
- state.timeRange.current = convertToFixedRange(defaultTimeRange);
- state.pods.options = [];
- state.pods.current = null;
- state.logs.isLoading = true;
- };
-
- const mockStateWithData = () => {
- state.timeRange.selected = defaultTimeRange;
- state.timeRange.current = convertToFixedRange(defaultTimeRange);
- state.pods.options = mockPods;
- state.pods.current = null;
- state.logs.isLoading = false;
- };
-
- const initWrapper = (propsData = {}) => {
- wrapper = shallowMount(LogAdvancedFilters, {
- propsData: {
- ...propsData,
- },
- store,
- });
- };
-
- beforeEach(() => {
- store = createStore();
- state = store.state.environmentLogs;
-
- jest.spyOn(store, 'dispatch').mockResolvedValue();
-
- dispatch = store.dispatch;
- });
-
- afterEach(() => {
- store.dispatch.mockReset();
-
- if (wrapper) {
- wrapper.destroy();
- }
- });
-
- it('displays UI elements', () => {
- initWrapper();
-
- expect(findFilteredSearch().exists()).toBe(true);
- expect(findTimeRangePicker().exists()).toBe(true);
- });
-
- it('displays search tokens', () => {
- initWrapper();
-
- expect(getSearchToken(TOKEN_TYPE_POD_NAME)).toMatchObject({
- title: 'Pod name',
- unique: true,
- operators: OPERATOR_IS_ONLY,
- });
- });
-
- describe('disabled state', () => {
- beforeEach(() => {
- mockStateLoading();
- initWrapper({
- disabled: true,
- });
- });
-
- it('displays disabled filters', () => {
- expect(findFilteredSearch().attributes('disabled')).toBeTruthy();
- expect(findTimeRangePicker().attributes('disabled')).toBeTruthy();
- });
- });
-
- describe('when the state is loading', () => {
- beforeEach(() => {
- mockStateLoading();
- initWrapper();
- });
-
- it('displays a disabled search', () => {
- expect(findFilteredSearch().attributes('disabled')).toBeTruthy();
- });
-
- it('displays an enable date filter', () => {
- expect(findTimeRangePicker().attributes('disabled')).toBeFalsy();
- });
-
- it('displays no pod options when no pods are available, so suggestions can be displayed', () => {
- expect(getSearchToken(TOKEN_TYPE_POD_NAME).options).toBe(null);
- expect(getSearchToken(TOKEN_TYPE_POD_NAME).loading).toBe(true);
- });
- });
-
- describe('when the state has data', () => {
- beforeEach(() => {
- mockStateWithData();
- initWrapper();
- });
-
- it('displays a single token for pods', () => {
- initWrapper();
-
- const tokens = findFilteredSearch().props('availableTokens');
-
- expect(tokens).toHaveLength(1);
- expect(tokens[0].type).toBe(TOKEN_TYPE_POD_NAME);
- });
-
- it('displays a enabled filters', () => {
- expect(findFilteredSearch().attributes('disabled')).toBeFalsy();
- expect(findTimeRangePicker().attributes('disabled')).toBeFalsy();
- });
-
- it('displays options in the pods token', () => {
- const { options } = getSearchToken(TOKEN_TYPE_POD_NAME);
-
- expect(options).toHaveLength(mockPods.length);
- });
-
- it('displays options in date time picker', () => {
- const options = findTimeRangePicker().props('options');
-
- expect(options).toEqual(expect.any(Array));
- expect(options.length).toBeGreaterThan(0);
- });
-
- describe('when the user interacts', () => {
- it('clicks on the search button, showFilteredLogs is dispatched', () => {
- findFilteredSearch().vm.$emit('submit', null);
-
- expect(dispatch).toHaveBeenCalledWith(`${module}/showFilteredLogs`, null);
- });
-
- it('clicks on the search button, showFilteredLogs is dispatched with null', () => {
- findFilteredSearch().vm.$emit('submit', [mockSearch]);
-
- expect(dispatch).toHaveBeenCalledWith(`${module}/showFilteredLogs`, [mockSearch]);
- });
-
- it('selects a new time range', () => {
- expect(findTimeRangePicker().attributes('disabled')).toBeFalsy();
-
- const mockRange = { start: 'START_DATE', end: 'END_DATE' };
- findTimeRangePicker().vm.$emit('input', mockRange);
-
- expect(dispatch).toHaveBeenCalledWith(`${module}/setTimeRange`, mockRange);
- });
- });
- });
-});
diff --git a/spec/frontend/logs/components/log_simple_filters_spec.js b/spec/frontend/logs/components/log_simple_filters_spec.js
deleted file mode 100644
index 04ad2e03542..00000000000
--- a/spec/frontend/logs/components/log_simple_filters_spec.js
+++ /dev/null
@@ -1,134 +0,0 @@
-import { GlDropdownItem } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import LogSimpleFilters from '~/logs/components/log_simple_filters.vue';
-import { createStore } from '~/logs/stores';
-import { mockPods, mockPodName } from '../mock_data';
-
-const module = 'environmentLogs';
-
-describe('LogSimpleFilters', () => {
- let store;
- let dispatch;
- let wrapper;
- let state;
-
- const findPodsDropdown = () => wrapper.find({ ref: 'podsDropdown' });
- const findPodsNoPodsText = () => wrapper.find({ ref: 'noPodsMsg' });
- const findPodsDropdownItems = () =>
- findPodsDropdown()
- .findAll(GlDropdownItem)
- .filter((item) => !('disabled' in item.attributes()));
-
- const mockPodsLoading = () => {
- state.pods.options = [];
- state.pods.current = null;
- };
-
- const mockPodsLoaded = () => {
- state.pods.options = mockPods;
- state.pods.current = mockPodName;
- };
-
- const initWrapper = (propsData = {}) => {
- wrapper = shallowMount(LogSimpleFilters, {
- propsData: {
- ...propsData,
- },
- store,
- });
- };
-
- beforeEach(() => {
- store = createStore();
- state = store.state.environmentLogs;
-
- jest.spyOn(store, 'dispatch').mockResolvedValue();
-
- dispatch = store.dispatch;
- });
-
- afterEach(() => {
- store.dispatch.mockReset();
-
- if (wrapper) {
- wrapper.destroy();
- }
- });
-
- it('displays UI elements', () => {
- initWrapper();
-
- expect(findPodsDropdown().exists()).toBe(true);
- });
-
- describe('disabled state', () => {
- beforeEach(() => {
- mockPodsLoading();
- initWrapper({
- disabled: true,
- });
- });
-
- it('displays a disabled pods dropdown', () => {
- expect(findPodsDropdown().props('text')).toBe('No pod selected');
- expect(findPodsDropdown().attributes('disabled')).toBeTruthy();
- });
- });
-
- describe('loading state', () => {
- beforeEach(() => {
- mockPodsLoading();
- initWrapper();
- });
-
- it('displays an enabled pods dropdown', () => {
- expect(findPodsDropdown().attributes('disabled')).toBeFalsy();
- expect(findPodsDropdown().props('text')).toBe('No pod selected');
- });
-
- it('displays an empty pods dropdown', () => {
- expect(findPodsNoPodsText().exists()).toBe(true);
- expect(findPodsDropdownItems()).toHaveLength(0);
- });
- });
-
- describe('pods available state', () => {
- beforeEach(() => {
- mockPodsLoaded();
- initWrapper();
- });
-
- it('displays an enabled pods dropdown', () => {
- expect(findPodsDropdown().attributes('disabled')).toBeFalsy();
- expect(findPodsDropdown().props('text')).toBe(mockPods[0]);
- });
-
- it('displays a pods dropdown with items', () => {
- expect(findPodsNoPodsText().exists()).toBe(false);
- expect(findPodsDropdownItems()).toHaveLength(mockPods.length);
- });
-
- it('dropdown has one pod selected', () => {
- const items = findPodsDropdownItems();
- mockPods.forEach((pod, i) => {
- const item = items.at(i);
- if (item.text() !== mockPodName) {
- expect(item.find(GlDropdownItem).attributes('ischecked')).toBeFalsy();
- } else {
- expect(item.find(GlDropdownItem).attributes('ischecked')).toBeTruthy();
- }
- });
- });
-
- it('when the user clicks on a pod, showPodLogs is dispatched', () => {
- const items = findPodsDropdownItems();
- const index = 2; // any pod
-
- expect(dispatch).not.toHaveBeenCalledWith(`${module}/showPodLogs`, expect.anything());
-
- items.at(index).vm.$emit('click');
-
- expect(dispatch).toHaveBeenCalledWith(`${module}/showPodLogs`, mockPods[index]);
- });
- });
-});
diff --git a/spec/frontend/logs/stores/actions_spec.js b/spec/frontend/logs/stores/actions_spec.js
deleted file mode 100644
index 46ef1500a20..00000000000
--- a/spec/frontend/logs/stores/actions_spec.js
+++ /dev/null
@@ -1,521 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import testAction from 'helpers/vuex_action_helper';
-import axios from '~/lib/utils/axios_utils';
-import { convertToFixedRange } from '~/lib/utils/datetime_range';
-import { TOKEN_TYPE_POD_NAME } from '~/logs/constants';
-import {
- setInitData,
- showFilteredLogs,
- showPodLogs,
- fetchEnvironments,
- fetchLogs,
- fetchMoreLogsPrepend,
-} from '~/logs/stores/actions';
-import * as types from '~/logs/stores/mutation_types';
-import logsPageState from '~/logs/stores/state';
-import Tracking from '~/tracking';
-
-import { defaultTimeRange } from '~/vue_shared/constants';
-
-import {
- mockPodName,
- mockEnvironmentsEndpoint,
- mockEnvironments,
- mockPods,
- mockLogsResult,
- mockEnvName,
- mockSearch,
- mockLogsEndpoint,
- mockResponse,
- mockCursor,
- mockNextCursor,
-} from '../mock_data';
-
-jest.mock('~/lib/utils/datetime_range');
-jest.mock('~/logs/utils');
-
-const mockDefaultRange = {
- start: '2020-01-10T18:00:00.000Z',
- end: '2020-01-10T19:00:00.000Z',
-};
-const mockFixedRange = {
- start: '2020-01-09T18:06:20.000Z',
- end: '2020-01-09T18:36:20.000Z',
-};
-const mockRollingRange = {
- duration: 120,
-};
-const mockRollingRangeAsFixed = {
- start: '2020-01-10T18:00:00.000Z',
- end: '2020-01-10T17:58:00.000Z',
-};
-
-describe('Logs Store actions', () => {
- let state;
- let mock;
-
- const latestGetParams = () => mock.history.get[mock.history.get.length - 1].params;
-
- convertToFixedRange.mockImplementation((range) => {
- if (range === defaultTimeRange) {
- return { ...mockDefaultRange };
- }
- if (range === mockFixedRange) {
- return { ...mockFixedRange };
- }
- if (range === mockRollingRange) {
- return { ...mockRollingRangeAsFixed };
- }
- throw new Error('Invalid time range');
- });
-
- beforeEach(() => {
- state = logsPageState();
- });
-
- describe('setInitData', () => {
- it('should commit environment and pod name mutation', () =>
- testAction(
- setInitData,
- { timeRange: mockFixedRange, environmentName: mockEnvName, podName: mockPodName },
- state,
- [
- { type: types.SET_TIME_RANGE, payload: mockFixedRange },
- { type: types.SET_PROJECT_ENVIRONMENT, payload: mockEnvName },
- { type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
- ],
- ));
- });
-
- describe('showFilteredLogs', () => {
- it('empty search should filter with defaults', () =>
- testAction(
- showFilteredLogs,
- undefined,
- state,
- [
- { type: types.SET_CURRENT_POD_NAME, payload: null },
- { type: types.SET_SEARCH, payload: '' },
- ],
- [{ type: 'fetchLogs', payload: 'used_search_bar' }],
- ));
-
- it('text search should filter with a search term', () =>
- testAction(
- showFilteredLogs,
- [mockSearch],
- state,
- [
- { type: types.SET_CURRENT_POD_NAME, payload: null },
- { type: types.SET_SEARCH, payload: mockSearch },
- ],
- [{ type: 'fetchLogs', payload: 'used_search_bar' }],
- ));
-
- it('pod search should filter with a search term', () =>
- testAction(
- showFilteredLogs,
- [{ type: TOKEN_TYPE_POD_NAME, value: { data: mockPodName, operator: '=' } }],
- state,
- [
- { type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
- { type: types.SET_SEARCH, payload: '' },
- ],
- [{ type: 'fetchLogs', payload: 'used_search_bar' }],
- ));
-
- it('pod search should filter with a pod selection and a search term', () =>
- testAction(
- showFilteredLogs,
- [{ type: TOKEN_TYPE_POD_NAME, value: { data: mockPodName, operator: '=' } }, mockSearch],
- state,
- [
- { type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
- { type: types.SET_SEARCH, payload: mockSearch },
- ],
- [{ type: 'fetchLogs', payload: 'used_search_bar' }],
- ));
-
- it('pod search should filter with a pod selection and two search terms', () =>
- testAction(
- showFilteredLogs,
- ['term1', 'term2'],
- state,
- [
- { type: types.SET_CURRENT_POD_NAME, payload: null },
- { type: types.SET_SEARCH, payload: `term1 term2` },
- ],
- [{ type: 'fetchLogs', payload: 'used_search_bar' }],
- ));
-
- it('pod search should filter with a pod selection and a search terms before and after', () =>
- testAction(
- showFilteredLogs,
- [
- 'term1',
- { type: TOKEN_TYPE_POD_NAME, value: { data: mockPodName, operator: '=' } },
- 'term2',
- ],
- state,
- [
- { type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
- { type: types.SET_SEARCH, payload: `term1 term2` },
- ],
- [{ type: 'fetchLogs', payload: 'used_search_bar' }],
- ));
- });
-
- describe('showPodLogs', () => {
- it('should commit pod name', () =>
- testAction(
- showPodLogs,
- mockPodName,
- state,
- [{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName }],
- [{ type: 'fetchLogs', payload: 'pod_log_changed' }],
- ));
- });
-
- describe('fetchEnvironments', () => {
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- it('should commit RECEIVE_ENVIRONMENTS_DATA_SUCCESS mutation on correct data', () => {
- mock.onGet(mockEnvironmentsEndpoint).replyOnce(200, mockEnvironments);
- return testAction(
- fetchEnvironments,
- mockEnvironmentsEndpoint,
- state,
- [
- { type: types.REQUEST_ENVIRONMENTS_DATA },
- { type: types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS, payload: mockEnvironments },
- ],
- [{ type: 'fetchLogs', payload: 'environment_selected' }],
- );
- });
-
- it('should commit RECEIVE_ENVIRONMENTS_DATA_ERROR on wrong data', () => {
- mock.onGet(mockEnvironmentsEndpoint).replyOnce(500);
- return testAction(
- fetchEnvironments,
- mockEnvironmentsEndpoint,
- state,
- [
- { type: types.REQUEST_ENVIRONMENTS_DATA },
- { type: types.RECEIVE_ENVIRONMENTS_DATA_ERROR },
- ],
- [],
- );
- });
- });
-
- describe('when the backend responds succesfully', () => {
- let expectedMutations;
- let expectedActions;
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- mock.onGet(mockLogsEndpoint).reply(200, mockResponse);
- mock.onGet(mockLogsEndpoint).replyOnce(202); // mock reactive cache
-
- state.environments.options = mockEnvironments;
- state.environments.current = mockEnvName;
- });
-
- afterEach(() => {
- mock.reset();
- });
-
- describe('fetchLogs', () => {
- beforeEach(() => {
- expectedMutations = [
- { type: types.REQUEST_LOGS_DATA },
- {
- type: types.RECEIVE_LOGS_DATA_SUCCESS,
- payload: { logs: mockLogsResult, cursor: mockNextCursor },
- },
- { type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
- { type: types.RECEIVE_PODS_DATA_SUCCESS, payload: mockPods },
- ];
-
- expectedActions = [];
- });
-
- it('should commit logs and pod data when there is pod name defined', () => {
- state.pods.current = mockPodName;
- state.timeRange.current = mockFixedRange;
-
- return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
- expect(latestGetParams()).toMatchObject({
- pod_name: mockPodName,
- });
- });
- });
-
- it('should commit logs and pod data when there is pod name defined and a non-default date range', () => {
- state.pods.current = mockPodName;
- state.timeRange.current = mockFixedRange;
- state.logs.cursor = mockCursor;
-
- return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
- expect(latestGetParams()).toEqual({
- pod_name: mockPodName,
- start_time: mockFixedRange.start,
- end_time: mockFixedRange.end,
- cursor: mockCursor,
- });
- });
- });
-
- it('should commit logs and pod data when there is pod name and search and a faulty date range', () => {
- state.pods.current = mockPodName;
- state.search = mockSearch;
- state.timeRange.current = 'INVALID_TIME_RANGE';
-
- expectedMutations.splice(1, 0, {
- type: types.SHOW_TIME_RANGE_INVALID_WARNING,
- });
-
- return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
- expect(latestGetParams()).toEqual({
- pod_name: mockPodName,
- search: mockSearch,
- });
- });
- });
-
- it('should commit logs and pod data when no pod name defined', () => {
- state.timeRange.current = defaultTimeRange;
-
- return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
- expect(latestGetParams()).toEqual({
- start_time: expect.any(String),
- end_time: expect.any(String),
- });
- });
- });
- });
-
- describe('fetchMoreLogsPrepend', () => {
- beforeEach(() => {
- expectedMutations = [
- { type: types.REQUEST_LOGS_DATA_PREPEND },
- {
- type: types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS,
- payload: { logs: mockLogsResult, cursor: mockNextCursor },
- },
- ];
-
- expectedActions = [];
- });
-
- it('should commit logs and pod data when there is pod name defined', () => {
- state.pods.current = mockPodName;
- state.timeRange.current = mockFixedRange;
-
- expectedActions = [];
-
- return testAction(
- fetchMoreLogsPrepend,
- null,
- state,
- expectedMutations,
- expectedActions,
- () => {
- expect(latestGetParams()).toMatchObject({
- pod_name: mockPodName,
- });
- },
- );
- });
-
- it('should commit logs and pod data when there is pod name defined and a non-default date range', () => {
- state.pods.current = mockPodName;
- state.timeRange.current = mockFixedRange;
- state.logs.cursor = mockCursor;
-
- return testAction(
- fetchMoreLogsPrepend,
- null,
- state,
- expectedMutations,
- expectedActions,
- () => {
- expect(latestGetParams()).toEqual({
- pod_name: mockPodName,
- start_time: mockFixedRange.start,
- end_time: mockFixedRange.end,
- cursor: mockCursor,
- });
- },
- );
- });
-
- it('should commit logs and pod data when there is pod name and search and a faulty date range', () => {
- state.pods.current = mockPodName;
- state.search = mockSearch;
- state.timeRange.current = 'INVALID_TIME_RANGE';
-
- expectedMutations.splice(1, 0, {
- type: types.SHOW_TIME_RANGE_INVALID_WARNING,
- });
-
- return testAction(
- fetchMoreLogsPrepend,
- null,
- state,
- expectedMutations,
- expectedActions,
- () => {
- expect(latestGetParams()).toEqual({
- pod_name: mockPodName,
- search: mockSearch,
- });
- },
- );
- });
-
- it('should commit logs and pod data when no pod name defined', () => {
- state.timeRange.current = defaultTimeRange;
-
- return testAction(
- fetchMoreLogsPrepend,
- null,
- state,
- expectedMutations,
- expectedActions,
- () => {
- expect(latestGetParams()).toEqual({
- start_time: expect.any(String),
- end_time: expect.any(String),
- });
- },
- );
- });
-
- it('should not commit logs or pod data when it has reached the end', () => {
- state.logs.isComplete = true;
- state.logs.cursor = null;
-
- return testAction(
- fetchMoreLogsPrepend,
- null,
- state,
- [], // no mutations done
- [], // no actions dispatched
- () => {
- expect(mock.history.get).toHaveLength(0);
- },
- );
- });
- });
- });
-
- describe('when the backend responds with an error', () => {
- beforeEach(() => {
- mock = new MockAdapter(axios);
- mock.onGet(mockLogsEndpoint).reply(500);
- });
-
- afterEach(() => {
- mock.reset();
- });
-
- it('fetchLogs should commit logs and pod errors', () => {
- state.environments.options = mockEnvironments;
- state.environments.current = mockEnvName;
- state.timeRange.current = defaultTimeRange;
-
- return testAction(
- fetchLogs,
- null,
- state,
- [
- { type: types.REQUEST_LOGS_DATA },
- { type: types.RECEIVE_PODS_DATA_ERROR },
- { type: types.RECEIVE_LOGS_DATA_ERROR },
- ],
- [],
- () => {
- expect(mock.history.get[0].url).toBe(mockLogsEndpoint);
- },
- );
- });
-
- it('fetchMoreLogsPrepend should commit logs and pod errors', () => {
- state.environments.options = mockEnvironments;
- state.environments.current = mockEnvName;
- state.timeRange.current = defaultTimeRange;
-
- return testAction(
- fetchMoreLogsPrepend,
- null,
- state,
- [
- { type: types.REQUEST_LOGS_DATA_PREPEND },
- { type: types.RECEIVE_LOGS_DATA_PREPEND_ERROR },
- ],
- [],
- () => {
- expect(mock.history.get[0].url).toBe(mockLogsEndpoint);
- },
- );
- });
- });
-});
-
-describe('Tracking user interaction', () => {
- let commit;
- let dispatch;
- let state;
- let mock;
-
- beforeEach(() => {
- jest.spyOn(Tracking, 'event');
- commit = jest.fn();
- dispatch = jest.fn();
- state = logsPageState();
- state.environments.options = mockEnvironments;
- state.environments.current = mockEnvName;
-
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- mock.reset();
- });
-
- describe('Logs with data', () => {
- beforeEach(() => {
- mock.onGet(mockLogsEndpoint).reply(200, mockResponse);
- mock.onGet(mockLogsEndpoint).replyOnce(202); // mock reactive cache
- });
-
- it('tracks fetched logs with data', () => {
- return fetchLogs({ state, commit, dispatch }, 'environment_selected').then(() => {
- expect(Tracking.event).toHaveBeenCalledWith(document.body.dataset.page, 'logs_view', {
- label: 'environment_selected',
- property: 'count',
- value: 1,
- });
- });
- });
- });
-
- describe('Logs without data', () => {
- beforeEach(() => {
- mock.onGet(mockLogsEndpoint).reply(200, {
- ...mockResponse,
- logs: [],
- });
- mock.onGet(mockLogsEndpoint).replyOnce(202); // mock reactive cache
- });
-
- it('does not track empty log responses', () => {
- return fetchLogs({ state, commit, dispatch }).then(() => {
- expect(Tracking.event).not.toHaveBeenCalled();
- });
- });
- });
-});
diff --git a/spec/frontend/logs/stores/getters_spec.js b/spec/frontend/logs/stores/getters_spec.js
deleted file mode 100644
index 9d213d8c01f..00000000000
--- a/spec/frontend/logs/stores/getters_spec.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import { trace, showAdvancedFilters } from '~/logs/stores/getters';
-import logsPageState from '~/logs/stores/state';
-
-import { mockLogsResult, mockTrace, mockEnvName, mockEnvironments } from '../mock_data';
-
-describe('Logs Store getters', () => {
- let state;
-
- beforeEach(() => {
- state = logsPageState();
- });
-
- describe('trace', () => {
- describe('when state is initialized', () => {
- it('returns an empty string', () => {
- expect(trace(state)).toEqual('');
- });
- });
-
- describe('when state logs are empty', () => {
- beforeEach(() => {
- state.logs.lines = [];
- });
-
- it('returns an empty string', () => {
- expect(trace(state)).toEqual('');
- });
- });
-
- describe('when state logs are set', () => {
- beforeEach(() => {
- state.logs.lines = mockLogsResult;
- });
-
- it('returns an empty string', () => {
- expect(trace(state)).toEqual(mockTrace.join('\n'));
- });
- });
- });
-
- describe('showAdvancedFilters', () => {
- describe('when no environments are set', () => {
- beforeEach(() => {
- state.environments.current = mockEnvName;
- state.environments.options = [];
- });
-
- it('returns false', () => {
- expect(showAdvancedFilters(state)).toBe(false);
- });
- });
-
- describe('when the environment supports filters', () => {
- beforeEach(() => {
- state.environments.current = mockEnvName;
- state.environments.options = mockEnvironments;
- });
-
- it('returns true', () => {
- expect(showAdvancedFilters(state)).toBe(true);
- });
- });
-
- describe('when the environment does not support filters', () => {
- beforeEach(() => {
- state.environments.options = mockEnvironments;
- state.environments.current = mockEnvironments[1].name;
- });
-
- it('returns true', () => {
- expect(showAdvancedFilters(state)).toBe(false);
- });
- });
- });
-});
diff --git a/spec/frontend/notebook/cells/prompt_spec.js b/spec/frontend/notebook/cells/prompt_spec.js
index 89b2d7b2b90..0cda0c5bc2b 100644
--- a/spec/frontend/notebook/cells/prompt_spec.js
+++ b/spec/frontend/notebook/cells/prompt_spec.js
@@ -1,52 +1,40 @@
-import Vue, { nextTick } from 'vue';
-import PromptComponent from '~/notebook/cells/prompt.vue';
-
-const Component = Vue.extend(PromptComponent);
+import { shallowMount } from '@vue/test-utils';
+import Prompt from '~/notebook/cells/prompt.vue';
describe('Prompt component', () => {
- let vm;
+ let wrapper;
+
+ const mountComponent = ({ type }) => shallowMount(Prompt, { propsData: { type, count: 1 } });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
describe('input', () => {
beforeEach(() => {
- vm = new Component({
- propsData: {
- type: 'In',
- count: 1,
- },
- });
- vm.$mount();
-
- return nextTick();
+ wrapper = mountComponent({ type: 'In' });
});
it('renders in label', () => {
- expect(vm.$el.textContent.trim()).toContain('In');
+ expect(wrapper.text()).toContain('In');
});
it('renders count', () => {
- expect(vm.$el.textContent.trim()).toContain('1');
+ expect(wrapper.text()).toContain('1');
});
});
describe('output', () => {
beforeEach(() => {
- vm = new Component({
- propsData: {
- type: 'Out',
- count: 1,
- },
- });
- vm.$mount();
-
- return nextTick();
+ wrapper = mountComponent({ type: 'Out' });
});
it('renders in label', () => {
- expect(vm.$el.textContent.trim()).toContain('Out');
+ expect(wrapper.text()).toContain('Out');
});
it('renders count', () => {
- expect(vm.$el.textContent.trim()).toContain('1');
+ expect(wrapper.text()).toContain('1');
});
});
});
diff --git a/spec/frontend/notes/components/note_signed_out_widget_spec.js b/spec/frontend/notes/components/note_signed_out_widget_spec.js
index e217a2caa73..84f20e4ad58 100644
--- a/spec/frontend/notes/components/note_signed_out_widget_spec.js
+++ b/spec/frontend/notes/components/note_signed_out_widget_spec.js
@@ -1,41 +1,30 @@
-import Vue from 'vue';
-import noteSignedOut from '~/notes/components/note_signed_out_widget.vue';
+import { shallowMount } from '@vue/test-utils';
+import NoteSignedOutWidget from '~/notes/components/note_signed_out_widget.vue';
import createStore from '~/notes/stores';
import { notesDataMock } from '../mock_data';
-describe('note_signed_out_widget component', () => {
- let store;
- let vm;
+describe('NoteSignedOutWidget component', () => {
+ let wrapper;
beforeEach(() => {
- const Component = Vue.extend(noteSignedOut);
- store = createStore();
+ const store = createStore();
store.dispatch('setNotesData', notesDataMock);
-
- vm = new Component({
- store,
- }).$mount();
+ wrapper = shallowMount(NoteSignedOutWidget, { store });
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
- it('should render sign in link provided in the store', () => {
- expect(vm.$el.querySelector(`a[href="${notesDataMock.newSessionPath}"]`).textContent).toEqual(
- 'sign in',
- );
+ it('renders sign in link provided in the store', () => {
+ expect(wrapper.find(`a[href="${notesDataMock.newSessionPath}"]`).text()).toBe('sign in');
});
- it('should render register link provided in the store', () => {
- expect(vm.$el.querySelector(`a[href="${notesDataMock.registerPath}"]`).textContent).toEqual(
- 'register',
- );
+ it('renders register link provided in the store', () => {
+ expect(wrapper.find(`a[href="${notesDataMock.registerPath}"]`).text()).toBe('register');
});
- it('should render information text', () => {
- expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual(
- 'Please register or sign in to reply',
- );
+ it('renders information text', () => {
+ expect(wrapper.text()).toContain('Please register or sign in to reply');
});
});
diff --git a/spec/frontend/pdf/index_spec.js b/spec/frontend/pdf/index_spec.js
index 2b0932493bb..98946412264 100644
--- a/spec/frontend/pdf/index_spec.js
+++ b/spec/frontend/pdf/index_spec.js
@@ -1,48 +1,33 @@
-import Vue from 'vue';
-
+import { shallowMount } from '@vue/test-utils';
import { FIXTURES_PATH } from 'spec/test_constants';
import PDFLab from '~/pdf/index.vue';
-jest.mock('pdfjs-dist/webpack', () => {
- return { default: jest.requireActual('pdfjs-dist/build/pdf') };
-});
-
-const pdf = `${FIXTURES_PATH}/blob/pdf/test.pdf`;
+describe('PDFLab component', () => {
+ let wrapper;
-const Component = Vue.extend(PDFLab);
+ const mountComponent = ({ pdf }) => shallowMount(PDFLab, { propsData: { pdf } });
-describe('PDF component', () => {
- let vm;
+ afterEach(() => {
+ wrapper.destroy();
+ });
describe('without PDF data', () => {
beforeEach(() => {
- vm = new Component({
- propsData: {
- pdf: '',
- },
- });
-
- vm.$mount();
+ wrapper = mountComponent({ pdf: '' });
});
it('does not render', () => {
- expect(vm.$el.tagName).toBeUndefined();
+ expect(wrapper.isVisible()).toBe(false);
});
});
describe('with PDF data', () => {
beforeEach(() => {
- vm = new Component({
- propsData: {
- pdf,
- },
- });
-
- vm.$mount();
+ wrapper = mountComponent({ pdf: `${FIXTURES_PATH}/blob/pdf/test.pdf` });
});
- it('renders pdf component', () => {
- expect(vm.$el.tagName).toBeDefined();
+ it('renders', () => {
+ expect(wrapper.isVisible()).toBe(true);
});
});
});
diff --git a/spec/frontend/vue_shared/components/page_size_selector_spec.js b/spec/frontend/vue_shared/components/page_size_selector_spec.js
new file mode 100644
index 00000000000..5ec0b863afd
--- /dev/null
+++ b/spec/frontend/vue_shared/components/page_size_selector_spec.js
@@ -0,0 +1,44 @@
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import PageSizeSelector, { PAGE_SIZES } from '~/vue_shared/components/page_size_selector.vue';
+
+describe('Page size selector component', () => {
+ let wrapper;
+
+ const createWrapper = ({ pageSize = 20 } = {}) => {
+ wrapper = shallowMount(PageSizeSelector, {
+ propsData: { value: pageSize },
+ });
+ };
+
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it.each(PAGE_SIZES)('shows expected text in the dropdown button for page size %s', (pageSize) => {
+ createWrapper({ pageSize });
+
+ expect(findDropdown().props('text')).toBe(`Show ${pageSize} items`);
+ });
+
+ it('shows the expected dropdown items', () => {
+ createWrapper();
+
+ PAGE_SIZES.forEach((pageSize, index) => {
+ expect(findDropdownItems().at(index).text()).toBe(`Show ${pageSize} items`);
+ });
+ });
+
+ it('will emit the new page size when a dropdown item is clicked', () => {
+ createWrapper();
+
+ findDropdownItems().wrappers.forEach((itemWrapper, index) => {
+ itemWrapper.vm.$emit('click');
+
+ expect(wrapper.emitted('input')[index][0]).toBe(PAGE_SIZES[index]);
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/issuable/list/components/issuable_list_root_spec.js b/spec/frontend/vue_shared/issuable/list/components/issuable_list_root_spec.js
index 66f71c0b028..50e79dbe589 100644
--- a/spec/frontend/vue_shared/issuable/list/components/issuable_list_root_spec.js
+++ b/spec/frontend/vue_shared/issuable/list/components/issuable_list_root_spec.js
@@ -9,6 +9,7 @@ import IssuableItem from '~/vue_shared/issuable/list/components/issuable_item.vu
import IssuableListRoot from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
import IssuableTabs from '~/vue_shared/issuable/list/components/issuable_tabs.vue';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
+import PageSizeSelector from '~/vue_shared/components/page_size_selector.vue';
import { mockIssuableListProps, mockIssuables } from '../mock_data';
@@ -44,6 +45,7 @@ describe('IssuableListRoot', () => {
const findIssuableItem = () => wrapper.findComponent(IssuableItem);
const findIssuableTabs = () => wrapper.findComponent(IssuableTabs);
const findVueDraggable = () => wrapper.findComponent(VueDraggable);
+ const findPageSizeSelector = () => wrapper.findComponent(PageSizeSelector);
afterEach(() => {
wrapper.destroy();
@@ -292,6 +294,7 @@ describe('IssuableListRoot', () => {
});
expect(findGlKeysetPagination().exists()).toBe(false);
+ expect(findPageSizeSelector().exists()).toBe(false);
expect(findGlPagination().props()).toMatchObject({
perPage: 20,
value: 1,
@@ -483,4 +486,24 @@ describe('IssuableListRoot', () => {
});
});
});
+
+ describe('page size selector', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ props: {
+ showPageSizeChangeControls: true,
+ },
+ });
+ });
+
+ it('has the page size change component', async () => {
+ expect(findPageSizeSelector().exists()).toBe(true);
+ });
+
+ it('emits "page-size-change" event when its input is changed', () => {
+ const pageSize = 123;
+ findPageSizeSelector().vm.$emit('input', pageSize);
+ expect(wrapper.emitted('page-size-change')).toEqual([[pageSize]]);
+ });
+ });
});