initial commit

This commit is contained in:
2021-12-10 12:03:04 +00:00
commit c46c7ddbf0
3643 changed files with 582794 additions and 0 deletions

View File

@ -0,0 +1,66 @@
/**
* External dependencies
*/
import { isValidElement } from '@wordpress/element';
import type { ReactNode } from 'react';
import type {
PaymentMethodConfiguration,
ExpressPaymentMethodConfiguration,
} from '@woocommerce/type-defs/payments';
export const assertValidPaymentMethodComponent = (
component: () => unknown,
componentName: string
): void => {
if ( typeof component !== 'function' ) {
throw new TypeError(
`The ${ componentName } property for the payment method must be a functional component`
);
}
};
export const assertValidElement = (
element: ReactNode,
elementName: string
): void => {
if ( element !== null && ! isValidElement( element ) ) {
throw new TypeError(
`The ${ elementName } property for the payment method must be a React element or null.`
);
}
};
export const assertValidElementOrString = (
element: ReactNode,
elementName: string
): void => {
if (
element !== null &&
! isValidElement( element ) &&
typeof element !== 'string'
) {
throw new TypeError(
`The ${ elementName } property for the payment method must be a React element, a string, or null.`
);
}
};
export const assertConfigHasProperties = (
config: ExpressPaymentMethodConfiguration | PaymentMethodConfiguration,
expectedProperties: string[] = []
): void => {
const missingProperties = expectedProperties.reduce(
( acc: string[], property: string ) => {
if ( ! config.hasOwnProperty( property ) ) {
acc.push( property );
}
return acc;
},
[]
);
if ( missingProperties.length > 0 ) {
const message =
'The payment method configuration object is missing the following properties:';
throw new TypeError( message + missingProperties.join( ', ' ) );
}
};

View File

@ -0,0 +1,82 @@
/**
* External dependencies
*/
import type { ReactNode } from 'react';
import type {
ExpressPaymentMethodConfiguration,
Supports,
CanMakePaymentCallback,
ExpressPaymentMethodConfigInstance,
} from '@woocommerce/type-defs/payments';
/**
* Internal dependencies
*/
import { getCanMakePayment } from './payment-method-config-helper';
import { assertConfigHasProperties, assertValidElement } from './assertions';
export default class ExpressPaymentMethodConfig
implements ExpressPaymentMethodConfigInstance {
public name: string;
public content: ReactNode;
public edit: ReactNode;
public paymentMethodId?: string;
public supports: Supports;
public canMakePaymentFromConfig: CanMakePaymentCallback;
constructor( config: ExpressPaymentMethodConfiguration ) {
// validate config
ExpressPaymentMethodConfig.assertValidConfig( config );
this.name = config.name;
this.content = config.content;
this.edit = config.edit;
this.paymentMethodId = config.paymentMethodId || this.name;
this.supports = {
features: config?.supports?.features || [ 'products' ],
};
this.canMakePaymentFromConfig = config.canMakePayment;
}
// canMakePayment is calculated each time based on data that modifies outside of the class (eg: cart data).
get canMakePayment(): CanMakePaymentCallback {
return getCanMakePayment(
this.canMakePaymentFromConfig,
this.supports.features,
this.name
);
}
static assertValidConfig = (
config: ExpressPaymentMethodConfiguration
): void => {
assertConfigHasProperties( config, [ 'name', 'content', 'edit' ] );
if ( typeof config.name !== 'string' ) {
throw new TypeError(
'The name property for the express payment method must be a string'
);
}
if (
typeof config.paymentMethodId !== 'string' &&
typeof config.paymentMethodId !== 'undefined'
) {
throw new Error(
'The paymentMethodId property for the payment method must be a string or undefined (in which case it will be the value of the name property).'
);
}
if (
typeof config.supports?.features !== 'undefined' &&
! Array.isArray( config.supports?.features )
) {
throw new Error(
'The features property for the payment method must be an array or undefined.'
);
}
assertValidElement( config.content, 'content' );
assertValidElement( config.edit, 'edit' );
if ( typeof config.canMakePayment !== 'function' ) {
throw new TypeError(
'The canMakePayment property for the express payment method must be a function.'
);
}
};
}

