debian-mirror-gitlab/spec/frontend/error_tracking/components/error_tracking_list_spec.js

500 lines
14 KiB
JavaScript
Raw Normal View History

2020-03-13 15:44:24 +05:30
import { createLocalVue, mount } from '@vue/test-utils';
2019-02-15 15:39:39 +05:30
import Vuex from 'vuex';
2020-04-08 14:13:33 +05:30
import { GlEmptyState, GlLoadingIcon, GlFormInput, GlPagination, GlDropdown } from '@gitlab/ui';
2020-03-13 15:44:24 +05:30
import stubChildren from 'helpers/stub_children';
2019-02-15 15:39:39 +05:30
import ErrorTrackingList from '~/error_tracking/components/error_tracking_list.vue';
2020-04-22 19:07:51 +05:30
import ErrorTrackingActions from '~/error_tracking/components/error_tracking_actions.vue';
2020-06-23 00:09:42 +05:30
import { trackErrorListViewsOptions, trackErrorStatusUpdateOptions } from '~/error_tracking/utils';
2020-01-01 13:55:28 +05:30
import errorsList from './list_mock.json';
2020-06-23 00:09:42 +05:30
import Tracking from '~/tracking';
2019-02-15 15:39:39 +05:30
const localVue = createLocalVue();
localVue.use(Vuex);
describe('ErrorTrackingList', () => {
let store;
let wrapper;
2019-07-07 11:18:12 +05:30
let actions;
2019-02-15 15:39:39 +05:30
2020-01-01 13:55:28 +05:30
const findErrorListTable = () => wrapper.find('table');
const findErrorListRows = () => wrapper.findAll('tbody tr');
2020-04-08 14:13:33 +05:30
const dropdownsArray = () => wrapper.findAll(GlDropdown);
2020-01-01 13:55:28 +05:30
const findRecentSearchesDropdown = () =>
2020-04-08 14:13:33 +05:30
dropdownsArray()
.at(0)
.find(GlDropdown);
const findStatusFilterDropdown = () =>
dropdownsArray()
.at(1)
.find(GlDropdown);
const findSortDropdown = () =>
dropdownsArray()
.at(2)
.find(GlDropdown);
2020-01-01 13:55:28 +05:30
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findPagination = () => wrapper.find(GlPagination);
2020-04-22 19:07:51 +05:30
const findErrorActions = () => wrapper.find(ErrorTrackingActions);
2020-01-01 13:55:28 +05:30
2019-12-04 20:38:33 +05:30
function mountComponent({
errorTrackingEnabled = true,
userCanEnableErrorTracking = true,
2020-03-13 15:44:24 +05:30
stubs = {},
2019-12-04 20:38:33 +05:30
} = {}) {
2020-03-13 15:44:24 +05:30
wrapper = mount(ErrorTrackingList, {
2019-02-15 15:39:39 +05:30
localVue,
store,
propsData: {
indexPath: '/path',
2020-03-13 15:44:24 +05:30
listPath: '/error_tracking',
projectPath: 'project/test',
2019-02-15 15:39:39 +05:30
enableErrorTrackingLink: '/link',
2019-12-04 20:38:33 +05:30
userCanEnableErrorTracking,
2019-02-15 15:39:39 +05:30
errorTrackingEnabled,
illustrationPath: 'illustration/path',
},
2020-03-13 15:44:24 +05:30
stubs: {
...stubChildren(ErrorTrackingList),
...stubs,
2020-01-01 13:55:28 +05:30
},
2019-02-15 15:39:39 +05:30
});
}
beforeEach(() => {
2019-07-07 11:18:12 +05:30
actions = {
2020-01-01 13:55:28 +05:30
getErrorList: () => {},
startPolling: jest.fn(),
2019-07-31 22:56:46 +05:30
restartPolling: jest.fn().mockName('restartPolling'),
2020-01-01 13:55:28 +05:30
addRecentSearch: jest.fn(),
loadRecentSearches: jest.fn(),
setIndexPath: jest.fn(),
clearRecentSearches: jest.fn(),
setEndpoint: jest.fn(),
searchByQuery: jest.fn(),
sortByField: jest.fn(),
2020-03-13 15:44:24 +05:30
fetchPaginatedResults: jest.fn(),
updateStatus: jest.fn(),
removeIgnoredResolvedErrors: jest.fn(),
2020-04-08 14:13:33 +05:30
filterByStatus: jest.fn(),
2019-02-15 15:39:39 +05:30
};
const state = {
2020-01-01 13:55:28 +05:30
indexPath: '',
recentSearches: [],
errors: errorsList,
2019-02-15 15:39:39 +05:30
loading: true,
2020-01-01 13:55:28 +05:30
pagination: {
previous: {
cursor: 'previousCursor',
},
next: {
cursor: 'nextCursor',
},
},
2019-02-15 15:39:39 +05:30
};
store = new Vuex.Store({
2019-12-26 22:10:19 +05:30
modules: {
list: {
namespaced: true,
actions,
state,
},
},
2019-02-15 15:39:39 +05:30
});
});
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
});
describe('loading', () => {
beforeEach(() => {
2020-01-01 13:55:28 +05:30
store.state.list.loading = true;
2019-02-15 15:39:39 +05:30
mountComponent();
});
it('shows spinner', () => {
2020-01-01 13:55:28 +05:30
expect(findLoadingIcon().exists()).toBe(true);
expect(findErrorListTable().exists()).toBe(false);
2019-02-15 15:39:39 +05:30
});
});
describe('results', () => {
beforeEach(() => {
2019-12-26 22:10:19 +05:30
store.state.list.loading = false;
2020-01-01 13:55:28 +05:30
store.state.list.errors = errorsList;
2020-03-13 15:44:24 +05:30
mountComponent({
stubs: {
GlTable: false,
GlDropdown: false,
GlDropdownItem: false,
GlLink: false,
},
});
2019-02-15 15:39:39 +05:30
});
it('shows table', () => {
2020-01-01 13:55:28 +05:30
expect(findLoadingIcon().exists()).toBe(false);
expect(findErrorListTable().exists()).toBe(true);
expect(findSortDropdown().exists()).toBe(true);
});
it('shows list of errors in a table', () => {
expect(findErrorListRows().length).toEqual(store.state.list.errors.length);
});
it('each error in a list should have a link to the error page', () => {
const errorTitle = wrapper.findAll('tbody tr a');
errorTitle.wrappers.forEach((_, index) => {
expect(errorTitle.at(index).attributes('href')).toEqual(
expect.stringMatching(/error_tracking\/\d+\/details$/),
);
});
});
2020-04-22 19:07:51 +05:30
it('each error in the list should have an action button set', () => {
2020-03-13 15:44:24 +05:30
findErrorListRows().wrappers.forEach(row => {
2020-04-22 19:07:51 +05:30
expect(row.contains(ErrorTrackingActions)).toBe(true);
2020-03-13 15:44:24 +05:30
});
});
2020-01-01 13:55:28 +05:30
describe('filtering', () => {
const findSearchBox = () => wrapper.find(GlFormInput);
it('shows search box & sort dropdown', () => {
expect(findSearchBox().exists()).toBe(true);
expect(findSortDropdown().exists()).toBe(true);
});
it('it searches by query', () => {
2020-03-13 15:44:24 +05:30
findSearchBox().vm.$emit('input', 'search');
2020-01-01 13:55:28 +05:30
findSearchBox().trigger('keyup.enter');
2020-03-13 15:44:24 +05:30
expect(actions.searchByQuery.mock.calls[0][1]).toBe('search');
2020-01-01 13:55:28 +05:30
});
it('it sorts by fields', () => {
2020-04-08 14:13:33 +05:30
const findSortItem = () => findSortDropdown().find('.dropdown-item');
2020-01-01 13:55:28 +05:30
findSortItem().trigger('click');
expect(actions.sortByField).toHaveBeenCalled();
});
2020-04-08 14:13:33 +05:30
it('it filters by status', () => {
const findStatusFilter = () => findStatusFilterDropdown().find('.dropdown-item');
findStatusFilter().trigger('click');
expect(actions.filterByStatus).toHaveBeenCalled();
});
2019-02-15 15:39:39 +05:30
});
});
describe('no results', () => {
2020-01-01 13:55:28 +05:30
const findRefreshLink = () => wrapper.find('.js-try-again');
2019-02-15 15:39:39 +05:30
beforeEach(() => {
2019-12-26 22:10:19 +05:30
store.state.list.loading = false;
2020-01-01 13:55:28 +05:30
store.state.list.errors = [];
2019-02-15 15:39:39 +05:30
2020-03-13 15:44:24 +05:30
mountComponent({
stubs: {
GlTable: false,
GlDropdown: false,
GlDropdownItem: false,
},
});
2019-02-15 15:39:39 +05:30
});
it('shows empty table', () => {
2020-01-01 13:55:28 +05:30
expect(findLoadingIcon().exists()).toBe(false);
expect(findErrorListRows().length).toEqual(1);
expect(findSortDropdown().exists()).toBe(true);
2019-02-15 15:39:39 +05:30
});
2019-07-07 11:18:12 +05:30
it('shows a message prompting to refresh', () => {
2020-01-01 13:55:28 +05:30
expect(findRefreshLink().text()).toContain('Check again');
2019-07-07 11:18:12 +05:30
});
it('restarts polling', () => {
2020-03-13 15:44:24 +05:30
findRefreshLink().vm.$emit('click');
2019-07-07 11:18:12 +05:30
expect(actions.restartPolling).toHaveBeenCalled();
});
2019-02-15 15:39:39 +05:30
});
describe('error tracking feature disabled', () => {
beforeEach(() => {
mountComponent({ errorTrackingEnabled: false });
});
it('shows empty state', () => {
2020-01-01 13:55:28 +05:30
expect(wrapper.find(GlEmptyState).exists()).toBe(true);
expect(findLoadingIcon().exists()).toBe(false);
expect(findErrorListTable().exists()).toBe(false);
2020-04-08 14:13:33 +05:30
expect(dropdownsArray().length).toBe(0);
2019-02-15 15:39:39 +05:30
});
});
2019-12-04 20:38:33 +05:30
2020-03-13 15:44:24 +05:30
describe('When the ignore button on an error is clicked', () => {
beforeEach(() => {
store.state.list.loading = false;
store.state.list.errors = errorsList;
mountComponent({
stubs: {
GlTable: false,
GlLink: false,
2020-04-22 19:07:51 +05:30
GlDeprecatedButton: false,
2020-03-13 15:44:24 +05:30
},
});
});
it('sends the "ignored" status and error ID', () => {
2020-04-22 19:07:51 +05:30
findErrorActions().vm.$emit('update-issue-status', {
errorId: errorsList[0].id,
status: 'ignored',
});
2020-03-13 15:44:24 +05:30
expect(actions.updateStatus).toHaveBeenCalledWith(
expect.anything(),
{
endpoint: `/project/test/-/error_tracking/${errorsList[0].id}.json`,
status: 'ignored',
},
undefined,
);
});
it('calls an action to remove the item from the list', () => {
2020-04-22 19:07:51 +05:30
findErrorActions().vm.$emit('update-issue-status', { errorId: '1', status: undefined });
2020-03-13 15:44:24 +05:30
expect(actions.removeIgnoredResolvedErrors).toHaveBeenCalledWith(
expect.anything(),
'1',
undefined,
);
});
});
describe('When the resolve button on an error is clicked', () => {
beforeEach(() => {
store.state.list.loading = false;
store.state.list.errors = errorsList;
mountComponent({
stubs: {
GlTable: false,
GlLink: false,
2020-04-22 19:07:51 +05:30
GlDeprecatedButton: false,
2020-03-13 15:44:24 +05:30
},
});
});
it('sends "resolved" status and error ID', () => {
2020-04-22 19:07:51 +05:30
findErrorActions().vm.$emit('update-issue-status', {
errorId: errorsList[0].id,
status: 'resolved',
});
2020-03-13 15:44:24 +05:30
expect(actions.updateStatus).toHaveBeenCalledWith(
expect.anything(),
{
endpoint: `/project/test/-/error_tracking/${errorsList[0].id}.json`,
status: 'resolved',
},
undefined,
);
});
it('calls an action to remove the item from the list', () => {
2020-04-22 19:07:51 +05:30
findErrorActions().vm.$emit('update-issue-status', { errorId: '1', status: undefined });
2020-03-13 15:44:24 +05:30
expect(actions.removeIgnoredResolvedErrors).toHaveBeenCalledWith(
expect.anything(),
'1',
undefined,
);
});
});
2019-12-04 20:38:33 +05:30
describe('When error tracking is disabled and user is not allowed to enable it', () => {
beforeEach(() => {
mountComponent({
errorTrackingEnabled: false,
userCanEnableErrorTracking: false,
stubs: {
2020-03-13 15:44:24 +05:30
GlLink: false,
GlEmptyState: false,
2019-12-04 20:38:33 +05:30
},
});
});
it('shows empty state', () => {
expect(wrapper.find('a').attributes('href')).toBe(
'/help/user/project/operations/error_tracking.html',
);
});
});
2020-01-01 13:55:28 +05:30
describe('recent searches', () => {
beforeEach(() => {
2020-03-13 15:44:24 +05:30
mountComponent({
stubs: {
GlDropdown: false,
GlDropdownItem: false,
},
});
2020-01-01 13:55:28 +05:30
});
it('shows empty message', () => {
store.state.list.recentSearches = [];
expect(findRecentSearchesDropdown().text()).toContain("You don't have any recent searches");
});
it('shows items', () => {
store.state.list.recentSearches = ['great', 'search'];
2020-03-13 15:44:24 +05:30
return wrapper.vm.$nextTick().then(() => {
const dropdownItems = wrapper.findAll('.filtered-search-box li');
expect(dropdownItems.length).toBe(3);
expect(dropdownItems.at(0).text()).toBe('great');
expect(dropdownItems.at(1).text()).toBe('search');
});
2020-01-01 13:55:28 +05:30
});
describe('clear', () => {
const clearRecentButton = () => wrapper.find({ ref: 'clearRecentSearches' });
it('is hidden when list empty', () => {
store.state.list.recentSearches = [];
expect(clearRecentButton().exists()).toBe(false);
});
it('is visible when list has items', () => {
store.state.list.recentSearches = ['some', 'searches'];
2020-03-13 15:44:24 +05:30
return wrapper.vm.$nextTick().then(() => {
expect(clearRecentButton().exists()).toBe(true);
expect(clearRecentButton().text()).toBe('Clear recent searches');
});
2020-01-01 13:55:28 +05:30
});
it('clears items on click', () => {
store.state.list.recentSearches = ['some', 'searches'];
2020-03-13 15:44:24 +05:30
return wrapper.vm.$nextTick().then(() => {
clearRecentButton().vm.$emit('click');
2020-01-01 13:55:28 +05:30
2020-03-13 15:44:24 +05:30
expect(actions.clearRecentSearches).toHaveBeenCalledTimes(1);
});
2020-01-01 13:55:28 +05:30
});
});
});
describe('When pagination is not required', () => {
beforeEach(() => {
2020-03-13 15:44:24 +05:30
store.state.list.loading = false;
2020-01-01 13:55:28 +05:30
store.state.list.pagination = {};
mountComponent();
});
it('should not render the pagination component', () => {
expect(findPagination().exists()).toBe(false);
});
});
describe('When pagination is required', () => {
describe('and the user is on the first page', () => {
beforeEach(() => {
2020-03-13 15:44:24 +05:30
store.state.list.loading = false;
mountComponent({
stubs: {
GlPagination: false,
},
});
2020-01-01 13:55:28 +05:30
});
it('shows a disabled Prev button', () => {
expect(wrapper.find('.prev-page-item').attributes('aria-disabled')).toBe('true');
});
});
describe('and the user is not on the first page', () => {
describe('and the previous button is clicked', () => {
beforeEach(() => {
2020-03-13 15:44:24 +05:30
store.state.list.loading = false;
mountComponent({
stubs: {
GlTable: false,
GlPagination: false,
},
});
2020-01-01 13:55:28 +05:30
wrapper.setData({ pageValue: 2 });
2020-03-13 15:44:24 +05:30
return wrapper.vm.$nextTick();
2020-01-01 13:55:28 +05:30
});
it('fetches the previous page of results', () => {
expect(wrapper.find('.prev-page-item').attributes('aria-disabled')).toBe(undefined);
wrapper.vm.goToPrevPage();
2020-03-13 15:44:24 +05:30
expect(actions.fetchPaginatedResults).toHaveBeenCalled();
expect(actions.fetchPaginatedResults).toHaveBeenLastCalledWith(
2020-01-01 13:55:28 +05:30
expect.anything(),
2020-03-13 15:44:24 +05:30
'previousCursor',
2020-01-01 13:55:28 +05:30
undefined,
);
});
});
describe('and the next page button is clicked', () => {
beforeEach(() => {
2020-03-13 15:44:24 +05:30
store.state.list.loading = false;
mountComponent();
2020-01-01 13:55:28 +05:30
});
it('fetches the next page of results', () => {
window.scrollTo = jest.fn();
findPagination().vm.$emit('input', 2);
expect(window.scrollTo).toHaveBeenCalledWith(0, 0);
2020-03-13 15:44:24 +05:30
expect(actions.fetchPaginatedResults).toHaveBeenCalled();
expect(actions.fetchPaginatedResults).toHaveBeenLastCalledWith(
2020-01-01 13:55:28 +05:30
expect.anything(),
2020-03-13 15:44:24 +05:30
'nextCursor',
2020-01-01 13:55:28 +05:30
undefined,
);
});
});
});
});
2020-06-23 00:09:42 +05:30
describe('Snowplow tracking', () => {
beforeEach(() => {
jest.spyOn(Tracking, 'event');
store.state.list.loading = false;
store.state.list.errors = errorsList;
mountComponent({
stubs: {
GlTable: false,
GlLink: false,
GlDeprecatedButton: false,
},
});
});
it('should track list views', () => {
const { category, action } = trackErrorListViewsOptions;
expect(Tracking.event).toHaveBeenCalledWith(category, action);
});
it('should track status updates', () => {
Tracking.event.mockClear();
const status = 'ignored';
findErrorActions().vm.$emit('update-issue-status', {
errorId: 1,
status,
});
setImmediate(() => {
const { category, action } = trackErrorStatusUpdateOptions(status);
expect(Tracking.event).toHaveBeenCalledWith(category, action);
});
});
});
2019-02-15 15:39:39 +05:30
});