Commit 3df64071 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch '245333-delete-http-multiple' into 'master'

Resolve "Delete alert integration"

See merge request gitlab-org/gitlab!46849
parents 63185a6e 5c44fe0d
......@@ -4,12 +4,15 @@ import {
GlButton,
GlIcon,
GlLoadingIcon,
GlModal,
GlModalDirective,
GlTable,
GlTooltipDirective,
GlSprintf,
} from '@gitlab/ui';
import { s__, __ } from '~/locale';
import Tracking from '~/tracking';
import { trackAlertIntegrationsViewsOptions } from '../constants';
import { trackAlertIntegrationsViewsOptions, integrationToDeleteDefault } from '../constants';
export const i18n = {
title: s__('AlertsIntegrations|Current integrations'),
......@@ -36,10 +39,13 @@ export default {
GlButton,
GlIcon,
GlLoadingIcon,
GlModal,
GlTable,
GlSprintf,
},
directives: {
GlTooltip: GlTooltipDirective,
GlModal: GlModalDirective,
},
props: {
integrations: {
......@@ -71,6 +77,11 @@ export default {
label: __('Actions'),
},
],
data() {
return {
integrationToDelete: integrationToDeleteDefault,
};
},
computed: {
tbodyTrClass() {
return {
......@@ -86,6 +97,14 @@ export default {
const { category, action } = trackAlertIntegrationsViewsOptions;
Tracking.event(category, action);
},
intergrationToDelete({ name, id }) {
this.integrationToDelete.id = id;
this.integrationToDelete.name = name;
},
deleteIntergration() {
this.$emit('delete-integration', { id: this.integrationToDelete.id });
this.integrationToDelete = { ...integrationToDeleteDefault };
},
},
};
</script>
......@@ -127,7 +146,11 @@ export default {
<template #cell(actions)="{ item }">
<gl-button-group>
<gl-button icon="pencil" @click="$emit('edit-integration', { id: item.id })" />
<gl-button icon="remove" @click="$emit('delete-integration', { id: item.id })" />
<gl-button
v-gl-modal.deleteIntegration
icon="remove"
@click="intergrationToDelete(item)"
/>
</gl-button-group>
</template>
......@@ -143,5 +166,22 @@ export default {
</div>
</template>
</gl-table>
<gl-modal
modal-id="deleteIntegration"
:title="__('Are you sure?')"
:ok-title="s__('AlertSettings|Delete integration')"
ok-variant="danger"
@ok="deleteIntergration"
>
<gl-sprintf
:message="
s__(
'AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone.',
)
"
>
<template #integrationName>{{ integrationToDelete.name }}</template>
</gl-sprintf>
</gl-modal>
</div>
</template>
......@@ -22,14 +22,12 @@ import {
JSON_VALIDATE_DELAY,
targetPrometheusUrlPlaceholder,
typeSet,
defaultFormState,
} from '../constants';
export default {
targetPrometheusUrlPlaceholder,
JSON_VALIDATE_DELAY,
typeSet,
defaultFormState,
i18n: {
integrationFormSteps: {
step1: {
......@@ -113,14 +111,18 @@ export default {
data() {
return {
selectedIntegration: integrationTypesNew[0].value,
active: false,
options: integrationTypesNew,
active: false,
formVisible: false,
integrationTestPayload: {
json: null,
error: null,
},
};
},
computed: {
jsonIsValid() {
return this.integrationForm.integrationTestPayload.error === null;
return this.integrationTestPayload.error === null;
},
selectedIntegrationType() {
switch (this.selectedIntegration) {
......@@ -129,43 +131,42 @@ export default {
case this.$options.typeSet.prometheus:
return this.prometheus;
default:
return this.defaultFormState;
return {};
}
},
integrationForm() {
return {
name: this.currentIntegration?.name || '',
integrationTestPayload: {
json: null,
error: null,
},
active: this.currentIntegration?.active || false,
token: this.currentIntegration?.token || '',
url: this.currentIntegration?.url || '',
token: this.currentIntegration?.token || this.selectedIntegrationType.token,
url: this.currentIntegration?.url || this.selectedIntegrationType.url,
apiUrl: this.currentIntegration?.apiUrl || '',
};
},
},
watch: {
currentIntegration(val) {
if (val === null) {
return this.reset();
}
this.selectedIntegration = val.type;
this.active = val.active;
this.onIntegrationTypeSelect();
return this.integrationTypeSelect();
},
},
methods: {
onIntegrationTypeSelect() {
integrationTypeSelect() {
if (this.selectedIntegration === integrationTypesNew[0].value) {
this.formVisible = false;
} else {
this.formVisible = true;
}
},
onSubmitWithTestPayload() {
submitWithTestPayload() {
// TODO: Test payload before saving via GraphQL
this.onSubmit();
this.submit();
},
onSubmit() {
submit() {
const { name, apiUrl } = this.integrationForm;
const variables =
this.selectedIntegration === this.$options.typeSet.http
......@@ -179,27 +180,45 @@ export default {
return this.$emit('create-new-integration', integrationPayload);
},
onReset() {
this.integrationForm = this.defaultFormState;
reset() {
this.selectedIntegration = integrationTypesNew[0].value;
this.onIntegrationTypeSelect();
this.integrationTypeSelect();
if (this.currentIntegration) {
return this.$emit('clear-current-integration');
}
return this.resetFormValues();
},
resetFormValues() {
this.integrationForm.name = '';
this.integrationForm.apiUrl = '';
this.integrationTestPayload = {
json: null,
error: null,
};
this.active = false;
},
onResetAuthKey() {
resetAuthKey() {
if (!this.currentIntegration) {
return;
}
this.$emit('reset-token', {
type: this.selectedIntegration,
variables: { id: this.currentIntegration.id },
});
},
validateJson() {
this.integrationForm.integrationTestPayload.error = null;
if (this.integrationForm.integrationTestPayload.json === '') {
this.integrationTestPayload.error = null;
if (this.integrationTestPayload.json === '') {
return;
}
try {
JSON.parse(this.integrationForm.integrationTestPayload.json);
JSON.parse(this.integrationTestPayload.json);
} catch (e) {
this.integrationForm.integrationTestPayload.error = JSON.stringify(e.message);
this.integrationTestPayload.error = JSON.stringify(e.message);
}
},
},
......@@ -207,7 +226,7 @@ export default {
</script>
<template>
<gl-form class="gl-mt-6" @submit.prevent="onSubmit" @reset.prevent="onReset">
<gl-form class="gl-mt-6" @submit.prevent="submit" @reset.prevent="reset">
<h5 class="gl-font-lg gl-my-5">{{ s__('AlertSettings|Add new integrations') }}</h5>
<gl-form-group
......@@ -217,8 +236,9 @@ export default {
>
<gl-form-select
v-model="selectedIntegration"
:disabled="currentIntegration !== null"
:options="options"
@change="onIntegrationTypeSelect"
@change="integrationTypeSelect"
/>
<alert-settings-form-help-block
......@@ -279,7 +299,11 @@ export default {
<gl-form-input-group id="url" readonly :value="integrationForm.url">
<template #append>
<clipboard-button :text="integrationForm.url" :title="__('Copy')" class="gl-m-0!" />
<clipboard-button
:text="integrationForm.url || ''"
:title="__('Copy')"
class="gl-m-0!"
/>
</template>
</gl-form-input-group>
</div>
......@@ -296,7 +320,11 @@ export default {
:value="integrationForm.token"
>
<template #append>
<clipboard-button :text="integrationForm.token" :title="__('Copy')" class="gl-m-0!" />
<clipboard-button
:text="integrationForm.token || ''"
:title="__('Copy')"
class="gl-m-0!"
/>
</template>
</gl-form-input-group>
......@@ -308,7 +336,7 @@ export default {
:title="$options.i18n.integrationFormSteps.step3.reset"
:ok-title="$options.i18n.integrationFormSteps.step3.reset"
ok-variant="danger"
@ok="onResetAuthKey"
@ok="resetAuthKey"
>
{{ $options.i18n.integrationFormSteps.restKeyInfo.label }}
</gl-modal>
......@@ -318,7 +346,7 @@ export default {
id="test-integration"
:label="$options.i18n.integrationFormSteps.step4.label"
label-for="test-integration"
:invalid-feedback="integrationForm.integrationTestPayload.error"
:invalid-feedback="integrationTestPayload.error"
>
<alert-settings-form-help-block
:message="$options.i18n.integrationFormSteps.step4.help"
......@@ -327,8 +355,8 @@ export default {
<gl-form-textarea
id="test-integration"
v-model.trim="integrationForm.integrationTestPayload.json"
:disabled="!integrationForm.active"
v-model.trim="integrationTestPayload.json"
:disabled="!active"
:state="jsonIsValid"
:placeholder="$options.i18n.integrationFormSteps.step4.placeholder"
class="gl-my-4"
......@@ -354,7 +382,7 @@ export default {
category="secondary"
variant="success"
class="gl-mr-1 js-no-auto-disable"
@click="onSubmitWithTestPayload"
@click="submitWithTestPayload"
>{{ s__('AlertSettings|Save and test payload') }}</gl-button
>
<gl-button
......
<script>
import produce from 'immer';
import { s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { fetchPolicies } from '~/lib/graphql';
......@@ -9,12 +8,17 @@ import createHttpIntegrationMutation from '../graphql/mutations/create_http_inte
import createPrometheusIntegrationMutation from '../graphql/mutations/create_prometheus_integration.mutation.graphql';
import updateHttpIntegrationMutation from '../graphql/mutations/update_http_integration.mutation.graphql';
import updatePrometheusIntegrationMutation from '../graphql/mutations/update_prometheus_integration.mutation.graphql';
import destroyHttpIntegrationMutation from '../graphql/mutations/destroy_http_integration.mutation.graphql';
import resetHttpTokenMutation from '../graphql/mutations/reset_http_token.mutation.graphql';
import resetPrometheusTokenMutation from '../graphql/mutations/reset_prometheus_token.mutation.graphql';
import IntegrationsList from './alerts_integrations_list.vue';
import SettingsFormOld from './alerts_settings_form_old.vue';
import SettingsFormNew from './alerts_settings_form_new.vue';
import { typeSet } from '../constants';
import {
updateStoreAfterIntegrationDelete,
updateStoreAfterIntegrationAdd,
} from '../utils/cache_updates';
export default {
typeSet,
......@@ -22,6 +26,7 @@ export default {
changesSaved: s__(
'AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list.',
),
integrationRemoved: s__('AlertsIntegrations|The integration has been successfully removed.'),
},
components: {
IntegrationsList,
......@@ -89,6 +94,8 @@ export default {
},
methods: {
createNewIntegration({ type, variables }) {
const { projectPath } = this;
this.isUpdating = true;
this.$apollo
.mutate({
......@@ -98,9 +105,11 @@ export default {
: createPrometheusIntegrationMutation,
variables: {
...variables,
projectPath: this.projectPath,
projectPath,
},
update(store, { data }) {
updateStoreAfterIntegrationAdd(store, getIntegrationsQuery, data, { projectPath });
},
update: this.updateIntegrations,
})
.then(({ data: { httpIntegrationCreate, prometheusIntegrationCreate } = {} } = {}) => {
const error = httpIntegrationCreate?.errors[0] || prometheusIntegrationCreate?.errors[0];
......@@ -119,41 +128,6 @@ export default {
this.isUpdating = false;
});
},
updateIntegrations(
store,
{
data: { httpIntegrationCreate, prometheusIntegrationCreate },
},
) {
const integration =
httpIntegrationCreate?.integration || prometheusIntegrationCreate?.integration;
if (!integration) {
return;
}
const sourceData = store.readQuery({
query: getIntegrationsQuery,
variables: {
projectPath: this.projectPath,
},
});
const data = produce(sourceData, draftData => {
// eslint-disable-next-line no-param-reassign
draftData.project.alertManagementIntegrations.nodes = [
integration,
...draftData.project.alertManagementIntegrations.nodes,
];
});
store.writeQuery({
query: getIntegrationsQuery,
variables: {
projectPath: this.projectPath,
},
data,
});
},
updateIntegration({ type, variables }) {
this.isUpdating = true;
this.$apollo
......@@ -201,6 +175,12 @@ export default {
if (error) {
return createFlash({ message: error });
}
const integration =
httpIntegrationResetToken?.integration ||
prometheusIntegrationResetToken?.integration;
this.currentIntegration = integration;
return createFlash({
message: this.$options.i18n.changesSaved,
type: FLASH_TYPES.SUCCESS,
......@@ -217,8 +197,41 @@ export default {
editIntegration({ id }) {
this.currentIntegration = this.integrations.list.find(integration => integration.id === id);
},
deleteIntegration() {
// TODO, handle delete via GraphQL
deleteIntegration({ id }) {
const { projectPath } = this;
this.isUpdating = true;
this.$apollo
.mutate({
mutation: destroyHttpIntegrationMutation,
variables: {
id,
},
update(store, { data }) {
updateStoreAfterIntegrationDelete(store, getIntegrationsQuery, data, { projectPath });
},
})
.then(({ data: { httpIntegrationDestroy } = {} } = {}) => {
const error = httpIntegrationDestroy?.errors[0];
if (error) {
return createFlash({ message: error });
}
this.currentIntegration = null;
return createFlash({
message: this.$options.i18n.integrationRemoved,
type: FLASH_TYPES.SUCCESS,
});
})
.catch(err => {
this.errored = true;
createFlash({ message: err });
})
.finally(() => {
this.isUpdating = false;
});
},
clearCurrentIntegration() {
this.currentIntegration = null;
},
},
};
......@@ -239,6 +252,7 @@ export default {
@create-new-integration="createNewIntegration"
@update-integration="updateIntegration"
@reset-token="resetToken"
@clear-current-integration="clearCurrentIntegration"
/>
<settings-form-old v-else />
</div>
......
......@@ -66,6 +66,8 @@ export const defaultFormState = {
integrationTestPayload: { json: null, error: null },
};
export const integrationToDeleteDefault = { id: null, name: '' };
export const JSON_VALIDATE_DELAY = 250;
export const targetPrometheusUrlPlaceholder = 'http://prometheus.example.com/';
......
#import "../fragments/integration_item.fragment.graphql"
mutation destroyHttpIntegration($id: ID!) {
httpIntegrationDestroy(input: { id: $id }) {
errors
integration {
...IntegrationItem
}
}
}
import produce from 'immer';
import createFlash from '~/flash';
import { DELETE_INTEGRATION_ERROR, ADD_INTEGRATION_ERROR } from './error_messages';
const deleteIntegrationFromStore = (store, query, { httpIntegrationDestroy }, variables) => {
const integration = httpIntegrationDestroy?.integration;
if (!integration) {
return;
}
const sourceData = store.readQuery({
query,
variables,
});
const data = produce(sourceData, draftData => {
// eslint-disable-next-line no-param-reassign
draftData.project.alertManagementIntegrations.nodes = draftData.project.alertManagementIntegrations.nodes.filter(
({ id }) => id !== integration.id,
);
});
store.writeQuery({
query,
variables,
data,
});
};
const addIntegrationToStore = (
store,
query,
{ httpIntegrationCreate, prometheusIntegrationCreate },
variables,
) => {
const integration =
httpIntegrationCreate?.integration || prometheusIntegrationCreate?.integration;
if (!integration) {
return;
}
const sourceData = store.readQuery({
query,
variables,
});
const data = produce(sourceData, draftData => {
// eslint-disable-next-line no-param-reassign
draftData.project.alertManagementIntegrations.nodes = [
integration,
...draftData.project.alertManagementIntegrations.nodes,
];
});
store.writeQuery({
query,
variables,
data,
});
};
const onError = (data, message) => {
createFlash({ message });
throw new Error(data.errors);
};
export const hasErrors = ({ errors = [] }) => errors?.length;
export const updateStoreAfterIntegrationDelete = (store, query, data, variables) => {
if (hasErrors(data)) {
onError(data, DELETE_INTEGRATION_ERROR);
} else {
deleteIntegrationFromStore(store, query, data, variables);
}
};
export const updateStoreAfterIntegrationAdd = (store, query, data, variables) => {
if (hasErrors(data)) {
onError(data, ADD_INTEGRATION_ERROR);
} else {
addIntegrationToStore(store, query, data, variables);
}
};
import { s__ } from '~/locale';
export const DELETE_INTEGRATION_ERROR = s__(
'AlertsIntegrations|The integration could not be deleted. Please try again.',
);
export const ADD_INTEGRATION_ERROR = s__(
'AlertsIntegrations|The integration could not be added. Please try again.',
);
......@@ -2551,6 +2551,9 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
msgid "AlertSettings|Delete integration"
msgstr ""
msgid "AlertSettings|Enter integration name"
msgstr ""
......@@ -2668,9 +2671,21 @@ msgstr ""
msgid "AlertsIntegrations|Prometheus"
msgstr ""
msgid "AlertsIntegrations|The integration could not be added. Please try again."
msgstr ""
msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
msgstr ""
msgid "AlertsIntegrations|The integration has been successfully removed."
msgstr ""
msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
msgstr ""
msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
msgstr ""
......
......@@ -128,18 +128,18 @@ describe('AlertsSettingsFormNew', () => {
it('allows for update-integration with the correct form values for HTTP', async () => {
createComponent({
data: {
selectedIntegration: typeSet.http,
},
props: {
currentIntegration: { id: '1' },
currentIntegration: { id: '1', name: 'Test integration pre' },
loading: false,
},
});
const options = findSelect().findAll('option');
await options.at(1).setSelected();
await findFormFields()
.at(0)
.setValue('Test integration');
.setValue('Test integration post');
await findFormToggle().trigger('click');
await wrapper.vm.$nextTick();
......@@ -153,27 +153,27 @@ describe('AlertsSettingsFormNew', () => {
expect(wrapper.emitted('update-integration')).toBeTruthy();
expect(wrapper.emitted('update-integration')[0]).toEqual([
{ type: typeSet.http, variables: { name: 'Test integration', active: true } },
{ type: typeSet.http, variables: { name: 'Test integration post', active: true } },
]);
});
it('allows for update-integration with the correct form values for PROMETHEUS', async () => {
createComponent({
data: {
selectedIntegration: typeSet.prometheus,
},
props: {
currentIntegration: { id: '1' },
currentIntegration: { id: '1', apiUrl: 'https://test-pre.com' },
loading: false,
},
});
const options = findSelect().findAll('option');
await options.at(2).setSelected();
await findFormFields()
.at(0)
.setValue('Test integration');
await findFormFields()
.at(1)
.setValue('https://test.com');
.setValue('https://test-post.com');
await findFormToggle().trigger('click');
await wrapper.vm.$nextTick();
......@@ -187,7 +187,7 @@ describe('AlertsSettingsFormNew', () => {
expect(wrapper.emitted('update-integration')).toBeTruthy();
expect(wrapper.emitted('update-integration')[0]).toEqual([
{ type: typeSet.prometheus, variables: { apiUrl: 'https://test.com', active: true } },
{ type: typeSet.prometheus, variables: { apiUrl: 'https://test-post.com', active: true } },
]);
});
});
......
import { mount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import { mount, createLocalVue } from '@vue/test-utils';
import createMockApollo from 'jest/helpers/mock_apollo_helper';
import { GlLoadingIcon } from '@gitlab/ui';
import AlertsSettingsWrapper from '~/alerts_settings/components/alerts_settings_wrapper.vue';
import AlertsSettingsFormOld from '~/alerts_settings/components/alerts_settings_form_old.vue';
import AlertsSettingsFormNew from '~/alerts_settings/components/alerts_settings_form_new.vue';
import IntegrationsList from '~/alerts_settings/components/alerts_integrations_list.vue';
import getIntegrationsQuery from '~/alerts_settings/graphql/queries/get_integrations.query.graphql';
import createHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql';
import createPrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/create_prometheus_integration.mutation.graphql';
import updateHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql';
import updatePrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_prometheus_integration.mutation.graphql';
import destroyHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql';
import resetHttpTokenMutation from '~/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql';
import resetPrometheusTokenMutation from '~/alerts_settings/graphql/mutations/reset_prometheus_token.mutation.graphql';
import { typeSet } from '~/alerts_settings/constants';
......@@ -20,16 +24,34 @@ import {
createPrometheusVariables,
updatePrometheusVariables,
ID,
errorMsg,
getIntegrationsQueryResponse,
destroyIntegrationResponse,
integrationToDestroy,
destroyIntegrationResponseWithErrors,
} from './mocks/apollo_mock';
jest.mock('~/flash');
const localVue = createLocalVue();
describe('AlertsSettingsWrapper', () => {
let wrapper;
let fakeApollo;
let destroyIntegrationHandler;
const findLoader = () => wrapper.find(IntegrationsList).find(GlLoadingIcon);
const findIntegrations = () => wrapper.find(IntegrationsList).findAll('table tbody tr');
async function destroyHttpIntegration(localWrapper) {
await jest.runOnlyPendingTimers();
await localWrapper.vm.$nextTick();
localWrapper
.find(IntegrationsList)
.vm.$emit('delete-integration', { id: integrationToDestroy.id });
}
const createComponent = ({ data = {}, provide = {}, loading = false } = {}) => {
wrapper = mount(AlertsSettingsWrapper, {
data() {
......@@ -54,6 +76,29 @@ describe('AlertsSettingsWrapper', () => {
});
};
function createComponentWithApollo({
destroyHandler = jest.fn().mockResolvedValue(destroyIntegrationResponse),
} = {}) {
localVue.use(VueApollo);
destroyIntegrationHandler = destroyHandler;
const requestHandlers = [
[getIntegrationsQuery, jest.fn().mockResolvedValue(getIntegrationsQueryResponse)],
[destroyHttpIntegrationMutation, destroyIntegrationHandler],
];
fakeApollo = createMockApollo(requestHandlers);
wrapper = mount(AlertsSettingsWrapper, {
localVue,
apolloProvider: fakeApollo,
provide: {
...defaultAlertSettingsConfig,
glFeatures: { httpIntegrationsList: true },
},
});
}
afterEach(() => {
if (wrapper) {
wrapper.destroy();
......@@ -243,7 +288,6 @@ describe('AlertsSettingsWrapper', () => {
});
it('shows error alert when integration creation fails ', async () => {
const errorMsg = 'Something went wrong';
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
provide: { glFeatures: { httpIntegrationsList: true } },
......@@ -259,7 +303,6 @@ describe('AlertsSettingsWrapper', () => {
});
it('shows error alert when integration token reset fails ', () => {
const errorMsg = 'Something went wrong';
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
provide: { glFeatures: { httpIntegrationsList: true } },
......@@ -276,7 +319,6 @@ describe('AlertsSettingsWrapper', () => {
});
it('shows error alert when integration update fails ', () => {
const errorMsg = 'Something went wrong';
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
provide: { glFeatures: { httpIntegrationsList: true } },
......@@ -292,4 +334,41 @@ describe('AlertsSettingsWrapper', () => {
});
});
});
describe('with mocked Apollo client', () => {
it('has a selection of integrations loaded via the getIntegrationsQuery', async () => {
createComponentWithApollo();
await jest.runOnlyPendingTimers();
await wrapper.vm.$nextTick();
expect(findIntegrations()).toHaveLength(4);
});
it('calls a mutation with correct parameters and destroys a integration', async () => {
createComponentWithApollo();
await destroyHttpIntegration(wrapper);
expect(destroyIntegrationHandler).toHaveBeenCalled();
await wrapper.vm.$nextTick();
expect(findIntegrations()).toHaveLength(3);
});
it('displays flash if mutation had a recoverable error', async () => {
createComponentWithApollo({
destroyHandler: jest.fn().mockResolvedValue(destroyIntegrationResponseWithErrors),
});
await destroyHttpIntegration(wrapper);
await wrapper.vm.$nextTick(); // kick off the DOM update
await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
await wrapper.vm.$nextTick(); // kick off the DOM update for flash
expect(createFlash).toHaveBeenCalledWith({ message: 'Houston, we have a problem' });
});
});
});
const projectPath = '';
export const ID = 'gid://gitlab/AlertManagement::HttpIntegration/7';
export const errorMsg = 'Something went wrong';
export const createHttpVariables = {
name: 'Test Pre',
......@@ -24,3 +25,99 @@ export const updatePrometheusVariables = {
active: true,
id: ID,
};
export const getIntegrationsQueryResponse = {
data: {
project: {
alertManagementIntegrations: {
nodes: [
{
id: '37',
type: 'HTTP',
active: true,
name: 'Test 5',
url:
'http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-5/d4875758e67334f3.json',
token: '89eb01df471d990ff5162a1c640408cf',
apiUrl: null,
},
{
id: '41',
type: 'HTTP',
active: true,
name: 'Test 9999',
url:
'http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-9999/b78a566e1776cfc2.json',
token: 'f7579aa03844e07af3b1f0fca3f79f81',
apiUrl: null,
},
{
id: '40',
type: 'HTTP',
active: true,
name: 'Test 6',
url:
'http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-6/3e828ae28a240222.json',
token: '6536102a607a5dd74fcdde921f2349ee',
apiUrl: null,
},
{
id: '12',
type: 'PROMETHEUS',
active: false,
name: 'Prometheus',
url: 'http://127.0.0.1:3000/h5bp/html5-boilerplate/prometheus/alerts/notify.json',
token: '256f687c6225aa5d6ee50c3d68120c4c',
apiUrl: 'https://localhost.ieeeesassadasasa',
},
],
},
},
},
};
export const integrationToDestroy = {
id: '37',
type: 'HTTP',
active: true,
name: 'Test 5',
url: 'http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-5/d4875758e67334f3.json',
token: '89eb01df471d990ff5162a1c640408cf',
apiUrl: null,
};
export const destroyIntegrationResponse = {
data: {
httpIntegrationDestroy: {
errors: [],
integration: {
id: '37',
type: 'HTTP',
active: true,
name: 'Test 5',
url:
'http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-5/d4875758e67334f3.json',
token: '89eb01df471d990ff5162a1c640408cf',
apiUrl: null,
},
},
},
};
export const destroyIntegrationResponseWithErrors = {
data: {
httpIntegrationDestroy: {
errors: ['Houston, we have a problem'],
integration: {
id: '37',
type: 'HTTP',
active: true,
name: 'Test 5',
url:
'http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-5/d4875758e67334f3.json',
token: '89eb01df471d990ff5162a1c640408cf',
apiUrl: null,
},
},
},
};
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