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>2021-06-09 18:10:05 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-06-09 18:10:05 +0300
commitc0f42c6d662b776777afbf79ba72d8e833b8de48 (patch)
treed94d38bccd5297f59522090fd3c814d9264a1cc9 /spec/frontend/nav
parentf4d6d3ec77286fa64810bd6a25c58671e0deedaf (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/nav')
-rw-r--r--spec/frontend/nav/components/responsive_app_spec.js92
-rw-r--r--spec/frontend/nav/components/responsive_header_spec.js67
-rw-r--r--spec/frontend/nav/components/responsive_home_spec.js137
-rw-r--r--spec/frontend/nav/components/top_nav_container_view_spec.js6
-rw-r--r--spec/frontend/nav/components/top_nav_menu_item_spec.js40
-rw-r--r--spec/frontend/nav/components/top_nav_menu_sections_spec.js8
-rw-r--r--spec/frontend/nav/components/top_nav_new_dropdown_spec.js122
-rw-r--r--spec/frontend/nav/mock_data.js4
8 files changed, 469 insertions, 7 deletions
diff --git a/spec/frontend/nav/components/responsive_app_spec.js b/spec/frontend/nav/components/responsive_app_spec.js
index 1a153b38a05..3274a1b7086 100644
--- a/spec/frontend/nav/components/responsive_app_spec.js
+++ b/spec/frontend/nav/components/responsive_app_spec.js
@@ -1,7 +1,12 @@
import { shallowMount } from '@vue/test-utils';
import { range } from 'lodash';
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 eventHub, { EVENT_RESPONSIVE_TOGGLE } from '~/nav/event_hub';
+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', () => {
@@ -12,11 +17,19 @@ describe('~/nav/components/responsive_app.vue', () => {
propsData: {
navData: TEST_NAV_DATA,
},
+ stubs: {
+ KeepAliveSlots,
+ },
});
};
const triggerResponsiveToggle = () => eventHub.$emit(EVENT_RESPONSIVE_TOGGLE);
+ const findHome = () => wrapper.findComponent(ResponsiveHome);
+ const findMobileOverlay = () => wrapper.find('[data-testid="mobile-overlay"]');
+ const findSubviewHeader = () => wrapper.findComponent(ResponsiveHeader);
+ const findSubviewContainer = () => wrapper.findComponent(TopNavContainerView);
const hasBodyResponsiveOpen = () => document.body.classList.contains('top-nav-responsive-open');
+ const hasMobileOverlayVisible = () => findMobileOverlay().classes('mobile-nav-open');
beforeEach(() => {
// Add test class to reset state + assert that we're adding classes correctly
@@ -32,6 +45,13 @@ describe('~/nav/components/responsive_app.vue', () => {
createComponent();
});
+ it('shows home by default', () => {
+ expect(findHome().isVisible()).toBe(true);
+ expect(findHome().props()).toEqual({
+ navData: resetMenuItemsActive(TEST_NAV_DATA),
+ });
+ });
+
it.each`
times | expectation
${0} | ${false}
@@ -45,6 +65,78 @@ describe('~/nav/components/responsive_app.vue', () => {
expect(hasBodyResponsiveOpen()).toBe(expectation);
},
);
+
+ 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 wrapper.vm.$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,
+ 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,
+ 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 wrapper.vm.$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);
+ });
+ });
});
describe('when destroyed', () => {
diff --git a/spec/frontend/nav/components/responsive_header_spec.js b/spec/frontend/nav/components/responsive_header_spec.js
new file mode 100644
index 00000000000..937c44727c7
--- /dev/null
+++ b/spec/frontend/nav/components/responsive_header_spec.js
@@ -0,0 +1,67 @@
+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(),
+ },
+ });
+ };
+
+ const findMenuItem = () => wrapper.findComponent(TopNavMenuItem);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ 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: 'angle-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: 'angle-left' }]],
+ });
+ });
+ });
+});
diff --git a/spec/frontend/nav/components/responsive_home_spec.js b/spec/frontend/nav/components/responsive_home_spec.js
new file mode 100644
index 00000000000..8f198d92747
--- /dev/null
+++ b/spec/frontend/nav/components/responsive_home_spec.js
@@ -0,0 +1,137 @@
+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(),
+ },
+ listeners: {
+ 'menu-item-click': menuItemClickListener,
+ },
+ });
+ };
+
+ const findSearchMenuItem = () => wrapper.findComponent(TopNavMenuItem);
+ const findNewDropdown = () => wrapper.findComponent(TopNavNewDropdown);
+ const findMenuSections = () => wrapper.findComponent(TopNavMenuSections);
+
+ beforeEach(() => {
+ menuItemClickListener = jest.fn();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ 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_container_view_spec.js b/spec/frontend/nav/components/top_nav_container_view_spec.js
index 45c243191d8..06d2179b859 100644
--- a/spec/frontend/nav/components/top_nav_container_view_spec.js
+++ b/spec/frontend/nav/components/top_nav_container_view_spec.js
@@ -13,6 +13,7 @@ const DEFAULT_PROPS = {
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',
@@ -44,6 +45,7 @@ describe('~/nav/components/top_nav_container_view.vue', () => {
attributes: parent.findComponent(FrequentItemsApp).attributes(),
};
};
+ const findFrequentItemsContainer = () => wrapper.find('[data-testid="frequent-items-container"]');
afterEach(() => {
wrapper.destroy();
@@ -85,6 +87,10 @@ describe('~/nav/components/top_nav_container_view.vue', () => {
});
});
+ 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 },
diff --git a/spec/frontend/nav/components/top_nav_menu_item_spec.js b/spec/frontend/nav/components/top_nav_menu_item_spec.js
index 37b58412389..fd2b4d3b056 100644
--- a/spec/frontend/nav/components/top_nav_menu_item_spec.js
+++ b/spec/frontend/nav/components/top_nav_menu_item_spec.js
@@ -30,7 +30,10 @@ describe('~/nav/components/top_nav_menu_item.vue', () => {
const findButtonIcons = () =>
findButton()
.findAllComponents(GlIcon)
- .wrappers.map((x) => x.props('name'));
+ .wrappers.map((x) => ({
+ name: x.props('name'),
+ classes: x.classes(),
+ }));
beforeEach(() => {
listener = jest.fn();
@@ -65,11 +68,42 @@ describe('~/nav/components/top_nav_menu_item.vue', () => {
expect(listener).toHaveBeenCalledWith('TEST');
});
+
+ it('renders expected icons', () => {
+ expect(findButtonIcons()).toEqual([
+ {
+ name: TEST_MENU_ITEM.icon,
+ classes: ['gl-mr-2!'],
+ },
+ {
+ 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
- ${'default'} | ${TEST_MENU_ITEM} | ${[TEST_MENU_ITEM.icon, 'chevron-right']}
${'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 }} | ${[]}
@@ -79,7 +113,7 @@ describe('~/nav/components/top_nav_menu_item.vue', () => {
});
it(`renders expected icons ${JSON.stringify(expectedIcons)}`, () => {
- expect(findButtonIcons()).toEqual(expectedIcons);
+ expect(findButtonIcons().map((x) => x.name)).toEqual(expectedIcons);
});
});
diff --git a/spec/frontend/nav/components/top_nav_menu_sections_spec.js b/spec/frontend/nav/components/top_nav_menu_sections_spec.js
index a6739507915..d56542fe572 100644
--- a/spec/frontend/nav/components/top_nav_menu_sections_spec.js
+++ b/spec/frontend/nav/components/top_nav_menu_sections_spec.js
@@ -51,11 +51,11 @@ describe('~/nav/components/top_nav_menu_sections.vue', () => {
menuItems: [
{
menuItem: TEST_SECTIONS[0].menuItems[0],
- classes: [],
+ classes: ['gl-w-full'],
},
...TEST_SECTIONS[0].menuItems.slice(1).map((menuItem) => ({
menuItem,
- classes: ['gl-mt-1'],
+ classes: ['gl-w-full', 'gl-mt-1'],
})),
],
},
@@ -64,11 +64,11 @@ describe('~/nav/components/top_nav_menu_sections.vue', () => {
menuItems: [
{
menuItem: TEST_SECTIONS[1].menuItems[0],
- classes: [],
+ classes: ['gl-w-full'],
},
...TEST_SECTIONS[1].menuItems.slice(1).map((menuItem) => ({
menuItem,
- classes: ['gl-mt-1'],
+ classes: ['gl-w-full', 'gl-mt-1'],
})),
],
},
diff --git a/spec/frontend/nav/components/top_nav_new_dropdown_spec.js b/spec/frontend/nav/components/top_nav_new_dropdown_spec.js
new file mode 100644
index 00000000000..18210658b89
--- /dev/null
+++ b/spec/frontend/nav/components/top_nav_new_dropdown_spec.js
@@ -0,0 +1,122 @@
+import { GlDropdown } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import TopNavNewDropdown from '~/nav/components/top_nav_new_dropdown.vue';
+
+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' },
+ ],
+ },
+ ],
+};
+
+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 findDropdownContents = () =>
+ findDropdown()
+ .findAll('[data-testid]')
+ .wrappers.map((child) => {
+ const type = child.attributes('data-testid');
+
+ if (type === 'divider') {
+ return { type };
+ } else if (type === 'header') {
+ return { type, text: child.text() };
+ }
+
+ return {
+ type,
+ text: child.text(),
+ href: child.attributes('href'),
+ };
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ 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', () => {
+ 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,
+ },
+ ...TEST_VIEW_MODEL.menu_sections[1].menu_items.map(({ title, href }) => ({
+ type: 'item',
+ href,
+ text: title,
+ })),
+ ]);
+ });
+ });
+
+ 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
index 2987d8deb16..c2ad86a4605 100644
--- a/spec/frontend/nav/mock_data.js
+++ b/spec/frontend/nav/mock_data.js
@@ -25,11 +25,15 @@ export const TEST_NAV_DATA = {
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' }],
},
},
};