Commit b7968531 authored by Paul Gascou-Vaillancourt's avatar Paul Gascou-Vaillancourt Committed by Paul Slaughter

Cleanup DAST profile forms specs

This refactors DAST profile forms specs by...

* ...addings specs for BaseDastProfileForm. Those specs are responsible
  for testing shared behaviors like mutations, the cancel modal, etc.
* ...removing or updating test cases in the form-specific specs to
  prevent redundant cases.
* ...leveraging shallow wrappers whenever possible in the form-specific
  specs.

https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67995
parent bcdf86cb
......@@ -125,7 +125,7 @@ export default {
<template>
<gl-form novalidate @submit.prevent="onSubmit">
<h2 v-if="showHeader" class="gl-mb-6">
<h2 v-if="showHeader" class="gl-mb-6" data-testid="header">
<slot name="title"></slot>
</h2>
......
import { GlAlert, GlForm, GlModal } from '@gitlab/ui';
import { createLocalVue } from '@vue/test-utils';
import merge from 'lodash/merge';
import { createMockClient } from 'mock-apollo-client';
import VueApollo from 'vue-apollo';
import BaseDastProfileForm from 'ee/security_configuration/dast_profiles/components/base_dast_profile_form.vue';
import dastSiteProfileCreateMutation from 'ee/security_configuration/dast_profiles/dast_site_profiles/graphql/dast_site_profile_create.mutation.graphql';
import { dastSiteProfileCreate } from 'ee_jest/security_configuration/dast_profiles/dast_site_profiles/mock_data/apollo_mock';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
const localVue = createLocalVue();
localVue.use(VueApollo);
const mutationVariables = {
foo: 'bar',
};
const defaultProps = {
mutation: dastSiteProfileCreateMutation,
mutationType: 'dastSiteProfileCreate',
mutationVariables,
modalProps: {},
};
const defaultRequestHandler = jest.fn().mockResolvedValue(dastSiteProfileCreate());
describe('BaseDastProfileForm', () => {
let wrapper;
let apolloProvider;
let requestHandler;
// Finders
const findForm = () => wrapper.findComponent(GlForm);
const findHeader = () => wrapper.findByTestId('header');
const findPolicyProfileAlert = () => wrapper.findByTestId('dast-policy-profile-alert');
const findErrorAlert = () => wrapper.findByTestId('dast-profile-form-alert');
const findCancelModal = () => wrapper.findByTestId('dast-profile-form-cancel-modal');
const findCancelButton = () => wrapper.findByTestId('dast-profile-form-cancel-button');
const findSubmitButton = () => wrapper.findByTestId('dast-profile-form-submit-button');
// Helpers
const submitForm = () => findForm().vm.$emit('submit', { preventDefault: () => {} });
// Assertions
const expectSubmitNotLoading = () => expect(findSubmitButton().props('loading')).toBe(false);
const mockClientFactory = (handler = defaultRequestHandler) => {
const mockClient = createMockClient();
mockClient.setRequestHandler(dastSiteProfileCreateMutation, handler);
requestHandler = handler;
return mockClient;
};
const withHandler = (handler) => {
apolloProvider.defaultClient = mockClientFactory(handler);
};
const createComponent = (options) => {
apolloProvider = new VueApollo({
defaultClient: mockClientFactory(),
});
const mountOpts = merge(
{},
{
propsData: defaultProps,
},
options,
{
localVue,
apolloProvider,
},
);
wrapper = shallowMountExtended(BaseDastProfileForm, mountOpts);
return { withHandler };
};
afterEach(() => {
wrapper.destroy();
apolloProvider = null;
});
it('renders default slot', () => {
const testId = 'default-slot-content';
createComponent({
slots: {
default: `<div data-testid='${testId}' />`,
},
});
expect(wrapper.findByTestId(testId).exists()).toBe(true);
});
describe('header', () => {
const title = 'Page title';
it('renders by default', () => {
createComponent({
slots: {
title,
},
});
const header = findHeader();
expect(header.exists()).toBe(true);
expect(header.text()).toBe(title);
});
it('does not render header if show-header is false', () => {
createComponent({
propsData: {
showHeader: false,
},
slots: {
title,
},
});
expect(findHeader().exists()).toBe(false);
});
});
describe('security policies', () => {
it('does not render policy alert by default', () => {
createComponent();
expect(findPolicyProfileAlert().exists()).toBe(false);
});
describe('when profile comes from a policy', () => {
beforeEach(() => {
createComponent({
propsData: {
isPolicyProfile: true,
},
});
});
it('shows a policy alert', () => {
expect(findPolicyProfileAlert().exists()).toBe(true);
});
it('disables submit button', () => {
expect(findSubmitButton().props('disabled')).toBe(true);
});
});
});
describe('modal', () => {
const modalProps = {
title: 'Modal title',
};
beforeEach(() => {
createComponent({
propsData: {
modalProps,
},
});
});
it('passes props to the modal', () => {
expect(findCancelModal().props()).toEqual(expect.objectContaining(modalProps));
});
});
describe('when submitting the form', () => {
it('triggers GraphQL mutation', () => {
createComponent();
expect(requestHandler).not.toHaveBeenCalled();
submitForm();
expect(requestHandler).toHaveBeenCalledWith({
input: mutationVariables,
});
});
it('sets loading state', async () => {
createComponent();
expectSubmitNotLoading();
submitForm();
await wrapper.vm.$nextTick();
expect(findSubmitButton().props('loading')).toBe(true);
});
it('on success, emits success event', async () => {
createComponent();
expect(wrapper.emitted('success')).toBeUndefined();
submitForm();
await waitForPromises();
expect(wrapper.emitted('success')).toHaveLength(1);
});
describe('when the API returns a top-level error', () => {
const defaultErrorMessage = 'Default error message';
beforeEach(async () => {
createComponent({
slots: {
'error-message': defaultErrorMessage,
},
}).withHandler(jest.fn().mockRejectedValue(new Error('GraphQL Network Error')));
submitForm();
await waitForPromises();
});
it('resets loading state', () => {
expectSubmitNotLoading();
});
it('shows an alert with the default error message', () => {
expect(findErrorAlert().exists()).toBe(true);
expect(findErrorAlert().text()).toBe(defaultErrorMessage);
});
});
describe('when the API returns errors as data', () => {
const errors = ['error#1', 'error#2', 'error#3'];
beforeEach(async () => {
createComponent({
stubs: { GlAlert },
}).withHandler(jest.fn().mockResolvedValue(dastSiteProfileCreate(errors)));
submitForm();
await waitForPromises();
});
it('resets loading state', () => {
expectSubmitNotLoading();
});
it('shows an alert with the returned errors', () => {
const alert = findErrorAlert();
expect(alert.exists()).toBe(true);
errors.forEach((error) => {
expect(alert.text()).toContain(error);
});
});
});
});
describe('when cancelling the action', () => {
describe('without changing the form', () => {
beforeEach(() => {
createComponent();
});
it('emits cancel event', () => {
findCancelButton().vm.$emit('click');
expect(wrapper.emitted('cancel')).toBeTruthy();
});
});
describe('after changing the form', () => {
beforeEach(() => {
createComponent({
propsData: {
formTouched: true,
},
stubs: { GlModal },
});
});
it('asks the user to confirm the action', () => {
jest.spyOn(findCancelModal().vm, 'show').mockReturnValue();
findCancelButton().vm.$emit('click');
expect(findCancelModal().vm.show).toHaveBeenCalled();
});
it('emits cancel event upon confirming', () => {
findCancelModal().vm.$emit('ok');
expect(wrapper.emitted('cancel')).toBeTruthy();
});
});
});
});
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