diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-16 18:25:58 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-16 18:25:58 +0000 |
commit | a5f4bba440d7f9ea47046a0a561d49adf0a1e6d4 (patch) | |
tree | fb69158581673816a8cd895f9d352dcb3c678b1e /spec/frontend/user_lists/components | |
parent | d16b2e8639e99961de6ddc93909f3bb5c1445ba1 (diff) | |
download | gitlab-ce-a5f4bba440d7f9ea47046a0a561d49adf0a1e6d4.tar.gz |
Add latest changes from gitlab-org/gitlab@14-0-stable-eev14.0.0-rc42
Diffstat (limited to 'spec/frontend/user_lists/components')
-rw-r--r-- | spec/frontend/user_lists/components/user_lists_spec.js | 195 | ||||
-rw-r--r-- | spec/frontend/user_lists/components/user_lists_table_spec.js | 98 |
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(); + }); + }); + }); +}); |