Commit 23babc2b authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '262707-get-GL-fields-from-data-attributes' into 'master'

Add integration with BE based on mocks

See merge request gitlab-org/gitlab!47019
parents 52cd3d90 23a92496
......@@ -10,10 +10,9 @@ import {
} from '@gitlab/ui';
import { s__, __ } from '~/locale';
// Mocks will be removed when integrating with BE is ready
// data format most likely will differ but UI will not
// data format is defined and will be the same as mocked (maybe with some minor changes)
// feature rollout plan - https://gitlab.com/gitlab-org/gitlab/-/issues/262707#note_442529171
import gitlabFields from './mocks/gitlabFields.json';
import parsedMapping from './mocks/parsedMapping.json';
import gitlabFieldsMock from './mocks/gitlabFields.json';
export const i18n = {
columns: {
......@@ -41,19 +40,44 @@ export default {
directives: {
GlTooltip,
},
props: {
payloadFields: {
type: Array,
required: false,
default: () => [],
},
mapping: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
gitlabFields,
gitlabFields: this.gitlabAlertFields,
};
},
inject: {
gitlabAlertFields: {
default: gitlabFieldsMock,
},
},
computed: {
mappingData() {
return this.gitlabFields.map(gitlabField => {
const mappingFields = parsedMapping.filter(field => field.type === gitlabField.type);
const mappingFields = this.payloadFields.filter(({ type }) =>
type.some(t => gitlabField.compatibleTypes.includes(t)),
);
const foundMapping = this.mapping.find(
({ alertFieldName }) => alertFieldName === gitlabField.name,
);
const { fallbackAlertPaths, payloadAlertPaths } = foundMapping || {};
return {
mapping: null,
fallback: null,
mapping: payloadAlertPaths,
fallback: fallbackAlertPaths,
searchTerm: '',
fallbackSearchTerm: '',
mappingFields,
......@@ -64,12 +88,12 @@ export default {
},
methods: {
setMapping(gitlabKey, mappingKey, valueKey) {
const fieldIndex = this.gitlabFields.findIndex(field => field.key === gitlabKey);
const fieldIndex = this.gitlabFields.findIndex(field => field.name === gitlabKey);
const updatedField = { ...this.gitlabFields[fieldIndex], ...{ [valueKey]: mappingKey } };
Vue.set(this.gitlabFields, fieldIndex, updatedField);
},
setSearchTerm(search = '', searchFieldKey, gitlabKey) {
const fieldIndex = this.gitlabFields.findIndex(field => field.key === gitlabKey);
const fieldIndex = this.gitlabFields.findIndex(field => field.name === gitlabKey);
const updatedField = { ...this.gitlabFields[fieldIndex], ...{ [searchFieldKey]: search } };
Vue.set(this.gitlabFields, fieldIndex, updatedField);
},
......@@ -81,13 +105,14 @@ export default {
isSelected(fieldValue, mapping) {
return fieldValue === mapping;
},
selectedValue(key) {
selectedValue(name) {
return (
parsedMapping.find(item => item.key === key)?.label || this.$options.i18n.makeSelection
this.payloadFields.find(item => item.name === name)?.label ||
this.$options.i18n.makeSelection
);
},
getFieldValue({ label, type }) {
return `${label} (${type})`;
return `${label} (${type.join(__(' or '))})`;
},
noResults(searchTerm, fields) {
return !this.filterFields(searchTerm, fields).length;
......@@ -116,7 +141,7 @@ export default {
/>
</h5>
</div>
<div v-for="gitlabField in mappingData" :key="gitlabField.key" class="gl-display-table-row">
<div v-for="gitlabField in mappingData" :key="gitlabField.name" class="gl-display-table-row">
<div class="gl-display-table-cell gl-py-3 gl-pr-3 w-30p">
<gl-form-input
disabled
......@@ -137,18 +162,17 @@ export default {
class="gl-w-full"
:header-text="$options.i18n.selectMappingKey"
>
<gl-search-box-by-type @input="setSearchTerm($event, 'searchTerm', gitlabField.key)" />
<gl-search-box-by-type @input="setSearchTerm($event, 'searchTerm', gitlabField.name)" />
<gl-dropdown-item
v-for="mappingField in filterFields(gitlabField.searchTerm, gitlabField.mappingFields)"
:key="`${mappingField.key}__mapping`"
:is-checked="isSelected(gitlabField.mapping, mappingField.key)"
:key="`${mappingField.name}__mapping`"
:is-checked="isSelected(gitlabField.mapping, mappingField.name)"
is-check-item
@click="setMapping(gitlabField.key, mappingField.key, 'mapping')"
@click="setMapping(gitlabField.name, mappingField.name, 'mapping')"
>
{{ mappingField.label }}
</gl-dropdown-item>
<gl-dropdown-item v-if="noResults(gitlabField.searchTerm, gitlabField.mappingFields)">
>
{{ $options.i18n.noResults }}
</gl-dropdown-item>
</gl-dropdown>
......@@ -156,23 +180,23 @@ export default {
<div class="gl-display-table-cell gl-py-3 w-30p">
<gl-dropdown
v-if="gitlabField.hasFallback"
v-if="Boolean(gitlabField.numberOfFallbacks)"
:text="selectedValue(gitlabField.fallback)"
class="gl-w-full"
:header-text="$options.i18n.selectMappingKey"
>
<gl-search-box-by-type
@input="setSearchTerm($event, 'fallbackSearchTerm', gitlabField.key)"
@input="setSearchTerm($event, 'fallbackSearchTerm', gitlabField.name)"
/>
<gl-dropdown-item
v-for="mappingField in filterFields(
gitlabField.fallbackSearchTerm,
gitlabField.mappingFields,
)"
:key="`${mappingField.key}__fallback`"
:is-checked="isSelected(gitlabField.fallback, mappingField.key)"
:key="`${mappingField.name}__fallback`"
:is-checked="isSelected(gitlabField.fallback, mappingField.name)"
is-check-item
@click="setMapping(gitlabField.key, mappingField.key, 'fallback')"
@click="setMapping(gitlabField.name, mappingField.name, 'fallback')"
>
{{ mappingField.label }}
</gl-dropdown-item>
......
......@@ -24,6 +24,10 @@ import {
targetPrometheusUrlPlaceholder,
typeSet,
} from '../constants';
// Mocks will be removed when integrating with BE is ready
// data format is defined and will be the same as mocked (maybe with some minor changes)
// feature rollout plan - https://gitlab.com/gitlab-org/gitlab/-/issues/262707#note_442529171
import mockedCustomMapping from './mocks/parsedMapping.json';
export default {
targetPrometheusUrlPlaceholder,
......@@ -56,6 +60,13 @@ export default {
'AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with to send a test alert to the %{linkStart}alerts page%{linkEnd}. This payload can be used to test the integration using the "save and test payload" button.',
),
placeholder: s__('AlertSettings|Enter test alert JSON....'),
resetHeader: s__('AlertSettings|Reset the mapping'),
resetBody: s__(
"AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields.",
),
resetOk: s__('AlertSettings|Proceed with editing'),
editPayload: s__('AlertSettings|Edit payload'),
submitPayload: s__('AlertSettings|Submit payload'),
},
step5: {
label: s__('AlertSettings|5. Map fields (optional)'),
......@@ -126,6 +137,9 @@ export default {
json: null,
error: null,
},
resetSamplePayloadConfirmed: false,
customMapping: null,
parsingPayload: false,
};
},
computed: {
......@@ -158,6 +172,27 @@ export default {
token: this.integrationForm.token,
};
},
showMappingBuilder() {
return (
this.glFeatures.multipleHttpIntegrationsCustomMapping &&
this.selectedIntegration === typeSet.http
);
},
mappingBuilderFields() {
return this.customMapping?.samplePayload?.payloadAlerFields?.nodes || [];
},
mappingBuilderMapping() {
return this.customMapping?.storedMapping?.nodes || [];
},
hasSamplePayload() {
return Boolean(this.customMapping?.samplePayload);
},
canEditPayload() {
return this.hasSamplePayload && !this.resetSamplePayloadConfirmed;
},
isPayloadEditDisabled() {
return !this.active || this.canEditPayload;
},
},
watch: {
currentIntegration(val) {
......@@ -166,6 +201,7 @@ export default {
}
this.selectedIntegration = val.type;
this.active = val.active;
if (val.type === typeSet.http) this.getIntegrationMapping(val.id);
return this.integrationTypeSelect();
},
},
......@@ -242,6 +278,31 @@ export default {
this.integrationTestPayload.error = JSON.stringify(e.message);
}
},
parseMapping() {
// TODO: replace with real BE mutation when ready;
this.parsingPayload = true;
return new Promise(resolve => {
setTimeout(() => resolve(mockedCustomMapping), 1000);
})
.then(res => {
const mapping = { ...res };
delete mapping.storedMapping;
this.customMapping = res;
this.integrationTestPayload.json = res?.samplePayload.body;
this.resetSamplePayloadConfirmed = false;
})
.finally(() => {
this.parsingPayload = false;
});
},
getIntegrationMapping() {
// TODO: replace with real BE mutation when ready;
return Promise.resolve(mockedCustomMapping).then(res => {
this.customMapping = res;
this.integrationTestPayload.json = res?.samplePayload.body;
});
},
},
};
</script>
......@@ -358,9 +419,9 @@ export default {
</template>
</gl-form-input-group>
<gl-button v-gl-modal.authKeyModal :disabled="!integrationForm.active" class="gl-mt-3">{{
$options.i18n.integrationFormSteps.step3.reset
}}</gl-button>
<gl-button v-gl-modal.authKeyModal :disabled="!active" class="gl-mt-3">
{{ $options.i18n.integrationFormSteps.step3.reset }}
</gl-button>
<gl-modal
modal-id="authKeyModal"
:title="$options.i18n.integrationFormSteps.step3.reset"
......@@ -373,6 +434,7 @@ export default {
</div>
</gl-form-group>
<gl-form-group
id="test-integration"
:label="$options.i18n.integrationFormSteps.step4.label"
label-for="test-integration"
:invalid-feedback="integrationTestPayload.error"
......@@ -383,9 +445,9 @@ export default {
/>
<gl-form-textarea
id="test-integration"
id="test-payload"
v-model.trim="integrationTestPayload.json"
:disabled="!active"
:disabled="isPayloadEditDisabled"
:state="jsonIsValid"
:placeholder="$options.i18n.integrationFormSteps.step4.placeholder"
class="gl-my-4"
......@@ -394,16 +456,46 @@ export default {
max-rows="10"
@input="validateJson"
/>
<template v-if="showMappingBuilder">
<gl-button
v-if="canEditPayload"
v-gl-modal.resetPayloadModal
:disabled="!active"
class="gl-mt-3"
>
{{ $options.i18n.integrationFormSteps.step4.editPayload }}
</gl-button>
<gl-button
v-else
:disabled="!active"
:loading="parsingPayload"
class="gl-mt-3"
@click="parseMapping"
>
{{ $options.i18n.integrationFormSteps.step4.submitPayload }}
</gl-button>
<gl-modal
modal-id="resetPayloadModal"
:title="$options.i18n.integrationFormSteps.step4.resetHeader"
:ok-title="$options.i18n.integrationFormSteps.step4.resetOk"
ok-variant="danger"
@ok="resetSamplePayloadConfirmed = true"
>
{{ $options.i18n.integrationFormSteps.step4.resetBody }}
</gl-modal>
</template>
</gl-form-group>
<gl-form-group
v-if="glFeatures.multipleHttpIntegrationsCustomMapping"
v-if="showMappingBuilder"
id="mapping-builder"
:label="$options.i18n.integrationFormSteps.step5.label"
label-for="mapping-builder"
>
<span class="gl-text-gray-500">{{ $options.i18n.integrationFormSteps.step5.intro }}</span>
<mapping-builder />
<mapping-builder :payload-fields="mappingBuilderFields" :mapping="mappingBuilderMapping" />
</gl-form-group>
<div class="gl-display-flex gl-justify-content-end">
<gl-button type="reset" class="gl-mr-3 js-no-auto-disable">{{ __('Cancel') }}</gl-button>
......@@ -421,8 +513,8 @@ export default {
variant="success"
class="js-no-auto-disable"
data-testid="integration-form-submit"
>{{ s__('AlertSettings|Save integration') }}</gl-button
>
>{{ s__('AlertSettings|Save integration') }}
</gl-button>
</div>
</gl-collapse>
</gl-form>
......
[
{
"key":"title",
"label":"Title",
"type":"String",
"hasFallback": true
"name": "title",
"label": "Title",
"type": [
"String"
],
"compatibleTypes": [
"String",
"Number",
"DateTime"
],
"numberOfFallbacks": 1
},
{
"key":"description",
"label":"Description",
"type":"String"
"name": "description",
"label": "Description",
"type": [
"String"
],
"compatibleTypes": [
"String",
"Number",
"DateTime"
]
},
{
"key":"startTime",
"label":"Start time",
"type":"DateTime"
"name": "startTime",
"label": "Start time",
"type": [
"DateTime"
],
"compatibleTypes": [
"Number",
"DateTime"
]
},
{
"key":"service",
"label":"Service",
"type":"String"
"name": "service",
"label": "Service",
"type": [
"String"
],
"compatibleTypes": [
"String",
"Number",
"DateTime"
]
},
{
"key":"monitoringTool",
"label":"Monitoring tool",
"type":"String"
"name": "monitoringTool",
"label": "Monitoring tool",
"type": [
"String"
],
"compatibleTypes": [
"String",
"Number",
"DateTime"
]
},
{
"key":"hosts",
"label":"Hosts",
"type":"String or Array"
"name": "hosts",
"label": "Hosts",
"type": [
"String",
"Array"
],
"compatibleTypes": [
"String",
"Array",
"Number",
"DateTime"
]
},
{
"key":"severity",
"label":"Severity",
"type":"String"
"name": "severity",
"label": "Severity",
"type": [
"String"
],
"compatibleTypes": [
"String",
"Number",
"DateTime"
]
},
{
"key":"fingerprint",
"label":"Fingerprint",
"type":"String"
"name": "fingerprint",
"label": "Fingerprint",
"type": [
"String"
],
"compatibleTypes": [
"String",
"Number",
"DateTime"
]
},
{
"key":"environment",
"label":"Environment",
"type":"String"
"name": "environment",
"label": "Environment",
"type": [
"String"
],
"compatibleTypes": [
"String",
"Number",
"DateTime"
]
}
]
[
{
"key":"title",
"label":"Title",
"type":"String"
{
"samplePayload": {
"body": "{\n \"dashboardId\":1,\n \"evalMatches\":[\n {\n \"value\":1,\n \"metric\":\"Count\",\n \"tags\":{}\n }\n ],\n \"imageUrl\":\"https://grafana.com/static/assets/img/blog/mixed_styles.png\",\n \"message\":\"Notification Message\",\n \"orgId\":1,\n \"panelId\":2,\n \"ruleId\":1,\n \"ruleName\":\"Panel Title alert\",\n \"ruleUrl\":\"http://localhost:3000/d/hZ7BuVbWz/test-dashboard?fullscreen\\u0026edit\\u0026tab=alert\\u0026panelId=2\\u0026orgId=1\",\n \"state\":\"alerting\",\n \"tags\":{\n \"tag name\":\"tag value\"\n },\n \"title\":\"[Alerting] Panel Title alert\"\n}\n",
"payloadAlerFields": {
"nodes": [
{
"name": "dashboardId",
"label": "Dashboard Id",
"type": [
"Number"
]
},
{
"name": "evalMatches",
"label": "Eval Matches",
"type": [
"Array"
]
},
{
"name": "createdAt",
"label": "Created At",
"type": [
"DateTime"
]
},
{
"name": "imageUrl",
"label": "Image Url",
"type": [
"String"
]
},
{
"name": "message",
"label": "Message",
"type": [
"String"
]
},
{
"name": "orgId",
"label": "Org Id",
"type": [
"Number"
]
},
{
"name": "panelId",
"label": "Panel Id",
"type": [
"String"
]
},
{
"name": "ruleId",
"label": "Rule Id",
"type": [
"Number"
]
},
{
"name": "ruleName",
"label": "Rule Name",
"type": [
"String"
]
},
{
"name": "ruleUrl",
"label": "Rule Url",
"type": [
"String"
]
},
{
"name": "state",
"label": "State",
"type": [
"String"
]
},
{
"name": "title",
"label": "Title",
"type": [
"String"
]
},
{
"name": "tags",
"label": "Tags",
"type": [
"Object"
]
}
]
}
},
{
"key":"description",
"label":"Description",
"type":"String"
},
{
"key":"startTime",
"label":"Start time",
"type":"DateTime"
},
{
"key":"service",
"label":"Service",
"type":"String"
},
{
"key":"monitoringTool",
"label":"Monitoring tool",
"type":"String"
},
{
"key":"hosts",
"label":"Hosts",
"type":"String or Array"
},
{
"key":"severity",
"label":"Severity",
"type":"String"
},
{
"key":"fingerprint",
"label":"Fingerprint",
"type":"String"
},
{
"key":"environment",
"label":"Environment",
"type":"String"
"storedMapping": {
"nodes": [
{
"alertFieldName": "title",
"payloadAlertPaths": "title",
"fallbackAlertPaths": "ruleUrl"
},
{
"alertFieldName": "description",
"payloadAlertPaths": "message"
},
{
"alertFieldName": "hosts",
"payloadAlertPaths": "evalMatches"
},
{
"alertFieldName": "startTime",
"payloadAlertPaths": "createdAt"
}
]
}
]
}
......@@ -2541,6 +2541,9 @@ msgstr ""
msgid "AlertSettings|Delete integration"
msgstr ""
msgid "AlertSettings|Edit payload"
msgstr ""
msgid "AlertSettings|Enter integration name"
msgstr ""
......@@ -2556,6 +2559,9 @@ msgstr ""
msgid "AlertSettings|HTTP endpoint"
msgstr ""
msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
msgstr ""
msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
......@@ -2571,6 +2577,9 @@ msgstr ""
msgid "AlertSettings|Opsgenie"
msgstr ""
msgid "AlertSettings|Proceed with editing"
msgstr ""
msgid "AlertSettings|Prometheus API base URL"
msgstr ""
......@@ -2583,6 +2592,9 @@ msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
msgid "AlertSettings|Reset the mapping"
msgstr ""
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
......@@ -2598,6 +2610,9 @@ msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
msgid "AlertSettings|Submit payload"
msgstr ""
msgid "AlertSettings|Test alert payload"
msgstr ""
......
......@@ -64,7 +64,9 @@ exports[`AlertsSettingsFormNew with default values renders the initial template
</div>
</div> <button type=\\"button\\" disabled=\\"disabled\\" class=\\"btn gl-mt-3 btn-default btn-md disabled gl-button\\">
<!---->
<!----> <span class=\\"gl-button-text\\">Reset Key</span></button>
<!----> <span class=\\"gl-button-text\\">
Reset Key
</span></button>
<!---->
</div>
<!---->
......@@ -72,8 +74,9 @@ exports[`AlertsSettingsFormNew with default values renders the initial template
<!---->
</div>
</div>
<div role=\\"group\\" class=\\"form-group gl-form-group\\" id=\\"__BVID__40\\"><label for=\\"test-integration\\" class=\\"d-block col-form-label\\" id=\\"__BVID__40__BV_label_\\">4. Test integration(optional)</label>
<div class=\\"bv-no-focus-ring\\"><span class=\\"gl-text-gray-500\\">Provide an example payload from the monitoring tool you intend to integrate with to send a test alert to the <a rel=\\"noopener noreferrer\\" target=\\"_blank\\" href=\\"http://invalid\\" class=\\"gl-link gl-display-inline-block\\">alerts page</a>. This payload can be used to test the integration using the \\"save and test payload\\" button.</span> <textarea id=\\"test-integration\\" disabled=\\"disabled\\" placeholder=\\"Enter test alert JSON....\\" wrap=\\"soft\\" class=\\"gl-form-input gl-form-textarea gl-my-4 form-control is-valid\\" style=\\"resize: none; overflow-y: scroll;\\"></textarea>
<div id=\\"test-integration\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"test-integration__BV_label_\\" for=\\"test-integration\\" class=\\"d-block col-form-label\\">4. Test integration(optional)</label>
<div class=\\"bv-no-focus-ring\\"><span class=\\"gl-text-gray-500\\">Provide an example payload from the monitoring tool you intend to integrate with to send a test alert to the <a rel=\\"noopener noreferrer\\" target=\\"_blank\\" href=\\"http://invalid\\" class=\\"gl-link gl-display-inline-block\\">alerts page</a>. This payload can be used to test the integration using the \\"save and test payload\\" button.</span> <textarea id=\\"test-payload\\" disabled=\\"disabled\\" placeholder=\\"Enter test alert JSON....\\" wrap=\\"soft\\" class=\\"gl-form-input gl-form-textarea gl-my-4 form-control is-valid\\" style=\\"resize: none; overflow-y: scroll;\\"></textarea>
<!---->
<!---->
<!---->
<!---->
......@@ -86,7 +89,8 @@ exports[`AlertsSettingsFormNew with default values renders the initial template
<!---->
<!----> <span class=\\"gl-button-text\\">Save and test payload</span></button> <button data-testid=\\"integration-form-submit\\" type=\\"submit\\" class=\\"btn js-no-auto-disable btn-success btn-md gl-button\\">
<!---->
<!----> <span class=\\"gl-button-text\\">Save integration</span></button></div>
<!----> <span class=\\"gl-button-text\\">Save integration
</span></button></div>
</div>
</form>"
`;
......@@ -8,7 +8,12 @@ describe('AlertMappingBuilder', () => {
let wrapper;
function mountComponent() {
wrapper = shallowMount(AlertMappingBuilder);
wrapper = shallowMount(AlertMappingBuilder, {
propsData: {
payloadFields: parsedMapping.samplePayload.payloadAlerFields.nodes,
mapping: parsedMapping.storedMapping.nodes,
},
});
}
afterEach(() => {
......@@ -29,11 +34,6 @@ describe('AlertMappingBuilder', () => {
.findAll('.gl-display-table-cell ')
.at(column);
const fieldsByTypeCount = parsedMapping.reduce((acc, { type }) => {
acc[type] = (acc[type] || 0) + 1;
return acc;
}, {});
it('renders column captions', () => {
expect(findColumnInRow(0, 0).text()).toContain(i18n.columns.gitlabKeyTitle);
expect(findColumnInRow(0, 2).text()).toContain(i18n.columns.payloadKeyTitle);
......@@ -48,7 +48,7 @@ describe('AlertMappingBuilder', () => {
it('renders disabled form input for each mapped field', () => {
gitlabFields.forEach((field, index) => {
const input = findColumnInRow(index + 1, 0).find(GlFormInput);
expect(input.attributes('value')).toBe(`${field.label} (${field.type})`);
expect(input.attributes('value')).toBe(`${field.label} (${field.type.join(' or ')})`);
expect(input.attributes('disabled')).toBe('');
});
});
......@@ -61,27 +61,36 @@ describe('AlertMappingBuilder', () => {
});
it('renders mapping dropdown for each field', () => {
gitlabFields.forEach(({ type }, index) => {
gitlabFields.forEach(({ compatibleTypes }, index) => {
const dropdown = findColumnInRow(index + 1, 2).find(GlDropdown);
const searchBox = dropdown.find(GlSearchBoxByType);
const dropdownItems = dropdown.findAll(GlDropdownItem);
const { nodes } = parsedMapping.samplePayload.payloadAlerFields;
const numberOfMappingOptions = nodes.filter(({ type }) =>
type.some(t => compatibleTypes.includes(t)),
);
expect(dropdown.exists()).toBe(true);
expect(searchBox.exists()).toBe(true);
expect(dropdownItems.length).toBe(fieldsByTypeCount[type]);
expect(dropdownItems).toHaveLength(numberOfMappingOptions.length);
});
});
it('renders fallback dropdown only for the fields that have fallback', () => {
gitlabFields.forEach(({ type, hasFallback }, index) => {
gitlabFields.forEach(({ compatibleTypes, numberOfFallbacks }, index) => {
const dropdown = findColumnInRow(index + 1, 3).find(GlDropdown);
expect(dropdown.exists()).toBe(Boolean(hasFallback));
expect(dropdown.exists()).toBe(Boolean(numberOfFallbacks));
if (hasFallback) {
if (numberOfFallbacks) {
const searchBox = dropdown.find(GlSearchBoxByType);
const dropdownItems = dropdown.findAll(GlDropdownItem);
expect(searchBox.exists()).toBe(hasFallback);
expect(dropdownItems.length).toBe(fieldsByTypeCount[type]);
const { nodes } = parsedMapping.samplePayload.payloadAlerFields;
const numberOfMappingOptions = nodes.filter(({ type }) =>
type.some(t => compatibleTypes.includes(t)),
);
expect(searchBox.exists()).toBe(Boolean(numberOfFallbacks));
expect(dropdownItems).toHaveLength(numberOfMappingOptions.length);
}
});
});
......
import { mount } from '@vue/test-utils';
import { GlForm, GlFormSelect, GlCollapse, GlFormInput, GlToggle } from '@gitlab/ui';
import {
GlForm,
GlFormSelect,
GlCollapse,
GlFormInput,
GlToggle,
GlFormTextarea,
GlButton,
} from '@gitlab/ui';
import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form_new.vue';
import { defaultAlertSettingsConfig } from './util';
import { typeSet } from '~/alerts_settings/constants';
......@@ -33,12 +41,13 @@ describe('AlertsSettingsFormNew', () => {
const findFormSteps = () => wrapper.find(GlCollapse);
const findFormFields = () => wrapper.findAll(GlFormInput);
const findFormToggle = () => wrapper.find(GlToggle);
const findTestPayloadSection = () => wrapper.find(`[id = "test-integration"]`);
const findMappingBuilderSection = () => wrapper.find(`[id = "mapping-builder"]`);
const findSubmitButton = () => wrapper.find(`[type = "submit"]`);
const findMultiSupportText = () =>
wrapper.find(`[data-testid="multi-integrations-not-supported"]`);
const findJsonTestSubmit = () => wrapper.find(`[data-testid="integration-test-and-submit"]`);
const findJsonTextArea = () => wrapper.find(`[id = "test-integration"]`);
const findJsonTextArea = () => wrapper.find(`[id = "test-payload"]`);
afterEach(() => {
if (wrapper) {
......@@ -241,18 +250,91 @@ describe('AlertsSettingsFormNew', () => {
});
});
describe('Mapping builder section', () => {
describe('Test payload section for HTTP integration', () => {
beforeEach(() => {
createComponent({});
createComponent({
multipleHttpIntegrationsCustomMapping: true,
props: {
currentIntegration: {
type: typeSet.http,
},
},
});
});
it('should NOT render when feature flag disabled', () => {
expect(findMappingBuilderSection().exists()).toBe(false);
describe.each`
active | resetSamplePayloadConfirmed | disabled
${true} | ${true} | ${undefined}
${false} | ${true} | ${'disabled'}
${true} | ${false} | ${'disabled'}
${false} | ${false} | ${'disabled'}
`('', ({ active, resetSamplePayloadConfirmed, disabled }) => {
const payloadResetMsg = resetSamplePayloadConfirmed ? 'was confirmed' : 'was not confirmed';
const enabledState = disabled === 'disabled' ? 'disabled' : 'enabled';
const activeState = active ? 'active' : 'not active';
it(`textarea should be ${enabledState} when payload reset ${payloadResetMsg} and current integration is ${activeState}`, async () => {
wrapper.setData({
customMapping: { samplePayload: true },
active,
resetSamplePayloadConfirmed,
});
await wrapper.vm.$nextTick();
expect(
findTestPayloadSection()
.find(GlFormTextarea)
.attributes('disabled'),
).toBe(disabled);
});
});
it('should render when feature flag is enabled', () => {
createComponent({ multipleHttpIntegrationsCustomMapping: true });
expect(findMappingBuilderSection().exists()).toBe(true);
describe('action buttons for sample payload', () => {
describe.each`
resetSamplePayloadConfirmed | samplePayload | caption
${false} | ${true} | ${'Edit payload'}
${true} | ${false} | ${'Submit payload'}
${true} | ${true} | ${'Submit payload'}
${false} | ${false} | ${'Submit payload'}
`('', ({ resetSamplePayloadConfirmed, samplePayload, caption }) => {
const samplePayloadMsg = samplePayload ? 'was provided' : 'was not provided';
const payloadResetMsg = resetSamplePayloadConfirmed ? 'was confirmed' : 'was not confirmed';
it(`shows ${caption} button when sample payload ${samplePayloadMsg} and payload reset ${payloadResetMsg}`, async () => {
wrapper.setData({
selectedIntegration: typeSet.http,
customMapping: { samplePayload },
resetSamplePayloadConfirmed,
});
await wrapper.vm.$nextTick();
expect(
findTestPayloadSection()
.find(GlButton)
.text(),
).toBe(caption);
});
});
});
});
describe('Mapping builder section', () => {
describe.each`
featureFlag | integrationOption | visible
${true} | ${1} | ${true}
${true} | ${2} | ${false}
${false} | ${1} | ${false}
${false} | ${2} | ${false}
`('', ({ featureFlag, integrationOption, visible }) => {
const visibleMsg = visible ? 'is rendered' : 'is not rendered';
const featureFlagMsg = featureFlag ? 'is enabled' : 'is disabled';
const integrationType = integrationOption === 1 ? typeSet.http : typeSet.prometheus;
it(`${visibleMsg} when multipleHttpIntegrationsCustomMapping feature flag ${featureFlagMsg} and integration type is ${integrationType}`, async () => {
createComponent({ multipleHttpIntegrationsCustomMapping: featureFlag });
const options = findSelect().findAll('option');
options.at(integrationOption).setSelected();
await wrapper.vm.$nextTick();
expect(findMappingBuilderSection().exists()).toBe(visible);
});
});
});
});
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