Commit 3334ec9a authored by David O'Regan's avatar David O'Regan

Merge branch '294444-apollo-management-table-part-4' into 'master'

Render Corpus Management Table - Part 4

See merge request gitlab-org/gitlab!57433
parents 6d8cb44b 8eaee929
import initCorpusManagement from 'ee/security_configuration/corpus_management/corpus_management_bundle';
initCorpusManagement();
<script> <script>
import { GlLoadingIcon, GlLink } from '@gitlab/ui';
import CorpusTable from 'ee/security_configuration/corpus_management/components/corpus_table.vue';
import CorpusUpload from 'ee/security_configuration/corpus_management/components/corpus_upload.vue';
import { s__, __ } from '~/locale';
import getCorpusesQuery from '../graphql/queries/get_corpuses.query.graphql';
export default { export default {
props: { components: {
projectFullPath: { GlLoadingIcon,
type: String, GlLink,
required: true, CorpusTable,
CorpusUpload,
},
apollo: {
states: {
query: getCorpusesQuery,
variables() {
return {
projectPath: this.projectFullPath,
};
},
update: (data) => {
return data;
},
error() {
this.states = null;
},
},
},
inject: ['projectFullPath', 'corpusHelpPath'],
i18n: {
header: s__('CorpusManagement|Fuzz testing corpus management'),
subHeader: s__(
'CorpusManagement|Corpus are used in fuzz testing as mutation source to Improve future testing.',
),
learnMore: __('Learn More'),
},
computed: {
mockedData() {
return this.states?.mockedPackages.data || [];
},
isLoading() {
return this.$apollo.loading;
},
totalSize() {
return this.states?.mockedPackages.totalSize;
}, },
}, },
}; };
</script> </script>
<template> <template>
<span></span> <div>
<header>
<h4 class="gl-my-5">
{{ this.$options.i18n.header }}
</h4>
<h5 class="gl-font-base gl-font-weight-100">
{{ this.$options.i18n.subHeader }}
<gl-link :href="corpusHelpPath">{{ this.$options.i18n.learnMore }}</gl-link>
</h5>
</header>
<gl-loading-icon v-if="isLoading" size="lg" />
<template v-else>
<corpus-upload :total-size="totalSize" />
<corpus-table :corpuses="mockedData" />
</template>
</div>
</template> </template>
<script>
import { GlTable } from '@gitlab/ui';
import Actions from 'ee/security_configuration/corpus_management/components/columns/actions.vue';
import Name from 'ee/security_configuration/corpus_management/components/columns/name.vue';
import Target from 'ee/security_configuration/corpus_management/components/columns/target.vue';
import { s__ } from '~/locale';
import UserDate from '~/vue_shared/components/user_date.vue';
import deleteCorpusMutation from '../graphql/mutations/delete_corpus.mutation.graphql';
const thClass = 'gl-bg-transparent! gl-border-gray-100! gl-border-b-solid! gl-border-b-1!';
export default {
components: {
GlTable,
Name,
Target,
UserDate,
Actions,
},
inject: ['projectFullPath'],
props: {
corpuses: {
type: Array,
required: true,
},
},
fields: [
{
key: 'name',
label: s__('CorpusManagement|Corpus name'),
thClass,
},
{
key: 'target',
label: s__('CorpusManagement|Target'),
thClass,
},
{
key: 'lastUpdated',
label: s__('CorpusManagement|Last updated'),
thClass,
},
{
key: 'lastUsed',
label: s__('CorpusManagement|Last used'),
thClass,
},
{
key: 'actions',
label: s__('CorpusManagement|Actions'),
thClass,
},
],
methods: {
onDelete({ name }) {
this.$apollo.mutate({
mutation: deleteCorpusMutation,
variables: { name, projectPath: this.projectFullPath },
});
},
},
};
</script>
<template>
<gl-table :items="corpuses" :fields="$options.fields">
<template #cell(name)="{ item }">
<name :corpus="item" />
</template>
<template #cell(target)="{ item }">
<target :target="item.target" />
</template>
<template #cell(lastUpdated)="{ item }">
<user-date :date="item.lastUpdated" />
</template>
<template #cell(lastUsed)="{ item }">
<user-date :date="item.lastUsed" />
</template>
<template #cell(actions)="{ item }">
<actions :corpus="item" @delete="onDelete" />
</template>
</gl-table>
</template>
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import CorpusManagement from './components/corpus_management.vue'; import CorpusManagement from './components/corpus_management.vue';
import resolvers from './graphql/resolvers/resolvers';
Vue.use(VueApollo);
export default () => { export default () => {
const el = document.querySelector('.js-corpus-management'); const el = document.querySelector('.js-corpus-management');
...@@ -8,20 +14,36 @@ export default () => { ...@@ -8,20 +14,36 @@ export default () => {
return undefined; return undefined;
} }
const defaultClient = createDefaultClient(resolvers, {
cacheConfig: {
dataIdFromObject: (object) => {
return object.id || defaultDataIdFromObject(object);
},
},
});
const { const {
dataset: { projectFullPath }, dataset: { projectFullPath },
} = el; } = el;
const props = { let {
dataset: { corpusHelpPath },
} = el;
// TODO: This is fake data for a POC and will be removed as part of https://gitlab.com/groups/gitlab-org/-/epics/5017
corpusHelpPath = 'https://docs.gitlab.com/ee/user/application_security/coverage_fuzzing/';
const provide = {
projectFullPath, projectFullPath,
corpusHelpPath,
}; };
return new Vue({ return new Vue({
el, el,
provide,
apolloProvider: new VueApollo({ defaultClient }),
render(h) { render(h) {
return h(CorpusManagement, { return h(CorpusManagement, {});
props,
});
}, },
}); });
}; };
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`EE - CorpusManagement corpus management when loaded renders the correct header 1`] = `
<header>
<h4
class="gl-my-5"
>
Fuzz testing corpus management
</h4>
<h5
class="gl-font-base gl-font-weight-100"
>
Corpus are used in fuzz testing as mutation source to Improve future testing.
<gl-link-stub
href="/docs/corpus-management"
>
Learn More
</gl-link-stub>
</h5>
</header>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Corpus table corpus management renders with the correct columns 1`] = `
<tr
class=""
role="row"
>
<th
aria-colindex="1"
class="gl-bg-transparent! gl-border-gray-100! gl-border-b-solid! gl-border-b-1!"
role="columnheader"
scope="col"
>
Corpus name
</th>
<th
aria-colindex="2"
class="gl-bg-transparent! gl-border-gray-100! gl-border-b-solid! gl-border-b-1!"
role="columnheader"
scope="col"
>
Target
</th>
<th
aria-colindex="3"
class="gl-bg-transparent! gl-border-gray-100! gl-border-b-solid! gl-border-b-1!"
role="columnheader"
scope="col"
>
Last updated
</th>
<th
aria-colindex="4"
class="gl-bg-transparent! gl-border-gray-100! gl-border-b-solid! gl-border-b-1!"
role="columnheader"
scope="col"
>
Last used
</th>
<th
aria-colindex="5"
class="gl-bg-transparent! gl-border-gray-100! gl-border-b-solid! gl-border-b-1!"
role="columnheader"
scope="col"
>
Actions
</th>
</tr>
`;
import { GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import CorpusManagement from 'ee/security_configuration/corpus_management/components/corpus_management.vue'; import CorpusManagement from 'ee/security_configuration/corpus_management/components/corpus_management.vue';
import CorpusTable from 'ee/security_configuration/corpus_management/components/corpus_table.vue';
import CorpusUpload from 'ee/security_configuration/corpus_management/components/corpus_upload.vue';
const TEST_PROJECT_FULL_PATH = '/namespace/project'; const TEST_PROJECT_FULL_PATH = '/namespace/project';
const TEST_CORPUS_HELP_PATH = '/docs/corpus-management';
describe('EE - CorpusManagement', () => { describe('EE - CorpusManagement', () => {
let wrapper; let wrapper;
const createComponentFactory = (mountFn = shallowMount) => (options = {}) => { const createComponentFactory = (mountFn = shallowMount) => (options = {}) => {
const defaultProps = { const defaultMocks = {
projectFullPath: TEST_PROJECT_FULL_PATH, $apollo: {
loading: false,
},
}; };
wrapper = mountFn(CorpusManagement, { wrapper = mountFn(CorpusManagement, {
propsData: defaultProps, mocks: defaultMocks,
provide: {
projectFullPath: TEST_PROJECT_FULL_PATH,
corpusHelpPath: TEST_CORPUS_HELP_PATH,
},
...options, ...options,
}); });
}; };
...@@ -23,9 +34,41 @@ describe('EE - CorpusManagement', () => { ...@@ -23,9 +34,41 @@ describe('EE - CorpusManagement', () => {
}); });
describe('corpus management', () => { describe('corpus management', () => {
describe('when loaded', () => {
beforeEach(() => {
const data = () => {
return { states: { mockedPackages: { totalSize: 12 } } };
};
createComponent({ data });
});
it('bootstraps and renders the component', () => { it('bootstraps and renders the component', () => {
createComponent(); expect(wrapper.findComponent(CorpusManagement).exists()).toBe(true);
expect(wrapper.find(CorpusManagement).exists()).toBe(true); expect(wrapper.findComponent(CorpusTable).exists()).toBe(true);
expect(wrapper.findComponent(CorpusUpload).exists()).toBe(true);
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
});
it('renders the correct header', () => {
const header = wrapper.findComponent(CorpusManagement).find('header');
expect(header.element).toMatchSnapshot();
});
});
describe('when loading', () => {
it('shows loading state when loading', () => {
const mocks = {
$apollo: {
loading: jest.fn().mockResolvedValue(true),
},
};
createComponent({ mocks });
expect(wrapper.findComponent(CorpusManagement).exists()).toBe(true);
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
expect(wrapper.findComponent(CorpusTable).exists()).toBe(false);
expect(wrapper.findComponent(CorpusUpload).exists()).toBe(false);
});
}); });
}); });
}); });
import { mount } from '@vue/test-utils';
import Actions from 'ee/security_configuration/corpus_management/components/columns/actions.vue';
import CorpusTable from 'ee/security_configuration/corpus_management/components/corpus_table.vue';
import { corpuses } from './mock_data';
const TEST_PROJECT_FULL_PATH = '/namespace/project';
describe('Corpus table', () => {
let wrapper;
const createComponentFactory = () => (options = {}) => {
const defaultProps = {
corpuses,
};
const defaultMocks = {
$apollo: {
mutate: jest.fn().mockResolvedValue(),
},
};
wrapper = mount(CorpusTable, {
propsData: defaultProps,
provide: {
projectFullPath: TEST_PROJECT_FULL_PATH,
},
mocks: defaultMocks,
...options,
});
};
const createComponent = createComponentFactory();
afterEach(() => {
wrapper.destroy();
});
describe('corpus management', () => {
beforeEach(() => {
createComponent();
});
it('bootstraps and renders the component', () => {
expect(wrapper.findComponent(CorpusTable).exists()).toBe(true);
});
it('renders with the correct columns', () => {
const columnHeaders = wrapper.findComponent(CorpusTable).find('thead tr');
expect(columnHeaders.element).toMatchSnapshot();
});
it('triggers the corpus deletion mutation', () => {
const {
$apollo: { mutate },
} = wrapper.vm;
const actionComponent = wrapper.findComponent(Actions);
expect(mutate).not.toHaveBeenCalled();
actionComponent.vm.$emit('delete', 'corpus-name');
expect(mutate).toHaveBeenCalledTimes(1);
});
});
});
export const corpuses = [ export const corpuses = [
{ {
name: 'Test corpus 1', name: 'Test corpus 1',
lastUpdated: new Date(2021, 1, 12), lastUpdated: new Date(2021, 1, 12).toString(),
lastUsed: new Date(2020, 4, 3), lastUsed: new Date(2020, 4, 3).toString(),
latestJobPath: 'gitlab-examples/security/security-reports/-/jobs/1107103952', latestJobPath: 'gitlab-examples/security/security-reports/-/jobs/1107103952',
target: '', target: '',
downloadPath: 'farias-gl/go-fuzzing-example/-/jobs/959593462/artifacts/download', downloadPath: 'farias-gl/go-fuzzing-example/-/jobs/959593462/artifacts/download',
...@@ -10,8 +10,8 @@ export const corpuses = [ ...@@ -10,8 +10,8 @@ export const corpuses = [
}, },
{ {
name: 'Corpus-sample-1-5830-2393', name: 'Corpus-sample-1-5830-2393',
lastUpdated: new Date(2021, 2, 5), lastUpdated: new Date(2021, 2, 5).toString(),
lastUsed: new Date(2021, 6, 3), lastUsed: new Date(2021, 6, 3).toString(),
latestJobPath: 'gitlab-examples/security/security-reports/-/jobs/1107103952', latestJobPath: 'gitlab-examples/security/security-reports/-/jobs/1107103952',
downloadPath: 'farias-gl/go-fuzzing-example/-/jobs/959593462/artifacts/download', downloadPath: 'farias-gl/go-fuzzing-example/-/jobs/959593462/artifacts/download',
target: '294444-apollo-management-table', target: '294444-apollo-management-table',
...@@ -19,16 +19,16 @@ export const corpuses = [ ...@@ -19,16 +19,16 @@ export const corpuses = [
}, },
{ {
name: 'Corpus-sample-2-1431-4425', name: 'Corpus-sample-2-1431-4425',
lastUpdated: new Date(2021, 1, 17), lastUpdated: new Date(2021, 1, 17).toString(),
lastUsed: new Date(2020, 4, 23), lastUsed: new Date(2020, 4, 23).toString(),
latestJobPath: '', latestJobPath: '',
target: '120498-another-branch', target: '120498-another-branch',
size: 3.21e8, size: 3.21e8,
}, },
{ {
name: 'Corpus-sample-5-5830-1393', name: 'Corpus-sample-5-5830-1393',
lastUpdated: new Date(2021, 4, 9), lastUpdated: new Date(2021, 4, 9).toString(),
lastUsed: new Date(2020, 8, 4), lastUsed: new Date(2020, 8, 4).toString(),
latestJobPath: 'gitlab-examples/security/security-reports/-/jobs/1107103952', latestJobPath: 'gitlab-examples/security/security-reports/-/jobs/1107103952',
downloadPath: 'farias-gl/go-fuzzing-example/-/jobs/959593462/artifacts/download', downloadPath: 'farias-gl/go-fuzzing-example/-/jobs/959593462/artifacts/download',
target: '120341-feature-branch', target: '120341-feature-branch',
...@@ -36,8 +36,8 @@ export const corpuses = [ ...@@ -36,8 +36,8 @@ export const corpuses = [
}, },
{ {
name: 'Corpus-sample-8-1830-1393', name: 'Corpus-sample-8-1830-1393',
lastUpdated: new Date(2020, 4, 9), lastUpdated: new Date(2020, 4, 9).toString(),
lastUsed: new Date(2019, 8, 4), lastUsed: new Date(2019, 8, 4).toString(),
latestJobPath: 'gitlab-examples/security/security-reports/-/jobs/1107103952', latestJobPath: 'gitlab-examples/security/security-reports/-/jobs/1107103952',
downloadPath: 'farias-gl/go-fuzzing-example/-/jobs/959593462/artifacts/download', downloadPath: 'farias-gl/go-fuzzing-example/-/jobs/959593462/artifacts/download',
target: '', target: '',
...@@ -45,8 +45,8 @@ export const corpuses = [ ...@@ -45,8 +45,8 @@ export const corpuses = [
}, },
{ {
name: 'Corpus-sample-9-2450-2393', name: 'Corpus-sample-9-2450-2393',
lastUpdated: new Date(2019, 4, 6), lastUpdated: new Date(2019, 4, 6).toString(),
lastUsed: new Date(2020, 3, 12), lastUsed: new Date(2020, 3, 12).toString(),
latestJobPath: 'gitlab-examples/security/security-reports/-/jobs/1107103952', latestJobPath: 'gitlab-examples/security/security-reports/-/jobs/1107103952',
downloadPath: 'farias-gl/go-fuzzing-example/-/jobs/959593462/artifacts/download', downloadPath: 'farias-gl/go-fuzzing-example/-/jobs/959593462/artifacts/download',
target: '403847-feature-branch', target: '403847-feature-branch',
......
...@@ -8804,9 +8804,24 @@ msgstr "" ...@@ -8804,9 +8804,24 @@ msgstr ""
msgid "Corpus Management|Are you sure you want to delete the corpus?" msgid "Corpus Management|Are you sure you want to delete the corpus?"
msgstr "" msgstr ""
msgid "CorpusManagement|Actions"
msgstr ""
msgid "CorpusManagement|Corpus are used in fuzz testing as mutation source to Improve future testing."
msgstr ""
msgid "CorpusManagement|Corpus name"
msgstr ""
msgid "CorpusManagement|Fuzz testing corpus management" msgid "CorpusManagement|Fuzz testing corpus management"
msgstr "" msgstr ""
msgid "CorpusManagement|Last updated"
msgstr ""
msgid "CorpusManagement|Last used"
msgstr ""
msgid "CorpusManagement|Latest Job:" msgid "CorpusManagement|Latest Job:"
msgstr "" msgstr ""
...@@ -8816,6 +8831,9 @@ msgstr "" ...@@ -8816,6 +8831,9 @@ msgstr ""
msgid "CorpusManagement|Not Set" msgid "CorpusManagement|Not Set"
msgstr "" msgstr ""
msgid "CorpusManagement|Target"
msgstr ""
msgid "CorpusManagement|Total Size: %{totalSize}" msgid "CorpusManagement|Total Size: %{totalSize}"
msgstr "" msgstr ""
...@@ -18397,6 +18415,9 @@ msgstr "" ...@@ -18397,6 +18415,9 @@ msgstr ""
msgid "Learn GitLab|Trial only" msgid "Learn GitLab|Trial only"
msgstr "" msgstr ""
msgid "Learn More"
msgstr ""
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}" msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr "" msgstr ""
......
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