From d53d2912c27f11345de9e5aa735e40f56e8d2cba Mon Sep 17 00:00:00 2001 From: alexkoumarianos-okta Date: Fri, 8 Dec 2023 10:25:05 -0500 Subject: [PATCH 01/12] added support for auth0 v2 captcha provider --- css/index.styl | 12 ++++++++++ src/__tests__/field/captcha.test.jsx | 22 +++++++++++++++++++ .../__snapshots__/auth0_v2.test.jsx.snap | 11 ++++++++++ src/__tests__/field/captcha/auth0_v2.test.jsx | 20 +++++++++++++++++ src/connection/captcha.js | 1 + src/connection/passwordless/actions.js | 1 + src/core/index.js | 1 + src/field/captcha/third_party_captcha.jsx | 21 ++++++++++++++---- 8 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 src/__tests__/field/captcha/__snapshots__/auth0_v2.test.jsx.snap create mode 100644 src/__tests__/field/captcha/auth0_v2.test.jsx diff --git a/css/index.styl b/css/index.styl index 07eb0902b..929ae183e 100644 --- a/css/index.styl +++ b/css/index.styl @@ -1414,6 +1414,18 @@ loadingSize = 30px transform-origin: 0px 0px; position: relative; +.auth0-lock-auth0-v2-block + border-radius: 4px; + height: 65px; + + &.auth0-lock-auth0-v2-block-error + border: 1px solid red; + + .auth0-lock-auth0-v2 + transform: scale(0.855); + transform-origin: 0px 0px; + position: relative; + .auth0-lock-friendly-captcha-block border-radius: 4px; border: 1px solid #eee; diff --git a/src/__tests__/field/captcha.test.jsx b/src/__tests__/field/captcha.test.jsx index d94a92f1e..274ffad14 100644 --- a/src/__tests__/field/captcha.test.jsx +++ b/src/__tests__/field/captcha.test.jsx @@ -100,6 +100,28 @@ describe('CaptchaPane', () => { }); }); + describe('auth0_v2', () => { + let wrapper; + beforeAll(() => { + const lockMock = createLockMock({ + provider: 'auth0_v2', + siteKey: 'mySiteKey' + }); + const i8nMock = createI18nMock(); + const onReloadMock = jest.fn(); + + wrapper = mount(); + }); + + it('should render ThirdPartyCaptcha if provider is auth0_v2', () => { + expect(wrapper.find(ThirdPartyCaptcha)).toHaveLength(1); + }); + + it('should pass the sitekey', () => { + expect(wrapper.find(ThirdPartyCaptcha).props().sitekey).toBe('mySiteKey'); + }); + }); + describe('recaptcha enterprise', () => { let wrapper; beforeAll(() => { diff --git a/src/__tests__/field/captcha/__snapshots__/auth0_v2.test.jsx.snap b/src/__tests__/field/captcha/__snapshots__/auth0_v2.test.jsx.snap new file mode 100644 index 000000000..71c7e969c --- /dev/null +++ b/src/__tests__/field/captcha/__snapshots__/auth0_v2.test.jsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Auth0 V2 should match the snapshot 1`] = ` +
+
+
+`; diff --git a/src/__tests__/field/captcha/auth0_v2.test.jsx b/src/__tests__/field/captcha/auth0_v2.test.jsx new file mode 100644 index 000000000..98f7a51d5 --- /dev/null +++ b/src/__tests__/field/captcha/auth0_v2.test.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { expectComponent } from 'testUtils'; +import { ThirdPartyCaptcha } from '../../../field/captcha/third_party_captcha'; + +describe('Auth0 V2', () => { + const component = ; + + it('should match the snapshot', () => { + expectComponent(component).toMatchSnapshot(); + }); + + it('injects the script', () => { + const script = [...window.document.querySelectorAll('script')].find(s => + s.src.startsWith( + 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit&onload=' + ) + ); + expect(script).not.toBeUndefined(); + }); +}); diff --git a/src/connection/captcha.js b/src/connection/captcha.js index 8f26a8b86..f166ceb1a 100644 --- a/src/connection/captcha.js +++ b/src/connection/captcha.js @@ -18,6 +18,7 @@ export function showMissingCaptcha(m, id, isPasswordless = false) { captchaConfig.get('provider') === 'recaptcha_v2' || captchaConfig.get('provider') === 'recaptcha_enterprise' || captchaConfig.get('provider') === 'hcaptcha' || + captchaConfig.get('provider') === 'auth0_v2' || captchaConfig.get('provider') === 'friendly_captcha' ) ? 'invalid_recaptcha' : 'invalid_captcha'; diff --git a/src/connection/passwordless/actions.js b/src/connection/passwordless/actions.js index 0d617e741..9c25598ed 100644 --- a/src/connection/passwordless/actions.js +++ b/src/connection/passwordless/actions.js @@ -34,6 +34,7 @@ function getErrorMessage(m, id, error) { captchaConfig.get('provider') === 'recaptcha_v2' || captchaConfig.get('provider') === 'recaptcha_enterprise' || captchaConfig.get('provider') === 'hcaptcha' || + captchaConfig.get('provider') === 'auth0_v2' || captchaConfig.get('provider') === 'friendly_captcha' ) ? 'invalid_recaptcha' : 'invalid_captcha'; } diff --git a/src/core/index.js b/src/core/index.js index 1b38a6477..03c0f5164 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -584,6 +584,7 @@ export function loginErrorMessage(m, error, type) { currentCaptcha.get('provider') === 'recaptcha_v2' || currentCaptcha.get('provider') === 'recaptcha_enterprise' || currentCaptcha.get('provider') === 'hcaptcha' || + currentCaptcha.get('provider') === 'auth0_v2' || captchaConfig.get('provider') === 'friendly_captcha' )) { code = 'invalid_recaptcha'; diff --git a/src/field/captcha/third_party_captcha.jsx b/src/field/captcha/third_party_captcha.jsx index d0874e7dd..835e6e07b 100644 --- a/src/field/captcha/third_party_captcha.jsx +++ b/src/field/captcha/third_party_captcha.jsx @@ -9,6 +9,7 @@ const RECAPTCHA_ENTERPRISE_PROVIDER = 'recaptcha_enterprise'; const HCAPTCHA_PROVIDER = 'hcaptcha'; const FRIENDLY_CAPTCHA_PROVIDER = 'friendly_captcha'; const ARKOSE_PROVIDER = 'arkose'; +const AUTH0_V2_CAPTCHA_PROVIDER = 'auth0_v2'; const TIMEOUT_MS = 500; const MAX_RETRY = 3; @@ -17,7 +18,8 @@ export const isThirdPartyCaptcha = provider => || provider === RECAPTCHA_V2_PROVIDER || provider === HCAPTCHA_PROVIDER || provider === FRIENDLY_CAPTCHA_PROVIDER - || provider === ARKOSE_PROVIDER; + || provider === ARKOSE_PROVIDER + || provider === AUTH0_V2_CAPTCHA_PROVIDER; const getCaptchaProvider = provider => { switch (provider) { @@ -31,6 +33,8 @@ const getCaptchaProvider = provider => { return window.friendlyChallenge; case ARKOSE_PROVIDER: return window.arkose; + case AUTH0_V2_CAPTCHA_PROVIDER: + return window.turnstile; } }; @@ -46,6 +50,8 @@ const scriptForProvider = (provider, lang, callback, clientSubdomain, siteKey) = return 'https://cdn.jsdelivr.net/npm/friendly-challenge@0.9.12/widget.min.js'; case ARKOSE_PROVIDER: return 'https://' + clientSubdomain + '.arkoselabs.com/v2/' + siteKey + '/api.js'; + case AUTH0_V2_CAPTCHA_PROVIDER: + return `https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit&onload=${callback}`; } }; @@ -61,6 +67,8 @@ const providerDomPrefix = (provider) => { return 'friendly-captcha'; case ARKOSE_PROVIDER: return 'arkose'; + case AUTH0_V2_CAPTCHA_PROVIDER: + return 'auth0-v2'; } }; @@ -199,13 +207,18 @@ export class ThirdPartyCaptcha extends React.Component { errorCallback: this.erroredHandler, }); } else { - // if this is enterprise then we change this to window.grecaptcha.enterprise.render - this.widgetId = provider.render(this.ref.current, { + const renderParams = { callback: this.changeHandler, 'expired-callback': this.expiredHandler, 'error-callback': this.erroredHandler, sitekey: this.props.sitekey - }); + } + if (this.props.provider === AUTH0_V2_CAPTCHA_PROVIDER) { + renderParams.language = this.props.hl; + renderParams.theme = 'light'; + } + // if this is enterprise then we change this to window.grecaptcha.enterprise.render + this.widgetId = provider.render(this.ref.current, renderParams); } }); } From fa3553fea998d69f4c9ba98c1ccea7fc17b13787 Mon Sep 17 00:00:00 2001 From: alexkoumarianos-okta Date: Fri, 8 Dec 2023 13:32:38 -0500 Subject: [PATCH 02/12] fix test coverage --- src/__tests__/field/captcha.test.jsx | 34 ++++++++++++++++++++++- src/field/captcha/third_party_captcha.jsx | 25 ++++++++++------- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/src/__tests__/field/captcha.test.jsx b/src/__tests__/field/captcha.test.jsx index 274ffad14..90cd66210 100644 --- a/src/__tests__/field/captcha.test.jsx +++ b/src/__tests__/field/captcha.test.jsx @@ -3,7 +3,7 @@ import { mount } from 'enzyme'; import I from 'immutable'; import CaptchaPane from '../../field/captcha/captcha_pane'; -import { ThirdPartyCaptcha } from '../../field/captcha/third_party_captcha'; +import { ThirdPartyCaptcha, getRenderParams } from '../../field/captcha/third_party_captcha'; import CaptchaInput from '../../ui/input/captcha_input'; const createLockMock = ({ provider = 'auth0', required = true, siteKey = '', clientSubdomain = '' } = {}) => @@ -120,6 +120,38 @@ describe('CaptchaPane', () => { it('should pass the sitekey', () => { expect(wrapper.find(ThirdPartyCaptcha).props().sitekey).toBe('mySiteKey'); }); + + it('renderParams auth0_v2', () => { + const renderParams = getRenderParams({ + props: { provider: 'auth0_v2', hl: 'en', sitekey: 'mySiteKey' }, + changeHandler: () => {}, + expiredHandler: () => {}, + erroredHandler: () => {} + }); + expect(renderParams).toMatchObject({ + sitekey: 'mySiteKey', + language: 'en', + callback: expect.any(Function), + 'expired-callback': expect.any(Function), + 'error-callback': expect.any(Function), + theme: 'light' + }); + }); + + it('renderParams', () => { + const renderParams = getRenderParams({ + props: { provider: 'not_auth0_v2', hl: 'en', sitekey: 'mySiteKey' }, + changeHandler: () => {}, + expiredHandler: () => {}, + erroredHandler: () => {} + }); + expect(renderParams).toMatchObject({ + sitekey: 'mySiteKey', + callback: expect.any(Function), + 'expired-callback': expect.any(Function), + 'error-callback': expect.any(Function) + }); + }); }); describe('recaptcha enterprise', () => { diff --git a/src/field/captcha/third_party_captcha.jsx b/src/field/captcha/third_party_captcha.jsx index 835e6e07b..c4970f1de 100644 --- a/src/field/captcha/third_party_captcha.jsx +++ b/src/field/captcha/third_party_captcha.jsx @@ -38,6 +38,20 @@ const getCaptchaProvider = provider => { } }; +export const getRenderParams = (self) => { + const renderParams = { + callback: self.changeHandler, + 'expired-callback': self.expiredHandler, + 'error-callback': self.erroredHandler, + sitekey: self.props.sitekey + } + if (self.props.provider === AUTH0_V2_CAPTCHA_PROVIDER) { + renderParams.language = self.props.hl; + renderParams.theme = 'light'; + } + return renderParams; +}; + const scriptForProvider = (provider, lang, callback, clientSubdomain, siteKey) => { switch (provider) { case RECAPTCHA_V2_PROVIDER: @@ -207,16 +221,7 @@ export class ThirdPartyCaptcha extends React.Component { errorCallback: this.erroredHandler, }); } else { - const renderParams = { - callback: this.changeHandler, - 'expired-callback': this.expiredHandler, - 'error-callback': this.erroredHandler, - sitekey: this.props.sitekey - } - if (this.props.provider === AUTH0_V2_CAPTCHA_PROVIDER) { - renderParams.language = this.props.hl; - renderParams.theme = 'light'; - } + const renderParams = getRenderParams(this); // if this is enterprise then we change this to window.grecaptcha.enterprise.render this.widgetId = provider.render(this.ref.current, renderParams); } From ab926cedbb911bd7540790a84d10129ed1d49ce2 Mon Sep 17 00:00:00 2001 From: alexkoumarianos-okta Date: Fri, 8 Dec 2023 13:36:16 -0500 Subject: [PATCH 03/12] fix test name --- src/__tests__/field/captcha.test.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__tests__/field/captcha.test.jsx b/src/__tests__/field/captcha.test.jsx index 90cd66210..3e2732d4c 100644 --- a/src/__tests__/field/captcha.test.jsx +++ b/src/__tests__/field/captcha.test.jsx @@ -138,7 +138,7 @@ describe('CaptchaPane', () => { }); }); - it('renderParams', () => { + it('renderParams not auth0_v2', () => { const renderParams = getRenderParams({ props: { provider: 'not_auth0_v2', hl: 'en', sitekey: 'mySiteKey' }, changeHandler: () => {}, From 9229d7df5ef1ad0db264a147a929746a4ac3c9e2 Mon Sep 17 00:00:00 2001 From: alexkoumarianos-okta Date: Mon, 11 Dec 2023 08:36:25 -0500 Subject: [PATCH 04/12] changed params from this to individual values --- src/__tests__/field/captcha.test.jsx | 28 +++++++++++++++++------ src/field/captcha/third_party_captcha.jsx | 25 ++++++++++++-------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/__tests__/field/captcha.test.jsx b/src/__tests__/field/captcha.test.jsx index 3e2732d4c..e2ac6fbce 100644 --- a/src/__tests__/field/captcha.test.jsx +++ b/src/__tests__/field/captcha.test.jsx @@ -122,29 +122,43 @@ describe('CaptchaPane', () => { }); it('renderParams auth0_v2', () => { - const renderParams = getRenderParams({ + const mockThis = { props: { provider: 'auth0_v2', hl: 'en', sitekey: 'mySiteKey' }, changeHandler: () => {}, expiredHandler: () => {}, erroredHandler: () => {} - }); + }; + const renderParams = getRenderParams( + mockThis.props.provider, + mockThis.props.sitekey, + mockThis.props.hl, + mockThis.changeHandler, + mockThis.expiredHandler, + mockThis.erroredHandler + ); expect(renderParams).toMatchObject({ sitekey: 'mySiteKey', - language: 'en', callback: expect.any(Function), 'expired-callback': expect.any(Function), - 'error-callback': expect.any(Function), - theme: 'light' + 'error-callback': expect.any(Function) }); }); it('renderParams not auth0_v2', () => { - const renderParams = getRenderParams({ + const mockThis = { props: { provider: 'not_auth0_v2', hl: 'en', sitekey: 'mySiteKey' }, changeHandler: () => {}, expiredHandler: () => {}, erroredHandler: () => {} - }); + }; + const renderParams = getRenderParams( + mockThis.props.provider, + mockThis.props.sitekey, + mockThis.props.hl, + mockThis.changeHandler, + mockThis.expiredHandler, + mockThis.erroredHandler + ); expect(renderParams).toMatchObject({ sitekey: 'mySiteKey', callback: expect.any(Function), diff --git a/src/field/captcha/third_party_captcha.jsx b/src/field/captcha/third_party_captcha.jsx index c4970f1de..6da27df1a 100644 --- a/src/field/captcha/third_party_captcha.jsx +++ b/src/field/captcha/third_party_captcha.jsx @@ -38,15 +38,15 @@ const getCaptchaProvider = provider => { } }; -export const getRenderParams = (self) => { +export const getRenderParams = (provider, sitekey, hl, changeHandler, expiredHandler, erroredHandler) => { const renderParams = { - callback: self.changeHandler, - 'expired-callback': self.expiredHandler, - 'error-callback': self.erroredHandler, - sitekey: self.props.sitekey - } - if (self.props.provider === AUTH0_V2_CAPTCHA_PROVIDER) { - renderParams.language = self.props.hl; + callback: changeHandler, + 'expired-callback': expiredHandler, + 'error-callback': erroredHandler, + sitekey: sitekey + }; + if (provider === AUTH0_V2_CAPTCHA_PROVIDER) { + renderParams.language = hl; renderParams.theme = 'light'; } return renderParams; @@ -221,7 +221,14 @@ export class ThirdPartyCaptcha extends React.Component { errorCallback: this.erroredHandler, }); } else { - const renderParams = getRenderParams(this); + const renderParams = getRenderParams( + this.props.provider, + this.props.sitekey, + this.props.hl, + this.changeHandler, + this.expiredHandler, + this.erroredHandler + ); // if this is enterprise then we change this to window.grecaptcha.enterprise.render this.widgetId = provider.render(this.ref.current, renderParams); } From 49a9ce1e318bdf519c634a4b0181cb80c528e116 Mon Sep 17 00:00:00 2001 From: alexkoumarianos-okta Date: Tue, 12 Dec 2023 09:12:21 -0500 Subject: [PATCH 05/12] fixed tests to properly mock --- src/__tests__/field/captcha.test.jsx | 48 +--- .../captcha/third_party_captcha.test.jsx | 238 ++++++++++++++++++ src/field/captcha/third_party_captcha.jsx | 79 +++--- 3 files changed, 288 insertions(+), 77 deletions(-) create mode 100644 src/__tests__/field/captcha/third_party_captcha.test.jsx diff --git a/src/__tests__/field/captcha.test.jsx b/src/__tests__/field/captcha.test.jsx index e2ac6fbce..274ffad14 100644 --- a/src/__tests__/field/captcha.test.jsx +++ b/src/__tests__/field/captcha.test.jsx @@ -3,7 +3,7 @@ import { mount } from 'enzyme'; import I from 'immutable'; import CaptchaPane from '../../field/captcha/captcha_pane'; -import { ThirdPartyCaptcha, getRenderParams } from '../../field/captcha/third_party_captcha'; +import { ThirdPartyCaptcha } from '../../field/captcha/third_party_captcha'; import CaptchaInput from '../../ui/input/captcha_input'; const createLockMock = ({ provider = 'auth0', required = true, siteKey = '', clientSubdomain = '' } = {}) => @@ -120,52 +120,6 @@ describe('CaptchaPane', () => { it('should pass the sitekey', () => { expect(wrapper.find(ThirdPartyCaptcha).props().sitekey).toBe('mySiteKey'); }); - - it('renderParams auth0_v2', () => { - const mockThis = { - props: { provider: 'auth0_v2', hl: 'en', sitekey: 'mySiteKey' }, - changeHandler: () => {}, - expiredHandler: () => {}, - erroredHandler: () => {} - }; - const renderParams = getRenderParams( - mockThis.props.provider, - mockThis.props.sitekey, - mockThis.props.hl, - mockThis.changeHandler, - mockThis.expiredHandler, - mockThis.erroredHandler - ); - expect(renderParams).toMatchObject({ - sitekey: 'mySiteKey', - callback: expect.any(Function), - 'expired-callback': expect.any(Function), - 'error-callback': expect.any(Function) - }); - }); - - it('renderParams not auth0_v2', () => { - const mockThis = { - props: { provider: 'not_auth0_v2', hl: 'en', sitekey: 'mySiteKey' }, - changeHandler: () => {}, - expiredHandler: () => {}, - erroredHandler: () => {} - }; - const renderParams = getRenderParams( - mockThis.props.provider, - mockThis.props.sitekey, - mockThis.props.hl, - mockThis.changeHandler, - mockThis.expiredHandler, - mockThis.erroredHandler - ); - expect(renderParams).toMatchObject({ - sitekey: 'mySiteKey', - callback: expect.any(Function), - 'expired-callback': expect.any(Function), - 'error-callback': expect.any(Function) - }); - }); }); describe('recaptcha enterprise', () => { diff --git a/src/__tests__/field/captcha/third_party_captcha.test.jsx b/src/__tests__/field/captcha/third_party_captcha.test.jsx new file mode 100644 index 000000000..e5a7afcb4 --- /dev/null +++ b/src/__tests__/field/captcha/third_party_captcha.test.jsx @@ -0,0 +1,238 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import I from 'immutable'; +import * as l from '../../../core/index'; +import { ThirdPartyCaptcha } from '../../../field/captcha/third_party_captcha'; + +const createLockMock = ({ + provider = 'auth0', + required = true, + siteKey = '', + clientSubdomain = '' +} = {}) => + I.fromJS({ + id: '__lock-id__', + core: { + captcha: { provider, siteKey, clientSubdomain, required: required } + } + }); + +describe('ThirdPartyCaptcha', () => { + describe('recaptchav2', () => { + let shallowWrapper; + let getRenderParamsSpy; + beforeAll(() => { + const lockMock = createLockMock({ + provider: 'recaptcha_v2', + siteKey: 'mySiteKey' + }); + + const captcha = l.captcha(lockMock); + shallowWrapper = shallow( + + ).instance(); + getRenderParamsSpy = jest.spyOn(shallowWrapper, 'getRenderParams'); + shallowWrapper.componentDidMount(); + }); + + it('should pass the sitekey', () => { + const renderParams = getRenderParamsSpy.mock.results[0].value; + expect(renderParams).toMatchObject({ + sitekey: 'mySiteKey', + callback: expect.any(Function), + 'expired-callback': expect.any(Function), + 'error-callback': expect.any(Function) + }); + expect(renderParams.language).toBeUndefined(); + expect(renderParams.theme).toBeUndefined(); + }); + }); + + describe('friendly captcha', () => { + let shallowWrapper; + let getRenderParamsSpy; + beforeAll(() => { + const lockMock = createLockMock({ + provider: 'friendly_captcha', + siteKey: 'mySiteKey' + }); + + const captcha = l.captcha(lockMock); + shallowWrapper = shallow( + + ).instance(); + getRenderParamsSpy = jest.spyOn(shallowWrapper, 'getRenderParams'); + shallowWrapper.componentDidMount(); + }); + + it('should pass the sitekey', () => { + const renderParams = getRenderParamsSpy.mock.results[0].value; + expect(renderParams).toMatchObject({ + sitekey: 'mySiteKey', + doneCallback: expect.any(Function), + errorCallback: expect.any(Function), + language: 'en' + }); + expect(renderParams.theme).toBeUndefined(); + expect(renderParams.callback).toBeUndefined(); + expect(renderParams['expired-callback']).toBeUndefined(); + expect(renderParams['error-callback']).toBeUndefined(); + }); + }); + + describe('hcaptcha', () => { + let shallowWrapper; + let getRenderParamsSpy; + beforeAll(() => { + const lockMock = createLockMock({ + provider: 'hcaptcha', + siteKey: 'mySiteKey' + }); + + const captcha = l.captcha(lockMock); + shallowWrapper = shallow( + + ).instance(); + getRenderParamsSpy = jest.spyOn(shallowWrapper, 'getRenderParams'); + shallowWrapper.componentDidMount(); + }); + + it('should pass the sitekey', () => { + const renderParams = getRenderParamsSpy.mock.results[0].value; + expect(renderParams).toMatchObject({ + sitekey: 'mySiteKey', + callback: expect.any(Function), + 'expired-callback': expect.any(Function), + 'error-callback': expect.any(Function) + }); + expect(renderParams.language).toBeUndefined(); + expect(renderParams.theme).toBeUndefined(); + }); + }); + + describe('auth0_v2', () => { + let shallowWrapper; + let getRenderParamsSpy; + beforeAll(() => { + const lockMock = createLockMock({ + provider: 'auth0_v2', + siteKey: 'mySiteKey' + }); + + const captcha = l.captcha(lockMock); + shallowWrapper = shallow( + + ).instance(); + getRenderParamsSpy = jest.spyOn(shallowWrapper, 'getRenderParams'); + shallowWrapper.componentDidMount(); + }); + + it('should pass the sitekey', () => { + const renderParams = getRenderParamsSpy.mock.results[0].value; + expect(renderParams).toMatchObject({ + sitekey: 'mySiteKey', + callback: expect.any(Function), + 'expired-callback': expect.any(Function), + 'error-callback': expect.any(Function), + language: 'en', + theme: 'light' + }); + }); + }); + + describe('recaptcha enterprise', () => { + let shallowWrapper; + let getRenderParamsSpy; + beforeAll(() => { + const lockMock = createLockMock({ + provider: 'recaptcha_enterprise', + siteKey: 'mySiteKey' + }); + + const captcha = l.captcha(lockMock); + shallowWrapper = shallow( + + ).instance(); + getRenderParamsSpy = jest.spyOn(shallowWrapper, 'getRenderParams'); + shallowWrapper.componentDidMount(); + }); + + it('should pass the sitekey', () => { + const renderParams = getRenderParamsSpy.mock.results[0].value; + expect(renderParams).toMatchObject({ + sitekey: 'mySiteKey', + callback: expect.any(Function), + 'expired-callback': expect.any(Function), + 'error-callback': expect.any(Function) + }); + expect(renderParams.language).toBeUndefined(); + expect(renderParams.theme).toBeUndefined(); + }); + }); + + describe('Arkose', () => { + let shallowWrapper; + let getRenderParamsSpy; + beforeAll(() => { + const lockMock = createLockMock({ + provider: 'arkose', + siteKey: 'mySiteKey', + clientSubdomain: 'client-api' + }); + + const captcha = l.captcha(lockMock); + shallowWrapper = shallow( + + ).instance(); + getRenderParamsSpy = jest.spyOn(shallowWrapper, 'getRenderParams'); + shallowWrapper.componentDidMount(); + }); + + it('should pass the sitekey', () => { + const renderParams = getRenderParamsSpy.mock.results[0].value; + expect(renderParams).toEqual({}); + }); + }); +}); diff --git a/src/field/captcha/third_party_captcha.jsx b/src/field/captcha/third_party_captcha.jsx index 6da27df1a..99b1777ee 100644 --- a/src/field/captcha/third_party_captcha.jsx +++ b/src/field/captcha/third_party_captcha.jsx @@ -38,20 +38,6 @@ const getCaptchaProvider = provider => { } }; -export const getRenderParams = (provider, sitekey, hl, changeHandler, expiredHandler, erroredHandler) => { - const renderParams = { - callback: changeHandler, - 'expired-callback': expiredHandler, - 'error-callback': erroredHandler, - sitekey: sitekey - }; - if (provider === AUTH0_V2_CAPTCHA_PROVIDER) { - renderParams.language = hl; - renderParams.theme = 'light'; - } - return renderParams; -}; - const scriptForProvider = (provider, lang, callback, clientSubdomain, siteKey) => { switch (provider) { case RECAPTCHA_V2_PROVIDER: @@ -137,9 +123,41 @@ export class ThirdPartyCaptcha extends React.Component { this.props.onChange(value); this.props.onErrored(); }); - }; + }; } + getRenderParams() { + if (this.props.provider === ARKOSE_PROVIDER) { + return {}; + } + + if(this.props.provider === FRIENDLY_CAPTCHA_PROVIDER) { + return { + sitekey: this.props.sitekey, + language: this.props.hl, + doneCallback: this.changeHandler, + errorCallback: this.erroredHandler, + } + } + + let renderParams = { + sitekey: this.props.sitekey, + callback: this.changeHandler, + 'expired-callback': this.expiredHandler, + 'error-callback': this.erroredHandler, + } + + if (this.props.provider === AUTH0_V2_CAPTCHA_PROVIDER) { + renderParams = { + ...renderParams, + language: this.props.hl, + theme: 'light' + } + } + + return renderParams; + } + injectCaptchaScript(callback = noop) { const { provider, hl, clientSubdomain, sitekey } = this.props; const callbackName = `${providerDomPrefix(provider)}Callback_${Math.floor(Math.random() * 1000001)}`; @@ -188,6 +206,8 @@ export class ThirdPartyCaptcha extends React.Component { } componentDidMount() { + // grab the render params outside of the callback just to spy on it in the test + const renderParams = this.getRenderParams(); this.injectCaptchaScript((arkose) => { const provider = getCaptchaProvider(this.props.provider); if (this.props.provider === ARKOSE_PROVIDER) { @@ -214,21 +234,8 @@ export class ThirdPartyCaptcha extends React.Component { } }); } else if (this.props.provider === FRIENDLY_CAPTCHA_PROVIDER) { - this.widgetInstance = new provider.WidgetInstance(this.ref.current, { - sitekey: this.props.sitekey, - language: this.props.hl, - doneCallback: this.changeHandler, - errorCallback: this.erroredHandler, - }); + this.widgetInstance = new provider.WidgetInstance(this.ref.current, renderParams); } else { - const renderParams = getRenderParams( - this.props.provider, - this.props.sitekey, - this.props.hl, - this.changeHandler, - this.expiredHandler, - this.erroredHandler - ); // if this is enterprise then we change this to window.grecaptcha.enterprise.render this.widgetId = provider.render(this.ref.current, renderParams); } @@ -321,7 +328,19 @@ ThirdPartyCaptcha.propTypes = { value: propTypes.string, isValid: propTypes.bool }; - +ThirdPartyCaptcha.getRenderParams = () =>{ + const renderParams = { + callback: this.changeHandler, + 'expired-callback': this.expiredHandler, + 'error-callback': this.erroredHandler, + sitekey: this.props.sitekey + }; + if (this.props.provider === AUTH0_V2_CAPTCHA_PROVIDER) { + renderParams.language = this.props.hl; + renderParams.theme = 'light'; + } + return renderParams; +} ThirdPartyCaptcha.defaultProps = { onChange: noop, onExpired: noop, From 92766f464195643fe00947d279d6d55fc8109d65 Mon Sep 17 00:00:00 2001 From: alexkoumarianos-okta Date: Tue, 12 Dec 2023 09:17:02 -0500 Subject: [PATCH 06/12] clean up --- src/field/captcha/third_party_captcha.jsx | 26 ++++++----------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/field/captcha/third_party_captcha.jsx b/src/field/captcha/third_party_captcha.jsx index 99b1777ee..2268d0a8f 100644 --- a/src/field/captcha/third_party_captcha.jsx +++ b/src/field/captcha/third_party_captcha.jsx @@ -131,28 +131,28 @@ export class ThirdPartyCaptcha extends React.Component { return {}; } - if(this.props.provider === FRIENDLY_CAPTCHA_PROVIDER) { + if (this.props.provider === FRIENDLY_CAPTCHA_PROVIDER) { return { sitekey: this.props.sitekey, language: this.props.hl, doneCallback: this.changeHandler, - errorCallback: this.erroredHandler, - } + errorCallback: this.erroredHandler + }; } let renderParams = { sitekey: this.props.sitekey, callback: this.changeHandler, 'expired-callback': this.expiredHandler, - 'error-callback': this.erroredHandler, - } + 'error-callback': this.erroredHandler + }; if (this.props.provider === AUTH0_V2_CAPTCHA_PROVIDER) { renderParams = { ...renderParams, language: this.props.hl, theme: 'light' - } + }; } return renderParams; @@ -328,19 +328,7 @@ ThirdPartyCaptcha.propTypes = { value: propTypes.string, isValid: propTypes.bool }; -ThirdPartyCaptcha.getRenderParams = () =>{ - const renderParams = { - callback: this.changeHandler, - 'expired-callback': this.expiredHandler, - 'error-callback': this.erroredHandler, - sitekey: this.props.sitekey - }; - if (this.props.provider === AUTH0_V2_CAPTCHA_PROVIDER) { - renderParams.language = this.props.hl; - renderParams.theme = 'light'; - } - return renderParams; -} + ThirdPartyCaptcha.defaultProps = { onChange: noop, onExpired: noop, From 32be0bb499603541343a0fb18ece7fa0a3cc6575 Mon Sep 17 00:00:00 2001 From: alexkoumarianos-okta Date: Tue, 12 Dec 2023 09:29:22 -0500 Subject: [PATCH 07/12] make renderParams an extensible object --- .../captcha/third_party_captcha.test.jsx | 29 +++++++------------ src/field/captcha/third_party_captcha.jsx | 27 ++++++++--------- 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/src/__tests__/field/captcha/third_party_captcha.test.jsx b/src/__tests__/field/captcha/third_party_captcha.test.jsx index e5a7afcb4..80b85b087 100644 --- a/src/__tests__/field/captcha/third_party_captcha.test.jsx +++ b/src/__tests__/field/captcha/third_party_captcha.test.jsx @@ -20,7 +20,6 @@ const createLockMock = ({ describe('ThirdPartyCaptcha', () => { describe('recaptchav2', () => { let shallowWrapper; - let getRenderParamsSpy; beforeAll(() => { const lockMock = createLockMock({ provider: 'recaptcha_v2', @@ -38,12 +37,11 @@ describe('ThirdPartyCaptcha', () => { value={undefined} /> ).instance(); - getRenderParamsSpy = jest.spyOn(shallowWrapper, 'getRenderParams'); shallowWrapper.componentDidMount(); }); it('should pass the sitekey', () => { - const renderParams = getRenderParamsSpy.mock.results[0].value; + const { renderParams } = shallowWrapper; expect(renderParams).toMatchObject({ sitekey: 'mySiteKey', callback: expect.any(Function), @@ -57,7 +55,6 @@ describe('ThirdPartyCaptcha', () => { describe('friendly captcha', () => { let shallowWrapper; - let getRenderParamsSpy; beforeAll(() => { const lockMock = createLockMock({ provider: 'friendly_captcha', @@ -75,12 +72,11 @@ describe('ThirdPartyCaptcha', () => { value={undefined} /> ).instance(); - getRenderParamsSpy = jest.spyOn(shallowWrapper, 'getRenderParams'); shallowWrapper.componentDidMount(); }); it('should pass the sitekey', () => { - const renderParams = getRenderParamsSpy.mock.results[0].value; + const { renderParams } = shallowWrapper; expect(renderParams).toMatchObject({ sitekey: 'mySiteKey', doneCallback: expect.any(Function), @@ -96,7 +92,6 @@ describe('ThirdPartyCaptcha', () => { describe('hcaptcha', () => { let shallowWrapper; - let getRenderParamsSpy; beforeAll(() => { const lockMock = createLockMock({ provider: 'hcaptcha', @@ -114,12 +109,11 @@ describe('ThirdPartyCaptcha', () => { value={undefined} /> ).instance(); - getRenderParamsSpy = jest.spyOn(shallowWrapper, 'getRenderParams'); shallowWrapper.componentDidMount(); }); it('should pass the sitekey', () => { - const renderParams = getRenderParamsSpy.mock.results[0].value; + const { renderParams } = shallowWrapper; expect(renderParams).toMatchObject({ sitekey: 'mySiteKey', callback: expect.any(Function), @@ -133,7 +127,6 @@ describe('ThirdPartyCaptcha', () => { describe('auth0_v2', () => { let shallowWrapper; - let getRenderParamsSpy; beforeAll(() => { const lockMock = createLockMock({ provider: 'auth0_v2', @@ -151,12 +144,11 @@ describe('ThirdPartyCaptcha', () => { value={undefined} /> ).instance(); - getRenderParamsSpy = jest.spyOn(shallowWrapper, 'getRenderParams'); shallowWrapper.componentDidMount(); }); it('should pass the sitekey', () => { - const renderParams = getRenderParamsSpy.mock.results[0].value; + const { renderParams } = shallowWrapper; expect(renderParams).toMatchObject({ sitekey: 'mySiteKey', callback: expect.any(Function), @@ -170,7 +162,6 @@ describe('ThirdPartyCaptcha', () => { describe('recaptcha enterprise', () => { let shallowWrapper; - let getRenderParamsSpy; beforeAll(() => { const lockMock = createLockMock({ provider: 'recaptcha_enterprise', @@ -188,12 +179,11 @@ describe('ThirdPartyCaptcha', () => { value={undefined} /> ).instance(); - getRenderParamsSpy = jest.spyOn(shallowWrapper, 'getRenderParams'); shallowWrapper.componentDidMount(); }); it('should pass the sitekey', () => { - const renderParams = getRenderParamsSpy.mock.results[0].value; + const { renderParams } = shallowWrapper; expect(renderParams).toMatchObject({ sitekey: 'mySiteKey', callback: expect.any(Function), @@ -207,7 +197,6 @@ describe('ThirdPartyCaptcha', () => { describe('Arkose', () => { let shallowWrapper; - let getRenderParamsSpy; beforeAll(() => { const lockMock = createLockMock({ provider: 'arkose', @@ -226,13 +215,15 @@ describe('ThirdPartyCaptcha', () => { value={undefined} /> ).instance(); - getRenderParamsSpy = jest.spyOn(shallowWrapper, 'getRenderParams'); shallowWrapper.componentDidMount(); }); it('should pass the sitekey', () => { - const renderParams = getRenderParamsSpy.mock.results[0].value; - expect(renderParams).toEqual({}); + const { renderParams } = shallowWrapper; + expect(renderParams).toEqual({ + sitekey: 'mySiteKey' + }); + expect(Object.keys(renderParams)).toHaveLength(1); }); }); }); diff --git a/src/field/captcha/third_party_captcha.jsx b/src/field/captcha/third_party_captcha.jsx index 2268d0a8f..47d705cea 100644 --- a/src/field/captcha/third_party_captcha.jsx +++ b/src/field/captcha/third_party_captcha.jsx @@ -124,38 +124,39 @@ export class ThirdPartyCaptcha extends React.Component { this.props.onErrored(); }); }; + this.renderParams = { + sitekey: this.props.sitekey, + } } getRenderParams() { if (this.props.provider === ARKOSE_PROVIDER) { - return {}; + return; } if (this.props.provider === FRIENDLY_CAPTCHA_PROVIDER) { - return { - sitekey: this.props.sitekey, + this.renderParams = { + ...this.renderParams, language: this.props.hl, doneCallback: this.changeHandler, errorCallback: this.erroredHandler }; + return; } - - let renderParams = { - sitekey: this.props.sitekey, + this.renderParams = { + ...this.renderParams, callback: this.changeHandler, 'expired-callback': this.expiredHandler, 'error-callback': this.erroredHandler }; if (this.props.provider === AUTH0_V2_CAPTCHA_PROVIDER) { - renderParams = { - ...renderParams, + this.renderParams = { + ...this.renderParams, language: this.props.hl, theme: 'light' }; } - - return renderParams; } injectCaptchaScript(callback = noop) { @@ -207,7 +208,7 @@ export class ThirdPartyCaptcha extends React.Component { componentDidMount() { // grab the render params outside of the callback just to spy on it in the test - const renderParams = this.getRenderParams(); + this.getRenderParams(); this.injectCaptchaScript((arkose) => { const provider = getCaptchaProvider(this.props.provider); if (this.props.provider === ARKOSE_PROVIDER) { @@ -234,10 +235,10 @@ export class ThirdPartyCaptcha extends React.Component { } }); } else if (this.props.provider === FRIENDLY_CAPTCHA_PROVIDER) { - this.widgetInstance = new provider.WidgetInstance(this.ref.current, renderParams); + this.widgetInstance = new provider.WidgetInstance(this.ref.current, this.renderParams); } else { // if this is enterprise then we change this to window.grecaptcha.enterprise.render - this.widgetId = provider.render(this.ref.current, renderParams); + this.widgetId = provider.render(this.ref.current, this.renderParams); } }); } From 397ca15f7fbfa0b3959482480f23ac938e22b845 Mon Sep 17 00:00:00 2001 From: alexkoumarianos-okta Date: Tue, 12 Dec 2023 09:36:03 -0500 Subject: [PATCH 08/12] small test improvements --- .../field/captcha/third_party_captcha.test.jsx | 10 ++++++---- src/field/captcha/third_party_captcha.jsx | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/__tests__/field/captcha/third_party_captcha.test.jsx b/src/__tests__/field/captcha/third_party_captcha.test.jsx index 80b85b087..fad9dbf10 100644 --- a/src/__tests__/field/captcha/third_party_captcha.test.jsx +++ b/src/__tests__/field/captcha/third_party_captcha.test.jsx @@ -50,6 +50,7 @@ describe('ThirdPartyCaptcha', () => { }); expect(renderParams.language).toBeUndefined(); expect(renderParams.theme).toBeUndefined(); + expect(Object.keys(renderParams)).toHaveLength(4); }); }); @@ -87,6 +88,7 @@ describe('ThirdPartyCaptcha', () => { expect(renderParams.callback).toBeUndefined(); expect(renderParams['expired-callback']).toBeUndefined(); expect(renderParams['error-callback']).toBeUndefined(); + expect(Object.keys(renderParams)).toHaveLength(4); }); }); @@ -122,6 +124,7 @@ describe('ThirdPartyCaptcha', () => { }); expect(renderParams.language).toBeUndefined(); expect(renderParams.theme).toBeUndefined(); + expect(Object.keys(renderParams)).toHaveLength(4); }); }); @@ -157,6 +160,7 @@ describe('ThirdPartyCaptcha', () => { language: 'en', theme: 'light' }); + expect(Object.keys(renderParams)).toHaveLength(6); }); }); @@ -192,6 +196,7 @@ describe('ThirdPartyCaptcha', () => { }); expect(renderParams.language).toBeUndefined(); expect(renderParams.theme).toBeUndefined(); + expect(Object.keys(renderParams)).toHaveLength(4); }); }); @@ -220,10 +225,7 @@ describe('ThirdPartyCaptcha', () => { it('should pass the sitekey', () => { const { renderParams } = shallowWrapper; - expect(renderParams).toEqual({ - sitekey: 'mySiteKey' - }); - expect(Object.keys(renderParams)).toHaveLength(1); + expect(renderParams).toBeUndefined(); }); }); }); diff --git a/src/field/captcha/third_party_captcha.jsx b/src/field/captcha/third_party_captcha.jsx index 47d705cea..bc232af52 100644 --- a/src/field/captcha/third_party_captcha.jsx +++ b/src/field/captcha/third_party_captcha.jsx @@ -131,6 +131,8 @@ export class ThirdPartyCaptcha extends React.Component { getRenderParams() { if (this.props.provider === ARKOSE_PROVIDER) { + // Arkose doesn't use render params + delete this.renderParams; return; } From f7ec9437bd20d9458c5208711db69a778e4e96a3 Mon Sep 17 00:00:00 2001 From: alexkoumarianos-okta Date: Tue, 12 Dec 2023 11:08:26 -0500 Subject: [PATCH 09/12] fix test names --- .../field/captcha/third_party_captcha.test.jsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/__tests__/field/captcha/third_party_captcha.test.jsx b/src/__tests__/field/captcha/third_party_captcha.test.jsx index fad9dbf10..e89b66a09 100644 --- a/src/__tests__/field/captcha/third_party_captcha.test.jsx +++ b/src/__tests__/field/captcha/third_party_captcha.test.jsx @@ -40,7 +40,7 @@ describe('ThirdPartyCaptcha', () => { shallowWrapper.componentDidMount(); }); - it('should pass the sitekey', () => { + it('should have correct renderParams', () => { const { renderParams } = shallowWrapper; expect(renderParams).toMatchObject({ sitekey: 'mySiteKey', @@ -76,7 +76,7 @@ describe('ThirdPartyCaptcha', () => { shallowWrapper.componentDidMount(); }); - it('should pass the sitekey', () => { + it('should have correct renderParams', () => { const { renderParams } = shallowWrapper; expect(renderParams).toMatchObject({ sitekey: 'mySiteKey', @@ -114,7 +114,7 @@ describe('ThirdPartyCaptcha', () => { shallowWrapper.componentDidMount(); }); - it('should pass the sitekey', () => { + it('should have correct renderParams', () => { const { renderParams } = shallowWrapper; expect(renderParams).toMatchObject({ sitekey: 'mySiteKey', @@ -150,7 +150,7 @@ describe('ThirdPartyCaptcha', () => { shallowWrapper.componentDidMount(); }); - it('should pass the sitekey', () => { + it('should have correct renderParams', () => { const { renderParams } = shallowWrapper; expect(renderParams).toMatchObject({ sitekey: 'mySiteKey', @@ -186,7 +186,7 @@ describe('ThirdPartyCaptcha', () => { shallowWrapper.componentDidMount(); }); - it('should pass the sitekey', () => { + it('should have correct renderParams', () => { const { renderParams } = shallowWrapper; expect(renderParams).toMatchObject({ sitekey: 'mySiteKey', @@ -223,7 +223,7 @@ describe('ThirdPartyCaptcha', () => { shallowWrapper.componentDidMount(); }); - it('should pass the sitekey', () => { + it('should have correct renderParams', () => { const { renderParams } = shallowWrapper; expect(renderParams).toBeUndefined(); }); From 7d86db818acff20c0a8b0a33ba302e41dddea944 Mon Sep 17 00:00:00 2001 From: alexkoumarianos-okta Date: Tue, 12 Dec 2023 12:24:50 -0500 Subject: [PATCH 10/12] change tests --- .../captcha/third_party_captcha.test.jsx | 163 ++++++++++-------- src/field/captcha/third_party_captcha.jsx | 1 - 2 files changed, 88 insertions(+), 76 deletions(-) diff --git a/src/__tests__/field/captcha/third_party_captcha.test.jsx b/src/__tests__/field/captcha/third_party_captcha.test.jsx index e89b66a09..da788308d 100644 --- a/src/__tests__/field/captcha/third_party_captcha.test.jsx +++ b/src/__tests__/field/captcha/third_party_captcha.test.jsx @@ -1,5 +1,6 @@ import React from 'react'; -import { shallow } from 'enzyme'; +import { mount } from 'enzyme'; +import { act } from 'react-dom/test-utils'; import I from 'immutable'; import * as l from '../../../core/index'; import { ThirdPartyCaptcha } from '../../../field/captcha/third_party_captcha'; @@ -18,8 +19,32 @@ const createLockMock = ({ }); describe('ThirdPartyCaptcha', () => { + let prevWindow; + beforeAll(() => { + prevWindow = global.window; + global.window.grecaptcha = { + render: jest.fn(), + enterprise: { + render: jest.fn() + } + }; + global.window.hcaptcha = { + render: jest.fn() + }; + global.window.friendlyChallenge = { + WidgetInstance: jest.fn().mockImplementation((...args) => { + return jest.fn(...args); + }) + }; + global.window.turnstile = { + render: jest.fn() + }; + }); + afterAll(() => { + global.window = prevWindow; + }); describe('recaptchav2', () => { - let shallowWrapper; + let wrapper; beforeAll(() => { const lockMock = createLockMock({ provider: 'recaptcha_v2', @@ -27,7 +52,7 @@ describe('ThirdPartyCaptcha', () => { }); const captcha = l.captcha(lockMock); - shallowWrapper = shallow( + wrapper = mount( { value={undefined} /> ).instance(); - shallowWrapper.componentDidMount(); + act(() => { + injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); + + wrapper.componentDidMount(); + + injectCaptchaScriptSpy.mock.calls[0][0](); + }); }); - it('should have correct renderParams', () => { - const { renderParams } = shallowWrapper; - expect(renderParams).toMatchObject({ + it('should call render with the correct renderParams', () => { + const renderParams = global.window.grecaptcha.render.mock.calls[0][1]; + + expect(renderParams).toEqual({ sitekey: 'mySiteKey', callback: expect.any(Function), 'expired-callback': expect.any(Function), 'error-callback': expect.any(Function) }); - expect(renderParams.language).toBeUndefined(); - expect(renderParams.theme).toBeUndefined(); - expect(Object.keys(renderParams)).toHaveLength(4); }); }); describe('friendly captcha', () => { - let shallowWrapper; + let wrapper; beforeAll(() => { const lockMock = createLockMock({ provider: 'friendly_captcha', @@ -63,7 +92,7 @@ describe('ThirdPartyCaptcha', () => { }); const captcha = l.captcha(lockMock); - shallowWrapper = shallow( + wrapper = mount( { value={undefined} /> ).instance(); - shallowWrapper.componentDidMount(); + act(() => { + injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); + + wrapper.componentDidMount(); + jest.spyOn(global.window.friendlyChallenge, 'WidgetInstance'); + + injectCaptchaScriptSpy.mock.calls[0][0](); + }); }); - it('should have correct renderParams', () => { - const { renderParams } = shallowWrapper; - expect(renderParams).toMatchObject({ + it('should call WidgetInstance constructor with the correct renderParams', () => { + const renderParams = global.window.friendlyChallenge.WidgetInstance.mock.calls[0][1]; + expect(renderParams).toEqual({ sitekey: 'mySiteKey', doneCallback: expect.any(Function), errorCallback: expect.any(Function), language: 'en' }); - expect(renderParams.theme).toBeUndefined(); - expect(renderParams.callback).toBeUndefined(); - expect(renderParams['expired-callback']).toBeUndefined(); - expect(renderParams['error-callback']).toBeUndefined(); - expect(Object.keys(renderParams)).toHaveLength(4); }); }); describe('hcaptcha', () => { - let shallowWrapper; + let wrapper; beforeAll(() => { const lockMock = createLockMock({ provider: 'hcaptcha', @@ -101,7 +132,7 @@ describe('ThirdPartyCaptcha', () => { }); const captcha = l.captcha(lockMock); - shallowWrapper = shallow( + wrapper = mount( { value={undefined} /> ).instance(); - shallowWrapper.componentDidMount(); + act(() => { + injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); + + wrapper.componentDidMount(); + + injectCaptchaScriptSpy.mock.calls[0][0](); + }); }); - it('should have correct renderParams', () => { - const { renderParams } = shallowWrapper; - expect(renderParams).toMatchObject({ + it('should call render with the correct renderParams', () => { + const renderParams = global.window.hcaptcha.render.mock.calls[0][1]; + expect(renderParams).toEqual({ sitekey: 'mySiteKey', callback: expect.any(Function), 'expired-callback': expect.any(Function), 'error-callback': expect.any(Function) }); - expect(renderParams.language).toBeUndefined(); - expect(renderParams.theme).toBeUndefined(); - expect(Object.keys(renderParams)).toHaveLength(4); }); }); describe('auth0_v2', () => { - let shallowWrapper; + let wrapper; beforeAll(() => { const lockMock = createLockMock({ provider: 'auth0_v2', @@ -137,7 +171,7 @@ describe('ThirdPartyCaptcha', () => { }); const captcha = l.captcha(lockMock); - shallowWrapper = shallow( + wrapper = mount( { value={undefined} /> ).instance(); - shallowWrapper.componentDidMount(); + act(() => { + injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); + + wrapper.componentDidMount(); + + injectCaptchaScriptSpy.mock.calls[0][0](); + }); }); - it('should have correct renderParams', () => { - const { renderParams } = shallowWrapper; - expect(renderParams).toMatchObject({ + it('should call render with the correct renderParams', () => { + const renderParams = global.window.turnstile.render.mock.calls[0][1]; + expect(renderParams).toEqual({ sitekey: 'mySiteKey', callback: expect.any(Function), 'expired-callback': expect.any(Function), @@ -160,12 +200,11 @@ describe('ThirdPartyCaptcha', () => { language: 'en', theme: 'light' }); - expect(Object.keys(renderParams)).toHaveLength(6); }); }); describe('recaptcha enterprise', () => { - let shallowWrapper; + let wrapper; beforeAll(() => { const lockMock = createLockMock({ provider: 'recaptcha_enterprise', @@ -173,7 +212,7 @@ describe('ThirdPartyCaptcha', () => { }); const captcha = l.captcha(lockMock); - shallowWrapper = shallow( + wrapper = mount( { value={undefined} /> ).instance(); - shallowWrapper.componentDidMount(); + act(() => { + injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); + + wrapper.componentDidMount(); + + injectCaptchaScriptSpy.mock.calls[0][0](); + }); }); - it('should have correct renderParams', () => { - const { renderParams } = shallowWrapper; - expect(renderParams).toMatchObject({ + it('should call render with the correct renderParams', () => { + const renderParams = global.window.grecaptcha.enterprise.render.mock.calls[0][1]; + expect(renderParams).toEqual({ sitekey: 'mySiteKey', callback: expect.any(Function), 'expired-callback': expect.any(Function), 'error-callback': expect.any(Function) }); - expect(renderParams.language).toBeUndefined(); - expect(renderParams.theme).toBeUndefined(); - expect(Object.keys(renderParams)).toHaveLength(4); - }); - }); - - describe('Arkose', () => { - let shallowWrapper; - beforeAll(() => { - const lockMock = createLockMock({ - provider: 'arkose', - siteKey: 'mySiteKey', - clientSubdomain: 'client-api' - }); - - const captcha = l.captcha(lockMock); - shallowWrapper = shallow( - - ).instance(); - shallowWrapper.componentDidMount(); - }); - - it('should have correct renderParams', () => { - const { renderParams } = shallowWrapper; - expect(renderParams).toBeUndefined(); }); }); }); diff --git a/src/field/captcha/third_party_captcha.jsx b/src/field/captcha/third_party_captcha.jsx index bc232af52..f1c5610a9 100644 --- a/src/field/captcha/third_party_captcha.jsx +++ b/src/field/captcha/third_party_captcha.jsx @@ -209,7 +209,6 @@ export class ThirdPartyCaptcha extends React.Component { } componentDidMount() { - // grab the render params outside of the callback just to spy on it in the test this.getRenderParams(); this.injectCaptchaScript((arkose) => { const provider = getCaptchaProvider(this.props.provider); From f74a2ec17106c6292d3f48a833227e83a2c7c9b7 Mon Sep 17 00:00:00 2001 From: alexkoumarianos-okta Date: Tue, 12 Dec 2023 12:28:31 -0500 Subject: [PATCH 11/12] fix injectCaptchaScriptSpy --- .../field/captcha/third_party_captcha.test.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/__tests__/field/captcha/third_party_captcha.test.jsx b/src/__tests__/field/captcha/third_party_captcha.test.jsx index da788308d..31adbdbe3 100644 --- a/src/__tests__/field/captcha/third_party_captcha.test.jsx +++ b/src/__tests__/field/captcha/third_party_captcha.test.jsx @@ -63,7 +63,7 @@ describe('ThirdPartyCaptcha', () => { /> ).instance(); act(() => { - injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); + const injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); wrapper.componentDidMount(); @@ -103,7 +103,7 @@ describe('ThirdPartyCaptcha', () => { /> ).instance(); act(() => { - injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); + const injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); wrapper.componentDidMount(); jest.spyOn(global.window.friendlyChallenge, 'WidgetInstance'); @@ -143,7 +143,7 @@ describe('ThirdPartyCaptcha', () => { /> ).instance(); act(() => { - injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); + const injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); wrapper.componentDidMount(); @@ -182,7 +182,7 @@ describe('ThirdPartyCaptcha', () => { /> ).instance(); act(() => { - injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); + const injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); wrapper.componentDidMount(); @@ -223,7 +223,7 @@ describe('ThirdPartyCaptcha', () => { /> ).instance(); act(() => { - injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); + const injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript'); wrapper.componentDidMount(); From 38d1dc2c2d014c275e205579a976e0a7d3825b88 Mon Sep 17 00:00:00 2001 From: alexkoumarianos-okta Date: Thu, 14 Dec 2023 08:00:32 -0500 Subject: [PATCH 12/12] removed this.renderParams --- src/field/captcha/third_party_captcha.jsx | 28 ++++++++--------------- 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/field/captcha/third_party_captcha.jsx b/src/field/captcha/third_party_captcha.jsx index f1c5610a9..6a5da9676 100644 --- a/src/field/captcha/third_party_captcha.jsx +++ b/src/field/captcha/third_party_captcha.jsx @@ -124,41 +124,34 @@ export class ThirdPartyCaptcha extends React.Component { this.props.onErrored(); }); }; - this.renderParams = { - sitekey: this.props.sitekey, - } } getRenderParams() { - if (this.props.provider === ARKOSE_PROVIDER) { - // Arkose doesn't use render params - delete this.renderParams; - return; - } + if (this.props.provider === FRIENDLY_CAPTCHA_PROVIDER) { - this.renderParams = { - ...this.renderParams, + return { + sitekey: this.props.sitekey, language: this.props.hl, doneCallback: this.changeHandler, errorCallback: this.erroredHandler }; - return; } - this.renderParams = { - ...this.renderParams, + let renderParams = { + sitekey: this.props.sitekey, callback: this.changeHandler, 'expired-callback': this.expiredHandler, 'error-callback': this.erroredHandler }; if (this.props.provider === AUTH0_V2_CAPTCHA_PROVIDER) { - this.renderParams = { - ...this.renderParams, + renderParams = { + ...renderParams, language: this.props.hl, theme: 'light' }; } + return renderParams; } injectCaptchaScript(callback = noop) { @@ -209,7 +202,6 @@ export class ThirdPartyCaptcha extends React.Component { } componentDidMount() { - this.getRenderParams(); this.injectCaptchaScript((arkose) => { const provider = getCaptchaProvider(this.props.provider); if (this.props.provider === ARKOSE_PROVIDER) { @@ -236,10 +228,10 @@ export class ThirdPartyCaptcha extends React.Component { } }); } else if (this.props.provider === FRIENDLY_CAPTCHA_PROVIDER) { - this.widgetInstance = new provider.WidgetInstance(this.ref.current, this.renderParams); + this.widgetInstance = new provider.WidgetInstance(this.ref.current, this.getRenderParams()); } else { // if this is enterprise then we change this to window.grecaptcha.enterprise.render - this.widgetId = provider.render(this.ref.current, this.renderParams); + this.widgetId = provider.render(this.ref.current, this.getRenderParams()); } }); }