aboutsummaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/tests/hooks
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-10-24 10:46:14 +0200
committerFlorian Dold <florian@dold.me>2022-10-24 10:46:14 +0200
commit3e060b80428943c6562250a6ff77eff10a0259b7 (patch)
treed08472bc5ca28621c62ac45b229207d8215a9ea7 /packages/merchant-backoffice-ui/tests/hooks
parentfb52ced35ac872349b0e1062532313662552ff6c (diff)
downloadwallet-core-3e060b80428943c6562250a6ff77eff10a0259b7.tar.xz
repo: integrate packages from former merchant-backoffice.git
Diffstat (limited to 'packages/merchant-backoffice-ui/tests/hooks')
-rw-r--r--packages/merchant-backoffice-ui/tests/hooks/async.test.ts158
-rw-r--r--packages/merchant-backoffice-ui/tests/hooks/listener.test.ts62
-rw-r--r--packages/merchant-backoffice-ui/tests/hooks/notification.test.ts51
-rw-r--r--packages/merchant-backoffice-ui/tests/hooks/swr/index.tsx45
-rw-r--r--packages/merchant-backoffice-ui/tests/hooks/swr/instance.test.ts636
-rw-r--r--packages/merchant-backoffice-ui/tests/hooks/swr/order.test.ts567
-rw-r--r--packages/merchant-backoffice-ui/tests/hooks/swr/product.test.ts338
-rw-r--r--packages/merchant-backoffice-ui/tests/hooks/swr/reserve.test.ts470
-rw-r--r--packages/merchant-backoffice-ui/tests/hooks/swr/transfer.test.ts268
9 files changed, 2595 insertions, 0 deletions
diff --git a/packages/merchant-backoffice-ui/tests/hooks/async.test.ts b/packages/merchant-backoffice-ui/tests/hooks/async.test.ts
new file mode 100644
index 000000000..a6d0cddfa
--- /dev/null
+++ b/packages/merchant-backoffice-ui/tests/hooks/async.test.ts
@@ -0,0 +1,158 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { renderHook } from "@testing-library/preact-hooks"
+import { useAsync } from "../../src/hooks/async"
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+test("async function is called", async () => {
+ jest.useFakeTimers()
+
+ const timeout = 500
+
+ const asyncFunction = jest.fn(() => new Promise((res) => {
+ setTimeout(() => {
+ res({ the_answer: 'yes' })
+ }, timeout);
+ }))
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ return useAsync(asyncFunction)
+ })
+
+ expect(result.current?.isLoading).toBeFalsy()
+
+ result.current?.request()
+ expect(asyncFunction).toBeCalled()
+ await waitForNextUpdate({ timeout: 1 })
+ expect(result.current?.isLoading).toBeTruthy()
+
+ jest.advanceTimersByTime(timeout + 1)
+ await waitForNextUpdate({ timeout: 1 })
+ expect(result.current?.isLoading).toBeFalsy()
+ expect(result.current?.data).toMatchObject({ the_answer: 'yes' })
+ expect(result.current?.error).toBeUndefined()
+ expect(result.current?.isSlow).toBeFalsy()
+})
+
+test("async function return error if rejected", async () => {
+ jest.useFakeTimers()
+
+ const timeout = 500
+
+ const asyncFunction = jest.fn(() => new Promise((_, rej) => {
+ setTimeout(() => {
+ rej({ the_error: 'yes' })
+ }, timeout);
+ }))
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ return useAsync(asyncFunction)
+ })
+
+ expect(result.current?.isLoading).toBeFalsy()
+
+ result.current?.request()
+ expect(asyncFunction).toBeCalled()
+ await waitForNextUpdate({ timeout: 1 })
+ expect(result.current?.isLoading).toBeTruthy()
+
+ jest.advanceTimersByTime(timeout + 1)
+ await waitForNextUpdate({ timeout: 1 })
+ expect(result.current?.isLoading).toBeFalsy()
+ expect(result.current?.error).toMatchObject({ the_error: 'yes' })
+ expect(result.current?.data).toBeUndefined()
+ expect(result.current?.isSlow).toBeFalsy()
+})
+
+test("async function is slow", async () => {
+ jest.useFakeTimers()
+
+ const timeout = 2200
+
+ const asyncFunction = jest.fn(() => new Promise((res) => {
+ setTimeout(() => {
+ res({ the_answer: 'yes' })
+ }, timeout);
+ }))
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ return useAsync(asyncFunction)
+ })
+
+ expect(result.current?.isLoading).toBeFalsy()
+
+ result.current?.request()
+ expect(asyncFunction).toBeCalled()
+ await waitForNextUpdate({ timeout: 1 })
+ expect(result.current?.isLoading).toBeTruthy()
+
+ jest.advanceTimersByTime(timeout / 2)
+ await waitForNextUpdate({ timeout: 1 })
+ expect(result.current?.isLoading).toBeTruthy()
+ expect(result.current?.isSlow).toBeTruthy()
+ expect(result.current?.data).toBeUndefined()
+ expect(result.current?.error).toBeUndefined()
+
+ jest.advanceTimersByTime(timeout / 2)
+ await waitForNextUpdate({ timeout: 1 })
+ expect(result.current?.isLoading).toBeFalsy()
+ expect(result.current?.data).toMatchObject({ the_answer: 'yes' })
+ expect(result.current?.error).toBeUndefined()
+ expect(result.current?.isSlow).toBeFalsy()
+
+})
+
+test("async function is cancellable", async () => {
+ jest.useFakeTimers()
+
+ const timeout = 2200
+
+ const asyncFunction = jest.fn(() => new Promise((res) => {
+ setTimeout(() => {
+ res({ the_answer: 'yes' })
+ }, timeout);
+ }))
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ return useAsync(asyncFunction)
+ })
+
+ expect(result.current?.isLoading).toBeFalsy()
+
+ result.current?.request()
+ expect(asyncFunction).toBeCalled()
+ await waitForNextUpdate({ timeout: 1 })
+ expect(result.current?.isLoading).toBeTruthy()
+
+ jest.advanceTimersByTime(timeout / 2)
+ await waitForNextUpdate({ timeout: 1 })
+ expect(result.current?.isLoading).toBeTruthy()
+ expect(result.current?.isSlow).toBeTruthy()
+ expect(result.current?.data).toBeUndefined()
+ expect(result.current?.error).toBeUndefined()
+
+ result.current?.cancel()
+ await waitForNextUpdate({ timeout: 1 })
+ expect(result.current?.isLoading).toBeFalsy()
+ expect(result.current?.data).toBeUndefined()
+ expect(result.current?.error).toBeUndefined()
+ expect(result.current?.isSlow).toBeFalsy()
+
+})
diff --git a/packages/merchant-backoffice-ui/tests/hooks/listener.test.ts b/packages/merchant-backoffice-ui/tests/hooks/listener.test.ts
new file mode 100644
index 000000000..ae34c1339
--- /dev/null
+++ b/packages/merchant-backoffice-ui/tests/hooks/listener.test.ts
@@ -0,0 +1,62 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { renderHook, act } from '@testing-library/preact-hooks';
+import { useListener } from '../../src/hooks/listener';
+
+// jest.useFakeTimers()
+
+test('listener', async () => {
+
+
+ function createSomeString() {
+ return "hello"
+ }
+ async function addWorldToTheEnd(resultFromComponentB: string) {
+ return `${resultFromComponentB} world`
+ }
+ const expectedResult = "hello world"
+
+ const { result } = renderHook(() => useListener(addWorldToTheEnd))
+
+ if (!result.current) {
+ expect(result.current).toBeDefined()
+ return;
+ }
+
+ {
+ const [activator, subscriber] = result.current
+ expect(activator).toBeUndefined()
+
+ act(() => {
+ subscriber(createSomeString)
+ })
+
+ }
+
+ const [activator] = result.current
+ expect(activator).toBeDefined()
+ if (!activator) return;
+
+ const response = await activator()
+ expect(response).toBe(expectedResult)
+
+});
diff --git a/packages/merchant-backoffice-ui/tests/hooks/notification.test.ts b/packages/merchant-backoffice-ui/tests/hooks/notification.test.ts
new file mode 100644
index 000000000..6825a825a
--- /dev/null
+++ b/packages/merchant-backoffice-ui/tests/hooks/notification.test.ts
@@ -0,0 +1,51 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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/>
+ */
+
+ /**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { renderHook, act} from '@testing-library/preact-hooks';
+import { useNotifications } from '../../src/hooks/notifications';
+
+jest.useFakeTimers()
+
+test('notification should disapear after timeout', () => {
+ jest.spyOn(global, 'setTimeout');
+
+ const timeout = 1000
+ const { result, rerender } = renderHook(() => useNotifications(undefined, timeout));
+
+ expect(result.current?.notifications.length).toBe(0);
+
+ act(() => {
+ result.current?.pushNotification({
+ message: 'some_id',
+ type: 'INFO'
+ });
+ });
+ expect(result.current?.notifications.length).toBe(1);
+
+ jest.advanceTimersByTime(timeout/2);
+ rerender()
+ expect(result.current?.notifications.length).toBe(1);
+
+ jest.advanceTimersByTime(timeout);
+ rerender()
+ expect(result.current?.notifications.length).toBe(0);
+
+});
diff --git a/packages/merchant-backoffice-ui/tests/hooks/swr/index.tsx b/packages/merchant-backoffice-ui/tests/hooks/swr/index.tsx
new file mode 100644
index 000000000..44514855d
--- /dev/null
+++ b/packages/merchant-backoffice-ui/tests/hooks/swr/index.tsx
@@ -0,0 +1,45 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { ComponentChildren, h, VNode } from "preact";
+import { SWRConfig } from "swr";
+import { BackendContextProvider } from "../../../src/context/backend";
+import { InstanceContextProvider } from "../../../src/context/instance";
+
+interface TestingContextProps {
+ children?: ComponentChildren;
+}
+export function TestingContext({ children }: TestingContextProps): VNode {
+ return (
+ <BackendContextProvider defaultUrl="http://backend" initialToken="token">
+ <InstanceContextProvider
+ value={{
+ token: "token",
+ id: "default",
+ admin: true,
+ changeToken: () => null,
+ }}
+ >
+ <SWRConfig value={{ provider: () => new Map() }}>{children}</SWRConfig>
+ </InstanceContextProvider>
+ </BackendContextProvider>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/tests/hooks/swr/instance.test.ts b/packages/merchant-backoffice-ui/tests/hooks/swr/instance.test.ts
new file mode 100644
index 000000000..55d9fa6ee
--- /dev/null
+++ b/packages/merchant-backoffice-ui/tests/hooks/swr/instance.test.ts
@@ -0,0 +1,636 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { renderHook } from "@testing-library/preact-hooks";
+import { act } from "preact/test-utils";
+import { MerchantBackend } from "../../../src/declaration";
+import { useAdminAPI, useBackendInstances, useInstanceAPI, useInstanceDetails, useManagementAPI } from "../../../src/hooks/instance";
+import {
+ API_CREATE_INSTANCE,
+ API_DELETE_INSTANCE,
+ API_GET_CURRENT_INSTANCE,
+ API_LIST_INSTANCES,
+ API_UPDATE_CURRENT_INSTANCE,
+ API_UPDATE_CURRENT_INSTANCE_AUTH,
+ API_UPDATE_INSTANCE_AUTH_BY_ID,
+ API_UPDATE_INSTANCE_BY_ID,
+ assertJustExpectedRequestWereMade,
+ AxiosMockEnvironment
+} from "../../axiosMock";
+import { TestingContext } from "./index";
+
+describe("instance api interaction with details ", () => {
+
+ it("should evict cache when updating an instance", async () => {
+
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_GET_CURRENT_INSTANCE, {
+ response: {
+ name: 'instance_name'
+ } as MerchantBackend.Instances.QueryInstancesResponse,
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ const api = useInstanceAPI();
+ const query = useInstanceDetails();
+
+ return { query, api };
+ },
+ { wrapper: TestingContext }
+ );
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ name: 'instance_name'
+ });
+
+ env.addRequestExpectation(API_UPDATE_CURRENT_INSTANCE, {
+ request: {
+ name: 'other_name'
+ } as MerchantBackend.Instances.InstanceReconfigurationMessage,
+ });
+
+ act(async () => {
+ await result.current?.api.updateInstance({
+ name: 'other_name'
+ } as MerchantBackend.Instances.InstanceReconfigurationMessage);
+ });
+
+ assertJustExpectedRequestWereMade(env);
+
+ env.addRequestExpectation(API_GET_CURRENT_INSTANCE, {
+ response: {
+ name: 'other_name'
+ } as MerchantBackend.Instances.QueryInstancesResponse,
+ });
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+
+ expect(result.current.query.data).toEqual({
+ name: 'other_name'
+ });
+ });
+
+ it("should evict cache when setting the instance's token", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_GET_CURRENT_INSTANCE, {
+ response: {
+ name: 'instance_name',
+ auth: {
+ method: 'token',
+ token: 'not-secret',
+ }
+ } as MerchantBackend.Instances.QueryInstancesResponse,
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ const api = useInstanceAPI();
+ const query = useInstanceDetails();
+
+ return { query, api };
+ },
+ { wrapper: TestingContext }
+ );
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ name: 'instance_name',
+ auth: {
+ method: 'token',
+ token: 'not-secret',
+ }
+ });
+
+ env.addRequestExpectation(API_UPDATE_CURRENT_INSTANCE_AUTH, {
+ request: {
+ method: 'token',
+ token: 'secret'
+ } as MerchantBackend.Instances.InstanceAuthConfigurationMessage,
+ });
+
+ act(async () => {
+ await result.current?.api.setNewToken('secret');
+ });
+
+ assertJustExpectedRequestWereMade(env);
+
+ env.addRequestExpectation(API_GET_CURRENT_INSTANCE, {
+ response: {
+ name: 'instance_name',
+ auth: {
+ method: 'token',
+ token: 'secret',
+ }
+ } as MerchantBackend.Instances.QueryInstancesResponse,
+ });
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+
+ expect(result.current.query.data).toEqual({
+ name: 'instance_name',
+ auth: {
+ method: 'token',
+ token: 'secret',
+ }
+ });
+ });
+
+ it("should evict cache when clearing the instance's token", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_GET_CURRENT_INSTANCE, {
+ response: {
+ name: 'instance_name',
+ auth: {
+ method: 'token',
+ token: 'not-secret',
+ }
+ } as MerchantBackend.Instances.QueryInstancesResponse,
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ const api = useInstanceAPI();
+ const query = useInstanceDetails();
+
+ return { query, api };
+ },
+ { wrapper: TestingContext }
+ );
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ name: 'instance_name',
+ auth: {
+ method: 'token',
+ token: 'not-secret',
+ }
+ });
+
+ env.addRequestExpectation(API_UPDATE_CURRENT_INSTANCE_AUTH, {
+ request: {
+ method: 'external',
+ } as MerchantBackend.Instances.InstanceAuthConfigurationMessage,
+ });
+
+ act(async () => {
+ await result.current?.api.clearToken();
+ });
+
+ assertJustExpectedRequestWereMade(env);
+
+ env.addRequestExpectation(API_GET_CURRENT_INSTANCE, {
+ response: {
+ name: 'instance_name',
+ auth: {
+ method: 'external',
+ }
+ } as MerchantBackend.Instances.QueryInstancesResponse,
+ });
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+
+ expect(result.current.query.data).toEqual({
+ name: 'instance_name',
+ auth: {
+ method: 'external',
+ }
+ });
+ });
+});
+
+describe("instance admin api interaction with listing ", () => {
+
+ it("should evict cache when creating a new instance", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_LIST_INSTANCES, {
+ response: {
+ instances: [{
+ name: 'instance_name'
+ } as MerchantBackend.Instances.Instance]
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ const api = useAdminAPI();
+ const query = useBackendInstances();
+
+ return { query, api };
+ },
+ { wrapper: TestingContext }
+ );
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ instances: [{
+ name: 'instance_name'
+ }]
+ });
+
+ env.addRequestExpectation(API_CREATE_INSTANCE, {
+ request: {
+ name: 'other_name'
+ } as MerchantBackend.Instances.InstanceConfigurationMessage,
+ });
+
+ act(async () => {
+ await result.current?.api.createInstance({
+ name: 'other_name'
+ } as MerchantBackend.Instances.InstanceConfigurationMessage);
+ });
+
+ assertJustExpectedRequestWereMade(env);
+
+ env.addRequestExpectation(API_LIST_INSTANCES, {
+ response: {
+ instances: [{
+ name: 'instance_name'
+ } as MerchantBackend.Instances.Instance,
+ {
+ name: 'other_name'
+ } as MerchantBackend.Instances.Instance]
+ },
+ });
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+
+ expect(result.current.query.data).toEqual({
+ instances: [{
+ name: 'instance_name'
+ }, {
+ name: 'other_name'
+ }]
+ });
+ });
+
+ it("should evict cache when deleting an instance", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_LIST_INSTANCES, {
+ response: {
+ instances: [{
+ id: 'default',
+ name: 'instance_name'
+ } as MerchantBackend.Instances.Instance,
+ {
+ id: 'the_id',
+ name: 'second_instance'
+ } as MerchantBackend.Instances.Instance]
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ const api = useAdminAPI();
+ const query = useBackendInstances();
+
+ return { query, api };
+ },
+ { wrapper: TestingContext }
+ );
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ instances: [{
+ id: 'default',
+ name: 'instance_name'
+ }, {
+ id: 'the_id',
+ name: 'second_instance'
+ }]
+ });
+
+ env.addRequestExpectation(API_DELETE_INSTANCE('the_id'), {});
+
+ act(async () => {
+ await result.current?.api.deleteInstance('the_id');
+ });
+
+ assertJustExpectedRequestWereMade(env);
+
+ env.addRequestExpectation(API_LIST_INSTANCES, {
+ response: {
+ instances: [{
+ id: 'default',
+ name: 'instance_name'
+ } as MerchantBackend.Instances.Instance]
+ },
+ });
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+
+ expect(result.current.query.data).toEqual({
+ instances: [{
+ id: 'default',
+ name: 'instance_name'
+ }]
+ });
+ });
+ it("should evict cache when deleting (purge) an instance", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_LIST_INSTANCES, {
+ response: {
+ instances: [{
+ id: 'default',
+ name: 'instance_name'
+ } as MerchantBackend.Instances.Instance,
+ {
+ id: 'the_id',
+ name: 'second_instance'
+ } as MerchantBackend.Instances.Instance]
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ const api = useAdminAPI();
+ const query = useBackendInstances();
+
+ return { query, api };
+ },
+ { wrapper: TestingContext }
+ );
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ instances: [{
+ id: 'default',
+ name: 'instance_name'
+ }, {
+ id: 'the_id',
+ name: 'second_instance'
+ }]
+ });
+
+ env.addRequestExpectation(API_DELETE_INSTANCE('the_id'), {
+ qparam: {
+ purge: 'YES'
+ }
+ });
+
+ act(async () => {
+ await result.current?.api.purgeInstance('the_id');
+ });
+
+ assertJustExpectedRequestWereMade(env);
+
+ env.addRequestExpectation(API_LIST_INSTANCES, {
+ response: {
+ instances: [{
+ id: 'default',
+ name: 'instance_name'
+ } as MerchantBackend.Instances.Instance]
+ },
+ });
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+
+ expect(result.current.query.data).toEqual({
+ instances: [{
+ id: 'default',
+ name: 'instance_name'
+ }]
+ });
+ });
+});
+
+describe("instance management api interaction with listing ", () => {
+
+ it("should evict cache when updating an instance", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_LIST_INSTANCES, {
+ response: {
+ instances: [{
+ id: 'managed',
+ name: 'instance_name'
+ } as MerchantBackend.Instances.Instance]
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ const api = useManagementAPI('managed');
+ const query = useBackendInstances();
+
+ return { query, api };
+ },
+ { wrapper: TestingContext }
+ );
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ instances: [{
+ id: 'managed',
+ name: 'instance_name'
+ }]
+ });
+
+ env.addRequestExpectation(API_UPDATE_INSTANCE_BY_ID('managed'), {
+ request: {
+ name: 'other_name'
+ } as MerchantBackend.Instances.InstanceReconfigurationMessage,
+ });
+
+ act(async () => {
+ await result.current?.api.updateInstance({
+ name: 'other_name'
+ } as MerchantBackend.Instances.InstanceConfigurationMessage);
+ });
+
+ assertJustExpectedRequestWereMade(env);
+
+ env.addRequestExpectation(API_LIST_INSTANCES, {
+ response: {
+ instances: [
+ {
+ id: 'managed',
+ name: 'other_name'
+ } as MerchantBackend.Instances.Instance]
+ },
+ });
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+
+ expect(result.current.query.data).toEqual({
+ instances: [{
+ id: 'managed',
+ name: 'other_name'
+ }]
+ });
+ });
+
+});
+
diff --git a/packages/merchant-backoffice-ui/tests/hooks/swr/order.test.ts b/packages/merchant-backoffice-ui/tests/hooks/swr/order.test.ts
new file mode 100644
index 000000000..e7f6c9334
--- /dev/null
+++ b/packages/merchant-backoffice-ui/tests/hooks/swr/order.test.ts
@@ -0,0 +1,567 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { renderHook } from "@testing-library/preact-hooks";
+import { act } from "preact/test-utils";
+import { TestingContext } from ".";
+import { MerchantBackend } from "../../../src/declaration";
+import { useInstanceOrders, useOrderAPI, useOrderDetails } from "../../../src/hooks/order";
+import {
+ API_CREATE_ORDER,
+ API_DELETE_ORDER,
+ API_FORGET_ORDER_BY_ID,
+ API_GET_ORDER_BY_ID,
+ API_LIST_ORDERS, API_REFUND_ORDER_BY_ID, assertJustExpectedRequestWereMade, assertNextRequest, assertNoMoreRequestWereMade, AxiosMockEnvironment
+} from "../../axiosMock";
+
+describe("order api interaction with listing", () => {
+
+ it("should evict cache when creating an order", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: 0, paid: "yes" },
+ response: {
+ orders: [{ order_id: "1" } as MerchantBackend.Orders.OrderHistoryEntry],
+ },
+ });
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: -20, paid: "yes" },
+ response: {
+ orders: [{ order_id: "2" } as MerchantBackend.Orders.OrderHistoryEntry],
+ },
+ });
+
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ const newDate = (d: Date) => {
+ console.log("new date", d);
+ };
+ const query = useInstanceOrders({ paid: "yes" }, newDate);
+ const api = useOrderAPI();
+
+ return { query, api };
+ }, { wrapper: TestingContext });
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+
+ expect(result.current.query.loading).toBeTruthy();
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ orders: [{ order_id: "1" }, { order_id: "2" }],
+ });
+
+ env.addRequestExpectation(API_CREATE_ORDER, {
+ request: {
+ order: { amount: "ARS:12", summary: "pay me" },
+ },
+ response: { order_id: "3" },
+ });
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: 0, paid: "yes" },
+ response: {
+ orders: [{ order_id: "1" } as any],
+ },
+ });
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: -20, paid: "yes" },
+ response: {
+ orders: [{ order_id: "2" } as any, { order_id: "3" } as any],
+ },
+ });
+
+ act(async () => {
+ await result.current?.api.createOrder({
+ order: { amount: "ARS:12", summary: "pay me" },
+ } as any);
+ });
+
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ orders: [{ order_id: "1" }, { order_id: "2" }, { order_id: "3" }],
+ });
+ });
+ it("should evict cache when doing a refund", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: 0, paid: "yes" },
+ response: {
+ orders: [{ order_id: "1", amount: 'EUR:12', refundable: true } as MerchantBackend.Orders.OrderHistoryEntry],
+ },
+ });
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: -20, paid: "yes" },
+ response: { orders: [], },
+ });
+
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ const newDate = (d: Date) => {
+ console.log("new date", d);
+ };
+ const query = useInstanceOrders({ paid: "yes" }, newDate);
+ const api = useOrderAPI();
+
+ return { query, api };
+ }, { wrapper: TestingContext });
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+
+ expect(result.current.query.loading).toBeTruthy();
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ orders: [{
+ order_id: "1",
+ amount: 'EUR:12',
+ refundable: true,
+ }],
+ });
+
+ env.addRequestExpectation(API_REFUND_ORDER_BY_ID('1'), {
+ request: {
+ reason: 'double pay',
+ refund: 'EUR:1'
+ },
+ });
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: 0, paid: "yes" },
+ response: {
+ orders: [{ order_id: "1", amount: 'EUR:12', refundable: false } as any],
+ },
+ });
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: -20, paid: "yes" },
+ response: { orders: [], },
+ });
+
+ act(async () => {
+ await result.current?.api.refundOrder('1', {
+ reason: 'double pay',
+ refund: 'EUR:1'
+ });
+ });
+
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ orders: [{
+ order_id: "1",
+ amount: 'EUR:12',
+ refundable: false,
+ }],
+ });
+ });
+ it("should evict cache when deleting an order", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: 0, paid: "yes" },
+ response: {
+ orders: [{ order_id: "1" } as MerchantBackend.Orders.OrderHistoryEntry],
+ },
+ });
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: -20, paid: "yes" },
+ response: {
+ orders: [{ order_id: "2" } as MerchantBackend.Orders.OrderHistoryEntry],
+ },
+ });
+
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ const newDate = (d: Date) => {
+ console.log("new date", d);
+ };
+ const query = useInstanceOrders({ paid: "yes" }, newDate);
+ const api = useOrderAPI();
+
+ return { query, api };
+ }, { wrapper: TestingContext });
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+
+ expect(result.current.query.loading).toBeTruthy();
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ orders: [{ order_id: "1" }, { order_id: "2" }],
+ });
+
+ env.addRequestExpectation(API_DELETE_ORDER('1'), {});
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: 0, paid: "yes" },
+ response: {
+ orders: [],
+ },
+ });
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: -20, paid: "yes" },
+ response: {
+ orders: [{ order_id: "2" } as any],
+ },
+ });
+
+ act(async () => {
+ await result.current?.api.deleteOrder('1');
+ });
+
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ orders: [{ order_id: "2" }],
+ });
+ });
+
+});
+
+describe("order api interaction with details", () => {
+
+ it("should evict cache when doing a refund", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_GET_ORDER_BY_ID('1'), {
+ // qparam: { delta: 0, paid: "yes" },
+ response: {
+ summary: 'description',
+ refund_amount: 'EUR:0',
+ } as unknown as MerchantBackend.Orders.CheckPaymentPaidResponse,
+ });
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ const query = useOrderDetails('1')
+ const api = useOrderAPI();
+
+ return { query, api };
+ }, { wrapper: TestingContext });
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+
+ expect(result.current.query.loading).toBeTruthy();
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ summary: 'description',
+ refund_amount: 'EUR:0',
+ });
+
+ env.addRequestExpectation(API_REFUND_ORDER_BY_ID('1'), {
+ request: {
+ reason: 'double pay',
+ refund: 'EUR:1'
+ },
+ });
+
+ env.addRequestExpectation(API_GET_ORDER_BY_ID('1'), {
+ response: {
+ summary: 'description',
+ refund_amount: 'EUR:1',
+ } as unknown as MerchantBackend.Orders.CheckPaymentPaidResponse,
+ });
+
+ act(async () => {
+ await result.current?.api.refundOrder('1', {
+ reason: 'double pay',
+ refund: 'EUR:1'
+ });
+ });
+
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ summary: 'description',
+ refund_amount: 'EUR:1',
+ });
+ })
+ it("should evict cache when doing a forget", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_GET_ORDER_BY_ID('1'), {
+ // qparam: { delta: 0, paid: "yes" },
+ response: {
+ summary: 'description',
+ refund_amount: 'EUR:0',
+ } as unknown as MerchantBackend.Orders.CheckPaymentPaidResponse,
+ });
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ const query = useOrderDetails('1')
+ const api = useOrderAPI();
+
+ return { query, api };
+ }, { wrapper: TestingContext });
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+
+ expect(result.current.query.loading).toBeTruthy();
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ summary: 'description',
+ refund_amount: 'EUR:0',
+ });
+
+ env.addRequestExpectation(API_FORGET_ORDER_BY_ID('1'), {
+ request: {
+ fields: ['$.summary']
+ },
+ });
+
+ env.addRequestExpectation(API_GET_ORDER_BY_ID('1'), {
+ response: {
+ summary: undefined,
+ } as unknown as MerchantBackend.Orders.CheckPaymentPaidResponse,
+ });
+
+ act(async () => {
+ await result.current?.api.forgetOrder('1', {
+ fields: ['$.summary']
+ });
+ });
+
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ summary: undefined,
+ });
+ })
+})
+
+describe("order listing pagination", () => {
+
+ it("should not load more if has reach the end", async () => {
+ const env = new AxiosMockEnvironment();
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: 20, wired: "yes", date_ms: 12 },
+ response: {
+ orders: [{ order_id: "1" } as any],
+ },
+ });
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: -20, wired: "yes", date_ms: 13 },
+ response: {
+ orders: [{ order_id: "2" } as any],
+ },
+ });
+
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ const newDate = (d: Date) => {
+ console.log("new date", d);
+ };
+ const date = new Date(12);
+ const query = useInstanceOrders({ wired: "yes", date }, newDate)
+ return { query }
+ }, { wrapper: TestingContext });
+
+ assertJustExpectedRequestWereMade(env);
+
+ await waitForNextUpdate();
+
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ orders: [{ order_id: "1" }, { order_id: "2" }],
+ });
+
+ expect(result.current.query.isReachingEnd).toBeTruthy()
+ expect(result.current.query.isReachingStart).toBeTruthy()
+
+ await act(() => {
+ if (!result.current?.query.ok) throw Error("not ok");
+ result.current.query.loadMore();
+ });
+ assertNoMoreRequestWereMade(env);
+
+ await act(() => {
+ if (!result.current?.query.ok) throw Error("not ok");
+ result.current.query.loadMorePrev();
+ });
+ assertNoMoreRequestWereMade(env);
+
+ expect(result.current.query.data).toEqual({
+ orders: [
+ { order_id: "1" },
+ { order_id: "2" },
+ ],
+ });
+ });
+
+ it("should load more if result brings more that PAGE_SIZE", async () => {
+ const env = new AxiosMockEnvironment();
+
+ const ordersFrom0to20 = Array.from({ length: 20 }).map((e, i) => ({ order_id: String(i) }))
+ const ordersFrom20to40 = Array.from({ length: 20 }).map((e, i) => ({ order_id: String(i + 20) }))
+ const ordersFrom20to0 = [...ordersFrom0to20].reverse()
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: 20, wired: "yes", date_ms: 12 },
+ response: {
+ orders: ordersFrom0to20,
+ },
+ });
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: -20, wired: "yes", date_ms: 13 },
+ response: {
+ orders: ordersFrom20to40,
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ const newDate = (d: Date) => {
+ console.log("new date", d);
+ };
+ const date = new Date(12);
+ const query = useInstanceOrders({ wired: "yes", date }, newDate)
+ return { query }
+ }, { wrapper: TestingContext });
+
+ assertJustExpectedRequestWereMade(env);
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ orders: [...ordersFrom20to0, ...ordersFrom20to40],
+ });
+
+ expect(result.current.query.isReachingEnd).toBeFalsy()
+ expect(result.current.query.isReachingStart).toBeFalsy()
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: -40, wired: "yes", date_ms: 13 },
+ response: {
+ orders: [...ordersFrom20to40, { order_id: '41' }],
+ },
+ });
+
+ await act(() => {
+ if (!result.current?.query.ok) throw Error("not ok");
+ result.current.query.loadMore();
+ });
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ env.addRequestExpectation(API_LIST_ORDERS, {
+ qparam: { delta: 40, wired: "yes", date_ms: 12 },
+ response: {
+ orders: [...ordersFrom0to20, { order_id: '-1' }],
+ },
+ });
+
+ await act(() => {
+ if (!result.current?.query.ok) throw Error("not ok");
+ result.current.query.loadMorePrev();
+ });
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.data).toEqual({
+ orders: [{ order_id: '-1' }, ...ordersFrom20to0, ...ordersFrom20to40, { order_id: '41' }],
+ });
+ });
+
+
+});
diff --git a/packages/merchant-backoffice-ui/tests/hooks/swr/product.test.ts b/packages/merchant-backoffice-ui/tests/hooks/swr/product.test.ts
new file mode 100644
index 000000000..5d39a7c47
--- /dev/null
+++ b/packages/merchant-backoffice-ui/tests/hooks/swr/product.test.ts
@@ -0,0 +1,338 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { renderHook } from "@testing-library/preact-hooks";
+import { act } from "preact/test-utils";
+import { TestingContext } from ".";
+import { MerchantBackend } from "../../../src/declaration";
+import { useInstanceProducts, useProductAPI, useProductDetails } from "../../../src/hooks/product";
+import {
+ API_CREATE_PRODUCT,
+ API_DELETE_PRODUCT, API_GET_PRODUCT_BY_ID,
+ API_LIST_PRODUCTS,
+ API_UPDATE_PRODUCT_BY_ID,
+ assertJustExpectedRequestWereMade,
+ assertNextRequest,
+ AxiosMockEnvironment
+} from "../../axiosMock";
+
+describe("product api interaction with listing ", () => {
+ it("should evict cache when creating a product", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_LIST_PRODUCTS, {
+ response: {
+ products: [{ product_id: "1234" }],
+ },
+ });
+ env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
+ response: { price: "ARS:12" } as MerchantBackend.Products.ProductDetail,
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ const query = useInstanceProducts();
+ const api = useProductAPI();
+ return { api, query };
+ },
+ { wrapper: TestingContext }
+ ); // get products -> loading
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+ await waitForNextUpdate({ timeout: 1 });
+
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual([
+ { id: "1234", price: "ARS:12" },
+ ]);
+
+ env.addRequestExpectation(API_CREATE_PRODUCT, {
+ request: { price: "ARS:23" } as MerchantBackend.Products.ProductAddDetail,
+ });
+
+ env.addRequestExpectation(API_LIST_PRODUCTS, {
+ response: {
+ products: [{ product_id: "1234" }, { product_id: "2345" }],
+ },
+ });
+ env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
+ response: { price: "ARS:12" } as MerchantBackend.Products.ProductDetail,
+ });
+ env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
+ response: { price: "ARS:12" } as MerchantBackend.Products.ProductDetail,
+ });
+ env.addRequestExpectation(API_GET_PRODUCT_BY_ID("2345"), {
+ response: { price: "ARS:23" } as MerchantBackend.Products.ProductDetail,
+ });
+
+ act(async () => {
+ await result.current?.api.createProduct({
+ price: "ARS:23",
+ } as any);
+ });
+
+ assertNextRequest(env);
+ await waitForNextUpdate({ timeout: 1 });
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual([
+ {
+ id: "1234",
+ price: "ARS:12",
+ },
+ {
+ id: "2345",
+ price: "ARS:23",
+ },
+ ]);
+ });
+
+ it("should evict cache when updating a product", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_LIST_PRODUCTS, {
+ response: {
+ products: [{ product_id: "1234" }],
+ },
+ });
+ env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
+ response: { price: "ARS:12" } as MerchantBackend.Products.ProductDetail,
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ const query = useInstanceProducts();
+ const api = useProductAPI();
+ return { api, query };
+ },
+ { wrapper: TestingContext }
+ ); // get products -> loading
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+ await waitForNextUpdate({ timeout: 1 });
+
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual([
+ { id: "1234", price: "ARS:12" },
+ ]);
+
+ env.addRequestExpectation(API_UPDATE_PRODUCT_BY_ID("1234"), {
+ request: { price: "ARS:13" } as MerchantBackend.Products.ProductPatchDetail,
+ });
+
+ env.addRequestExpectation(API_LIST_PRODUCTS, {
+ response: {
+ products: [{ product_id: "1234" }],
+ },
+ });
+ env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
+ response: { price: "ARS:13" } as MerchantBackend.Products.ProductDetail,
+ });
+
+ act(async () => {
+ await result.current?.api.updateProduct("1234", {
+ price: "ARS:13",
+ } as any);
+ });
+
+ assertNextRequest(env);
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual([
+ {
+ id: "1234",
+ price: "ARS:13",
+ },
+ ]);
+ });
+
+ it("should evict cache when deleting a product", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_LIST_PRODUCTS, {
+ response: {
+ products: [{ product_id: "1234" }, { product_id: "2345" }],
+ },
+ });
+ env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
+ response: { price: "ARS:12" } as MerchantBackend.Products.ProductDetail,
+ });
+ env.addRequestExpectation(API_GET_PRODUCT_BY_ID("2345"), {
+ response: { price: "ARS:23" } as MerchantBackend.Products.ProductDetail,
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ const query = useInstanceProducts();
+ const api = useProductAPI();
+ return { api, query };
+ },
+ { wrapper: TestingContext }
+ ); // get products -> loading
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+ await waitForNextUpdate({ timeout: 1 });
+
+ await waitForNextUpdate({ timeout: 1 });
+ // await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual([
+ { id: "1234", price: "ARS:12" },
+ { id: "2345", price: "ARS:23" },
+ ]);
+
+ env.addRequestExpectation(API_DELETE_PRODUCT("2345"), {});
+
+ env.addRequestExpectation(API_LIST_PRODUCTS, {
+ response: {
+ products: [{ product_id: "1234" }],
+ },
+ });
+ env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
+ response: { price: "ARS:13" } as MerchantBackend.Products.ProductDetail,
+ });
+
+ act(async () => {
+ await result.current?.api.deleteProduct("2345");
+ });
+
+ assertNextRequest(env);
+ await waitForNextUpdate({ timeout: 1 });
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual([
+ {
+ id: "1234",
+ price: "ARS:13",
+ },
+ ]);
+ });
+
+});
+
+describe("product api interaction with details", () => {
+ it("should evict cache when updating a product", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_GET_PRODUCT_BY_ID("12"), {
+ response: {
+ description: "this is a description",
+ } as MerchantBackend.Products.ProductDetail,
+ });
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ const query = useProductDetails("12");
+ const api = useProductAPI();
+ return { query, api };
+ }, { wrapper: TestingContext });
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+ await waitForNextUpdate();
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ description: "this is a description",
+ });
+
+ env.addRequestExpectation(API_UPDATE_PRODUCT_BY_ID("12"), {
+ request: { description: "other description" } as MerchantBackend.Products.ProductPatchDetail,
+ });
+
+ env.addRequestExpectation(API_GET_PRODUCT_BY_ID("12"), {
+ response: {
+ description: "other description",
+ } as MerchantBackend.Products.ProductDetail,
+ });
+
+ act(async () => {
+ return await result.current?.api.updateProduct("12", {
+ description: "other description",
+ } as any);
+ });
+
+ assertNextRequest(env);
+ await waitForNextUpdate();
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ description: "other description",
+ });
+ })
+}) \ No newline at end of file
diff --git a/packages/merchant-backoffice-ui/tests/hooks/swr/reserve.test.ts b/packages/merchant-backoffice-ui/tests/hooks/swr/reserve.test.ts
new file mode 100644
index 000000000..0361c54e8
--- /dev/null
+++ b/packages/merchant-backoffice-ui/tests/hooks/swr/reserve.test.ts
@@ -0,0 +1,470 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { renderHook } from "@testing-library/preact-hooks";
+import { act } from "preact/test-utils";
+import { MerchantBackend } from "../../../src/declaration";
+import {
+ useInstanceReserves,
+ useReserveDetails,
+ useReservesAPI,
+ useTipDetails,
+} from "../../../src/hooks/reserves";
+import {
+ API_AUTHORIZE_TIP,
+ API_AUTHORIZE_TIP_FOR_RESERVE,
+ API_CREATE_RESERVE,
+ API_DELETE_RESERVE,
+ API_GET_RESERVE_BY_ID,
+ API_GET_TIP_BY_ID,
+ API_LIST_RESERVES,
+ assertJustExpectedRequestWereMade,
+ AxiosMockEnvironment,
+} from "../../axiosMock";
+import { TestingContext } from "./index";
+
+describe("reserve api interaction with listing ", () => {
+ it("should evict cache when creating a reserve", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_LIST_RESERVES, {
+ response: {
+ reserves: [
+ {
+ reserve_pub: "11",
+ } as MerchantBackend.Tips.ReserveStatusEntry,
+ ],
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ const api = useReservesAPI();
+ const query = useInstanceReserves();
+
+ return { query, api };
+ },
+ { wrapper: TestingContext }
+ );
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ reserves: [{ reserve_pub: "11" }],
+ });
+
+ env.addRequestExpectation(API_CREATE_RESERVE, {
+ request: {
+ initial_balance: "ARS:3333",
+ exchange_url: "http://url",
+ wire_method: "iban",
+ },
+ response: {
+ reserve_pub: "22",
+ payto_uri: "payto",
+ },
+ });
+
+ act(async () => {
+ await result.current?.api.createReserve({
+ initial_balance: "ARS:3333",
+ exchange_url: "http://url",
+ wire_method: "iban",
+ });
+ return;
+ });
+
+ assertJustExpectedRequestWereMade(env);
+
+ env.addRequestExpectation(API_LIST_RESERVES, {
+ response: {
+ reserves: [
+ {
+ reserve_pub: "11",
+ } as MerchantBackend.Tips.ReserveStatusEntry,
+ {
+ reserve_pub: "22",
+ } as MerchantBackend.Tips.ReserveStatusEntry,
+ ],
+ },
+ });
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+
+ expect(result.current.query.data).toEqual({
+ reserves: [
+ {
+ reserve_pub: "11",
+ } as MerchantBackend.Tips.ReserveStatusEntry,
+ {
+ reserve_pub: "22",
+ } as MerchantBackend.Tips.ReserveStatusEntry,
+ ],
+ });
+ });
+
+ it("should evict cache when deleting a reserve", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_LIST_RESERVES, {
+ response: {
+ reserves: [
+ {
+ reserve_pub: "11",
+ } as MerchantBackend.Tips.ReserveStatusEntry,
+ {
+ reserve_pub: "22",
+ } as MerchantBackend.Tips.ReserveStatusEntry,
+ {
+ reserve_pub: "33",
+ } as MerchantBackend.Tips.ReserveStatusEntry,
+ ],
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ const api = useReservesAPI();
+ const query = useInstanceReserves();
+
+ return { query, api };
+ },
+ {
+ wrapper: TestingContext,
+ }
+ );
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ reserves: [
+ { reserve_pub: "11" },
+ { reserve_pub: "22" },
+ { reserve_pub: "33" },
+ ],
+ });
+
+ env.addRequestExpectation(API_DELETE_RESERVE("11"), {});
+
+ act(async () => {
+ await result.current?.api.deleteReserve("11");
+ return;
+ });
+
+ assertJustExpectedRequestWereMade(env);
+
+ env.addRequestExpectation(API_LIST_RESERVES, {
+ response: {
+ reserves: [
+ {
+ reserve_pub: "22",
+ } as MerchantBackend.Tips.ReserveStatusEntry,
+ {
+ reserve_pub: "33",
+ } as MerchantBackend.Tips.ReserveStatusEntry,
+ ],
+ },
+ });
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+
+ expect(result.current.query.data).toEqual({
+ reserves: [
+ {
+ reserve_pub: "22",
+ } as MerchantBackend.Tips.ReserveStatusEntry,
+ {
+ reserve_pub: "33",
+ } as MerchantBackend.Tips.ReserveStatusEntry,
+ ],
+ });
+ });
+});
+
+describe("reserve api interaction with details", () => {
+ it("should evict cache when adding a tip for a specific reserve", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), {
+ response: {
+ payto_uri: "payto://here",
+ tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }],
+ } as MerchantBackend.Tips.ReserveDetail,
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ const api = useReservesAPI();
+ const query = useReserveDetails("11");
+
+ return { query, api };
+ },
+ {
+ wrapper: TestingContext,
+ }
+ );
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ payto_uri: "payto://here",
+ tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }],
+ });
+
+ env.addRequestExpectation(API_AUTHORIZE_TIP_FOR_RESERVE("11"), {
+ request: {
+ amount: "USD:12",
+ justification: "not",
+ next_url: "http://taler.net",
+ },
+ response: {
+ tip_id: "id2",
+ taler_tip_uri: "uri",
+ tip_expiration: { t_s: 1 },
+ tip_status_url: "url",
+ },
+ });
+
+ act(async () => {
+ await result.current?.api.authorizeTipReserve("11", {
+ amount: "USD:12",
+ justification: "not",
+ next_url: "http://taler.net",
+ });
+ });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), {
+ response: {
+ payto_uri: "payto://here",
+ tips: [
+ { reason: "why?", tip_id: "id1", total_amount: "USD:10" },
+ { reason: "not", tip_id: "id2", total_amount: "USD:12" },
+ ],
+ } as MerchantBackend.Tips.ReserveDetail,
+ });
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+
+ expect(result.current.query.data).toEqual({
+ payto_uri: "payto://here",
+ tips: [
+ { reason: "why?", tip_id: "id1", total_amount: "USD:10" },
+ { reason: "not", tip_id: "id2", total_amount: "USD:12" },
+ ],
+ });
+ });
+
+ it("should evict cache when adding a tip for a random reserve", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), {
+ response: {
+ payto_uri: "payto://here",
+ tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }],
+ } as MerchantBackend.Tips.ReserveDetail,
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ const api = useReservesAPI();
+ const query = useReserveDetails("11");
+
+ return { query, api };
+ },
+ {
+ wrapper: TestingContext,
+ }
+ );
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ payto_uri: "payto://here",
+ tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }],
+ });
+
+ env.addRequestExpectation(API_AUTHORIZE_TIP, {
+ request: {
+ amount: "USD:12",
+ justification: "not",
+ next_url: "http://taler.net",
+ },
+ response: {
+ tip_id: "id2",
+ taler_tip_uri: "uri",
+ tip_expiration: { t_s: 1 },
+ tip_status_url: "url",
+ },
+ });
+
+ act(async () => {
+ await result.current?.api.authorizeTip({
+ amount: "USD:12",
+ justification: "not",
+ next_url: "http://taler.net",
+ });
+ });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+
+ env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), {
+ response: {
+ payto_uri: "payto://here",
+ tips: [
+ { reason: "why?", tip_id: "id1", total_amount: "USD:10" },
+ { reason: "not", tip_id: "id2", total_amount: "USD:12" },
+ ],
+ } as MerchantBackend.Tips.ReserveDetail,
+ });
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+
+ expect(result.current.query.data).toEqual({
+ payto_uri: "payto://here",
+ tips: [
+ { reason: "why?", tip_id: "id1", total_amount: "USD:10" },
+ { reason: "not", tip_id: "id2", total_amount: "USD:12" },
+ ],
+ });
+ });
+});
+
+describe("reserve api interaction with tip details", () => {
+ it("should list tips", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_GET_TIP_BY_ID("11"), {
+ response: {
+ total_picked_up: "USD:12",
+ reason: "not",
+ } as MerchantBackend.Tips.TipDetails,
+ });
+
+ const { result, waitForNextUpdate } = renderHook(
+ () => {
+ // const api = useReservesAPI();
+ const query = useTipDetails("11");
+
+ return { query };
+ },
+ {
+ wrapper: TestingContext,
+ }
+ );
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+ expect(result.current.query.loading).toBeTruthy();
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ total_picked_up: "USD:12",
+ reason: "not",
+ });
+ });
+});
diff --git a/packages/merchant-backoffice-ui/tests/hooks/swr/transfer.test.ts b/packages/merchant-backoffice-ui/tests/hooks/swr/transfer.test.ts
new file mode 100644
index 000000000..612cf8842
--- /dev/null
+++ b/packages/merchant-backoffice-ui/tests/hooks/swr/transfer.test.ts
@@ -0,0 +1,268 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { act, renderHook } from "@testing-library/preact-hooks";
+import { TestingContext } from "./index";
+import { useInstanceTransfers, useTransferAPI } from "../../../src/hooks/transfer";
+import {
+ API_INFORM_TRANSFERS,
+ API_LIST_TRANSFERS,
+ assertJustExpectedRequestWereMade,
+ assertNoMoreRequestWereMade,
+ AxiosMockEnvironment,
+} from "../../axiosMock";
+import { MerchantBackend } from "../../../src/declaration";
+
+describe("transfer api interaction with listing", () => {
+
+ it("should evict cache when informing a transfer", async () => {
+ const env = new AxiosMockEnvironment();
+
+ env.addRequestExpectation(API_LIST_TRANSFERS, {
+ qparam: { limit: 0 },
+ response: {
+ transfers: [{ wtid: "2" } as MerchantBackend.Transfers.TransferDetails],
+ },
+ });
+ // FIXME: is this query really needed? if the hook is rendered without
+ // position argument then then backend is returning the newest and no need
+ // to this second query
+ env.addRequestExpectation(API_LIST_TRANSFERS, {
+ qparam: { limit: -20 },
+ response: {
+ transfers: [],
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ const moveCursor = (d: string) => {
+ console.log("new position", d);
+ };
+ const query = useInstanceTransfers({}, moveCursor);
+ const api = useTransferAPI();
+
+ return { query, api };
+ }, { wrapper: TestingContext });
+
+ if (!result.current) {
+ expect(result.current).toBeDefined();
+ return;
+ }
+
+ expect(result.current.query.loading).toBeTruthy();
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+ if (!result.current.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ transfers: [{ wtid: "2" }],
+ });
+
+ env.addRequestExpectation(API_INFORM_TRANSFERS, {
+ request: {
+ wtid: '3',
+ credit_amount: 'EUR:1',
+ exchange_url: 'exchange.url',
+ payto_uri: 'payto://'
+ },
+ response: { total: '' } as any,
+ });
+
+ env.addRequestExpectation(API_LIST_TRANSFERS, {
+ qparam: { limit: 0 },
+ response: {
+ transfers: [{ wtid: "2" } as any, { wtid: "3" } as any],
+ },
+ });
+
+ env.addRequestExpectation(API_LIST_TRANSFERS, {
+ qparam: { limit: -20 },
+ response: {
+ transfers: [],
+ },
+ });
+
+ act(async () => {
+ await result.current?.api.informTransfer({
+ wtid: '3',
+ credit_amount: 'EUR:1',
+ exchange_url: 'exchange.url',
+ payto_uri: 'payto://'
+ });
+ });
+
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.loading).toBeFalsy();
+ expect(result.current.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ transfers: [{ wtid: "3" }, { wtid: "2" }],
+ });
+ });
+
+});
+
+describe("transfer listing pagination", () => {
+
+ it("should not load more if has reach the end", async () => {
+ const env = new AxiosMockEnvironment();
+ env.addRequestExpectation(API_LIST_TRANSFERS, {
+ qparam: { limit: 0, payto_uri: 'payto://' },
+ response: {
+ transfers: [{ wtid: "2" } as any],
+ },
+ });
+
+ env.addRequestExpectation(API_LIST_TRANSFERS, {
+ qparam: { limit: -20, payto_uri: 'payto://' },
+ response: {
+ transfers: [{ wtid: "1" } as any],
+ },
+ });
+
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ const moveCursor = (d: string) => {
+ console.log("new position", d);
+ };
+ const query = useInstanceTransfers({ payto_uri: 'payto://' }, moveCursor)
+ return { query }
+ }, { wrapper: TestingContext });
+
+ assertJustExpectedRequestWereMade(env);
+
+ await waitForNextUpdate();
+
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ transfers: [{ wtid: "2" }, { wtid: "1" }],
+ });
+
+ expect(result.current.query.isReachingEnd).toBeTruthy()
+ expect(result.current.query.isReachingStart).toBeTruthy()
+
+ await act(() => {
+ if (!result.current?.query.ok) throw Error("not ok");
+ result.current.query.loadMore();
+ });
+ assertNoMoreRequestWereMade(env);
+
+ await act(() => {
+ if (!result.current?.query.ok) throw Error("not ok");
+ result.current.query.loadMorePrev();
+ });
+ assertNoMoreRequestWereMade(env);
+
+ expect(result.current.query.data).toEqual({
+ transfers: [
+ { wtid: "2" },
+ { wtid: "1" },
+ ],
+ });
+ });
+
+ it("should load more if result brings more that PAGE_SIZE", async () => {
+ const env = new AxiosMockEnvironment();
+
+ const transfersFrom0to20 = Array.from({ length: 20 }).map((e, i) => ({ wtid: String(i) }))
+ const transfersFrom20to40 = Array.from({ length: 20 }).map((e, i) => ({ wtid: String(i + 20) }))
+ const transfersFrom20to0 = [...transfersFrom0to20].reverse()
+
+ env.addRequestExpectation(API_LIST_TRANSFERS, {
+ qparam: { limit: 20, payto_uri: 'payto://' },
+ response: {
+ transfers: transfersFrom0to20,
+ },
+ });
+
+ env.addRequestExpectation(API_LIST_TRANSFERS, {
+ qparam: { limit: -20, payto_uri: 'payto://' },
+ response: {
+ transfers: transfersFrom20to40,
+ },
+ });
+
+ const { result, waitForNextUpdate } = renderHook(() => {
+ const moveCursor = (d: string) => {
+ console.log("new position", d);
+ };
+ const query = useInstanceTransfers({ payto_uri: 'payto://', position: '1' }, moveCursor)
+ return { query }
+ }, { wrapper: TestingContext });
+
+ assertJustExpectedRequestWereMade(env);
+
+ await waitForNextUpdate({ timeout: 1 });
+
+ expect(result.current?.query.ok).toBeTruthy();
+ if (!result.current?.query.ok) return;
+
+ expect(result.current.query.data).toEqual({
+ transfers: [...transfersFrom20to0, ...transfersFrom20to40],
+ });
+
+ expect(result.current.query.isReachingEnd).toBeFalsy()
+ expect(result.current.query.isReachingStart).toBeFalsy()
+
+ env.addRequestExpectation(API_LIST_TRANSFERS, {
+ qparam: { limit: -40, payto_uri: 'payto://', offset: "1" },
+ response: {
+ transfers: [...transfersFrom20to40, { wtid: '41' }],
+ },
+ });
+
+ await act(() => {
+ if (!result.current?.query.ok) throw Error("not ok");
+ result.current.query.loadMore();
+ });
+ await waitForNextUpdate({ timeout: 1 });
+
+ assertJustExpectedRequestWereMade(env);
+
+ env.addRequestExpectation(API_LIST_TRANSFERS, {
+ qparam: { limit: 40, payto_uri: 'payto://', offset: "1" },
+ response: {
+ transfers: [...transfersFrom0to20, { wtid: '-1' }],
+ },
+ });
+
+ await act(() => {
+ if (!result.current?.query.ok) throw Error("not ok");
+ result.current.query.loadMorePrev();
+ });
+ await waitForNextUpdate({ timeout: 1 });
+ assertJustExpectedRequestWereMade(env);
+
+ expect(result.current.query.data).toEqual({
+ transfers: [{ wtid: '-1' }, ...transfersFrom20to0, ...transfersFrom20to40, { wtid: '41' }],
+ });
+ });
+
+
+});