View File

@ -0,0 +1,23 @@
/**
* External dependencies
*/
import { CanMakePaymentExtensionCallback } from '@woocommerce/type-defs/payments';
type CanMakePaymentExtensionCallbacks = Record<
string,
CanMakePaymentExtensionCallback
>;
export type NamespacedCanMakePaymentExtensionsCallbacks = Record<
string,
CanMakePaymentExtensionCallbacks
>;
export type ExtensionNamespace = keyof NamespacedCanMakePaymentExtensionsCallbacks;
export type PaymentMethodName = keyof CanMakePaymentExtensionCallbacks;
// Keeps callbacks registered by extensions for different payment methods
// eslint-disable-next-line prefer-const
export const canMakePaymentExtensionsCallbacks: NamespacedCanMakePaymentExtensionsCallbacks = {};
export const extensionsConfig = {
canMakePayment: canMakePaymentExtensionsCallbacks,
};

View File

@ -0,0 +1 @@
export * from './registry';

View File

@ -0,0 +1,90 @@
/**
* External dependencies
*/
import type {
CanMakePaymentCallback,
CanMakePaymentExtensionCallback,
} from '@woocommerce/type-defs/payments';
/**
* Internal dependencies
*/
import {
NamespacedCanMakePaymentExtensionsCallbacks,
PaymentMethodName,
ExtensionNamespace,
extensionsConfig,
} from './extensions-config';
// Filter out payment methods by supported features and cart requirement.
export const canMakePaymentWithFeaturesCheck = (
canMakePayment: CanMakePaymentCallback,
features: string[]
): CanMakePaymentCallback => ( canPayArgument ) => {
const requirements = canPayArgument?.paymentRequirements || [];
const featuresSupportRequirements = requirements.every( ( requirement ) =>
features.includes( requirement )
);
return featuresSupportRequirements && canMakePayment( canPayArgument );
};
// Filter out payment methods by callbacks registered by extensions.
export const canMakePaymentWithExtensions = (
canMakePayment: CanMakePaymentCallback,
extensionsCallbacks: NamespacedCanMakePaymentExtensionsCallbacks,
paymentMethodName: PaymentMethodName
): CanMakePaymentCallback => ( canPayArgument ) => {
// Validate whether the payment method is available based on its own criteria first.
let canPay = canMakePayment( canPayArgument );
if ( canPay ) {
// Gather all callbacks for paymentMethodName.
const namespacedCallbacks: Record<
ExtensionNamespace,
CanMakePaymentExtensionCallback
> = {};
Object.entries( extensionsCallbacks ).forEach(
( [ namespace, callbacks ] ) => {
namespacedCallbacks[ namespace ] =
callbacks[ paymentMethodName ];
}
);
canPay = Object.keys( namespacedCallbacks ).every( ( namespace ) => {
try {
return namespacedCallbacks[ namespace ]( canPayArgument );
} catch ( err ) {
// eslint-disable-next-line no-console
console.error(
`Error when executing callback for ${ paymentMethodName } in ${ namespace }`,
err
);
// .every() expects a return value at the end of every arrow function and
// this ensures that the error is ignored when computing the whole result.
return true;
}
} );
}
return canPay;
};
export const getCanMakePayment = (
canMakePayment: CanMakePaymentCallback,
features: string[],
paymentMethodName: string
): CanMakePaymentCallback => {
const canPay = canMakePaymentWithFeaturesCheck( canMakePayment, features );
// Loop through all callbacks to check if there are any registered for this payment method.
return ( Object.values( extensionsConfig.canMakePayment ) as Record<
PaymentMethodName,
CanMakePaymentCallback
>[] ).some( ( callbacks ) => paymentMethodName in callbacks )
? canMakePaymentWithExtensions(
canPay,
extensionsConfig.canMakePayment,
paymentMethodName
)
: canPay;
};

View File

