summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/frontend/behaviors/markdown/paste_markdown_table_spec.js44
-rw-r--r--spec/frontend/monitoring/components/dashboard_spec.js9
-rw-r--r--spec/frontend/monitoring/components/dashboard_time_url_spec.js13
-rw-r--r--spec/frontend/monitoring/components/dashboard_time_window_spec.js7
-rw-r--r--spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js45
-rw-r--r--spec/frontend/monitoring/components/graph_group_spec.js7
-rw-r--r--spec/frontend/vue_shared/components/callout_spec.js7
-rw-r--r--spec/frontend/vue_shared/components/expand_button_spec.js7
-rw-r--r--spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js7
-rw-r--r--spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js7
-rw-r--r--spec/frontend/vue_shared/components/notes/system_note_spec.js5
-rw-r--r--spec/frontend/vue_shared/components/notes/timeline_entry_item_spec.js5
-rw-r--r--spec/frontend/vue_shared/components/pagination_links_spec.js5
-rw-r--r--spec/frontend/vue_shared/components/time_ago_tooltip_spec.js3
-rw-r--r--spec/frontend/vue_shared/directives/track_event_spec.js7
-rw-r--r--spec/frontend/vue_shared/droplab_dropdown_button_spec.js10
-rw-r--r--spec/frontend/vue_shared/mixins/gl_feature_flags_mixin_spec.js5
-rw-r--r--spec/javascripts/dropzone_input_spec.js8
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb14
-rw-r--r--spec/lib/sentry/client/issue_spec.rb210
-rw-r--r--spec/lib/sentry/client_spec.rb216
21 files changed, 312 insertions, 329 deletions
diff --git a/spec/frontend/behaviors/markdown/paste_markdown_table_spec.js b/spec/frontend/behaviors/markdown/paste_markdown_table_spec.js
index a8177a5ad39..a98919e2113 100644
--- a/spec/frontend/behaviors/markdown/paste_markdown_table_spec.js
+++ b/spec/frontend/behaviors/markdown/paste_markdown_table_spec.js
@@ -10,9 +10,9 @@ describe('PasteMarkdownTable', () => {
value: {
getData: jest.fn().mockImplementation(type => {
if (type === 'text/html') {
- return '<table><tr><td></td></tr></table>';
+ return '<table><tr><td>First</td><td>Second</td></tr></table>';
}
- return 'hello world';
+ return 'First\tSecond';
}),
},
});
@@ -24,39 +24,48 @@ describe('PasteMarkdownTable', () => {
it('return false when no HTML data is provided', () => {
data.types = ['text/plain'];
- expect(PasteMarkdownTable.isTable(data)).toBe(false);
+ expect(new PasteMarkdownTable(data).isTable()).toBe(false);
});
it('returns false when no text data is provided', () => {
data.types = ['text/html'];
- expect(PasteMarkdownTable.isTable(data)).toBe(false);
+ expect(new PasteMarkdownTable(data).isTable()).toBe(false);
});
it('returns true when a table is provided in both text and HTML', () => {
data.types = ['text/html', 'text/plain'];
- expect(PasteMarkdownTable.isTable(data)).toBe(true);
+ expect(new PasteMarkdownTable(data).isTable()).toBe(true);
});
it('returns false when no HTML table is included', () => {
data.types = ['text/html', 'text/plain'];
data.getData = jest.fn().mockImplementation(() => 'nothing');
- expect(PasteMarkdownTable.isTable(data)).toBe(false);
+ expect(new PasteMarkdownTable(data).isTable()).toBe(false);
});
- });
- describe('convertToTableMarkdown', () => {
- let converter;
+ it('returns false when the number of rows are not consistent', () => {
+ data.types = ['text/html', 'text/plain'];
+ data.getData = jest.fn().mockImplementation(mimeType => {
+ if (mimeType === 'text/html') {
+ return '<table><tr><td>def test<td></tr></table>';
+ }
+ return "def test\n 'hello'\n";
+ });
- beforeEach(() => {
- converter = new PasteMarkdownTable(data);
+ expect(new PasteMarkdownTable(data).isTable()).toBe(false);
});
+ });
+ describe('convertToTableMarkdown', () => {
it('returns a Markdown table', () => {
+ data.types = ['text/html', 'text/plain'];
data.getData = jest.fn().mockImplementation(type => {
- if (type === 'text/plain') {
+ if (type === 'text/html') {
+ return '<table><tr><td>First</td><td>Last</td><tr><td>John</td><td>Doe</td><tr><td>Jane</td><td>Doe</td></table>';
+ } else if (type === 'text/plain') {
return 'First\tLast\nJohn\tDoe\nJane\tDoe';
}
@@ -70,12 +79,18 @@ describe('PasteMarkdownTable', () => {
'| Jane | Doe |',
].join('\n');
+ const converter = new PasteMarkdownTable(data);
+
+ expect(converter.isTable()).toBe(true);
expect(converter.convertToTableMarkdown()).toBe(expected);
});
it('returns a Markdown table with rows normalized', () => {
+ data.types = ['text/html', 'text/plain'];
data.getData = jest.fn().mockImplementation(type => {
- if (type === 'text/plain') {
+ if (type === 'text/html') {
+ return '<table><tr><td>First</td><td>Last</td><tr><td>John</td><td>Doe</td><tr><td>Jane</td><td>/td></table>';
+ } else if (type === 'text/plain') {
return 'First\tLast\nJohn\tDoe\nJane';
}
@@ -89,6 +104,9 @@ describe('PasteMarkdownTable', () => {
'| Jane | |',
].join('\n');
+ const converter = new PasteMarkdownTable(data);
+
+ expect(converter.isTable()).toBe(true);
expect(converter.convertToTableMarkdown()).toBe(expected);
});
});
diff --git a/spec/frontend/monitoring/components/dashboard_spec.js b/spec/frontend/monitoring/components/dashboard_spec.js
index 8a10857d0ff..f77e8b61050 100644
--- a/spec/frontend/monitoring/components/dashboard_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_spec.js
@@ -10,7 +10,6 @@ import DateTimePicker from '~/monitoring/components/date_time_picker/date_time_p
import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
import { createStore } from '~/monitoring/stores';
import * as types from '~/monitoring/stores/mutation_types';
-import * as monitoringUtils from '~/monitoring/utils';
import { setupComponentStore, propsData } from '../init_utils';
import {
metricsGroupsAPIResponse,
@@ -24,13 +23,12 @@ const localVue = createLocalVue();
const expectedPanelCount = 2;
describe('Dashboard', () => {
- let DashboardComponent;
let store;
let wrapper;
let mock;
const createShallowWrapper = (props = {}, options = {}) => {
- wrapper = shallowMount(localVue.extend(DashboardComponent), {
+ wrapper = shallowMount(Dashboard, {
localVue,
sync: false,
propsData: { ...propsData, ...props },
@@ -40,7 +38,7 @@ describe('Dashboard', () => {
};
const createMountedWrapper = (props = {}, options = {}) => {
- wrapper = mount(localVue.extend(DashboardComponent), {
+ wrapper = mount(Dashboard, {
localVue,
sync: false,
propsData: { ...propsData, ...props },
@@ -51,7 +49,6 @@ describe('Dashboard', () => {
beforeEach(() => {
store = createStore();
- DashboardComponent = localVue.extend(Dashboard);
mock = new MockAdapter(axios);
});
@@ -137,7 +134,6 @@ describe('Dashboard', () => {
});
it('fetches the metrics data with proper time window', done => {
- const getTimeDiffSpy = jest.spyOn(monitoringUtils, 'getTimeDiff');
jest.spyOn(store, 'dispatch');
createMountedWrapper(
@@ -154,7 +150,6 @@ describe('Dashboard', () => {
.$nextTick()
.then(() => {
expect(store.dispatch).toHaveBeenCalled();
- expect(getTimeDiffSpy).toHaveBeenCalled();
done();
})
diff --git a/spec/frontend/monitoring/components/dashboard_time_url_spec.js b/spec/frontend/monitoring/components/dashboard_time_url_spec.js
index 8dc450cf131..747f07bcd0c 100644
--- a/spec/frontend/monitoring/components/dashboard_time_url_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_time_url_spec.js
@@ -1,10 +1,10 @@
-import { mount, createLocalVue } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
import createFlash from '~/flash';
+import MockAdapter from 'axios-mock-adapter';
import Dashboard from '~/monitoring/components/dashboard.vue';
import { createStore } from '~/monitoring/stores';
import { propsData } from '../init_utils';
-
-const localVue = createLocalVue();
+import axios from '~/lib/utils/axios_utils';
jest.mock('~/flash');
@@ -15,10 +15,10 @@ jest.mock('~/lib/utils/url_utility', () => ({
describe('dashboard invalid url parameters', () => {
let store;
let wrapper;
+ let mock;
const createMountedWrapper = (props = {}, options = {}) => {
- wrapper = mount(localVue.extend(Dashboard), {
- localVue,
+ wrapper = mount(Dashboard, {
sync: false,
propsData: { ...propsData, ...props },
store,
@@ -28,12 +28,14 @@ describe('dashboard invalid url parameters', () => {
beforeEach(() => {
store = createStore();
+ mock = new MockAdapter(axios);
});
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
+ mock.restore();
});
it('shows an error message if invalid url parameters are passed', done => {
@@ -46,7 +48,6 @@ describe('dashboard invalid url parameters', () => {
.$nextTick()
.then(() => {
expect(createFlash).toHaveBeenCalled();
-
done();
})
.catch(done.fail);
diff --git a/spec/frontend/monitoring/components/dashboard_time_window_spec.js b/spec/frontend/monitoring/components/dashboard_time_window_spec.js
index d49af6f84cb..658d3b68a76 100644
--- a/spec/frontend/monitoring/components/dashboard_time_window_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_time_window_spec.js
@@ -1,4 +1,4 @@
-import { mount, createLocalVue } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
import { GlDropdownItem } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
@@ -8,8 +8,6 @@ import { createStore } from '~/monitoring/stores';
import { propsData, setupComponentStore } from '../init_utils';
import { metricsGroupsAPIResponse, mockApiEndpoint } from '../mock_data';
-const localVue = createLocalVue();
-
jest.mock('~/lib/utils/url_utility', () => ({
getParameterValues: jest.fn().mockImplementation(param => {
if (param === 'start') return ['2019-10-01T18:27:47.000Z'];
@@ -25,8 +23,7 @@ describe('dashboard time window', () => {
let mock;
const createComponentWrapperMounted = (props = {}, options = {}) => {
- wrapper = mount(localVue.extend(Dashboard), {
- localVue,
+ wrapper = mount(Dashboard, {
sync: false,
propsData: { ...propsData, ...props },
store,
diff --git a/spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js b/spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js
index 88463d781ee..ba40ced9545 100644
--- a/spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js
+++ b/spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js
@@ -3,10 +3,8 @@ import DateTimePicker from '~/monitoring/components/date_time_picker/date_time_p
import { timeWindows } from '~/monitoring/constants';
const timeWindowsCount = Object.keys(timeWindows).length;
-const selectedTimeWindow = {
- start: '2019-10-10T07:00:00.000Z',
- end: '2019-10-13T07:00:00.000Z',
-};
+const start = '2019-10-10T07:00:00.000Z';
+const end = '2019-10-13T07:00:00.000Z';
const selectedTimeWindowText = `3 days`;
describe('DateTimePicker', () => {
@@ -28,7 +26,8 @@ describe('DateTimePicker', () => {
dateTimePicker = mount(DateTimePicker, {
propsData: {
timeWindows,
- selectedTimeWindow,
+ start,
+ end,
...props,
},
sync: false,
@@ -66,10 +65,8 @@ describe('DateTimePicker', () => {
it('renders inputs with h/m/s truncated if its all 0s', done => {
createComponent({
- selectedTimeWindow: {
- start: '2019-10-10T00:00:00.000Z',
- end: '2019-10-14T00:10:00.000Z',
- },
+ start: '2019-10-10T00:00:00.000Z',
+ end: '2019-10-14T00:10:00.000Z',
});
dateTimePicker.vm.$nextTick(() => {
expect(dateTimePicker.find('#custom-time-from').element.value).toBe('2019-10-10');
@@ -98,8 +95,10 @@ describe('DateTimePicker', () => {
});
});
- it('renders a disabled apply button on load', () => {
- createComponent();
+ it('renders a disabled apply button on wrong input', () => {
+ createComponent({
+ start: 'invalid-input-date',
+ });
expect(applyButtonElement().getAttribute('disabled')).toBe('disabled');
});
@@ -131,29 +130,29 @@ describe('DateTimePicker', () => {
fillInputAndBlur('#custom-time-from', '2019-10-01')
.then(() => fillInputAndBlur('#custom-time-to', '2019-10-19'))
.then(() => {
- dateTimePicker.vm.$nextTick(() => {
- expect(applyButtonElement().getAttribute('disabled')).toBeNull();
- done();
- });
+ expect(applyButtonElement().getAttribute('disabled')).toBeNull();
+ done();
})
- .catch(done);
+ .catch(done.fail);
});
- it('returns an object when apply is clicked', done => {
+ it('emits dates in an object when apply is clicked', done => {
createComponent();
fillInputAndBlur('#custom-time-from', '2019-10-01')
.then(() => fillInputAndBlur('#custom-time-to', '2019-10-19'))
.then(() => {
- jest.spyOn(dateTimePicker.vm, '$emit');
applyButtonElement().click();
- expect(dateTimePicker.vm.$emit).toHaveBeenCalledWith('onApply', {
- end: '2019-10-19T00:00:00Z',
- start: '2019-10-01T00:00:00Z',
- });
+ expect(dateTimePicker.emitted().apply).toHaveLength(1);
+ expect(dateTimePicker.emitted().apply[0]).toEqual([
+ {
+ end: '2019-10-19T00:00:00Z',
+ start: '2019-10-01T00:00:00Z',
+ },
+ ]);
done();
})
- .catch(done);
+ .catch(done.fail);
});
it('hides the popover with cancel button', done => {
diff --git a/spec/frontend/monitoring/components/graph_group_spec.js b/spec/frontend/monitoring/components/graph_group_spec.js
index 43ca17c3cbc..edd08cdb4c9 100644
--- a/spec/frontend/monitoring/components/graph_group_spec.js
+++ b/spec/frontend/monitoring/components/graph_group_spec.js
@@ -1,9 +1,7 @@
-import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
import GraphGroup from '~/monitoring/components/graph_group.vue';
import Icon from '~/vue_shared/components/icon.vue';
-const localVue = createLocalVue();
-
describe('Graph group component', () => {
let wrapper;
@@ -12,10 +10,9 @@ describe('Graph group component', () => {
const findCaretIcon = () => wrapper.find(Icon);
const createComponent = propsData => {
- wrapper = shallowMount(localVue.extend(GraphGroup), {
+ wrapper = shallowMount(GraphGroup, {
propsData,
sync: false,
- localVue,
});
};
diff --git a/spec/frontend/vue_shared/components/callout_spec.js b/spec/frontend/vue_shared/components/callout_spec.js
index 91208dfb31a..7c9bb6b4650 100644
--- a/spec/frontend/vue_shared/components/callout_spec.js
+++ b/spec/frontend/vue_shared/components/callout_spec.js
@@ -1,17 +1,14 @@
-import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
import Callout from '~/vue_shared/components/callout.vue';
const TEST_MESSAGE = 'This is a callout message!';
const TEST_SLOT = '<button>This is a callout slot!</button>';
-const localVue = createLocalVue();
-
describe('Callout Component', () => {
let wrapper;
const factory = options => {
- wrapper = shallowMount(localVue.extend(Callout), {
- localVue,
+ wrapper = shallowMount(Callout, {
...options,
});
};
diff --git a/spec/frontend/vue_shared/components/expand_button_spec.js b/spec/frontend/vue_shared/components/expand_button_spec.js
index e0893d02843..3b1c8f6219c 100644
--- a/spec/frontend/vue_shared/components/expand_button_spec.js
+++ b/spec/frontend/vue_shared/components/expand_button_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import { mount, createLocalVue } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
import ExpandButton from '~/vue_shared/components/expand_button.vue';
const text = {
@@ -14,10 +14,7 @@ describe('Expand button', () => {
const expanderAppendEl = () => wrapper.find('.js-text-expander-append');
const factory = (options = {}) => {
- const localVue = createLocalVue();
-
- wrapper = mount(localVue.extend(ExpandButton), {
- localVue,
+ wrapper = mount(ExpandButton, {
...options,
});
};
diff --git a/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js b/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js
index 21d05471d51..85cd90d2f8c 100644
--- a/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js
+++ b/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import { mount, createLocalVue } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
import { formatDate } from '~/lib/utils/datetime_utility';
import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue';
import {
@@ -29,10 +29,7 @@ describe('RelatedIssuableItem', () => {
};
beforeEach(() => {
- const localVue = createLocalVue();
-
- wrapper = mount(localVue.extend(RelatedIssuableItem), {
- localVue,
+ wrapper = mount(RelatedIssuableItem, {
slots,
sync: false,
attachToDocument: true,
diff --git a/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js b/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js
index 71f9b5e3244..3d42c02ebb6 100644
--- a/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js
@@ -1,9 +1,7 @@
import { GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
import SuggestionDiffHeader from '~/vue_shared/components/markdown/suggestion_diff_header.vue';
-const localVue = createLocalVue();
-
const DEFAULT_PROPS = {
canApply: true,
isApplied: false,
@@ -14,12 +12,11 @@ describe('Suggestion Diff component', () => {
let wrapper;
const createComponent = props => {
- wrapper = shallowMount(localVue.extend(SuggestionDiffHeader), {
+ wrapper = shallowMount(SuggestionDiffHeader, {
propsData: {
...DEFAULT_PROPS,
...props,
},
- localVue,
sync: false,
attachToDocument: true,
});
diff --git a/spec/frontend/vue_shared/components/notes/system_note_spec.js b/spec/frontend/vue_shared/components/notes/system_note_spec.js
index 603c37c6c49..080dd778e29 100644
--- a/spec/frontend/vue_shared/components/notes/system_note_spec.js
+++ b/spec/frontend/vue_shared/components/notes/system_note_spec.js
@@ -1,12 +1,10 @@
-import { createLocalVue, mount } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
import IssueSystemNote from '~/vue_shared/components/notes/system_note.vue';
import createStore from '~/notes/stores';
import initMRPopovers from '~/mr_popover/index';
jest.mock('~/mr_popover/index', () => jest.fn());
-const localVue = createLocalVue();
-
describe('system note component', () => {
let vm;
let props;
@@ -34,7 +32,6 @@ describe('system note component', () => {
vm = mount(IssueSystemNote, {
store,
- localVue,
propsData: props,
attachToDocument: true,
sync: false,
diff --git a/spec/frontend/vue_shared/components/notes/timeline_entry_item_spec.js b/spec/frontend/vue_shared/components/notes/timeline_entry_item_spec.js
index be6c58f0683..f73d3edec5d 100644
--- a/spec/frontend/vue_shared/components/notes/timeline_entry_item_spec.js
+++ b/spec/frontend/vue_shared/components/notes/timeline_entry_item_spec.js
@@ -1,14 +1,11 @@
-import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
describe(`TimelineEntryItem`, () => {
let wrapper;
const factory = (options = {}) => {
- const localVue = createLocalVue();
-
wrapper = shallowMount(TimelineEntryItem, {
- localVue,
...options,
});
};
diff --git a/spec/frontend/vue_shared/components/pagination_links_spec.js b/spec/frontend/vue_shared/components/pagination_links_spec.js
index efa5825d92f..3c53cda45f5 100644
--- a/spec/frontend/vue_shared/components/pagination_links_spec.js
+++ b/spec/frontend/vue_shared/components/pagination_links_spec.js
@@ -1,4 +1,4 @@
-import { mount, createLocalVue } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
import { GlPagination } from '@gitlab/ui';
import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
import {
@@ -10,8 +10,6 @@ import {
LABEL_LAST_PAGE,
} from '~/vue_shared/components/pagination/constants';
-const localVue = createLocalVue();
-
describe('Pagination links component', () => {
const pageInfo = {
page: 3,
@@ -38,7 +36,6 @@ describe('Pagination links component', () => {
change: changeMock,
pageInfo,
},
- localVue,
sync: false,
});
};
diff --git a/spec/frontend/vue_shared/components/time_ago_tooltip_spec.js b/spec/frontend/vue_shared/components/time_ago_tooltip_spec.js
index ebba0cc4ad4..49591c3ce1c 100644
--- a/spec/frontend/vue_shared/components/time_ago_tooltip_spec.js
+++ b/spec/frontend/vue_shared/components/time_ago_tooltip_spec.js
@@ -1,4 +1,4 @@
-import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { formatDate, getTimeago } from '~/lib/utils/datetime_utility';
@@ -10,7 +10,6 @@ describe('Time ago with tooltip component', () => {
attachToDocument: true,
sync: false,
propsData,
- localVue: createLocalVue(),
});
};
const timestamp = '2017-05-08T14:57:39.781Z';
diff --git a/spec/frontend/vue_shared/directives/track_event_spec.js b/spec/frontend/vue_shared/directives/track_event_spec.js
index c9b0520ab2c..e1009e5079a 100644
--- a/spec/frontend/vue_shared/directives/track_event_spec.js
+++ b/spec/frontend/vue_shared/directives/track_event_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
import Tracking from '~/tracking';
import TrackEvent from '~/vue_shared/directives/track_event';
@@ -17,15 +17,12 @@ const Component = Vue.component('dummy-element', {
template: '<button id="trackable" v-track-event="trackingOptions"></button>',
});
-const localVue = createLocalVue();
let wrapper;
let button;
describe('Error Tracking directive', () => {
beforeEach(() => {
- wrapper = shallowMount(localVue.extend(Component), {
- localVue,
- });
+ wrapper = shallowMount(Component, {});
button = wrapper.find('#trackable');
});
diff --git a/spec/frontend/vue_shared/droplab_dropdown_button_spec.js b/spec/frontend/vue_shared/droplab_dropdown_button_spec.js
index 22295721328..e57c730ecee 100644
--- a/spec/frontend/vue_shared/droplab_dropdown_button_spec.js
+++ b/spec/frontend/vue_shared/droplab_dropdown_button_spec.js
@@ -1,4 +1,4 @@
-import { mount, createLocalVue } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
import DroplabDropdownButton from '~/vue_shared/components/droplab_dropdown_button.vue';
@@ -18,11 +18,8 @@ const createComponent = ({
dropdownClass = '',
actions = mockActions,
defaultAction = 0,
-}) => {
- const localVue = createLocalVue();
-
- return mount(DroplabDropdownButton, {
- localVue,
+}) =>
+ mount(DroplabDropdownButton, {
propsData: {
size,
dropdownClass,
@@ -30,7 +27,6 @@ const createComponent = ({
defaultAction,
},
});
-};
describe('DroplabDropdownButton', () => {
let wrapper;
diff --git a/spec/frontend/vue_shared/mixins/gl_feature_flags_mixin_spec.js b/spec/frontend/vue_shared/mixins/gl_feature_flags_mixin_spec.js
index a3e3270a4e8..3ce12caf95a 100644
--- a/spec/frontend/vue_shared/mixins/gl_feature_flags_mixin_spec.js
+++ b/spec/frontend/vue_shared/mixins/gl_feature_flags_mixin_spec.js
@@ -1,8 +1,6 @@
-import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-const localVue = createLocalVue();
-
describe('GitLab Feature Flags Mixin', () => {
let wrapper;
@@ -20,7 +18,6 @@ describe('GitLab Feature Flags Mixin', () => {
};
wrapper = shallowMount(component, {
- localVue,
provide: {
glFeatures: { ...(gon.features || {}) },
},
diff --git a/spec/javascripts/dropzone_input_spec.js b/spec/javascripts/dropzone_input_spec.js
index 44a11097815..6f6f20ccca2 100644
--- a/spec/javascripts/dropzone_input_spec.js
+++ b/spec/javascripts/dropzone_input_spec.js
@@ -39,17 +39,17 @@ describe('dropzone_input', () => {
const event = $.Event('paste');
const origEvent = new Event('paste');
const pasteData = new DataTransfer();
- pasteData.setData('text/plain', 'hello world');
- pasteData.setData('text/html', '<table></table>');
+ pasteData.setData('text/plain', 'Hello World');
+ pasteData.setData('text/html', '<table><tr><td>Hello World</td></tr></table>');
origEvent.clipboardData = pasteData;
event.originalEvent = origEvent;
- spyOn(PasteMarkdownTable, 'isTable').and.callThrough();
+ spyOn(PasteMarkdownTable.prototype, 'isTable').and.callThrough();
spyOn(PasteMarkdownTable.prototype, 'convertToTableMarkdown').and.callThrough();
$('.js-gfm-input').trigger(event);
- expect(PasteMarkdownTable.isTable).toHaveBeenCalled();
+ expect(PasteMarkdownTable.prototype.isTable).toHaveBeenCalled();
expect(PasteMarkdownTable.prototype.convertToTableMarkdown).toHaveBeenCalled();
});
});
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 3a56462ec1b..cf1dacd088e 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -17,8 +17,8 @@ describe Gitlab::UsageData do
create(:service, project: projects[0], type: 'SlackSlashCommandsService', active: true)
create(:service, project: projects[1], type: 'SlackService', active: true)
create(:service, project: projects[2], type: 'SlackService', active: true)
- create(:service, project: projects[2], type: 'MattermostService', active: true)
- create(:service, project: projects[2], type: 'JenkinsService', active: true)
+ create(:service, project: projects[2], type: 'MattermostService', active: false)
+ create(:service, project: projects[2], type: 'MattermostService', active: true, template: true)
create(:service, project: projects[2], type: 'CustomIssueTrackerService', active: true)
create(:project_error_tracking_setting, project: projects[0])
create(:project_error_tracking_setting, project: projects[1], enabled: false)
@@ -168,13 +168,15 @@ describe Gitlab::UsageData do
pool_repositories
projects
projects_imported_from_github
+ projects_asana_active
projects_jira_active
projects_jira_server_active
projects_jira_cloud_active
projects_slack_notifications_active
projects_slack_slash_active
+ projects_slack_active
+ projects_slack_slash_commands_active
projects_custom_issue_tracker_active
- projects_jenkins_active
projects_mattermost_active
projects_prometheus_active
projects_with_repositories_enabled
@@ -203,15 +205,17 @@ describe Gitlab::UsageData do
count_data = subject[:counts]
expect(count_data[:projects]).to eq(4)
+ expect(count_data[:projects_asana_active]).to eq(0)
expect(count_data[:projects_prometheus_active]).to eq(1)
expect(count_data[:projects_jira_active]).to eq(4)
expect(count_data[:projects_jira_server_active]).to eq(2)
expect(count_data[:projects_jira_cloud_active]).to eq(2)
expect(count_data[:projects_slack_notifications_active]).to eq(2)
expect(count_data[:projects_slack_slash_active]).to eq(1)
+ expect(count_data[:projects_slack_active]).to eq(2)
+ expect(count_data[:projects_slack_slash_commands_active]).to eq(1)
expect(count_data[:projects_custom_issue_tracker_active]).to eq(1)
- expect(count_data[:projects_jenkins_active]).to eq(1)
- expect(count_data[:projects_mattermost_active]).to eq(1)
+ expect(count_data[:projects_mattermost_active]).to eq(0)
expect(count_data[:projects_with_repositories_enabled]).to eq(3)
expect(count_data[:projects_with_error_tracking_enabled]).to eq(1)
expect(count_data[:issues_created_from_gitlab_error_tracking_ui]).to eq(1)
diff --git a/spec/lib/sentry/client/issue_spec.rb b/spec/lib/sentry/client/issue_spec.rb
index 20665c59a8d..b5ee5063e86 100644
--- a/spec/lib/sentry/client/issue_spec.rb
+++ b/spec/lib/sentry/client/issue_spec.rb
@@ -8,6 +8,216 @@ describe Sentry::Client::Issue do
let(:token) { 'test-token' }
let(:client) { Sentry::Client.new(sentry_url, token) }
+ describe '#list_issues' do
+ shared_examples 'issues have correct return type' do |klass|
+ it "returns objects of type #{klass}" do
+ expect(subject[:issues]).to all( be_a(klass) )
+ end
+ end
+
+ shared_examples 'issues have correct length' do |length|
+ it { expect(subject[:issues].length).to eq(length) }
+ end
+
+ let(:issues_sample_response) do
+ Gitlab::Utils.deep_indifferent_access(
+ JSON.parse(fixture_file('sentry/issues_sample_response.json'))
+ )
+ end
+
+ let(:default_httparty_options) do
+ {
+ follow_redirects: false,
+ headers: { "Authorization" => "Bearer test-token" }
+ }
+ end
+
+ let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
+ let(:issue_status) { 'unresolved' }
+ let(:limit) { 20 }
+ let(:search_term) { '' }
+ let(:cursor) { nil }
+ let(:sort) { 'last_seen' }
+ let(:sentry_api_response) { issues_sample_response }
+ let(:sentry_request_url) { sentry_url + '/issues/?limit=20&query=is:unresolved' }
+ let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response) }
+
+ subject { client.list_issues(issue_status: issue_status, limit: limit, search_term: search_term, sort: sort, cursor: cursor) }
+
+ it_behaves_like 'calls sentry api'
+
+ it_behaves_like 'issues have correct return type', Gitlab::ErrorTracking::Error
+ it_behaves_like 'issues have correct length', 1
+
+ shared_examples 'has correct external_url' do
+ context 'external_url' do
+ it 'is constructed correctly' do
+ expect(subject[:issues][0].external_url).to eq('https://sentrytest.gitlab.com/sentry-org/sentry-project/issues/11')
+ end
+ end
+ end
+
+ context 'when response has a pagination info' do
+ let(:headers) do
+ {
+ link: '<https://sentrytest.gitlab.com>; rel="previous"; results="true"; cursor="1573556671000:0:1", <https://sentrytest.gitlab.com>; rel="next"; results="true"; cursor="1572959139000:0:0"'
+ }
+ end
+ let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response, headers: headers) }
+
+ it 'parses the pagination' do
+ expect(subject[:pagination]).to eq(
+ 'previous' => { 'cursor' => '1573556671000:0:1' },
+ 'next' => { 'cursor' => '1572959139000:0:0' }
+ )
+ end
+ end
+
+ context 'error object created from sentry response' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:error_object, :sentry_response) do
+ :id | :id
+ :first_seen | :firstSeen
+ :last_seen | :lastSeen
+ :title | :title
+ :type | :type
+ :user_count | :userCount
+ :count | :count
+ :message | [:metadata, :value]
+ :culprit | :culprit
+ :short_id | :shortId
+ :status | :status
+ :frequency | [:stats, '24h']
+ :project_id | [:project, :id]
+ :project_name | [:project, :name]
+ :project_slug | [:project, :slug]
+ end
+
+ with_them do
+ it { expect(subject[:issues][0].public_send(error_object)).to eq(sentry_api_response[0].dig(*sentry_response)) }
+ end
+
+ it_behaves_like 'has correct external_url'
+ end
+
+ context 'redirects' do
+ let(:sentry_api_url) { sentry_url + '/issues/?limit=20&query=is:unresolved' }
+
+ it_behaves_like 'no Sentry redirects'
+ end
+
+ # Sentry API returns 404 if there are extra slashes in the URL!
+ context 'extra slashes in URL' do
+ let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects//sentry-org/sentry-project/' }
+
+ let(:sentry_request_url) do
+ 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/' \
+ 'issues/?limit=20&query=is:unresolved'
+ end
+
+ it 'removes extra slashes in api url' do
+ expect(client.url).to eq(sentry_url)
+ expect(Gitlab::HTTP).to receive(:get).with(
+ URI('https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/issues/'),
+ anything
+ ).and_call_original
+
+ subject
+
+ expect(sentry_api_request).to have_been_requested
+ end
+ end
+
+ context 'requests with sort parameter in sentry api' do
+ let(:sentry_request_url) do
+ 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/' \
+ 'issues/?limit=20&query=is:unresolved&sort=freq'
+ end
+ let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response) }
+
+ subject { client.list_issues(issue_status: issue_status, limit: limit, sort: 'frequency') }
+
+ it 'calls the sentry api with sort params' do
+ expect(Gitlab::HTTP).to receive(:get).with(
+ URI("#{sentry_url}/issues/"),
+ default_httparty_options.merge(query: { limit: 20, query: "is:unresolved", sort: "freq" })
+ ).and_call_original
+
+ subject
+
+ expect(sentry_api_request).to have_been_requested
+ end
+ end
+
+ context 'with invalid sort params' do
+ subject { client.list_issues(issue_status: issue_status, limit: limit, sort: 'fish') }
+
+ it 'throws an error' do
+ expect { subject }.to raise_error(Sentry::Client::BadRequestError, 'Invalid value for sort param')
+ end
+ end
+
+ context 'Older sentry versions where keys are not present' do
+ let(:sentry_api_response) do
+ issues_sample_response[0...1].map do |issue|
+ issue[:project].delete(:id)
+ issue
+ end
+ end
+
+ it_behaves_like 'calls sentry api'
+
+ it_behaves_like 'issues have correct return type', Gitlab::ErrorTracking::Error
+ it_behaves_like 'issues have correct length', 1
+
+ it_behaves_like 'has correct external_url'
+ end
+
+ context 'essential keys missing in API response' do
+ let(:sentry_api_response) do
+ issues_sample_response[0...1].map do |issue|
+ issue.except(:id)
+ end
+ end
+
+ it 'raises exception' do
+ expect { subject }.to raise_error(Sentry::Client::MissingKeysError, 'Sentry API response is missing keys. key not found: "id"')
+ end
+ end
+
+ context 'sentry api response too large' do
+ it 'raises exception' do
+ deep_size = double('Gitlab::Utils::DeepSize', valid?: false)
+ allow(Gitlab::Utils::DeepSize).to receive(:new).with(sentry_api_response).and_return(deep_size)
+
+ expect { subject }.to raise_error(Sentry::Client::ResponseInvalidSizeError, 'Sentry API response is too big. Limit is 1 MB.')
+ end
+ end
+
+ it_behaves_like 'maps Sentry exceptions'
+
+ context 'when search term is present' do
+ let(:search_term) { 'NoMethodError' }
+ let(:sentry_request_url) { "#{sentry_url}/issues/?limit=20&query=is:unresolved NoMethodError" }
+
+ it_behaves_like 'calls sentry api'
+
+ it_behaves_like 'issues have correct return type', Gitlab::ErrorTracking::Error
+ it_behaves_like 'issues have correct length', 1
+ end
+
+ context 'when cursor is present' do
+ let(:cursor) { '1572959139000:0:0' }
+ let(:sentry_request_url) { "#{sentry_url}/issues/?limit=20&cursor=#{cursor}&query=is:unresolved" }
+
+ it_behaves_like 'calls sentry api'
+
+ it_behaves_like 'issues have correct return type', Gitlab::ErrorTracking::Error
+ it_behaves_like 'issues have correct length', 1
+ end
+ end
+
describe '#issue_details' do
let(:issue_sample_response) do
Gitlab::Utils.deep_indifferent_access(
diff --git a/spec/lib/sentry/client_spec.rb b/spec/lib/sentry/client_spec.rb
index 8500f67b8e9..409e8be3198 100644
--- a/spec/lib/sentry/client_spec.rb
+++ b/spec/lib/sentry/client_spec.rb
@@ -3,219 +3,13 @@
require 'spec_helper'
describe Sentry::Client do
- include SentryClientHelpers
-
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
let(:token) { 'test-token' }
- let(:default_httparty_options) do
- {
- follow_redirects: false,
- headers: { "Authorization" => "Bearer test-token" }
- }
- end
-
- subject(:client) { described_class.new(sentry_url, token) }
-
- shared_examples 'issues has correct return type' do |klass|
- it "returns objects of type #{klass}" do
- expect(subject[:issues]).to all( be_a(klass) )
- end
- end
-
- shared_examples 'issues has correct length' do |length|
- it { expect(subject[:issues].length).to eq(length) }
- end
-
- describe '#list_issues' do
- let(:issues_sample_response) do
- Gitlab::Utils.deep_indifferent_access(
- JSON.parse(fixture_file('sentry/issues_sample_response.json'))
- )
- end
-
- let(:issue_status) { 'unresolved' }
- let(:limit) { 20 }
- let(:search_term) { '' }
- let(:cursor) { nil }
- let(:sort) { 'last_seen' }
- let(:sentry_api_response) { issues_sample_response }
- let(:sentry_request_url) { sentry_url + '/issues/?limit=20&query=is:unresolved' }
-
- let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response) }
-
- subject { client.list_issues(issue_status: issue_status, limit: limit, search_term: search_term, sort: sort, cursor: cursor) }
-
- it_behaves_like 'calls sentry api'
-
- it_behaves_like 'issues has correct return type', Gitlab::ErrorTracking::Error
- it_behaves_like 'issues has correct length', 1
-
- shared_examples 'has correct external_url' do
- context 'external_url' do
- it 'is constructed correctly' do
- expect(subject[:issues][0].external_url).to eq('https://sentrytest.gitlab.com/sentry-org/sentry-project/issues/11')
- end
- end
- end
-
- context 'when response has a pagination info' do
- let(:headers) do
- {
- link: '<https://sentrytest.gitlab.com>; rel="previous"; results="true"; cursor="1573556671000:0:1", <https://sentrytest.gitlab.com>; rel="next"; results="true"; cursor="1572959139000:0:0"'
- }
- end
- let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response, headers: headers) }
-
- it 'parses the pagination' do
- expect(subject[:pagination]).to eq(
- 'previous' => { 'cursor' => '1573556671000:0:1' },
- 'next' => { 'cursor' => '1572959139000:0:0' }
- )
- end
- end
-
- context 'error object created from sentry response' do
- using RSpec::Parameterized::TableSyntax
-
- where(:error_object, :sentry_response) do
- :id | :id
- :first_seen | :firstSeen
- :last_seen | :lastSeen
- :title | :title
- :type | :type
- :user_count | :userCount
- :count | :count
- :message | [:metadata, :value]
- :culprit | :culprit
- :short_id | :shortId
- :status | :status
- :frequency | [:stats, '24h']
- :project_id | [:project, :id]
- :project_name | [:project, :name]
- :project_slug | [:project, :slug]
- end
-
- with_them do
- it { expect(subject[:issues][0].public_send(error_object)).to eq(sentry_api_response[0].dig(*sentry_response)) }
- end
-
- it_behaves_like 'has correct external_url'
- end
-
- context 'redirects' do
- let(:sentry_api_url) { sentry_url + '/issues/?limit=20&query=is:unresolved' }
-
- it_behaves_like 'no Sentry redirects'
- end
-
- # Sentry API returns 404 if there are extra slashes in the URL!
- context 'extra slashes in URL' do
- let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects//sentry-org/sentry-project/' }
-
- let(:sentry_request_url) do
- 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/' \
- 'issues/?limit=20&query=is:unresolved'
- end
-
- it 'removes extra slashes in api url' do
- expect(client.url).to eq(sentry_url)
- expect(Gitlab::HTTP).to receive(:get).with(
- URI('https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/issues/'),
- anything
- ).and_call_original
-
- subject
-
- expect(sentry_api_request).to have_been_requested
- end
- end
-
- context 'requests with sort parameter in sentry api' do
- let(:sentry_request_url) do
- 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/' \
- 'issues/?limit=20&query=is:unresolved&sort=freq'
- end
- let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response) }
-
- subject { client.list_issues(issue_status: issue_status, limit: limit, sort: 'frequency') }
-
- it 'calls the sentry api with sort params' do
- expect(Gitlab::HTTP).to receive(:get).with(
- URI("#{sentry_url}/issues/"),
- default_httparty_options.merge(query: { limit: 20, query: "is:unresolved", sort: "freq" })
- ).and_call_original
-
- subject
-
- expect(sentry_api_request).to have_been_requested
- end
- end
-
- context 'with invalid sort params' do
- subject { client.list_issues(issue_status: issue_status, limit: limit, sort: 'fish') }
-
- it 'throws an error' do
- expect { subject }.to raise_error(Sentry::Client::BadRequestError, 'Invalid value for sort param')
- end
- end
-
- context 'Older sentry versions where keys are not present' do
- let(:sentry_api_response) do
- issues_sample_response[0...1].map do |issue|
- issue[:project].delete(:id)
- issue
- end
- end
-
- it_behaves_like 'calls sentry api'
-
- it_behaves_like 'issues has correct return type', Gitlab::ErrorTracking::Error
- it_behaves_like 'issues has correct length', 1
-
- it_behaves_like 'has correct external_url'
- end
-
- context 'essential keys missing in API response' do
- let(:sentry_api_response) do
- issues_sample_response[0...1].map do |issue|
- issue.except(:id)
- end
- end
-
- it 'raises exception' do
- expect { subject }.to raise_error(Sentry::Client::MissingKeysError, 'Sentry API response is missing keys. key not found: "id"')
- end
- end
-
- context 'sentry api response too large' do
- it 'raises exception' do
- deep_size = double('Gitlab::Utils::DeepSize', valid?: false)
- allow(Gitlab::Utils::DeepSize).to receive(:new).with(sentry_api_response).and_return(deep_size)
-
- expect { subject }.to raise_error(Sentry::Client::ResponseInvalidSizeError, 'Sentry API response is too big. Limit is 1 MB.')
- end
- end
-
- it_behaves_like 'maps Sentry exceptions'
-
- context 'when search term is present' do
- let(:search_term) { 'NoMethodError' }
- let(:sentry_request_url) { "#{sentry_url}/issues/?limit=20&query=is:unresolved NoMethodError" }
-
- it_behaves_like 'calls sentry api'
-
- it_behaves_like 'issues has correct return type', Gitlab::ErrorTracking::Error
- it_behaves_like 'issues has correct length', 1
- end
-
- context 'when cursor is present' do
- let(:cursor) { '1572959139000:0:0' }
- let(:sentry_request_url) { "#{sentry_url}/issues/?limit=20&cursor=#{cursor}&query=is:unresolved" }
- it_behaves_like 'calls sentry api'
+ subject { Sentry::Client.new(sentry_url, token) }
- it_behaves_like 'issues has correct return type', Gitlab::ErrorTracking::Error
- it_behaves_like 'issues has correct length', 1
- end
- end
+ it { is_expected.to respond_to :projects }
+ it { is_expected.to respond_to :list_issues }
+ it { is_expected.to respond_to :issue_details }
+ it { is_expected.to respond_to :issue_latest_event }
end