UNPKG

quodolores

Version:

Monorepo for the Firebase JavaScript SDK

237 lines (202 loc) 7.62 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 { testAuth, TestAuth } from '../../../test/helpers/mock_auth'; import { stubTimeouts, TimerMap } from '../../../test/helpers/timeout_stub'; import { _EXPIRATION_TIME_MS, _SOLVE_TIME_MS, _WIDGET_ID_START, MockReCaptcha, MockWidget, Widget } from './recaptcha_mock'; use(sinonChai); use(chaiAsPromised); describe('platform_browser/recaptcha/recaptcha_mock', () => { let container: HTMLElement; let auth: TestAuth; beforeEach(async () => { container = document.createElement('div'); auth = await testAuth(); }); afterEach(() => { sinon.restore(); }); describe('MockRecaptcha', () => { let rc: MockReCaptcha; let widget: Widget; beforeEach(async () => { rc = new MockReCaptcha(auth); widget = { getResponse: sinon.stub(), delete: sinon.stub(), execute: sinon.stub() }; }); context('#reset', () => { it('resets the default widget if no id provided', () => { rc._widgets.set(_WIDGET_ID_START, widget); rc.reset(); expect(widget.delete).to.have.been.called; expect(rc._widgets).to.be.empty; }); it('resets and removes the widgetId only if passed in', () => { const widget2 = { getResponse: sinon.stub(), delete: sinon.stub(), execute: sinon.stub() }; rc._widgets.set(_WIDGET_ID_START, widget); rc._widgets.set(_WIDGET_ID_START + 1, widget2); rc.reset(_WIDGET_ID_START + 1); expect(widget2.delete).to.have.been.called; expect(widget.delete).not.to.have.been.called; expect(rc._widgets.get(_WIDGET_ID_START)).to.eq(widget); expect(rc._widgets.size).to.eq(1); }); }); context('#render', () => { // These tests all use invisible recaptcha to prevent the mock widget // from setting timers const params = { size: 'invisible' }; it('adds a mock widget object to the set and returns the id', () => { const id = rc.render(container, params); expect(id).to.eq(_WIDGET_ID_START); expect(rc._widgets.get(_WIDGET_ID_START)).to.not.be.undefined; }); it('sequentially creates new widgets', () => { rc.render(container, params); rc.render(container, params); expect(rc._widgets.get(_WIDGET_ID_START)).to.not.be.undefined; expect(rc._widgets.get(_WIDGET_ID_START + 1)).to.not.be.undefined; }); }); context('#getResponse', () => { it('returns the result from the widget if available', () => { (widget.getResponse as sinon.SinonStub).returns('widget-result'); rc._widgets.set(_WIDGET_ID_START, widget); expect(rc.getResponse()).to.eq('widget-result'); }); it('returns the empty string if the widget does not exist', () => { expect(rc.getResponse()).to.eq(''); }); }); context('#execute', () => { it('calls execute on the underlying widget', async () => { rc._widgets.set(_WIDGET_ID_START, widget); await rc.execute(); expect(widget.execute).to.have.been.called; }); it('returns the empty string', async () => { expect(await rc.execute()).to.eq(''); }); }); }); describe('MockWidget', () => { context('#constructor', () => { it('errors if a bad container is passed in', () => { sinon.stub(document, 'getElementById').returns(null); expect(() => new MockWidget('foo', 'app-name', {})).to.throw( FirebaseError, 'Firebase: Error (auth/argument-error).' ); }); it('attaches an event listener if invisible', () => { sinon.spy(container, 'addEventListener'); void new MockWidget(container, 'app-name', { size: 'invisible' }); expect(container.addEventListener).to.have.been.called; }); }); context('#execute', () => { // Stub out a bunch of stuff on setTimer let pendingTimers: TimerMap; let callbacks: { [key: string]: sinon.SinonSpy }; let widget: MockWidget; let timeoutStub: sinon.SinonStub; beforeEach(() => { callbacks = { 'callback': sinon.spy(), 'expired-callback': sinon.spy() }; pendingTimers = stubTimeouts(); timeoutStub = (window.setTimeout as unknown) as sinon.SinonStub; widget = new MockWidget(container, auth.name, callbacks); }); it('keeps re-executing with new tokens if expiring', () => { pendingTimers[_SOLVE_TIME_MS](); pendingTimers[_EXPIRATION_TIME_MS](); pendingTimers[_SOLVE_TIME_MS](); pendingTimers[_EXPIRATION_TIME_MS](); expect(callbacks['callback']).to.have.been.calledTwice; expect(callbacks['callback'].getCall(0).args[0]).not.to.eq( callbacks['callback'].getCall(1).args[0] ); }); it('posts callback with a random alphanumeric code', () => { pendingTimers[_SOLVE_TIME_MS](); const arg: string = callbacks['callback'].getCall(0).args[0]; expect(arg).to.be.a('string'); expect(arg.length).to.equal(50); }); it('expired callback does execute if just solve trips', () => { pendingTimers[_SOLVE_TIME_MS](); expect(callbacks['callback']).to.have.been.called; expect(callbacks['expired-callback']).not.to.have.been.called; }); it('expired callback executes if just expiration timer trips', () => { pendingTimers[_SOLVE_TIME_MS](); pendingTimers[_EXPIRATION_TIME_MS](); expect(callbacks['callback']).to.have.been.called; expect(callbacks['expired-callback']).to.have.been.called; }); it('throws an error if the widget is deleted', () => { widget.delete(); expect(() => widget.execute()).to.throw(Error); }); it('returns immediately if timer is already in flight', () => { expect(timeoutStub.getCalls().length).to.eq(1); widget.execute(); expect(timeoutStub.getCalls().length).to.eq(1); }); }); context('#delete', () => { let widget: MockWidget; beforeEach(() => { widget = new MockWidget(container, auth.name, {}); }); it('throws if already deleted', () => { widget.delete(); expect(() => widget.delete()).to.throw(Error); }); it('clears any timeouts that are set', () => { const spy = sinon.spy(window, 'clearTimeout'); widget.delete(); expect(spy).to.have.been.called; }); it('removes the event listener from the container', () => { const spy = sinon.spy(container, 'removeEventListener'); widget.delete(); expect(spy).to.have.been.called; }); }); }); });