@ -0,0 +1,167 @@
/**
* External dependencies
*/
import deprecated from '@wordpress/deprecated';
import type { ReactNode } from 'react';
import type {
PaymentMethodConfiguration,
Supports,
CanMakePaymentCallback,
PaymentMethodConfigInstance,
PaymentMethodIcons,
} from '@woocommerce/type-defs/payments';
/**
* Internal dependencies
*/
import { getCanMakePayment } from './payment-method-config-helper';
import {
assertConfigHasProperties,
assertValidElement,
assertValidElementOrString,
} from './assertions';
const NullComponent = () => {
return null;
};
export default class PaymentMethodConfig
implements PaymentMethodConfigInstance {
public name: string;
public content: ReactNode;
public edit: ReactNode;
public paymentMethodId?: string;
public supports: Supports;
public icons: null | PaymentMethodIcons;
public label: ReactNode;
public ariaLabel: string;
public placeOrderButtonLabel?: string;
public savedTokenComponent?: ReactNode | null;
public canMakePaymentFromConfig: CanMakePaymentCallback;
constructor( config: PaymentMethodConfiguration ) {
// validate config
PaymentMethodConfig.assertValidConfig( config );
this.name = config.name;
this.label = config.label;
this.placeOrderButtonLabel = config.placeOrderButtonLabel;
this.ariaLabel = config.ariaLabel;
this.content = config.content;
this.savedTokenComponent = config.savedTokenComponent;
this.icons = config.icons || null;
this.edit = config.edit;
this.paymentMethodId = config.paymentMethodId || this.name;
this.supports = {
showSavedCards:
config?.supports?.showSavedCards ||
config?.supports?.savePaymentInfo || // Kept for backward compatibility if methods still pass this when registering.
false,
showSaveOption: config?.supports?.showSaveOption || false,
features: config?.supports?.features || [ 'products' ],
};
this.canMakePaymentFromConfig = config.canMakePayment;
}
// canMakePayment is calculated each time based on data that modifies outside of the class (eg: cart data).
get canMakePayment(): CanMakePaymentCallback {
return getCanMakePayment(
this.canMakePaymentFromConfig,
this.supports.features,
this.name
);
}
static assertValidConfig = ( config: PaymentMethodConfiguration ): void => {
// set default for optional
config.savedTokenComponent = config.savedTokenComponent || (
<NullComponent />
);
assertConfigHasProperties( config, [
'name',
'label',
'ariaLabel',
'content',
'edit',
'canMakePayment',
] );
if ( typeof config.name !== 'string' ) {
throw new Error(
'The name property for the payment method must be a string'
);
}
if (
typeof config.icons !== 'undefined' &&
! Array.isArray( config.icons ) &&
config.icons !== null
) {
throw new Error(
'The icons property for the payment method must be an array or null.'
);
}
if (
typeof config.paymentMethodId !== 'string' &&
typeof config.paymentMethodId !== 'undefined'
) {
throw new Error(
'The paymentMethodId property for the payment method must be a string or undefined (in which case it will be the value of the name property).'
);
}
if (
typeof config.placeOrderButtonLabel !== 'string' &&
typeof config.placeOrderButtonLabel !== 'undefined'
) {
throw new TypeError(
'The placeOrderButtonLabel property for the payment method must be a string'
);
}
assertValidElementOrString( config.label, 'label' );
assertValidElement( config.content, 'content' );
assertValidElement( config.edit, 'edit' );
assertValidElement( config.savedTokenComponent, 'savedTokenComponent' );
if ( typeof config.ariaLabel !== 'string' ) {
throw new TypeError(
'The ariaLabel property for the payment method must be a string'
);
}
if ( typeof config.canMakePayment !== 'function' ) {
throw new TypeError(
'The canMakePayment property for the payment method must be a function.'
);
}
if (
typeof config.supports?.showSavedCards !== 'undefined' &&
typeof config.supports?.showSavedCards !== 'boolean'
) {
throw new TypeError(
'If the payment method includes the `supports.showSavedCards` property, it must be a boolean'
);
}
if ( typeof config.supports?.savePaymentInfo !== 'undefined' ) {
deprecated(
'Passing savePaymentInfo when registering a payment method.',
{
alternative: 'Pass showSavedCards and showSaveOption',
plugin: 'woocommerce-gutenberg-products-block',
link:
'https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3686',
}
);
}
if (
typeof config.supports?.features !== 'undefined' &&
! Array.isArray( config.supports?.features )
) {
throw new Error(
'The features property for the payment method must be an array or undefined.'
);
}
if (
typeof config.supports?.showSaveOption !== 'undefined' &&
typeof config.supports?.showSaveOption !== 'boolean'
) {
throw new TypeError(
'If the payment method includes the `supports.showSaveOption` property, it must be a boolean'
);
}
};
}

