Commit f70d8fe5 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch 'jnnkl-sec-config-graphql-error' into 'master'

Add ErrorHandling for Network Errors on Secure-Config Page

See merge request gitlab-org/gitlab!72680
parents 8cd2e168 2b25c045
<script> <script>
import { GlTab, GlTabs, GlSprintf, GlLink } from '@gitlab/ui'; import { GlTab, GlTabs, GlSprintf, GlLink, GlAlert } from '@gitlab/ui';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue'; import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
...@@ -31,6 +31,7 @@ export default { ...@@ -31,6 +31,7 @@ export default {
AutoDevOpsAlert, AutoDevOpsAlert,
AutoDevOpsEnabledAlert, AutoDevOpsEnabledAlert,
FeatureCard, FeatureCard,
GlAlert,
GlLink, GlLink,
GlSprintf, GlSprintf,
GlTab, GlTab,
...@@ -79,6 +80,7 @@ export default { ...@@ -79,6 +80,7 @@ export default {
data() { data() {
return { return {
autoDevopsEnabledAlertDismissedProjects: [], autoDevopsEnabledAlertDismissedProjects: [],
errorMessage: '',
}; };
}, },
computed: { computed: {
...@@ -106,6 +108,12 @@ export default { ...@@ -106,6 +108,12 @@ export default {
dismissedProjects.add(this.projectPath); dismissedProjects.add(this.projectPath);
this.autoDevopsEnabledAlertDismissedProjects = Array.from(dismissedProjects); this.autoDevopsEnabledAlertDismissedProjects = Array.from(dismissedProjects);
}, },
onError(message) {
this.errorMessage = message;
},
dismissAlert() {
this.errorMessage = '';
},
}, },
autoDevopsEnabledAlertStorageKey: AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY, autoDevopsEnabledAlertStorageKey: AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY,
}; };
...@@ -113,6 +121,16 @@ export default { ...@@ -113,6 +121,16 @@ export default {
<template> <template>
<article> <article>
<gl-alert
v-if="errorMessage"
sticky
class="gl-top-8 gl-z-index-1"
data-testid="manage-via-mr-error-alert"
variant="danger"
@dismiss="dismissAlert"
>
{{ errorMessage }}
</gl-alert>
<local-storage-sync <local-storage-sync
v-model="autoDevopsEnabledAlertDismissedProjects" v-model="autoDevopsEnabledAlertDismissedProjects"
:storage-key="$options.autoDevopsEnabledAlertStorageKey" :storage-key="$options.autoDevopsEnabledAlertStorageKey"
...@@ -174,6 +192,7 @@ export default { ...@@ -174,6 +192,7 @@ export default {
data-testid="security-testing-card" data-testid="security-testing-card"
:feature="feature" :feature="feature"
class="gl-mb-6" class="gl-mb-6"
@error="onError"
/> />
</template> </template>
</section-layout> </section-layout>
...@@ -207,6 +226,7 @@ export default { ...@@ -207,6 +226,7 @@ export default {
:key="feature.type" :key="feature.type"
:feature="feature" :feature="feature"
class="gl-mb-6" class="gl-mb-6"
@error="onError"
/> />
</template> </template>
</section-layout> </section-layout>
......
...@@ -66,6 +66,11 @@ export default { ...@@ -66,6 +66,11 @@ export default {
return Boolean(name && description && configurationText); return Boolean(name && description && configurationText);
}, },
}, },
methods: {
onError(message) {
this.$emit('error', message);
},
},
i18n: { i18n: {
enabled: s__('SecurityConfiguration|Enabled'), enabled: s__('SecurityConfiguration|Enabled'),
notEnabled: s__('SecurityConfiguration|Not enabled'), notEnabled: s__('SecurityConfiguration|Not enabled'),
...@@ -129,6 +134,7 @@ export default { ...@@ -129,6 +134,7 @@ export default {
category="primary" category="primary"
class="gl-mt-5" class="gl-mt-5"
:data-qa-selector="`${feature.type}_mr_button`" :data-qa-selector="`${feature.type}_mr_button`"
@error="onError"
/> />
<gl-button <gl-button
......
import { GlTab } from '@gitlab/ui'; import { GlTab } from '@gitlab/ui';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisser'; import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisser';
import stubChildren from 'helpers/stub_children'; import stubChildren from 'helpers/stub_children';
...@@ -70,6 +71,7 @@ describe('App component', () => { ...@@ -70,6 +71,7 @@ describe('App component', () => {
const findTabs = () => wrapper.findAllComponents(GlTab); const findTabs = () => wrapper.findAllComponents(GlTab);
const findByTestId = (id) => wrapper.findByTestId(id); const findByTestId = (id) => wrapper.findByTestId(id);
const findFeatureCards = () => wrapper.findAllComponents(FeatureCard); const findFeatureCards = () => wrapper.findAllComponents(FeatureCard);
const findManageViaMRErrorAlert = () => wrapper.findByTestId('manage-via-mr-error-alert');
const findLink = ({ href, text, container = wrapper }) => { const findLink = ({ href, text, container = wrapper }) => {
const selector = `a[href="${href}"]`; const selector = `a[href="${href}"]`;
const link = container.find(selector); const link = container.find(selector);
...@@ -173,6 +175,43 @@ describe('App component', () => { ...@@ -173,6 +175,43 @@ describe('App component', () => {
}); });
}); });
describe('Manage via MR Error Alert', () => {
beforeEach(() => {
createComponent({
augmentedSecurityFeatures: securityFeaturesMock,
augmentedComplianceFeatures: complianceFeaturesMock,
});
});
describe('on initial load', () => {
it('should not show Manage via MR Error Alert', () => {
expect(findManageViaMRErrorAlert().exists()).toBe(false);
});
});
describe('when error occurs', () => {
it('should show Alert with error Message', async () => {
expect(findManageViaMRErrorAlert().exists()).toBe(false);
findFeatureCards().at(1).vm.$emit('error', 'There was a manage via MR error');
await nextTick();
expect(findManageViaMRErrorAlert().exists()).toBe(true);
expect(findManageViaMRErrorAlert().text()).toEqual('There was a manage via MR error');
});
it('should hide Alert when it is dismissed', async () => {
findFeatureCards().at(1).vm.$emit('error', 'There was a manage via MR error');
await nextTick();
expect(findManageViaMRErrorAlert().exists()).toBe(true);
findManageViaMRErrorAlert().vm.$emit('dismiss');
await nextTick();
expect(findManageViaMRErrorAlert().exists()).toBe(false);
});
});
});
describe('Auto DevOps hint alert', () => { describe('Auto DevOps hint alert', () => {
describe('given the right props', () => { describe('given the right props', () => {
beforeEach(() => { beforeEach(() => {
......
...@@ -80,7 +80,11 @@ describe('FeatureCard component', () => { ...@@ -80,7 +80,11 @@ describe('FeatureCard component', () => {
describe('basic structure', () => { describe('basic structure', () => {
beforeEach(() => { beforeEach(() => {
feature = makeFeature(); feature = makeFeature({
type: 'sast',
available: true,
canEnableByMergeRequest: true,
});
createComponent({ feature }); createComponent({ feature });
}); });
...@@ -97,6 +101,11 @@ describe('FeatureCard component', () => { ...@@ -97,6 +101,11 @@ describe('FeatureCard component', () => {
expect(links.exists()).toBe(true); expect(links.exists()).toBe(true);
expect(links).toHaveLength(1); expect(links).toHaveLength(1);
}); });
it('should catch and emit manage-via-mr-error', () => {
findManageViaMr().vm.$emit('error', 'There was a manage via MR error');
expect(wrapper.emitted('error')).toEqual([['There was a manage via MR error']]);
});
}); });
describe('status', () => { describe('status', () => {
......
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