From d77cb71ae51d176135a3ca87e239fa6d86281845 Mon Sep 17 00:00:00 2001 From: Christopher Rogers Date: Thu, 25 Jan 2024 15:01:47 -0800 Subject: [PATCH 1/2] Adds onReady callback to ThreeDSecureAction --- lib/three-d-secure-action.jsx | 12 ++++++++++++ test/three-d-secure-action.test.jsx | 28 ++++++++++++++++++++++++++++ types/index.d.ts | 1 + 3 files changed, 41 insertions(+) diff --git a/lib/three-d-secure-action.jsx b/lib/three-d-secure-action.jsx index de8c7ab..f9a8d06 100644 --- a/lib/three-d-secure-action.jsx +++ b/lib/three-d-secure-action.jsx @@ -20,6 +20,16 @@ export default class ThreeDSecureAction extends React.PureComponent { */ actionTokenId: PropTypes.string, + /** + * Called when the 3-D Secure flow is ready for interaction + * @type {ThreeDSecureAction~onReady} + */ + + /** + * @callback ThreeDSecureAction~onReady + */ + onReady: PropTypes.func, + /** * Called when the user has completed the 3D Secure flow * @type {ThreeDSecureAction~onToken} @@ -47,6 +57,7 @@ export default class ThreeDSecureAction extends React.PureComponent { id: undefined, className: undefined, actionTokenId: '', + onReady: () => {}, onToken: () => {}, onError: e => { throw e } }; @@ -65,6 +76,7 @@ export default class ThreeDSecureAction extends React.PureComponent { this._container = React.createRef(); this._risk = this.context.recurly.Risk(); this._threeDSecure = this._risk.ThreeDSecure({ actionTokenId }); + this._threeDSecure.on('ready', (...args) => this.props.onReady(...args)); this._threeDSecure.on('token', (...args) => this.props.onToken(...args)); this._threeDSecure.on('error', (...args) => this.props.onError(...args)); } diff --git a/test/three-d-secure-action.test.jsx b/test/three-d-secure-action.test.jsx index 60dc31e..60d8ee1 100644 --- a/test/three-d-secure-action.test.jsx +++ b/test/three-d-secure-action.test.jsx @@ -45,6 +45,34 @@ describe('', function () { describe('event handlers', function () { const example = { arbitrary: 'properties' }; + describe('[onReady]', function () { + it('is called when the underlying ThreeDSecure instance is ready', function () { + const subject = jest.fn(); + let fixture; + + render(withRecurlyProvider( + fixture = ref} + /> + )); + + getThreeDSecureInstanceFrom(fixture).emit('ready', ); + expect(subject).toHaveBeenCalled(); + }); + + it('does nothing when no handler is provided', function () { + let fixture; + + render(withRecurlyProvider( + fixture = ref} /> + )); + + expect(() => getThreeDSecureInstanceFrom(fixture).emit('ready', example)).not.toThrow(); + }); + }); + describe('[onToken]', function () { it('is called when the underlying ThreeDSecure instance receives a token', function () { const subject = jest.fn(); diff --git a/types/index.d.ts b/types/index.d.ts index e46a1a3..e0407c5 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -117,6 +117,7 @@ export type ThreeDSecureActionProps = { id?: string; className?: string; actionTokenId?: string; + onReady?: () => void; onToken?: (token: TokenPayload) => void; onError?: (e: RecurlyError) => void; }; From 054cd52e0da57e63ef07516d43973298ba1ded8c Mon Sep 17 00:00:00 2001 From: Christopher Rogers Date: Mon, 29 Jan 2024 12:49:01 -0800 Subject: [PATCH 2/2] Ensures singular attachment for 3DS container --- lib/three-d-secure-action.jsx | 3 +++ test/three-d-secure-action.test.jsx | 17 +++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/three-d-secure-action.jsx b/lib/three-d-secure-action.jsx index f9a8d06..37bfc40 100644 --- a/lib/three-d-secure-action.jsx +++ b/lib/three-d-secure-action.jsx @@ -73,6 +73,7 @@ export default class ThreeDSecureAction extends React.PureComponent { const { actionTokenId } = props; + this._attached = false; this._container = React.createRef(); this._risk = this.context.recurly.Risk(); this._threeDSecure = this._risk.ThreeDSecure({ actionTokenId }); @@ -82,7 +83,9 @@ export default class ThreeDSecureAction extends React.PureComponent { } componentDidMount () { + if (this._attached) return; this._threeDSecure.attach(this._container.current); + this._attached = true; } render () { diff --git a/test/three-d-secure-action.test.jsx b/test/three-d-secure-action.test.jsx index 60d8ee1..d4d0928 100644 --- a/test/three-d-secure-action.test.jsx +++ b/test/three-d-secure-action.test.jsx @@ -58,18 +58,15 @@ describe('', function () { /> )); - getThreeDSecureInstanceFrom(fixture).emit('ready', ); - expect(subject).toHaveBeenCalled(); - }); + const instance = getThreeDSecureInstanceFrom(fixture); - it('does nothing when no handler is provided', function () { - let fixture; + // stub strategy so that the readiness chain will not fall apart + instance.strategy = { + attach: () => {} + }; - render(withRecurlyProvider( - fixture = ref} /> - )); - - expect(() => getThreeDSecureInstanceFrom(fixture).emit('ready', example)).not.toThrow(); + instance.emit('ready'); + expect(subject).toHaveBeenCalled(); }); });