Commit 6b57808b authored by Mark Florian's avatar Mark Florian Committed by Phil Hughes

Use mock-apollo-provider in SAST UI tests

Using mock-apollo-provider results in a more realistic test, since the
internals of the component(s) (i.e., `data`) aren't manipulated by the
test suite.

This change also removed an unnecessary use of `localVue` in a related
test suite, and moved the SAST spec helpers up a level in the tree.

Addresses https://gitlab.com/gitlab-org/gitlab/-/issues/249556.
parent 78da8949
import { mount } from '@vue/test-utils';
import AnalyzerConfiguration from 'ee/security_configuration/sast/components/analyzer_configuration.vue';
import DynamicFields from 'ee/security_configuration/sast/components/dynamic_fields.vue';
import { makeAnalyzerEntities, makeEntities, makeSastCiConfiguration } from './helpers';
import { makeAnalyzerEntities, makeEntities, makeSastCiConfiguration } from '../helpers';
describe('AnalyzerConfiguration component', () => {
let wrapper;
......
import { merge } from 'lodash';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { GlLink, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import SASTConfigurationApp from 'ee/security_configuration/sast/components/app.vue';
import ConfigurationForm from 'ee/security_configuration/sast/components/configuration_form.vue';
import { makeSastCiConfiguration } from './helpers';
import { stripTypenames } from 'helpers/graphql_helpers';
import createMockApollo from 'helpers/mock_apollo_helper';
import sastCiConfigurationQuery from 'ee/security_configuration/sast/graphql/sast_ci_configuration.query.graphql';
import { sastCiConfigurationQueryResponse } from '../mock_data';
Vue.use(VueApollo);
const sastDocumentationPath = '/help/sast';
const projectPath = 'namespace/project';
......@@ -10,29 +18,29 @@ const projectPath = 'namespace/project';
describe('SAST Configuration App', () => {
let wrapper;
const createComponent = ({
stubs = {},
loading = true,
hasLoadingError = false,
sastCiConfiguration = null,
} = {}) => {
wrapper = shallowMount(SASTConfigurationApp, {
mocks: { $apollo: { loading } },
stubs,
provide: {
sastDocumentationPath,
projectPath,
},
// While setting data is usually frowned upon, it is the documented way
// of mocking GraphQL response data:
// https://docs.gitlab.com/ee/development/fe_guide/graphql.html#testing
data() {
return {
hasLoadingError,
sastCiConfiguration,
};
},
});
const pendingHandler = () => new Promise(() => {});
const successHandler = async () => sastCiConfigurationQueryResponse;
const failureHandler = async () => ({ errors: [{ message: 'some error' }] });
const createMockApolloProvider = (handler) =>
createMockApollo([[sastCiConfigurationQuery, handler]]);
const createComponent = (options) => {
wrapper = shallowMount(
SASTConfigurationApp,
merge(
{
// Use a function reference here so it's lazily initialized, and can
// be replaced with other handlers in certain tests without
// initialising twice.
apolloProvider: () => createMockApolloProvider(successHandler),
provide: {
sastDocumentationPath,
projectPath,
},
},
options,
),
);
};
const findHeader = () => wrapper.find('header');
......@@ -98,7 +106,7 @@ describe('SAST Configuration App', () => {
describe('when loading', () => {
beforeEach(() => {
createComponent({
loading: true,
apolloProvider: createMockApolloProvider(pendingHandler),
});
});
......@@ -118,8 +126,7 @@ describe('SAST Configuration App', () => {
describe('when loading failed', () => {
beforeEach(() => {
createComponent({
loading: false,
hasLoadingError: true,
apolloProvider: createMockApolloProvider(failureHandler),
});
});
......@@ -137,14 +144,8 @@ describe('SAST Configuration App', () => {
});
describe('when loaded', () => {
let sastCiConfiguration;
beforeEach(() => {
sastCiConfiguration = makeSastCiConfiguration();
createComponent({
loading: false,
sastCiConfiguration,
});
createComponent();
});
it('does not display a loading spinner', () => {
......@@ -156,7 +157,9 @@ describe('SAST Configuration App', () => {
});
it('passes the sastCiConfiguration to the sastCiConfiguration prop', () => {
expect(findConfigurationForm().props('sastCiConfiguration')).toBe(sastCiConfiguration);
expect(findConfigurationForm().props('sastCiConfiguration')).toEqual(
stripTypenames(sastCiConfigurationQueryResponse.data.project.sastCiConfiguration),
);
});
it('does not display an alert message', () => {
......
......@@ -8,7 +8,7 @@ import ExpandableSection from 'ee/security_configuration/sast/components/expanda
import configureSastMutation from 'ee/security_configuration/sast/graphql/configure_sast.mutation.graphql';
import { redirectTo } from '~/lib/utils/url_utility';
import * as Sentry from '~/sentry/wrapper';
import { makeEntities, makeSastCiConfiguration } from './helpers';
import { makeEntities, makeSastCiConfiguration } from '../helpers';
jest.mock('~/lib/utils/url_utility', () => ({
redirectTo: jest.fn(),
......
import { shallowMount, mount } from '@vue/test-utils';
import DynamicFields from 'ee/security_configuration/sast/components/dynamic_fields.vue';
import { makeEntities } from './helpers';
import { makeEntities } from '../helpers';
describe('DynamicFields component', () => {
let wrapper;
......
......@@ -4,7 +4,7 @@ import {
toSastCiConfigurationEntityInput,
toSastCiConfigurationAnalyzerEntityInput,
} from 'ee/security_configuration/sast/components/utils';
import { makeEntities, makeAnalyzerEntities } from './helpers';
import { makeEntities, makeAnalyzerEntities } from '../helpers';
describe('isValidConfigurationEntity', () => {
const validEntities = makeEntities(3);
......
......@@ -15,6 +15,7 @@ export const makeEntities = (count, changes) =>
label: `label${i}`,
type: 'string',
value: `value${i}`,
size: `MEDIUM`,
...changes,
}));
......
import { makeEntities } from './helpers';
export const sastCiConfigurationQueryResponse = {
data: {
project: {
sastCiConfiguration: {
global: {
nodes: makeEntities(2, { __typename: 'SastCiConfigurationEntity' }),
__typename: 'SastCiConfigurationEntityConnection',
},
pipeline: {
nodes: makeEntities(2, { __typename: 'SastCiConfigurationEntity' }),
__typename: 'SastCiConfigurationEntityConnection',
},
analyzers: {
nodes: [
{
description: 'Ruby on Rails',
enabled: false,
label: 'Brakeman',
name: 'brakeman',
variables: {
nodes: makeEntities(2, { __typename: 'SastCiConfigurationEntity' }),
__typename: 'SastCiConfigurationEntityConnection',
},
__typename: 'SastCiConfigurationAnalyzersEntity',
},
{
description: 'Python',
enabled: false,
label: 'Bandit',
name: 'bandit',
variables: {
nodes: [],
__typename: 'SastCiConfigurationEntityConnection',
},
__typename: 'SastCiConfigurationAnalyzersEntity',
},
],
__typename: 'SastCiConfigurationAnalyzersEntityConnection',
},
__typename: 'SastCiConfiguration',
},
__typename: 'Project',
},
},
};
/**
* Returns a clone of the given object with all __typename keys omitted,
* including deeply nested ones.
*
* Only works with JSON-serializable objects.
*
* @param {object} An object with __typename keys (e.g., a GraphQL response)
* @returns {object} A new object with no __typename keys
*/
export const stripTypenames = (object) => {
return JSON.parse(
JSON.stringify(object, (key, value) => (key === '__typename' ? undefined : value)),
);
};
import { stripTypenames } from './graphql_helpers';
describe('stripTypenames', () => {
it.each`
input | expected
${{}} | ${{}}
${{ __typename: 'Foo' }} | ${{}}
${{ bar: 'bar', __typename: 'Foo' }} | ${{ bar: 'bar' }}
${{ bar: { __typename: 'Bar' }, __typename: 'Foo' }} | ${{ bar: {} }}
${{ bar: [{ __typename: 'Bar' }], __typename: 'Foo' }} | ${{ bar: [{}] }}
${[]} | ${[]}
${[{ __typename: 'Foo' }]} | ${[{}]}
${[{ bar: [{ a: 1, __typename: 'Bar' }] }]} | ${[{ bar: [{ a: 1 }] }]}
`('given $input returns $expected, with all __typename keys removed', ({ input, expected }) => {
const actual = stripTypenames(input);
expect(actual).toEqual(expected);
expect(input).not.toBe(actual);
});
it('given null returns null', () => {
expect(stripTypenames(null)).toEqual(null);
});
});
import { mount, createLocalVue } from '@vue/test-utils';
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { merge } from 'lodash';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import Vuex from 'vuex';
import createMockApollo from 'helpers/mock_apollo_helper';
......@@ -26,8 +27,8 @@ import securityReportDownloadPathsQuery from '~/vue_shared/security_reports/quer
jest.mock('~/flash');
const localVue = createLocalVue();
localVue.use(Vuex);
Vue.use(VueApollo);
Vue.use(Vuex);
const SAST_COMPARISON_PATH = '/sast.json';
const SECRET_SCANNING_COMPARISON_PATH = '/secret_detection.json';
......@@ -47,7 +48,6 @@ describe('Security reports app', () => {
SecurityReportsApp,
merge(
{
localVue,
propsData: { ...props },
stubs: {
HelpIcon: true,
......@@ -64,8 +64,6 @@ describe('Security reports app', () => {
Promise.resolve({ data: securityReportDownloadPathsQueryNoArtifactsResponse });
const failureHandler = () => Promise.resolve({ errors: [{ message: 'some error' }] });
const createMockApolloProvider = (handler) => {
localVue.use(VueApollo);
const requestHandlers = [[securityReportDownloadPathsQuery, handler]];
return createMockApollo(requestHandlers);
......
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