Files
gitlabhq/spec/frontend/organizations/shared/components/groups_view_spec.js
2024-02-07 06:06:55 +00:00

324 lines
9.0 KiB
JavaScript

import VueApollo from 'vue-apollo';
import Vue from 'vue';
import { GlEmptyState, GlLoadingIcon, GlKeysetPagination } from '@gitlab/ui';
import GroupsView from '~/organizations/shared/components/groups_view.vue';
import { SORT_DIRECTION_ASC, SORT_ITEM_NAME } from '~/organizations/shared/constants';
import NewGroupButton from '~/organizations/shared/components/new_group_button.vue';
import { formatGroups } from '~/organizations/shared/utils';
import groupsQuery from '~/organizations/shared/graphql/queries/groups.query.graphql';
import GroupsList from '~/vue_shared/components/groups_list/groups_list.vue';
import { createAlert } from '~/alert';
import { DEFAULT_PER_PAGE } from '~/api';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import {
organizationGroups as nodes,
pageInfo,
pageInfoEmpty,
pageInfoOnePage,
} from '~/organizations/mock_data';
jest.mock('~/alert');
Vue.use(VueApollo);
describe('GroupsView', () => {
let wrapper;
let mockApollo;
const defaultProvide = {
groupsEmptyStateSvgPath: 'illustrations/empty-state/empty-groups-md.svg',
newGroupPath: '/groups/new',
organizationGid: 'gid://gitlab/Organizations::Organization/1',
};
const defaultPropsData = {
listItemClass: 'gl-px-5',
search: 'foo',
sortName: SORT_ITEM_NAME.value,
sortDirection: SORT_DIRECTION_ASC,
};
const groups = {
nodes,
pageInfo,
};
const successHandler = jest.fn().mockResolvedValue({
data: {
organization: {
id: defaultProvide.organizationGid,
groups,
},
},
});
const createComponent = ({ handler = successHandler, propsData = {} } = {}) => {
mockApollo = createMockApollo([[groupsQuery, handler]]);
wrapper = shallowMountExtended(GroupsView, {
apolloProvider: mockApollo,
provide: defaultProvide,
propsData: {
...defaultPropsData,
...propsData,
},
});
};
const findPagination = () => wrapper.findComponent(GlKeysetPagination);
const findNewGroupButton = () => wrapper.findComponent(NewGroupButton);
afterEach(() => {
mockApollo = null;
});
describe('when API call is loading', () => {
it('renders loading icon', () => {
createComponent();
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
});
describe('when API call is successful', () => {
describe.each`
shouldShowEmptyStateButtons
${false}
${true}
`(
'when there are no groups and `shouldShowEmptyStateButtons` is `$shouldShowEmptyStateButtons`',
({ shouldShowEmptyStateButtons }) => {
const emptyHandler = jest.fn().mockResolvedValue({
data: {
organization: {
id: defaultProvide.organizationGid,
groups: {
nodes: [],
pageInfo: pageInfoEmpty,
},
},
},
});
it(`renders empty state ${
shouldShowEmptyStateButtons ? 'with' : 'without'
} buttons`, async () => {
createComponent({
handler: emptyHandler,
propsData: { shouldShowEmptyStateButtons },
});
await waitForPromises();
expect(wrapper.findComponent(GlEmptyState).props()).toMatchObject({
title: "You don't have any groups yet.",
description:
'A group is a collection of several projects. If you organize your projects under a group, it works like a folder.',
svgHeight: 144,
svgPath: defaultProvide.groupsEmptyStateSvgPath,
});
expect(findNewGroupButton().exists()).toBe(shouldShowEmptyStateButtons);
});
},
);
describe('when there are groups', () => {
beforeEach(() => {
createComponent({ propsData: {} });
});
it('calls GraphQL query with correct variables', async () => {
await waitForPromises();
expect(successHandler).toHaveBeenCalledWith({
id: defaultProvide.organizationGid,
search: defaultPropsData.search,
sort: 'NAME_ASC',
last: null,
first: DEFAULT_PER_PAGE,
before: null,
after: null,
});
});
it('renders `GroupsList` component and passes correct props', async () => {
await waitForPromises();
expect(wrapper.findComponent(GroupsList).props()).toMatchObject({
groups: formatGroups(nodes),
showGroupIcon: true,
listItemClass: defaultPropsData.listItemClass,
});
});
});
describe('when there is one page of groups', () => {
beforeEach(async () => {
createComponent({
handler: jest.fn().mockResolvedValue({
data: {
organization: {
id: defaultProvide.organizationGid,
groups: {
nodes,
pageInfo: pageInfoOnePage,
},
},
},
}),
});
await waitForPromises();
});
it('does not render pagination', () => {
expect(findPagination().exists()).toBe(false);
});
});
describe('when there is a next page of groups', () => {
const mockEndCursor = 'mockEndCursor';
const handler = jest.fn().mockResolvedValue({
data: {
organization: {
id: defaultProvide.organizationGid,
groups: {
nodes,
pageInfo: {
...pageInfo,
hasNextPage: true,
hasPreviousPage: false,
},
},
},
},
});
beforeEach(async () => {
createComponent({ handler });
await waitForPromises();
});
it('renders pagination', () => {
expect(findPagination().exists()).toBe(true);
});
describe('when next button is clicked', () => {
beforeEach(() => {
findPagination().vm.$emit('next', mockEndCursor);
});
it('emits `page-change` event', () => {
expect(wrapper.emitted('page-change')[0]).toEqual([
{
endCursor: mockEndCursor,
startCursor: null,
},
]);
});
});
describe('when `endCursor` prop is changed', () => {
beforeEach(() => {
wrapper.setProps({ endCursor: mockEndCursor });
});
it('calls query with correct variables', () => {
expect(handler).toHaveBeenCalledWith({
after: mockEndCursor,
before: null,
first: DEFAULT_PER_PAGE,
id: defaultProvide.organizationGid,
last: null,
search: defaultPropsData.search,
sort: 'NAME_ASC',
});
});
});
});
describe('when there is a previous page of groups', () => {
const mockStartCursor = 'mockStartCursor';
const handler = jest.fn().mockResolvedValue({
data: {
organization: {
id: defaultProvide.organizationGid,
groups: {
nodes,
pageInfo: {
...pageInfo,
hasNextPage: false,
hasPreviousPage: true,
},
},
},
},
});
beforeEach(async () => {
createComponent({ handler });
await waitForPromises();
});
it('renders pagination', () => {
expect(findPagination().exists()).toBe(true);
});
describe('when previous button is clicked', () => {
beforeEach(async () => {
findPagination().vm.$emit('prev', mockStartCursor);
await waitForPromises();
});
it('emits `page-change` event', () => {
expect(wrapper.emitted('page-change')[0]).toEqual([
{
endCursor: null,
startCursor: mockStartCursor,
},
]);
});
});
describe('when `startCursor` prop is changed', () => {
beforeEach(async () => {
wrapper.setProps({ startCursor: mockStartCursor });
await waitForPromises();
});
it('calls query with correct variables', () => {
expect(handler).toHaveBeenCalledWith({
after: null,
before: mockStartCursor,
first: null,
id: defaultProvide.organizationGid,
last: DEFAULT_PER_PAGE,
search: defaultPropsData.search,
sort: 'NAME_ASC',
});
});
});
});
});
describe('when API call is not successful', () => {
const error = new Error();
beforeEach(() => {
createComponent({ handler: jest.fn().mockRejectedValue(error) });
});
it('displays error alert', async () => {
await waitForPromises();
expect(createAlert).toHaveBeenCalledWith({
message: GroupsView.i18n.errorMessage,
error,
captureError: true,
});
});
});
});