Skip to content

Commit

Permalink
[INTER-3588] Add style options for fastlane components (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
kfalkowski11 authored May 8, 2024
1 parent daa435c commit 898a084
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 8 deletions.
8 changes: 5 additions & 3 deletions src/fastlane/initFastlane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
getBraintreeClient,
IBraintreeClient,
FastlaneLoadingError,
IFastlaneOptions,
} from 'src';
import Fastlane from './fastlane';

Expand All @@ -28,7 +29,7 @@ interface PPCPTokenResponse extends TokenResponse {
client_id: string;
}

export async function initFastlane(): Promise<IFastlaneInstance> {
export async function initFastlane(options?: IFastlaneOptions): Promise<IFastlaneInstance> {
const {clientJsURL, dataCollectorJsURL, fastlaneJsURL} = getBraintreeJsUrls('3.101.0-fastlane-beta.7.2');

try {
Expand Down Expand Up @@ -71,6 +72,7 @@ export async function initFastlane(): Promise<IFastlaneInstance> {
client,
authorization: clientToken,
deviceData: dataCollector.deviceData,
styles: options?.styles
});

break;
Expand All @@ -81,8 +83,8 @@ export async function initFastlane(): Promise<IFastlaneInstance> {
clientId: clientId,
components: 'fastlane',
debug: isTest,
}) as unknown as {Fastlane: () => Promise<IFastlaneInstance>};
fastlane = await paypal.Fastlane();
}) as unknown as {Fastlane: (options?: IFastlaneOptions) => Promise<IFastlaneInstance>};
fastlane = await paypal.Fastlane(options);

break;
}
Expand Down
6 changes: 3 additions & 3 deletions src/fastlane/manageFastlaneState.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {IFastlaneInstance} from 'src/types';
import {IFastlaneInstance, IFastlaneOptions} from 'src/types';
import {fastlaneState} from 'src/variables';
import {initFastlane} from './initFastlane';

Expand All @@ -8,8 +8,8 @@ import {initFastlane} from './initFastlane';
* and instance is being initialized will return the same promise, avoiding duplicate initializations
* of the Fastlane instance.
*/
export const getFastlaneInstance = async (): Promise<IFastlaneInstance> => {
return fastlaneState.instance ?? (fastlaneState.instance = initFastlane().catch((e) => {
export const getFastlaneInstance = async (options?: IFastlaneOptions): Promise<IFastlaneInstance> => {
return fastlaneState.instance ?? (fastlaneState.instance = initFastlane(options).catch((e) => {
// Clearing the rejected promise from state so we can try again
fastlaneState.instance = null;
throw e;
Expand Down
3 changes: 2 additions & 1 deletion src/types/braintree.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {IFastlaneInstance} from './fastlane';
import {IFastlaneInstance, IFastlaneStyleOptions} from './fastlane';
import ApplePayPaymentRequest = ApplePayJS.ApplePayPaymentRequest;
import ApplePayPaymentToken = ApplePayJS.ApplePayPaymentToken;
import GooglePaymentData = google.payments.api.PaymentData;
Expand Down Expand Up @@ -33,6 +33,7 @@ export interface IBraintreeFastlaneCreateRequest {
metadata?: {
geoLocOverride: string;
};
styles: IFastlaneStyleOptions|undefined
}

export interface IBraintreeDataCollectorCreateRequest {
Expand Down
51 changes: 51 additions & 0 deletions src/types/fastlane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,55 @@ export interface IFastlaneInstance {
};
FastlanePaymentComponent: (options: IFastlaneComponentOptions) => Promise<IFastlanePaymentComponent>;
FastlaneCardComponent: (options: Omit<IFastlaneComponentOptions, 'shippingAddress'>) => IFastlaneCardComponent;
}

export interface IFastlaneOptions {
shippingAddressOptions: IFastlaneAddressOptions,
cardOptions: IFastlaneCardOptions,
styles: IFastlaneStyleOptions
}

export interface IFastlaneAddressOptions {
// default: empty array = all locations allowed
allowedLocations: string[];
}

export interface IFastlaneCardOptions {
// default: empty array = all brands allowed
allowedBrands: IFastlaneCardBrandTypes[];
}

export type IFastlaneCardBrandTypes =
'VISA' |
'MASTERCARD' |
'AMERICAN-EXPRESS' |
'DINERS-CLUB' |
'DISCOVER' |
'JCB' |
'UNION-PAY' |
'MAESTRO' |
'ELO' |
'MIR' |
'HIPER' |
'HIPERCARD';


export interface IFastlaneStyleOptions {
root: {
backgroundColor: string
errorColor: string,
fontFamily: string,
textColorBase: string,
fontSizeBase: string,
padding: string,
primaryColor: string
},
input: {
backgroundColor: string,
borderRadius: string,
borderColor: string,
borderWidth: string,
textColorBase: string,
focusBorderColor: string,
}
}
125 changes: 124 additions & 1 deletion tests/fastlane/initFastlane.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {getEnvironment, getJwtToken, getPublicOrderId, getShopIdentifier} from '@boldcommerce/checkout-frontend-library';
import {loadScript} from '@paypal/paypal-js';
import {mocked} from 'jest-mock';
import {IBraintreeUrls, braintreeOnLoadClient, getBraintreeClient, getBraintreeJsUrls, initFastlane, loadJS} from 'src';
import {IBraintreeUrls, IFastlaneOptions, braintreeOnLoadClient, getBraintreeClient, getBraintreeJsUrls, initFastlane, loadJS} from 'src';

jest.mock('src/braintree/getBraintreeJsUrls.ts');
jest.mock('src/utils/loadJS.ts');
Expand Down Expand Up @@ -113,6 +113,79 @@ describe('testing initFastlane function', () => {

});

test('init braintree correctly with options', async () => {
// Arranging
getBraintreeJsUrlsMock.mockReturnValue({
clientJsURL: 'client', dataCollectorJsURL: 'data', fastlaneJsURL: 'fastlane',
} as IBraintreeUrls);
getEnvironmentMock.mockReturnValue({
path: 'path',
type: 'testing',
url: 'https://staging.com',
});
getPublicOrderIdMock.mockReturnValue('testOrderId');
getShopIdentifierMock.mockReturnValue('testShopId');
getJwtTokenMock.mockReturnValue('jwt');
fetchMock.mockResolvedValue({
json: () => Promise.resolve({
data: {
client_token: 'client_token',
client_id: null,
type: 'braintree',
is_test_mode: false,
gateway_public_id: 'gatewayPublicId',
},
}),
});
const client = {create: jest.fn()};
const fastlane = {create: jest.fn()};
fastlane.create.mockReturnValue({
setLocale: jest.fn(),
FastlaneCardComponent: jest.fn(),
FastlanePaymentComponent: jest.fn(),
identity: {
lookupCustomerByEmail: jest.fn(),
},
profile: {
showCardSelector: jest.fn(),
},
});

const dataCollector = {create: jest.fn()};
dataCollector.create.mockReturnValue({deviceData: null});

getBraintreeClientMock.mockReturnValue({
client,
fastlane,
dataCollector,
applePay: {create: jest.fn()},
googlePayment: {create: jest.fn()},
});

const options = {
styles: {}
} as IFastlaneOptions;

// Assigning
const actualFastlane = await initFastlane(options);

// Asserting
expect(actualFastlane.gatewayPublicId).toBe('gatewayPublicId');
expect(actualFastlane.type).toBe('braintree');

expect(loadJSMock).toBeCalledTimes(3);
expect(loadJSMock).toBeCalledWith('client');
expect(loadJSMock).toBeCalledWith('fastlane');
expect(loadJSMock).toBeCalledWith('data');

expect(braintreeOnLoadClientMock).toBeCalled();

expect(client.create).toBeCalled();
expect(dataCollector.create).toBeCalled();
expect(fastlane.create).toBeCalled();

});

test('init ppcp correctly', async () => {
// Arranging
getBraintreeJsUrlsMock.mockReturnValue({
Expand Down Expand Up @@ -159,6 +232,56 @@ describe('testing initFastlane function', () => {
expect(actualFastlane.type).toBe('ppcp');
});

test('init ppcp correctly with options', async () => {
// Arranging
getBraintreeJsUrlsMock.mockReturnValue({
clientJsURL: 'client', dataCollectorJsURL: 'data', fastlaneJsURL: 'fastlane',
} as IBraintreeUrls);
getEnvironmentMock.mockReturnValue({
path: 'path',
type: 'testing',
url: 'https://staging.com',
});
getPublicOrderIdMock.mockReturnValue('testOrderId');
getShopIdentifierMock.mockReturnValue('testShopId');
getJwtTokenMock.mockReturnValue('jwt');
fetchMock.mockResolvedValue({
json: () => Promise.resolve({
data: {
client_token: 'client_token',
client_id: 'client_id',
type: 'ppcp',
is_test_mode: false,
gateway_public_id: 'gatewayPublicId',
},
}),
});
loadScriptMock.mockResolvedValue({
Fastlane: () => Promise.resolve({
setLocale: jest.fn(),
FastlaneCardComponent: jest.fn(),
FastlanePaymentComponent: jest.fn(),
identity: {
lookupCustomerByEmail: jest.fn(),
},
profile: {
showCardSelector: jest.fn(),
},
}),
});

const options = {
styles: {}
} as IFastlaneOptions;

// Assigning
const actualFastlane = await initFastlane(options);

// Asserting
expect(actualFastlane.gatewayPublicId).toBe('gatewayPublicId');
expect(actualFastlane.type).toBe('ppcp');
});

test('init error', async () => {
// Arranging
getBraintreeJsUrlsMock.mockReturnValue({
Expand Down

0 comments on commit 898a084

Please sign in to comment.