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>
import {
GlAlert,
GlButton,
GlForm,
GlFormCheckbox,
GlFormGroup,
GlFormInput,
GlFormInputGroup,
GlModal,
GlInputGroupText,
GlFormCheckbox,
GlFormRadioGroup,
GlInputGroupText,
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { isEqual } from 'lodash';
import { initFormField } from 'ee/security_configuration/utils';
import { serializeFormObject } from '~/lib/utils/forms';
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 dastScannerProfileCreateMutation from '../graphql/dast_scanner_profile_create.mutation.graphql';
import dastScannerProfileUpdateMutation from '../graphql/dast_scanner_profile_update.mutation.graphql';
......@@ -28,39 +23,20 @@ const TARGET_TIMEOUT_MIN = 1;
const TARGET_TIMEOUT_MAX = 3600;
export default {
dastScannerProfileCreateMutation,
dastScannerProfileUpdateMutation,
name: 'DastScannerProfileForm',
components: {
GlAlert,
GlButton,
GlForm,
BaseDastProfileForm,
GlFormCheckbox,
GlFormGroup,
GlFormInput,
GlFormInputGroup,
GlModal,
GlInputGroupText,
GlFormCheckbox,
GlFormRadioGroup,
GlInputGroupText,
tooltipIcon,
},
directives: {
validation: validation(),
},
props: {
projectFullPath: {
type: String,
required: true,
},
profile: {
type: Object,
required: false,
default: () => ({}),
},
showHeader: {
type: Boolean,
required: false,
default: true,
},
},
mixins: [dastProfileFormMixin()],
data() {
const {
profileName = '',
......@@ -95,8 +71,6 @@ export default {
return {
form,
initialFormValues: serializeFormObject(form.fields),
loading: false,
showAlert: false,
};
},
spiderTimeoutRange: {
......@@ -109,9 +83,6 @@ export default {
},
SCAN_TYPE_OPTIONS,
computed: {
isEdit() {
return Boolean(this.profile.id);
},
i18n() {
const { isEdit } = this;
return {
......@@ -145,121 +116,50 @@ export default {
},
};
},
formTouched() {
return !isEqual(serializeFormObject(this.form.fields), this.initialFormValues);
},
isSubmitDisabled() {
return this.isPolicyProfile;
},
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: {
mutationVariables() {
return {
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>
<template>
<gl-form novalidate @submit.prevent="onSubmit">
<h2 v-if="showHeader" class="gl-mb-6">{{ i18n.title }}</h2>
<gl-alert
v-if="isPolicyProfile"
data-testid="dast-policy-scanner-profile-alert"
variant="info"
class="gl-mb-5"
:dismissible="false"
<base-dast-profile-form
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>
<template #policy-profile-notice>
{{
s__(
'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
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>
<template #error-message>{{ i18n.errorMessage }}</template>
<gl-form-group data-testid="dast-scanner-parent-group" :disabled="isPolicyProfile">
<gl-form-group
......@@ -379,37 +279,5 @@ export default {
</gl-form-group>
</div>
</gl-form-group>
<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>
</base-dast-profile-form>
</template>
......@@ -17,9 +17,7 @@ export default () => {
dastConfigurationPath,
} = el.dataset;
const props = {
projectFullPath,
};
const props = {};
if (el.dataset.scannerProfile) {
props.profile = convertObjectPropsToCamelCase(JSON.parse(el.dataset.scannerProfile));
......@@ -34,6 +32,9 @@ export default () => {
return new Vue({
el,
apolloProvider,
provide: {
projectFullPath,
},
render(h) {
return h(DastScannerProfileForm, {
props,
......
<script>
import {
GlAlert,
GlButton,
GlForm,
GlFormGroup,
GlFormInput,
GlModal,
GlFormTextarea,
GlFormText,
GlFormRadioGroup,
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { isEqual } from 'lodash';
import { GlFormGroup, GlFormInput, GlFormRadioGroup, GlFormText, GlFormTextarea } from '@gitlab/ui';
import { initFormField } from 'ee/security_configuration/utils';
import { serializeFormObject } from '~/lib/utils/forms';
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 {
MAX_CHAR_LIMIT_EXCLUDED_URLS,
......@@ -30,39 +19,20 @@ import dastSiteProfileUpdateMutation from '../graphql/dast_site_profile_update.m
import DastSiteAuthSection from './dast_site_auth_section.vue';
export default {
dastSiteProfileCreateMutation,
dastSiteProfileUpdateMutation,
name: 'DastSiteProfileForm',
components: {
GlAlert,
GlButton,
GlForm,
BaseDastProfileForm,
DastSiteAuthSection,
GlFormGroup,
GlFormInput,
GlModal,
GlFormTextarea,
DastSiteAuthSection,
GlFormRadioGroup,
GlFormText,
GlFormTextarea,
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() {
const {
name = '',
......@@ -71,7 +41,7 @@ export default {
requestHeaders = '',
auth = {},
targetType = TARGET_TYPES.WEBSITE.value,
} = this.siteProfile || {};
} = this.profile;
const form = {
state: false,
......@@ -97,21 +67,14 @@ export default {
form,
authSection: { fields: auth },
initialFormValues: serializeFormObject(form.fields),
isLoading: false,
hasAlert: false,
tokenId: null,
token: null,
errorMessage: '',
errors: [],
targetTypesOptions: Object.values(TARGET_TYPES),
};
},
computed: {
isEdit() {
return Boolean(this.siteProfile?.id);
},
hasRequestHeaders() {
return Boolean(this.siteProfile?.requestHeaders);
return Boolean(this.profile.requestHeaders);
},
i18n() {
const { isEdit } = this;
......@@ -146,12 +109,6 @@ export default {
},
};
},
formTouched() {
return !isEqual(serializeFormObject(this.form.fields), this.initialFormValues);
},
isPolicyProfile() {
return Boolean(this.siteProfile?.referencedInSecurityPolicies?.length);
},
parsedExcludedUrls() {
return this.form.fields.excludedUrls.value
.split(EXCLUDED_URLS_SEPARATOR)
......@@ -168,21 +125,13 @@ export default {
isTargetAPI() {
return this.form.fields.targetType.value === TARGET_TYPES.API.value;
},
isAuthEnabled() {
return this.authSection.fields.enabled && !this.isTargetAPI;
},
methods: {
onSubmit() {
const isAuthEnabled = this.authSection.fields.enabled && !this.isTargetAPI;
this.form.showValidation = true;
if (!this.form.state || (isAuthEnabled && !this.authSection.state)) {
return;
}
this.isLoading = true;
this.hideErrors();
const { errorMessage } = this.i18n;
isSubmitBlocked() {
return !this.form.state || (this.isAuthEnabled && !this.authSection.state);
},
mutationVariables() {
const {
profileName,
targetUrl,
......@@ -191,10 +140,9 @@ export default {
excludedUrls,
} = serializeFormObject(this.form.fields);
const variables = {
input: {
fullPath: this.fullPath,
...(this.isEdit ? { id: this.siteProfile.id } : {}),
return {
fullPath: this.projectFullPath,
...(this.isEdit ? { id: this.profile.id } : {}),
profileName,
targetUrl,
targetType,
......@@ -205,62 +153,10 @@ export default {
...(requestHeaders !== REDACTED_REQUEST_HEADERS && {
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) {
return value.length
? n__('%d character remaining', '%d characters remaining', limit - value.length)
......@@ -269,44 +165,44 @@ export default {
});
},
},
modalId: 'deleteDastProfileModal',
MAX_CHAR_LIMIT_EXCLUDED_URLS,
MAX_CHAR_LIMIT_REQUEST_HEADERS,
};
</script>
<template>
<gl-form novalidate @submit.prevent="onSubmit">
<h2 v-if="showHeader" class="gl-mb-6">
<base-dast-profile-form
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 }}
</h2>
</template>
<gl-alert
v-if="isPolicyProfile"
data-testid="dast-policy-site-profile-form-alert"
variant="info"
class="gl-mb-5"
:dismissible="false"
>
<template #policy-profile-notice>
{{
s__(
'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
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>
<template #error-message>{{ i18n.errorMessage }}</template>
<gl-form-group data-testid="dast-site-parent-group" :disabled="isPolicyProfile">
<gl-form-group
......@@ -408,37 +304,5 @@ export default {
:show-validation="form.showValidation"
:is-edit-mode="isEdit"
/>
<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>
</base-dast-profile-form>
</template>
......@@ -10,14 +10,17 @@ export default () => {
return;
}
const { fullPath, profilesLibraryPath, onDemandScansPath, dastConfigurationPath } = el.dataset;
const {
projectFullPath,
profilesLibraryPath,
onDemandScansPath,
dastConfigurationPath,
} = el.dataset;
const props = {
fullPath,
};
const props = {};
if (el.dataset.siteProfile) {
props.siteProfile = convertObjectPropsToCamelCase(JSON.parse(el.dataset.siteProfile));
props.profile = convertObjectPropsToCamelCase(JSON.parse(el.dataset.siteProfile));
}
const factoryParams = {
......@@ -30,6 +33,9 @@ export default () => {
new Vue({
el,
apolloProvider,
provide: {
projectFullPath,
},
render(h) {
return h(DastSiteProfileForm, {
props,
......
......@@ -3,7 +3,7 @@
- breadcrumb_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'),
site_profile: @site_profile.to_json,
on_demand_scans_path: new_project_on_demand_scan_path(@project),
......
......@@ -3,7 +3,7 @@
- breadcrumb_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'),
on_demand_scans_path: new_project_on_demand_scan_path(@project),
dast_configuration_path: project_security_configuration_dast_path(@project) } }
import { GlForm, GlModal } from '@gitlab/ui';
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 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 { 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';
......@@ -25,7 +26,6 @@ const {
const defaultProps = {
profilesLibraryPath,
projectFullPath,
onDemandScansPath,
};
......@@ -35,18 +35,19 @@ describe('DAST Scanner Profile', () => {
const withinComponent = () => within(wrapper.element);
const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"`);
const findBaseDastProfileForm = () => wrapper.findComponent(BaseDastProfileForm);
const findParentFormGroup = () => findByTestId('dast-scanner-parent-group');
const findForm = () => wrapper.find(GlForm);
const findProfileNameInput = () => findByTestId('profile-name-input');
const findSpiderTimeoutInput = () => findByTestId('spider-timeout-input');
const findTargetTimeoutInput = () => findByTestId('target-timeout-input');
const findSubmitButton = () => findByTestId('dast-scanner-profile-form-submit-button');
const findCancelButton = () => findByTestId('dast-scanner-profile-form-cancel-button');
const findSubmitButton = () => findByTestId('dast-profile-form-submit-button');
const findCancelButton = () => findByTestId('dast-profile-form-cancel-button');
const findScanType = () => findByTestId('scan-type-option');
const findCancelModal = () => wrapper.find(GlModal);
const findAlert = () => findByTestId('dast-scanner-profile-alert');
const findPolicyProfileAlert = () => findByTestId('dast-policy-scanner-profile-alert');
const findAlert = () => findByTestId('dast-profile-form-alert');
const findPolicyProfileAlert = () => findByTestId('dast-policy-profile-alert');
const submitForm = () => findForm().vm.$emit('submit', { preventDefault: () => {} });
const setFieldValue = async (field, value) => {
......@@ -61,13 +62,16 @@ describe('DAST Scanner Profile', () => {
await submitForm();
};
const componentFactory = (mountFn = shallowMount) => (options) => {
wrapper = mountFn(
const createComponent = (options) => {
wrapper = mount(
DastScannerProfileForm,
merge(
{},
{
propsData: defaultProps,
provide: {
projectFullPath,
},
mocks: {
$apollo: {
mutate: jest.fn(),
......@@ -78,8 +82,6 @@ describe('DAST Scanner Profile', () => {
),
);
};
const createComponent = componentFactory();
const createFullComponent = componentFactory(mount);
afterEach(() => {
wrapper.destroy();
......@@ -139,7 +141,7 @@ describe('DAST Scanner Profile', () => {
const errorMessage = 'Constraints not satisfied';
beforeEach(() => {
createFullComponent();
createComponent();
});
it.each(invalidValues)('is marked as invalid provided an invalid value', async (value) => {
......@@ -163,7 +165,7 @@ describe('DAST Scanner Profile', () => {
${'Edit scanner profile'} | ${defaultProfile} | ${dastScannerProfileUpdateMutation} | ${{ id: defaultProfile.id }} | ${'dastScannerProfileUpdate'}
`('$title', ({ profile, title, mutation, mutationVars, mutationKind }) => {
beforeEach(() => {
createFullComponent({
createComponent({
propsData: {
profile,
},
......@@ -218,9 +220,10 @@ describe('DAST Scanner Profile', () => {
});
it('emits success event with correct params', () => {
expect(wrapper.emitted('success')).toBeTruthy();
expect(wrapper.emitted('success')).toHaveLength(1);
expect(wrapper.emitted('success')[0]).toStrictEqual([{ id: 30203 }]);
const baseDastProfileForm = findBaseDastProfileForm();
expect(baseDastProfileForm.emitted('success')).toBeTruthy();
expect(baseDastProfileForm.emitted('success')).toHaveLength(1);
expect(baseDastProfileForm.emitted('success')[0]).toStrictEqual([{ id: 30203 }]);
});
it('does not show an alert', () => {
......@@ -230,7 +233,7 @@ describe('DAST Scanner Profile', () => {
describe('on top-level error', () => {
beforeEach(async () => {
createFullComponent();
createComponent();
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue();
await fillAndSubmitForm();
});
......@@ -274,13 +277,13 @@ describe('DAST Scanner Profile', () => {
describe('cancellation', () => {
beforeEach(() => {
createFullComponent();
createComponent();
});
describe('when form is empty', () => {
it('emits cancel event', () => {
findCancelButton().vm.$emit('click');
expect(wrapper.emitted('cancel')).toBeTruthy();
expect(findBaseDastProfileForm().emitted('cancel')).toBeTruthy();
});
});
......@@ -297,7 +300,7 @@ describe('DAST Scanner Profile', () => {
it('emits cancel event', () => {
findCancelModal().vm.$emit('ok');
expect(wrapper.emitted('cancel')).toBeTruthy();
expect(findBaseDastProfileForm().emitted('cancel')).toBeTruthy();
});
});
});
......@@ -335,7 +338,7 @@ describe('DAST Scanner Profile', () => {
});
it('should disable all form groups', () => {
expect(findParentFormGroup().attributes('disabled')).toBe('true');
expect(findParentFormGroup().attributes('disabled')).toBe('disabled');
});
it('should disable the save button', () => {
......
import { GlForm, GlModal } from '@gitlab/ui';
import { within } from '@testing-library/dom';
import { createLocalVue, mount, shallowMount } from '@vue/test-utils';
import { createLocalVue, mount } from '@vue/test-utils';
import merge from 'lodash/merge';
import { createMockClient } from 'mock-apollo-client';
import VueApollo from 'vue-apollo';
import BaseDastProfileForm from 'ee/security_configuration/dast_profiles/components/base_dast_profile_form.vue';
import DastSiteAuthSection from 'ee/security_configuration/dast_profiles/dast_site_profiles/components/dast_site_auth_section.vue';
import DastSiteProfileForm from 'ee/security_configuration/dast_profiles/dast_site_profiles/components/dast_site_profile_form.vue';
import dastSiteProfileCreateMutation from 'ee/security_configuration/dast_profiles/dast_site_profiles/graphql/dast_site_profile_create.mutation.graphql';
......@@ -18,9 +19,9 @@ const localVue = createLocalVue();
localVue.use(VueApollo);
const [siteProfileOne] = siteProfiles;
const fullPath = 'group/project';
const profilesLibraryPath = `${TEST_HOST}/${fullPath}/-/security/configuration/dast_scans`;
const onDemandScansPath = `${TEST_HOST}/${fullPath}/-/on_demand_scans`;
const projectFullPath = 'group/project';
const profilesLibraryPath = `${TEST_HOST}/${projectFullPath}/-/security/configuration/dast_scans`;
const onDemandScansPath = `${TEST_HOST}/${projectFullPath}/-/on_demand_scans`;
const profileName = 'My DAST site profile';
const targetUrl = 'http://example.com';
const excludedUrls = 'https://foo.com/logout, https://foo.com/send_mail';
......@@ -28,7 +29,6 @@ const requestHeaders = 'my-new-header=something';
const defaultProps = {
profilesLibraryPath,
fullPath,
onDemandScansPath,
};
......@@ -45,6 +45,7 @@ describe('DastSiteProfileForm', () => {
const withinComponent = () => within(wrapper.element);
const findForm = () => wrapper.findComponent(GlForm);
const findBaseDastProfileForm = () => wrapper.findComponent(BaseDastProfileForm);
const findParentFormGroup = () => wrapper.findByTestId('dast-site-parent-group');
const findAuthSection = () => wrapper.findComponent(DastSiteAuthSection);
const findCancelModal = () => wrapper.findComponent(GlModal);
......@@ -55,10 +56,10 @@ describe('DastSiteProfileForm', () => {
const findRequestHeadersInput = () => wrapper.findByTestId('request-headers-input');
const findAuthCheckbox = () => wrapper.findByTestId('auth-enable-checkbox');
const findTargetTypeOption = () => wrapper.findByTestId('site-type-option');
const findSubmitButton = () => wrapper.findByTestId('dast-site-profile-form-submit-button');
const findCancelButton = () => wrapper.findByTestId('dast-site-profile-form-cancel-button');
const findAlert = () => wrapper.findByTestId('dast-site-profile-form-alert');
const findPolicyAlert = () => wrapper.findByTestId('dast-policy-site-profile-form-alert');
const findSubmitButton = () => wrapper.findByTestId('dast-profile-form-submit-button');
const findCancelButton = () => wrapper.findByTestId('dast-profile-form-cancel-button');
const findAlert = () => wrapper.findByTestId('dast-profile-form-alert');
const findPolicyAlert = () => wrapper.findByTestId('dast-policy-profile-alert');
const submitForm = () => findForm().vm.$emit('submit', { preventDefault: () => {} });
const setFieldValue = async (field, value) => {
......@@ -121,7 +122,7 @@ describe('DastSiteProfileForm', () => {
apolloProvider.defaultClient = mockClientFactory(handlers);
};
const componentFactory = (mountFn = shallowMount) => (options, handlers) => {
const createComponent = (options, handlers) => {
apolloProvider = new VueApollo({
defaultClient: mockClientFactory(handlers),
});
......@@ -130,6 +131,7 @@ describe('DastSiteProfileForm', () => {
{},
{
propsData: defaultProps,
provide: { projectFullPath },
},
options,
{
......@@ -138,10 +140,8 @@ describe('DastSiteProfileForm', () => {
},
);
wrapper = extendedWrapper(mountFn(DastSiteProfileForm, mountOpts));
wrapper = extendedWrapper(mount(DastSiteProfileForm, mountOpts));
};
const createComponent = componentFactory();
const createFullComponent = componentFactory(mount);
afterEach(() => {
wrapper.destroy();
......@@ -169,7 +169,7 @@ describe('DastSiteProfileForm', () => {
const errorMessage = 'Please enter a valid URL format, ex: http://www.example.com/home';
beforeEach(() => {
createFullComponent();
createComponent();
});
it.each(['asd', 'example.com'])(
......@@ -190,7 +190,7 @@ describe('DastSiteProfileForm', () => {
describe('additional fields', () => {
beforeEach(() => {
createFullComponent();
createComponent();
});
it('should render correctly with default values', () => {
......@@ -216,9 +216,9 @@ describe('DastSiteProfileForm', () => {
});
it('when updating an existing profile', () => {
createFullComponent({
createComponent({
propsData: {
siteProfile: siteProfileOne,
profile: siteProfileOne,
},
});
expect(findRequestHeadersInput().element.value).toBe(siteProfileOne.requestHeaders);
......@@ -226,9 +226,9 @@ describe('DastSiteProfileForm', () => {
});
it('when updating an existing profile with no request-header & password', () => {
createFullComponent({
createComponent({
propsData: {
siteProfile: { ...siteProfileOne, requestHeaders: null, auth: { enabled: true } },
profile: { ...siteProfileOne, requestHeaders: null, auth: { enabled: true } },
},
});
expect(findRequestHeadersInput().element.value).toBe('');
......@@ -246,14 +246,14 @@ describe('DastSiteProfileForm', () => {
});
describe.each`
title | siteProfile | mutationVars | mutationKind
${'New site profile'} | ${null} | ${{}} | ${'dastSiteProfileCreate'}
title | profile | mutationVars | mutationKind
${'New site profile'} | ${{}} | ${{}} | ${'dastSiteProfileCreate'}
${'Edit site profile'} | ${siteProfileOne} | ${{ id: siteProfileOne.id }} | ${'dastSiteProfileUpdate'}
`('$title', ({ siteProfile, mutationVars, mutationKind }) => {
`('$title', ({ profile, mutationVars, mutationKind }) => {
beforeEach(() => {
createFullComponent({
createComponent({
propsData: {
siteProfile,
profile,
},
});
});
......@@ -267,7 +267,7 @@ describe('DastSiteProfileForm', () => {
input: {
profileName,
targetUrl,
fullPath,
fullPath: projectFullPath,
excludedUrls: siteProfileOne.excludedUrls,
requestHeaders,
targetType: 'API',
......@@ -280,14 +280,14 @@ describe('DastSiteProfileForm', () => {
});
describe.each`
title | siteProfile | mutationVars | mutationKind
${'New site profile'} | ${null} | ${{}} | ${'dastSiteProfileCreate'}
title | profile | mutationVars | mutationKind
${'New site profile'} | ${{}} | ${{}} | ${'dastSiteProfileCreate'}
${'Edit site profile'} | ${siteProfileOne} | ${{ id: siteProfileOne.id }} | ${'dastSiteProfileUpdate'}
`('$title', ({ siteProfile, title, mutationVars, mutationKind }) => {
`('$title', ({ profile, title, mutationVars, mutationKind }) => {
beforeEach(() => {
createFullComponent({
createComponent({
propsData: {
siteProfile,
profile,
},
});
});
......@@ -297,7 +297,7 @@ describe('DastSiteProfileForm', () => {
});
it('populates the fields with the data passed in via the siteProfile prop', () => {
expect(findProfileNameInput().element.value).toBe(siteProfile?.name ?? '');
expect(findProfileNameInput().element.value).toBe(profile?.name ?? '');
});
describe('submission', () => {
......@@ -316,7 +316,7 @@ describe('DastSiteProfileForm', () => {
profileName,
targetUrl,
requestHeaders,
fullPath,
fullPath: projectFullPath,
auth: siteProfileOne.auth,
excludedUrls: siteProfileOne.excludedUrls,
targetType: siteProfileOne.targetType,
......@@ -326,9 +326,10 @@ describe('DastSiteProfileForm', () => {
});
it('emits success event with correct params', () => {
expect(wrapper.emitted('success')).toBeTruthy();
expect(wrapper.emitted('success')).toHaveLength(1);
expect(wrapper.emitted('success')[0]).toStrictEqual([{ id: '3083' }]);
const baseDastProfileForm = findBaseDastProfileForm();
expect(baseDastProfileForm.emitted('success')).toBeTruthy();
expect(baseDastProfileForm.emitted('success')).toHaveLength(1);
expect(baseDastProfileForm.emitted('success')[0]).toStrictEqual([{ id: '3083' }]);
});
it('does not show an alert', () => {
......@@ -388,7 +389,7 @@ describe('DastSiteProfileForm', () => {
describe('form unchanged', () => {
it('emits cancel event', () => {
findCancelButton().vm.$emit('click');
expect(wrapper.emitted('cancel')).toBeTruthy();
expect(findBaseDastProfileForm().emitted('cancel')).toBeTruthy();
});
});
......@@ -406,7 +407,7 @@ describe('DastSiteProfileForm', () => {
it('emits cancel event', () => {
findCancelModal().vm.$emit('ok');
expect(wrapper.emitted('cancel')).toBeTruthy();
expect(findBaseDastProfileForm().emitted('cancel')).toBeTruthy();
});
});
});
......@@ -416,7 +417,7 @@ describe('DastSiteProfileForm', () => {
beforeEach(() => {
createComponent({
propsData: {
siteProfile: siteProfileOne,
profile: siteProfileOne,
},
});
});
......@@ -434,7 +435,7 @@ describe('DastSiteProfileForm', () => {
beforeEach(() => {
createComponent({
propsData: {
siteProfile: policySiteProfile,
profile: policySiteProfile,
},
});
});
......@@ -444,7 +445,7 @@ describe('DastSiteProfileForm', () => {
});
it('should disable all form groups', () => {
expect(findParentFormGroup().attributes('disabled')).toBe('true');
expect(findParentFormGroup().attributes('disabled')).toBe('disabled');
});
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