diff options
Diffstat (limited to 'spec')
9 files changed, 312 insertions, 177 deletions
diff --git a/spec/features/error_tracking/user_filters_errors_by_status_spec.rb b/spec/features/error_tracking/user_filters_errors_by_status_spec.rb new file mode 100644 index 00000000000..51e29e2a5ec --- /dev/null +++ b/spec/features/error_tracking/user_filters_errors_by_status_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'When a user filters Sentry errors by status', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline do + include_context 'sentry error tracking context feature' + + let_it_be(:issues_response_body) { fixture_file('sentry/issues_sample_response.json') } + let_it_be(:filtered_errors_by_status_response) { JSON.parse(issues_response_body).filter { |error| error['status'] == 'ignored' }.to_json } + let(:issues_api_url) { "#{sentry_api_urls.issues_url}?limit=20&query=is:unresolved" } + let(:issues_api_url_filter) { "#{sentry_api_urls.issues_url}?limit=20&query=is:ignored" } + let(:auth_token) {{ 'Authorization' => 'Bearer access_token_123' }} + let(:return_header) {{ 'Content-Type' => 'application/json' }} + + before do + stub_request(:get, issues_api_url).with(headers: auth_token) + .to_return(status: 200, body: issues_response_body, headers: return_header) + + stub_request(:get, issues_api_url_filter).with(headers: auth_token) + .to_return(status: 200, body: filtered_errors_by_status_response, headers: return_header) + end + + it 'displays the results' do + sign_in(project.owner) + visit project_error_tracking_index_path(project) + page.within(find('.gl-table')) do + results = page.all('.table-row') + expect(results.count).to be(3) + end + + find('.status-dropdown .dropdown-toggle').click + find('.dropdown-item', text: 'Ignored').click + + page.within(find('.gl-table')) do + results = page.all('.table-row') + expect(results.count).to be(1) + expect(results.first).to have_content(filtered_errors_by_status_response[0]['title']) + end + end +end diff --git a/spec/features/error_tracking/user_searches_sentry_errors_spec.rb b/spec/features/error_tracking/user_searches_sentry_errors_spec.rb index 690c60a3c3f..c5559081feb 100644 --- a/spec/features/error_tracking/user_searches_sentry_errors_spec.rb +++ b/spec/features/error_tracking/user_searches_sentry_errors_spec.rb @@ -26,7 +26,7 @@ describe 'When a user searches for Sentry errors', :js, :use_clean_rails_memory_ page.within(find('.gl-table')) do results = page.all('.table-row') - expect(results.count).to be(2) + expect(results.count).to be(3) end find('.gl-form-input').set('NotFound').native.send_keys(:return) diff --git a/spec/fixtures/sentry/issues_sample_response.json b/spec/fixtures/sentry/issues_sample_response.json index 495562ac960..980e2fb3bd6 100644 --- a/spec/fixtures/sentry/issues_sample_response.json +++ b/spec/fixtures/sentry/issues_sample_response.json @@ -82,5 +82,47 @@ "name": "Internal" }, "statusDetails": {} + }, + { + "lastSeen": "2018-12-31T12:00:11Z", + "numComments": 0, + "userCount": 0, + "stats": { + "24h": [ + [ + 1546437600, + 0 + ] + ] + }, + "culprit": "sentry.tasks.reports.deliver_organization_user_report", + "title": "Service unknown", + "id": "12", + "assignedTo": null, + "logger": null, + "type": "error", + "annotations": [], + "metadata": { + "type": "gaierror", + "value": "Service unknown" + }, + "status": "ignored", + "subscriptionDetails": null, + "isPublic": false, + "hasSeen": false, + "shortId": "INTERNAL-4", + "shareId": null, + "firstSeen": "2018-12-17T12:00:14Z", + "count": "70", + "permalink": "35.228.54.90/sentry/internal/issues/12/", + "level": "error", + "isSubscribed": true, + "isBookmarked": false, + "project": { + "slug": "internal", + "id": "1", + "name": "Internal" + }, + "statusDetails": {} } ] diff --git a/spec/frontend/diffs/components/commit_item_spec.js b/spec/frontend/diffs/components/commit_item_spec.js new file mode 100644 index 00000000000..ecc28c78fb7 --- /dev/null +++ b/spec/frontend/diffs/components/commit_item_spec.js @@ -0,0 +1,179 @@ +import { mount } from '@vue/test-utils'; +import { TEST_HOST } from 'helpers/test_constants'; +import { trimText } from 'helpers/text_helper'; +import { getTimeago } from '~/lib/utils/datetime_utility'; +import Component from '~/diffs/components/commit_item.vue'; +import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; +import getDiffWithCommit from '../mock_data/diff_with_commit'; + +jest.mock('~/user_popovers'); + +const TEST_AUTHOR_NAME = 'test'; +const TEST_AUTHOR_EMAIL = 'test+test@gitlab.com'; +const TEST_AUTHOR_GRAVATAR = `${TEST_HOST}/avatar/test?s=40`; +const TEST_SIGNATURE_HTML = '<a>Legit commit</a>'; +const TEST_PIPELINE_STATUS_PATH = `${TEST_HOST}/pipeline/status`; + +describe('diffs/components/commit_item', () => { + let wrapper; + + const timeago = getTimeago(); + const { commit } = getDiffWithCommit(); + + const getTitleElement = () => wrapper.find('.commit-row-message.item-title'); + const getDescElement = () => wrapper.find('pre.commit-row-description'); + const getDescExpandElement = () => + wrapper.find('.commit-content .text-expander.js-toggle-button'); + const getShaElement = () => wrapper.find('.commit-sha-group'); + const getAvatarElement = () => wrapper.find('.user-avatar-link'); + const getCommitterElement = () => wrapper.find('.committer'); + const getCommitActionsElement = () => wrapper.find('.commit-actions'); + const getCommitPipelineStatus = () => wrapper.find(CommitPipelineStatus); + + const defaultProps = { + commit: getDiffWithCommit().commit, + }; + const mountComponent = (propsData = defaultProps) => { + wrapper = mount(Component, { + propsData, + stubs: { + CommitPipelineStatus: true, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('default state', () => { + beforeEach(() => { + mountComponent(); + }); + + it('renders commit title', () => { + const titleElement = getTitleElement(); + + expect(titleElement.attributes('href')).toBe(commit.commit_url); + expect(titleElement.text()).toBe(commit.title_html); + }); + + it('renders commit description', () => { + const descElement = getDescElement(); + const descExpandElement = getDescExpandElement(); + + const expected = commit.description_html.replace(/
/g, ''); + + expect(trimText(descElement.text())).toEqual(trimText(expected)); + expect(descExpandElement.exists()).toBe(true); + }); + + it('renders commit sha', () => { + const shaElement = getShaElement(); + const labelElement = shaElement.find('.label'); + const buttonElement = shaElement.find('button'); + + expect(labelElement.text()).toEqual(commit.short_id); + expect(buttonElement.props('text')).toBe(commit.id); + }); + + it('renders author avatar', () => { + const avatarElement = getAvatarElement(); + const imgElement = avatarElement.find('img'); + + expect(avatarElement.attributes('href')).toBe(commit.author.web_url); + expect(imgElement.classes()).toContain('s40'); + expect(imgElement.attributes('alt')).toBe(commit.author.name); + expect(imgElement.attributes('src')).toBe(commit.author.avatar_url); + }); + + it('renders committer text', () => { + const committerElement = getCommitterElement(); + const nameElement = committerElement.find('a'); + + const expectTimeText = timeago.format(commit.authored_date); + const expectedText = `${commit.author.name} authored ${expectTimeText}`; + + expect(trimText(committerElement.text())).toEqual(expectedText); + expect(nameElement.attributes('href')).toBe(commit.author.web_url); + expect(nameElement.text()).toBe(commit.author.name); + expect(nameElement.classes()).toContain('js-user-link'); + expect(nameElement.attributes('data-user-id')).toEqual(commit.author.id.toString()); + }); + }); + + describe('without commit description', () => { + beforeEach(() => { + mountComponent({ defaultProps, commit: { ...defaultProps.commit, description_html: '' } }); + }); + + it('hides description', () => { + const descElement = getDescElement(); + const descExpandElement = getDescExpandElement(); + + expect(descElement.exists()).toBeFalsy(); + expect(descExpandElement.exists()).toBeFalsy(); + }); + }); + + describe('with no matching user', () => { + beforeEach(() => { + mountComponent({ + defaultProps, + commit: { + ...defaultProps.commit, + author: null, + author_email: TEST_AUTHOR_EMAIL, + author_name: TEST_AUTHOR_NAME, + author_gravatar_url: TEST_AUTHOR_GRAVATAR, + }, + }); + }); + + it('renders author avatar', () => { + const avatarElement = getAvatarElement(); + const imgElement = avatarElement.find('img'); + + expect(avatarElement.attributes('href')).toBe(`mailto:${TEST_AUTHOR_EMAIL}`); + expect(imgElement.attributes('alt')).toBe(TEST_AUTHOR_NAME); + expect(imgElement.attributes('src')).toBe(TEST_AUTHOR_GRAVATAR); + }); + + it('renders committer text', () => { + const committerElement = getCommitterElement(); + const nameElement = committerElement.find('a'); + + expect(nameElement.attributes('href')).toBe(`mailto:${TEST_AUTHOR_EMAIL}`); + expect(nameElement.text()).toBe(TEST_AUTHOR_NAME); + }); + }); + + describe('with signature', () => { + beforeEach(() => { + mountComponent({ + defaultProps, + commit: { ...defaultProps.commit, signature_html: TEST_SIGNATURE_HTML }, + }); + }); + + it('renders signature html', () => { + const actionsElement = getCommitActionsElement(); + + expect(actionsElement.html()).toContain(TEST_SIGNATURE_HTML); + }); + }); + + describe('with pipeline status', () => { + beforeEach(() => { + mountComponent({ + defaultProps, + commit: { ...defaultProps.commit, pipeline_status_path: TEST_PIPELINE_STATUS_PATH }, + }); + }); + + it('renders pipeline status', () => { + expect(getCommitPipelineStatus().exists()).toBe(true); + }); + }); +}); 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 f852a3091aa..cd6dd5c7519 100644 --- a/spec/frontend/error_tracking/components/error_tracking_list_spec.js +++ b/spec/frontend/error_tracking/components/error_tracking_list_spec.js @@ -1,6 +1,6 @@ import { createLocalVue, mount } from '@vue/test-utils'; import Vuex from 'vuex'; -import { GlEmptyState, GlLoadingIcon, GlFormInput, GlPagination } from '@gitlab/ui'; +import { GlEmptyState, GlLoadingIcon, GlFormInput, GlPagination, GlDropdown } from '@gitlab/ui'; import stubChildren from 'helpers/stub_children'; import ErrorTrackingList from '~/error_tracking/components/error_tracking_list.vue'; import errorsList from './list_mock.json'; @@ -15,9 +15,19 @@ describe('ErrorTrackingList', () => { const findErrorListTable = () => wrapper.find('table'); const findErrorListRows = () => wrapper.findAll('tbody tr'); - const findSortDropdown = () => wrapper.find('.sort-dropdown'); + const dropdownsArray = () => wrapper.findAll(GlDropdown); const findRecentSearchesDropdown = () => - wrapper.find('.filtered-search-history-dropdown-wrapper'); + dropdownsArray() + .at(0) + .find(GlDropdown); + const findStatusFilterDropdown = () => + dropdownsArray() + .at(1) + .find(GlDropdown); + const findSortDropdown = () => + dropdownsArray() + .at(2) + .find(GlDropdown); const findLoadingIcon = () => wrapper.find(GlLoadingIcon); const findPagination = () => wrapper.find(GlPagination); @@ -60,6 +70,7 @@ describe('ErrorTrackingList', () => { fetchPaginatedResults: jest.fn(), updateStatus: jest.fn(), removeIgnoredResolvedErrors: jest.fn(), + filterByStatus: jest.fn(), }; const state = { @@ -167,10 +178,16 @@ describe('ErrorTrackingList', () => { }); it('it sorts by fields', () => { - const findSortItem = () => wrapper.find('.dropdown-item'); + const findSortItem = () => findSortDropdown().find('.dropdown-item'); findSortItem().trigger('click'); expect(actions.sortByField).toHaveBeenCalled(); }); + + it('it filters by status', () => { + const findStatusFilter = () => findStatusFilterDropdown().find('.dropdown-item'); + findStatusFilter().trigger('click'); + expect(actions.filterByStatus).toHaveBeenCalled(); + }); }); }); @@ -215,7 +232,7 @@ describe('ErrorTrackingList', () => { expect(wrapper.find(GlEmptyState).exists()).toBe(true); expect(findLoadingIcon().exists()).toBe(false); expect(findErrorListTable().exists()).toBe(false); - expect(findSortDropdown().exists()).toBe(false); + expect(dropdownsArray().length).toBe(0); }); }); diff --git a/spec/frontend/error_tracking/store/list/actions_spec.js b/spec/frontend/error_tracking/store/list/actions_spec.js index 54fdde88818..3cb740bf05d 100644 --- a/spec/frontend/error_tracking/store/list/actions_spec.js +++ b/spec/frontend/error_tracking/store/list/actions_spec.js @@ -88,6 +88,20 @@ describe('error tracking actions', () => { }); }); + describe('filterByStatus', () => { + it('should search errors by status', () => { + const status = 'ignored'; + + testAction( + actions.filterByStatus, + status, + {}, + [{ type: types.SET_STATUS_FILTER, payload: status }], + [{ type: 'stopPolling' }, { type: 'startPolling' }], + ); + }); + }); + describe('sortByField', () => { it('should search by query', () => { const field = 'frequency'; diff --git a/spec/frontend/error_tracking/store/list/mutation_spec.js b/spec/frontend/error_tracking/store/list/mutation_spec.js index 65f11aeeda1..a326a6c55c0 100644 --- a/spec/frontend/error_tracking/store/list/mutation_spec.js +++ b/spec/frontend/error_tracking/store/list/mutation_spec.js @@ -6,6 +6,7 @@ const ADD_RECENT_SEARCH = mutations[types.ADD_RECENT_SEARCH]; const CLEAR_RECENT_SEARCHES = mutations[types.CLEAR_RECENT_SEARCHES]; const LOAD_RECENT_SEARCHES = mutations[types.LOAD_RECENT_SEARCHES]; const REMOVE_IGNORED_RESOLVED_ERRORS = mutations[types.REMOVE_IGNORED_RESOLVED_ERRORS]; +const SET_STATUS_FILTER = mutations[types.SET_STATUS_FILTER]; describe('Error tracking mutations', () => { describe('SET_ERRORS', () => { @@ -139,5 +140,15 @@ describe('Error tracking mutations', () => { expect(state.errors).not.toContain(ignoredError); }); }); + + describe('SET_STATUS_FILTER', () => { + it('sets the filter to ignored, resolved or unresolved', () => { + state.statusFilter = 'unresolved'; + + SET_STATUS_FILTER(state, 'ignored'); + + expect(state.statusFilter).toBe('ignored'); + }); + }); }); }); diff --git a/spec/javascripts/diffs/components/commit_item_spec.js b/spec/javascripts/diffs/components/commit_item_spec.js deleted file mode 100644 index 9fe31a5e727..00000000000 --- a/spec/javascripts/diffs/components/commit_item_spec.js +++ /dev/null @@ -1,168 +0,0 @@ -import Vue from 'vue'; -import { TEST_HOST } from 'spec/test_constants'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import { trimText } from 'spec/helpers/text_helper'; -import { getTimeago } from '~/lib/utils/datetime_utility'; -import CommitItem from '~/diffs/components/commit_item.vue'; -import getDiffWithCommit from '../mock_data/diff_with_commit'; - -const TEST_AUTHOR_NAME = 'test'; -const TEST_AUTHOR_EMAIL = 'test+test@gitlab.com'; -const TEST_AUTHOR_GRAVATAR = `${TEST_HOST}/avatar/test?s=40`; -const TEST_SIGNATURE_HTML = '<a>Legit commit</a>'; -const TEST_PIPELINE_STATUS_PATH = `${TEST_HOST}/pipeline/status`; - -const getTitleElement = vm => vm.$el.querySelector('.commit-row-message.item-title'); -const getDescElement = vm => vm.$el.querySelector('pre.commit-row-description'); -const getDescExpandElement = vm => - vm.$el.querySelector('.commit-content .text-expander.js-toggle-button'); -const getShaElement = vm => vm.$el.querySelector('.commit-sha-group'); -const getAvatarElement = vm => vm.$el.querySelector('.user-avatar-link'); -const getCommitterElement = vm => vm.$el.querySelector('.committer'); -const getCommitActionsElement = vm => vm.$el.querySelector('.commit-actions'); - -describe('diffs/components/commit_item', () => { - const Component = Vue.extend(CommitItem); - const timeago = getTimeago(); - const { commit } = getDiffWithCommit(); - - let vm; - - beforeEach(() => { - vm = mountComponent(Component, { - commit: getDiffWithCommit().commit, - }); - }); - - it('renders commit title', () => { - const titleElement = getTitleElement(vm); - - expect(titleElement).toHaveAttr('href', commit.commit_url); - expect(titleElement).toHaveText(commit.title_html); - }); - - // https://gitlab.com/gitlab-org/gitlab/issues/197139 - // eslint-disable-next-line jasmine/no-disabled-tests - xit('renders commit description', () => { - const descElement = getDescElement(vm); - const descExpandElement = getDescExpandElement(vm); - - const expected = commit.description_html.replace(/
/g, ''); - - expect(trimText(descElement.innerHTML)).toEqual(trimText(expected)); - expect(descExpandElement).not.toBeNull(); - }); - - it('renders commit sha', () => { - const shaElement = getShaElement(vm); - const labelElement = shaElement.querySelector('.label'); - const buttonElement = shaElement.querySelector('button'); - - expect(labelElement.textContent).toEqual(commit.short_id); - expect(buttonElement).toHaveData('clipboard-text', commit.id); - }); - - it('renders author avatar', () => { - const avatarElement = getAvatarElement(vm); - const imgElement = avatarElement.querySelector('img'); - - expect(avatarElement).toHaveAttr('href', commit.author.web_url); - expect(imgElement).toHaveClass('s40'); - expect(imgElement).toHaveAttr('alt', commit.author.name); - expect(imgElement).toHaveAttr('src', commit.author.avatar_url); - }); - - it('renders committer text', () => { - const committerElement = getCommitterElement(vm); - const nameElement = committerElement.querySelector('a'); - - const expectTimeText = timeago.format(commit.authored_date); - const expectedText = `${commit.author.name} authored ${expectTimeText}`; - - expect(trimText(committerElement.textContent)).toEqual(expectedText); - expect(nameElement).toHaveAttr('href', commit.author.web_url); - expect(nameElement).toHaveText(commit.author.name); - expect(nameElement).toHaveClass('js-user-link'); - expect(nameElement.dataset.userId).toEqual(commit.author.id.toString()); - }); - - describe('without commit description', () => { - beforeEach(done => { - vm.commit.description_html = ''; - - vm.$nextTick() - .then(done) - .catch(done.fail); - }); - - it('hides description', () => { - const descElement = getDescElement(vm); - const descExpandElement = getDescExpandElement(vm); - - expect(descElement).toBeNull(); - expect(descExpandElement).toBeNull(); - }); - }); - - describe('with no matching user', () => { - beforeEach(done => { - vm.commit.author = null; - vm.commit.author_email = TEST_AUTHOR_EMAIL; - vm.commit.author_name = TEST_AUTHOR_NAME; - vm.commit.author_gravatar_url = TEST_AUTHOR_GRAVATAR; - - vm.$nextTick() - .then(done) - .catch(done.fail); - }); - - it('renders author avatar', () => { - const avatarElement = getAvatarElement(vm); - const imgElement = avatarElement.querySelector('img'); - - expect(avatarElement).toHaveAttr('href', `mailto:${TEST_AUTHOR_EMAIL}`); - expect(imgElement).toHaveAttr('alt', TEST_AUTHOR_NAME); - expect(imgElement).toHaveAttr('src', TEST_AUTHOR_GRAVATAR); - }); - - it('renders committer text', () => { - const committerElement = getCommitterElement(vm); - const nameElement = committerElement.querySelector('a'); - - expect(nameElement).toHaveAttr('href', `mailto:${TEST_AUTHOR_EMAIL}`); - expect(nameElement).toHaveText(TEST_AUTHOR_NAME); - }); - }); - - describe('with signature', () => { - beforeEach(done => { - vm.commit.signature_html = TEST_SIGNATURE_HTML; - - vm.$nextTick() - .then(done) - .catch(done.fail); - }); - - it('renders signature html', () => { - const actionsElement = getCommitActionsElement(vm); - - expect(actionsElement).toContainHtml(TEST_SIGNATURE_HTML); - }); - }); - - describe('with pipeline status', () => { - beforeEach(done => { - vm.commit.pipeline_status_path = TEST_PIPELINE_STATUS_PATH; - - vm.$nextTick() - .then(done) - .catch(done.fail); - }); - - it('renders pipeline status', () => { - const actionsElement = getCommitActionsElement(vm); - - expect(actionsElement).toContainElement('.ci-status-link'); - }); - }); -}); diff --git a/spec/lib/sentry/client/issue_spec.rb b/spec/lib/sentry/client/issue_spec.rb index d35e4b83d7f..62cbfbf0b30 100644 --- a/spec/lib/sentry/client/issue_spec.rb +++ b/spec/lib/sentry/client/issue_spec.rb @@ -49,7 +49,7 @@ describe Sentry::Client::Issue do it_behaves_like 'calls sentry api' it_behaves_like 'issues have correct return type', Gitlab::ErrorTracking::Error - it_behaves_like 'issues have correct length', 2 + it_behaves_like 'issues have correct length', 3 shared_examples 'has correct external_url' do context 'external_url' do @@ -184,7 +184,7 @@ describe Sentry::Client::Issue do it_behaves_like 'calls sentry api' it_behaves_like 'issues have correct return type', Gitlab::ErrorTracking::Error - it_behaves_like 'issues have correct length', 2 + it_behaves_like 'issues have correct length', 3 end context 'when cursor is present' do @@ -194,7 +194,7 @@ describe Sentry::Client::Issue do it_behaves_like 'calls sentry api' it_behaves_like 'issues have correct return type', Gitlab::ErrorTracking::Error - it_behaves_like 'issues have correct length', 2 + it_behaves_like 'issues have correct length', 3 end end |