View File

@ -0,0 +1,130 @@
/**
* External dependencies
*/
import deprecated from '@wordpress/deprecated';
import type {
PaymentMethodConfiguration,
ExpressPaymentMethodConfiguration,
CanMakePaymentExtensionCallback,
PaymentMethodConfigInstance,
PaymentMethods,
ExpressPaymentMethods,
} from '@woocommerce/type-defs/payments';
/**
* Internal dependencies
*/
import { default as PaymentMethodConfig } from './payment-method-config';
import { default as ExpressPaymentMethodConfig } from './express-payment-method-config';
import { canMakePaymentExtensionsCallbacks } from './extensions-config';
type LegacyRegisterPaymentMethodFunction = ( config: unknown ) => unknown;
type LegacyRegisterExpessPaymentMethodFunction = ( config: unknown ) => unknown;
const paymentMethods: PaymentMethods = {};
const expressPaymentMethods: ExpressPaymentMethods = {};
/**
* Register a regular payment method.
*/
export const registerPaymentMethod = (
options: PaymentMethodConfiguration | LegacyRegisterPaymentMethodFunction
): void => {
let paymentMethodConfig: PaymentMethodConfigInstance | unknown;
if ( typeof options === 'function' ) {
// Legacy fallback for previous API, where client passes a function:
// registerPaymentMethod( ( Config ) => new Config( options ) );
paymentMethodConfig = options( PaymentMethodConfig );
deprecated( 'Passing a callback to registerPaymentMethod()', {
alternative: 'a config options object',
plugin: 'woocommerce-gutenberg-products-block',
link:
'https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3404',
} );
} else {
paymentMethodConfig = new PaymentMethodConfig( options );
}
if ( paymentMethodConfig instanceof PaymentMethodConfig ) {
paymentMethods[ paymentMethodConfig.name ] = paymentMethodConfig;
}
};
/**
* Register an express payment method.
*/
export const registerExpressPaymentMethod = (
options:
| ExpressPaymentMethodConfiguration
| LegacyRegisterExpessPaymentMethodFunction
): void => {
let paymentMethodConfig;
if ( typeof options === 'function' ) {
// Legacy fallback for previous API, where client passes a function:
// registerExpressPaymentMethod( ( Config ) => new Config( options ) );
paymentMethodConfig = options( ExpressPaymentMethodConfig );
deprecated( 'Passing a callback to registerExpressPaymentMethod()', {
alternative: 'a config options object',
plugin: 'woocommerce-gutenberg-products-block',
link:
'https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3404',
} );
} else {
paymentMethodConfig = new ExpressPaymentMethodConfig( options );
}
if ( paymentMethodConfig instanceof ExpressPaymentMethodConfig ) {
expressPaymentMethods[ paymentMethodConfig.name ] = paymentMethodConfig;
}
};
/**
* Allows extension to register callbacks for specific payment methods to determine if they can make payments
*/
export const registerPaymentMethodExtensionCallbacks = (
namespace: string,
callbacks: Record< string, CanMakePaymentExtensionCallback >
): void => {
if ( canMakePaymentExtensionsCallbacks[ namespace ] ) {
// eslint-disable-next-line no-console
console.error(
`The namespace provided to registerPaymentMethodExtensionCallbacks must be unique. Callbacks have already been registered for the ${ namespace } namespace.`
);
} else {
// Set namespace up as an empty object.
canMakePaymentExtensionsCallbacks[ namespace ] = {};
Object.entries( callbacks ).forEach(
( [ paymentMethodName, callback ] ) => {
if ( typeof callback === 'function' ) {
canMakePaymentExtensionsCallbacks[ namespace ][
paymentMethodName
] = callback;
} else {
// eslint-disable-next-line no-console
console.error(
`All callbacks provided to registerPaymentMethodExtensionCallbacks must be functions. The callback for the ${ paymentMethodName } payment method in the ${ namespace } namespace was not a function.`
);
}
}
);
}
};
export const __experimentalDeRegisterPaymentMethod = (
paymentMethodName: string
): void => {
delete paymentMethods[ paymentMethodName ];
};
export const __experimentalDeRegisterExpressPaymentMethod = (
paymentMethodName: string
): void => {
delete expressPaymentMethods[ paymentMethodName ];
};
export const getPaymentMethods = (): PaymentMethods => {
return paymentMethods;
};
export const getExpressPaymentMethods = (): ExpressPaymentMethods => {
return expressPaymentMethods;
};

View File

@ -0,0 +1,205 @@
/**
* External dependencies
*/
import { registerPaymentMethodExtensionCallbacks } from '@woocommerce/blocks-registry';
/**
* Internal dependencies
*/
import * as helpers from '../payment-method-config-helper';
import { canMakePaymentExtensionsCallbacks } from '../extensions-config';
const canMakePaymentArgument = {
cartTotals: {
total_items: '1488',
total_items_tax: '312',
total_fees: '0',
total_fees_tax: '0',
total_discount: '0',
total_discount_tax: '0',
total_shipping: '0',
total_shipping_tax: '0',
total_price: '1800',
total_tax: '312',
tax_lines: [
{
name: 'BTW',
price: '312',
rate: '21%',
},
],
currency_code: 'EUR',
currency_symbol: '€',
currency_minor_unit: 2,
currency_decimal_separator: ',',
currency_thousand_separator: '.',
currency_prefix: '€',
currency_suffix: '',
},
cartNeedsShipping: true,
billingData: {
first_name: 'name',
last_name: 'Name',
company: '',
address_1: 'fdsfdsfdsf',
address_2: '',
city: 'Berlin',
state: '',
postcode: 'xxxxx',
country: 'DE',
email: 'name.Name@test.com',
phone: '1234',
},
shippingAddress: {
first_name: 'name',
last_name: 'Name',
company: '',
address_1: 'fdsfdsfdsf',
address_2: '',
city: 'Berlin',
state: '',
postcode: 'xxxxx',
country: 'DE',
phone: '1234',
},
selectedShippingMethods: {
'0': 'free_shipping:1',
},
paymentRequirements: [ 'products' ],
};
describe( 'payment-method-config-helper', () => {
const trueCallback = jest.fn().mockReturnValue( true );
const falseCallback = jest.fn().mockReturnValue( false );
const bacsCallback = jest.fn().mockReturnValue( false );
const throwsCallback = jest.fn().mockImplementation( () => {
throw new Error();
} );
beforeAll( () => {
// Register extension callbacks for two payment methods.
registerPaymentMethodExtensionCallbacks(
'woocommerce-marketplace-extension',
{
// cod: one extension returns true, the other returns false.
cod: trueCallback,
// cheque: returns true only if arg.billingData.postcode is 12345.
cheque: ( arg ) => arg.billingData.postcode === '12345',
// bacs: both extensions return false.
bacs: bacsCallback,
// woopay: both extensions return true.
woopay: trueCallback,
// testpay: one callback errors, one returns true
testpay: throwsCallback,
}
);
registerPaymentMethodExtensionCallbacks(
'other-woocommerce-marketplace-extension',
{
cod: falseCallback,
woopay: trueCallback,
testpay: trueCallback,
bacs: bacsCallback,
}
);
} );
beforeEach( () => {
trueCallback.mockClear();
throwsCallback.mockClear();
falseCallback.mockClear();
bacsCallback.mockClear();
} );
describe( 'getCanMakePayment', () => {
it( 'returns callback canMakePaymentWithFeaturesCheck if no extension callback is detected', () => {
// Define arguments from a payment method ('missing-payment-method') with no registered extension callbacks.
const args = {
canMakePayment: jest.fn().mockImplementation( () => true ),
features: [ 'products' ],
paymentMethodName: 'missing-payment-method',
};
const canMakePayment = helpers.getCanMakePayment(
args.canMakePayment,
args.features,
args.paymentMethodName
)( canMakePaymentArgument );
// Expect that the result of getCanMakePayment is the result of
// the payment method's own canMakePayment, as no extension callbacks are called.
expect( canMakePayment ).toEqual( args.canMakePayment() );
} );
it( 'returns callbacks from the extensions when they are defined', () => {
// Define arguments from a payment method (bacs) with registered extension callbacks.
const args = {
canMakePaymentConfiguration: jest
.fn()
.mockImplementation( () => true ),
features: [ 'products' ],
paymentMethodName: 'bacs',
};
const canMakePayment = helpers.getCanMakePayment(
args.canMakePaymentConfiguration,
args.features,
args.paymentMethodName
)( canMakePaymentArgument );
// Expect that the result of getCanMakePayment is not the result of
// the payment method's own canMakePayment (args.canMakePaymentConfiguration),
// but of the registered bacsCallback.
expect( canMakePayment ).toBe( bacsCallback() );
} );
} );
describe( 'canMakePaymentWithExtensions', () => {
it( "Returns false without executing the registered callbacks, if the payment method's canMakePayment callback returns false.", () => {
const canMakePayment = () => false;
const canMakePaymentWithExtensionsResult = helpers.canMakePaymentWithExtensions(
canMakePayment,
canMakePaymentExtensionsCallbacks,
'cod'
)( canMakePaymentArgument );
expect( canMakePaymentWithExtensionsResult ).toBe( false );
expect( trueCallback ).not.toHaveBeenCalled();
} );
it( 'Returns early when a registered callback returns false, without executing all the registered callbacks', () => {
helpers.canMakePaymentWithExtensions(
() => true,
canMakePaymentExtensionsCallbacks,
'bacs'
)( canMakePaymentArgument );
expect( bacsCallback ).toHaveBeenCalledTimes( 1 );
} );
it( 'Returns true if all extension callbacks return true', () => {
const result = helpers.canMakePaymentWithExtensions(
() => true,
canMakePaymentExtensionsCallbacks,
'woopay'
)( canMakePaymentArgument );
expect( result ).toBe( true );
} );
it( 'Passes canPayArg to the callback', () => {
helpers.canMakePaymentWithExtensions(
() => true,
canMakePaymentExtensionsCallbacks,
'woopay'
)( canMakePaymentArgument );
expect( trueCallback ).toHaveBeenCalledWith(
canMakePaymentArgument
);
} );
it( 'Allows all valid callbacks to run, even if one causes an error', () => {
helpers.canMakePaymentWithExtensions(
() => true,
canMakePaymentExtensionsCallbacks,
'testpay'
)( canMakePaymentArgument );
expect( console ).toHaveErrored();
expect( throwsCallback ).toHaveBeenCalledTimes( 1 );
expect( trueCallback ).toHaveBeenCalledTimes( 1 );
} );
} );
} );

