Commit daa15704 authored by Samantha Ming's avatar Samantha Ming

Update TrainingProviderList to use Apollo

Refactor apollo query into TrainingProviderList component
and update specs accordingly.
parent 92f4b73c
...@@ -4,7 +4,6 @@ import { __, s__ } from '~/locale'; ...@@ -4,7 +4,6 @@ import { __, s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
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';
import securityTrainingProvidersQuery from '../graphql/security_training_providers.query.graphql';
import AutoDevOpsAlert from './auto_dev_ops_alert.vue'; import AutoDevOpsAlert from './auto_dev_ops_alert.vue';
import AutoDevOpsEnabledAlert from './auto_dev_ops_enabled_alert.vue'; import AutoDevOpsEnabledAlert from './auto_dev_ops_enabled_alert.vue';
import { AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY } from './constants'; import { AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY } from './constants';
...@@ -88,7 +87,6 @@ export default { ...@@ -88,7 +87,6 @@ export default {
return { return {
autoDevopsEnabledAlertDismissedProjects: [], autoDevopsEnabledAlertDismissedProjects: [],
errorMessage: '', errorMessage: '',
securityTrainingProviders: [],
}; };
}, },
computed: { computed: {
...@@ -110,11 +108,6 @@ export default { ...@@ -110,11 +108,6 @@ export default {
); );
}, },
}, },
apollo: {
securityTrainingProviders: {
query: securityTrainingProvidersQuery,
},
},
methods: { methods: {
dismissAutoDevopsEnabledAlert() { dismissAutoDevopsEnabledAlert() {
const dismissedProjects = new Set(this.autoDevopsEnabledAlertDismissedProjects); const dismissedProjects = new Set(this.autoDevopsEnabledAlertDismissedProjects);
...@@ -251,10 +244,7 @@ export default { ...@@ -251,10 +244,7 @@ export default {
> >
<section-layout :heading="$options.i18n.securityTraining"> <section-layout :heading="$options.i18n.securityTraining">
<template #features> <template #features>
<training-provider-list <training-provider-list />
:loading="$apollo.queries.securityTrainingProviders.loading"
:providers="securityTrainingProviders"
/>
</template> </template>
</section-layout> </section-layout>
</gl-tab> </gl-tab>
......
<script> <script>
import { GlCard, GlToggle, GlLink, GlSkeletonLoader } from '@gitlab/ui'; import { GlCard, GlToggle, GlLink, GlSkeletonLoader } from '@gitlab/ui';
import securityTrainingProvidersQuery from '../graphql/security_training_providers.query.graphql';
export default { export default {
components: { components: {
...@@ -8,15 +9,19 @@ export default { ...@@ -8,15 +9,19 @@ export default {
GlLink, GlLink,
GlSkeletonLoader, GlSkeletonLoader,
}, },
props: { apollo: {
providers: { securityTrainingProviders: {
type: Array, query: securityTrainingProvidersQuery,
required: true,
}, },
loading: { },
type: Boolean, data() {
required: false, return {
default: false, securityTrainingProviders: [],
};
},
computed: {
isLoading() {
return this.$apollo.queries.securityTrainingProviders.loading;
}, },
}, },
}; };
...@@ -24,7 +29,7 @@ export default { ...@@ -24,7 +29,7 @@ export default {
<template> <template>
<div <div
v-if="loading" v-if="isLoading"
class="gl-bg-white gl-py-6 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100" class="gl-bg-white gl-py-6 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100"
> >
<gl-skeleton-loader :width="350" :height="44"> <gl-skeleton-loader :width="350" :height="44">
...@@ -34,7 +39,11 @@ export default { ...@@ -34,7 +39,11 @@ export default {
</gl-skeleton-loader> </gl-skeleton-loader>
</div> </div>
<ul v-else class="gl-list-style-none gl-m-0 gl-p-0"> <ul v-else class="gl-list-style-none gl-m-0 gl-p-0">
<li v-for="{ id, isEnabled, name, description, url } in providers" :key="id" class="gl-mb-6"> <li
v-for="{ id, isEnabled, name, description, url } in securityTrainingProviders"
:key="id"
class="gl-mb-6"
>
<gl-card> <gl-card>
<div class="gl-display-flex"> <div class="gl-display-flex">
<gl-toggle :value="isEnabled" :label="__('Training mode')" label-position="hidden" /> <gl-toggle :value="isEnabled" :label="__('Training mode')" label-position="hidden" />
......
import { GlTab } from '@gitlab/ui'; import { GlTab } from '@gitlab/ui';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue'; import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
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';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import SecurityConfigurationApp, { i18n } from '~/security_configuration/components/app.vue'; import SecurityConfigurationApp, { i18n } from '~/security_configuration/components/app.vue';
import AutoDevopsAlert from '~/security_configuration/components/auto_dev_ops_alert.vue'; import AutoDevopsAlert from '~/security_configuration/components/auto_dev_ops_alert.vue';
import AutoDevopsEnabledAlert from '~/security_configuration/components/auto_dev_ops_enabled_alert.vue'; import AutoDevopsEnabledAlert from '~/security_configuration/components/auto_dev_ops_enabled_alert.vue';
...@@ -29,8 +27,6 @@ import { ...@@ -29,8 +27,6 @@ import {
REPORT_TYPE_LICENSE_COMPLIANCE, REPORT_TYPE_LICENSE_COMPLIANCE,
REPORT_TYPE_SAST, REPORT_TYPE_SAST,
} from '~/vue_shared/security_reports/constants'; } from '~/vue_shared/security_reports/constants';
import waitForPromises from 'helpers/wait_for_promises';
import { securityTrainingProviders } from '../mock_data';
const upgradePath = '/upgrade'; const upgradePath = '/upgrade';
const autoDevopsHelpPagePath = '/autoDevopsHelpPagePath'; const autoDevopsHelpPagePath = '/autoDevopsHelpPagePath';
...@@ -39,21 +35,10 @@ const gitlabCiHistoryPath = 'test/historyPath'; ...@@ -39,21 +35,10 @@ const gitlabCiHistoryPath = 'test/historyPath';
const projectPath = 'namespace/project'; const projectPath = 'namespace/project';
useLocalStorageSpy(); useLocalStorageSpy();
Vue.use(VueApollo);
describe('App component', () => { describe('App component', () => {
let wrapper; let wrapper;
let userCalloutDismissSpy; let userCalloutDismissSpy;
let mockApollo;
let mockSecurityTrainingProvidersData;
const mockResolvers = {
Query: {
securityTrainingProviders() {
return securityTrainingProviders;
},
},
};
const createComponent = ({ const createComponent = ({
shouldShowCallout = true, shouldShowCallout = true,
...@@ -61,11 +46,9 @@ describe('App component', () => { ...@@ -61,11 +46,9 @@ describe('App component', () => {
...propsData ...propsData
}) => { }) => {
userCalloutDismissSpy = jest.fn(); userCalloutDismissSpy = jest.fn();
mockApollo = createMockApollo([], mockResolvers);
wrapper = extendedWrapper( wrapper = extendedWrapper(
mount(SecurityConfigurationApp, { mount(SecurityConfigurationApp, {
apolloProvider: mockApollo,
propsData, propsData,
provide: { provide: {
upgradePath, upgradePath,
...@@ -148,14 +131,10 @@ describe('App component', () => { ...@@ -148,14 +131,10 @@ describe('App component', () => {
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
mockApollo = null;
}); });
describe('basic structure', () => { describe('basic structure', () => {
beforeEach(() => { beforeEach(() => {
mockSecurityTrainingProvidersData = jest.fn();
mockSecurityTrainingProvidersData.mockResolvedValue(securityTrainingProviders);
createComponent({ createComponent({
augmentedSecurityFeatures: securityFeaturesMock, augmentedSecurityFeatures: securityFeaturesMock,
augmentedComplianceFeatures: complianceFeaturesMock, augmentedComplianceFeatures: complianceFeaturesMock,
...@@ -204,9 +183,8 @@ describe('App component', () => { ...@@ -204,9 +183,8 @@ describe('App component', () => {
expect(findSecurityViewHistoryLink().exists()).toBe(false); expect(findSecurityViewHistoryLink().exists()).toBe(false);
}); });
it('renders training provider list with correct props', async () => { it('renders TrainingProviderList component', () => {
await waitForPromises(); expect(findTrainingProviderList().exists()).toBe(true);
expect(findTrainingProviderList().props('providers')).toEqual(securityTrainingProviders);
}); });
}); });
......
import { GlLink, GlToggle, GlCard, GlSkeletonLoader } from '@gitlab/ui'; import { GlLink, GlToggle, GlCard, GlSkeletonLoader } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import TrainingProviderList from '~/security_configuration/components/training_provider_list.vue'; import TrainingProviderList from '~/security_configuration/components/training_provider_list.vue';
import { securityTrainingProviders } from '../mock_data'; import waitForPromises from 'helpers/wait_for_promises';
import { securityTrainingProviders, mockResolvers } from '../mock_data';
const DEFAULT_PROPS = { Vue.use(VueApollo);
providers: securityTrainingProviders,
};
describe('TrainingProviderList component', () => { describe('TrainingProviderList component', () => {
let wrapper; let wrapper;
let mockApollo;
let mockSecurityTrainingProvidersData;
const createComponent = () => {
mockApollo = createMockApollo([], mockResolvers);
const createComponent = (props = {}) => {
wrapper = shallowMount(TrainingProviderList, { wrapper = shallowMount(TrainingProviderList, {
propsData: { apolloProvider: mockApollo,
...DEFAULT_PROPS,
...props,
},
}); });
}; };
const waitForQueryToBeLoaded = () => waitForPromises();
const findCards = () => wrapper.findAllComponents(GlCard); const findCards = () => wrapper.findAllComponents(GlCard);
const findLinks = () => wrapper.findAllComponents(GlLink); const findLinks = () => wrapper.findAllComponents(GlLink);
const findToggles = () => wrapper.findAllComponents(GlToggle); const findToggles = () => wrapper.findAllComponents(GlToggle);
const findLoader = () => wrapper.findComponent(GlSkeletonLoader); const findLoader = () => wrapper.findComponent(GlSkeletonLoader);
beforeEach(() => {
mockSecurityTrainingProvidersData = jest.fn();
mockSecurityTrainingProvidersData.mockResolvedValue(securityTrainingProviders);
createComponent();
});
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
mockApollo = null;
});
describe('when loading', () => {
it('shows the loader', () => {
expect(findLoader().exists()).toBe(true);
});
it('does not show the cards', () => {
expect(findCards().exists()).toBe(false);
});
}); });
describe('basic structure', () => { describe('basic structure', () => {
beforeEach(() => { beforeEach(async () => {
createComponent(); await waitForQueryToBeLoaded();
}); });
it('renders correct amount of cards', () => { it('renders correct amount of cards', () => {
expect(findCards()).toHaveLength(DEFAULT_PROPS.providers.length); expect(findCards()).toHaveLength(securityTrainingProviders.length);
}); });
DEFAULT_PROPS.providers.forEach(({ name, description, url, isEnabled }, index) => { securityTrainingProviders.forEach(({ name, description, url, isEnabled }, index) => {
it(`shows the name for card ${index}`, () => { it(`shows the name for card ${index}`, () => {
expect(findCards().at(index).text()).toContain(name); expect(findCards().at(index).text()).toContain(name);
}); });
...@@ -56,25 +79,10 @@ describe('TrainingProviderList component', () => { ...@@ -56,25 +79,10 @@ describe('TrainingProviderList component', () => {
it(`shows the toggle with the correct value for card ${index}`, () => { it(`shows the toggle with the correct value for card ${index}`, () => {
expect(findToggles().at(index).props('value')).toEqual(isEnabled); expect(findToggles().at(index).props('value')).toEqual(isEnabled);
}); });
});
});
describe('loading', () => {
beforeEach(() => {
createComponent({ loading: true });
});
it('shows the loader', () => {
expect(findLoader().exists()).toBe(true);
});
it('does not show the cards', () => {
expect(findCards().exists()).toBe(false);
});
it('does not show loader when not loading', () => { it('does not show loader when query is populated', () => {
createComponent({ loading: false });
expect(findLoader().exists()).toBe(false); expect(findLoader().exists()).toBe(false);
}); });
}); });
});
}); });
...@@ -20,3 +20,11 @@ export const securityTrainingProvidersResponse = { ...@@ -20,3 +20,11 @@ export const securityTrainingProvidersResponse = {
securityTrainingProviders, securityTrainingProviders,
}, },
}; };
export const mockResolvers = {
Query: {
securityTrainingProviders() {
return securityTrainingProviders;
},
},
};
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