Add integrated setting to error tracking

parent 6390a219
<script> <script>
import { GlButton, GlFormGroup, GlFormCheckbox } from '@gitlab/ui'; import { GlButton, GlFormGroup, GlFormCheckbox, GlFormRadioGroup, GlFormRadio } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import ErrorTrackingForm from './error_tracking_form.vue'; import ErrorTrackingForm from './error_tracking_form.vue';
import ProjectDropdown from './project_dropdown.vue'; import ProjectDropdown from './project_dropdown.vue';
...@@ -10,6 +10,8 @@ export default { ...@@ -10,6 +10,8 @@ export default {
GlButton, GlButton,
GlFormCheckbox, GlFormCheckbox,
GlFormGroup, GlFormGroup,
GlFormRadioGroup,
GlFormRadio,
ProjectDropdown, ProjectDropdown,
}, },
props: { props: {
...@@ -22,6 +24,10 @@ export default { ...@@ -22,6 +24,10 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
initialIntegrated: {
type: String,
required: true,
},
initialProject: { initialProject: {
type: String, type: String,
required: false, required: false,
...@@ -49,12 +55,20 @@ export default { ...@@ -49,12 +55,20 @@ export default {
'isProjectInvalid', 'isProjectInvalid',
'projectSelectionLabel', 'projectSelectionLabel',
]), ]),
...mapState(['enabled', 'projects', 'selectedProject', 'settingsLoading', 'token']), ...mapState([
'enabled',
'integrated',
'projects',
'selectedProject',
'settingsLoading',
'token',
]),
}, },
created() { created() {
this.setInitialState({ this.setInitialState({
apiHost: this.initialApiHost, apiHost: this.initialApiHost,
enabled: this.initialEnabled, enabled: this.initialEnabled,
integrated: this.initialIntegrated,
project: this.initialProject, project: this.initialProject,
token: this.initialToken, token: this.initialToken,
listProjectsEndpoint: this.listProjectsEndpoint, listProjectsEndpoint: this.listProjectsEndpoint,
...@@ -62,7 +76,13 @@ export default { ...@@ -62,7 +76,13 @@ export default {
}); });
}, },
methods: { methods: {
...mapActions(['setInitialState', 'updateEnabled', 'updateSelectedProject', 'updateSettings']), ...mapActions([
'setInitialState',
'updateEnabled',
'updateIntegrated',
'updateSelectedProject',
'updateSettings',
]),
handleSubmit() { handleSubmit() {
this.updateSettings(); this.updateSettings();
}, },
...@@ -76,14 +96,30 @@ export default { ...@@ -76,14 +96,30 @@ export default {
:label="s__('ErrorTracking|Enable error tracking')" :label="s__('ErrorTracking|Enable error tracking')"
label-for="error-tracking-enabled" label-for="error-tracking-enabled"
> >
<gl-form-checkbox <gl-form-checkbox id="error-tracking-enabled" :checked="enabled" @change="updateEnabled">
id="error-tracking-enabled"
:checked="enabled"
@change="updateEnabled($event)"
>
{{ s__('ErrorTracking|Active') }} {{ s__('ErrorTracking|Active') }}
</gl-form-checkbox> </gl-form-checkbox>
</gl-form-group> </gl-form-group>
<gl-form-group
:label="s__('ErrorTracking|Error tracking backend')"
data-testid="tracking-backend-settings"
>
<gl-form-radio-group name="explicit" :checked="integrated" @change="updateIntegrated">
<gl-form-radio name="error-tracking-integrated" :value="false">
{{ __('Sentry') }}
<template #help>
{{ __('Requires you to deploy or set up cloud-hosted Sentry.') }}
</template>
</gl-form-radio>
<gl-form-radio name="error-tracking-integrated" :value="true">
{{ __('GitLab') }}
<template #help>
{{ __('Uses GitLab as a lightweight alternative to Sentry.') }}
</template>
</gl-form-radio>
</gl-form-radio-group>
</gl-form-group>
<div v-if="!integrated" class="js-sentry-setting-form" data-testid="sentry-setting-form">
<error-tracking-form /> <error-tracking-form />
<div class="form-group"> <div class="form-group">
<project-dropdown <project-dropdown
...@@ -98,6 +134,7 @@ export default { ...@@ -98,6 +134,7 @@ export default {
@select-project="updateSelectedProject" @select-project="updateSelectedProject"
/> />
</div> </div>
</div>
<gl-button <gl-button
:disabled="settingsLoading" :disabled="settingsLoading"
class="js-error-tracking-button" class="js-error-tracking-button"
......
...@@ -5,7 +5,15 @@ import createStore from './store'; ...@@ -5,7 +5,15 @@ import createStore from './store';
export default () => { export default () => {
const formContainerEl = document.querySelector('.js-error-tracking-form'); const formContainerEl = document.querySelector('.js-error-tracking-form');
const { const {
dataset: { apiHost, enabled, project, token, listProjectsEndpoint, operationsSettingsEndpoint }, dataset: {
apiHost,
enabled,
integrated,
project,
token,
listProjectsEndpoint,
operationsSettingsEndpoint,
},
} = formContainerEl; } = formContainerEl;
return new Vue({ return new Vue({
...@@ -16,6 +24,7 @@ export default () => { ...@@ -16,6 +24,7 @@ export default () => {
props: { props: {
initialApiHost: apiHost, initialApiHost: apiHost,
initialEnabled: enabled, initialEnabled: enabled,
initialIntegrated: integrated,
initialProject: project, initialProject: project,
initialToken: token, initialToken: token,
listProjectsEndpoint, listProjectsEndpoint,
......
...@@ -79,6 +79,10 @@ export const updateEnabled = ({ commit }, enabled) => { ...@@ -79,6 +79,10 @@ export const updateEnabled = ({ commit }, enabled) => {
commit(types.UPDATE_ENABLED, enabled); commit(types.UPDATE_ENABLED, enabled);
}; };
export const updateIntegrated = ({ commit }, integrated) => {
commit(types.UPDATE_INTEGRATED, integrated);
};
export const updateToken = ({ commit }, token) => { export const updateToken = ({ commit }, token) => {
commit(types.UPDATE_TOKEN, token); commit(types.UPDATE_TOKEN, token);
commit(types.RESET_CONNECT); commit(types.RESET_CONNECT);
......
...@@ -6,6 +6,7 @@ export const UPDATE_API_HOST = 'UPDATE_API_HOST'; ...@@ -6,6 +6,7 @@ export const UPDATE_API_HOST = 'UPDATE_API_HOST';
export const UPDATE_CONNECT_ERROR = 'UPDATE_CONNECT_ERROR'; export const UPDATE_CONNECT_ERROR = 'UPDATE_CONNECT_ERROR';
export const UPDATE_CONNECT_SUCCESS = 'UPDATE_CONNECT_SUCCESS'; export const UPDATE_CONNECT_SUCCESS = 'UPDATE_CONNECT_SUCCESS';
export const UPDATE_ENABLED = 'UPDATE_ENABLED'; export const UPDATE_ENABLED = 'UPDATE_ENABLED';
export const UPDATE_INTEGRATED = 'UPDATE_INTEGRATED';
export const UPDATE_SELECTED_PROJECT = 'UPDATE_SELECTED_PROJECT'; export const UPDATE_SELECTED_PROJECT = 'UPDATE_SELECTED_PROJECT';
export const UPDATE_SETTINGS_LOADING = 'UPDATE_SETTINGS_LOADING'; export const UPDATE_SETTINGS_LOADING = 'UPDATE_SETTINGS_LOADING';
export const UPDATE_TOKEN = 'UPDATE_TOKEN'; export const UPDATE_TOKEN = 'UPDATE_TOKEN';
......
...@@ -20,9 +20,18 @@ export default { ...@@ -20,9 +20,18 @@ export default {
}, },
[types.SET_INITIAL_STATE]( [types.SET_INITIAL_STATE](
state, state,
{ apiHost, enabled, project, token, listProjectsEndpoint, operationsSettingsEndpoint }, {
apiHost,
enabled,
integrated,
project,
token,
listProjectsEndpoint,
operationsSettingsEndpoint,
},
) { ) {
state.enabled = parseBoolean(enabled); state.enabled = parseBoolean(enabled);
state.integrated = parseBoolean(integrated);
state.apiHost = apiHost; state.apiHost = apiHost;
state.token = token; state.token = token;
state.listProjectsEndpoint = listProjectsEndpoint; state.listProjectsEndpoint = listProjectsEndpoint;
...@@ -38,6 +47,9 @@ export default { ...@@ -38,6 +47,9 @@ export default {
[types.UPDATE_ENABLED](state, enabled) { [types.UPDATE_ENABLED](state, enabled) {
state.enabled = enabled; state.enabled = enabled;
}, },
[types.UPDATE_INTEGRATED](state, integrated) {
state.integrated = integrated;
},
[types.UPDATE_TOKEN](state, token) { [types.UPDATE_TOKEN](state, token) {
state.token = token; state.token = token;
}, },
......
export default () => ({ export default () => ({
apiHost: '', apiHost: '',
enabled: false, enabled: false,
integrated: false,
token: '', token: '',
projects: [], projects: [],
isLoadingProjects: false, isLoadingProjects: false,
......
export const projectKeys = ['name', 'organizationName', 'organizationSlug', 'slug']; export const projectKeys = ['name', 'organizationName', 'organizationSlug', 'slug'];
export const transformFrontendSettings = ({ apiHost, enabled, token, selectedProject }) => { export const transformFrontendSettings = ({
apiHost,
enabled,
integrated,
token,
selectedProject,
}) => {
const project = selectedProject const project = selectedProject
? { ? {
slug: selectedProject.slug, slug: selectedProject.slug,
...@@ -10,7 +16,7 @@ export const transformFrontendSettings = ({ apiHost, enabled, token, selectedPro ...@@ -10,7 +16,7 @@ export const transformFrontendSettings = ({ apiHost, enabled, token, selectedPro
} }
: null; : null;
return { api_host: apiHost || null, enabled, token: token || null, project }; return { api_host: apiHost || null, enabled, integrated, token: token || null, project };
}; };
export const getDisplayName = (project) => `${project.organizationName} | ${project.slug}`; export const getDisplayName = (project) => `${project.organizationName} | ${project.slug}`;
...@@ -136,6 +136,7 @@ module Projects ...@@ -136,6 +136,7 @@ module Projects
error_tracking_setting_attributes: [ error_tracking_setting_attributes: [
:enabled, :enabled,
:integrated,
:api_host, :api_host,
:token, :token,
project: [:slug, :name, :organization_slug, :organization_name] project: [:slug, :name, :organization_slug, :organization_name]
......
...@@ -94,6 +94,7 @@ module Projects ...@@ -94,6 +94,7 @@ module Projects
} }
} }
params[:error_tracking_setting_attributes][:token] = settings[:token] unless /\A\*+\z/.match?(settings[:token]) # Don't update token if we receive masked value params[:error_tracking_setting_attributes][:token] = settings[:token] unless /\A\*+\z/.match?(settings[:token]) # Don't update token if we receive masked value
params[:error_tracking_setting_attributes][:integrated] = settings[:integrated] unless settings[:integrated].nil?
params params
end end
......
...@@ -17,4 +17,5 @@ ...@@ -17,4 +17,5 @@
project: error_tracking_setting_project_json, project: error_tracking_setting_project_json,
api_host: setting.api_host, api_host: setting.api_host,
enabled: setting.enabled.to_json, enabled: setting.enabled.to_json,
integrated: setting.integrated.to_json,
token: setting.token.present? ? '*' * 12 : nil } } token: setting.token.present? ? '*' * 12 : nil } }
...@@ -13366,6 +13366,9 @@ msgstr "" ...@@ -13366,6 +13366,9 @@ msgstr ""
msgid "ErrorTracking|Enable error tracking" msgid "ErrorTracking|Enable error tracking"
msgstr "" msgstr ""
msgid "ErrorTracking|Error tracking backend"
msgstr ""
msgid "ErrorTracking|If you self-host Sentry, enter your Sentry instance's full URL. If you use Sentry's hosted solution, enter https://sentry.io" msgid "ErrorTracking|If you self-host Sentry, enter your Sentry instance's full URL. If you use Sentry's hosted solution, enter https://sentry.io"
msgstr "" msgstr ""
...@@ -28637,6 +28640,9 @@ msgstr[1] "" ...@@ -28637,6 +28640,9 @@ msgstr[1] ""
msgid "Requires values to meet regular expression requirements." msgid "Requires values to meet regular expression requirements."
msgstr "" msgstr ""
msgid "Requires you to deploy or set up cloud-hosted Sentry."
msgstr ""
msgid "Requires your primary GitLab email address." msgid "Requires your primary GitLab email address."
msgstr "" msgstr ""
...@@ -30517,6 +30523,9 @@ msgstr "" ...@@ -30517,6 +30523,9 @@ msgstr ""
msgid "Send service data" msgid "Send service data"
msgstr "" msgstr ""
msgid "Sentry"
msgstr ""
msgid "Sentry API URL" msgid "Sentry API URL"
msgstr "" msgstr ""
...@@ -37013,6 +37022,9 @@ msgstr "" ...@@ -37013,6 +37022,9 @@ msgstr ""
msgid "UsersSelect|Unassigned" msgid "UsersSelect|Unassigned"
msgstr "" msgstr ""
msgid "Uses GitLab as a lightweight alternative to Sentry."
msgstr ""
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}" msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr "" msgstr ""
......
...@@ -150,6 +150,33 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do ...@@ -150,6 +150,33 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do
assert_text('Connection failed. Check Auth Token and try again.') assert_text('Connection failed. Check Auth Token and try again.')
end end
end end
context 'integrated error tracking backend' do
it 'successfully fills and submits the form' do
visit project_settings_operations_path(project)
wait_for_requests
within '.js-error-tracking-settings' do
click_button('Expand')
end
expect(page).to have_content('Error tracking backend')
within '.js-error-tracking-settings' do
check('Active')
choose('GitLab')
end
expect(page).not_to have_content('Sentry API URL')
click_button('Save changes')
wait_for_requests
assert_text('Your changes have been saved')
end
end
end end
context 'grafana integration settings form' do context 'grafana integration settings form' do
......
import { GlFormRadioGroup, GlFormRadio } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ErrorTrackingSettings from '~/error_tracking_settings/components/app.vue'; import ErrorTrackingSettings from '~/error_tracking_settings/components/app.vue';
import ErrorTrackingForm from '~/error_tracking_settings/components/error_tracking_form.vue'; import ErrorTrackingForm from '~/error_tracking_settings/components/error_tracking_form.vue';
import ProjectDropdown from '~/error_tracking_settings/components/project_dropdown.vue'; import ProjectDropdown from '~/error_tracking_settings/components/project_dropdown.vue';
...@@ -14,20 +17,31 @@ describe('error tracking settings app', () => { ...@@ -14,20 +17,31 @@ describe('error tracking settings app', () => {
let wrapper; let wrapper;
function mountComponent() { function mountComponent() {
wrapper = shallowMount(ErrorTrackingSettings, { wrapper = extendedWrapper(
shallowMount(ErrorTrackingSettings, {
localVue, localVue,
store, // Override the imported store store, // Override the imported store
propsData: { propsData: {
initialEnabled: 'true', initialEnabled: 'true',
initialIntegrated: 'false',
initialApiHost: TEST_HOST, initialApiHost: TEST_HOST,
initialToken: 'someToken', initialToken: 'someToken',
initialProject: null, initialProject: null,
listProjectsEndpoint: TEST_HOST, listProjectsEndpoint: TEST_HOST,
operationsSettingsEndpoint: TEST_HOST, operationsSettingsEndpoint: TEST_HOST,
}, },
}); }),
);
} }
const findBackendSettingsSection = () => wrapper.findByTestId('tracking-backend-settings');
const findBackendSettingsRadioGroup = () =>
findBackendSettingsSection().findComponent(GlFormRadioGroup);
const findBackendSettingsRadioButtons = () =>
findBackendSettingsRadioGroup().findAllComponents(GlFormRadio);
const findElementWithText = (wrappers, text) => wrappers.filter((item) => item.text() === text);
const findSentrySettings = () => wrapper.findByTestId('sentry-setting-form');
beforeEach(() => { beforeEach(() => {
store = createStore(); store = createStore();
...@@ -62,4 +76,46 @@ describe('error tracking settings app', () => { ...@@ -62,4 +76,46 @@ describe('error tracking settings app', () => {
}); });
}); });
}); });
describe('tracking-backend settings', () => {
it('contains a form-group with the correct label', () => {
expect(findBackendSettingsSection().attributes('label')).toBe('Error tracking backend');
});
it('contains a radio group', () => {
expect(findBackendSettingsRadioGroup().exists()).toBe(true);
});
it('contains the correct radio buttons', () => {
expect(findBackendSettingsRadioButtons()).toHaveLength(2);
expect(findElementWithText(findBackendSettingsRadioButtons(), 'Sentry')).toHaveLength(1);
expect(findElementWithText(findBackendSettingsRadioButtons(), 'GitLab')).toHaveLength(1);
});
it('toggles the sentry-settings section when sentry is selected as a tracking-backend', async () => {
expect(findSentrySettings().exists()).toBe(true);
// set the "integrated" setting to "true"
findBackendSettingsRadioGroup().vm.$emit('change', true);
await nextTick();
expect(findSentrySettings().exists()).toBe(false);
});
it.each([true, false])(
'calls the `updateIntegrated` action when the setting changes to `%s`',
(integrated) => {
jest.spyOn(store, 'dispatch').mockImplementation();
expect(store.dispatch).toHaveBeenCalledTimes(0);
findBackendSettingsRadioGroup().vm.$emit('change', integrated);
expect(store.dispatch).toHaveBeenCalledTimes(1);
expect(store.dispatch).toHaveBeenCalledWith('updateIntegrated', integrated);
},
);
});
}); });
...@@ -42,6 +42,7 @@ export const sampleBackendProject = { ...@@ -42,6 +42,7 @@ export const sampleBackendProject = {
export const sampleFrontendSettings = { export const sampleFrontendSettings = {
apiHost: 'apiHost', apiHost: 'apiHost',
enabled: false, enabled: false,
integrated: false,
token: 'token', token: 'token',
selectedProject: { selectedProject: {
slug: normalizedProject.slug, slug: normalizedProject.slug,
...@@ -54,6 +55,7 @@ export const sampleFrontendSettings = { ...@@ -54,6 +55,7 @@ export const sampleFrontendSettings = {
export const transformedSettings = { export const transformedSettings = {
api_host: 'apiHost', api_host: 'apiHost',
enabled: false, enabled: false,
integrated: false,
token: 'token', token: 'token',
project: { project: {
slug: normalizedProject.slug, slug: normalizedProject.slug,
...@@ -71,6 +73,7 @@ export const defaultProps = { ...@@ -71,6 +73,7 @@ export const defaultProps = {
export const initialEmptyState = { export const initialEmptyState = {
apiHost: '', apiHost: '',
enabled: false, enabled: false,
integrated: false,
project: null, project: null,
token: '', token: '',
listProjectsEndpoint: TEST_HOST, listProjectsEndpoint: TEST_HOST,
...@@ -80,6 +83,7 @@ export const initialEmptyState = { ...@@ -80,6 +83,7 @@ export const initialEmptyState = {
export const initialPopulatedState = { export const initialPopulatedState = {
apiHost: 'apiHost', apiHost: 'apiHost',
enabled: true, enabled: true,
integrated: true,
project: JSON.stringify(projectList[0]), project: JSON.stringify(projectList[0]),
token: 'token', token: 'token',
listProjectsEndpoint: TEST_HOST, listProjectsEndpoint: TEST_HOST,
......
...@@ -202,5 +202,11 @@ describe('error tracking settings actions', () => { ...@@ -202,5 +202,11 @@ describe('error tracking settings actions', () => {
done, done,
); );
}); });
it.each([true, false])('should set the `integrated` flag to `%s`', async (payload) => {
await testAction(actions.updateIntegrated, payload, state, [
{ type: types.UPDATE_INTEGRATED, payload },
]);
});
}); });
}); });
...@@ -25,6 +25,7 @@ describe('error tracking settings mutations', () => { ...@@ -25,6 +25,7 @@ describe('error tracking settings mutations', () => {
expect(state.apiHost).toEqual(''); expect(state.apiHost).toEqual('');
expect(state.enabled).toEqual(false); expect(state.enabled).toEqual(false);
expect(state.integrated).toEqual(false);
expect(state.selectedProject).toEqual(null); expect(state.selectedProject).toEqual(null);
expect(state.token).toEqual(''); expect(state.token).toEqual('');
expect(state.listProjectsEndpoint).toEqual(TEST_HOST); expect(state.listProjectsEndpoint).toEqual(TEST_HOST);
...@@ -38,6 +39,7 @@ describe('error tracking settings mutations', () => { ...@@ -38,6 +39,7 @@ describe('error tracking settings mutations', () => {
expect(state.apiHost).toEqual('apiHost'); expect(state.apiHost).toEqual('apiHost');
expect(state.enabled).toEqual(true); expect(state.enabled).toEqual(true);
expect(state.integrated).toEqual(true);
expect(state.selectedProject).toEqual(projectList[0]); expect(state.selectedProject).toEqual(projectList[0]);
expect(state.token).toEqual('token'); expect(state.token).toEqual('token');
expect(state.listProjectsEndpoint).toEqual(TEST_HOST); expect(state.listProjectsEndpoint).toEqual(TEST_HOST);
...@@ -78,5 +80,11 @@ describe('error tracking settings mutations', () => { ...@@ -78,5 +80,11 @@ describe('error tracking settings mutations', () => {
expect(state.connectSuccessful).toBe(false); expect(state.connectSuccessful).toBe(false);
expect(state.connectError).toBe(false); expect(state.connectError).toBe(false);
}); });
it.each([true, false])('should update `integrated` to `%s`', (integrated) => {
mutations[types.UPDATE_INTEGRATED](state, integrated);
expect(state.integrated).toBe(integrated);
});
}); });
}); });
...@@ -11,12 +11,14 @@ describe('error tracking settings utils', () => { ...@@ -11,12 +11,14 @@ describe('error tracking settings utils', () => {
const emptyFrontendSettingsObject = { const emptyFrontendSettingsObject = {
apiHost: '', apiHost: '',
enabled: false, enabled: false,
integrated: false,
token: '', token: '',
selectedProject: null, selectedProject: null,
}; };
const transformedEmptySettingsObject = { const transformedEmptySettingsObject = {
api_host: null, api_host: null,
enabled: false, enabled: false,
integrated: false,
token: null, token: null,
project: null, project: null,
}; };
......
...@@ -153,6 +153,7 @@ RSpec.describe Projects::Operations::UpdateService do ...@@ -153,6 +153,7 @@ RSpec.describe Projects::Operations::UpdateService do
{ {
error_tracking_setting_attributes: { error_tracking_setting_attributes: {
enabled: false, enabled: false,
integrated: true,
api_host: 'http://gitlab.com/', api_host: 'http://gitlab.com/',
token: 'token', token: 'token',
project: { project: {
...@@ -174,6 +175,7 @@ RSpec.describe Projects::Operations::UpdateService do ...@@ -174,6 +175,7 @@ RSpec.describe Projects::Operations::UpdateService do
project.reload project.reload
expect(project.error_tracking_setting).not_to be_enabled expect(project.error_tracking_setting).not_to be_enabled
expect(project.error_tracking_setting.integrated).to be_truthy
expect(project.error_tracking_setting.api_url).to eq( expect(project.error_tracking_setting.api_url).to eq(
'http://gitlab.com/api/0/projects/org/project/' 'http://gitlab.com/api/0/projects/org/project/'
) )
...@@ -206,6 +208,7 @@ RSpec.describe Projects::Operations::UpdateService do ...@@ -206,6 +208,7 @@ RSpec.describe Projects::Operations::UpdateService do
{ {
error_tracking_setting_attributes: { error_tracking_setting_attributes: {
enabled: true, enabled: true,
integrated: true,
api_host: 'http://gitlab.com/', api_host: 'http://gitlab.com/',
token: 'token', token: 'token',
project: { project: {
...@@ -222,6 +225,7 @@ RSpec.describe Projects::Operations::UpdateService do ...@@ -222,6 +225,7 @@ RSpec.describe Projects::Operations::UpdateService do
expect(result[:status]).to eq(:success) expect(result[:status]).to eq(:success)
expect(project.error_tracking_setting).to be_enabled expect(project.error_tracking_setting).to be_enabled
expect(project.error_tracking_setting.integrated).to be_truthy
expect(project.error_tracking_setting.api_url).to eq( expect(project.error_tracking_setting.api_url).to eq(
'http://gitlab.com/api/0/projects/org/project/' 'http://gitlab.com/api/0/projects/org/project/'
) )
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment