Commit 11b002b9 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch 'ag/341564-use-account-id-for-iframe-FE' into 'master'

Use account id as part of the namespace to init Zuora iframe

See merge request gitlab-org/gitlab!71735
parents 9be3299a 75f1f22a
<script> <script>
import { GlIcon, GlCollapse, GlCollapseToggleDirective } from '@gitlab/ui'; import { GlIcon, GlCollapse, GlCollapseToggleDirective } from '@gitlab/ui';
import find from 'lodash/find';
import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql'; import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql';
import { TAX_RATE } from 'ee/subscriptions/new/constants'; import { TAX_RATE } from 'ee/subscriptions/new/constants';
import formattingMixins from 'ee/subscriptions/new/formatting_mixins'; import formattingMixins from 'ee/subscriptions/new/formatting_mixins';
...@@ -35,25 +36,23 @@ export default { ...@@ -35,25 +36,23 @@ export default {
query: stateQuery, query: stateQuery,
manual: true, manual: true,
result({ data }) { result({ data }) {
this.namespaces = data.namespaces; const id = Number(data.selectedNamespaceId);
this.selectedNamespace = find(data.eligibleNamespaces, { id });
this.subscription = data.subscription; this.subscription = data.subscription;
}, },
}, },
}, },
data() { data() {
return { return {
subscription: {},
namespaces: [],
isBottomSummaryVisible: false, isBottomSummaryVisible: false,
selectedNamespace: {},
subscription: {},
}; };
}, },
computed: { computed: {
selectedPlanPrice() { selectedPlanPrice() {
return this.plan.pricePerYear; return this.plan.pricePerYear;
}, },
selectedGroup() {
return this.namespaces.find((group) => group.id === Number(this.subscription.namespaceId));
},
totalExVat() { totalExVat() {
return this.subscription.quantity * this.selectedPlanPrice; return this.subscription.quantity * this.selectedPlanPrice;
}, },
...@@ -67,7 +66,7 @@ export default { ...@@ -67,7 +66,7 @@ export default {
return this.subscription.quantity > 0; return this.subscription.quantity > 0;
}, },
namespaceName() { namespaceName() {
return this.selectedGroup.name; return this.selectedNamespace.name;
}, },
titleWithName() { titleWithName() {
return sprintf(this.title, { name: this.namespaceName }); return sprintf(this.title, { name: this.namespaceName });
......
...@@ -6,7 +6,7 @@ export const planTags = { ...@@ -6,7 +6,7 @@ export const planTags = {
STORAGE_PLAN: 'STORAGE_PLAN', STORAGE_PLAN: 'STORAGE_PLAN',
}; };
/* eslint-enable @gitlab/require-i18n-strings */ /* eslint-enable @gitlab/require-i18n-strings */
export const CUSTOMER_CLIENT = 'customerClient'; export const CUSTOMERSDOT_CLIENT = 'customersDotClient';
export const GITLAB_CLIENT = 'gitlabClient'; export const GITLAB_CLIENT = 'gitlabClient';
export const CI_MINUTES_PER_PACK = 1000; export const CI_MINUTES_PER_PACK = 1000;
......
...@@ -4,7 +4,7 @@ import VueApollo from 'vue-apollo'; ...@@ -4,7 +4,7 @@ import VueApollo from 'vue-apollo';
import purchaseFlowResolvers from 'ee/vue_shared/purchase_flow/graphql/resolvers'; import purchaseFlowResolvers from 'ee/vue_shared/purchase_flow/graphql/resolvers';
import typeDefs from 'ee/vue_shared/purchase_flow/graphql/typedefs.graphql'; import typeDefs from 'ee/vue_shared/purchase_flow/graphql/typedefs.graphql';
import createClient from '~/lib/graphql'; import createClient from '~/lib/graphql';
import { GITLAB_CLIENT, CUSTOMER_CLIENT } from './constants'; import { GITLAB_CLIENT, CUSTOMERSDOT_CLIENT } from './constants';
import { resolvers } from './graphql/resolvers'; import { resolvers } from './graphql/resolvers';
Vue.use(VueApollo); Vue.use(VueApollo);
...@@ -13,7 +13,7 @@ const gitlabClient = createClient(merge({}, resolvers, purchaseFlowResolvers), { ...@@ -13,7 +13,7 @@ const gitlabClient = createClient(merge({}, resolvers, purchaseFlowResolvers), {
typeDefs, typeDefs,
assumeImmutableResults: true, assumeImmutableResults: true,
}); });
const customerClient = createClient( const customersDotClient = createClient(
{}, {},
{ {
path: '/-/customers_dot/proxy/graphql', path: '/-/customers_dot/proxy/graphql',
...@@ -26,6 +26,6 @@ export default new VueApollo({ ...@@ -26,6 +26,6 @@ export default new VueApollo({
defaultClient: gitlabClient, defaultClient: gitlabClient,
clients: { clients: {
[GITLAB_CLIENT]: gitlabClient, [GITLAB_CLIENT]: gitlabClient,
[CUSTOMER_CLIENT]: customerClient, [CUSTOMERSDOT_CLIENT]: customersDotClient,
}, },
}); });
...@@ -39,11 +39,9 @@ export const resolvers = { ...@@ -39,11 +39,9 @@ export const resolvers = {
}, },
updateState: (_, { input }, { cache }) => { updateState: (_, { input }, { cache }) => {
const oldState = cache.readQuery({ query: stateQuery }); const oldState = cache.readQuery({ query: stateQuery });
const state = produce(oldState, (draftState) => { const state = produce(oldState, (draftState) => {
merge(draftState, input); merge(draftState, input);
}); });
cache.writeQuery({ query: stateQuery, data: state }); cache.writeQuery({ query: stateQuery, data: state });
}, },
}, },
......
...@@ -12,8 +12,9 @@ function arrayToGraphqlArray(arr, typename) { ...@@ -12,8 +12,9 @@ function arrayToGraphqlArray(arr, typename) {
export function writeInitialDataToApolloCache(apolloProvider, dataset) { export function writeInitialDataToApolloCache(apolloProvider, dataset) {
const { groupData, namespaceId, redirectAfterSuccess, subscriptionQuantity } = dataset; const { groupData, namespaceId, redirectAfterSuccess, subscriptionQuantity } = dataset;
// eslint-disable-next-line @gitlab/require-i18n-strings // eslint-disable-next-line @gitlab/require-i18n-strings
const namespaces = arrayToGraphqlArray(JSON.parse(groupData), 'Namespace'); const eligibleNamespaces = arrayToGraphqlArray(JSON.parse(groupData), 'Namespace');
const quantity = subscriptionQuantity || 1; const quantity = subscriptionQuantity || 1;
apolloProvider.clients.defaultClient.cache.writeQuery({ apolloProvider.clients.defaultClient.cache.writeQuery({
...@@ -23,11 +24,11 @@ export function writeInitialDataToApolloCache(apolloProvider, dataset) { ...@@ -23,11 +24,11 @@ export function writeInitialDataToApolloCache(apolloProvider, dataset) {
fullName: null, fullName: null,
isSetupForCompany: false, isSetupForCompany: false,
selectedPlanId: null, selectedPlanId: null,
namespaces, eligibleNamespaces,
redirectAfterSuccess, redirectAfterSuccess,
selectedNamespaceId: namespaceId,
subscription: { subscription: {
quantity, quantity,
namespaceId,
// eslint-disable-next-line @gitlab/require-i18n-strings // eslint-disable-next-line @gitlab/require-i18n-strings
__typename: 'Subscription', __typename: 'Subscription',
}, },
......
...@@ -20,14 +20,13 @@ import { ...@@ -20,14 +20,13 @@ import {
I18N_CI_MINUTES_PRICE_PRE_UNIT, I18N_CI_MINUTES_PRICE_PRE_UNIT,
I18N_CI_MINUTES_TITLE, I18N_CI_MINUTES_TITLE,
planTags, planTags,
CUSTOMER_CLIENT, CUSTOMERSDOT_CLIENT,
CI_MINUTES_PER_PACK, CI_MINUTES_PER_PACK,
} from '../../buy_addons_shared/constants'; } from '../../buy_addons_shared/constants';
import plansQuery from '../../graphql/queries/plans.customer.query.graphql'; import plansQuery from '../../graphql/queries/plans.customer.query.graphql';
export default { export default {
name: 'BuyCIMinutesApp',
components: { components: {
Checkout, Checkout,
GlEmptyState, GlEmptyState,
...@@ -93,7 +92,7 @@ export default { ...@@ -93,7 +92,7 @@ export default {
}, },
apollo: { apollo: {
plans: { plans: {
client: CUSTOMER_CLIENT, client: CUSTOMERSDOT_CLIENT,
query: plansQuery, query: plansQuery,
variables: { variables: {
tags: [planTags.CI_1000_MINUTES_PLAN], tags: [planTags.CI_1000_MINUTES_PLAN],
......
...@@ -17,6 +17,7 @@ export default (el) => { ...@@ -17,6 +17,7 @@ export default (el) => {
return new Vue({ return new Vue({
el, el,
name: 'BuyCIMinutes',
apolloProvider, apolloProvider,
render(createElement) { render(createElement) {
return createElement(extendedApp); return createElement(extendedApp);
......
...@@ -20,7 +20,7 @@ import { ...@@ -20,7 +20,7 @@ import {
I18N_STORAGE_PRICE_PRE_UNIT, I18N_STORAGE_PRICE_PRE_UNIT,
I18N_STORAGE_TOOLTIP_NOTE, I18N_STORAGE_TOOLTIP_NOTE,
planTags, planTags,
CUSTOMER_CLIENT, CUSTOMERSDOT_CLIENT,
STORAGE_PER_PACK, STORAGE_PER_PACK,
} from '../../buy_addons_shared/constants'; } from '../../buy_addons_shared/constants';
import plansQuery from '../../graphql/queries/plans.customer.query.graphql'; import plansQuery from '../../graphql/queries/plans.customer.query.graphql';
...@@ -94,7 +94,7 @@ export default { ...@@ -94,7 +94,7 @@ export default {
}, },
apollo: { apollo: {
plans: { plans: {
client: CUSTOMER_CLIENT, client: CUSTOMERSDOT_CLIENT,
query: plansQuery, query: plansQuery,
variables: { variables: {
tags: [planTags.STORAGE_PLAN], tags: [planTags.STORAGE_PLAN],
......
query State { query State {
namespaces @client { eligibleNamespaces @client {
id id
accountId
name name
users users
} }
...@@ -8,6 +9,7 @@ query State { ...@@ -8,6 +9,7 @@ query State {
fullName @client fullName @client
isSetupForCompany @client isSetupForCompany @client
selectedPlanId @client selectedPlanId @client
selectedNamespaceId @client
redirectAfterSuccess @client redirectAfterSuccess @client
customer @client { customer @client {
country country
...@@ -27,7 +29,6 @@ query State { ...@@ -27,7 +29,6 @@ query State {
} }
subscription @client { subscription @client {
quantity quantity
namespaceId
} }
activeStep @client { activeStep @client {
id id
......
<script> <script>
import { GlSprintf } from '@gitlab/ui'; import { GlSprintf } from '@gitlab/ui';
import find from 'lodash/find';
import { STEPS } from 'ee/subscriptions/constants'; import { STEPS } from 'ee/subscriptions/constants';
import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql'; import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql';
import Step from 'ee/vue_shared/purchase_flow/components/step.vue'; import Step from 'ee/vue_shared/purchase_flow/components/step.vue';
...@@ -17,8 +18,18 @@ export default { ...@@ -17,8 +18,18 @@ export default {
query: stateQuery, query: stateQuery,
update: (data) => data.paymentMethod, update: (data) => data.paymentMethod,
}, },
selectedNamespace: {
query: stateQuery,
update: ({ eligibleNamespaces, selectedNamespaceId }) => {
const id = Number(selectedNamespaceId);
return find(eligibleNamespaces, { id });
},
},
}, },
computed: { computed: {
accountId() {
return this.selectedNamespace?.accountId || '';
},
isValid() { isValid() {
return Boolean(this.paymentMethod.id); return Boolean(this.paymentMethod.id);
}, },
...@@ -40,10 +51,10 @@ export default { ...@@ -40,10 +51,10 @@ export default {
<template> <template>
<step :step-id="$options.stepId" :title="$options.i18n.stepTitle" :is-valid="isValid"> <step :step-id="$options.stepId" :title="$options.i18n.stepTitle" :is-valid="isValid">
<template #body="{ active }"> <template #body="{ active }">
<zuora :active="active" /> <zuora :active="active" :account-id="accountId" />
</template> </template>
<template #summary> <template #summary>
<div class="js-summary-line-1"> <div data-testid="card-details">
<gl-sprintf :message="$options.i18n.paymentMethod"> <gl-sprintf :message="$options.i18n.paymentMethod">
<template #cardType> <template #cardType>
{{ paymentMethod.creditCardType }} {{ paymentMethod.creditCardType }}
...@@ -53,7 +64,7 @@ export default { ...@@ -53,7 +64,7 @@ export default {
</template> </template>
</gl-sprintf> </gl-sprintf>
</div> </div>
<div class="js-summary-line-2"> <div data-testid="card-expiration">
{{ expirationDate }} {{ expirationDate }}
</div> </div>
</template> </template>
......
...@@ -23,11 +23,16 @@ export default { ...@@ -23,11 +23,16 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
accountId: {
type: String,
required: false,
default: '',
},
}, },
data() { data() {
return { return {
isLoading: false, isLoading: false,
paymentFormParams: null, paymentFormParams: {},
zuoraLoaded: false, zuoraLoaded: false,
zuoraScriptEl: null, zuoraScriptEl: null,
}; };
...@@ -36,6 +41,14 @@ export default { ...@@ -36,6 +41,14 @@ export default {
shouldShowZuoraFrame() { shouldShowZuoraFrame() {
return this.active && this.zuoraLoaded && !this.isLoading; return this.active && this.zuoraLoaded && !this.isLoading;
}, },
renderParams() {
return {
...this.paymentFormParams,
...ZUORA_IFRAME_OVERRIDE_PARAMS,
// @TODO: should the component handle re-rendering the form in case this changes?
field_accountId: this.accountId,
};
},
}, },
mounted() { mounted() {
this.loadZuoraScript(); this.loadZuoraScript();
...@@ -94,9 +107,8 @@ export default { ...@@ -94,9 +107,8 @@ export default {
}); });
}, },
renderZuoraIframe() { renderZuoraIframe() {
const params = { ...this.paymentFormParams, ...ZUORA_IFRAME_OVERRIDE_PARAMS };
window.Z.runAfterRender(this.zuoraIframeRendered); window.Z.runAfterRender(this.zuoraIframeRendered);
window.Z.render(params, {}, this.paymentFormSubmitted); window.Z.render(this.renderParams, {}, this.paymentFormSubmitted);
}, },
activateNextStep() { activateNextStep() {
return this.$apollo return this.$apollo
......
...@@ -60,6 +60,7 @@ module SubscriptionsHelper ...@@ -60,6 +60,7 @@ module SubscriptionsHelper
def present_group(namespace) def present_group(namespace)
{ {
id: namespace.id, id: namespace.id,
account_id: nil,
name: namespace.name, name: namespace.name,
users: namespace.member_count, users: namespace.member_count,
guests: namespace.guest_count guests: namespace.guest_count
......
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { createLocalVue } from '@vue/test-utils';
import { merge } from 'lodash'; import { merge } from 'lodash';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import OrderSummary from 'ee/subscriptions/buy_addons_shared/components/order_summary.vue'; import OrderSummary from 'ee/subscriptions/buy_addons_shared/components/order_summary.vue';
import subscriptionsResolvers from 'ee/subscriptions/buy_addons_shared/graphql/resolvers'; import subscriptionsResolvers from 'ee/subscriptions/buy_addons_shared/graphql/resolvers';
import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql'; import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql';
...@@ -17,32 +18,30 @@ localVue.use(VueApollo); ...@@ -17,32 +18,30 @@ localVue.use(VueApollo);
describe('Order Summary', () => { describe('Order Summary', () => {
const resolvers = { ...purchaseFlowResolvers, ...subscriptionsResolvers }; const resolvers = { ...purchaseFlowResolvers, ...subscriptionsResolvers };
const selectedNamespaceId = mockParsedNamespaces[0].id;
const initialStateData = { const initialStateData = {
selectedPlanId: 'ciMinutesPackPlanId', eligibleNamespaces: mockParsedNamespaces,
namespaces: [mockParsedNamespaces[0]], selectedNamespaceId,
subscription: { subscription: {},
namespaceId: mockParsedNamespaces[0].id,
},
}; };
let wrapper; let wrapper;
const findAmount = () => wrapper.findByTestId('amount');
const findTitle = () => wrapper.findByTestId('title');
const createMockApolloProvider = (stateData = {}) => { const createMockApolloProvider = (stateData = {}) => {
const mockApollo = createMockApollo([], resolvers); const mockApollo = createMockApollo([], resolvers);
const data = merge({}, mockStateData, initialStateData, stateData); const data = merge({}, mockStateData, initialStateData, stateData);
mockApollo.clients.defaultClient.cache.writeQuery({ mockApollo.clients.defaultClient.cache.writeQuery({
query: stateQuery, query: stateQuery,
data, data,
}); });
return mockApollo; return mockApollo;
}; };
const createComponent = (stateData) => { const createComponent = (stateData) => {
const apolloProvider = createMockApolloProvider(stateData); const apolloProvider = createMockApolloProvider(stateData);
wrapper = shallowMountExtended(OrderSummary, {
wrapper = shallowMount(OrderSummary, {
localVue, localVue,
apolloProvider, apolloProvider,
propsData: { propsData: {
...@@ -65,9 +64,7 @@ describe('Order Summary', () => { ...@@ -65,9 +64,7 @@ describe('Order Summary', () => {
}); });
it('displays the title', () => { it('displays the title', () => {
expect(wrapper.find('[data-testid="title"]').text()).toMatchInterpolatedText( expect(findTitle().text()).toMatchInterpolatedText("Gitlab Org's CI minutes");
"Gitlab Org's CI minutes",
);
}); });
}); });
...@@ -79,7 +76,7 @@ describe('Order Summary', () => { ...@@ -79,7 +76,7 @@ describe('Order Summary', () => {
}); });
it('renders amount', () => { it('renders amount', () => {
expect(wrapper.find('[data-testid="amount"]').text()).toBe('$30'); expect(findAmount().text()).toBe('$30');
}); });
}); });
...@@ -91,7 +88,7 @@ describe('Order Summary', () => { ...@@ -91,7 +88,7 @@ describe('Order Summary', () => {
}); });
it('does not render amount', () => { it('does not render amount', () => {
expect(wrapper.find('[data-testid="amount"]').text()).toBe('-'); expect(findAmount().text()).toBe('-');
}); });
}); });
}); });
import apolloProvider from 'ee/subscriptions/buy_addons_shared/graphql'; import apolloProvider from 'ee/subscriptions/buy_addons_shared/graphql';
import { writeInitialDataToApolloCache } from 'ee/subscriptions/buy_addons_shared/utils'; import { writeInitialDataToApolloCache } from 'ee/subscriptions/buy_addons_shared/utils';
import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql'; import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql';
import { mockNamespaces, mockParsedNamespaces } from './mock_data'; import { mockNamespaces, mockParsedNamespaces } from '../buy_minutes/mock_data';
const DEFAULT_DATA = { const DEFAULT_DATA = {
groupData: mockNamespaces, groupData: mockNamespaces,
...@@ -26,7 +26,7 @@ describe('utils', () => { ...@@ -26,7 +26,7 @@ describe('utils', () => {
${''} | ${{}} | ${true} ${''} | ${{}} | ${true}
${mockNamespaces} | ${mockParsedNamespaces} | ${false} ${mockNamespaces} | ${mockParsedNamespaces} | ${false}
`('parameter decoding', ({ namespaces, parsedNamespaces, throws }) => { `('parameter decoding', ({ namespaces, parsedNamespaces, throws }) => {
it(`decodes ${namespaces} to ${parsedNamespaces}`, async () => { it(`decodes $namespaces to $parsedNamespaces`, async () => {
if (throws) { if (throws) {
expect(() => { expect(() => {
writeInitialDataToApolloCache(apolloProvider, { groupData: namespaces }); writeInitialDataToApolloCache(apolloProvider, { groupData: namespaces });
...@@ -39,7 +39,7 @@ describe('utils', () => { ...@@ -39,7 +39,7 @@ describe('utils', () => {
const sourceData = await apolloProvider.clients.defaultClient.query({ const sourceData = await apolloProvider.clients.defaultClient.query({
query: stateQuery, query: stateQuery,
}); });
expect(sourceData.data.namespaces).toStrictEqual(parsedNamespaces); expect(sourceData.data.eligibleNamespaces).toStrictEqual(parsedNamespaces);
} }
}); });
}); });
......
...@@ -61,7 +61,7 @@ describe('Billing Address', () => { ...@@ -61,7 +61,7 @@ describe('Billing Address', () => {
}); });
describe('validations', () => { describe('validations', () => {
const isStepValid = () => wrapper.find(Step).props('isValid'); const isStepValid = () => wrapper.findComponent(Step).props('isValid');
const customerData = { const customerData = {
country: 'US', country: 'US',
address1: 'address line 1', address1: 'address line 1',
......
...@@ -11,20 +11,19 @@ import { createMockApolloProvider } from 'ee_jest/vue_shared/purchase_flow/spec_ ...@@ -11,20 +11,19 @@ import { createMockApolloProvider } from 'ee_jest/vue_shared/purchase_flow/spec_
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import flash from '~/flash'; import flash from '~/flash';
import * as UrlUtility from '~/lib/utils/url_utility'; import * as UrlUtility from '~/lib/utils/url_utility';
import flushPromises from 'helpers/flush_promises';
jest.mock('~/lib/utils/url_utility'); jest.mock('~/lib/utils/url_utility');
jest.mock('~/flash'); jest.mock('~/flash');
jest.mock('ee/api.js'); jest.mock('ee/api.js');
const flushPromises = () => new Promise(setImmediate);
describe('Confirm Order', () => { describe('Confirm Order', () => {
let mockApolloProvider; let mockApolloProvider;
let wrapper; let wrapper;
const findRootElement = () => wrapper.findByTestId('confirm-order-root'); const findRootElement = () => wrapper.findByTestId('confirm-order-root');
const findConfirmButton = () => wrapper.find(GlButton); const findConfirmButton = () => wrapper.findComponent(GlButton);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(VueApollo); localVue.use(VueApollo);
...@@ -77,7 +76,7 @@ describe('Confirm Order', () => { ...@@ -77,7 +76,7 @@ describe('Confirm Order', () => {
expect(Api.confirmOrder).toHaveBeenCalledTimes(1); expect(Api.confirmOrder).toHaveBeenCalledTimes(1);
expect(Api.confirmOrder.mock.calls[0][0]).toMatchObject({ expect(Api.confirmOrder.mock.calls[0][0]).toMatchObject({
setup_for_company: true, setup_for_company: true,
selected_group: null, selected_group: undefined,
new_user: false, new_user: false,
redirect_after_success: '/path/to/redirect/', redirect_after_success: '/path/to/redirect/',
customer: { customer: {
......
import { STEPS } from 'ee/subscriptions/constants'; import { STEPS } from 'ee/subscriptions/constants';
export const accountId = '111111111111';
export const mockCiMinutesPlans = [ export const mockCiMinutesPlans = [
{ {
id: 'ciMinutesPackPlanId', id: 'ciMinutesPackPlanId',
...@@ -9,16 +11,18 @@ export const mockCiMinutesPlans = [ ...@@ -9,16 +11,18 @@ export const mockCiMinutesPlans = [
__typename: 'Plan', __typename: 'Plan',
}, },
]; ];
export const mockNamespaces =
'[{"id":132,"name":"Gitlab Org","users":3},{"id":483,"name":"Gnuwget","users":12}]';
export const mockParsedNamespaces = [ export const mockNamespaces = `
{ __typename: 'Namespace', id: 132, name: 'Gitlab Org', users: 3 }, [{"id":132,"accountId":"${accountId}","name":"Gitlab Org","users":3},
{ __typename: 'Namespace', id: 483, name: 'Gnuwget', users: 12 }, {"id":483,"accountId":null,"name":"Gnuwget","users":12}]
]; `;
export const mockParsedNamespaces = JSON.parse(mockNamespaces).map((namespace) => ({
...namespace,
__typename: 'Namespace',
}));
export const mockNewUser = 'false'; export const mockNewUser = 'false';
export const mockFullName = 'John Admin';
export const mockSetupForCompany = 'true'; export const mockSetupForCompany = 'true';
export const mockDefaultCache = { export const mockDefaultCache = {
...@@ -28,13 +32,13 @@ export const mockDefaultCache = { ...@@ -28,13 +32,13 @@ export const mockDefaultCache = {
}; };
export const stateData = { export const stateData = {
namespaces: [], eligibleNamespaces: [],
subscription: { subscription: {
quantity: 1, quantity: 1,
namespaceId: null,
__typename: 'Subscription', __typename: 'Subscription',
}, },
redirectAfterSuccess: '/path/to/redirect/', redirectAfterSuccess: '/path/to/redirect/',
selectedNamespaceId: null,
selectedPlanId: null, selectedPlanId: null,
paymentMethod: { paymentMethod: {
id: null, id: null,
......
...@@ -2,6 +2,7 @@ import VueApollo from 'vue-apollo'; ...@@ -2,6 +2,7 @@ import VueApollo from 'vue-apollo';
import { writeInitialDataToApolloCache } from 'ee/subscriptions/buy_addons_shared/utils'; import { writeInitialDataToApolloCache } from 'ee/subscriptions/buy_addons_shared/utils';
import plansQuery from 'ee/subscriptions/graphql/queries/plans.customer.query.graphql'; import plansQuery from 'ee/subscriptions/graphql/queries/plans.customer.query.graphql';
import { createMockClient } from 'helpers/mock_apollo_helper'; import { createMockClient } from 'helpers/mock_apollo_helper';
import { CUSTOMERSDOT_CLIENT } from 'ee/subscriptions/buy_addons_shared/constants';
import { mockCiMinutesPlans, mockDefaultCache } from './mock_data'; import { mockCiMinutesPlans, mockDefaultCache } from './mock_data';
export function createMockApolloProvider(mockResponses = {}, dataset = {}) { export function createMockApolloProvider(mockResponses = {}, dataset = {}) {
...@@ -12,11 +13,11 @@ export function createMockApolloProvider(mockResponses = {}, dataset = {}) { ...@@ -12,11 +13,11 @@ export function createMockApolloProvider(mockResponses = {}, dataset = {}) {
const { quantity } = dataset; const { quantity } = dataset;
const mockDefaultClient = createMockClient(); const mockDefaultClient = createMockClient();
const mockCustomerClient = createMockClient([[plansQuery, plansQueryMock]]); const mockCustomersDotClient = createMockClient([[plansQuery, plansQueryMock]]);
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
defaultClient: mockDefaultClient, defaultClient: mockDefaultClient,
clients: { customerClient: mockCustomerClient }, clients: { [CUSTOMERSDOT_CLIENT]: mockCustomersDotClient },
}); });
writeInitialDataToApolloCache(apolloProvider, { writeInitialDataToApolloCache(apolloProvider, {
......
...@@ -85,7 +85,7 @@ describe('Billing Address', () => { ...@@ -85,7 +85,7 @@ describe('Billing Address', () => {
}); });
describe('validations', () => { describe('validations', () => {
const isStepValid = () => wrapper.find(Step).props('isValid'); const isStepValid = () => wrapper.findComponent(Step).props('isValid');
beforeEach(() => { beforeEach(() => {
store.commit(types.UPDATE_COUNTRY, 'country'); store.commit(types.UPDATE_COUNTRY, 'country');
......
...@@ -31,8 +31,8 @@ describe('Confirm Order', () => { ...@@ -31,8 +31,8 @@ describe('Confirm Order', () => {
}); });
} }
const findConfirmButton = () => wrapper.find(GlButton); const findConfirmButton = () => wrapper.findComponent(GlButton);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
......
...@@ -44,7 +44,7 @@ describe('Payment Method', () => { ...@@ -44,7 +44,7 @@ describe('Payment Method', () => {
}); });
describe('validations', () => { describe('validations', () => {
const isStepValid = () => wrapper.find(Step).props('isValid'); const isStepValid = () => wrapper.findComponent(Step).props('isValid');
it('should be valid when paymentMethodId is defined', () => { it('should be valid when paymentMethodId is defined', () => {
expect(isStepValid()).toBe(true); expect(isStepValid()).toBe(true);
......
...@@ -263,7 +263,7 @@ describe('Subscription Details', () => { ...@@ -263,7 +263,7 @@ describe('Subscription Details', () => {
}); });
describe('validations', () => { describe('validations', () => {
const isStepValid = () => wrapper.find(Step).props('isValid'); const isStepValid = () => wrapper.findComponent(Step).props('isValid');
let store; let store;
beforeEach(() => { beforeEach(() => {
......
...@@ -39,7 +39,7 @@ describe('Zuora', () => { ...@@ -39,7 +39,7 @@ describe('Zuora', () => {
}); });
}; };
const findLoading = () => wrapper.find(GlLoadingIcon); const findLoading = () => wrapper.findComponent(GlLoadingIcon);
const findZuoraPayment = () => wrapper.find('#zuora_payment'); const findZuoraPayment = () => wrapper.find('#zuora_payment');
beforeEach(() => { beforeEach(() => {
......
...@@ -17,7 +17,7 @@ describe('Checkout', () => { ...@@ -17,7 +17,7 @@ describe('Checkout', () => {
}); });
}; };
const findProgressBar = () => wrapper.find(ProgressBar); const findProgressBar = () => wrapper.findComponent(ProgressBar);
beforeEach(() => { beforeEach(() => {
store = createStore(); store = createStore();
......
import { mount, createLocalVue } from '@vue/test-utils'; import { createLocalVue } from '@vue/test-utils';
import { merge } from 'lodash'; import { merge } from 'lodash';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import { resolvers } from 'ee/subscriptions/buy_addons_shared/graphql/resolvers'; import { resolvers } from 'ee/subscriptions/buy_addons_shared/graphql/resolvers';
...@@ -6,8 +6,13 @@ import { STEPS } from 'ee/subscriptions/constants'; ...@@ -6,8 +6,13 @@ import { STEPS } from 'ee/subscriptions/constants';
import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql'; import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql';
import PaymentMethod from 'ee/vue_shared/purchase_flow/components/checkout/payment_method.vue'; import PaymentMethod from 'ee/vue_shared/purchase_flow/components/checkout/payment_method.vue';
import Step from 'ee/vue_shared/purchase_flow/components/step.vue'; import Step from 'ee/vue_shared/purchase_flow/components/step.vue';
import { stateData as initialStateData } from 'ee_jest/subscriptions/buy_minutes/mock_data'; import {
mockParsedNamespaces,
stateData as initialStateData,
} from 'ee_jest/subscriptions/buy_minutes/mock_data';
import { createMockApolloProvider } from 'ee_jest/vue_shared/purchase_flow/spec_helper'; import { createMockApolloProvider } from 'ee_jest/vue_shared/purchase_flow/spec_helper';
import Zuora from 'ee/vue_shared/purchase_flow/components/checkout/zuora.vue';
import { mountExtended } from 'helpers/vue_test_utils_helper';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(VueApollo); localVue.use(VueApollo);
...@@ -15,6 +20,12 @@ localVue.use(VueApollo); ...@@ -15,6 +20,12 @@ localVue.use(VueApollo);
describe('Payment Method', () => { describe('Payment Method', () => {
let wrapper; let wrapper;
const findCCDetails = () => wrapper.findByTestId('card-details');
const findCCExpiration = () => wrapper.findByTestId('card-expiration');
const findZuora = () => wrapper.findComponent(Zuora);
const isStepValid = () => wrapper.findComponent(Step).props('isValid');
const createComponent = (apolloLocalState = {}) => { const createComponent = (apolloLocalState = {}) => {
const apolloProvider = createMockApolloProvider(STEPS, STEPS[2], { const apolloProvider = createMockApolloProvider(STEPS, STEPS[2], {
...resolvers, ...resolvers,
...@@ -24,7 +35,7 @@ describe('Payment Method', () => { ...@@ -24,7 +35,7 @@ describe('Payment Method', () => {
data: merge({}, initialStateData, apolloLocalState), data: merge({}, initialStateData, apolloLocalState),
}); });
return mount(PaymentMethod, { return mountExtended(PaymentMethod, {
localVue, localVue,
apolloProvider, apolloProvider,
}); });
...@@ -46,14 +57,12 @@ describe('Payment Method', () => { ...@@ -46,14 +57,12 @@ describe('Payment Method', () => {
wrapper.destroy(); wrapper.destroy();
}); });
describe('validations', () => { describe('payment method step', () => {
const isStepValid = () => wrapper.find(Step).props('isValid'); it('is valid when paymentMethodId is defined', () => {
it('should be valid when paymentMethodId is defined', () => {
expect(isStepValid()).toBe(true); expect(isStepValid()).toBe(true);
}); });
it('should be invalid when paymentMethodId is undefined', () => { it('is invalid when paymentMethodId is undefined', () => {
wrapper = createComponent({ wrapper = createComponent({
paymentMethod: { id: null }, paymentMethod: { id: null },
}); });
...@@ -62,15 +71,50 @@ describe('Payment Method', () => { ...@@ -62,15 +71,50 @@ describe('Payment Method', () => {
}); });
}); });
describe('showing the summary', () => { describe('summary', () => {
it('should show the entered credit card details', () => { it('shows the entered credit card details', () => {
expect(wrapper.find('.js-summary-line-1').text()).toMatchInterpolatedText( expect(findCCDetails().text()).toMatchInterpolatedText('Visa ending in 4242');
'Visa ending in 4242', });
);
it('shows the entered credit card expiration date', () => {
expect(findCCExpiration().text()).toBe('Exp 12/09');
});
});
describe('Zuora Component', () => {
const eligibleNamespaces = mockParsedNamespaces;
const active = true;
const activeStep = STEPS[2];
describe('when the selected namespace exists', () => {
describe('when it has an account id', () => {
it('has the selected account id', () => {
const { accountId, id } = mockParsedNamespaces[0];
const selectedNamespaceId = `${id}`;
wrapper = createComponent({ eligibleNamespaces, selectedNamespaceId, activeStep });
expect(findZuora().props()).toMatchObject({ active, accountId });
});
});
describe('when it has no account id', () => {
it('has the default account id', () => {
const { id } = mockParsedNamespaces[1];
const selectedNamespaceId = `${id}`;
wrapper = createComponent({ eligibleNamespaces, selectedNamespaceId, activeStep });
expect(findZuora().props()).toMatchObject({ active, accountId: '' });
});
});
}); });
it('should show the entered credit card expiration date', () => { describe('when the selected namespace does not exists', () => {
expect(wrapper.find('.js-summary-line-2').text()).toBe('Exp 12/09'); it('has the default account id', () => {
const selectedNamespaceId = `000`;
wrapper = createComponent({ eligibleNamespaces, selectedNamespaceId, activeStep });
expect(findZuora().props()).toMatchObject({ active, accountId: '' });
});
}); });
}); });
}); });
...@@ -10,6 +10,7 @@ import Zuora from 'ee/vue_shared/purchase_flow/components/checkout/zuora.vue'; ...@@ -10,6 +10,7 @@ import Zuora from 'ee/vue_shared/purchase_flow/components/checkout/zuora.vue';
import { stateData as initialStateData } from 'ee_jest/subscriptions/buy_minutes/mock_data'; import { stateData as initialStateData } from 'ee_jest/subscriptions/buy_minutes/mock_data';
import { createMockApolloProvider } from 'ee_jest/vue_shared/purchase_flow/spec_helper'; import { createMockApolloProvider } from 'ee_jest/vue_shared/purchase_flow/spec_helper';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import flushPromises from 'helpers/flush_promises';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(VueApollo); localVue.use(VueApollo);
...@@ -39,7 +40,7 @@ describe('Zuora', () => { ...@@ -39,7 +40,7 @@ describe('Zuora', () => {
}); });
}; };
const findLoading = () => wrapper.find(GlLoadingIcon); const findLoading = () => wrapper.findComponent(GlLoadingIcon);
const findZuoraPayment = () => wrapper.find('#zuora_payment'); const findZuoraPayment = () => wrapper.find('#zuora_payment');
beforeEach(() => { beforeEach(() => {
...@@ -47,7 +48,7 @@ describe('Zuora', () => { ...@@ -47,7 +48,7 @@ describe('Zuora', () => {
runAfterRender(fn) { runAfterRender(fn) {
return Promise.resolve().then(fn); return Promise.resolve().then(fn);
}, },
render() {}, render: jest.fn(),
}; };
axiosMock = new AxiosMockAdapter(axios); axiosMock = new AxiosMockAdapter(axios);
...@@ -97,4 +98,25 @@ describe('Zuora', () => { ...@@ -97,4 +98,25 @@ describe('Zuora', () => {
expect(findZuoraPayment().isVisible()).toBe(false); expect(findZuoraPayment().isVisible()).toBe(false);
}); });
}); });
describe.each(['', '111111'])('when rendering the iframe with account id: %s', (id) => {
beforeEach(() => {
wrapper = createComponent({ accountId: id }, { isLoading: false });
wrapper.vm.zuoraScriptEl.onload();
return flushPromises();
});
it(`calls render with ${id}`, () => {
expect(window.Z.render).toHaveBeenCalledWith(
{
field_accountId: id,
retainValues: 'true',
style: 'inline',
submitEnabled: 'true',
},
{},
expect.any(Function),
);
});
});
}); });
...@@ -171,14 +171,14 @@ describe('Step', () => { ...@@ -171,14 +171,14 @@ describe('Step', () => {
const mockApollo = createMockApolloProvider(STEPS, 1); const mockApollo = createMockApolloProvider(STEPS, 1);
wrapper = createComponent({ apolloProvider: mockApollo }); wrapper = createComponent({ apolloProvider: mockApollo });
expect(wrapper.find(GlButton).attributes('disabled')).toBeUndefined(); expect(wrapper.findComponent(GlButton).attributes('disabled')).toBeUndefined();
}); });
it('displays an error if navigating too far', async () => { it('displays an error if navigating too far', async () => {
const mockApollo = createMockApolloProvider(STEPS, 2); const mockApollo = createMockApolloProvider(STEPS, 2);
wrapper = createComponent({ propsData: { stepId: STEPS[2].id }, apolloProvider: mockApollo }); wrapper = createComponent({ propsData: { stepId: STEPS[2].id }, apolloProvider: mockApollo });
wrapper.find(GlButton).vm.$emit('click'); wrapper.findComponent(GlButton).vm.$emit('click');
await waitForPromises(); await waitForPromises();
expect(flash.mock.calls).toHaveLength(1); expect(flash.mock.calls).toHaveLength(1);
......
...@@ -51,7 +51,7 @@ RSpec.describe SubscriptionsHelper do ...@@ -51,7 +51,7 @@ RSpec.describe SubscriptionsHelper do
it { is_expected.to include(plan_id: 'bronze_id') } it { is_expected.to include(plan_id: 'bronze_id') }
it { is_expected.to include(namespace_id: group.id.to_s) } it { is_expected.to include(namespace_id: group.id.to_s) }
it { is_expected.to include(source: 'some_source') } it { is_expected.to include(source: 'some_source') }
it { is_expected.to include(group_data: %Q{[{"id":#{group.id},"name":"My Namespace","users":2,"guests":1}]}) } it { is_expected.to include(group_data: %Q{[{"id":#{group.id},"account_id":null,"name":"My Namespace","users":2,"guests":1}]}) }
describe 'new_user' do describe 'new_user' do
where(:referer, :expected_result) do where(:referer, :expected_result) do
...@@ -150,7 +150,7 @@ RSpec.describe SubscriptionsHelper do ...@@ -150,7 +150,7 @@ RSpec.describe SubscriptionsHelper do
it { is_expected.to include(namespace_id: group.id.to_s) } it { is_expected.to include(namespace_id: group.id.to_s) }
it { is_expected.to include(source: 'some_source') } it { is_expected.to include(source: 'some_source') }
it { is_expected.to include(group_data: %Q{[{"id":#{group.id},"name":"My Namespace","users":1,"guests":0}]}) } it { is_expected.to include(group_data: %Q{[{"id":#{group.id},"account_id":null,"name":"My Namespace","users":1,"guests":0}]}) }
it { is_expected.to include(redirect_after_success: group_usage_quotas_path(group, anchor: anchor, purchased_product: purchased_product)) } it { is_expected.to include(redirect_after_success: group_usage_quotas_path(group, anchor: anchor, purchased_product: purchased_product)) }
end end
end end
export default function flushPromises() {
return new Promise(setImmediate);
}
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