View File

@ -0,0 +1,61 @@
/**
* External dependencies
*/
import { registerPaymentMethodExtensionCallbacks } from '@woocommerce/blocks-registry';
import type { PaymentMethodConfigInstance } from '@woocommerce/type-defs/payments';
/**
* Internal dependencies
*/
import PaymentMethodConfig from '../payment-method-config';
import * as paymentMethodConfigHelpers from '../payment-method-config-helper';
describe( 'PaymentMethodConfig', () => {
let paymentMethod: PaymentMethodConfigInstance;
const extensionsCallbackSpy = jest.spyOn(
paymentMethodConfigHelpers,
'canMakePaymentWithExtensions'
);
beforeEach( () => {
paymentMethod = new PaymentMethodConfig( {
name: 'test-payment-method',
label: 'Test payment method',
ariaLabel: 'Test payment method',
content: <div>Test payment content</div>,
edit: <div>Test payment edit</div>,
canMakePayment: () => true,
supports: { features: [ 'products' ] },
} );
} );
it( 'Uses canMakePaymentWithExtensions as the canMakePayment function if an extension registers a callback', () => {
registerPaymentMethodExtensionCallbacks(
'woocommerce-marketplace-extension',
{
'unrelated-payment-method': () => true,
}
);
// At this point, since no extensions have registered a callback for
// test-payment-method we can expect the canMakePayment getter NOT
// to execute canMakePaymentWithExtensions.
// Disable no-unused-expressions because we just want to test the getter
// eslint-disable-next-line no-unused-expressions
paymentMethod.canMakePayment;
expect( extensionsCallbackSpy ).toHaveBeenCalledTimes( 0 );
registerPaymentMethodExtensionCallbacks(
'other-woocommerce-marketplace-extension',
{
'test-payment-method': () => true,
}
);
// Now, because an extension _has_ registered a callback for test-payment-method
// The getter will use canMakePaymentWithExtensions to create the
// canMakePayment function.
// Disable no-unused-expressions because we just want to test the getter
// eslint-disable-next-line no-unused-expressions
paymentMethod.canMakePayment;
expect( extensionsCallbackSpy ).toHaveBeenCalledTimes( 1 );
} );
} );

