wallet-core/packages/taler-wallet-webextension/src/test-utils.ts
2023-04-21 10:49:47 -03:00

204 lines
5.7 KiB
TypeScript

/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { NotificationType } from "@gnu-taler/taler-util";
import {
WalletCoreApiClient,
WalletCoreOpKeys,
WalletCoreRequestType,
WalletCoreResponseType,
} from "@gnu-taler/taler-wallet-core";
import { TranslationProvider } from "@gnu-taler/web-util/lib/index.browser";
import {
ComponentChildren,
FunctionalComponent,
VNode,
h as create,
} from "preact";
import { AlertProvider } from "./context/alert.js";
import { BackendProvider } from "./context/backend.js";
import { strings } from "./i18n/strings.js";
import { nullFunction } from "./mui/handlers.js";
import { BackgroundApiClient, wxApi } from "./wxApi.js";
// export const nullFunction: any = () => null;
interface MockHandler {
addWalletCallResponse<Op extends WalletCoreOpKeys>(
operation: Op,
payload?: Partial<WalletCoreRequestType<Op>>,
response?: WalletCoreResponseType<Op>,
callback?: () => void,
): MockHandler;
getCallingQueueState(): "empty" | string;
notifyEventFromWallet(event: NotificationType): void;
}
type CallRecord = WalletCallRecord | BackgroundCallRecord;
interface WalletCallRecord {
source: "wallet";
callback: () => void;
operation: WalletCoreOpKeys;
payload?: WalletCoreRequestType<WalletCoreOpKeys>;
response?: WalletCoreResponseType<WalletCoreOpKeys>;
}
interface BackgroundCallRecord {
source: "background";
name: string;
args: any;
response: any;
}
type Subscriptions = {
[key in NotificationType]?: VoidFunction;
};
export function createWalletApiMock(): {
handler: MockHandler;
TestingContext: FunctionalComponent<{ children: ComponentChildren }>;
} {
const calls = new Array<CallRecord>();
const subscriptions: Subscriptions = {};
const mock: typeof wxApi = {
wallet: new Proxy<WalletCoreApiClient>({} as any, {
get(target, name, receiver) {
const functionName = String(name);
if (functionName !== "call") {
throw Error(
`the only method in wallet api should be 'call': ${functionName}`,
);
}
return function (
operation: WalletCoreOpKeys,
payload: WalletCoreRequestType<WalletCoreOpKeys>,
) {
const next = calls.shift();
if (!next) {
throw Error(
`wallet operation was called but none was expected: ${operation} (${JSON.stringify(
payload,
undefined,
2,
)})`,
);
}
if (next.source !== "wallet") {
throw Error(`wallet operation expected`);
}
if (operation !== next.operation) {
//more checks, deep check payload
throw Error(
`wallet operation doesn't match: expected ${next.operation} actual ${operation}`,
);
}
next.callback();
return next.response ?? {};
};
},
}),
listener: {
onUpdateNotification(
mTypes: NotificationType[],
callback: (() => void) | undefined,
): () => void {
mTypes.forEach((m) => {
subscriptions[m] = callback;
});
return nullFunction;
},
},
background: new Proxy<BackgroundApiClient>({} as any, {
get(target, name, receiver) {
const functionName = String(name);
return function (...args: any) {
const next = calls.shift();
if (!next) {
throw Error(
`background operation was called but none was expected: ${functionName} (${JSON.stringify(
args,
undefined,
2,
)})`,
);
}
if (next.source !== "background" || functionName !== next.name) {
//more checks, deep check args
throw Error(`background operation doesn't match`);
}
return next.response;
};
},
}),
};
const handler: MockHandler = {
addWalletCallResponse(operation, payload, response, cb) {
calls.push({
source: "wallet",
operation,
payload,
response,
callback: cb
? cb
: () => {
null;
},
});
return handler;
},
notifyEventFromWallet(event: NotificationType): void {
const callback = subscriptions[event];
if (!callback)
throw Error(`Expected to have a subscription for ${event}`);
return callback();
},
getCallingQueueState() {
return calls.length === 0 ? "empty" : `${calls.length} left`;
},
};
function TestingContext({
children: _cs,
}: {
children: ComponentChildren;
}): VNode {
let children = _cs;
children = create(AlertProvider, { children }, children);
children = create(
TranslationProvider,
{ children, source: strings, initial: "en", forceLang: "en" },
children,
);
return create(
BackendProvider,
{
wallet: mock.wallet,
background: mock.background,
listener: mock.listener,
children,
},
children,
);
}
return { handler, TestingContext };
}