248 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			248 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
import { nextTick } from 'vue';
 | 
						|
import { Mousetrap } from '~/lib/mousetrap';
 | 
						|
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
 | 
						|
import SuperSidebar from '~/super_sidebar/components/super_sidebar.vue';
 | 
						|
import HelpCenter from '~/super_sidebar/components/help_center.vue';
 | 
						|
import UserBar from '~/super_sidebar/components/user_bar.vue';
 | 
						|
import SidebarPeekBehavior, {
 | 
						|
  STATE_CLOSED,
 | 
						|
  STATE_WILL_OPEN,
 | 
						|
  STATE_OPEN,
 | 
						|
  STATE_WILL_CLOSE,
 | 
						|
} from '~/super_sidebar/components/sidebar_peek_behavior.vue';
 | 
						|
import SidebarPortalTarget from '~/super_sidebar/components/sidebar_portal_target.vue';
 | 
						|
import ContextSwitcher from '~/super_sidebar/components/context_switcher.vue';
 | 
						|
import SidebarMenu from '~/super_sidebar/components/sidebar_menu.vue';
 | 
						|
import { sidebarState } from '~/super_sidebar/constants';
 | 
						|
import {
 | 
						|
  toggleSuperSidebarCollapsed,
 | 
						|
  isCollapsed,
 | 
						|
} from '~/super_sidebar/super_sidebar_collapsed_state_manager';
 | 
						|
import { stubComponent } from 'helpers/stub_component';
 | 
						|
import { sidebarData as mockSidebarData } from '../mock_data';
 | 
						|
 | 
						|
const initialSidebarState = { ...sidebarState };
 | 
						|
 | 
						|
jest.mock('~/super_sidebar/super_sidebar_collapsed_state_manager');
 | 
						|
const closeContextSwitcherMock = jest.fn();
 | 
						|
 | 
						|
const trialStatusWidgetStubTestId = 'trial-status-widget';
 | 
						|
const TrialStatusWidgetStub = { template: `<div data-testid="${trialStatusWidgetStubTestId}" />` };
 | 
						|
const trialStatusPopoverStubTestId = 'trial-status-popover';
 | 
						|
const TrialStatusPopoverStub = {
 | 
						|
  template: `<div data-testid="${trialStatusPopoverStubTestId}" />`,
 | 
						|
};
 | 
						|
 | 
						|
const peekClass = 'super-sidebar-peek';
 | 
						|
const peekHintClass = 'super-sidebar-peek-hint';
 | 
						|
 | 
						|