View File

@ -0,0 +1,97 @@
/**
* External dependencies
*/
import { registerPaymentMethodExtensionCallbacks } from '@woocommerce/blocks-registry';
/**
* Internal dependencies
*/
import { canMakePaymentExtensionsCallbacks } from '../extensions-config';
describe( 'registerPaymentMethodExtensionCallbacks', () => {
it( 'Logs an error to console if namespace is already registered', () => {
registerPaymentMethodExtensionCallbacks(
'woocommerce-marketplace-extension',
{
cod: () => false,
}
);
// eslint-disable-next-line no-console
expect( console ).not.toHaveErrored();
registerPaymentMethodExtensionCallbacks(
'woocommerce-marketplace-extension',
{
cod: () => false,
}
);
expect( console ).toHaveErrored();
// eslint-disable-next-line no-console
expect( console.error ).toHaveBeenCalledTimes( 1 );
} );
it( 'Does not overwrite a namespace if a second extensions tries to register with the same name', () => {
const firstCodCallback = jest.fn().mockReturnValue( false );
registerPaymentMethodExtensionCallbacks(
'overwrite-marketplace-extension',
{
cod: firstCodCallback,
}
);
// eslint-disable-next-line no-console
expect( console ).not.toHaveErrored();
registerPaymentMethodExtensionCallbacks(
'overwrite-marketplace-extension',
{
cod: () => false,
}
);
expect(
canMakePaymentExtensionsCallbacks[
'overwrite-marketplace-extension'
].cod
).toEqual( firstCodCallback );
} );
it( 'Logs an error if a supplied callback is not a function and does not register the callback for that method', () => {
registerPaymentMethodExtensionCallbacks(
'other-woocommerce-marketplace-extension',
{
cod: false,
cheque: () => true,
}
);
// eslint-disable-next-line no-console
expect( console ).toHaveErrored();
expect( canMakePaymentExtensionsCallbacks ).toHaveProperty(
'other-woocommerce-marketplace-extension'
);
expect(
canMakePaymentExtensionsCallbacks[
'other-woocommerce-marketplace-extension'
]
).not.toHaveProperty( 'cod' );
expect(
canMakePaymentExtensionsCallbacks[
'other-woocommerce-marketplace-extension'
]
).toHaveProperty( 'cheque' );
} );
it( 'Adds the namespace and callbacks to the canMakePaymentExtensionCallbacks object', () => {
// We are using a new namespace here because canMakePaymentExtensionsCallbacks cannot be reset between tests.
registerPaymentMethodExtensionCallbacks(
'third-woocommerce-marketplace-extension',
{
cod: () => false,
}
);
expect( canMakePaymentExtensionsCallbacks ).toHaveProperty(
'third-woocommerce-marketplace-extension'
);
} );
} );