spec/frontend/repository/components/header_area_spec.js (330 lines of code) (raw):
import { RouterLinkStub } from '@vue/test-utils';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import RefSelector from '~/ref/components/ref_selector.vue';
import HeaderArea from '~/repository/components/header_area.vue';
import Breadcrumbs from '~/repository/components/header_area/breadcrumbs.vue';
import CodeDropdown from '~/vue_shared/components/code_dropdown/code_dropdown.vue';
import SourceCodeDownloadDropdown from '~/vue_shared/components/download_dropdown/download_dropdown.vue';
import AddToTree from '~/repository/components/header_area/add_to_tree.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import CloneCodeDropdown from '~/vue_shared/components/code_dropdown/clone_code_dropdown.vue';
import RepositoryOverflowMenu from '~/repository/components/header_area/repository_overflow_menu.vue';
import BlobControls from '~/repository/components/header_area/blob_controls.vue';
import Shortcuts from '~/behaviors/shortcuts/shortcuts';
import { useMockInternalEventsTracking } from 'helpers/tracking_internal_events_helper';
import { headerAppInjected } from 'ee_else_ce_jest/repository/mock_data';
import CompactCodeDropdown from 'ee_else_ce/repository/components/code_dropdown/compact_code_dropdown.vue';
const defaultMockRoute = {
params: {
path: 'index.js',
},
meta: {
refType: '',
},
query: {
ref_type: '',
},
};
describe('HeaderArea', () => {
let wrapper;
const findBreadcrumbs = () => wrapper.findComponent(Breadcrumbs);
const findRefSelector = () => wrapper.findComponent(RefSelector);
const findFindFileButton = () => wrapper.findByTestId('tree-find-file-control');
const findWebIdeButton = () => wrapper.findByTestId('js-tree-web-ide-link');
const findCodeDropdown = () => wrapper.findComponent(CodeDropdown);
const findSourceCodeDownloadDropdown = () => wrapper.findComponent(SourceCodeDownloadDropdown);
const findCloneCodeDropdown = () => wrapper.findComponent(CloneCodeDropdown);
const findCompactCodeDropdown = () => wrapper.findComponent(CompactCodeDropdown);
const findAddToTreeDropdown = () => wrapper.findComponent(AddToTree);
const findPageHeading = () => wrapper.findByTestId('repository-heading');
const findFileIcon = () => wrapper.findComponent(FileIcon);
const findRepositoryOverflowMenu = () => wrapper.findComponent(RepositoryOverflowMenu);
const findBlobControls = () => wrapper.findComponent(BlobControls);
const findTreeControls = () => wrapper.findByTestId('tree-controls-container');
const { bindInternalEventDocument } = useMockInternalEventsTracking();
const createComponent = ({
props = {},
route = { name: 'blobPathDecoded' },
provided = {},
} = {}) => {
return shallowMountExtended(HeaderArea, {
provide: {
...headerAppInjected,
...provided,
},
propsData: {
projectPath: 'test/project',
historyLink: '/history',
refType: 'branch',
projectId: '123',
currentRef: 'main',
...props,
},
stubs: {
RouterLink: RouterLinkStub,
CompactCodeDropdown,
},
mocks: {
$route: {
...defaultMockRoute,
...route,
params: {
...defaultMockRoute.params,
...(route.params || {}),
},
},
},
});
};
beforeEach(() => {
wrapper = createComponent();
});
it('renders the component', () => {
expect(wrapper.exists()).toBe(true);
});
it('renders RefSelector', () => {
expect(findRefSelector().exists()).toBe(true);
});
it('renders Breadcrumbs component', () => {
expect(findBreadcrumbs().exists()).toBe(true);
});
it('renders PageHeading component', () => {
expect(findPageHeading().exists()).toBe(true);
});
describe('showTreeControls', () => {
it('should not render tree controls for blob view', () => {
wrapper = createComponent({}, { name: 'blobPathDecoded' });
expect(findTreeControls().exists()).toBe(false);
});
});
describe('when rendered for tree view', () => {
beforeEach(() => {
wrapper = createComponent({
route: { name: 'treePathDecoded', params: { path: 'project' } },
});
});
describe('PageHeading', () => {
it('displays correct directory name', () => {
expect(findPageHeading().text()).toContain('project');
expect(findFileIcon().props('fileName')).toBe('folder-open');
expect(findFileIcon().props('folder')).toBe(true);
expect(findFileIcon().classes('gl-text-subtle')).toBe(true);
});
});
describe('Find file button', () => {
it('renders Find file button', () => {
expect(findFindFileButton().exists()).toBe(true);
});
it('triggers a `focusSearchFile` shortcut when the findFile button is clicked', () => {
jest.spyOn(Shortcuts, 'focusSearchFile').mockResolvedValue();
findFindFileButton().vm.$emit('click');
expect(Shortcuts.focusSearchFile).toHaveBeenCalled();
});
it('emits a tracking event when the Find file button is clicked', () => {
const { trackEventSpy } = bindInternalEventDocument(wrapper.element);
jest.spyOn(Shortcuts, 'focusSearchFile').mockResolvedValue();
findFindFileButton().vm.$emit('click');
expect(trackEventSpy).toHaveBeenCalledWith('click_find_file_button_on_repository_pages');
});
});
describe('Edit button', () => {
it('renders WebIdeLink component', () => {
expect(findWebIdeButton().exists()).toBe(true);
});
});
describe('when `directory_code_dropdown_updates` flag is `false`', () => {
describe('CodeDropdown', () => {
it('renders CodeDropdown component with correct props for desktop layout', () => {
expect(findCodeDropdown().exists()).toBe(true);
expect(findCodeDropdown().props('sshUrl')).toBe(headerAppInjected.sshUrl);
expect(findCodeDropdown().props('httpUrl')).toBe(headerAppInjected.httpUrl);
});
describe('SourceCodeDownloadDropdown', () => {
it('renders SourceCodeDownloadDropdown and CloneCodeDropdown component with correct props for mobile layout', () => {
expect(findSourceCodeDownloadDropdown().exists()).toBe(true);
expect(findSourceCodeDownloadDropdown().props('downloadLinks')).toEqual(
headerAppInjected.downloadLinks,
);
expect(findSourceCodeDownloadDropdown().props('downloadArtifacts')).toEqual(
headerAppInjected.downloadArtifacts,
);
expect(findCloneCodeDropdown().exists()).toBe(true);
expect(findCloneCodeDropdown().props('sshUrl')).toBe(headerAppInjected.sshUrl);
expect(findCloneCodeDropdown().props('httpUrl')).toBe(headerAppInjected.httpUrl);
});
});
describe('Add to tree dropdown', () => {
it('does not render AddToTree component', () => {
expect(findAddToTreeDropdown().exists()).toBe(false);
});
});
});
});
});
describe('when rendered for tree view and directory_code_dropdown_updates flag is true', () => {
beforeEach(() => {
wrapper = createComponent({
route: { name: 'treePathDecoded' },
provided: {
glFeatures: {
directoryCodeDropdownUpdates: true,
},
newWorkspacePath: '/workspaces/new',
},
});
});
describe('Add to tree dropdown', () => {
it('renders AddToTree component', () => {
expect(findAddToTreeDropdown().exists()).toBe(true);
});
});
it('renders CompactCodeDropdown with correct props', () => {
expect(findCompactCodeDropdown().exists()).toBe(true);
expect(findCompactCodeDropdown().props()).toMatchObject({
sshUrl: headerAppInjected.sshUrl,
httpUrl: headerAppInjected.httpUrl,
kerberosUrl: headerAppInjected.kerberosUrl,
xcodeUrl: headerAppInjected.xcodeUrl,
webIdeUrl: headerAppInjected.webIdeUrl,
gitpodUrl: headerAppInjected.gitpodUrl,
showWebIdeButton: headerAppInjected.showWebIdeButton,
isGitpodEnabledForInstance: headerAppInjected.isGitpodEnabledForInstance,
isGitpodEnabledForUser: headerAppInjected.isGitpodEnabledForUser,
currentPath: defaultMockRoute.params.path,
directoryDownloadLinks: headerAppInjected.downloadLinks,
});
});
describe('RepositoryOverflowMenu', () => {
it('renders RepositoryOverflowMenu component with correct props when on default branch', () => {
wrapper = createComponent({
route: { name: 'treePathDecoded' },
});
expect(findRepositoryOverflowMenu().props()).toStrictEqual({
currentRef: 'main',
fullPath: 'test/project',
path: 'index.js',
});
});
it('renders RepositoryOverflowMenu component with correct props when on non-default branch', () => {
wrapper = createComponent({
route: { name: 'treePathDecoded' },
provided: { comparePath: 'test/project/compare' },
});
expect(findRepositoryOverflowMenu().props()).toStrictEqual({
currentRef: 'main',
fullPath: 'test/project',
path: 'index.js',
});
});
});
});
describe('when rendered for blob view', () => {
describe('showBlobControls', () => {
it('should not render blob controls when filePath does not exist', () => {
wrapper = createComponent({ route: { name: 'blobPathDecoded', params: { path: null } } });
expect(findBlobControls().exists()).toBe(false);
});
it('should not render blob controls when route name is not blobPathDecoded', () => {
wrapper = createComponent({
route: { name: 'blobPath', params: { path: '/some/file.js' } },
});
expect(findBlobControls().exists()).toBe(false);
});
});
it('renders BlobControls component with correct props', () => {
wrapper = createComponent({ props: { refType: 'branch' } });
expect(findBlobControls().exists()).toBe(true);
expect(findBlobControls().props('projectPath')).toBe('test/project');
expect(findBlobControls().props('refType')).toBe('');
});
it('does not render CodeDropdown and SourceCodeDownloadDropdown', () => {
expect(findCodeDropdown().exists()).toBe(false);
expect(findSourceCodeDownloadDropdown().exists()).toBe(false);
});
it('does not render AddToTree component', () => {
expect(findAddToTreeDropdown().exists()).toBe(false);
});
it('displays correct file name and icon', () => {
expect(findPageHeading().text()).toContain('index.js');
expect(findFileIcon().props('fileName')).toBe('index.js');
expect(findFileIcon().props('folder')).toBe(false);
expect(findFileIcon().classes('gl-text-subtle')).toBe(false);
});
});
describe('when rendered for readme project overview', () => {
describe('when directory_code_dropdown_updates flag is false', () => {
beforeEach(() => {
wrapper = createComponent({
route: { name: 'treePathDecoded' },
provided: { isReadmeView: true },
});
});
it('does not render directory name and icon', () => {
expect(findPageHeading().exists()).toBe(false);
expect(findFileIcon().exists()).toBe(false);
});
it('does not render RefSelector or Breadcrumbs', () => {
expect(findRefSelector().exists()).toBe(false);
expect(findBreadcrumbs().exists()).toBe(false);
});
it('does not render AddToTree component', () => {
expect(findAddToTreeDropdown().exists()).toBe(false);
});
it('does not render CodeDropdown and SourceCodeDownloadDropdown', () => {
expect(findCodeDropdown().exists()).toBe(false);
expect(findSourceCodeDownloadDropdown().exists()).toBe(false);
});
it('does not render CompactCodeDropdown', () => {
expect(findCompactCodeDropdown().exists()).toBe(false);
});
});
describe('when directory_code_dropdown_updates flag is true', () => {
beforeEach(() => {
wrapper = createComponent({
route: { name: 'treePathDecoded' },
provided: {
glFeatures: {
directoryCodeDropdownUpdates: true,
},
newWorkspacePath: '/workspaces/new',
isReadmeView: true,
},
});
});
it('does render CompactCodeDropdown', () => {
expect(findCompactCodeDropdown().exists()).toBe(true);
});
it('does not render directory name and icon', () => {
expect(findPageHeading().exists()).toBe(false);
expect(findFileIcon().exists()).toBe(false);
});
it('does not render RefSelector or Breadcrumbs', () => {
expect(findRefSelector().exists()).toBe(false);
expect(findBreadcrumbs().exists()).toBe(false);
});
it('does not render AddToTree component', () => {
expect(findAddToTreeDropdown().exists()).toBe(false);
});
it('does not render CodeDropdown and SourceCodeDownloadDropdown', () => {
expect(findCodeDropdown().exists()).toBe(false);
expect(findSourceCodeDownloadDropdown().exists()).toBe(false);
});
});
});
describe('when rendered for full project overview', () => {
beforeEach(() => {
wrapper = createComponent({ route: { name: 'projectRoot' } });
});
it('does not render directory name and icon', () => {
expect(findPageHeading().exists()).toBe(false);
expect(findFileIcon().exists()).toBe(false);
});
it('renders refSelector, breadcrumbs and tree controls with correct layout', () => {
expect(wrapper.find('section').classes()).toEqual([
'gl-items-center',
'gl-justify-between',
'sm:gl-flex',
]);
});
});
});