Commit 17fb350f authored by Vitaly Slobodin's avatar Vitaly Slobodin

Merge branch '247272-refactor-dast-profile-forms' into 'master'

Deduplicate DAST profile forms' code

See merge request gitlab-org/gitlab!67680
parents 32625cc9 b782cf02
<script>
import { GlAlert, GlButton, GlForm, GlModal } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
export default {
components: {
GlAlert,
GlButton,
GlForm,
GlModal,
},
props: {
profile: {
type: Object,
required: false,
default: () => ({}),
},
mutation: {
type: Object,
required: true,
},
mutationType: {
type: String,
required: true,
},
mutationVariables: {
type: Object,
required: true,
},
showHeader: {
type: Boolean,
required: false,
default: true,
},
formTouched: {
type: Boolean,
required: false,
default: false,
},
isPolicyProfile: {
type: Boolean,
required: false,
default: false,
},
blockSubmit: {
type: Boolean,
required: false,
default: false,
},
modalProps: {
type: Object,
required: true,
},
},
data() {
return {
isLoading: false,
showAlert: false,
errors: [],
};
},
methods: {
onSubmit() {
this.$emit('submit');
if (this.blockSubmit) {
return;
}
this.isLoading = true;
this.hideErrors();
this.saveProfile();
},
saveProfile() {
this.$apollo
.mutate({
mutation: this.mutation,
variables: { input: this.mutationVariables },
})
.then(
({
data: {
[this.mutationType]: { id, errors = [] },
},
}) => {
if (errors.length > 0) {
this.showErrors(errors);
this.isLoading = false;
} else {
this.$emit('success', {
id,
});
}
},
)
.catch((exception) => {
Sentry.captureException(exception);
this.showErrors();
this.isLoading = false;
});
},
onCancelClicked() {
if (!this.formTouched) {
this.discard();
} else {
this.$refs[this.$options.modalId].show();
}
},
discard() {
this.$emit('cancel');
},
showErrors(errors = []) {
this.errors = errors;
this.showAlert = true;
},
hideErrors() {
this.errors = [];
this.showAlert = false;
},
},
modalId: 'discardConfirmationModal',
};
</script>
<template>
<gl-form novalidate @submit.prevent="onSubmit">
<h2 v-if="showHeader" class="gl-mb-6">
<slot name="title"></slot>
</h2>
<gl-alert
v-if="isPolicyProfile"
data-testid="dast-policy-profile-alert"
variant="info"
class="gl-mb-5"
:dismissible="false"
>
<slot name="policy-profile-notice"></slot>
</gl-alert>
<gl-alert
v-if="showAlert"
variant="danger"
class="gl-mb-5"
data-testid="dast-profile-form-alert"
@dismiss="hideErrors"
>
<slot name="error-message"></slot>
<ul v-if="errors.length" class="gl-mt-3 gl-mb-0">
<li v-for="error in errors" :key="error" v-text="error"></li>
</ul>
</gl-alert>
<slot></slot>
<hr class="gl-border-gray-100" />
<gl-button
:disabled="isPolicyProfile"
:loading="isLoading"
type="submit"
variant="confirm"
class="js-no-auto-disable"
data-testid="dast-profile-form-submit-button"
>
{{ s__('DastProfiles|Save profile') }}
</gl-button>
<gl-button
class="gl-ml-2"
data-testid="dast-profile-form-cancel-button"
@click="onCancelClicked"
>
{{ __('Cancel') }}
</gl-button>
<gl-modal
:ref="$options.modalId"
:modal-id="$options.modalId"
v-bind="modalProps"
ok-variant="danger"
body-class="gl-display-none"
data-testid="dast-profile-form-cancel-modal"
@ok="discard"
/>
</gl-form>
</template>
import { isEqual } from 'lodash';
import { serializeFormObject } from '~/lib/utils/forms';
import validation from '~/vue_shared/directives/validation';
export default () => ({
directives: {
validation: validation(),
},
inject: ['projectFullPath'],
props: {
profile: {
type: Object,
required: false,
default: () => ({}),
},
},
computed: {
isEdit() {
return Boolean(this.profile.id);
},
formTouched() {
return !isEqual(serializeFormObject(this.form.fields), this.initialFormValues);
},
isPolicyProfile() {
return Boolean(this.profile.referencedInSecurityPolicies?.length);
},
},
});
<script> <script>
import { import {
GlAlert, GlFormCheckbox,
GlButton,
GlForm,
GlFormGroup, GlFormGroup,
GlFormInput, GlFormInput,
GlFormInputGroup, GlFormInputGroup,
GlModal,
GlInputGroupText,
GlFormCheckbox,
GlFormRadioGroup, GlFormRadioGroup,
GlInputGroupText,
} from '@gitlab/ui'; } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { isEqual } from 'lodash';
import { initFormField } from 'ee/security_configuration/utils'; import { initFormField } from 'ee/security_configuration/utils';
import { serializeFormObject } from '~/lib/utils/forms'; import { serializeFormObject } from '~/lib/utils/forms';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import validation from '~/vue_shared/directives/validation'; import BaseDastProfileForm from '../../components/base_dast_profile_form.vue';
import dastProfileFormMixin from '../../dast_profile_form_mixin';
import { SCAN_TYPE, SCAN_TYPE_OPTIONS } from '../constants'; import { SCAN_TYPE, SCAN_TYPE_OPTIONS } from '../constants';
import dastScannerProfileCreateMutation from '../graphql/dast_scanner_profile_create.mutation.graphql'; import dastScannerProfileCreateMutation from '../graphql/dast_scanner_profile_create.mutation.graphql';
import dastScannerProfileUpdateMutation from '../graphql/dast_scanner_profile_update.mutation.graphql'; import dastScannerProfileUpdateMutation from '../graphql/dast_scanner_profile_update.mutation.graphql';
...@@ -28,39 +23,20 @@ const TARGET_TIMEOUT_MIN = 1; ...@@ -28,39 +23,20 @@ const TARGET_TIMEOUT_MIN = 1;
const TARGET_TIMEOUT_MAX = 3600; const TARGET_TIMEOUT_MAX = 3600;
export default { export default {
dastScannerProfileCreateMutation,
dastScannerProfileUpdateMutation,
name: 'DastScannerProfileForm', name: 'DastScannerProfileForm',
components: { components: {
GlAlert, BaseDastProfileForm,
GlButton, GlFormCheckbox,
GlForm,
GlFormGroup, GlFormGroup,
GlFormInput, GlFormInput,
GlFormInputGroup, GlFormInputGroup,
GlModal,
GlInputGroupText,
GlFormCheckbox,
GlFormRadioGroup, GlFormRadioGroup,
GlInputGroupText,
tooltipIcon, tooltipIcon,
}, },
directives: { mixins: [dastProfileFormMixin()],
validation: validation(),
},
props: {
projectFullPath: {
type: String,
required: true,
},
profile: {
type: Object,
required: false,
default: () => ({}),
},
showHeader: {
type: Boolean,
required: false,
default: true,
},
},
data() { data() {
const { const {
profileName = '', profileName = '',
...@@ -95,8 +71,6 @@ export default { ...@@ -95,8 +71,6 @@ export default {
return { return {
form, form,
initialFormValues: serializeFormObject(form.fields), initialFormValues: serializeFormObject(form.fields),
loading: false,
showAlert: false,
}; };
}, },
spiderTimeoutRange: { spiderTimeoutRange: {
...@@ -109,9 +83,6 @@ export default { ...@@ -109,9 +83,6 @@ export default {
}, },
SCAN_TYPE_OPTIONS, SCAN_TYPE_OPTIONS,
computed: { computed: {
isEdit() {
return Boolean(this.profile.id);
},
i18n() { i18n() {
const { isEdit } = this; const { isEdit } = this;
return { return {
...@@ -145,121 +116,50 @@ export default { ...@@ -145,121 +116,50 @@ export default {
}, },
}; };
}, },
formTouched() { mutationVariables() {
return !isEqual(serializeFormObject(this.form.fields), this.initialFormValues); return {
}, fullPath: this.projectFullPath,
isSubmitDisabled() { ...(this.isEdit ? { id: this.profile.id } : {}),
return this.isPolicyProfile; ...serializeFormObject(this.form.fields),
},
isPolicyProfile() {
return Boolean(this.profile?.referencedInSecurityPolicies?.length);
},
},
methods: {
onSubmit() {
this.form.showValidation = true;
if (!this.form.state) {
return;
}
this.loading = true;
this.hideErrors();
const variables = {
input: {
fullPath: this.projectFullPath,
...(this.isEdit ? { id: this.profile.id } : {}),
...serializeFormObject(this.form.fields),
},
}; };
this.$apollo
.mutate({
mutation: this.isEdit
? dastScannerProfileUpdateMutation
: dastScannerProfileCreateMutation,
variables,
})
.then(
({
data: {
[this.isEdit ? 'dastScannerProfileUpdate' : 'dastScannerProfileCreate']: {
id,
errors = [],
},
},
}) => {
if (errors.length > 0) {
this.showErrors(errors);
this.loading = false;
} else {
this.$emit('success', {
id,
});
}
},
)
.catch((e) => {
Sentry.captureException(e);
this.showErrors();
this.loading = false;
});
},
onCancelClicked() {
if (!this.formTouched) {
this.discard();
} else {
this.$refs[this.$options.modalId].show();
}
},
discard() {
this.$emit('cancel');
},
showErrors(errors = []) {
this.errors = errors;
this.showAlert = true;
},
hideErrors() {
this.errors = [];
this.showAlert = false;
}, },
}, },
modalId: 'deleteDastProfileModal',
}; };
</script> </script>
<template> <template>
<gl-form novalidate @submit.prevent="onSubmit"> <base-dast-profile-form
<h2 v-if="showHeader" class="gl-mb-6">{{ i18n.title }}</h2> v-bind="$attrs"
:profile="profile"
:mutation="
isEdit ? $options.dastScannerProfileUpdateMutation : $options.dastScannerProfileCreateMutation
"
:mutation-type="isEdit ? 'dastScannerProfileUpdate' : 'dastScannerProfileCreate'"
:mutation-variables="mutationVariables"
:form-touched="formTouched"
:is-policy-profile="isPolicyProfile"
:block-submit="!form.state"
:modal-props="{
title: i18n.modal.title,
okTitle: i18n.modal.okTitle,
cancelTitle: i18n.modal.cancelTitle,
}"
@submit="form.showValidation = true"
v-on="$listeners"
>
<template #title>
{{ i18n.title }}
</template>
<gl-alert <template #policy-profile-notice>
v-if="isPolicyProfile"
data-testid="dast-policy-scanner-profile-alert"
variant="info"
class="gl-mb-5"
:dismissible="false"
>
{{ {{
s__( s__(
'DastProfiles|This scanner profile is currently being used by a policy. To make edits you must remove it from the active policy.', 'DastProfiles|This scanner profile is currently being used by a policy. To make edits you must remove it from the active policy.',
) )
}} }}
</gl-alert> </template>
<gl-alert <template #error-message>{{ i18n.errorMessage }}</template>
v-if="showAlert"
data-testid="dast-scanner-profile-alert"
variant="danger"
class="gl-mb-5"
@dismiss="hideErrors"
>
{{ s__('DastProfiles|Could not create the scanner profile. Please try again.') }}
<ul v-if="errors.length" class="gl-mt-3 gl-mb-0">
<li v-for="error in errors" :key="error" v-text="error"></li>
</ul>
</gl-alert>
<gl-form-group data-testid="dast-scanner-parent-group" :disabled="isPolicyProfile"> <gl-form-group data-testid="dast-scanner-parent-group" :disabled="isPolicyProfile">
<gl-form-group <gl-form-group
...@@ -379,37 +279,5 @@ export default { ...@@ -379,37 +279,5 @@ export default {
</gl-form-group> </gl-form-group>
</div> </div>
</gl-form-group> </gl-form-group>
</base-dast-profile-form>
<hr class="gl-border-gray-100" />
<gl-button
type="submit"
variant="confirm"
class="js-no-auto-disable"
data-testid="dast-scanner-profile-form-submit-button"
:disabled="isSubmitDisabled"
:loading="loading"
>
{{ s__('DastProfiles|Save profile') }}
</gl-button>
<gl-button
class="gl-ml-2"
data-testid="dast-scanner-profile-form-cancel-button"
@click="onCancelClicked"
>
{{ __('Cancel') }}
</gl-button>
<gl-modal
:ref="$options.modalId"
:modal-id="$options.modalId"
:title="i18n.modal.title"
:ok-title="i18n.modal.okTitle"
:cancel-title="i18n.modal.cancelTitle"
ok-variant="danger"
body-class="gl-display-none"
data-testid="dast-scanner-profile-form-cancel-modal"
@ok="discard()"
/>
</gl-form>
</template> </template>
...@@ -17,9 +17,7 @@ export default () => { ...@@ -17,9 +17,7 @@ export default () => {
dastConfigurationPath, dastConfigurationPath,
} = el.dataset; } = el.dataset;
const props = { const props = {};
projectFullPath,
};
if (el.dataset.scannerProfile) { if (el.dataset.scannerProfile) {
props.profile = convertObjectPropsToCamelCase(JSON.parse(el.dataset.scannerProfile)); props.profile = convertObjectPropsToCamelCase(JSON.parse(el.dataset.scannerProfile));
...@@ -34,6 +32,9 @@ export default () => { ...@@ -34,6 +32,9 @@ export default () => {
return new Vue({ return new Vue({
el, el,
apolloProvider, apolloProvider,
provide: {
projectFullPath,
},
render(h) { render(h) {
return h(DastScannerProfileForm, { return h(DastScannerProfileForm, {
props, props,
......
<script> <script>
import { import { GlFormGroup, GlFormInput, GlFormRadioGroup, GlFormText, GlFormTextarea } from '@gitlab/ui';
GlAlert,
GlButton,
GlForm,
GlFormGroup,
GlFormInput,
GlModal,
GlFormTextarea,
GlFormText,
GlFormRadioGroup,
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { isEqual } from 'lodash';
import { initFormField } from 'ee/security_configuration/utils'; import { initFormField } from 'ee/security_configuration/utils';
import { serializeFormObject } from '~/lib/utils/forms'; import { serializeFormObject } from '~/lib/utils/forms';
import { __, s__, n__, sprintf } from '~/locale'; import { __, s__, n__, sprintf } from '~/locale';
import validation from '~/vue_shared/directives/validation'; import BaseDastProfileForm from '../../components/base_dast_profile_form.vue';
import dastProfileFormMixin from '../../dast_profile_form_mixin';
import tooltipIcon from '../../dast_scanner_profiles/components/tooltip_icon.vue'; import tooltipIcon from '../../dast_scanner_profiles/components/tooltip_icon.vue';
import { import {
MAX_CHAR_LIMIT_EXCLUDED_URLS, MAX_CHAR_LIMIT_EXCLUDED_URLS,
...@@ -30,39 +19,20 @@ import dastSiteProfileUpdateMutation from '../graphql/dast_site_profile_update.m ...@@ -30,39 +19,20 @@ import dastSiteProfileUpdateMutation from '../graphql/dast_site_profile_update.m
import DastSiteAuthSection from './dast_site_auth_section.vue'; import DastSiteAuthSection from './dast_site_auth_section.vue';
export default { export default {
dastSiteProfileCreateMutation,
dastSiteProfileUpdateMutation,
name: 'DastSiteProfileForm', name: 'DastSiteProfileForm',
components: { components: {
GlAlert, BaseDastProfileForm,
GlButton, DastSiteAuthSection,
GlForm,
GlFormGroup, GlFormGroup,
GlFormInput, GlFormInput,
GlModal, GlFormRadioGroup,
GlFormTextarea,
DastSiteAuthSection,
GlFormText, GlFormText,
GlFormTextarea,
tooltipIcon, tooltipIcon,
GlFormRadioGroup,
},
directives: {
validation: validation(),
},
props: {
fullPath: {
type: String,
required: true,
},
siteProfile: {
type: Object,
required: false,
default: null,
},
showHeader: {
type: Boolean,
required: false,
default: true,
},
}, },
mixins: [dastProfileFormMixin()],
data() { data() {
const { const {
name = '', name = '',
...@@ -71,7 +41,7 @@ export default { ...@@ -71,7 +41,7 @@ export default {
requestHeaders = '', requestHeaders = '',
auth = {}, auth = {},
targetType = TARGET_TYPES.WEBSITE.value, targetType = TARGET_TYPES.WEBSITE.value,
} = this.siteProfile || {}; } = this.profile;
const form = { const form = {
state: false, state: false,
...@@ -97,21 +67,14 @@ export default { ...@@ -97,21 +67,14 @@ export default {
form, form,
authSection: { fields: auth }, authSection: { fields: auth },
initialFormValues: serializeFormObject(form.fields), initialFormValues: serializeFormObject(form.fields),
isLoading: false,
hasAlert: false,
tokenId: null, tokenId: null,
token: null, token: null,
errorMessage: '',
errors: [],
targetTypesOptions: Object.values(TARGET_TYPES), targetTypesOptions: Object.values(TARGET_TYPES),
}; };
}, },
computed: { computed: {
isEdit() {
return Boolean(this.siteProfile?.id);
},
hasRequestHeaders() { hasRequestHeaders() {
return Boolean(this.siteProfile?.requestHeaders); return Boolean(this.profile.requestHeaders);
}, },
i18n() { i18n() {
const { isEdit } = this; const { isEdit } = this;
...@@ -146,12 +109,6 @@ export default { ...@@ -146,12 +109,6 @@ export default {
}, },
}; };
}, },
formTouched() {
return !isEqual(serializeFormObject(this.form.fields), this.initialFormValues);
},
isPolicyProfile() {
return Boolean(this.siteProfile?.referencedInSecurityPolicies?.length);
},
parsedExcludedUrls() { parsedExcludedUrls() {
return this.form.fields.excludedUrls.value return this.form.fields.excludedUrls.value
.split(EXCLUDED_URLS_SEPARATOR) .split(EXCLUDED_URLS_SEPARATOR)
...@@ -168,21 +125,13 @@ export default { ...@@ -168,21 +125,13 @@ export default {
isTargetAPI() { isTargetAPI() {
return this.form.fields.targetType.value === TARGET_TYPES.API.value; return this.form.fields.targetType.value === TARGET_TYPES.API.value;
}, },
}, isAuthEnabled() {
methods: { return this.authSection.fields.enabled && !this.isTargetAPI;
onSubmit() { },
const isAuthEnabled = this.authSection.fields.enabled && !this.isTargetAPI; isSubmitBlocked() {
return !this.form.state || (this.isAuthEnabled && !this.authSection.state);
this.form.showValidation = true; },
mutationVariables() {
if (!this.form.state || (isAuthEnabled && !this.authSection.state)) {
return;
}
this.isLoading = true;
this.hideErrors();
const { errorMessage } = this.i18n;
const { const {
profileName, profileName,
targetUrl, targetUrl,
...@@ -191,76 +140,23 @@ export default { ...@@ -191,76 +140,23 @@ export default {
excludedUrls, excludedUrls,
} = serializeFormObject(this.form.fields); } = serializeFormObject(this.form.fields);
const variables = { return {
input: { fullPath: this.projectFullPath,
fullPath: this.fullPath, ...(this.isEdit ? { id: this.profile.id } : {}),
...(this.isEdit ? { id: this.siteProfile.id } : {}), profileName,
profileName, targetUrl,
targetUrl, targetType,
targetType, ...(!this.isTargetAPI && { auth: this.serializedAuthFields }),
...(!this.isTargetAPI && { auth: this.serializedAuthFields }), ...(excludedUrls && {
...(excludedUrls && { excludedUrls: this.parsedExcludedUrls,
excludedUrls: this.parsedExcludedUrls, }),
}), ...(requestHeaders !== REDACTED_REQUEST_HEADERS && {
...(requestHeaders !== REDACTED_REQUEST_HEADERS && { requestHeaders,
requestHeaders, }),
}),
},
}; };
this.$apollo
.mutate({
mutation: this.isEdit ? dastSiteProfileUpdateMutation : dastSiteProfileCreateMutation,
variables,
})
.then(
({
data: {
[this.isEdit ? 'dastSiteProfileUpdate' : 'dastSiteProfileCreate']: {
id,
errors = [],
},
},
}) => {
if (errors.length > 0) {
this.showErrors({ message: errorMessage, errors });
this.isLoading = false;
} else {
this.$emit('success', {
id,
});
}
},
)
.catch((exception) => {
this.showErrors({ message: errorMessage });
this.captureException(exception);
this.isLoading = false;
});
},
onCancelClicked() {
if (!this.formTouched) {
this.discard();
} else {
this.$refs[this.$options.modalId].show();
}
},
discard() {
this.$emit('cancel');
},
captureException(exception) {
Sentry.captureException(exception);
},
showErrors({ message, errors = [] }) {
this.errorMessage = message;
this.errors = errors;
this.hasAlert = true;
},
hideErrors() {
this.errorMessage = '';
this.errors = [];
this.hasAlert = false;
}, },
},
methods: {
getCharacterLimitText(value, limit) { getCharacterLimitText(value, limit) {
return value.length return value.length
? n__('%d character remaining', '%d characters remaining', limit - value.length) ? n__('%d character remaining', '%d characters remaining', limit - value.length)
...@@ -269,44 +165,44 @@ export default { ...@@ -269,44 +165,44 @@ export default {
}); });
}, },
}, },
modalId: 'deleteDastProfileModal',
MAX_CHAR_LIMIT_EXCLUDED_URLS, MAX_CHAR_LIMIT_EXCLUDED_URLS,
MAX_CHAR_LIMIT_REQUEST_HEADERS, MAX_CHAR_LIMIT_REQUEST_HEADERS,
}; };
</script> </script>
<template> <template>
<gl-form novalidate @submit.prevent="onSubmit"> <base-dast-profile-form
<h2 v-if="showHeader" class="gl-mb-6"> v-bind="$attrs"
:profile="profile"
:mutation="
isEdit ? $options.dastSiteProfileUpdateMutation : $options.dastSiteProfileCreateMutation
"
:mutation-type="isEdit ? 'dastSiteProfileUpdate' : 'dastSiteProfileCreate'"
:mutation-variables="mutationVariables"
:form-touched="formTouched"
:is-policy-profile="isPolicyProfile"
:block-submit="isSubmitBlocked"
:modal-props="{
title: i18n.modal.title,
okTitle: i18n.modal.okTitle,
cancelTitle: i18n.modal.cancelTitle,
}"
@submit="form.showValidation = true"
v-on="$listeners"
>
<template #title>
{{ i18n.title }} {{ i18n.title }}
</h2> </template>
<gl-alert <template #policy-profile-notice>
v-if="isPolicyProfile"
data-testid="dast-policy-site-profile-form-alert"
variant="info"
class="gl-mb-5"
:dismissible="false"
>
{{ {{
s__( s__(
'DastProfiles|This site profile is currently being used by a policy. To make edits you must remove it from the active policy.', 'DastProfiles|This site profile is currently being used by a policy. To make edits you must remove it from the active policy.',
) )
}} }}
</gl-alert> </template>
<gl-alert <template #error-message>{{ i18n.errorMessage }}</template>
v-if="hasAlert"
variant="danger"
class="gl-mb-5"
data-testid="dast-site-profile-form-alert"
@dismiss="hideErrors"
>
{{ errorMessage }}
<ul v-if="errors.length" class="gl-mt-3 gl-mb-0">
<li v-for="error in errors" :key="error" v-text="error"></li>
</ul>
</gl-alert>
<gl-form-group data-testid="dast-site-parent-group" :disabled="isPolicyProfile"> <gl-form-group data-testid="dast-site-parent-group" :disabled="isPolicyProfile">
<gl-form-group <gl-form-group
...@@ -408,37 +304,5 @@ export default { ...@@ -408,37 +304,5 @@ export default {
:show-validation="form.showValidation" :show-validation="form.showValidation"
:is-edit-mode="isEdit" :is-edit-mode="isEdit"
/> />
</base-dast-profile-form>
<hr class="gl-border-gray-100" />
<gl-button
:disabled="isPolicyProfile"
type="submit"
variant="confirm"
class="js-no-auto-disable"
data-testid="dast-site-profile-form-submit-button"
:loading="isLoading"
>
{{ s__('DastProfiles|Save profile') }}
</gl-button>
<gl-button
class="gl-ml-2"
data-testid="dast-site-profile-form-cancel-button"
@click="onCancelClicked"
>
{{ __('Cancel') }}
</gl-button>
<gl-modal
:ref="$options.modalId"
:modal-id="$options.modalId"
:title="i18n.modal.title"
:ok-title="i18n.modal.okTitle"
:cancel-title="i18n.modal.cancelTitle"
ok-variant="danger"
body-class="gl-display-none"
data-testid="dast-site-profile-form-cancel-modal"
@ok="discard()"
/>
</gl-form>
</template> </template>
...@@ -10,14 +10,17 @@ export default () => { ...@@ -10,14 +10,17 @@ export default () => {
return; return;
} }
const { fullPath, profilesLibraryPath, onDemandScansPath, dastConfigurationPath } = el.dataset; const {
projectFullPath,
profilesLibraryPath,
onDemandScansPath,
dastConfigurationPath,
} = el.dataset;
const props = { const props = {};
fullPath,
};
if (el.dataset.siteProfile) { if (el.dataset.siteProfile) {
props.siteProfile = convertObjectPropsToCamelCase(JSON.parse(el.dataset.siteProfile)); props.profile = convertObjectPropsToCamelCase(JSON.parse(el.dataset.siteProfile));
} }
const factoryParams = { const factoryParams = {
...@@ -30,6 +33,9 @@ export default () => { ...@@ -30,6 +33,9 @@ export default () => {
new Vue({ new Vue({
el, el,
apolloProvider, apolloProvider,
provide: {
projectFullPath,
},
render(h) { render(h) {
return h(DastSiteProfileForm, { return h(DastSiteProfileForm, {
props, props,
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
- breadcrumb_title s_('DastProfiles|Edit site profile') - breadcrumb_title s_('DastProfiles|Edit site profile')
- page_title s_('DastProfiles|Edit site profile') - page_title s_('DastProfiles|Edit site profile')
.js-dast-site-profile-form{ data: { full_path: @project.path_with_namespace, .js-dast-site-profile-form{ data: { project_full_path: @project.path_with_namespace,
profiles_library_path: project_security_configuration_dast_scans_path(@project, anchor: 'site-profiles'), profiles_library_path: project_security_configuration_dast_scans_path(@project, anchor: 'site-profiles'),
site_profile: @site_profile.to_json, site_profile: @site_profile.to_json,
on_demand_scans_path: new_project_on_demand_scan_path(@project), on_demand_scans_path: new_project_on_demand_scan_path(@project),
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
- breadcrumb_title s_('DastProfiles|New site profile') - breadcrumb_title s_('DastProfiles|New site profile')
- page_title s_('DastProfiles|New site profile') - page_title s_('DastProfiles|New site profile')
.js-dast-site-profile-form{ data: { full_path: @project.path_with_namespace, .js-dast-site-profile-form{ data: { project_full_path: @project.path_with_namespace,
profiles_library_path: project_security_configuration_dast_scans_path(@project, anchor: 'site-profiles'), profiles_library_path: project_security_configuration_dast_scans_path(@project, anchor: 'site-profiles'),
on_demand_scans_path: new_project_on_demand_scan_path(@project), on_demand_scans_path: new_project_on_demand_scan_path(@project),
dast_configuration_path: project_security_configuration_dast_path(@project) } } dast_configuration_path: project_security_configuration_dast_path(@project) } }
import { GlForm, GlModal } from '@gitlab/ui'; import { GlForm, GlModal } from '@gitlab/ui';
import { within } from '@testing-library/dom'; import { within } from '@testing-library/dom';
import { mount, shallowMount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import merge from 'lodash/merge'; import merge from 'lodash/merge';
import BaseDastProfileForm from 'ee/security_configuration/dast_profiles/components/base_dast_profile_form.vue';
import DastScannerProfileForm from 'ee/security_configuration/dast_profiles/dast_scanner_profiles/components/dast_scanner_profile_form.vue'; import DastScannerProfileForm from 'ee/security_configuration/dast_profiles/dast_scanner_profiles/components/dast_scanner_profile_form.vue';
import { SCAN_TYPE } from 'ee/security_configuration/dast_profiles/dast_scanner_profiles/constants'; import { SCAN_TYPE } from 'ee/security_configuration/dast_profiles/dast_scanner_profiles/constants';
import dastScannerProfileCreateMutation from 'ee/security_configuration/dast_profiles/dast_scanner_profiles/graphql/dast_scanner_profile_create.mutation.graphql'; import dastScannerProfileCreateMutation from 'ee/security_configuration/dast_profiles/dast_scanner_profiles/graphql/dast_scanner_profile_create.mutation.graphql';
...@@ -25,7 +26,6 @@ const { ...@@ -25,7 +26,6 @@ const {
const defaultProps = { const defaultProps = {
profilesLibraryPath, profilesLibraryPath,
projectFullPath,
onDemandScansPath, onDemandScansPath,
}; };
...@@ -35,18 +35,19 @@ describe('DAST Scanner Profile', () => { ...@@ -35,18 +35,19 @@ describe('DAST Scanner Profile', () => {
const withinComponent = () => within(wrapper.element); const withinComponent = () => within(wrapper.element);
const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"`); const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"`);
const findBaseDastProfileForm = () => wrapper.findComponent(BaseDastProfileForm);
const findParentFormGroup = () => findByTestId('dast-scanner-parent-group'); const findParentFormGroup = () => findByTestId('dast-scanner-parent-group');
const findForm = () => wrapper.find(GlForm); const findForm = () => wrapper.find(GlForm);
const findProfileNameInput = () => findByTestId('profile-name-input'); const findProfileNameInput = () => findByTestId('profile-name-input');
const findSpiderTimeoutInput = () => findByTestId('spider-timeout-input'); const findSpiderTimeoutInput = () => findByTestId('spider-timeout-input');
const findTargetTimeoutInput = () => findByTestId('target-timeout-input'); const findTargetTimeoutInput = () => findByTestId('target-timeout-input');
const findSubmitButton = () => findByTestId('dast-scanner-profile-form-submit-button'); const findSubmitButton = () => findByTestId('dast-profile-form-submit-button');
const findCancelButton = () => findByTestId('dast-scanner-profile-form-cancel-button'); const findCancelButton = () => findByTestId('dast-profile-form-cancel-button');
const findScanType = () => findByTestId('scan-type-option'); const findScanType = () => findByTestId('scan-type-option');
const findCancelModal = () => wrapper.find(GlModal); const findCancelModal = () => wrapper.find(GlModal);
const findAlert = () => findByTestId('dast-scanner-profile-alert'); const findAlert = () => findByTestId('dast-profile-form-alert');
const findPolicyProfileAlert = () => findByTestId('dast-policy-scanner-profile-alert'); const findPolicyProfileAlert = () => findByTestId('dast-policy-profile-alert');
const submitForm = () => findForm().vm.$emit('submit', { preventDefault: () => {} }); const submitForm = () => findForm().vm.$emit('submit', { preventDefault: () => {} });
const setFieldValue = async (field, value) => { const setFieldValue = async (field, value) => {
...@@ -61,13 +62,16 @@ describe('DAST Scanner Profile', () => { ...@@ -61,13 +62,16 @@ describe('DAST Scanner Profile', () => {
await submitForm(); await submitForm();
}; };
const componentFactory = (mountFn = shallowMount) => (options) => { const createComponent = (options) => {
wrapper = mountFn( wrapper = mount(
DastScannerProfileForm, DastScannerProfileForm,
merge( merge(
{}, {},
{ {
propsData: defaultProps, propsData: defaultProps,
provide: {
projectFullPath,
},
mocks: { mocks: {
$apollo: { $apollo: {
mutate: jest.fn(), mutate: jest.fn(),
...@@ -78,8 +82,6 @@ describe('DAST Scanner Profile', () => { ...@@ -78,8 +82,6 @@ describe('DAST Scanner Profile', () => {
), ),
); );
}; };
const createComponent = componentFactory();
const createFullComponent = componentFactory(mount);
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
...@@ -139,7 +141,7 @@ describe('DAST Scanner Profile', () => { ...@@ -139,7 +141,7 @@ describe('DAST Scanner Profile', () => {
const errorMessage = 'Constraints not satisfied'; const errorMessage = 'Constraints not satisfied';
beforeEach(() => { beforeEach(() => {
createFullComponent(); createComponent();
}); });
it.each(invalidValues)('is marked as invalid provided an invalid value', async (value) => { it.each(invalidValues)('is marked as invalid provided an invalid value', async (value) => {
...@@ -163,7 +165,7 @@ describe('DAST Scanner Profile', () => { ...@@ -163,7 +165,7 @@ describe('DAST Scanner Profile', () => {
${'Edit scanner profile'} | ${defaultProfile} | ${dastScannerProfileUpdateMutation} | ${{ id: defaultProfile.id }} | ${'dastScannerProfileUpdate'} ${'Edit scanner profile'} | ${defaultProfile} | ${dastScannerProfileUpdateMutation} | ${{ id: defaultProfile.id }} | ${'dastScannerProfileUpdate'}
`('$title', ({ profile, title, mutation, mutationVars, mutationKind }) => { `('$title', ({ profile, title, mutation, mutationVars, mutationKind }) => {
beforeEach(() => { beforeEach(() => {
createFullComponent({ createComponent({
propsData: { propsData: {
profile, profile,
}, },
...@@ -218,9 +220,10 @@ describe('DAST Scanner Profile', () => { ...@@ -218,9 +220,10 @@ describe('DAST Scanner Profile', () => {
}); });
it('emits success event with correct params', () => { it('emits success event with correct params', () => {
expect(wrapper.emitted('success')).toBeTruthy(); const baseDastProfileForm = findBaseDastProfileForm();
expect(wrapper.emitted('success')).toHaveLength(1); expect(baseDastProfileForm.emitted('success')).toBeTruthy();
expect(wrapper.emitted('success')[0]).toStrictEqual([{ id: 30203 }]); expect(baseDastProfileForm.emitted('success')).toHaveLength(1);
expect(baseDastProfileForm.emitted('success')[0]).toStrictEqual([{ id: 30203 }]);
}); });
it('does not show an alert', () => { it('does not show an alert', () => {
...@@ -230,7 +233,7 @@ describe('DAST Scanner Profile', () => { ...@@ -230,7 +233,7 @@ describe('DAST Scanner Profile', () => {
describe('on top-level error', () => { describe('on top-level error', () => {
beforeEach(async () => { beforeEach(async () => {
createFullComponent(); createComponent();
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(); jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue();
await fillAndSubmitForm(); await fillAndSubmitForm();
}); });
...@@ -274,13 +277,13 @@ describe('DAST Scanner Profile', () => { ...@@ -274,13 +277,13 @@ describe('DAST Scanner Profile', () => {
describe('cancellation', () => { describe('cancellation', () => {
beforeEach(() => { beforeEach(() => {
createFullComponent(); createComponent();
}); });
describe('when form is empty', () => { describe('when form is empty', () => {
it('emits cancel event', () => { it('emits cancel event', () => {
findCancelButton().vm.$emit('click'); findCancelButton().vm.$emit('click');
expect(wrapper.emitted('cancel')).toBeTruthy(); expect(findBaseDastProfileForm().emitted('cancel')).toBeTruthy();
}); });
}); });
...@@ -297,7 +300,7 @@ describe('DAST Scanner Profile', () => { ...@@ -297,7 +300,7 @@ describe('DAST Scanner Profile', () => {
it('emits cancel event', () => { it('emits cancel event', () => {
findCancelModal().vm.$emit('ok'); findCancelModal().vm.$emit('ok');
expect(wrapper.emitted('cancel')).toBeTruthy(); expect(findBaseDastProfileForm().emitted('cancel')).toBeTruthy();
}); });
}); });
}); });
...@@ -335,7 +338,7 @@ describe('DAST Scanner Profile', () => { ...@@ -335,7 +338,7 @@ describe('DAST Scanner Profile', () => {
}); });
it('should disable all form groups', () => { it('should disable all form groups', () => {
expect(findParentFormGroup().attributes('disabled')).toBe('true'); expect(findParentFormGroup().attributes('disabled')).toBe('disabled');
}); });
it('should disable the save button', () => { it('should disable the save button', () => {
......
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