diff options
Diffstat (limited to 'spec/frontend/nav')
-rw-r--r-- | spec/frontend/nav/components/new_nav_toggle_spec.js | 214 | ||||
-rw-r--r-- | spec/frontend/nav/components/responsive_app_spec.js | 122 | ||||
-rw-r--r-- | spec/frontend/nav/components/responsive_header_spec.js | 63 | ||||
-rw-r--r-- | spec/frontend/nav/components/responsive_home_spec.js | 133 | ||||
-rw-r--r-- | spec/frontend/nav/components/top_nav_app_spec.js | 68 | ||||
-rw-r--r-- | spec/frontend/nav/components/top_nav_container_view_spec.js | 120 | ||||
-rw-r--r-- | spec/frontend/nav/components/top_nav_dropdown_menu_spec.js | 146 | ||||
-rw-r--r-- | spec/frontend/nav/components/top_nav_menu_item_spec.js | 145 | ||||
-rw-r--r-- | spec/frontend/nav/components/top_nav_menu_sections_spec.js | 138 | ||||
-rw-r--r-- | spec/frontend/nav/components/top_nav_new_dropdown_spec.js | 142 | ||||
-rw-r--r-- | spec/frontend/nav/mock_data.js | 39 |
11 files changed, 0 insertions, 1330 deletions
diff --git a/spec/frontend/nav/components/new_nav_toggle_spec.js b/spec/frontend/nav/components/new_nav_toggle_spec.js deleted file mode 100644 index cf8e59d6522..00000000000 --- a/spec/frontend/nav/components/new_nav_toggle_spec.js +++ /dev/null @@ -1,214 +0,0 @@ -import { mount, createWrapper } from '@vue/test-utils'; -import MockAdapter from 'axios-mock-adapter'; -import { getByText as getByTextHelper } from '@testing-library/dom'; -import { GlDisclosureDropdownItem, GlToggle } from '@gitlab/ui'; -import axios from '~/lib/utils/axios_utils'; -import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status'; -import { useMockLocationHelper } from 'helpers/mock_window_location_helper'; -import NewNavToggle from '~/nav/components/new_nav_toggle.vue'; -import waitForPromises from 'helpers/wait_for_promises'; -import { createAlert } from '~/alert'; -import { s__ } from '~/locale'; -import { mockTracking } from 'helpers/tracking_helper'; - -jest.mock('~/alert'); - -const TEST_ENDPONT = 'https://example.com/toggle'; - -describe('NewNavToggle', () => { - useMockLocationHelper(); - - let wrapper; - let trackingSpy; - - const findToggle = () => wrapper.findComponent(GlToggle); - const findDisclosureItem = () => wrapper.findComponent(GlDisclosureDropdownItem); - - const createComponent = (propsData = { enabled: false }) => { - wrapper = mount(NewNavToggle, { - propsData: { - endpoint: TEST_ENDPONT, - ...propsData, - }, - }); - - trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); - }; - - const getByText = (text, options) => - createWrapper(getByTextHelper(wrapper.element, text, options)); - - describe('When rendered in scope of the new navigation', () => { - it('renders the disclosure item', () => { - createComponent({ newNavigation: true, enabled: true }); - expect(findDisclosureItem().exists()).toBe(true); - }); - - describe('when user preference is enabled', () => { - beforeEach(() => { - createComponent({ newNavigation: true, enabled: true }); - }); - - it('renders the toggle as enabled', () => { - expect(findToggle().props('value')).toBe(true); - }); - }); - - describe('when user preference is disabled', () => { - beforeEach(() => { - createComponent({ enabled: false }); - }); - - it('renders the toggle as disabled', () => { - expect(findToggle().props('value')).toBe(false); - }); - }); - - describe.each` - desc | actFn | toggleValue | trackingLabel | trackingProperty - ${'when toggle button is clicked'} | ${() => findToggle().trigger('click')} | ${false} | ${'enable_new_nav_beta'} | ${'navigation_top'} - ${'when menu item text is clicked'} | ${() => getByText('New navigation').trigger('click')} | ${false} | ${'enable_new_nav_beta'} | ${'navigation_top'} - ${'when toggle button is clicked'} | ${() => findToggle().trigger('click')} | ${true} | ${'disable_new_nav_beta'} | ${'nav_user_menu'} - ${'when menu item text is clicked'} | ${() => getByText('New navigation').trigger('click')} | ${true} | ${'disable_new_nav_beta'} | ${'nav_user_menu'} - `('$desc', ({ actFn, toggleValue, trackingLabel, trackingProperty }) => { - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - createComponent({ enabled: toggleValue }); - }); - - it('reloads the page on success', async () => { - mock.onPut(TEST_ENDPONT).reply(HTTP_STATUS_OK); - - actFn(); - await waitForPromises(); - - expect(window.location.reload).toHaveBeenCalled(); - }); - - it('shows an alert on error', async () => { - mock.onPut(TEST_ENDPONT).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); - - actFn(); - await waitForPromises(); - - expect(createAlert).toHaveBeenCalledWith( - expect.objectContaining({ - message: s__( - 'NorthstarNavigation|Could not update the new navigation preference. Please try again later.', - ), - }), - ); - expect(window.location.reload).not.toHaveBeenCalled(); - }); - - it('changes the toggle', async () => { - await actFn(); - - expect(findToggle().props('value')).toBe(!toggleValue); - }); - - it('tracks the Snowplow event', async () => { - mock.onPut(TEST_ENDPONT).reply(HTTP_STATUS_OK); - await actFn(); - await waitForPromises(); - expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_toggle', { - label: trackingLabel, - property: trackingProperty, - }); - }); - - afterEach(() => { - mock.restore(); - }); - }); - }); - - describe('When rendered in scope of the current navigation', () => { - it('renders its title', () => { - createComponent(); - expect(getByText('Navigation redesign').exists()).toBe(true); - }); - - describe('when user preference is enabled', () => { - beforeEach(() => { - createComponent({ enabled: true }); - }); - - it('renders the toggle as enabled', () => { - expect(findToggle().props('value')).toBe(true); - }); - }); - - describe('when user preference is disabled', () => { - beforeEach(() => { - createComponent({ enabled: false }); - }); - - it('renders the toggle as disabled', () => { - expect(findToggle().props('value')).toBe(false); - }); - }); - - describe.each` - desc | actFn | toggleValue | trackingLabel | trackingProperty - ${'when toggle button is clicked'} | ${() => findToggle().trigger('click')} | ${false} | ${'enable_new_nav_beta'} | ${'navigation_top'} - ${'when menu item text is clicked'} | ${() => getByText('New navigation').trigger('click')} | ${false} | ${'enable_new_nav_beta'} | ${'navigation_top'} - ${'when toggle button is clicked'} | ${() => findToggle().trigger('click')} | ${true} | ${'disable_new_nav_beta'} | ${'nav_user_menu'} - ${'when menu item text is clicked'} | ${() => getByText('New navigation').trigger('click')} | ${true} | ${'disable_new_nav_beta'} | ${'nav_user_menu'} - `('$desc', ({ actFn, toggleValue, trackingLabel, trackingProperty }) => { - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - createComponent({ enabled: toggleValue }); - }); - - it('reloads the page on success', async () => { - mock.onPut(TEST_ENDPONT).reply(HTTP_STATUS_OK); - - actFn(); - await waitForPromises(); - - expect(window.location.reload).toHaveBeenCalled(); - }); - - it('shows an alert on error', async () => { - mock.onPut(TEST_ENDPONT).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); - - actFn(); - await waitForPromises(); - - expect(createAlert).toHaveBeenCalledWith( - expect.objectContaining({ - message: s__( - 'NorthstarNavigation|Could not update the new navigation preference. Please try again later.', - ), - }), - ); - expect(window.location.reload).not.toHaveBeenCalled(); - }); - - it('changes the toggle', async () => { - await actFn(); - - expect(findToggle().props('value')).toBe(!toggleValue); - }); - - it('tracks the Snowplow event', async () => { - mock.onPut(TEST_ENDPONT).reply(HTTP_STATUS_OK); - await actFn(); - await waitForPromises(); - expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_toggle', { - label: trackingLabel, - property: trackingProperty, - }); - }); - - afterEach(() => { - mock.restore(); - }); - }); - }); -}); diff --git a/spec/frontend/nav/components/responsive_app_spec.js b/spec/frontend/nav/components/responsive_app_spec.js deleted file mode 100644 index 9d3b43520ec..00000000000 --- a/spec/frontend/nav/components/responsive_app_spec.js +++ /dev/null @@ -1,122 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { nextTick } from 'vue'; -import ResponsiveApp from '~/nav/components/responsive_app.vue'; -import ResponsiveHeader from '~/nav/components/responsive_header.vue'; -import ResponsiveHome from '~/nav/components/responsive_home.vue'; -import TopNavContainerView from '~/nav/components/top_nav_container_view.vue'; -import { resetMenuItemsActive } from '~/nav/utils/reset_menu_items_active'; -import KeepAliveSlots from '~/vue_shared/components/keep_alive_slots.vue'; -import { TEST_NAV_DATA } from '../mock_data'; - -describe('~/nav/components/responsive_app.vue', () => { - let wrapper; - - const createComponent = () => { - wrapper = shallowMount(ResponsiveApp, { - propsData: { - navData: TEST_NAV_DATA, - }, - stubs: { - KeepAliveSlots, - }, - }); - }; - const findHome = () => wrapper.findComponent(ResponsiveHome); - const findMobileOverlay = () => wrapper.find('[data-testid="mobile-overlay"]'); - const findSubviewHeader = () => wrapper.findComponent(ResponsiveHeader); - const findSubviewContainer = () => wrapper.findComponent(TopNavContainerView); - const hasMobileOverlayVisible = () => findMobileOverlay().classes('mobile-nav-open'); - - beforeEach(() => { - document.body.innerHTML = ''; - // Add test class to reset state + assert that we're adding classes correctly - document.body.className = 'test-class'; - }); - - describe('default', () => { - beforeEach(() => { - createComponent(); - }); - - it('shows home by default', () => { - expect(findHome().isVisible()).toBe(true); - expect(findHome().props()).toEqual({ - navData: resetMenuItemsActive(TEST_NAV_DATA), - }); - }); - - it.each` - events | expectation - ${[]} | ${false} - ${['bv::dropdown::show']} | ${true} - ${['bv::dropdown::show', 'bv::dropdown::hide']} | ${false} - `( - 'with root events $events, movile overlay visible = $expectation', - async ({ events, expectation }) => { - // `await...reduce(async` is like doing an `forEach(async (...))` excpet it works - await events.reduce(async (acc, evt) => { - await acc; - - wrapper.vm.$root.$emit(evt); - - await nextTick(); - }, Promise.resolve()); - - expect(hasMobileOverlayVisible()).toBe(expectation); - }, - ); - }); - - const projectsContainerProps = { - containerClass: 'gl-px-3', - frequentItemsDropdownType: ResponsiveApp.FREQUENT_ITEMS_PROJECTS.namespace, - frequentItemsVuexModule: ResponsiveApp.FREQUENT_ITEMS_PROJECTS.vuexModule, - currentItem: {}, - linksPrimary: TEST_NAV_DATA.views.projects.linksPrimary, - linksSecondary: TEST_NAV_DATA.views.projects.linksSecondary, - }; - const groupsContainerProps = { - containerClass: 'gl-px-3', - frequentItemsDropdownType: ResponsiveApp.FREQUENT_ITEMS_GROUPS.namespace, - frequentItemsVuexModule: ResponsiveApp.FREQUENT_ITEMS_GROUPS.vuexModule, - currentItem: {}, - linksPrimary: TEST_NAV_DATA.views.groups.linksPrimary, - linksSecondary: TEST_NAV_DATA.views.groups.linksSecondary, - }; - - describe.each` - view | header | containerProps - ${'projects'} | ${'Projects'} | ${projectsContainerProps} - ${'groups'} | ${'Groups'} | ${groupsContainerProps} - `('when menu item with $view is clicked', ({ view, header, containerProps }) => { - beforeEach(async () => { - createComponent(); - - findHome().vm.$emit('menu-item-click', { view }); - - await nextTick(); - }); - - it('shows header', () => { - expect(findSubviewHeader().text()).toBe(header); - }); - - it('shows container subview', () => { - expect(findSubviewContainer().props()).toEqual(containerProps); - }); - - it('hides home', () => { - expect(findHome().isVisible()).toBe(false); - }); - - describe('when header back button is clicked', () => { - beforeEach(() => { - findSubviewHeader().vm.$emit('menu-item-click', { view: 'home' }); - }); - - it('shows home', () => { - expect(findHome().isVisible()).toBe(true); - }); - }); - }); -}); diff --git a/spec/frontend/nav/components/responsive_header_spec.js b/spec/frontend/nav/components/responsive_header_spec.js deleted file mode 100644 index 2514035270a..00000000000 --- a/spec/frontend/nav/components/responsive_header_spec.js +++ /dev/null @@ -1,63 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; -import ResponsiveHeader from '~/nav/components/responsive_header.vue'; -import TopNavMenuItem from '~/nav/components/top_nav_menu_item.vue'; - -const TEST_SLOT_CONTENT = 'Test slot content'; - -describe('~/nav/components/top_nav_menu_sections.vue', () => { - let wrapper; - - const createComponent = () => { - wrapper = shallowMount(ResponsiveHeader, { - slots: { - default: TEST_SLOT_CONTENT, - }, - directives: { - GlTooltip: createMockDirective('gl-tooltip'), - }, - }); - }; - - const findMenuItem = () => wrapper.findComponent(TopNavMenuItem); - - beforeEach(() => { - createComponent(); - }); - - it('renders slot', () => { - expect(wrapper.text()).toBe(TEST_SLOT_CONTENT); - }); - - it('renders back button', () => { - const button = findMenuItem(); - - const tooltip = getBinding(button.element, 'gl-tooltip').value.title; - - expect(tooltip).toBe('Go back'); - expect(button.props()).toEqual({ - menuItem: { - id: 'home', - view: 'home', - icon: 'chevron-lg-left', - }, - iconOnly: true, - }); - }); - - it('emits nothing', () => { - expect(wrapper.emitted()).toEqual({}); - }); - - describe('when back button is clicked', () => { - beforeEach(() => { - findMenuItem().vm.$emit('click'); - }); - - it('emits menu-item-click', () => { - expect(wrapper.emitted()).toEqual({ - 'menu-item-click': [[{ id: 'home', view: 'home', icon: 'chevron-lg-left' }]], - }); - }); - }); -}); diff --git a/spec/frontend/nav/components/responsive_home_spec.js b/spec/frontend/nav/components/responsive_home_spec.js deleted file mode 100644 index 5a5cfc93607..00000000000 --- a/spec/frontend/nav/components/responsive_home_spec.js +++ /dev/null @@ -1,133 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; -import ResponsiveHome from '~/nav/components/responsive_home.vue'; -import TopNavMenuItem from '~/nav/components/top_nav_menu_item.vue'; -import TopNavMenuSections from '~/nav/components/top_nav_menu_sections.vue'; -import TopNavNewDropdown from '~/nav/components/top_nav_new_dropdown.vue'; -import { TEST_NAV_DATA } from '../mock_data'; - -const TEST_SEARCH_MENU_ITEM = { - id: 'search', - title: 'search', - icon: 'search', - href: '/search', -}; - -const TEST_NEW_DROPDOWN_VIEW_MODEL = { - title: 'new', - menu_sections: [], -}; - -describe('~/nav/components/responsive_home.vue', () => { - let wrapper; - let menuItemClickListener; - - const createComponent = (props = {}) => { - wrapper = shallowMount(ResponsiveHome, { - propsData: { - navData: TEST_NAV_DATA, - ...props, - }, - directives: { - GlTooltip: createMockDirective('gl-tooltip'), - }, - listeners: { - 'menu-item-click': menuItemClickListener, - }, - }); - }; - - const findSearchMenuItem = () => wrapper.findComponent(TopNavMenuItem); - const findNewDropdown = () => wrapper.findComponent(TopNavNewDropdown); - const findMenuSections = () => wrapper.findComponent(TopNavMenuSections); - - beforeEach(() => { - menuItemClickListener = jest.fn(); - }); - - describe('default', () => { - beforeEach(() => { - createComponent(); - }); - - it.each` - desc | fn - ${'does not show search menu item'} | ${findSearchMenuItem} - ${'does not show new dropdown'} | ${findNewDropdown} - `('$desc', ({ fn }) => { - expect(fn().exists()).toBe(false); - }); - - it('shows menu sections', () => { - expect(findMenuSections().props('sections')).toEqual([ - { id: 'primary', menuItems: TEST_NAV_DATA.primary }, - { id: 'secondary', menuItems: TEST_NAV_DATA.secondary }, - ]); - }); - - it('emits when menu sections emits', () => { - expect(menuItemClickListener).not.toHaveBeenCalled(); - - findMenuSections().vm.$emit('menu-item-click', TEST_NAV_DATA.primary[0]); - - expect(menuItemClickListener).toHaveBeenCalledWith(TEST_NAV_DATA.primary[0]); - }); - }); - - describe('without secondary', () => { - beforeEach(() => { - createComponent({ navData: { ...TEST_NAV_DATA, secondary: null } }); - }); - - it('shows menu sections', () => { - expect(findMenuSections().props('sections')).toEqual([ - { id: 'primary', menuItems: TEST_NAV_DATA.primary }, - ]); - }); - }); - - describe('with search view', () => { - beforeEach(() => { - createComponent({ - navData: { - ...TEST_NAV_DATA, - views: { search: TEST_SEARCH_MENU_ITEM }, - }, - }); - }); - - it('shows search menu item', () => { - expect(findSearchMenuItem().props()).toEqual({ - menuItem: TEST_SEARCH_MENU_ITEM, - iconOnly: true, - }); - }); - - it('shows tooltip for search', () => { - const tooltip = getBinding(findSearchMenuItem().element, 'gl-tooltip'); - expect(tooltip.value).toEqual({ title: TEST_SEARCH_MENU_ITEM.title }); - }); - }); - - describe('with new view', () => { - beforeEach(() => { - createComponent({ - navData: { - ...TEST_NAV_DATA, - views: { new: TEST_NEW_DROPDOWN_VIEW_MODEL }, - }, - }); - }); - - it('shows new dropdown', () => { - expect(findNewDropdown().props()).toEqual({ - viewModel: TEST_NEW_DROPDOWN_VIEW_MODEL, - }); - }); - - it('shows tooltip for new dropdown', () => { - const tooltip = getBinding(findNewDropdown().element, 'gl-tooltip'); - expect(tooltip.value).toEqual({ title: TEST_NEW_DROPDOWN_VIEW_MODEL.title }); - }); - }); -}); diff --git a/spec/frontend/nav/components/top_nav_app_spec.js b/spec/frontend/nav/components/top_nav_app_spec.js deleted file mode 100644 index 7f39552eb42..00000000000 --- a/spec/frontend/nav/components/top_nav_app_spec.js +++ /dev/null @@ -1,68 +0,0 @@ -import { GlNavItemDropdown } from '@gitlab/ui'; -import { mount, shallowMount } from '@vue/test-utils'; -import { mockTracking } from 'helpers/tracking_helper'; -import TopNavApp from '~/nav/components/top_nav_app.vue'; -import TopNavDropdownMenu from '~/nav/components/top_nav_dropdown_menu.vue'; -import { TEST_NAV_DATA } from '../mock_data'; - -describe('~/nav/components/top_nav_app.vue', () => { - let wrapper; - - const createComponent = () => { - wrapper = mount(TopNavApp, { - propsData: { - navData: TEST_NAV_DATA, - }, - }); - }; - - const createComponentShallow = () => { - wrapper = shallowMount(TopNavApp, { - propsData: { - navData: TEST_NAV_DATA, - }, - }); - }; - - const findNavItemDropdown = () => wrapper.findComponent(GlNavItemDropdown); - const findNavItemDropdowToggle = () => findNavItemDropdown().find('.js-top-nav-dropdown-toggle'); - const findMenu = () => wrapper.findComponent(TopNavDropdownMenu); - - describe('default', () => { - beforeEach(() => { - createComponentShallow(); - }); - - it('renders nav item dropdown', () => { - expect(findNavItemDropdown().attributes('href')).toBeUndefined(); - expect(findNavItemDropdown().attributes()).toMatchObject({ - icon: '', - text: '', - 'no-flip': '', - 'no-caret': '', - }); - }); - - it('renders top nav dropdown menu', () => { - expect(findMenu().props()).toStrictEqual({ - primary: TEST_NAV_DATA.primary, - secondary: TEST_NAV_DATA.secondary, - views: TEST_NAV_DATA.views, - }); - }); - }); - - describe('tracking', () => { - it('emits a tracking event when the toggle is clicked', () => { - const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); - createComponent(); - - findNavItemDropdowToggle().trigger('click'); - - expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_nav', { - label: 'hamburger_menu', - property: 'navigation_top', - }); - }); - }); -}); diff --git a/spec/frontend/nav/components/top_nav_container_view_spec.js b/spec/frontend/nav/components/top_nav_container_view_spec.js deleted file mode 100644 index 388ac243648..00000000000 --- a/spec/frontend/nav/components/top_nav_container_view_spec.js +++ /dev/null @@ -1,120 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { merge } from 'lodash'; -import { nextTick } from 'vue'; -import FrequentItemsApp from '~/frequent_items/components/app.vue'; -import { FREQUENT_ITEMS_PROJECTS } from '~/frequent_items/constants'; -import eventHub from '~/frequent_items/event_hub'; -import TopNavContainerView from '~/nav/components/top_nav_container_view.vue'; -import TopNavMenuSections from '~/nav/components/top_nav_menu_sections.vue'; -import VuexModuleProvider from '~/vue_shared/components/vuex_module_provider.vue'; -import { TEST_NAV_DATA } from '../mock_data'; - -const DEFAULT_PROPS = { - frequentItemsDropdownType: FREQUENT_ITEMS_PROJECTS.namespace, - frequentItemsVuexModule: FREQUENT_ITEMS_PROJECTS.vuexModule, - linksPrimary: TEST_NAV_DATA.primary, - linksSecondary: TEST_NAV_DATA.secondary, - containerClass: 'test-frequent-items-container-class', -}; -const TEST_OTHER_PROPS = { - namespace: 'projects', - currentUserName: 'test-user', - currentItem: { id: 'test' }, -}; - -describe('~/nav/components/top_nav_container_view.vue', () => { - let wrapper; - - const createComponent = (props = {}, options = {}) => { - wrapper = shallowMount(TopNavContainerView, { - propsData: { - ...DEFAULT_PROPS, - ...TEST_OTHER_PROPS, - ...props, - }, - ...options, - }); - }; - - const findMenuSections = () => wrapper.findComponent(TopNavMenuSections); - const findFrequentItemsApp = () => { - const parent = wrapper.findComponent(VuexModuleProvider); - - return { - vuexModule: parent.props('vuexModule'), - props: parent.findComponent(FrequentItemsApp).props(), - attributes: parent.findComponent(FrequentItemsApp).attributes(), - }; - }; - const findFrequentItemsContainer = () => wrapper.find('[data-testid="frequent-items-container"]'); - - it.each(['projects', 'groups'])( - 'emits frequent items event to event hub (%s)', - async (frequentItemsDropdownType) => { - const listener = jest.fn(); - eventHub.$on(`${frequentItemsDropdownType}-dropdownOpen`, listener); - createComponent({ frequentItemsDropdownType }); - - expect(listener).not.toHaveBeenCalled(); - - await nextTick(); - - expect(listener).toHaveBeenCalled(); - }, - ); - - describe('default', () => { - const EXTRA_ATTRS = { 'data-test-attribute': 'foo' }; - - beforeEach(() => { - createComponent({}, { attrs: EXTRA_ATTRS }); - }); - - it('does not inherit extra attrs', () => { - expect(wrapper.attributes()).toEqual({ - class: expect.any(String), - }); - }); - - it('renders frequent items app', () => { - expect(findFrequentItemsApp()).toEqual({ - vuexModule: DEFAULT_PROPS.frequentItemsVuexModule, - props: expect.objectContaining( - merge({ currentItem: { lastAccessedOn: Date.now() } }, TEST_OTHER_PROPS), - ), - attributes: expect.objectContaining(EXTRA_ATTRS), - }); - }); - - it('renders given container class', () => { - expect(findFrequentItemsContainer().classes(DEFAULT_PROPS.containerClass)).toBe(true); - }); - - it('renders menu sections', () => { - const sections = [ - { id: 'primary', menuItems: TEST_NAV_DATA.primary }, - { id: 'secondary', menuItems: TEST_NAV_DATA.secondary }, - ]; - - expect(findMenuSections().props()).toEqual({ - sections, - withTopBorder: true, - isPrimarySection: false, - }); - }); - }); - - describe('without secondary links', () => { - beforeEach(() => { - createComponent({ - linksSecondary: [], - }); - }); - - it('renders one menu item group', () => { - expect(findMenuSections().props('sections')).toEqual([ - { id: 'primary', menuItems: TEST_NAV_DATA.primary }, - ]); - }); - }); -}); diff --git a/spec/frontend/nav/components/top_nav_dropdown_menu_spec.js b/spec/frontend/nav/components/top_nav_dropdown_menu_spec.js deleted file mode 100644 index 1d516240306..00000000000 --- a/spec/frontend/nav/components/top_nav_dropdown_menu_spec.js +++ /dev/null @@ -1,146 +0,0 @@ -import { shallowMount, mount } from '@vue/test-utils'; -import { nextTick } from 'vue'; -import TopNavDropdownMenu from '~/nav/components/top_nav_dropdown_menu.vue'; -import TopNavMenuItem from '~/nav/components/top_nav_menu_item.vue'; -import TopNavMenuSections from '~/nav/components/top_nav_menu_sections.vue'; -import KeepAliveSlots from '~/vue_shared/components/keep_alive_slots.vue'; -import { TEST_NAV_DATA } from '../mock_data'; -import { stubComponent } from '../../__helpers__/stub_component'; - -describe('~/nav/components/top_nav_dropdown_menu.vue', () => { - let wrapper; - - const createComponent = (props = {}, mountFn = shallowMount) => { - wrapper = mountFn(TopNavDropdownMenu, { - propsData: { - primary: TEST_NAV_DATA.primary, - secondary: TEST_NAV_DATA.secondary, - views: TEST_NAV_DATA.views, - ...props, - }, - stubs: { - // Stub the keep-alive-slots so we don't render frequent items which uses a store - KeepAliveSlots: stubComponent(KeepAliveSlots), - }, - }); - }; - - const findMenuItems = () => wrapper.findAllComponents(TopNavMenuItem); - const findMenuSections = () => wrapper.findComponent(TopNavMenuSections); - const findMenuSidebar = () => wrapper.find('[data-testid="menu-sidebar"]'); - const findMenuSubview = () => wrapper.findComponent(KeepAliveSlots); - const hasFullWidthMenuSidebar = () => findMenuSidebar().classes('gl-w-full'); - - const withActiveIndex = (menuItems, activeIndex) => - menuItems.map((x, idx) => ({ - ...x, - active: idx === activeIndex, - })); - - beforeEach(() => { - jest.spyOn(console, 'error').mockImplementation(); - }); - - describe('default', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders menu sections', () => { - expect(findMenuSections().props()).toEqual({ - sections: [ - { id: 'primary', menuItems: TEST_NAV_DATA.primary }, - { id: 'secondary', menuItems: TEST_NAV_DATA.secondary }, - ], - withTopBorder: false, - isPrimarySection: true, - }); - }); - - it('has full width menu sidebar', () => { - expect(hasFullWidthMenuSidebar()).toBe(true); - }); - - it('renders hidden subview with no slot key', () => { - const subview = findMenuSubview(); - - expect(subview.isVisible()).toBe(false); - expect(subview.props()).toEqual({ slotKey: '' }); - }); - }); - - describe('with pre-initialized active view', () => { - beforeEach(() => { - // We opt for a small integration test, to make sure the event is handled correctly - // as it would in prod. - createComponent( - { - primary: withActiveIndex(TEST_NAV_DATA.primary, 1), - }, - mount, - ); - }); - - it('renders menu sections', () => { - expect(findMenuSections().props('sections')).toStrictEqual([ - { id: 'primary', menuItems: withActiveIndex(TEST_NAV_DATA.primary, 1) }, - { id: 'secondary', menuItems: TEST_NAV_DATA.secondary }, - ]); - }); - - it('does not have full width menu sidebar', () => { - expect(hasFullWidthMenuSidebar()).toBe(false); - }); - - it('renders visible subview with slot key', () => { - const subview = findMenuSubview(); - - expect(subview.isVisible()).toBe(true); - expect(subview.props('slotKey')).toBe(TEST_NAV_DATA.primary[1].view); - }); - - it('does not change view if non-view menu item is clicked', async () => { - const secondaryLink = findMenuItems().at(TEST_NAV_DATA.primary.length); - - // Ensure this doesn't have a view - expect(secondaryLink.props('menuItem').view).toBeUndefined(); - - secondaryLink.vm.$emit('click'); - - await nextTick(); - - expect(findMenuSubview().props('slotKey')).toBe(TEST_NAV_DATA.primary[1].view); - }); - - describe('when menu item is clicked', () => { - let primaryLink; - - beforeEach(async () => { - primaryLink = findMenuItems().at(0); - primaryLink.vm.$emit('click'); - await nextTick(); - }); - - it('clicked on link with view', () => { - expect(primaryLink.props('menuItem').view).toBe(TEST_NAV_DATA.views.projects.namespace); - }); - - it('changes active view', () => { - expect(findMenuSubview().props('slotKey')).toBe(TEST_NAV_DATA.primary[0].view); - }); - - it('changes active status on menu item', () => { - expect(findMenuSections().props('sections')).toStrictEqual([ - { - id: 'primary', - menuItems: withActiveIndex(TEST_NAV_DATA.primary, 0), - }, - { - id: 'secondary', - menuItems: withActiveIndex(TEST_NAV_DATA.secondary, -1), - }, - ]); - }); - }); - }); -}); diff --git a/spec/frontend/nav/components/top_nav_menu_item_spec.js b/spec/frontend/nav/components/top_nav_menu_item_spec.js deleted file mode 100644 index b9cf39b8c1d..00000000000 --- a/spec/frontend/nav/components/top_nav_menu_item_spec.js +++ /dev/null @@ -1,145 +0,0 @@ -import { GlButton, GlIcon } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import TopNavMenuItem from '~/nav/components/top_nav_menu_item.vue'; - -const TEST_MENU_ITEM = { - title: 'Cheeseburger', - icon: 'search', - href: '/pretty/good/burger', - view: 'burger-view', - data: { qa_selector: 'not-a-real-selector', method: 'post', testFoo: 'test' }, -}; - -describe('~/nav/components/top_nav_menu_item.vue', () => { - let listener; - let wrapper; - - const createComponent = (props = {}) => { - wrapper = shallowMount(TopNavMenuItem, { - propsData: { - menuItem: TEST_MENU_ITEM, - ...props, - }, - listeners: { - click: listener, - }, - }); - }; - - const findButton = () => wrapper.findComponent(GlButton); - const findButtonIcons = () => - findButton() - .findAllComponents(GlIcon) - .wrappers.map((x) => ({ - name: x.props('name'), - classes: x.classes(), - })); - - beforeEach(() => { - listener = jest.fn(); - }); - - describe('default', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders button href and text', () => { - const button = findButton(); - - expect(button.attributes('href')).toBe(TEST_MENU_ITEM.href); - expect(button.text()).toBe(TEST_MENU_ITEM.title); - }); - - it('renders button data attributes', () => { - const button = findButton(); - - expect(button.attributes()).toMatchObject({ - 'data-qa-selector': TEST_MENU_ITEM.data.qa_selector, - 'data-method': TEST_MENU_ITEM.data.method, - 'data-test-foo': TEST_MENU_ITEM.data.testFoo, - }); - }); - - it('passes listeners to button', () => { - expect(listener).not.toHaveBeenCalled(); - - findButton().vm.$emit('click', 'TEST'); - - expect(listener).toHaveBeenCalledWith('TEST'); - }); - - it('renders expected icons', () => { - expect(findButtonIcons()).toEqual([ - { - name: TEST_MENU_ITEM.icon, - classes: ['gl-mr-3!'], - }, - { - name: 'chevron-right', - classes: ['gl-ml-auto'], - }, - ]); - }); - }); - - describe('with icon-only', () => { - beforeEach(() => { - createComponent({ iconOnly: true }); - }); - - it('does not render title or view icon', () => { - expect(wrapper.text()).toBe(''); - }); - - it('only renders menuItem icon', () => { - expect(findButtonIcons()).toEqual([ - { - name: TEST_MENU_ITEM.icon, - classes: [], - }, - ]); - }); - }); - - describe.each` - desc | menuItem | expectedIcons - ${'with no icon'} | ${{ ...TEST_MENU_ITEM, icon: null }} | ${['chevron-right']} - ${'with no view'} | ${{ ...TEST_MENU_ITEM, view: null }} | ${[TEST_MENU_ITEM.icon]} - ${'with no icon or view'} | ${{ ...TEST_MENU_ITEM, view: null, icon: null }} | ${[]} - `('$desc', ({ menuItem, expectedIcons }) => { - beforeEach(() => { - createComponent({ menuItem }); - }); - - it(`renders expected icons ${JSON.stringify(expectedIcons)}`, () => { - expect(findButtonIcons().map((x) => x.name)).toEqual(expectedIcons); - }); - }); - - describe.each` - desc | active | cssClass | expectedClasses - ${'default'} | ${false} | ${''} | ${[]} - ${'with css class'} | ${false} | ${'test-css-class testing-123'} | ${['test-css-class', 'testing-123']} - ${'with css class & active'} | ${true} | ${'test-css-class'} | ${['test-css-class', ...TopNavMenuItem.ACTIVE_CLASS.split(' ')]} - `('$desc', ({ active, cssClass, expectedClasses }) => { - beforeEach(() => { - createComponent({ - menuItem: { - ...TEST_MENU_ITEM, - active, - css_class: cssClass, - }, - }); - }); - - it('renders expected classes', () => { - expect(wrapper.classes()).toStrictEqual([ - 'top-nav-menu-item', - 'gl-display-block', - 'gl-pr-3!', - ...expectedClasses, - ]); - }); - }); -}); diff --git a/spec/frontend/nav/components/top_nav_menu_sections_spec.js b/spec/frontend/nav/components/top_nav_menu_sections_spec.js deleted file mode 100644 index 7a3e58fd964..00000000000 --- a/spec/frontend/nav/components/top_nav_menu_sections_spec.js +++ /dev/null @@ -1,138 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import TopNavMenuSections from '~/nav/components/top_nav_menu_sections.vue'; - -const TEST_SECTIONS = [ - { - id: 'primary', - menuItems: [ - { type: 'header', title: 'Heading' }, - { type: 'item', id: 'test', href: '/test/href' }, - { type: 'header', title: 'Another Heading' }, - { type: 'item', id: 'foo' }, - { type: 'item', id: 'bar' }, - ], - }, - { - id: 'secondary', - menuItems: [ - { type: 'item', id: 'lorem' }, - { type: 'item', id: 'ipsum' }, - ], - }, -]; - -describe('~/nav/components/top_nav_menu_sections.vue', () => { - let wrapper; - - const createComponent = (props = {}) => { - wrapper = shallowMount(TopNavMenuSections, { - propsData: { - sections: TEST_SECTIONS, - ...props, - }, - }); - }; - - const findMenuItemModels = (parent) => - parent.findAll('[data-testid="menu-header"],[data-testid="menu-item"]').wrappers.map((x) => { - return { - menuItem: x.vm - ? { - type: 'item', - ...x.props('menuItem'), - } - : { - type: 'header', - title: x.text(), - }, - classes: x.classes(), - }; - }); - const findSectionModels = () => - wrapper.findAll('[data-testid="menu-section"]').wrappers.map((x) => ({ - classes: x.classes(), - menuItems: findMenuItemModels(x), - })); - - describe('default', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders sections with menu items', () => { - const headerClasses = ['gl-px-4', 'gl-py-2', 'gl-text-gray-900', 'gl-display-block']; - const itemClasses = ['gl-w-full']; - - expect(findSectionModels()).toEqual([ - { - classes: [], - menuItems: TEST_SECTIONS[0].menuItems.map((menuItem, index) => { - const classes = menuItem.type === 'header' ? [...headerClasses] : [...itemClasses]; - if (index > 0) classes.push(menuItem.type === 'header' ? 'gl-pt-3!' : 'gl-mt-1'); - return { - menuItem, - classes, - }; - }), - }, - { - classes: [ - ...TopNavMenuSections.BORDER_CLASSES.split(' '), - 'gl-border-gray-50', - 'gl-mt-3', - ], - menuItems: TEST_SECTIONS[1].menuItems.map((menuItem, index) => { - const classes = menuItem.type === 'header' ? [...headerClasses] : [...itemClasses]; - if (index > 0) classes.push(menuItem.type === 'header' ? 'gl-pt-3!' : 'gl-mt-1'); - return { - menuItem, - classes, - }; - }), - }, - ]); - }); - - it('when clicked menu item with href, does nothing', () => { - const menuItem = wrapper.findAll('[data-testid="menu-item"]').at(0); - - menuItem.vm.$emit('click'); - - expect(wrapper.emitted()).toEqual({}); - }); - - it('when clicked menu item without href, emits "menu-item-click"', () => { - const menuItem = wrapper.findAll('[data-testid="menu-item"]').at(1); - - menuItem.vm.$emit('click'); - - expect(wrapper.emitted('menu-item-click')).toEqual([[TEST_SECTIONS[0].menuItems[3]]]); - }); - }); - - describe('with withTopBorder=true', () => { - beforeEach(() => { - createComponent({ withTopBorder: true }); - }); - - it('renders border classes for top section', () => { - expect(findSectionModels().map((x) => x.classes)).toEqual([ - [...TopNavMenuSections.BORDER_CLASSES.split(' '), 'gl-border-gray-50'], - [...TopNavMenuSections.BORDER_CLASSES.split(' '), 'gl-border-gray-50', 'gl-mt-3'], - ]); - }); - }); - - describe('with isPrimarySection=true', () => { - beforeEach(() => { - createComponent({ isPrimarySection: true }); - }); - - it('renders border classes for top section', () => { - expect(findSectionModels().map((x) => x.classes)).toEqual([ - [], - [...TopNavMenuSections.BORDER_CLASSES.split(' '), 'gl-border-gray-100', 'gl-mt-3'], - ]); - }); - }); -}); diff --git a/spec/frontend/nav/components/top_nav_new_dropdown_spec.js b/spec/frontend/nav/components/top_nav_new_dropdown_spec.js deleted file mode 100644 index 432ee5e9ecd..00000000000 --- a/spec/frontend/nav/components/top_nav_new_dropdown_spec.js +++ /dev/null @@ -1,142 +0,0 @@ -import { GlDropdown } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import TopNavNewDropdown from '~/nav/components/top_nav_new_dropdown.vue'; -import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue'; -import { TOP_NAV_INVITE_MEMBERS_COMPONENT } from '~/invite_members/constants'; - -const TEST_VIEW_MODEL = { - title: 'Dropdown', - menu_sections: [ - { - title: 'Section 1', - menu_items: [ - { id: 'foo-1', title: 'Foo 1', href: '/foo/1' }, - { id: 'foo-2', title: 'Foo 2', href: '/foo/2' }, - { id: 'foo-3', title: 'Foo 3', href: '/foo/3' }, - ], - }, - { - title: 'Section 2', - menu_items: [ - { id: 'bar-1', title: 'Bar 1', href: '/bar/1' }, - { id: 'bar-2', title: 'Bar 2', href: '/bar/2' }, - { - id: 'invite', - title: '_invite members title_', - component: TOP_NAV_INVITE_MEMBERS_COMPONENT, - icon: '_icon_', - data: { - trigger_element: '_trigger_element_', - trigger_source: '_trigger_source_', - }, - }, - ], - }, - ], -}; - -describe('~/nav/components/top_nav_menu_sections.vue', () => { - let wrapper; - - const createComponent = (props = {}) => { - wrapper = shallowMount(TopNavNewDropdown, { - propsData: { - viewModel: TEST_VIEW_MODEL, - ...props, - }, - }); - }; - - const findDropdown = () => wrapper.findComponent(GlDropdown); - const findInviteMembersTrigger = () => wrapper.findComponent(InviteMembersTrigger); - const findDropdownContents = () => - findDropdown() - .findAll('[data-testid]') - .wrappers.map((child) => { - const type = child.attributes('data-testid'); - - if (type === 'divider') { - return { type }; - } - if (type === 'header') { - return { type, text: child.text() }; - } - - return { - type, - text: child.text(), - href: child.attributes('href'), - }; - }); - - describe('default', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders dropdown parent', () => { - expect(findDropdown().props()).toMatchObject({ - text: TEST_VIEW_MODEL.title, - textSrOnly: true, - icon: 'plus', - }); - }); - - it('renders dropdown content', () => { - const hrefItems = TEST_VIEW_MODEL.menu_sections[1].menu_items.filter((item) => - Boolean(item.href), - ); - - expect(findDropdownContents()).toEqual([ - { - type: 'header', - text: TEST_VIEW_MODEL.menu_sections[0].title, - }, - ...TEST_VIEW_MODEL.menu_sections[0].menu_items.map(({ title, href }) => ({ - type: 'item', - href, - text: title, - })), - { - type: 'divider', - }, - { - type: 'header', - text: TEST_VIEW_MODEL.menu_sections[1].title, - }, - ...hrefItems.map(({ title, href }) => ({ - type: 'item', - href, - text: title, - })), - ]); - expect(findInviteMembersTrigger().props()).toMatchObject({ - displayText: '_invite members title_', - icon: '_icon_', - triggerElement: 'dropdown-_trigger_element_', - triggerSource: '_trigger_source_', - }); - }); - }); - - describe('with only 1 section', () => { - beforeEach(() => { - createComponent({ - viewModel: { - ...TEST_VIEW_MODEL, - menu_sections: TEST_VIEW_MODEL.menu_sections.slice(0, 1), - }, - }); - }); - - it('renders dropdown content without headers and dividers', () => { - expect(findDropdownContents()).toEqual( - TEST_VIEW_MODEL.menu_sections[0].menu_items.map(({ title, href }) => ({ - type: 'item', - href, - text: title, - })), - ); - }); - }); -}); diff --git a/spec/frontend/nav/mock_data.js b/spec/frontend/nav/mock_data.js deleted file mode 100644 index 2052acfe001..00000000000 --- a/spec/frontend/nav/mock_data.js +++ /dev/null @@ -1,39 +0,0 @@ -import { range } from 'lodash'; - -export const TEST_NAV_DATA = { - menuTitle: 'Test Menu Title', - primary: [ - ...['projects', 'groups'].map((view) => ({ - id: view, - href: null, - title: view, - view, - })), - ...range(0, 2).map((idx) => ({ - id: `primary-link-${idx}`, - href: `/path/to/primary/${idx}`, - title: `Title ${idx}`, - })), - ], - secondary: range(0, 2).map((idx) => ({ - id: `secondary-link-${idx}`, - href: `/path/to/secondary/${idx}`, - title: `SecTitle ${idx}`, - })), - views: { - projects: { - namespace: 'projects', - currentUserName: '', - currentItem: {}, - linksPrimary: [{ id: 'project-link', href: '/path/to/projects', title: 'Project Link' }], - linksSecondary: [], - }, - groups: { - namespace: 'groups', - currentUserName: '', - currentItem: {}, - linksPrimary: [], - linksSecondary: [{ id: 'group-link', href: '/path/to/groups', title: 'Group Link' }], - }, - }, -}; |