/*
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
*/
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/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(
operation: Op,
payload?: Partial>,
response?: WalletCoreResponseType,
callback?: () => void,
): MockHandler;
getCallingQueueState(): "empty" | string;
notifyEventFromWallet(event: NotificationType): void;
}
type CallRecord = WalletCallRecord | BackgroundCallRecord;
interface WalletCallRecord {
source: "wallet";
callback: () => void;
operation: WalletCoreOpKeys;
payload?: WalletCoreRequestType;
response?: WalletCoreResponseType;
}
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();
const subscriptions: Subscriptions = {};
const mock: typeof wxApi = {
wallet: new Proxy({} 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,
) {
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({} 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 };
}