Commit 1b97ff5b authored by Filipa Lacerda's avatar Filipa Lacerda

Improve CSS in modal for dast

Reuse modal.vue and delete old modal

Adds changelog entry

Update report_issues_spec.js

Fix broken test
parent 163b2b00
<script>
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import ExpandButton from '~/vue_shared/components/expand_button.vue';
export default {
name: 'ModalDast',
components: {
ExpandButton,
Icon,
},
props: {
title: {
type: String,
required: true,
default: '',
},
targetId: {
type: String,
required: false,
default: '',
},
description: {
type: String,
required: true,
default: '',
},
instances: {
type: Array,
required: false,
default: () => ([]),
},
},
computed: {
instancesLabel() {
return s__('ciReport|Instances');
},
},
mounted() {
$(this.$el).on('hidden.bs.modal', () => {
this.$emit('clearData');
});
},
};
</script>
<template>
<div
:id="targetId"
class="modal fade"
tabindex="-1"
role="dialog"
>
<div
class="modal-dialog modal-lg"
role="document"
>
<div class="modal-content">
<div class="modal-header">
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">
{{ title }}
</h4>
</div>
<div class="modal-body">
{{ description }}
<h5 class="prepend-top-20">{{ instancesLabel }}</h5>
<ul
v-if="instances"
class="report-block-list"
>
<li
v-for="(instance, i) in instances"
:key="i"
class="report-block-list-item-modal failed"
>
<icon
class="report-block-icon"
name="status_failed_borderless"
:size="32"
/>
{{ instance.method }}
<a
:href="instance.uri"
target="_blank"
rel="noopener noreferrer nofollow"
class="prepend-left-5"
>
{{ instance.uri }}
</a>
<expand-button v-if="instance.evidence">
<pre
slot="expanded"
class="block report-block-dast-code prepend-top-10">{{ instance.evidence }}</pre>
</expand-button>
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script>
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import Modal from './dast_modal.vue';
import Modal from '~/vue_shared/components/gl_modal.vue';
import ExpandButton from '~/vue_shared/components/expand_button.vue';
const modalDefaultData = {
modalId: 'modal-mrwidget-issue',
......@@ -16,6 +17,7 @@
components: {
Modal,
Icon,
ExpandButton,
},
props: {
issues: {
......@@ -79,6 +81,11 @@
return this.type === 'dast';
},
},
mounted() {
$(this.$refs.modal).on('hidden.bs.modal', () => {
this.clearModalData();
});
},
methods: {
shouldRenderPriority(issue) {
return this.hasPriority && issue.priority;
......@@ -117,88 +124,142 @@
};
</script>
<template>
<ul class="report-block-list">
<li
class="report-block-list-issue"
v-for="(issue, index) in issues"
:key="index"
>
<div
class="report-block-list-icon append-right-5"
:class="{
failed: isStatusFailed,
success: isStatusSuccess,
neutral: isStatusNeutral
}">
<icon
:name="iconName"
:size="32"
/>
</div>
<div class="report-block-list-issue-description">
<div class="report-block-list-issue-description-text append-right-5">
<template v-if="isStatusSuccess && isTypeQuality">{{ fixedLabel }}</template>
<template v-if="shouldRenderPriority(issue)">{{ issue.priority }}:</template>
<div>
<ul class="report-block-list">
<li
class="report-block-list-issue"
v-for="(issue, index) in issues"
:key="index"
>
<div
class="report-block-list-icon append-right-5"
:class="{
failed: isStatusFailed,
success: isStatusSuccess,
neutral: isStatusNeutral,
}"
>
<icon
:name="iconName"
:size="32"
/>
</div>
<div class="report-block-list-issue-description">
<div class="report-block-list-issue-description-text append-right-5">
<template v-if="isStatusSuccess && isTypeQuality">{{ fixedLabel }}</template>
<template v-if="shouldRenderPriority(issue)">{{ issue.priority }}:</template>
<template v-if="isTypeDocker">
<a
v-if="issue.nameLink"
:href="issue.nameLink"
target="_blank"
rel="noopener noreferrer nofollow"
>{{ issue.name }}</a>
<template v-if="isTypeDocker">
<a
v-if="issue.nameLink"
:href="issue.nameLink"
target="_blank"
rel="noopener noreferrer nofollow"
>{{ issue.name }}</a>
<template v-else>
{{ issue.name }}
</template>
</template>
<template v-else-if="isTypeDast">
<button
type="button"
@click="openDastModal(issue, index)"
data-toggle="modal"
class="js-modal-dast btn-link btn-blank btn-open-modal"
:data-target="modalTargetId"
>
{{ issue.name }}
</button>
</template>
<template v-else>
{{ issue.name }}
{{ issue.name }}<template v-if="issue.score">:
<strong>{{ formatScore(issue.score) }}</strong></template>
</template>
</template>
<template v-else-if="isTypeDast">
<button
type="button"
@click="openDastModal(issue, index)"
data-toggle="modal"
class="btn-link btn-blank btn-open-modal"
:data-target="modalTargetId"
>
{{ issue.name }}
</button>
</template>
<template v-else>
{{ issue.name }}<template v-if="issue.score">:
<strong>{{ formatScore(issue.score) }}</strong></template>
</template>
<template v-if="isTypePerformance && issue.delta != null">
({{ issue.delta >= 0 ? '+' : '' }}{{ formatScore(issue.delta) }})
</template>
</div>
<div class="report-block-list-issue-description-link">
<template v-if="issue.path">
in
<template v-if="isTypePerformance && issue.delta != null">
({{ issue.delta >= 0 ? '+' : '' }}{{ formatScore(issue.delta) }})
</template>
</div>
<div class="report-block-list-issue-description-link">
<template v-if="issue.path">
in
<a
v-if="issue.urlPath"
:href="issue.urlPath"
target="_blank"
rel="noopener noreferrer nofollow"
class="break-link"
>
{{ issue.path }}<template v-if="issue.line">:{{ issue.line }}</template>
</a>
<template v-else>
{{ issue.path }}<template v-if="issue.line">:{{ issue.line }}</template>
<a
v-if="issue.urlPath"
:href="issue.urlPath"
target="_blank"
rel="noopener noreferrer nofollow"
class="break-link"
>
{{ issue.path }}<template v-if="issue.line">:{{ issue.line }}</template>
</a>
<template v-else>
{{ issue.path }}<template v-if="issue.line">:{{ issue.line }}</template>
</template>
</template>
</template>
</div>
</div>
</div>
</li>
</li>
</ul>
<modal
:target-id="modalId"
:title="modalTitle"
:hide-footer="true"
:description="modalDesc"
:instances="modalInstances"
@clearData="clearModalData()"
/>
</ul>
v-if="isTypeDast"
:id="modalId"
:header-title-text="modalTitle"
ref="modal"
class="modal-security-report-dast"
>
<slot class="modal-lg">
{{ modalDesc }}
<h5 class="prepend-top-20">
{{ s__('ciReport|Instances') }}
</h5>
<ul
v-if="modalInstances"
class="report-block-list"
>
<li
v-for="(instance, i) in modalInstances"
:key="i"
class="report-block-list-issue"
>
<div class="report-block-list-icon append-right-5 failed">
<icon
name="status_failed_borderless"
:size="32"
/>
</div>
<div class="report-block-list-issue-description">
<div class="report-block-list-issue-description-text append-right-5">
{{ instance.method }}
</div>
<div class="report-block-list-issue-description-link">
<a
:href="instance.uri"
target="_blank"
rel="noopener noreferrer nofollow"
class="prepend-left-5"
>
{{ instance.uri }}
</a>
</div>
<div>
<expand-button v-if="instance.evidence">
<pre
slot="expanded"
class="block report-block-dast-code prepend-top-10"
>{{ instance.evidence }}</pre>
</expand-button>
</div>
</div>
</li>
</ul>
</slot>
<div slot="footer">
</div>
</modal>
</div>
</template>
......@@ -64,3 +64,17 @@
text-align: left;
}
}
.report-block-issue-code {
width: 100%;
}
.modal-security-report-dast {
.modal-dialog {
width: $modal-lg;
}
// TODO remove this when gl_modal support not rendering the footer
.moda-footer {
display: none;
}
}
---
title: Improve security reports to handle big links and to work on mobile devices
merge_request: 4671
author:
type: fixed
import Vue from 'vue';
import modal from 'ee/vue_shared/security_reports/components/dast_modal.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('mr widget modal', () => {
let vm;
let Modal;
beforeEach(() => {
Modal = Vue.extend(modal);
vm = mountComponent(Modal, {
title: 'Title',
targetId: 'targetId',
instances: [{
uri: 'uri',
method: 'GET',
evidence: 'evidence',
}],
description: 'Description!',
});
});
afterEach(() => {
vm.$destroy();
});
it('renders a title', () => {
expect(vm.$el.querySelector('.modal-title').textContent.trim()).toEqual('Title');
});
it('renders the target id', () => {
expect(vm.$el.getAttribute('id')).toEqual('targetId');
});
it('renders the description', () => {
expect(vm.$el.querySelector('.modal-body').textContent).toContain('Description!');
});
it('renders list of instances', () => {
const instance = vm.$el.querySelector('.modal-body li').textContent;
expect(instance).toContain('uri');
expect(instance).toContain('GET');
expect(instance).toContain('evidence');
});
});
......@@ -159,5 +159,20 @@ describe('Report issues', () => {
expect(vm.$el.textContent).toContain(parsedDast[0].name);
expect(vm.$el.textContent).toContain(parsedDast[0].priority);
});
it('opens modal with more information and list of instances', (done) => {
vm.$el.querySelector('.js-modal-dast ').click();
Vue.nextTick(() => {
expect(vm.$el.querySelector('.modal-title').textContent.trim()).toEqual('Low (Medium): Absence of Anti-CSRF Tokens');
expect(vm.$el.querySelector('.modal-body').textContent).toContain('No Anti-CSRF tokens were found in a HTML submission form.');
const instance = vm.$el.querySelector('.modal-body li').textContent;
expect(instance).toContain('http://192.168.32.236:3001/explore?sort=latest_activity_desc');
expect(instance).toContain('GET');
done();
});
});
});
});
......@@ -192,7 +192,7 @@ describe('Report section', () => {
it('should show the report by default', () => {
expect(
vm.$el.querySelectorAll('.report-block-list .report-block-list-item').length,
vm.$el.querySelectorAll('.report-block-list .report-block-list-issue').length,
).toEqual(codequalityParsedIssues.length);
});
});
......
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