Commit 7c6f815d authored by Vitaly Slobodin's avatar Vitaly Slobodin

Merge branch...

Merge branch 'ag/321645-ci-minutes-create-a-banner-to-display-success-from-purchasing-ci-minutes' into 'master'

[CI Minutes] Show the banner after successful purchase

See merge request gitlab-org/gitlab!66451
parents 75491057 091ea700
......@@ -10,6 +10,7 @@ import Checkout from './checkout.vue';
import OrderSummary from './order_summary.vue';
export default {
name: 'BuyCIMinutesApp',
components: {
Checkout,
GlEmptyState,
......
......@@ -30,13 +30,16 @@ export default {
},
confirmOrderParams: {
query: stateQuery,
skip() {
return !this.isActive;
},
update(data) {
const { customer } = data;
return {
setup_for_company: data.isSetupForCompany,
selected_group: data.subscription.namespaceId,
new_user: data.isNewUser,
redirect_after_success: data.redirectAfterSuccess,
customer: {
country: customer.country,
address_1: customer.address1,
......@@ -58,7 +61,6 @@ export default {
methods: {
confirmOrder() {
this.isLoading = true;
return Api.confirmOrder(this.confirmOrderParams)
.then(({ data }) => {
if (data.location) {
......@@ -82,7 +84,7 @@ export default {
};
</script>
<template>
<div v-if="isActive" class="full-width gl-mb-7">
<div v-if="isActive" class="full-width gl-mb-7" data-testid="confirm-order-root">
<gl-button :disabled="isLoading" variant="success" category="primary" @click="confirmOrder">
<gl-loading-icon v-if="isLoading" inline size="sm" />
{{ isLoading ? $options.i18n.confirming : $options.i18n.confirm }}
......
......@@ -11,7 +11,7 @@ function arrayToGraphqlArray(arr, typename) {
}
export function writeInitialDataToApolloCache(apolloProvider, dataset) {
const { groupData, namespaceId } = dataset;
const { groupData, namespaceId, redirectAfterSuccess } = dataset;
// eslint-disable-next-line @gitlab/require-i18n-strings
const namespaces = arrayToGraphqlArray(JSON.parse(groupData), 'Namespace');
......@@ -23,6 +23,7 @@ export function writeInitialDataToApolloCache(apolloProvider, dataset) {
isSetupForCompany: false,
selectedPlanId: null,
namespaces,
redirectAfterSuccess,
subscription: {
quantity: 1,
namespaceId,
......
......@@ -8,6 +8,7 @@ query State {
fullName @client
isSetupForCompany @client
selectedPlanId @client
redirectAfterSuccess @client
customer @client {
country
address1
......
import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import Api from 'ee/api';
import ConfirmOrder from 'ee/subscriptions/buy_minutes/components/checkout/confirm_order.vue';
import { STEPS } from 'ee/subscriptions/constants';
import ConfirmOrder from 'ee/subscriptions/new/components/checkout/confirm_order.vue';
import createStore from 'ee/subscriptions/new/store';
import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql';
import { GENERAL_ERROR_MESSAGE } from 'ee/vue_shared/purchase_flow/constants';
import { stateData as initialStateData } from 'ee_jest/subscriptions/buy_minutes/mock_data';
import { createMockApolloProvider } from 'ee_jest/vue_shared/purchase_flow/spec_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import flash from '~/flash';
import * as UrlUtility from '~/lib/utils/url_utility';
jest.mock('~/lib/utils/url_utility');
jest.mock('~/flash');
jest.mock('ee/api.js');
describe('Confirm Order', () => {
const localVue = createLocalVue();
localVue.use(VueApollo);
const flushPromises = () => new Promise(setImmediate);
describe('Confirm Order', () => {
let mockApolloProvider;
let wrapper;
jest.mock('ee/api.js');
const store = createStore();
const findRootElement = () => wrapper.findByTestId('confirm-order-root');
const findConfirmButton = () => wrapper.find(GlButton);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
function createComponent(options = {}) {
return shallowMount(ConfirmOrder, {
const localVue = createLocalVue();
localVue.use(VueApollo);
const createComponent = (options = {}) => {
wrapper = extendedWrapper(
shallowMount(ConfirmOrder, {
localVue,
store,
...options,
});
}
const findConfirmButton = () => wrapper.find(GlButton);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
}),
);
};
afterEach(() => {
wrapper.destroy();
......@@ -39,8 +44,8 @@ describe('Confirm Order', () => {
describe('Active', () => {
describe('when receiving proper step data', () => {
beforeEach(() => {
const mockApolloProvider = createMockApolloProvider(STEPS, 3);
wrapper = createComponent({ apolloProvider: mockApolloProvider });
mockApolloProvider = createMockApolloProvider(STEPS, 3);
createComponent({ apolloProvider: mockApolloProvider });
});
it('button should be visible', () => {
......@@ -58,15 +63,38 @@ describe('Confirm Order', () => {
describe('Clicking the button', () => {
beforeEach(() => {
const mockApolloProvider = createMockApolloProvider(STEPS, 3);
wrapper = createComponent({ apolloProvider: mockApolloProvider });
mockApolloProvider = createMockApolloProvider([]);
mockApolloProvider.clients.defaultClient.cache.writeQuery({
query: stateQuery,
data: { ...initialStateData, stepList: STEPS, activeStep: STEPS[3] },
});
createComponent({ apolloProvider: mockApolloProvider });
Api.confirmOrder = jest.fn().mockReturnValue(new Promise(jest.fn()));
findConfirmButton().vm.$emit('click');
});
it('calls the confirmOrder API method', () => {
expect(Api.confirmOrder).toHaveBeenCalled();
it('calls the confirmOrder API method with the correct params', () => {
expect(Api.confirmOrder).toHaveBeenCalledTimes(1);
expect(Api.confirmOrder.mock.calls[0][0]).toMatchObject({
setup_for_company: true,
selected_group: null,
new_user: false,
redirect_after_success: '/path/to/redirect/',
customer: {
country: null,
address_1: null,
address_2: null,
city: null,
state: null,
zip_code: null,
company: null,
},
subscription: {
plan_id: null,
payment_method_id: null,
quantity: 1,
},
});
});
it('shows the text "Confirming..."', () => {
......@@ -78,11 +106,44 @@ describe('Confirm Order', () => {
});
});
describe('Order confirmation', () => {
describe('when the confirmation succeeds', () => {
const location = 'group/location/path';
beforeEach(() => {
mockApolloProvider = createMockApolloProvider(STEPS, 3);
createComponent({ apolloProvider: mockApolloProvider });
});
it('should redirect to the location', async () => {
Api.confirmOrder = jest.fn().mockResolvedValueOnce({ data: { location } });
findConfirmButton().vm.$emit('click');
await flushPromises();
expect(UrlUtility.redirectTo).toHaveBeenCalledTimes(1);
expect(UrlUtility.redirectTo).toHaveBeenCalledWith(location);
});
it('shows an error', async () => {
const errors = 'an error';
Api.confirmOrder = jest.fn().mockResolvedValueOnce({ data: { errors } });
findConfirmButton().vm.$emit('click');
await flushPromises();
expect(flash.mock.calls[0][0]).toMatchObject({
message: GENERAL_ERROR_MESSAGE,
captureError: true,
error: new Error(JSON.stringify(errors)),
});
});
});
});
describe('when failing to receive step data', () => {
beforeEach(() => {
const mockApolloProvider = createMockApolloProvider([]);
mockApolloProvider = createMockApolloProvider([]);
createComponent({ apolloProvider: mockApolloProvider });
mockApolloProvider.clients.defaultClient.clearStore();
wrapper = createComponent({ apolloProvider: mockApolloProvider });
});
afterEach(() => {
......@@ -96,16 +157,18 @@ describe('Confirm Order', () => {
error: expect.any(Error),
});
});
it('does not render the root element', () => {
expect(findRootElement().exists()).toBe(false);
});
});
});
describe('Inactive', () => {
beforeEach(() => {
const mockApolloProvider = createMockApolloProvider(STEPS, 1);
wrapper = createComponent({ apolloProvider: mockApolloProvider });
});
it('does not show buttons', () => {
mockApolloProvider = createMockApolloProvider(STEPS, 1);
createComponent({ apolloProvider: mockApolloProvider });
it('button should not be visible', () => {
expect(findConfirmButton().exists()).toBe(false);
});
});
......
......@@ -28,6 +28,7 @@ export const stateData = {
namespaceId: null,
__typename: 'Subscription',
},
redirectAfterSuccess: '/path/to/redirect/',
selectedPlanId: null,
paymentMethod: {
id: null,
......
......@@ -9,6 +9,7 @@ const DEFAULT_DATA = {
newUser: false,
fullName: null,
setupForCompany: false,
redirectAfterSuccess: null,
};
describe('utils', () => {
......
......@@ -14,6 +14,5 @@ export function createMockApolloProvider(stepList, initialStepIndex = 0, additio
query: activeStepQuery,
data: { activeStep: stepList[initialStepIndex] },
});
return mockApollo;
}
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