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/frequent_items')
-rw-r--r--spec/frontend/frequent_items/components/app_spec.js62
-rw-r--r--spec/frontend/frequent_items/components/frequent_items_list_item_spec.js49
-rw-r--r--spec/frontend/frequent_items/components/frequent_items_list_spec.js35
-rw-r--r--spec/frontend/frequent_items/store/actions_spec.js87
-rw-r--r--spec/frontend/frequent_items/store/mutations_spec.js35
5 files changed, 232 insertions, 36 deletions
diff --git a/spec/frontend/frequent_items/components/app_spec.js b/spec/frontend/frequent_items/components/app_spec.js
index c201bbf4af2..b1e87aca63d 100644
--- a/spec/frontend/frequent_items/components/app_spec.js
+++ b/spec/frontend/frequent_items/components/app_spec.js
@@ -1,3 +1,4 @@
+import { GlButton, GlIcon } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
@@ -103,6 +104,7 @@ describe('Frequent Items App Component', () => {
expect(loading.exists()).toBe(true);
expect(loading.find('[aria-label="Loading projects"]').exists()).toBe(true);
+ expect(findSectionHeader().exists()).toBe(false);
});
it('should render frequent projects list header', () => {
@@ -112,25 +114,6 @@ describe('Frequent Items App Component', () => {
expect(sectionHeader.text()).toBe('Frequently visited');
});
- it('should render frequent projects list', async () => {
- const expectedResult = getTopFrequentItems(mockFrequentProjects);
- localStorage.setItem(TEST_STORAGE_KEY, JSON.stringify(mockFrequentProjects));
-
- expect(findFrequentItems().length).toBe(1);
-
- triggerDropdownOpen();
- await nextTick();
-
- expect(findFrequentItems().length).toBe(expectedResult.length);
- expect(findFrequentItemsList().props()).toEqual({
- items: expectedResult,
- namespace: TEST_NAMESPACE,
- hasSearchQuery: false,
- isFetchFailed: false,
- matcher: '',
- });
- });
-
it('should render searched projects list', async () => {
mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(200, mockSearchedProjects.data);
@@ -164,6 +147,47 @@ describe('Frequent Items App Component', () => {
}),
);
});
+
+ describe('with frequent items list', () => {
+ const expectedResult = getTopFrequentItems(mockFrequentProjects);
+
+ beforeEach(async () => {
+ localStorage.setItem(TEST_STORAGE_KEY, JSON.stringify(mockFrequentProjects));
+ triggerDropdownOpen();
+ await nextTick();
+ });
+
+ it('should render edit button within header', () => {
+ const itemEditButton = findSectionHeader().findComponent(GlButton);
+
+ expect(itemEditButton.exists()).toBe(true);
+ expect(itemEditButton.attributes('title')).toBe('Toggle edit mode');
+ expect(itemEditButton.findComponent(GlIcon).props('name')).toBe('pencil');
+ });
+
+ it('should render frequent projects list', () => {
+ expect(findFrequentItems().length).toBe(expectedResult.length);
+ expect(findFrequentItemsList().props()).toEqual({
+ items: expectedResult,
+ namespace: TEST_NAMESPACE,
+ hasSearchQuery: false,
+ isFetchFailed: false,
+ isItemRemovalFailed: false,
+ matcher: '',
+ });
+ });
+
+ it('dispatches action `toggleItemsListEditablity` when edit button is clicked', async () => {
+ const itemEditButton = findSectionHeader().findComponent(GlButton);
+ itemEditButton.vm.$emit('click');
+
+ await nextTick();
+
+ expect(store.dispatch).toHaveBeenCalledWith(
+ `${TEST_VUEX_MODULE}/toggleItemsListEditablity`,
+ );
+ });
+ });
});
describe('with searchClass', () => {
diff --git a/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js b/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js
index e6673fa78ec..4f2badf869d 100644
--- a/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js
+++ b/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js
@@ -1,5 +1,5 @@
-import { GlButton } from '@gitlab/ui';
-import Vue from 'vue';
+import { GlIcon } from '@gitlab/ui';
+import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { trimText } from 'helpers/text_helper';
@@ -12,6 +12,7 @@ import { mockProject } from '../mock_data';
Vue.use(Vuex);
describe('FrequentItemsListItemComponent', () => {
+ const TEST_VUEX_MODULE = 'frequentProjects';
let wrapper;
let trackingSpy;
let store;
@@ -20,11 +21,18 @@ describe('FrequentItemsListItemComponent', () => {
const findAvatar = () => wrapper.findComponent(ProjectAvatar);
const findAllTitles = () => wrapper.findAllByTestId('frequent-items-item-title');
const findNamespace = () => wrapper.findByTestId('frequent-items-item-namespace');
- const findAllButtons = () => wrapper.findAllComponents(GlButton);
+ const findAllFrequentItems = () => wrapper.findAllByTestId('frequent-item-link');
const findAllNamespace = () => wrapper.findAllByTestId('frequent-items-item-namespace');
const findAllAvatars = () => wrapper.findAllComponents(ProjectAvatar);
const findAllMetadataContainers = () =>
wrapper.findAllByTestId('frequent-items-item-metadata-container');
+ const findRemoveButton = () => wrapper.findByTestId('item-remove');
+
+ const toggleItemsListEditablity = async () => {
+ store.dispatch(`${TEST_VUEX_MODULE}/toggleItemsListEditablity`);
+
+ await nextTick();
+ };
const createComponent = (props = {}) => {
wrapper = shallowMountExtended(frequentItemsListItemComponent, {
@@ -38,7 +46,7 @@ describe('FrequentItemsListItemComponent', () => {
...props,
},
provide: {
- vuexModule: 'frequentProjects',
+ vuexModule: TEST_VUEX_MODULE,
},
});
};
@@ -102,7 +110,7 @@ describe('FrequentItemsListItemComponent', () => {
it.each`
name | selector | expected
- ${'button'} | ${findAllButtons} | ${1}
+ ${'list item'} | ${findAllFrequentItems} | ${1}
${'avatar container'} | ${findAllAvatars} | ${1}
${'metadata container'} | ${findAllMetadataContainers} | ${1}
${'title'} | ${findAllTitles} | ${1}
@@ -111,8 +119,37 @@ describe('FrequentItemsListItemComponent', () => {
expect(selector()).toHaveLength(expected);
});
+ it('renders remove button within item when `isItemsListEditable` is true', async () => {
+ await toggleItemsListEditablity();
+
+ const removeButton = findRemoveButton();
+ expect(removeButton.exists()).toBe(true);
+ expect(removeButton.attributes('title')).toBe('Remove');
+ expect(removeButton.findComponent(GlIcon).props('name')).toBe('close');
+ });
+
+ it('dispatches action `removeFrequentItem` when remove button is clicked', async () => {
+ await toggleItemsListEditablity();
+
+ jest.spyOn(store, 'dispatch');
+
+ const removeButton = findRemoveButton();
+ removeButton.vm.$emit(
+ 'click',
+ { stopPropagation: jest.fn(), preventDefault: jest.fn() },
+ mockProject.id,
+ );
+
+ await nextTick();
+
+ expect(store.dispatch).toHaveBeenCalledWith(
+ `${TEST_VUEX_MODULE}/removeFrequentItem`,
+ mockProject.id,
+ );
+ });
+
it('tracks when item link is clicked', () => {
- const link = wrapper.findComponent(GlButton);
+ const link = wrapper.findByTestId('frequent-item-link');
link.vm.$emit('click');
diff --git a/spec/frontend/frequent_items/components/frequent_items_list_spec.js b/spec/frontend/frequent_items/components/frequent_items_list_spec.js
index 9f08a432a3d..d024925f62b 100644
--- a/spec/frontend/frequent_items/components/frequent_items_list_spec.js
+++ b/spec/frontend/frequent_items/components/frequent_items_list_spec.js
@@ -18,6 +18,7 @@ describe('FrequentItemsListComponent', () => {
namespace: 'projects',
items: mockFrequentProjects,
isFetchFailed: false,
+ isItemRemovalFailed: false,
hasSearchQuery: false,
matcher: 'lab',
...props,
@@ -51,22 +52,34 @@ describe('FrequentItemsListComponent', () => {
});
describe('fetched item messages', () => {
- it('should return appropriate empty list message based on value of `localStorageFailed` prop with projects', async () => {
+ it('should show default empty list message', async () => {
createComponent({
- isFetchFailed: true,
+ items: [],
});
- expect(wrapper.vm.listEmptyMessage).toBe(
- 'This feature requires browser localStorage support',
+ expect(wrapper.findByTestId('frequent-items-list-empty').text()).toContain(
+ 'Projects you visit often will appear here',
);
-
- wrapper.setProps({
- isFetchFailed: false,
- });
- await nextTick();
-
- expect(wrapper.vm.listEmptyMessage).toBe('Projects you visit often will appear here');
});
+
+ it.each`
+ isFetchFailed | isItemRemovalFailed
+ ${true} | ${false}
+ ${false} | ${true}
+ `(
+ 'should show failure message when `isFetchFailed` is $isFetchFailed or `isItemRemovalFailed` is $isItemRemovalFailed',
+ ({ isFetchFailed, isItemRemovalFailed }) => {
+ createComponent({
+ items: [],
+ isFetchFailed,
+ isItemRemovalFailed,
+ });
+
+ expect(wrapper.findByTestId('frequent-items-list-empty').text()).toContain(
+ 'This feature requires browser localStorage support',
+ );
+ },
+ );
});
describe('searched item messages', () => {
diff --git a/spec/frontend/frequent_items/store/actions_spec.js b/spec/frontend/frequent_items/store/actions_spec.js
index 3fc3eaf52a2..4f998cc26da 100644
--- a/spec/frontend/frequent_items/store/actions_spec.js
+++ b/spec/frontend/frequent_items/store/actions_spec.js
@@ -5,6 +5,7 @@ import * as types from '~/frequent_items/store/mutation_types';
import state from '~/frequent_items/store/state';
import AccessorUtilities from '~/lib/utils/accessor';
import axios from '~/lib/utils/axios_utils';
+import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import {
mockNamespace,
mockStorageKey,
@@ -13,6 +14,7 @@ import {
} from '../mock_data';
describe('Frequent Items Dropdown Store Actions', () => {
+ useLocalStorageSpy();
let mockedState;
let mock;
@@ -52,6 +54,18 @@ describe('Frequent Items Dropdown Store Actions', () => {
});
});
+ describe('toggleItemsListEditablity', () => {
+ it('should toggle items list editablity', () => {
+ return testAction(
+ actions.toggleItemsListEditablity,
+ null,
+ mockedState,
+ [{ type: types.TOGGLE_ITEMS_LIST_EDITABILITY }],
+ [],
+ );
+ });
+ });
+
describe('requestFrequentItems', () => {
it('should request frequent items', () => {
return testAction(
@@ -211,4 +225,77 @@ describe('Frequent Items Dropdown Store Actions', () => {
);
});
});
+
+ describe('removeFrequentItemSuccess', () => {
+ it('should remove frequent item on success', () => {
+ return testAction(
+ actions.removeFrequentItemSuccess,
+ { itemId: 1 },
+ mockedState,
+ [
+ {
+ type: types.RECEIVE_REMOVE_FREQUENT_ITEM_SUCCESS,
+ payload: { itemId: 1 },
+ },
+ ],
+ [],
+ );
+ });
+ });
+
+ describe('removeFrequentItemError', () => {
+ it('should should not remove frequent item on failure', () => {
+ return testAction(
+ actions.removeFrequentItemError,
+ null,
+ mockedState,
+ [{ type: types.RECEIVE_REMOVE_FREQUENT_ITEM_ERROR }],
+ [],
+ );
+ });
+ });
+
+ describe('removeFrequentItem', () => {
+ beforeEach(() => {
+ mockedState.items = [...mockFrequentProjects];
+ window.localStorage.setItem(mockStorageKey, JSON.stringify(mockFrequentProjects));
+ });
+
+ it('should remove provided itemId from localStorage', () => {
+ jest.spyOn(AccessorUtilities, 'canUseLocalStorage').mockReturnValue(true);
+
+ actions.removeFrequentItem(
+ { commit: jest.fn(), dispatch: jest.fn(), state: mockedState },
+ mockFrequentProjects[0].id,
+ );
+
+ expect(window.localStorage.getItem(mockStorageKey)).toBe(
+ JSON.stringify(mockFrequentProjects.slice(1)), // First item was removed
+ );
+ });
+
+ it('should dispatch `removeFrequentItemSuccess` on localStorage update success', () => {
+ jest.spyOn(AccessorUtilities, 'canUseLocalStorage').mockReturnValue(true);
+
+ return testAction(
+ actions.removeFrequentItem,
+ mockFrequentProjects[0].id,
+ mockedState,
+ [],
+ [{ type: 'removeFrequentItemSuccess', payload: mockFrequentProjects[0].id }],
+ );
+ });
+
+ it('should dispatch `removeFrequentItemError` on localStorage update failure', () => {
+ jest.spyOn(AccessorUtilities, 'canUseLocalStorage').mockReturnValue(false);
+
+ return testAction(
+ actions.removeFrequentItem,
+ mockFrequentProjects[0].id,
+ mockedState,
+ [],
+ [{ type: 'removeFrequentItemError' }],
+ );
+ });
+ });
});
diff --git a/spec/frontend/frequent_items/store/mutations_spec.js b/spec/frontend/frequent_items/store/mutations_spec.js
index e593c9fae58..1e1878c3377 100644
--- a/spec/frontend/frequent_items/store/mutations_spec.js
+++ b/spec/frontend/frequent_items/store/mutations_spec.js
@@ -44,6 +44,18 @@ describe('Frequent Items dropdown mutations', () => {
});
});
+ describe('TOGGLE_ITEMS_LIST_EDITABILITY', () => {
+ it('should toggle items list editablity', () => {
+ mutations[types.TOGGLE_ITEMS_LIST_EDITABILITY](stateCopy);
+
+ expect(stateCopy.isItemsListEditable).toEqual(true);
+
+ mutations[types.TOGGLE_ITEMS_LIST_EDITABILITY](stateCopy);
+
+ expect(stateCopy.isItemsListEditable).toEqual(false);
+ });
+ });
+
describe('REQUEST_FREQUENT_ITEMS', () => {
it('should set view states when requesting frequent items', () => {
mutations[types.REQUEST_FREQUENT_ITEMS](stateCopy);
@@ -114,4 +126,27 @@ describe('Frequent Items dropdown mutations', () => {
expect(stateCopy.isFetchFailed).toEqual(true);
});
});
+
+ describe('RECEIVE_REMOVE_FREQUENT_ITEM_SUCCESS', () => {
+ it('should remove item with provided itemId from the items', () => {
+ stateCopy.isItemRemovalFailed = true;
+ stateCopy.items = mockFrequentProjects;
+
+ mutations[types.RECEIVE_REMOVE_FREQUENT_ITEM_SUCCESS](stateCopy, mockFrequentProjects[0].id);
+
+ expect(stateCopy.items).toHaveLength(mockFrequentProjects.length - 1);
+ expect(stateCopy.items).toEqual([...mockFrequentProjects.slice(1)]);
+ expect(stateCopy.isItemRemovalFailed).toBe(false);
+ });
+ });
+
+ describe('RECEIVE_REMOVE_FREQUENT_ITEM_ERROR', () => {
+ it('should remove item with provided itemId from the items', () => {
+ stateCopy.isItemRemovalFailed = false;
+
+ mutations[types.RECEIVE_REMOVE_FREQUENT_ITEM_ERROR](stateCopy);
+
+ expect(stateCopy.isItemRemovalFailed).toBe(true);
+ });
+ });
});