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:
Diffstat (limited to 'spec/frontend/user_lists/components')
-rw-r--r--spec/frontend/user_lists/components/user_lists_spec.js195
-rw-r--r--spec/frontend/user_lists/components/user_lists_table_spec.js98
2 files changed, 293 insertions, 0 deletions
diff --git a/spec/frontend/user_lists/components/user_lists_spec.js b/spec/frontend/user_lists/components/user_lists_spec.js
new file mode 100644
index 00000000000..7a33c6faac9
--- /dev/null
+++ b/spec/frontend/user_lists/components/user_lists_spec.js
@@ -0,0 +1,195 @@
+import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
+import { within } from '@testing-library/dom';
+import { mount, createWrapper } from '@vue/test-utils';
+import Vue from 'vue';
+import Vuex from 'vuex';
+import waitForPromises from 'helpers/wait_for_promises';
+import Api from '~/api';
+import UserListsComponent from '~/user_lists/components/user_lists.vue';
+import UserListsTable from '~/user_lists/components/user_lists_table.vue';
+import createStore from '~/user_lists/store/index';
+import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
+import { userList } from '../../feature_flags/mock_data';
+
+jest.mock('~/api');
+
+Vue.use(Vuex);
+
+describe('~/user_lists/components/user_lists.vue', () => {
+ const mockProvide = {
+ newUserListPath: '/user-lists/new',
+ featureFlagsHelpPagePath: '/help/feature-flags',
+ errorStateSvgPath: '/assets/illustrations/feature_flag.svg',
+ };
+
+ const mockState = {
+ projectId: '1',
+ };
+
+ let wrapper;
+ let store;
+
+ const factory = (provide = mockProvide, fn = mount) => {
+ store = createStore(mockState);
+ wrapper = fn(UserListsComponent, {
+ store,
+ provide,
+ });
+ };
+
+ const newButton = () => within(wrapper.element).queryAllByText('New user list');
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('without permissions', () => {
+ const provideData = {
+ ...mockProvide,
+ newUserListPath: null,
+ };
+
+ beforeEach(() => {
+ Api.fetchFeatureFlagUserLists.mockResolvedValue({ data: [], headers: {} });
+ factory(provideData);
+ });
+
+ it('does not render new user list button', () => {
+ expect(newButton()).toHaveLength(0);
+ });
+ });
+
+ describe('loading state', () => {
+ it('renders a loading icon', () => {
+ Api.fetchFeatureFlagUserLists.mockReturnValue(new Promise(() => {}));
+
+ factory();
+
+ const loadingElement = wrapper.findComponent(GlLoadingIcon);
+
+ expect(loadingElement.exists()).toBe(true);
+ expect(loadingElement.props('label')).toEqual('Loading user lists');
+ });
+ });
+
+ describe('successful request', () => {
+ describe('without user lists', () => {
+ let emptyState;
+
+ beforeEach(async () => {
+ Api.fetchFeatureFlagUserLists.mockResolvedValue({ data: [], headers: {} });
+
+ factory();
+ await waitForPromises();
+ await Vue.nextTick();
+
+ emptyState = wrapper.findComponent(GlEmptyState);
+ });
+
+ it('should render the empty state', async () => {
+ expect(emptyState.exists()).toBe(true);
+ });
+
+ it('renders new feature flag button', () => {
+ expect(newButton()).not.toHaveLength(0);
+ });
+
+ it('renders generic title', () => {
+ const title = createWrapper(
+ within(emptyState.element).getByText('Get started with user lists'),
+ );
+ expect(title.exists()).toBe(true);
+ });
+
+ it('renders generic description', () => {
+ const description = createWrapper(
+ within(emptyState.element).getByText(
+ 'User lists allow you to define a set of users to use with Feature Flags.',
+ ),
+ );
+ expect(description.exists()).toBe(true);
+ });
+ });
+
+ describe('with paginated user lists', () => {
+ let table;
+
+ beforeEach(async () => {
+ Api.fetchFeatureFlagUserLists.mockResolvedValue({
+ data: [userList],
+ headers: {
+ 'x-next-page': '2',
+ 'x-page': '1',
+ 'X-Per-Page': '2',
+ 'X-Prev-Page': '',
+ 'X-TOTAL': '37',
+ 'X-Total-Pages': '5',
+ },
+ });
+
+ factory();
+ jest.spyOn(store, 'dispatch');
+ await Vue.nextTick();
+ table = wrapper.findComponent(UserListsTable);
+ });
+
+ it('should render a table with feature flags', () => {
+ expect(table.exists()).toBe(true);
+ expect(table.props('userLists')).toEqual([userList]);
+ });
+
+ it('renders new feature flag button', () => {
+ expect(newButton()).not.toHaveLength(0);
+ });
+
+ describe('pagination', () => {
+ let pagination;
+
+ beforeEach(() => {
+ pagination = wrapper.findComponent(TablePagination);
+ });
+
+ it('should render pagination', () => {
+ expect(pagination.exists()).toBe(true);
+ });
+
+ it('should make an API request when page is clicked', () => {
+ jest.spyOn(store, 'dispatch');
+ pagination.vm.change('4');
+
+ expect(store.dispatch).toHaveBeenCalledWith('setUserListsOptions', {
+ page: '4',
+ });
+ });
+ });
+ });
+ });
+
+ describe('unsuccessful request', () => {
+ beforeEach(async () => {
+ Api.fetchFeatureFlagUserLists.mockRejectedValue();
+ factory();
+
+ await Vue.nextTick();
+ });
+
+ it('should render error state', () => {
+ const emptyState = wrapper.findComponent(GlEmptyState);
+ const title = createWrapper(
+ within(emptyState.element).getByText('There was an error fetching the user lists.'),
+ );
+ expect(title.exists()).toBe(true);
+ const description = createWrapper(
+ within(emptyState.element).getByText(
+ 'Try again in a few moments or contact your support team.',
+ ),
+ );
+ expect(description.exists()).toBe(true);
+ });
+
+ it('renders new feature flag button', () => {
+ expect(newButton()).not.toHaveLength(0);
+ });
+ });
+});
diff --git a/spec/frontend/user_lists/components/user_lists_table_spec.js b/spec/frontend/user_lists/components/user_lists_table_spec.js
new file mode 100644
index 00000000000..7f4d510a39c
--- /dev/null
+++ b/spec/frontend/user_lists/components/user_lists_table_spec.js
@@ -0,0 +1,98 @@
+import { GlModal } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import * as timeago from 'timeago.js';
+import UserListsTable from '~/user_lists/components/user_lists_table.vue';
+import { userList } from '../../feature_flags/mock_data';
+
+jest.mock('timeago.js', () => ({
+ format: jest.fn().mockReturnValue('2 weeks ago'),
+ register: jest.fn(),
+}));
+
+describe('User Lists Table', () => {
+ let wrapper;
+ let userLists;
+
+ beforeEach(() => {
+ userLists = new Array(5).fill(userList).map((x, i) => ({ ...x, id: i }));
+ wrapper = mount(UserListsTable, {
+ propsData: { userLists },
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('should display the details of a user list', () => {
+ expect(wrapper.find('[data-testid="ffUserListName"]').text()).toBe(userList.name);
+ expect(wrapper.find('[data-testid="ffUserListIds"]').text()).toBe(
+ userList.user_xids.replace(/,/g, ', '),
+ );
+ expect(wrapper.find('[data-testid="ffUserListTimestamp"]').text()).toBe('created 2 weeks ago');
+ expect(timeago.format).toHaveBeenCalledWith(userList.created_at);
+ });
+
+ it('should set the title for a tooltip on the created stamp', () => {
+ expect(wrapper.find('[data-testid="ffUserListTimestamp"]').attributes('title')).toBe(
+ 'Feb 4, 2020 8:13am UTC',
+ );
+ });
+
+ it('should display a user list entry per user list', () => {
+ const lists = wrapper.findAll('[data-testid="ffUserList"]');
+ expect(lists).toHaveLength(5);
+ lists.wrappers.forEach((list) => {
+ expect(list.find('[data-testid="ffUserListName"]').exists()).toBe(true);
+ expect(list.find('[data-testid="ffUserListIds"]').exists()).toBe(true);
+ expect(list.find('[data-testid="ffUserListTimestamp"]').exists()).toBe(true);
+ });
+ });
+
+ describe('edit button', () => {
+ it('should link to the path for the user list', () => {
+ expect(wrapper.find('[data-testid="edit-user-list"]').attributes('href')).toBe(userList.path);
+ });
+ });
+
+ describe('delete button', () => {
+ it('should display the confirmation modal', () => {
+ const modal = wrapper.find(GlModal);
+
+ wrapper.find('[data-testid="delete-user-list"]').trigger('click');
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(modal.text()).toContain(`Delete ${userList.name}?`);
+ expect(modal.text()).toContain(`User list ${userList.name} will be removed.`);
+ });
+ });
+ });
+
+ describe('confirmation modal', () => {
+ let modal;
+
+ beforeEach(() => {
+ modal = wrapper.find(GlModal);
+
+ wrapper.find('button').trigger('click');
+
+ return wrapper.vm.$nextTick();
+ });
+
+ it('should emit delete with list on confirmation', () => {
+ modal.find('[data-testid="modal-confirm"]').trigger('click');
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.emitted('delete')).toEqual([[userLists[0]]]);
+ });
+ });
+
+ it('should not emit delete with list when not confirmed', () => {
+ modal.find('button').trigger('click');
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.emitted('delete')).toBeUndefined();
+ });
+ });
+ });
+});