describe('SuperSidebar component', () => {
 | 
						|
  let wrapper;
 | 
						|
 | 
						|
  const findSidebar = () => wrapper.findByTestId('super-sidebar');
 | 
						|
  const findUserBar = () => wrapper.findComponent(UserBar);
 | 
						|
  const findContextSwitcher = () => wrapper.findComponent(ContextSwitcher);
 | 
						|
  const findNavContainer = () => wrapper.findByTestId('nav-container');
 | 
						|
  const findHelpCenter = () => wrapper.findComponent(HelpCenter);
 | 
						|
  const findSidebarPortalTarget = () => wrapper.findComponent(SidebarPortalTarget);
 | 
						|
  const findPeekBehavior = () => wrapper.findComponent(SidebarPeekBehavior);
 | 
						|
  const findTrialStatusWidget = () => wrapper.findByTestId(trialStatusWidgetStubTestId);
 | 
						|
  const findTrialStatusPopover = () => wrapper.findByTestId(trialStatusPopoverStubTestId);
 | 
						|
  const findSidebarMenu = () => wrapper.findComponent(SidebarMenu);
 | 
						|
 | 
						|
  const createWrapper = ({
 | 
						|
    provide = {},
 | 
						|
    sidebarData = mockSidebarData,
 | 
						|
    sidebarState: state = {},
 | 
						|
  } = {}) => {
 | 
						|
    Object.assign(sidebarState, state);
 | 
						|
 | 
						|
    wrapper = shallowMountExtended(SuperSidebar, {
 | 
						|
      provide: {
 | 
						|
        showTrialStatusWidget: false,
 | 
						|
        ...provide,
 | 
						|
      },
 | 
						|
      propsData: {
 | 
						|
        sidebarData,
 | 
						|
      },
 | 
						|
      stubs: {
 | 
						|
        ContextSwitcher: stubComponent(ContextSwitcher, {
 | 
						|
          methods: { close: closeContextSwitcherMock },
 | 
						|
        }),
 | 
						|
        TrialStatusWidget: TrialStatusWidgetStub,
 | 
						|
        TrialStatusPopover: TrialStatusPopoverStub,
 | 
						|
      },
 | 
						|
    });
 | 
						|
  };
 | 
						|
 | 
						|
  beforeEach(() => {
 | 
						|
    Object.assign(sidebarState, initialSidebarState);
 | 
						|
  });
 | 
						|
 | 
						|
  describe('default', () => {
 | 
						|
    it('adds inert attribute when collapsed', () => {
 | 
						|
      createWrapper({ sidebarState: { isCollapsed: true } });
 | 
						|
      expect(findSidebar().attributes('inert')).toBe('inert');
 | 
						|
    });
 | 
						|
 | 
						|
    it('does not add inert attribute when expanded', () => {
 | 
						|
      createWrapper();
 | 
						|
      expect(findSidebar().attributes('inert')).toBe(undefined);
 | 
						|
    });
 | 
						|
 | 
						|
    it('renders UserBar with sidebarData', () => {
 | 
						|
      createWrapper();
 | 
						|
      expect(findUserBar().props('sidebarData')).toBe(mockSidebarData);
 | 
						|
    });
 | 
						|
 | 
						|
    it('renders HelpCenter with sidebarData', () => {
 | 
						|
      createWrapper();
 | 
						|
      expect(findHelpCenter().props('sidebarData')).toBe(mockSidebarData);
 | 
						|
    });
 | 
						|
 | 
						|
    it('does not render SidebarMenu when items are empty', () => {
 | 
						|
      createWrapper();
 | 
						|
      expect(findSidebarMenu().exists()).toBe(false);
 | 
						|
    });
 | 
						|
 | 
						|
    it('renders SidebarMenu with menu items', () => {
 | 
						|
      const menuItems = [
 | 
						|
        { id: 1, title: 'Menu item 1' },
 | 
						|
        { id: 2, title: 'Menu item 2' },
 | 
						|
      ];
 | 
						|
      createWrapper({ sidebarData: { ...mockSidebarData, current_menu_items: menuItems } });
 | 
						|
      expect(findSidebarMenu().props('items')).toBe(menuItems);
 | 
						|
    });
 | 
						|
 | 
						|
    it('renders SidebarPortalTarget', () => {
 | 
						|
      createWrapper();
 | 
						|
      expect(findSidebarPortalTarget().exists()).toBe(true);
 | 
						|
    });
 | 
						|
 | 
						|
    it("does not call the context switcher's close method initially", () => {
 | 
						|
      createWrapper();
 | 
						|
 | 
						|
      expect(closeContextSwitcherMock).not.toHaveBeenCalled();
 | 
						|
    });
 | 
						|
 | 
						|
    it('renders hidden shortcut links', () => {
 | 
						|
      createWrapper();
 | 
						|
      const [linkAttrs] = mockSidebarData.shortcut_links;
 | 
						|
      const link = wrapper.find(`.${linkAttrs.css_class}`);
 | 
						|
 | 
						|
      expect(link.exists()).toBe(true);
 | 
						|
      expect(link.attributes('href')).toBe(linkAttrs.href);
 | 
						|
      expect(link.attributes('class')).toContain('gl-display-none');
 | 
						|
    });
 | 
						|
 | 
						|
    it('sets up the sidebar toggle shortcut', () => {
 | 
						|
      createWrapper();
 | 
						|
 | 
						|
      isCollapsed.mockReturnValue(false);
 | 
						|
      Mousetrap.trigger('mod+\\');
 | 
						|
 | 
						|
      expect(toggleSuperSidebarCollapsed).toHaveBeenCalledTimes(1);
 | 
						|
      expect(toggleSuperSidebarCollapsed).toHaveBeenCalledWith(true, true);
 | 
						|
 | 
						|
      isCollapsed.mockReturnValue(true);
 | 
						|
      Mousetrap.trigger('mod+\\');
 | 
						|
 | 
						|
      expect(toggleSuperSidebarCollapsed).toHaveBeenCalledTimes(2);
 | 
						|
      expect(toggleSuperSidebarCollapsed).toHaveBeenCalledWith(false, true);
 | 
						|
 | 
						|
      jest.spyOn(Mousetrap, 'unbind');
 | 
						|
 | 
						|
      wrapper.destroy();
 | 
						|
 | 
						|
      expect(Mousetrap.unbind).toHaveBeenCalledWith(['mod+\\']);
 | 
						|
    });
 | 
						|
 | 
						|
    it('does not render trial status widget', () => {
 | 
						|
      createWrapper();
 | 
						|
 | 
						|
      expect(findTrialStatusWidget().exists()).toBe(false);
 | 
						|
      expect(findTrialStatusPopover().exists()).toBe(false);
 | 
						|
    });
 | 
						|
 | 
						|
    it('does not have peek behavior', () => {
 | 
						|
      createWrapper();
 | 
						|
 | 
						|
      expect(findPeekBehavior().exists()).toBe(false);
 | 
						|
    });
 | 
						|
  });
 | 
						|
 | 
						|
  describe('on collapse', () => {
 | 
						|
    beforeEach(() => {
 | 
						|
      createWrapper();
 | 
						|
      sidebarState.isCollapsed = true;
 | 
						|
    });
 | 
						|
 | 
						|
    it('closes the context switcher', () => {
 | 
						|
      expect(closeContextSwitcherMock).toHaveBeenCalled();
 | 
						|
    });
 | 
						|
  });
 | 
						|
 | 
						|
  describe('peek behavior', () => {
 | 
						|
    it(`initially makes sidebar inert and peekable (${STATE_CLOSED})`, () => {
 | 
						|
      createWrapper({ sidebarState: { isCollapsed: true, isPeekable: true } });
 | 
						|
 | 
						|
      expect(findSidebar().attributes('inert')).toBe('inert');
 | 
						|
      expect(findSidebar().classes()).not.toContain(peekHintClass);
 | 
						|
      expect(findSidebar().classes()).not.toContain(peekClass);
 | 
						|
    });
 | 
						|
 | 
						|
    it(`makes sidebar inert and shows peek hint when peek state is ${STATE_WILL_OPEN}`, async () => {
 | 
						|
      createWrapper({ sidebarState: { isCollapsed: true, isPeekable: true } });
 | 
						|
 | 
						|
      findPeekBehavior().vm.$emit('change', STATE_WILL_OPEN);
 | 
						|
      await nextTick();
 | 
						|
 | 
						|
      expect(findSidebar().attributes('inert')).toBe('inert');
 | 
						|
      expect(findSidebar().classes()).toContain(peekHintClass);
 | 
						|
      expect(findSidebar().classes()).not.toContain(peekClass);
 | 
						|
    });
 | 
						|
 | 
						|
    it.each([STATE_OPEN, STATE_WILL_CLOSE])(
 | 
						|
      'makes sidebar interactive and visible when peek state is %s',
 | 
						|
      async (state) => {
 | 
						|
        createWrapper({ sidebarState: { isCollapsed: true, isPeekable: true } });
 | 
						|
 | 
						|
        findPeekBehavior().vm.$emit('change', state);
 | 
						|
        await nextTick();
 | 
						|
 | 
						|
        expect(findSidebar().attributes('inert')).toBe(undefined);
 | 
						|
        expect(findSidebar().classes()).toContain(peekClass);
 | 
						|
        expect(findSidebar().classes()).not.toContain(peekHintClass);
 | 
						|
      },
 | 
						|
    );
 | 
						|
  });
 | 
						|
 | 
						|
  describe('nav container', () => {
 | 
						|
    beforeEach(() => {
 | 
						|
      createWrapper();
 | 
						|
    });
 | 
						|
 | 
						|
    it('allows overflow while the context switcher is closed', () => {
 | 
						|
      expect(findNavContainer().classes()).toContain('gl-overflow-auto');
 | 
						|
    });
 | 
						|
 | 
						|
    it('hides overflow when context switcher is opened', async () => {
 | 
						|
      findContextSwitcher().vm.$emit('toggle', true);
 | 
						|
      await nextTick();
 | 
						|
 | 
						|
      expect(findNavContainer().classes()).not.toContain('gl-overflow-auto');
 | 
						|
    });
 | 
						|
  });
 | 
						|
 | 
						|
  describe('when a trial is active', () => {
 | 
						|
    beforeEach(() => {
 | 
						|
      createWrapper({ provide: { showTrialStatusWidget: true } });
 | 
						|
    });
 | 
						|
 | 
						|
    it('renders trial status widget', () => {
 | 
						|
      expect(findTrialStatusWidget().exists()).toBe(true);
 | 
						|
      expect(findTrialStatusPopover().exists()).toBe(true);
 | 
						|
    });
 | 
						|
  });
 | 
						|
});
 |