Commit dbdef82b authored by Angelo Gulina's avatar Angelo Gulina Committed by Nicolò Maria Mezzopera

Subscription Activation: Fix Subscription Form UX

parent f637b48d
......@@ -9,9 +9,16 @@ import {
GlLink,
GlSprintf,
} from '@gitlab/ui';
import { subscriptionActivationForm, subscriptionQueries } from '../constants';
import { helpPagePath } from '~/helpers/help_page_helper';
import validation from '~/vue_shared/directives/validation';
import {
fieldRequiredMessage,
subscriptionActivationForm,
subscriptionQueries,
} from '../constants';
export const SUBSCRIPTION_ACTIVATION_EVENT = 'subscription-activation';
export const adminLicenseUrl = helpPagePath('/user/admin_area/license');
export default {
i18n: {
......@@ -21,6 +28,7 @@ export default {
pasteActivationCode: subscriptionActivationForm.pasteActivationCode,
acceptTerms: subscriptionActivationForm.acceptTerms,
activateLabel: subscriptionActivationForm.activateLabel,
fieldRequiredMessage,
},
name: 'CloudLicenseSubscriptionActivationForm',
components: {
......@@ -33,27 +41,58 @@ export default {
GlSprintf,
GlLink,
},
links: {
adminLicenseUrl,
},
directives: {
validation: validation(),
},
data() {
const form = {
state: false,
showValidation: false,
fields: {
activationCode: {
required: true,
state: null,
value: '',
},
terms: {
required: true,
state: null,
},
},
};
return {
activationCode: null,
form,
isLoading: false,
termsAccepted: false,
};
},
computed: {
activateButtonDisabled() {
return this.isLoading || !this.termsAccepted;
isCheckboxValid() {
if (this.form.showValidation) {
return this.form.fields.terms.state ? null : false;
}
return null;
},
isRequestingActivation() {
return this.isLoading;
},
},
methods: {
submit() {
if (!this.form.state) {
this.form.showValidation = true;
return;
}
this.form.showValidation = false;
this.isLoading = true;
this.$apollo
.mutate({
mutation: subscriptionQueries.mutation,
variables: {
gitlabSubscriptionActivateInput: {
activationCode: this.activationCode,
activationCode: this.form.fields.activationCode.value,
},
},
})
......@@ -88,26 +127,40 @@ export default {
<p>
<gl-sprintf :message="$options.i18n.howToActivateSubscription">
<template #link="{ content }">
<gl-link href="" target="_blank">{{ content }}</gl-link>
<gl-link :href="$options.links.adminLicenseUrl" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
<gl-form @submit.stop.prevent="submit">
<gl-form-group class="gl-mb-0">
<div class="gl-display-flex gl-flex-wrap">
<gl-form novalidate @submit.prevent="submit">
<div class="gl-display-flex gl-flex-wrap">
<gl-form-group
class="gl-flex-grow-1"
:invalid-feedback="form.fields.activationCode.feedback"
data-testid="form-group-activation-code"
>
<label class="gl-w-full" for="activation-code-group">
{{ $options.i18n.activationCode }}
</label>
<gl-form-input
id="activation-code-group"
v-model="activationCode"
v-model="form.fields.activationCode.value"
v-validation:[form.showValidation]
:disabled="isLoading"
:placeholder="$options.i18n.pasteActivationCode"
class="gl-w-full gl-mb-4"
:state="form.fields.activationCode.state"
name="activationCode"
class="gl-mb-4"
required
/>
</gl-form-group>
<gl-form-checkbox v-model="termsAccepted">
<gl-form-group
class="gl-mb-0"
:state="isCheckboxValid"
:invalid-feedback="$options.i18n.fieldRequiredMessage"
data-testid="form-group-terms"
>
<gl-form-checkbox v-model="form.fields.terms.state" :state="isCheckboxValid">
<gl-sprintf :message="$options.i18n.acceptTerms">
<template #link="{ content }">
<gl-link href="https://about.gitlab.com/terms/" target="_blank"
......@@ -116,19 +169,19 @@ export default {
</template>
</gl-sprintf>
</gl-form-checkbox>
</gl-form-group>
<gl-button
:disabled="activateButtonDisabled"
category="primary"
class="gl-mt-6"
data-testid="activate-button"
type="submit"
variant="confirm"
>
{{ $options.i18n.activateLabel }}
</gl-button>
</div>
</gl-form-group>
<gl-button
:loading="isRequestingActivation"
category="primary"
class="gl-mt-6 js-no-auto-disable"
data-testid="activate-button"
type="submit"
variant="confirm"
>
{{ $options.i18n.activateLabel }}
</gl-button>
</div>
</gl-form>
</gl-card>
</template>
......@@ -3,6 +3,7 @@ import activateSubscriptionMutation from './graphql/mutations/activate_subscript
import getCurrentLicense from './graphql/queries/get_current_license.query.graphql';
import getLicenseHistory from './graphql/queries/get_license_history.query.graphql';
export const fieldRequiredMessage = s__('SuperSonics|This field is required.');
export const subscriptionMainTitle = s__('SuperSonics|Your subscription');
export const subscriptionActivationTitle = s__(
`SuperSonics|You do not have an active subscription`,
......
import { GlForm, GlFormInput, GlFormCheckbox } from '@gitlab/ui';
import { GlForm, GlFormCheckbox, GlFormInput } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import CloudLicenseSubscriptionActivationForm, {
SUBSCRIPTION_ACTIVATION_EVENT,
} from 'ee/pages/admin/cloud_licenses/components/subscription_activation_form.vue';
import { subscriptionQueries } from 'ee/pages/admin/cloud_licenses/constants';
import { fieldRequiredMessage, subscriptionQueries } from 'ee/pages/admin/cloud_licenses/constants';
import createMockApollo from 'helpers/mock_apollo_helper';
import { stubComponent } from 'helpers/stub_component';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
......@@ -26,9 +26,10 @@ describe('CloudLicenseApp', () => {
const findActivateButton = () => wrapper.findByTestId('activate-button');
const findAgreementCheckbox = () => wrapper.findComponent(GlFormCheckbox);
const findAgreementCheckboxFormGroup = () => wrapper.findByTestId('form-group-terms');
const findActivationCodeFormGroup = () => wrapper.findByTestId('form-group-activation-code');
const findActivationCodeInput = () => wrapper.findComponent(GlFormInput);
const findActivateSubscriptionForm = () => wrapper.findComponent(GlForm);
const enableAcceptAgreementCheckbox = () => findAgreementCheckbox().vm.$emit('input', true);
const GlFormInputStub = stubComponent(GlFormInput, {
template: `<input />`,
......@@ -39,16 +40,17 @@ describe('CloudLicenseApp', () => {
stopPropagation,
});
const createComponentWithApollo = (props = {}, resolverMock) => {
const createComponentWithApollo = ({ props = {}, mutationMock, stubs = {} } = {}) => {
wrapper = extendedWrapper(
shallowMount(CloudLicenseSubscriptionActivationForm, {
localVue,
apolloProvider: createMockApolloProvider(resolverMock),
apolloProvider: createMockApolloProvider(mutationMock),
propsData: {
...props,
},
stubs: {
GlFormInput: GlFormInputStub,
...stubs,
},
}),
);
......@@ -77,25 +79,45 @@ describe('CloudLicenseApp', () => {
expect(findAgreementCheckbox().exists()).toBe(true);
});
it('disables the activate button if the agreement is unaccepted', () => {
expect(findActivateButton().props('disabled')).toBe(true);
it('has the activate button enabled', () => {
expect(findActivateButton().props('disabled')).toBe(false);
});
});
it('enables the activate button if the agreement is accepted', async () => {
expect(findActivateButton().props('disabled')).toBe(true);
enableAcceptAgreementCheckbox();
await wrapper.vm.$nextTick();
describe('form errors', () => {
const mutationMock = jest.fn();
beforeEach(() => {
createComponentWithApollo({ mutationMock });
});
expect(findActivateButton().props('disabled')).toBe(false);
it('shows an error for the text field', async () => {
await findActivateSubscriptionForm().vm.$emit('submit', createFakeEvent());
expect(findActivationCodeFormGroup().attributes('invalid-feedback')).toBe(
'Please fill out this field.',
);
});
it('shows an error for the checkbox field', async () => {
await findActivationCodeInput().vm.$emit('input', fakeActivationCode);
expect(findAgreementCheckboxFormGroup().attributes('invalid-feedback')).toBe(
fieldRequiredMessage,
);
});
it('does not perform any mutation', () => {
expect(mutationMock).toHaveBeenCalledTimes(0);
});
});
describe('Activate the subscription', () => {
describe('activate the subscription', () => {
describe('when submitting the form', () => {
const mutationMock = jest.fn().mockResolvedValue(activateLicenseMutationResponse.SUCCESS);
beforeEach(() => {
createComponentWithApollo({}, mutationMock);
findActivationCodeInput().vm.$emit('input', fakeActivationCode);
beforeEach(async () => {
createComponentWithApollo({ mutationMock });
await findActivationCodeInput().vm.$emit('input', fakeActivationCode);
await findAgreementCheckbox().vm.$emit('input', true);
findActivateSubscriptionForm().vm.$emit('submit', createFakeEvent());
});
......@@ -110,17 +132,6 @@ describe('CloudLicenseApp', () => {
},
});
});
});
describe('when the mutation is successful', () => {
beforeEach(() => {
createComponentWithApollo(
{},
jest.fn().mockResolvedValue(activateLicenseMutationResponse.SUCCESS),
);
findActivationCodeInput().vm.$emit('input', fakeActivationCode);
findActivateSubscriptionForm().vm.$emit('submit', createFakeEvent());
});
it('emits a successful event', () => {
expect(wrapper.emitted(SUBSCRIPTION_ACTIVATION_EVENT)).toEqual([[true]]);
......@@ -128,11 +139,11 @@ describe('CloudLicenseApp', () => {
});
describe('when the mutation is not successful but looks like it is', () => {
const mutationMock = jest
.fn()
.mockResolvedValue(activateLicenseMutationResponse.ERRORS_AS_DATA);
beforeEach(() => {
createComponentWithApollo(
{},
jest.fn().mockResolvedValue(activateLicenseMutationResponse.FAILURE_IN_DISGUISE),
);
createComponentWithApollo({ mutationMock });
findActivateSubscriptionForm().vm.$emit('submit', createFakeEvent());
});
......@@ -144,11 +155,9 @@ describe('CloudLicenseApp', () => {
});
describe('when the mutation is not successful', () => {
const mutationMock = jest.fn().mockRejectedValue(activateLicenseMutationResponse.FAILURE);
beforeEach(() => {
createComponentWithApollo(
{},
jest.fn().mockRejectedValue(activateLicenseMutationResponse.FAILURE),
);
createComponentWithApollo({ mutationMock });
findActivateSubscriptionForm().vm.$emit('submit', createFakeEvent());
});
......
......@@ -72,7 +72,7 @@ export const activateLicenseMutationResponse = {
],
},
],
FAILURE_IN_DISGUISE: {
ERRORS_AS_DATA: {
data: {
gitlabSubscriptionActivate: {
license: null,
......
......@@ -30974,6 +30974,9 @@ msgstr ""
msgid "SuperSonics|There is a connectivity issue."
msgstr ""
msgid "SuperSonics|This field is required."
msgstr ""
msgid "SuperSonics|Type"
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