veritatisdoloremque
Version:
Monorepo for the Firebase JavaScript SDK
161 lines (140 loc) • 4.4 kB
text/typescript
/**
* @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 { AuthErrorCode } from '../../core/errors';
import { _assert } from '../../core/util/assert';
import { AuthInternal } from '../../model/auth';
import { Parameters, Recaptcha } from './recaptcha';
export const _SOLVE_TIME_MS = 500;
export const _EXPIRATION_TIME_MS = 60_000;
export const _WIDGET_ID_START = 1_000_000_000_000;
export interface Widget {
getResponse: () => string | null;
delete: () => void;
execute: () => void;
}
export class MockReCaptcha implements Recaptcha {
private counter = _WIDGET_ID_START;
_widgets = new Map<number, Widget>();
constructor(private readonly auth: AuthInternal) {}
render(container: string | HTMLElement, parameters?: Parameters): number {
const id = this.counter;
this._widgets.set(
id,
new MockWidget(container, this.auth.name, parameters || {})
);
this.counter++;
return id;
}
reset(optWidgetId?: number): void {
const id = optWidgetId || _WIDGET_ID_START;
void this._widgets.get(id)?.delete();
this._widgets.delete(id);
}
getResponse(optWidgetId?: number): string {
const id = optWidgetId || _WIDGET_ID_START;
return this._widgets.get(id)?.getResponse() || '';
}
async execute(optWidgetId?: number | string): Promise<string> {
const id: number = (optWidgetId as number) || _WIDGET_ID_START;
void this._widgets.get(id)?.execute();
return '';
}
}
export class MockWidget {
private readonly container: HTMLElement;
private readonly isVisible: boolean;
private timerId: number | null = null;
private deleted = false;
private responseToken: string | null = null;
private readonly clickHandler = (): void => {
this.execute();
};
constructor(
containerOrId: string | HTMLElement,
appName: string,
private readonly params: Parameters
) {
const container =
typeof containerOrId === 'string'
? document.getElementById(containerOrId)
: containerOrId;
_assert(container, AuthErrorCode.ARGUMENT_ERROR, { appName });
this.container = container;
this.isVisible = this.params.size !== 'invisible';
if (this.isVisible) {
this.execute();
} else {
this.container.addEventListener('click', this.clickHandler);
}
}
getResponse(): string | null {
this.checkIfDeleted();
return this.responseToken;
}
delete(): void {
this.checkIfDeleted();
this.deleted = true;
if (this.timerId) {
clearTimeout(this.timerId);
this.timerId = null;
}
this.container.removeEventListener('click', this.clickHandler);
}
execute(): void {
this.checkIfDeleted();
if (this.timerId) {
return;
}
this.timerId = window.setTimeout(() => {
this.responseToken = generateRandomAlphaNumericString(50);
const { callback, 'expired-callback': expiredCallback } = this.params;
if (callback) {
try {
callback(this.responseToken);
} catch (e) {}
}
this.timerId = window.setTimeout(() => {
this.timerId = null;
this.responseToken = null;
if (expiredCallback) {
try {
expiredCallback();
} catch (e) {}
}
if (this.isVisible) {
this.execute();
}
}, _EXPIRATION_TIME_MS);
}, _SOLVE_TIME_MS);
}
private checkIfDeleted(): void {
if (this.deleted) {
throw new Error('reCAPTCHA mock was already deleted!');
}
}
}
function generateRandomAlphaNumericString(len: number): string {
const chars = [];
const allowedChars =
'1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
for (let i = 0; i < len; i++) {
chars.push(
allowedChars.charAt(Math.floor(Math.random() * allowedChars.length))
);
}
return chars.join('');
}