UNPKG

quodolores

Version:

Monorepo for the Firebase JavaScript SDK

198 lines (171 loc) 6.73 kB
/** * @license * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { expect, use } from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; import * as sinon from 'sinon'; import * as sinonChai from 'sinon-chai'; import { FirebaseError } from '@firebase/util'; import { mockEndpoint } from '../../../test/helpers/api/helper'; import { testAuth, TestAuth } from '../../../test/helpers/mock_auth'; import * as fetch from '../../../test/helpers/mock_fetch'; import { Endpoint } from '../../api'; import { _window } from '../auth_window'; import { Parameters, Recaptcha } from './recaptcha'; import { ReCaptchaLoader } from './recaptcha_loader'; import { MockReCaptcha } from './recaptcha_mock'; import { RecaptchaVerifier } from './recaptcha_verifier'; use(chaiAsPromised); use(sinonChai); describe('platform_browser/recaptcha/recaptcha_verifier', () => { let auth: TestAuth; let container: HTMLElement; let verifier: RecaptchaVerifier; let parameters: Parameters; let recaptchaLoader: ReCaptchaLoader; beforeEach(async () => { fetch.setUp(); auth = await testAuth(); auth.languageCode = 'fr'; container = document.createElement('div'); parameters = {}; verifier = new RecaptchaVerifier(container, parameters, auth); // The verifier will have set the parameters.callback field to be the wrapped callback mockEndpoint(Endpoint.GET_RECAPTCHA_PARAM, { recaptchaSiteKey: 'recaptcha-key' }); recaptchaLoader = verifier._recaptchaLoader; }); afterEach(() => { sinon.restore(); fetch.tearDown(); }); context('#render', () => { it('caches the promise if not completed and returns if called multiple times', () => { // This will force the loader to never return so the render promise never completes sinon.stub(recaptchaLoader, 'load').returns(new Promise(() => {})); const renderPromise = verifier.render(); expect(verifier.render()).to.eq(renderPromise); }); it('appends an empty div to the container element', async () => { expect(container.childElementCount).to.eq(0); await verifier.render(); expect(container.childElementCount).to.eq(1); }); it('sets the site key on the parameters object', async () => { await verifier.render(); expect(parameters.sitekey).to.eq('recaptcha-key'); }); it('sets loads the recaptcha per the app language code', async () => { sinon.spy(recaptchaLoader, 'load'); await verifier.render(); expect(recaptchaLoader.load).to.have.been.calledWith(auth, 'fr'); }); it('calls render on the underlying recaptcha widget', async () => { const widget = new MockReCaptcha(auth); sinon.spy(widget, 'render'); sinon.stub(recaptchaLoader, 'load').returns(Promise.resolve(widget)); await verifier.render(); expect(widget.render).to.have.been.calledWith( container.children[0], parameters ); }); it('in case of error, resets render promise', async () => { sinon.stub(recaptchaLoader, 'load').returns(Promise.reject('nope')); const promise = verifier.render(); await expect(promise).to.be.rejectedWith('nope'); expect(verifier.render()).not.to.eq(promise); }); }); context('#verify', () => { let recaptcha: Recaptcha; beforeEach(() => { recaptcha = new MockReCaptcha(auth); sinon.stub(recaptchaLoader, 'load').returns(Promise.resolve(recaptcha)); }); it('returns immediately if response is available', async () => { sinon.stub(recaptcha, 'getResponse').returns('recaptcha-response'); expect(await verifier.verify()).to.eq('recaptcha-response'); }); it('resolves with the token in the callback', async () => { sinon.stub(recaptcha, 'getResponse').returns(''); const promise = verifier.verify(); expect(typeof (await promise)).to.eq('string'); }); it('calls existing callback if provided', async () => { let token = ''; parameters = { callback: (t: string): void => { token = t; } }; verifier = new RecaptchaVerifier(container, parameters, auth); const expected = await verifier.verify(); expect(token).to.eq(expected); }); it('calls existing global function if on the window', async () => { let token = ''; _window().callbackOnWindowObject = (t: unknown): void => { token = t as string; }; parameters = { callback: 'callbackOnWindowObject' }; verifier = new RecaptchaVerifier(container, parameters, auth); const expected = await verifier.verify(); expect(token).to.eq(expected); delete _window().callbackOnWindowObject; }); }); context('#reset', () => { it('calls reset on the underlying widget', async () => { const recaptcha = new MockReCaptcha(auth); sinon.stub(recaptchaLoader, 'load').returns(Promise.resolve(recaptcha)); sinon.spy(recaptcha, 'reset'); await verifier.render(); verifier._reset(); expect(recaptcha.reset).to.have.been.called; }); }); context('#clear', () => { it('removes the child node from the container', async () => { await verifier.render(); expect(container.children.length).to.eq(1); verifier.clear(); expect(container.children.length).to.eq(0); }); it('causes other methods of the verifier to throw if called subsequently', async () => { verifier.clear(); expect(() => verifier.clear()).to.throw( FirebaseError, 'Firebase: An internal AuthError has occurred. (auth/internal-error).' ); expect(() => verifier._reset()).to.throw( FirebaseError, 'Firebase: An internal AuthError has occurred. (auth/internal-error).' ); await expect(verifier.render()).to.be.rejectedWith( FirebaseError, 'Firebase: An internal AuthError has occurred. (auth/internal-error).' ); await expect(verifier.verify()).to.be.rejectedWith( FirebaseError, 'Firebase: An internal AuthError has occurred. (auth/internal-error).' ); }); }); });