Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IAMRISK-2916] Added support for Auth0 v2 captcha provider #2503

Prev Previous commit
Next Next commit
change tests
  • Loading branch information
alexkoumarianos-okta committed Dec 12, 2023
commit 7d86db818acff20c0a8b0a33ba302e41dddea944
163 changes: 88 additions & 75 deletions src/__tests__/field/captcha/third_party_captcha.test.jsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -18,16 +19,40 @@
});

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',
siteKey: 'mySiteKey'
});

const captcha = l.captcha(lockMock);
shallowWrapper = shallow(
wrapper = mount(
<ThirdPartyCaptcha
provider={captcha.get('provider')}
sitekey={captcha.get('siteKey')}
Expand All @@ -37,33 +62,37 @@
value={undefined}
/>
).instance();
shallowWrapper.componentDidMount();
act(() => {
injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript');
Fixed Show fixed Hide fixed

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',
siteKey: 'mySiteKey'
});

const captcha = l.captcha(lockMock);
shallowWrapper = shallow(
wrapper = mount(
<ThirdPartyCaptcha
provider={captcha.get('provider')}
sitekey={captcha.get('siteKey')}
Expand All @@ -73,35 +102,37 @@
value={undefined}
/>
).instance();
shallowWrapper.componentDidMount();
act(() => {
injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript');
Fixed Show fixed Hide fixed

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',
siteKey: 'mySiteKey'
});

const captcha = l.captcha(lockMock);
shallowWrapper = shallow(
wrapper = mount(
<ThirdPartyCaptcha
provider={captcha.get('provider')}
sitekey={captcha.get('siteKey')}
Expand All @@ -111,33 +142,36 @@
value={undefined}
/>
).instance();
shallowWrapper.componentDidMount();
act(() => {
injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript');
Fixed Show fixed Hide fixed

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',
siteKey: 'mySiteKey'
});

const captcha = l.captcha(lockMock);
shallowWrapper = shallow(
wrapper = mount(
<ThirdPartyCaptcha
provider={captcha.get('provider')}
sitekey={captcha.get('siteKey')}
Expand All @@ -147,33 +181,38 @@
value={undefined}
/>
).instance();
shallowWrapper.componentDidMount();
act(() => {
injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript');
Fixed Show fixed Hide fixed

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),
'error-callback': expect.any(Function),
language: 'en',
theme: 'light'
});
expect(Object.keys(renderParams)).toHaveLength(6);
});
});

describe('recaptcha enterprise', () => {
let shallowWrapper;
let wrapper;
beforeAll(() => {
const lockMock = createLockMock({
provider: 'recaptcha_enterprise',
siteKey: 'mySiteKey'
});

const captcha = l.captcha(lockMock);
shallowWrapper = shallow(
wrapper = mount(
<ThirdPartyCaptcha
provider={captcha.get('provider')}
sitekey={captcha.get('siteKey')}
Expand All @@ -183,49 +222,23 @@
value={undefined}
/>
).instance();
shallowWrapper.componentDidMount();
act(() => {
injectCaptchaScriptSpy = jest.spyOn(wrapper, 'injectCaptchaScript');
Fixed Show fixed Hide fixed

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(
<ThirdPartyCaptcha
provider={captcha.get('provider')}
sitekey={captcha.get('siteKey')}
clientSubdomain={captcha.get('clientSubdomain')}
hl={'en'}
isValid={true}
value={undefined}
/>
).instance();
shallowWrapper.componentDidMount();
});

it('should have correct renderParams', () => {
const { renderParams } = shallowWrapper;
expect(renderParams).toBeUndefined();
});
});
});
1 change: 0 additions & 1 deletion src/field/captcha/third_party_captcha.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Copy link
Member

@frederikprijck frederikprijck Dec 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should have this comment here.

Additionally, I am not sure I understand why we need this.getRenderParams in the first place, we shouldnt have to spy on it. Instead, we should verify what gets passed to provider.render as that's what counts. getRenderParams is an implementation details we shouldnt test, meaning that if we revert this to the original, tests should still succeed.

I am not saying we cant have this.getRenderParams, but I am saying we should ensure we do not need it in order for the tests to succeed. Whether you keep it in that case is up to you.

Copy link
Member

@frederikprijck frederikprijck Dec 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making all the changes here, appreciate it.

Currently, the code relies on this.getRenderParams to have been called on mount, and then just uses this.renderParams.

How do you feel about dropping this.renderParams, but instead call this.getRenderParams() where u need the params, and inline the current defaultRenderParams in the getRenderParams. It would also remove the need to do delete this.renderPrams, which I always try to avoid. Additionally we would also no longer be constantly mutating the same this.renderParams.

 getRenderParams() {
    if (this.props.provider === ARKOSE_PROVIDER) {
      return;
    }
    
    const defaultRenderParams = {
      sitekey: this.props.sitekey,
    };

    if (this.props.provider === FRIENDLY_CAPTCHA_PROVIDER) {
      return {
        ...this.defaultRenderParams,
        language: this.props.hl,
        doneCallback: this.changeHandler,
        errorCallback: this.erroredHandler
      };
    }

    if (this.props.provider === AUTH0_V2_CAPTCHA_PROVIDER) {
      return {
        ...this.defaultRenderParams,
        callback: this.changeHandler,
        'expired-callback': this.expiredHandler,
        'error-callback': this.erroredHandler
        language: this.props.hl,
        theme: 'light'
      };
    } else {
      return {
      ...this.defaultRenderParams,
      callback: this.changeHandler,
      'expired-callback': this.expiredHandler,
      'error-callback': this.erroredHandler
    };
    }
  }

this.injectCaptchaScript((arkose) => {
const provider = getCaptchaProvider(this.props.provider);
Expand Down