diff options
Diffstat (limited to 'spec/frontend/error_tracking')
4 files changed, 124 insertions, 41 deletions
diff --git a/spec/frontend/error_tracking/components/error_tracking_list_spec.js b/spec/frontend/error_tracking/components/error_tracking_list_spec.js index 4edc2a647c3..80f5b2ccb9f 100644 --- a/spec/frontend/error_tracking/components/error_tracking_list_spec.js +++ b/spec/frontend/error_tracking/components/error_tracking_list_spec.js @@ -9,6 +9,7 @@ import { GlLink, GlSearchBoxByClick, } from '@gitlab/ui'; +import errorsList from './list_mock.json'; const localVue = createLocalVue(); localVue.use(Vuex); @@ -18,11 +19,17 @@ describe('ErrorTrackingList', () => { let wrapper; let actions; + const findErrorListTable = () => wrapper.find('table'); + const findErrorListRows = () => wrapper.findAll('tbody tr'); + const findButton = () => wrapper.find(GlButton); + const findLoadingIcon = () => wrapper.find(GlLoadingIcon); + function mountComponent({ errorTrackingEnabled = true, userCanEnableErrorTracking = true, stubs = { 'gl-link': GlLink, + 'gl-table': GlTable, }, } = {}) { wrapper = shallowMount(ErrorTrackingList, { @@ -47,7 +54,7 @@ describe('ErrorTrackingList', () => { }; const state = { - errors: [], + errors: errorsList, loading: true, }; @@ -75,61 +82,74 @@ describe('ErrorTrackingList', () => { }); it('shows spinner', () => { - expect(wrapper.find(GlLoadingIcon).exists()).toBeTruthy(); - expect(wrapper.find(GlTable).exists()).toBeFalsy(); + expect(findLoadingIcon().exists()).toBe(true); + expect(findErrorListTable().exists()).toBe(false); }); }); describe('results', () => { beforeEach(() => { store.state.list.loading = false; - mountComponent(); }); it('shows table', () => { - expect(wrapper.find(GlLoadingIcon).exists()).toBeFalsy(); - expect(wrapper.find(GlTable).exists()).toBeTruthy(); - expect(wrapper.find(GlButton).exists()).toBeTruthy(); + expect(findLoadingIcon().exists()).toBe(false); + expect(findErrorListTable().exists()).toBe(true); + expect(findButton().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$/), + ); + }); }); describe('filtering', () => { + const findSearchBox = () => wrapper.find(GlSearchBoxByClick); + it('shows search box', () => { - expect(wrapper.find(GlSearchBoxByClick).exists()).toBeTruthy(); + expect(findSearchBox().exists()).toBe(true); }); it('makes network request on submit', () => { expect(actions.startPolling).toHaveBeenCalledTimes(1); - - wrapper.find(GlSearchBoxByClick).vm.$emit('submit'); - + findSearchBox().vm.$emit('submit'); expect(actions.startPolling).toHaveBeenCalledTimes(2); }); }); }); describe('no results', () => { + const findRefreshLink = () => wrapper.find('.js-try-again'); + beforeEach(() => { store.state.list.loading = false; + store.state.list.errors = []; mountComponent(); }); it('shows empty table', () => { - expect(wrapper.find(GlLoadingIcon).exists()).toBeFalsy(); - expect(wrapper.find(GlTable).exists()).toBeTruthy(); - expect(wrapper.find(GlButton).exists()).toBeTruthy(); + expect(findLoadingIcon().exists()).toBe(false); + expect(findErrorListRows().length).toEqual(1); + expect(findButton().exists()).toBe(true); }); it('shows a message prompting to refresh', () => { - const refreshLink = wrapper.vm.$refs.empty.querySelector('a'); - - expect(refreshLink.textContent.trim()).toContain('Check again'); + expect(findRefreshLink().text()).toContain('Check again'); }); it('restarts polling', () => { - wrapper.find('.js-try-again').trigger('click'); - + findRefreshLink().trigger('click'); expect(actions.restartPolling).toHaveBeenCalled(); }); }); @@ -140,10 +160,10 @@ describe('ErrorTrackingList', () => { }); it('shows empty state', () => { - expect(wrapper.find(GlEmptyState).exists()).toBeTruthy(); - expect(wrapper.find(GlLoadingIcon).exists()).toBeFalsy(); - expect(wrapper.find(GlTable).exists()).toBeFalsy(); - expect(wrapper.find(GlButton).exists()).toBeFalsy(); + expect(wrapper.find(GlEmptyState).exists()).toBe(true); + expect(findLoadingIcon().exists()).toBe(false); + expect(findErrorListTable().exists()).toBe(false); + expect(findButton().exists()).toBe(false); }); }); diff --git a/spec/frontend/error_tracking/components/list_mock.json b/spec/frontend/error_tracking/components/list_mock.json new file mode 100644 index 00000000000..a6e94c1a026 --- /dev/null +++ b/spec/frontend/error_tracking/components/list_mock.json @@ -0,0 +1,29 @@ +[ + { + "id": "1", + "title": "PG::ConnectionBad: FATAL", + "type": "error", + "userCount": 0, + "count": "52", + "firstSeen": "2019-05-30T07:21:46Z", + "lastSeen": "2019-11-06T03:21:39Z" + }, + { + "id": "2", + "title": "ActiveRecord::StatementInvalid", + "type": "error", + "userCount": 0, + "count": "12", + "firstSeen": "2019-10-19T03:53:56Z", + "lastSeen": "2019-11-05T03:51:54Z" + }, + { + "id": "3", + "title": "Command has failed", + "type": "default", + "userCount": 0, + "count": "275", + "firstSeen": "2019-02-12T07:22:36Z", + "lastSeen": "2019-10-22T03:20:48Z" + } +]
\ No newline at end of file diff --git a/spec/frontend/error_tracking/components/stacktrace_entry_spec.js b/spec/frontend/error_tracking/components/stacktrace_entry_spec.js index 95958408770..942585d5370 100644 --- a/spec/frontend/error_tracking/components/stacktrace_entry_spec.js +++ b/spec/frontend/error_tracking/components/stacktrace_entry_spec.js @@ -7,26 +7,23 @@ import Icon from '~/vue_shared/components/icon.vue'; describe('Stacktrace Entry', () => { let wrapper; + const lines = [ + [22, ' def safe_thread(name, \u0026block)\n'], + [23, ' Thread.new do\n'], + [24, " Thread.current['sidekiq_label'] = name\n"], + [25, ' watchdog(name, \u0026block)\n'], + ]; + function mountComponent(props) { wrapper = shallowMount(StackTraceEntry, { propsData: { filePath: 'sidekiq/util.rb', - lines: [ - [22, ' def safe_thread(name, \u0026block)\n'], - [23, ' Thread.new do\n'], - [24, " Thread.current['sidekiq_label'] = name\n"], - [25, ' watchdog(name, \u0026block)\n'], - ], errorLine: 24, ...props, }, }); } - beforeEach(() => { - mountComponent(); - }); - afterEach(() => { if (wrapper) { wrapper.destroy(); @@ -34,16 +31,47 @@ describe('Stacktrace Entry', () => { }); it('should render stacktrace entry collapsed', () => { + mountComponent({ lines }); expect(wrapper.find(StackTraceEntry).exists()).toBe(true); expect(wrapper.find(ClipboardButton).exists()).toBe(true); expect(wrapper.find(Icon).exists()).toBe(true); expect(wrapper.find(FileIcon).exists()).toBe(true); - expect(wrapper.element.querySelectorAll('table').length).toBe(0); + expect(wrapper.find('table').exists()).toBe(false); }); it('should render stacktrace entry table expanded', () => { - mountComponent({ expanded: true }); - expect(wrapper.element.querySelectorAll('tr.line_holder').length).toBe(4); - expect(wrapper.element.querySelectorAll('.line_content.old').length).toBe(1); + mountComponent({ expanded: true, lines }); + expect(wrapper.find('table').exists()).toBe(true); + expect(wrapper.findAll('tr.line_holder').length).toBe(4); + expect(wrapper.findAll('.line_content.old').length).toBe(1); + }); + + describe('no code block', () => { + const findFileHeaderContent = () => wrapper.find('.file-header-content').html(); + + it('should hide collapse icon and render error fn name and error line when there is no code block', () => { + const extraInfo = { errorLine: 34, errorFn: 'errorFn', errorColumn: 77 }; + mountComponent({ expanded: false, lines: [], ...extraInfo }); + expect(wrapper.find(Icon).exists()).toBe(false); + expect(findFileHeaderContent()).toContain( + `in ${extraInfo.errorFn} at line ${extraInfo.errorLine}:${extraInfo.errorColumn}`, + ); + }); + + it('should render only lineNo:columnNO when there is no errorFn ', () => { + const extraInfo = { errorLine: 34, errorFn: null, errorColumn: 77 }; + mountComponent({ expanded: false, lines: [], ...extraInfo }); + expect(findFileHeaderContent()).not.toContain(`in ${extraInfo.errorFn}`); + expect(findFileHeaderContent()).toContain(`${extraInfo.errorLine}:${extraInfo.errorColumn}`); + }); + + it('should render only lineNo when there is no errorColumn ', () => { + const extraInfo = { errorLine: 34, errorFn: 'errorFn', errorColumn: null }; + mountComponent({ expanded: false, lines: [], ...extraInfo }); + expect(findFileHeaderContent()).toContain( + `in ${extraInfo.errorFn} at line ${extraInfo.errorLine}`, + ); + expect(findFileHeaderContent()).not.toContain(`:${extraInfo.errorColumn}`); + }); }); }); diff --git a/spec/frontend/error_tracking/store/details/getters_spec.js b/spec/frontend/error_tracking/store/details/getters_spec.js index ea57de5872b..aba080790da 100644 --- a/spec/frontend/error_tracking/store/details/getters_spec.js +++ b/spec/frontend/error_tracking/store/details/getters_spec.js @@ -1,12 +1,18 @@ import * as getters from '~/error_tracking/store/details/getters'; describe('Sentry error details store getters', () => { - const state = { - stacktraceData: { stack_trace_entries: [1, 2] }, - }; - describe('stacktrace', () => { + it('should return empty stacktrace when there are no entries', () => { + const state = { + stacktraceData: { stack_trace_entries: null }, + }; + expect(getters.stacktrace(state)).toEqual([]); + }); + it('should get stacktrace', () => { + const state = { + stacktraceData: { stack_trace_entries: [1, 2] }, + }; expect(getters.stacktrace(state)).toEqual([2, 1]); }); }); |