Commit ab82aee6 authored by Vitaly Slobodin's avatar Vitaly Slobodin

Merge branch '280561-create-dast-site-validation-modal' into 'master'

Create DastSiteValidationModal component

See merge request gitlab-org/gitlab!47443
parents 865ce25b 4f5f5024
......@@ -2,84 +2,49 @@
import {
GlAlert,
GlButton,
GlCard,
GlFormGroup,
GlFormInput,
GlFormInputGroup,
GlFormRadioGroup,
GlIcon,
GlInputGroupText,
GlLoadingIcon,
GlModal,
GlSkeletonLoader,
GlTruncate,
} from '@gitlab/ui';
import { omit } from 'lodash';
import { __, s__ } from '~/locale';
import * as Sentry from '~/sentry/wrapper';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import download from '~/lib/utils/downloader';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { cleanLeadingSeparator, joinPaths, stripPathTail } from '~/lib/utils/url_utility';
import { fetchPolicies } from '~/lib/graphql';
import {
DAST_SITE_VALIDATION_MODAL_ID,
DAST_SITE_VALIDATION_HTTP_HEADER_KEY,
DAST_SITE_VALIDATION_METHOD_HTTP_HEADER,
DAST_SITE_VALIDATION_METHOD_TEXT_FILE,
DAST_SITE_VALIDATION_METHODS,
DAST_SITE_VALIDATION_STATUS,
DAST_SITE_VALIDATION_POLL_INTERVAL,
} from '../constants';
import dastSiteTokenCreateMutation from '../graphql/dast_site_token_create.mutation.graphql';
import dastSiteValidationCreateMutation from '../graphql/dast_site_validation_create.mutation.graphql';
import dastSiteValidationQuery from '../graphql/dast_site_validation.query.graphql';
export default {
name: 'DastSiteValidation',
name: 'DastSiteValidationModal',
DAST_SITE_VALIDATION_MODAL_ID,
components: {
ClipboardButton,
GlAlert,
ClipboardButton,
GlButton,
GlCard,
GlFormGroup,
GlFormInput,
GlFormInputGroup,
GlFormRadioGroup,
GlIcon,
GlInputGroupText,
GlLoadingIcon,
GlModal,
GlSkeletonLoader,
GlTruncate,
},
mixins: [glFeatureFlagsMixin()],
apollo: {
dastSiteValidation: {
query: dastSiteValidationQuery,
variables() {
return {
fullPath: this.fullPath,
targetUrl: this.targetUrl,
};
},
manual: true,
result({
data: {
project: {
dastSiteValidation: { status },
},
},
}) {
if (status === DAST_SITE_VALIDATION_STATUS.PASSED) {
this.onSuccess();
}
if (status === DAST_SITE_VALIDATION_STATUS.FAILED) {
this.onError();
}
},
skip() {
return !(this.isCreatingValidation || this.isValidating);
},
pollInterval: DAST_SITE_VALIDATION_POLL_INTERVAL,
fetchPolicy: fetchPolicies.NETWORK_ONLY,
error(e) {
this.onError(e);
},
},
},
props: {
fullPath: {
type: String,
......@@ -89,27 +54,36 @@ export default {
type: String,
required: true,
},
tokenId: {
type: String,
required: false,
default: null,
},
token: {
type: String,
required: false,
default: null,
},
},
data() {
return {
isCreatingValidation: false,
isValidating: false,
hasValidationError: false,
isCreatingToken: false,
hasErrors: false,
token: null,
tokenId: null,
validationMethod: DAST_SITE_VALIDATION_METHOD_TEXT_FILE,
validationPath: '',
};
},
computed: {
modalProps() {
return {
id: DAST_SITE_VALIDATION_MODAL_ID,
title: s__('DastSiteValidation|Validate target site'),
primaryProps: {
text: s__('DastSiteValidation|Validate'),
attributes: [
{ disabled: this.hasErrors },
{ variant: 'success' },
{ category: 'primary' },
{ 'data-testid': 'validate-dast-site-button' },
],
},
cancelProps: {
text: __('Cancel'),
},
};
},
validationMethodOptions() {
const isHttpHeaderValidationEnabled = this.glFeatures
.securityOnDemandScansHttpHeaderValidation;
......@@ -150,8 +124,9 @@ export default {
},
},
watch: {
targetUrl() {
this.hasValidationError = false;
targetUrl: {
immediate: true,
handler: 'createValidationToken',
},
},
created() {
......@@ -178,128 +153,127 @@ export default {
downloadTextFile() {
download({ fileName: this.textFileName, fileData: btoa(this.token) });
},
async validate() {
this.hasValidationError = false;
this.isCreatingValidation = true;
this.isValidating = true;
async createValidationToken() {
this.isCreatingToken = true;
this.hasErrors = false;
try {
const {
data: {
dastSiteValidationCreate: { status, errors },
dastSiteTokenCreate: { errors, id, token },
},
} = await this.$apollo.mutate({
mutation: dastSiteValidationCreateMutation,
mutation: dastSiteTokenCreateMutation,
variables: {
projectFullPath: this.fullPath,
dastSiteTokenId: this.tokenId,
validationPath: this.validationPath,
validationStrategy: this.validationMethod,
fullPath: this.fullPath,
targetUrl: this.targetUrl,
},
});
if (errors?.length) {
this.onError();
} else if (status === DAST_SITE_VALIDATION_STATUS.PASSED) {
this.onSuccess();
} else {
this.isCreatingValidation = false;
this.token = token;
this.tokenId = id;
}
} catch (exception) {
this.onError(exception);
}
this.isCreatingToken = false;
},
onSuccess() {
this.isCreatingValidation = false;
this.isValidating = false;
this.$emit('success');
async validate() {
try {
await this.$apollo.mutate({
mutation: dastSiteValidationCreateMutation,
variables: {
projectFullPath: this.fullPath,
dastSiteTokenId: this.tokenId,
validationPath: this.validationPath,
validationStrategy: this.validationMethod,
},
});
} catch (exception) {
this.onError(exception);
}
},
onError(exception = null) {
if (exception !== null) {
Sentry.captureException(exception);
}
this.isCreatingValidation = false;
this.isValidating = false;
this.hasValidationError = true;
this.hasErrors = true;
},
},
};
</script>
<template>
<gl-card class="gl-bg-gray-10">
<gl-alert variant="warning" :dismissible="false" class="gl-mb-3">
{{ s__('DastProfiles|Site is not validated yet, please follow the steps.') }}
<gl-modal
ref="modal"
:modal-id="modalProps.id"
:title="modalProps.title"
:action-primary="modalProps.primaryProps"
:action-cancel="modalProps.cancelProps"
v-bind="$attrs"
v-on="$listeners"
@primary="validate"
>
<template v-if="isCreatingToken">
<gl-skeleton-loader :width="768" :height="206">
<rect y="0" width="300" height="16" rx="4" />
<rect y="25" width="200" height="16" rx="4" />
<rect y="65" width="350" height="16" rx="4" />
<rect y="90" width="535" height="24" rx="4" />
<rect y="135" width="370" height="16" rx="4" />
<rect y="160" width="460" height="32" rx="4" />
</gl-skeleton-loader>
</template>
<gl-alert v-else-if="hasErrors" variant="danger" :dismissible="false">
{{ s__('DastSiteValidation|Could not create validation token. Please try again.') }}
</gl-alert>
<gl-form-group :label="s__('DastProfiles|Step 1 - Choose site validation method')">
<gl-form-radio-group v-model="validationMethod" :options="validationMethodOptions" />
</gl-form-group>
<gl-form-group
v-if="isTextFileValidation"
:label="s__('DastProfiles|Step 2 - Add following text to the target site')"
>
<gl-button
variant="info"
category="secondary"
size="small"
icon="download"
data-testid="download-dast-text-file-validation-button"
:aria-label="s__('DastProfiles|Download validation text file')"
@click="downloadTextFile()"
<template v-else>
<gl-form-group :label="s__('DastSiteValidation|Step 1 - Choose site validation method')">
<gl-form-radio-group v-model="validationMethod" :options="validationMethodOptions" />
</gl-form-group>
<gl-form-group
v-if="isTextFileValidation"
:label="s__('DastSiteValidation|Step 2 - Add following text to the target site')"
>
<gl-button
variant="info"
category="secondary"
size="small"
icon="download"
class="gl-display-inline-flex gl-max-w-full"
data-testid="download-dast-text-file-validation-button"
:aria-label="s__('DastSiteValidation|Download validation text file')"
@click="downloadTextFile()"
>
<gl-truncate :text="textFileName" position="middle" />
</gl-button>
</gl-form-group>
<gl-form-group
v-else-if="isHttpHeaderValidation"
:label="s__('DastSiteValidation|Step 2 - Add following HTTP header to your site')"
>
{{ textFileName }}
</gl-button>
</gl-form-group>
<gl-form-group
v-else-if="isHttpHeaderValidation"
:label="s__('DastProfiles|Step 2 - Add following HTTP header to your site')"
>
<code class="gl-p-3 gl-bg-black gl-text-white">{{ httpHeader }}</code>
<clipboard-button
:text="httpHeader"
:title="s__('DastProfiles|Copy HTTP header to clipboard')"
/>
</gl-form-group>
<gl-form-group :label="locationStepLabel" class="mw-460">
<gl-form-input-group>
<template #prepend>
<gl-input-group-text data-testid="dast-site-validation-path-prefix">{{
origin
}}</gl-input-group-text>
</template>
<gl-form-input
v-model="validationPath"
class="gl-bg-white!"
data-testid="dast-site-validation-path-input"
@input="onValidationPathInput()"
<code class="gl-p-3 gl-bg-black gl-text-white">{{ httpHeader }}</code>
<clipboard-button
:text="httpHeader"
:title="s__('DastSiteValidation|Copy HTTP header to clipboard')"
/>
</gl-form-input-group>
</gl-form-group>
<hr />
<gl-button
variant="success"
category="secondary"
data-testid="validate-dast-site-button"
:disabled="isValidating"
@click="validate"
>
{{ s__('DastProfiles|Validate') }}
</gl-button>
<span
class="gl-ml-3"
:class="{ 'gl-text-orange-600': isValidating, 'gl-text-red-500': hasValidationError }"
>
<template v-if="isValidating">
<gl-loading-icon inline /> {{ s__('DastProfiles|Validating...') }}
</template>
<template v-else-if="hasValidationError">
<gl-icon name="status_failed" />
{{
s__(
'DastProfiles|Validation failed, please make sure that you follow the steps above with the chosen method.',
)
}}
</template>
</span>
</gl-card>
</gl-form-group>
<gl-form-group :label="locationStepLabel" class="mw-460">
<gl-form-input-group>
<template #prepend>
<gl-input-group-text data-testid="dast-site-validation-path-prefix">{{
origin
}}</gl-input-group-text>
</template>
<gl-form-input
v-model="validationPath"
class="gl-bg-white!"
data-testid="dast-site-validation-path-input"
@input="onValidationPathInput()"
/>
</gl-form-input-group>
</gl-form-group>
</template>
</gl-modal>
</template>
......@@ -6,16 +6,16 @@ export const DAST_SITE_VALIDATION_METHOD_HTTP_HEADER = 'HEADER';
export const DAST_SITE_VALIDATION_METHODS = {
[DAST_SITE_VALIDATION_METHOD_TEXT_FILE]: {
value: DAST_SITE_VALIDATION_METHOD_TEXT_FILE,
text: s__('DastProfiles|Text file validation'),
text: s__('DastSiteValidation|Text file validation'),
i18n: {
locationStepLabel: s__('DastProfiles|Step 3 - Confirm text file location and validate'),
locationStepLabel: s__('DastSiteValidation|Step 3 - Confirm text file location and validate'),
},
},
[DAST_SITE_VALIDATION_METHOD_HTTP_HEADER]: {
value: DAST_SITE_VALIDATION_METHOD_HTTP_HEADER,
text: s__('DastProfiles|Header validation'),
text: s__('DastSiteValidation|Header validation'),
i18n: {
locationStepLabel: s__('DastProfiles|Step 3 - Confirm header location and validate'),
locationStepLabel: s__('DastSiteValidation|Step 3 - Confirm header location and validate'),
},
},
};
......@@ -27,5 +27,6 @@ export const DAST_SITE_VALIDATION_STATUS = {
FAILED: 'FAILED_VALIDATION',
};
export const DAST_SITE_VALIDATION_POLL_INTERVAL = 1000;
export const DAST_SITE_VALIDATION_HTTP_HEADER_KEY = 'Gitlab-On-Demand-DAST';
export const DAST_SITE_VALIDATION_MODAL_ID = 'dast-site-validation-modal';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo);
export default new VueApollo({
defaultClient: createDefaultClient(),
});
import { DAST_SITE_VALIDATION_STATUS } from 'ee/security_configuration/dast_site_profiles_form/constants';
export const dastSiteProfileCreate = (errors = []) => ({
data: { dastSiteProfileCreate: { id: '3083', errors } },
});
......@@ -7,23 +5,3 @@ export const dastSiteProfileCreate = (errors = []) => ({
export const dastSiteProfileUpdate = (errors = []) => ({
data: { dastSiteProfileUpdate: { id: '3083', errors } },
});
export const dastSiteValidation = (status = DAST_SITE_VALIDATION_STATUS.PENDING) => ({
data: { project: { dastSiteValidation: { status, id: '1' } } },
});
export const dastSiteValidationCreate = (errors = []) => ({
data: {
dastSiteValidationCreate: { status: DAST_SITE_VALIDATION_STATUS.PENDING, id: '1', errors },
},
});
export const dastSiteTokenCreate = ({ id = '1', token = '1', errors = [] }) => ({
data: {
dastSiteTokenCreate: {
id,
token,
errors,
},
},
});
import { GlLoadingIcon } from '@gitlab/ui';
import { within } from '@testing-library/dom';
import { createLocalVue, mount, shallowMount, createWrapper } from '@vue/test-utils';
import merge from 'lodash/merge';
import { createMockClient } from 'mock-apollo-client';
import createApolloProvider from 'helpers/mock_apollo_helper';
import VueApollo from 'vue-apollo';
import DastSiteValidation from 'ee/security_configuration/dast_site_profiles_form/components/dast_site_validation.vue';
import { DAST_SITE_VALIDATION_STATUS } from 'ee/security_configuration/dast_site_profiles_form/constants';
import dastSiteValidationQuery from 'ee/security_configuration/dast_site_profiles_form/graphql/dast_site_validation.query.graphql';
import dastSiteValidationCreateMutation from 'ee/security_configuration/dast_site_profiles_form/graphql/dast_site_validation_create.mutation.graphql';
import * as responses from 'ee_jest/security_configuration/dast_site_profiles_form/mock_data/apollo_mock';
import dastSiteValidationCreateMutation from 'ee/security_configuration/dast_site_validation/graphql/dast_site_validation_create.mutation.graphql';
import dastSiteTokenCreateMutation from 'ee/security_configuration/dast_site_validation/graphql/dast_site_token_create.mutation.graphql';
import waitForPromises from 'jest/helpers/wait_for_promises';
import { GlAlert, GlFormGroup, GlModal, GlSkeletonLoader } from '@gitlab/ui';
import DastSiteValidationModal from 'ee/security_configuration/dast_site_validation/components/dast_site_validation_modal.vue';
import * as responses from '../mock_data/apollo_mock';
import download from '~/lib/utils/downloader';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
......@@ -28,49 +27,26 @@ const validationMethods = ['text file', 'header'];
const defaultProps = {
fullPath,
targetUrl,
tokenId,
token,
};
const defaultRequestHandlers = {
dastSiteValidation: jest.fn().mockResolvedValue(responses.dastSiteValidation()),
dastSiteTokenCreate: jest
.fn()
.mockResolvedValue(responses.dastSiteTokenCreate({ id: tokenId, token })),
dastSiteValidationCreate: jest.fn().mockResolvedValue(responses.dastSiteValidationCreate()),
};
describe('DastSiteValidation', () => {
describe('DastSiteValidationModal', () => {
let wrapper;
let apolloProvider;
let requestHandlers;
const mockClientFactory = handlers => {
const mockClient = createMockClient();
requestHandlers = {
...defaultRequestHandlers,
...handlers,
};
mockClient.setRequestHandler(dastSiteValidationQuery, requestHandlers.dastSiteValidation);
mockClient.setRequestHandler(
dastSiteValidationCreateMutation,
requestHandlers.dastSiteValidationCreate,
);
return mockClient;
};
const respondWith = handlers => {
apolloProvider.defaultClient = mockClientFactory(handlers);
};
const componentFactory = (mountFn = shallowMount) => options => {
apolloProvider = new VueApollo({
defaultClient: mockClientFactory(),
});
const componentFactory = (mountFn = shallowMount) => ({
mountOptions = {},
handlers = {},
} = {}) => {
requestHandlers = { ...defaultRequestHandlers, ...handlers };
wrapper = mountFn(
DastSiteValidation,
DastSiteValidationModal,
merge(
{},
{
......@@ -78,11 +54,18 @@ describe('DastSiteValidation', () => {
provide: {
glFeatures: { securityOnDemandScansHttpHeaderValidation: true },
},
attrs: {
static: true,
visible: true,
},
},
options,
mountOptions,
{
localVue,
apolloProvider,
apolloProvider: createApolloProvider([
[dastSiteTokenCreateMutation, requestHandlers.dastSiteTokenCreate],
[dastSiteValidationCreateMutation, requestHandlers.dastSiteValidationCreate],
]),
},
),
);
......@@ -90,17 +73,12 @@ describe('DastSiteValidation', () => {
const createComponent = componentFactory();
const createFullComponent = componentFactory(mount);
const withinComponent = () => within(wrapper.element);
const withinComponent = () => within(wrapper.find(GlModal).element);
const findByTestId = id => wrapper.find(`[data-testid="${id}"`);
const findDownloadButton = () => findByTestId('download-dast-text-file-validation-button');
const findValidationPathPrefix = () => findByTestId('dast-site-validation-path-prefix');
const findValidationPathInput = () => findByTestId('dast-site-validation-path-input');
const findValidateButton = () => findByTestId('validate-dast-site-button');
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findErrorMessage = () =>
withinComponent().queryByText(
/validation failed, please make sure that you follow the steps above with the chosen method./i,
);
const findRadioInputForValidationMethod = validationMethod =>
withinComponent().queryByRole('radio', {
name: new RegExp(`${validationMethod} validation`, 'i'),
......@@ -113,27 +91,74 @@ describe('DastSiteValidation', () => {
});
describe('rendering', () => {
beforeEach(() => {
createFullComponent();
});
describe('loading', () => {
beforeEach(() => {
createFullComponent();
});
it('renders a download button containing the token', () => {
const downloadButton = withinComponent().getByRole('button', {
name: 'Download validation text file',
it('renders a skeleton loader, no alert and no form group while token is being created', () => {
expect(wrapper.find(GlSkeletonLoader).exists()).toBe(true);
expect(wrapper.find(GlAlert).exists()).toBe(false);
expect(wrapper.find(GlFormGroup).exists()).toBe(false);
});
expect(downloadButton).not.toBeNull();
});
it.each(validationMethods)('renders a radio input for "%s" validation', validationMethod => {
expect(findRadioInputForValidationMethod(validationMethod)).not.toBe(null);
describe('error', () => {
beforeEach(async () => {
createFullComponent({
handlers: {
dastSiteTokenCreate: jest.fn().mockRejectedValue(new Error('GraphQL Network Error')),
},
});
await waitForPromises();
});
it('renders an alert and no skeleton loader or form group if token could not be created', () => {
expect(wrapper.find(GlAlert).exists()).toBe(true);
expect(wrapper.find(GlSkeletonLoader).exists()).toBe(false);
expect(wrapper.find(GlFormGroup).exists()).toBe(false);
});
});
it('renders an input group with the target URL prepended', () => {
const inputGroup = withinComponent().getByRole('group', {
name: 'Step 3 - Confirm text file location and validate',
describe('loaded', () => {
beforeEach(async () => {
createFullComponent();
await waitForPromises();
});
it('renders form groups, no alert and no skeleton loader', () => {
expect(wrapper.find(GlFormGroup).exists()).toBe(true);
expect(wrapper.find(GlAlert).exists()).toBe(false);
expect(wrapper.find(GlSkeletonLoader).exists()).toBe(false);
});
expect(inputGroup).not.toBeNull();
expect(inputGroup.textContent).toContain(targetUrl);
it('renders a download button containing the token', () => {
const downloadButton = withinComponent().getByRole('button', {
name: 'Download validation text file',
});
expect(downloadButton).not.toBeNull();
});
it.each(validationMethods)('renders a radio input for "%s" validation', validationMethod => {
expect(findRadioInputForValidationMethod(validationMethod)).not.toBe(null);
});
it('renders an input group with the target URL prepended', () => {
const inputGroup = withinComponent().getByRole('group', {
name: 'Step 3 - Confirm text file location and validate',
});
expect(inputGroup).not.toBeNull();
expect(inputGroup.textContent).toContain(targetUrl);
});
});
});
it('triggers the dastSiteTokenCreate GraphQL mutation', () => {
createComponent();
expect(requestHandlers.dastSiteTokenCreate).toHaveBeenCalledWith({
fullPath,
targetUrl,
});
});
......@@ -157,12 +182,13 @@ describe('DastSiteValidation', () => {
({ targetUrl: url, expectedPrefix, expectedPath, expectedTextFilePath }) => {
beforeEach(async () => {
createFullComponent({
propsData: {
targetUrl: url,
mountOptions: {
propsData: {
targetUrl: url,
},
},
});
await wrapper.vm.$nextTick();
await waitForPromises();
enableValidationMethod(validationMethod);
});
......@@ -182,6 +208,7 @@ describe('DastSiteValidation', () => {
it("input value isn't automatically updated if it has been changed manually", async () => {
createFullComponent();
await waitForPromises();
const customValidationPath = 'custom/validation/path.txt';
findValidationPathInput().setValue(customValidationPath);
await wrapper.setProps({
......@@ -193,8 +220,9 @@ describe('DastSiteValidation', () => {
});
describe('text file validation', () => {
it('clicking on the download button triggers a download of a text file containing the token', () => {
it('clicking on the download button triggers a download of a text file containing the token', async () => {
createComponent();
await waitForPromises();
findDownloadButton().vm.$emit('click');
expect(download).toHaveBeenCalledWith({
......@@ -208,7 +236,7 @@ describe('DastSiteValidation', () => {
beforeEach(async () => {
createFullComponent();
await wrapper.vm.$nextTick();
await waitForPromises();
enableValidationMethod('header');
});
......@@ -257,8 +285,9 @@ describe('DastSiteValidation', () => {
});
describe.each(validationMethods)('"%s" validation submission', validationMethod => {
beforeEach(() => {
beforeEach(async () => {
createFullComponent();
await waitForPromises();
});
describe('passed', () => {
......@@ -266,15 +295,6 @@ describe('DastSiteValidation', () => {
enableValidationMethod(validationMethod);
});
it('while validating, shows a loading state', async () => {
findValidateButton().trigger('click');
await wrapper.vm.$nextTick();
expect(findLoadingIcon().exists()).toBe(true);
expect(wrapper.text()).toContain('Validating...');
});
it('triggers the dastSiteValidationCreate GraphQL mutation', () => {
findValidateButton().trigger('click');
......@@ -285,60 +305,6 @@ describe('DastSiteValidation', () => {
validationStrategy: wrapper.vm.validationMethod,
});
});
it('on success, emits success event', async () => {
respondWith({
dastSiteValidation: jest
.fn()
.mockResolvedValue(responses.dastSiteValidation('PASSED_VALIDATION')),
});
findValidateButton().trigger('click');
await waitForPromises();
expect(wrapper.emitted('success')).toHaveLength(1);
});
});
describe('failed', () => {
beforeEach(() => {
respondWith({
dastSiteValidation: () =>
Promise.resolve(responses.dastSiteValidation(DAST_SITE_VALIDATION_STATUS.FAILED)),
});
});
it('shows failure message', async () => {
expect(findErrorMessage()).toBe(null);
findValidateButton().vm.$emit('click');
await waitForPromises();
expect(findErrorMessage()).not.toBe(null);
});
});
describe.each`
errorKind | errorResponse
${'top level error'} | ${() => Promise.reject(new Error('GraphQL Network Error'))}
${'errors as data'} | ${() => Promise.resolve(responses.dastSiteValidationCreate(['error#1', 'error#2']))}
`('$errorKind', ({ errorResponse }) => {
beforeEach(() => {
respondWith({
dastSiteValidationCreate: errorResponse,
});
});
it('on error, shows error state', async () => {
expect(findErrorMessage()).toBe(null);
findValidateButton().vm.$emit('click');
await waitForPromises();
expect(findErrorMessage()).not.toBe(null);
});
});
});
});
import { DAST_SITE_VALIDATION_STATUS } from 'ee/security_configuration/dast_site_validation/constants';
export const dastSiteValidationCreate = (errors = []) => ({
data: {
dastSiteValidationCreate: { status: DAST_SITE_VALIDATION_STATUS.PENDING, id: '1', errors },
},
});
export const dastSiteTokenCreate = ({ id = '1', token = '1', errors = [] }) => ({
data: {
dastSiteTokenCreate: {
id,
token,
errors,
},
},
});
......@@ -8434,9 +8434,6 @@ msgstr ""
msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Copy HTTP header to clipboard"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
msgstr ""
......@@ -8482,9 +8479,6 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
msgid "DastProfiles|Download validation text file"
msgstr ""
msgid "DastProfiles|Edit scanner profile"
msgstr ""
......@@ -8497,9 +8491,6 @@ msgstr ""
msgid "DastProfiles|Error Details"
msgstr ""
msgid "DastProfiles|Header validation"
msgstr ""
msgid "DastProfiles|Hide debug messages"
msgstr ""
......@@ -8572,58 +8563,64 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
msgid "DastProfiles|Site is not validated yet, please follow the steps."
msgid "DastProfiles|Spider timeout"
msgstr ""
msgid "DastProfiles|Spider timeout"
msgid "DastProfiles|Target URL"
msgstr ""
msgid "DastProfiles|Target timeout"
msgstr ""
msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
msgid "DastProfiles|Step 1 - Choose site validation method"
msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
msgid "DastProfiles|Step 2 - Add following HTTP header to your site"
msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
msgid "DastProfiles|Step 2 - Add following text to the target site"
msgid "DastProfiles|Username"
msgstr ""
msgid "DastProfiles|Step 3 - Confirm header location and validate"
msgid "DastProfiles|Username form field"
msgstr ""
msgid "DastProfiles|Step 3 - Confirm text file location and validate"
msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
msgid "DastProfiles|Target URL"
msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
msgid "DastProfiles|Target timeout"
msgid "DastSiteValidation|Download validation text file"
msgstr ""
msgid "DastProfiles|Text file validation"
msgid "DastSiteValidation|Header validation"
msgstr ""
msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgid "DastSiteValidation|Step 1 - Choose site validation method"
msgstr ""
msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
msgstr ""
msgid "DastProfiles|Turn on AJAX spider"
msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
msgid "DastProfiles|Username"
msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
msgid "DastProfiles|Username form field"
msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
msgid "DastProfiles|Validate"
msgid "DastSiteValidation|Text file validation"
msgstr ""
msgid "DastProfiles|Validating..."
msgid "DastSiteValidation|Validate"
msgstr ""
msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the chosen method."
msgid "DastSiteValidation|Validate target site"
msgstr ""
msgid "Data is still calculating..."
......
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