gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant-backoffice] 03/03: fixing hook testing


From: gnunet
Subject: [taler-merchant-backoffice] 03/03: fixing hook testing
Date: Thu, 09 Dec 2021 19:03:38 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository merchant-backoffice.

commit 7948aee7d14323946b29ccafc838edeb1190963a
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Thu Dec 9 15:03:17 2021 -0300

    fixing hook testing
---
 .../src/components/product/ProductList.tsx         |   4 +-
 packages/merchant-backoffice/src/hooks/admin.ts    |   3 +-
 packages/merchant-backoffice/src/hooks/backend.ts  |  40 +++-
 packages/merchant-backoffice/src/hooks/instance.ts |   4 +-
 packages/merchant-backoffice/src/hooks/order.ts    |   9 +-
 packages/merchant-backoffice/src/hooks/product.ts  |  99 +++++-----
 packages/merchant-backoffice/src/hooks/tips.ts     |   3 +-
 packages/merchant-backoffice/src/hooks/transfer.ts |   3 +-
 packages/merchant-backoffice/tests/axiosMock.ts    | 216 +++++++++++++++++++++
 .../merchant-backoffice/tests/declarations.d.ts    |  10 +-
 packages/merchant-backoffice/tests/header.test.tsx |  16 +-
 .../tests/hooks/swr/order-create.test.tsx          |  97 +++++----
 .../tests/hooks/swr/order-pagination.test.tsx      |  38 ++--
 .../tests/hooks/swr/product-create.test.tsx        | 113 ++++++-----
 .../tests/hooks/swr/product-delete.test.tsx        | 110 +++++------
 .../hooks/swr/product-details-update.test.tsx      |  78 ++++----
 .../tests/hooks/swr/product-update.test.tsx        | 113 ++++++-----
 .../merchant-backoffice/tests/stories.test.tsx     |  65 ++++---
 packages/merchant-backoffice/tests/util.ts         |  62 ------
 19 files changed, 682 insertions(+), 401 deletions(-)

diff --git 
a/packages/merchant-backoffice/src/components/product/ProductList.tsx 
b/packages/merchant-backoffice/src/components/product/ProductList.tsx
index b1c03c6..ff141bb 100644
--- a/packages/merchant-backoffice/src/components/product/ProductList.tsx
+++ b/packages/merchant-backoffice/src/components/product/ProductList.tsx
@@ -13,10 +13,10 @@
  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 { Amounts } from "@gnu-taler/taler-util";
 import { h, VNode } from "preact";
-import { MerchantBackend } from "../../declaration";
-import { AmountJson, Amounts } from "@gnu-taler/taler-util";
 import emptyImage from "../../assets/empty.png";
+import { MerchantBackend } from "../../declaration";
 import { Translate } from "../../i18n";
 
 interface Props {
diff --git a/packages/merchant-backoffice/src/hooks/admin.ts 
b/packages/merchant-backoffice/src/hooks/admin.ts
index 918bca0..1ac9e69 100644
--- a/packages/merchant-backoffice/src/hooks/admin.ts
+++ b/packages/merchant-backoffice/src/hooks/admin.ts
@@ -15,10 +15,11 @@
  */
 import { MerchantBackend } from "../declaration";
 import { useBackendContext } from "../context/backend";
-import { request, mutateAll } from "./backend";
+import { request, useMatchMutate } from "./backend";
 
 export function useAdminAPI(): AdminAPI {
   const { url, token } = useBackendContext();
+  const mutateAll = useMatchMutate();
 
   const createInstance = async (
     instance: MerchantBackend.Instances.InstanceConfigurationMessage
diff --git a/packages/merchant-backoffice/src/hooks/backend.ts 
b/packages/merchant-backoffice/src/hooks/backend.ts
index b1544bd..59bd0b6 100644
--- a/packages/merchant-backoffice/src/hooks/backend.ts
+++ b/packages/merchant-backoffice/src/hooks/backend.ts
@@ -19,22 +19,36 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { mutate, cache } from "swr";
+import { useSWRConfig } from "swr";
 import axios, { AxiosError, AxiosResponse } from "axios";
 import { MerchantBackend } from "../declaration";
 import { useBackendContext } from "../context/backend";
 import { useEffect, useState } from "preact/hooks";
 import { DEFAULT_REQUEST_TIMEOUT } from "../utils/constants";
 
-export function mutateAll(re: RegExp, value?: unknown): Array<Promise<any>> {
-  return cache
-    .keys()
-    .filter((key) => {
-      return re.test(key);
-    })
-    .map((key) => {
-      return mutate(key, value);
+export function useMatchMutate(): (
+  re: RegExp,
+  value?: unknown
+) => Promise<any> {
+  const { cache, mutate } = useSWRConfig();
+
+  if (!(cache instanceof Map)) {
+    throw new Error(
+      "matchMutate requires the cache provider to be a Map instance"
+    );
+  }
+
+  return function matchRegexMutate(re: RegExp, value?: unknown) {
+    const allKeys = Array.from(cache.keys());
+    // console.log(allKeys)
+    const keys = allKeys.filter((key) => re.test(key));
+    // console.log(allKeys.length, keys.length)
+    const mutations = keys.map((key) => {
+      // console.log(key)
+      mutate(key, value, true);
     });
+    return Promise.all(mutations);
+  };
 }
 
 export type HttpResponse<T> =
@@ -261,6 +275,14 @@ export async function request<T>(
   }
 }
 
+export function multiFetcher<T>(
+  urls: string[],
+  token: string,
+  backend: string
+): Promise<HttpResponseOk<T>[]> {
+  return Promise.all(urls.map((url) => fetcher<T>(url, token, backend)));
+}
+
 export function fetcher<T>(
   url: string,
   token: string,
diff --git a/packages/merchant-backoffice/src/hooks/instance.ts 
b/packages/merchant-backoffice/src/hooks/instance.ts
index 2d15464..2f4923e 100644
--- a/packages/merchant-backoffice/src/hooks/instance.ts
+++ b/packages/merchant-backoffice/src/hooks/instance.ts
@@ -23,7 +23,7 @@ import {
   request,
   SwrError,
 } from "./backend";
-import useSWR, { mutate } from "swr";
+import useSWR, { useSWRConfig } from "swr";
 import { useInstanceContext } from "../context/instance";
 
 interface InstanceAPI {
@@ -36,6 +36,7 @@ interface InstanceAPI {
 }
 
 export function useManagementAPI(instanceId: string): InstanceAPI {
+  const { mutate } = useSWRConfig();
   const { url, token } = useBackendContext();
 
   const updateInstance = async (
@@ -83,6 +84,7 @@ export function useManagementAPI(instanceId: string): 
InstanceAPI {
 }
 
 export function useInstanceAPI(): InstanceAPI {
+  const { mutate } = useSWRConfig();
   const { url: baseUrl, token: adminToken } = useBackendContext();
   const { token: instanceToken, id, admin } = useInstanceContext();
 
diff --git a/packages/merchant-backoffice/src/hooks/order.ts 
b/packages/merchant-backoffice/src/hooks/order.ts
index 91d34c8..c0cf5b9 100644
--- a/packages/merchant-backoffice/src/hooks/order.ts
+++ b/packages/merchant-backoffice/src/hooks/order.ts
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import { useEffect, useState } from "preact/hooks";
-import useSWR from "swr";
+import useSWR, { useSWRConfig } from "swr";
 import { useBackendContext } from "../context/backend";
 import { useInstanceContext } from "../context/instance";
 import { MerchantBackend } from "../declaration";
@@ -25,8 +25,8 @@ import {
   HttpResponse,
   HttpResponseOk,
   HttpResponsePaginated,
-  mutateAll,
   request,
+  useMatchMutate,
 } from "./backend";
 
 export interface OrderAPI {
@@ -72,6 +72,8 @@ export function orderFetcher<T>(
 }
 
 export function useOrderAPI(): OrderAPI {
+  const mutateAll = useMatchMutate();
+  // const { mutate } = useSWRConfig();
   const { url: baseUrl, token: adminToken } = useBackendContext();
   const { token: instanceToken, id, admin } = useInstanceContext();
 
@@ -96,7 +98,8 @@ export function useOrderAPI(): OrderAPI {
         data,
       }
     );
-    await mutateAll(/@"\/private\/orders"@/);
+    await mutateAll(/.*private\/orders.*/);
+    // mutate('')
     return res;
   };
   const refundOrder = async (
diff --git a/packages/merchant-backoffice/src/hooks/product.ts 
b/packages/merchant-backoffice/src/hooks/product.ts
index 04cbea8..b2cc21c 100644
--- a/packages/merchant-backoffice/src/hooks/product.ts
+++ b/packages/merchant-backoffice/src/hooks/product.ts
@@ -14,7 +14,8 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import { useEffect } from "preact/hooks";
-import useSWR, { trigger, useSWRInfinite, cache, mutate } from "swr";
+import useSWR, { useSWRConfig } from "swr";
+import useSWRInfinite, { SWRInfiniteConfiguration } from "swr/infinite";
 import { useBackendContext } from "../context/backend";
 // import { useFetchContext } from '../context/fetch';
 import { useInstanceContext } from "../context/instance";
@@ -24,8 +25,9 @@ import {
   HttpError,
   HttpResponse,
   HttpResponseOk,
-  mutateAll,
+  multiFetcher,
   request,
+  useMatchMutate,
 } from "./backend";
 
 export interface ProductAPI {
@@ -44,6 +46,9 @@ export interface ProductAPI {
 }
 
 export function useProductAPI(): ProductAPI {
+  const mutateAll = useMatchMutate();
+  const { mutate } = useSWRConfig();
+  // const { mutate } = SWRInfiniteConfiguration();
   const { url: baseUrl, token: adminToken } = useBackendContext();
   const { token: instanceToken, id, admin } = useInstanceContext();
 
@@ -60,13 +65,13 @@ export function useProductAPI(): ProductAPI {
   const createProduct = async (
     data: MerchantBackend.Products.ProductAddDetail
   ): Promise<void> => {
-    await request(`${url}/private/products`, {
+    const res = await request(`${url}/private/products`, {
       method: "post",
       token,
       data,
     });
 
-    await mutateAll(/@"\/private\/products"@/, null);
+    return await mutateAll(/.*private\/products.*/);
   };
 
   const updateProduct = async (
@@ -79,6 +84,10 @@ export function useProductAPI(): ProductAPI {
       data,
     });
 
+    // await mutate([`/private/products`, token, url])
+    // await mutate([`/private/products/${productId}`, token, url], undefined, 
true);
+    return await mutateAll(/.*"\/private\/products.*/);
+    return Promise.resolve();
     /**
      * There is some inconsistency in how the cache is evicted.
      * I'm keeping this for later inspection
@@ -114,12 +123,12 @@ export function useProductAPI(): ProductAPI {
     // await mutate([`/private/products/${productId}`, token, url])
 
     // await mutateAll(/@"\/private\/products"@/);
-    await mutateAll(/@"\/private\/products\/.*"@/);
+    // await mutateAll(/@"\/private\/products\/.*"@/);
     // return true
     // return r
 
     // -- FIXME: why this un-break the tests?
-    return Promise.resolve();
+    // return Promise.resolve()
   };
 
   const deleteProduct = async (productId: string): Promise<void> => {
@@ -127,8 +136,7 @@ export function useProductAPI(): ProductAPI {
       method: "delete",
       token,
     });
-
-    await mutateAll(/@"\/private\/products"@/);
+    await mutate([`/private/products`, token, url]);
   };
 
   const lockProduct = async (
@@ -164,11 +172,7 @@ export function useInstanceProducts(): HttpResponse<
         token: instanceToken,
       };
 
-  const {
-    data: list,
-    error: listError,
-    isValidating: listLoading,
-  } = useSWR<
+  const { data: list, error: listError } = useSWR<
     HttpResponseOk<MerchantBackend.Products.InventorySummaryResponse>,
     HttpError
   >([`/private/products`, token, url], fetcher, {
@@ -179,42 +183,48 @@ export function useInstanceProducts(): HttpResponse<
     refreshWhenOffline: false,
   });
 
-  const {
-    data: products,
-    error: productError,
-    setSize,
-    size,
-  } = useSWRInfinite<
-    HttpResponseOk<MerchantBackend.Products.ProductDetail>,
-    HttpError
-  >(
-    (pageIndex: number) => {
-      if (!list?.data || !list.data.products.length || listError || 
listLoading)
-        return null;
-      return [
-        `/private/products/${list.data.products[pageIndex].product_id}`,
-        token,
-        url,
-      ];
-    },
-    fetcher,
-    {
-      revalidateAll: true,
-    }
+  // const dataLength = list?.data.products.length || 0
+  // const prods: Array<MerchantBackend.Products.InventoryEntry>[] = []
+  // prods[0] = list?.data.products || []
+
+  // const { data: products, error: productError, setSize } = 
useSWRInfinite<HttpResponseOk<MerchantBackend.Products.ProductDetail>, 
HttpError>((pageIndex: number) => {
+  //   console.log('fetcher', prods[0], pageIndex)
+  //   if (!list?.data || !prods[0].length || listError) return null
+
+  //   console.log(`/private/products/${prods[0][pageIndex].product_id}`)
+  //   return [`/private/products/${prods[0][pageIndex].product_id}`, token, 
url]
+  // }, fetcher, {
+  //   initialSize: dataLength,
+  //   revalidateAll: true,
+  // })
+  // [`/private/products`]
+  const paths = (list?.data.products || []).map(
+    (p) => `/private/products/${p.product_id}`
   );
+  const { data: products, error: productError } = useSWR<
+    HttpResponseOk<MerchantBackend.Products.ProductDetail>[],
+    HttpError
+  >([paths, token, url], multiFetcher, {
+    refreshInterval: 0,
+    refreshWhenHidden: false,
+    revalidateOnFocus: false,
+    revalidateOnReconnect: false,
+    refreshWhenOffline: false,
+  });
 
-  useEffect(() => {
-    if (list?.data && list.data.products.length > 0) {
-      setSize(list.data.products.length);
-    }
-  }, [list?.data.products.length, listLoading]);
+  // console.log('data length', dataLength, list?.data)
+  // useEffect(() => {
+  //   if (dataLength > 0) {
+  //     console.log('set size', dataLength)
+  //     setSize(dataLength)
+  //   }
+  // }, [dataLength, setSize])
 
-  if (listLoading) return { loading: true, data: [] };
   if (listError) return listError;
   if (productError) return productError;
-  if (list?.data && list.data.products.length === 0) {
-    return { ok: true, data: [] };
-  }
+  // if (dataLength === 0) {
+  //   return { ok: true, data: [] }
+  // }
   if (products) {
     const dataWithId = products.map((d) => {
       //take the id from the queried url
@@ -223,6 +233,7 @@ export function useInstanceProducts(): HttpResponse<
         id: d.info?.url.replace(/.*\/private\/products\//, "") || "",
       };
     });
+    // console.log('data with id', dataWithId)
     return { ok: true, data: dataWithId };
   }
   return { loading: true };
diff --git a/packages/merchant-backoffice/src/hooks/tips.ts 
b/packages/merchant-backoffice/src/hooks/tips.ts
index 821b794..a632e34 100644
--- a/packages/merchant-backoffice/src/hooks/tips.ts
+++ b/packages/merchant-backoffice/src/hooks/tips.ts
@@ -22,11 +22,12 @@ import {
   HttpError,
   HttpResponse,
   HttpResponseOk,
-  mutateAll,
   request,
+  useMatchMutate,
 } from "./backend";
 
 export function useReservesAPI(): ReserveMutateAPI {
+  const mutateAll = useMatchMutate();
   const { url: baseUrl, token: adminToken } = useBackendContext();
   const { token: instanceToken, id, admin } = useInstanceContext();
 
diff --git a/packages/merchant-backoffice/src/hooks/transfer.ts 
b/packages/merchant-backoffice/src/hooks/transfer.ts
index bdd64fe..c455b0c 100644
--- a/packages/merchant-backoffice/src/hooks/transfer.ts
+++ b/packages/merchant-backoffice/src/hooks/transfer.ts
@@ -17,11 +17,11 @@ import { MerchantBackend } from "../declaration";
 import { useBackendContext } from "../context/backend";
 import {
   request,
-  mutateAll,
   HttpResponse,
   HttpError,
   HttpResponseOk,
   HttpResponsePaginated,
+  useMatchMutate,
 } from "./backend";
 import useSWR from "swr";
 import { useInstanceContext } from "../context/instance";
@@ -54,6 +54,7 @@ async function transferFetcher<T>(
 }
 
 export function useTransferAPI(): TransferAPI {
+  const mutateAll = useMatchMutate();
   const { url: baseUrl, token: adminToken } = useBackendContext();
   const { token: instanceToken, id, admin } = useInstanceContext();
 
diff --git a/packages/merchant-backoffice/tests/axiosMock.ts 
b/packages/merchant-backoffice/tests/axiosMock.ts
new file mode 100644
index 0000000..c38bc44
--- /dev/null
+++ b/packages/merchant-backoffice/tests/axiosMock.ts
@@ -0,0 +1,216 @@
+/*
+ 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 * as axios from 'axios';
+import { MerchantBackend } from '../src/declaration';
+import { setAxiosRequestAsTestingEnvironment } from "../src/hooks/backend";
+
+export type Query<Req, Res> = (GetQuery | PostQuery | DeleteQuery | 
PatchQuery) & RequestResponse<Req, Res>
+
+interface RequestResponse<Req, Res> {
+  code?: number,
+}
+interface GetQuery { get: string }
+interface PostQuery { post: string }
+interface DeleteQuery { delete: string }
+interface PatchQuery { patch: string }
+
+setAxiosRequestAsTestingEnvironment();
+
+const JEST_DEBUG_LOG = process.env['JEST_DEBUG_LOG'] !== undefined
+
+type TestValues = [axios.AxiosRequestConfig | undefined, { query: Query<any, 
any>; params?: { request?: any, qparam?: any, response?: any } } | undefined]
+
+const defaultCallback = (actualQuery?: axios.AxiosRequestConfig) => {
+  if (JEST_DEBUG_LOG) {
+    console.log('UNEXPECTED QUERY', actualQuery)
+  }
+}
+
+export class AxiosMockEnvironment {
+  expectations: Array<{ query: Query<any, any>, params?: { request?: any, 
qparam?: any, response?: any } } | undefined> = []
+  axiosMock: jest.MockedFunction<axios.AxiosStatic>
+
+  constructor() {
+    this.axiosMock = (axios.default as 
jest.MockedFunction<axios.AxiosStatic>).mockImplementation(defaultCallback as 
any)
+  }
+
+  addRequestExpectation<RequestType, ResponseType>(expectedQuery?: 
Query<RequestType, ResponseType> | undefined, params?: { request?: RequestType, 
qparam?: any, response?: ResponseType }): void {
+    this.expectations.push(expectedQuery ? { query: expectedQuery, params } : 
undefined)
+    this.axiosMock = this.axiosMock.mockImplementationOnce(function 
(actualQuery?: axios.AxiosRequestConfig): axios.AxiosPromise {
+
+      if (JEST_DEBUG_LOG) {
+        console.log('query to the backend is made', actualQuery)
+      }
+      if (!expectedQuery) {
+        return Promise.reject()
+      }
+      if (JEST_DEBUG_LOG) {
+        console.log('expected query:', params?.request)
+        console.log('expected qparams:', params?.qparam)
+        console.log('sending response:', params?.response)
+      }
+
+      const responseCode = expectedQuery.code || 200
+
+      //This response is what buildRequestOk is expecting in file 
hook/backend.ts
+      if (responseCode >= 200 && responseCode < 300) {
+        return Promise.resolve({
+          data: params?.response, config: {
+            data: params?.response,
+            params: actualQuery?.params || {},
+          }, request: { params: actualQuery?.params || {} }
+        } as any);
+      }
+      //This response is what buildRequestFailed is expecting in file 
hook/backend.ts
+      return Promise.reject({
+        response: {
+          status: responseCode
+        },
+        request: {
+          data: params?.response,
+          params: actualQuery?.params || {},
+        }
+      })
+
+    } as any)
+  }
+
+  getLastTestValues(): TestValues {
+    const lastCall = this.axiosMock.mock.calls[0]
+    if (lastCall === undefined) {
+      const expectedQuery = this.expectations.shift()
+      return [undefined, expectedQuery]
+    }
+    const actualQuery = lastCall[0] as axios.AxiosRequestConfig
+
+    //Remove values from the last call
+    const expectedQuery = this.expectations.shift()
+    this.axiosMock.mock.calls.shift()
+    this.axiosMock.mock.results.shift()
+
+    return [
+      actualQuery, expectedQuery
+    ]
+  }
+
+}
+
+export function testAllExpectedRequestAndNoMore(env: AxiosMockEnvironment): 
void {
+  let size = env.expectations.length
+  while (size-- > 0) {
+    testOneRequestToBackend(env)
+  }
+  testNoOtherRequestWasMade(env)
+}
+
+export function testNoOtherRequestWasMade(env: AxiosMockEnvironment): void {
+  const [actualQuery, expectedQuery] = env.getLastTestValues()
+
+  expect(actualQuery).toBeUndefined();
+  expect(expectedQuery).toBeUndefined();
+}
+
+export function testOneRequestToBackend(env: AxiosMockEnvironment): void {
+  const [actualQuery, expectedQuery] = env.getLastTestValues()
+
+  if (!actualQuery) {
+    expect(actualQuery).toBe(expectedQuery);
+
+    // throw Error('actual query was undefined');
+    return
+  }
+
+  if (!expectedQuery) {
+    const errorMessage = 'a query was made to the backend but the test 
explicitly expected no query';
+    if (JEST_DEBUG_LOG) {
+      console.log(errorMessage, actualQuery)
+    }
+    throw Error(errorMessage)
+  }
+  if ('get' in expectedQuery.query) {
+    expect(actualQuery.method).toBe('get');
+    expect(actualQuery.url).toBe(expectedQuery.query.get);
+  }
+  if ('post' in expectedQuery.query) {
+    expect(actualQuery.method).toBe('post');
+    expect(actualQuery.url).toBe(expectedQuery.query.post);
+    if (actualQuery.method !== 'post') throw Error('tu vieja')
+  }
+  if ('delete' in expectedQuery.query) {
+    expect(actualQuery.method).toBe('delete');
+    expect(actualQuery.url).toBe(expectedQuery.query.delete);
+  }
+  if ('patch' in expectedQuery.query) {
+    expect(actualQuery.method).toBe('patch');
+    expect(actualQuery.url).toBe(expectedQuery.query.patch);
+  }
+
+  if (expectedQuery.params?.request) {
+    expect(actualQuery.data).toMatchObject(expectedQuery.params.request)
+  }
+  if (expectedQuery.params?.qparam) {
+    expect(actualQuery.params).toMatchObject(expectedQuery.params.qparam)
+  }
+
+}
+
+export const API_LIST_PRODUCTS: Query<
+  unknown,
+  MerchantBackend.Products.InventorySummaryResponse
+> = {
+  get: "http://backend/instances/default/private/products";,
+};
+
+export const API_LIST_ORDERS: Query<
+  unknown,
+  MerchantBackend.Orders.OrderHistory
+> = {
+  get: "http://backend/instances/default/private/orders";,
+};
+
+export const API_CREATE_PRODUCT: Query<
+  MerchantBackend.Products.ProductAddDetail,
+  unknown
+> = {
+  post: "http://backend/instances/default/private/products";,
+};
+
+export const API_CREATE_ORDER: Query<
+  MerchantBackend.Orders.PostOrderRequest,
+  MerchantBackend.Orders.PostOrderResponse
+> = {
+  post: "http://backend/instances/default/private/orders";,
+};
+
+export const API_GET_PRODUCT_BY_ID = (
+  id: string
+): Query<unknown, MerchantBackend.Products.InventorySummaryResponse> => ({
+  get: `http://backend/instances/default/private/products/${id}`,
+});
+
+export const API_UPDATE_PRODUCT_BY_ID = (
+  id: string
+): Query<
+  Partial<MerchantBackend.Products.ProductPatchDetail>,
+  MerchantBackend.Products.InventorySummaryResponse
+> => ({
+  patch: `http://backend/instances/default/private/products/${id}`,
+});
diff --git a/packages/merchant-backoffice/tests/declarations.d.ts 
b/packages/merchant-backoffice/tests/declarations.d.ts
index 097a30e..61a53dc 100644
--- a/packages/merchant-backoffice/tests/declarations.d.ts
+++ b/packages/merchant-backoffice/tests/declarations.d.ts
@@ -19,6 +19,10 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-// Enable enzyme adapter's integration with TypeScript
-// See: 
https://github.com/preactjs/enzyme-adapter-preact-pure#usage-with-typescript
-/// <reference types="enzyme-adapter-preact-pure" />
+declare global {
+  namespace jest {
+    interface Matchers<R> {
+      toBeWithinRange(a: number, b: number): R;
+    }
+  }
+}
diff --git a/packages/merchant-backoffice/tests/header.test.tsx 
b/packages/merchant-backoffice/tests/header.test.tsx
index c705eef..f098b70 100644
--- a/packages/merchant-backoffice/tests/header.test.tsx
+++ b/packages/merchant-backoffice/tests/header.test.tsx
@@ -22,17 +22,27 @@
 import { h } from "preact";
 import { ProductList } from "../src/components/product/ProductList";
 // See: https://github.com/preactjs/enzyme-adapter-preact-pure
-import { shallow } from "enzyme";
+// import { shallow } from 'enzyme';
 import * as backend from "../src/context/config";
+import { render, findAllByText } from "@testing-library/preact";
+import * as i18n from "../src/context/translation";
+
+import * as jedLib from "jed";
+const handler = new jedLib.Jed("en");
 
 describe("Initial Test of the Sidebar", () => {
   beforeEach(() => {
     jest
       .spyOn(backend, "useConfigContext")
       .mockImplementation(() => ({ version: "", currency: "" }));
+    jest.spyOn(i18n, "useTranslationContext").mockImplementation(() => ({
+      changeLanguage: () => null,
+      handler,
+      lang: "en",
+    }));
   });
   test("Product list renders a table", () => {
-    const context = shallow(
+    const context = render(
       <ProductList
         list={[
           {
@@ -47,7 +57,7 @@ describe("Initial Test of the Sidebar", () => {
       />
     );
 
-    expect(context.find("table")).toBeDefined();
+    expect(context.findAllByText("description of the product")).toBeDefined();
     // expect(context.find('table tr td img').map(img => 
img.prop('src'))).toEqual('');
   });
 });
diff --git a/packages/merchant-backoffice/tests/hooks/swr/order-create.test.tsx 
b/packages/merchant-backoffice/tests/hooks/swr/order-create.test.tsx
index ad46612..0defaec 100644
--- a/packages/merchant-backoffice/tests/hooks/swr/order-create.test.tsx
+++ b/packages/merchant-backoffice/tests/hooks/swr/order-create.test.tsx
@@ -20,15 +20,17 @@
  */
 
 import { renderHook } from "@testing-library/preact-hooks";
-import * as axios from "axios";
+import { act } from "preact/test-utils";
 import * as backend from "../../../src/context/backend";
 import * as instance from "../../../src/context/instance";
-import { MerchantBackend } from "../../../src/declaration";
-import { setAxiosRequestAsTestingEnvironment } from 
"../../../src/hooks/backend";
 import { useInstanceOrders, useOrderAPI } from "../../../src/hooks/order";
-import { simulateBackendResponse } from "../../util";
-
-setAxiosRequestAsTestingEnvironment();
+import {
+  API_CREATE_ORDER,
+  API_LIST_ORDERS,
+  AxiosMockEnvironment,
+  testNoOtherRequestWasMade,
+  testOneRequestToBackend,
+} from "../../axiosMock";
 
 jest.mock("axios");
 
@@ -47,19 +49,18 @@ describe("order api", () => {
   });
 
   it("should not have problem with cache after an creation", async () => {
-    simulateBackendResponse<unknown, MerchantBackend.Orders.OrderHistory>({
-      get: "http://backend/instances/default/private/orders";,
-      params: { delta: 0, paid: "yes" },
+    const env = new AxiosMockEnvironment();
+    env.addRequestExpectation(API_LIST_ORDERS, {
+      qparam: { delta: 0, paid: "yes" },
       response: {
-        orders: [{ order_id: "1" } as any],
+        orders: [{ order_id: "1" }],
       },
     });
 
-    simulateBackendResponse<unknown, MerchantBackend.Orders.OrderHistory>({
-      get: "http://backend/instances/default/private/orders";,
-      params: { delta: -20, paid: "yes" },
+    env.addRequestExpectation(API_LIST_ORDERS, {
+      qparam: { delta: -20, paid: "yes" },
       response: {
-        orders: [{ order_id: "2" } as any],
+        orders: [{ order_id: "2" }],
       },
     });
 
@@ -67,58 +68,70 @@ describe("order api", () => {
       console.log("new date", d);
     };
 
-    const { result, waitForNextUpdate } = renderHook(() =>
-      useInstanceOrders({ paid: "yes" }, newDate)
-    ); // get products -> loading
+    const { result, waitForNextUpdate } = renderHook(() => {
+      const query = useInstanceOrders({ paid: "yes" }, newDate);
+      const api = useOrderAPI();
+
+      return { query, api };
+    }); // get products -> loading
+
+    if (!result.current) {
+      expect(result.current).toBeDefined();
+      return;
+    }
 
-    await waitForNextUpdate(); // get info of every product, -> loading
-    await waitForNextUpdate(); // loading product -> products
-    // await waitForNextUpdate(); // loading product -> products
+    expect(result.current.query.loading).toBeTruthy();
+    await waitForNextUpdate();
+    testOneRequestToBackend(env);
+    testOneRequestToBackend(env);
+    testNoOtherRequestWasMade(env);
 
-    expect(result.current?.ok).toBeTruthy();
-    if (!result.current?.ok) return;
+    expect(result.current.query.loading).toBeFalsy();
+    expect(result.current?.query.ok).toBeTruthy();
+    if (!result.current?.query.ok) return;
 
-    expect(result.current.data).toEqual({
+    expect(result.current.query.data).toEqual({
       orders: [{ order_id: "1" }, { order_id: "2" }],
     });
 
-    const { createOrder } = useOrderAPI();
-
-    simulateBackendResponse<
-      MerchantBackend.Orders.PostOrderRequest,
-      MerchantBackend.Orders.PostOrderResponse
-    >({
-      post: "http://backend/instances/default/private/orders";,
+    env.addRequestExpectation(API_CREATE_ORDER, {
       request: {
         order: { amount: "ARS:12", summary: "pay me" },
       },
       response: { order_id: "3" },
     });
 
-    simulateBackendResponse<unknown, MerchantBackend.Orders.OrderHistory>({
-      get: "http://backend/instances/default/private/orders";,
-      params: { delta: 0, paid: "yes" },
+    env.addRequestExpectation(API_LIST_ORDERS, {
+      qparam: { delta: 0, paid: "yes" },
       response: {
         orders: [{ order_id: "1" } as any],
       },
     });
 
-    simulateBackendResponse<unknown, MerchantBackend.Orders.OrderHistory>({
-      get: "http://backend/instances/default/private/orders";,
-      params: { delta: -20, paid: "yes" },
+    env.addRequestExpectation(API_LIST_ORDERS, {
+      qparam: { delta: -20, paid: "yes" },
       response: {
         orders: [{ order_id: "2" } as any, { order_id: "3" } as any],
       },
     });
 
-    await createOrder({
-      order: { amount: "ARS:12", summary: "pay me" },
-    } as any);
+    act(async () => {
+      await result.current?.api.createOrder({
+        order: { amount: "ARS:12", summary: "pay me" },
+      } as any);
+    });
+
+    testOneRequestToBackend(env); //post
+    await waitForNextUpdate();
+    testOneRequestToBackend(env); //get
+    testOneRequestToBackend(env); //get
+    testNoOtherRequestWasMade(env);
 
-    await waitForNextUpdate(); // loading product -> products
-    await waitForNextUpdate(); // loading product -> products
+    expect(result.current.query.loading).toBeFalsy();
+    expect(result.current?.query.ok).toBeTruthy();
+    if (!result.current?.query.ok) return;
 
-    expect(result.current.data).toEqual({
+    expect(result.current.query.data).toEqual({
       orders: [{ order_id: "1" }, { order_id: "2" }, { order_id: "3" }],
     });
   });
diff --git 
a/packages/merchant-backoffice/tests/hooks/swr/order-pagination.test.tsx 
b/packages/merchant-backoffice/tests/hooks/swr/order-pagination.test.tsx
index f02fa46..d62ead7 100644
--- a/packages/merchant-backoffice/tests/hooks/swr/order-pagination.test.tsx
+++ b/packages/merchant-backoffice/tests/hooks/swr/order-pagination.test.tsx
@@ -20,13 +20,17 @@
  */
 
 import { act, renderHook } from "@testing-library/preact-hooks";
-import * as axios from "axios";
 import * as backend from "../../../src/context/backend";
 import * as instance from "../../../src/context/instance";
 import { MerchantBackend } from "../../../src/declaration";
 import { setAxiosRequestAsTestingEnvironment } from 
"../../../src/hooks/backend";
 import { useInstanceOrders } from "../../../src/hooks/order";
-import { simulateBackendResponse } from "../../util";
+import {
+  API_LIST_ORDERS,
+  AxiosMockEnvironment,
+  testNoOtherRequestWasMade,
+  testOneRequestToBackend,
+} from "../../axiosMock";
 
 setAxiosRequestAsTestingEnvironment();
 
@@ -47,17 +51,16 @@ describe("order pagination", () => {
   });
 
   it("should change pagination", async () => {
-    simulateBackendResponse<unknown, MerchantBackend.Orders.OrderHistory>({
-      get: "http://backend/instances/default/private/orders";,
-      params: { delta: 20, wired: "yes", date_ms: 12 },
+    const env = new AxiosMockEnvironment();
+    env.addRequestExpectation(API_LIST_ORDERS, {
+      qparam: { delta: 20, wired: "yes", date_ms: 12 },
       response: {
         orders: [{ order_id: "1" } as any],
       },
     });
 
-    simulateBackendResponse<unknown, MerchantBackend.Orders.OrderHistory>({
-      get: "http://backend/instances/default/private/orders";,
-      params: { delta: -20, wired: "yes", date_ms: 13 },
+    env.addRequestExpectation(API_LIST_ORDERS, {
+      qparam: { delta: -20, wired: "yes", date_ms: 13 },
       response: {
         orders: [{ order_id: "2" } as any],
       },
@@ -72,6 +75,10 @@ describe("order pagination", () => {
       useInstanceOrders({ wired: "yes", date }, newDate)
     ); // get products -> loading
 
+    testOneRequestToBackend(env);
+    testOneRequestToBackend(env);
+    testNoOtherRequestWasMade(env);
+
     await waitForNextUpdate(); // get info of every product, -> loading
 
     expect(result.current?.ok).toBeTruthy();
@@ -81,23 +88,24 @@ describe("order pagination", () => {
       orders: [{ order_id: "1" }, { order_id: "2" }],
     });
 
-    simulateBackendResponse<unknown, MerchantBackend.Orders.OrderHistory>({
-      get: "http://backend/instances/default/private/orders";,
-      params: { delta: -40, wired: "yes", date_ms: 13 },
+    env.addRequestExpectation(API_LIST_ORDERS, {
+      qparam: { delta: -40, wired: "yes", date_ms: 13 },
       response: {
         orders: [{ order_id: "2" } as any, { order_id: "3" } as any],
       },
     });
+
     await act(() => {
       if (!result.current?.ok) throw Error("not ok");
       result.current.loadMore();
     });
     await waitForNextUpdate(); // loading product -> products
     // await waitForNextUpdate(); // loading product -> products
+    testOneRequestToBackend(env);
+    testNoOtherRequestWasMade(env);
 
-    simulateBackendResponse<unknown, MerchantBackend.Orders.OrderHistory>({
-      get: "http://backend/instances/default/private/orders";,
-      params: { delta: 40, wired: "yes", date_ms: 12 },
+    env.addRequestExpectation(API_LIST_ORDERS, {
+      qparam: { delta: 40, wired: "yes", date_ms: 12 },
       response: {
         orders: [{ order_id: "1" } as any, { order_id: "0" } as any],
       },
@@ -107,6 +115,8 @@ describe("order pagination", () => {
       result.current.loadMorePrev();
     });
     await waitForNextUpdate(); // loading product -> products
+    testOneRequestToBackend(env);
+    testNoOtherRequestWasMade(env);
 
     expect(result.current.data).toEqual({
       orders: [
diff --git 
a/packages/merchant-backoffice/tests/hooks/swr/product-create.test.tsx 
b/packages/merchant-backoffice/tests/hooks/swr/product-create.test.tsx
index ec9c0ab..611a470 100644
--- a/packages/merchant-backoffice/tests/hooks/swr/product-create.test.tsx
+++ b/packages/merchant-backoffice/tests/hooks/swr/product-create.test.tsx
@@ -20,19 +20,21 @@
  */
 
 import { renderHook } from "@testing-library/preact-hooks";
-import * as axios from "axios";
+import { act } from "preact/test-utils";
 import * as backend from "../../../src/context/backend";
 import * as instance from "../../../src/context/instance";
-import { MerchantBackend } from "../../../src/declaration";
-import { setAxiosRequestAsTestingEnvironment } from 
"../../../src/hooks/backend";
 import { useInstanceProducts, useProductAPI } from 
"../../../src/hooks/product";
-import { simulateBackendResponse } from "../../util";
-
-setAxiosRequestAsTestingEnvironment();
+import {
+  API_CREATE_PRODUCT,
+  API_LIST_PRODUCTS,
+  API_GET_PRODUCT_BY_ID,
+  AxiosMockEnvironment,
+  testAllExpectedRequestAndNoMore,
+} from "../../axiosMock";
 
 jest.mock("axios");
 
-describe("product api", () => {
+describe("product create api", () => {
   beforeEach(() => {
     jest
       .spyOn(backend, "useBackendContext")
@@ -46,76 +48,85 @@ describe("product api", () => {
       );
   });
 
-  it.skip("should not have problem with cache after an creation", async () => {
-    simulateBackendResponse<
-      unknown,
-      MerchantBackend.Products.InventorySummaryResponse
-    >({
-      get: "http://backend/instances/default/private/products";,
+  it("should not have problem with cache after an creation", async () => {
+    const env = new AxiosMockEnvironment();
+
+    env.addRequestExpectation(API_LIST_PRODUCTS, {
       response: {
         products: [{ product_id: "1234" }],
       },
     });
 
-    simulateBackendResponse<
-      unknown,
-      Partial<MerchantBackend.Products.ProductDetail>
-    >({
-      get: "http://backend/instances/default/private/products/1234";,
+    env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
       response: { price: "ARS:33" },
     });
 
-    const { result, waitForNextUpdate } = renderHook(() =>
-      useInstanceProducts()
-    ); // get products -> loading
+    const { result, waitForNextUpdate } = renderHook(() => {
+      const api = useProductAPI();
+      const query = useInstanceProducts();
 
-    await waitForNextUpdate(); // get info of every product, -> loading
-    await waitForNextUpdate(); // loading product -> products
+      return { query, api };
+    }); // get products -> loading
 
-    expect(result.current?.ok).toBeTruthy();
-    if (!result.current?.ok) return;
+    if (!result.current) {
+      expect(result.current).toBeDefined();
+      return;
+    }
+    expect(result.current.query.loading).toBeTruthy();
 
-    expect(result.current.data).toEqual([
-      {
-        id: "1234",
-        price: "ARS:33",
-      },
-    ]);
+    await waitForNextUpdate(); // first query to list products
+    expect(result.current.query.loading).toBeTruthy();
+
+    await waitForNextUpdate(); // second query to get product details
+    testAllExpectedRequestAndNoMore(env);
+
+    expect(result.current.query.loading).toBeFalsy();
+    expect(result.current?.query.ok).toBeTruthy();
+    if (!result.current?.query.ok) return;
 
-    const { createProduct } = useProductAPI();
+    expect(result.current.query.data).toEqual([
+      { id: "1234", price: "ARS:33" },
+    ]);
 
-    simulateBackendResponse<
-      Partial<MerchantBackend.Products.ProductAddDetail>,
-      unknown
-    >({
-      post: "http://backend/instances/default/private/products";,
+    env.addRequestExpectation(API_CREATE_PRODUCT, {
       request: { price: "ARS:3333" },
     });
 
-    simulateBackendResponse<
-      unknown,
-      MerchantBackend.Products.InventorySummaryResponse
-    >({
-      get: "http://backend/instances/default/private/products";,
+    act(async () => {
+      return await result.current?.api.createProduct({
+        price: "ARS:3333",
+      } as any);
+    });
+
+    testAllExpectedRequestAndNoMore(env);
+
+    env.addRequestExpectation(API_LIST_PRODUCTS, {
       response: {
         products: [{ product_id: "1234" }, { product_id: "2222" }],
       },
     });
-    simulateBackendResponse<
-      unknown,
-      Partial<MerchantBackend.Products.ProductDetail>
-    >({
-      get: "http://backend/instances/default/private/products/2222";,
-      response: { price: "ARS:3333" },
+
+    env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
+      response: { price: "ARS:33" },
+    });
+    env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
+      response: { price: "ARS:33" },
     });
 
-    await createProduct({ price: "ARS:3333" } as any);
+    env.addRequestExpectation(API_GET_PRODUCT_BY_ID("2222"), {
+      response: { price: "ARS:3333" },
+    });
+    expect(result.current.query.loading).toBeFalsy();
 
     await waitForNextUpdate(); // loading product -> products
     await waitForNextUpdate(); // loading product -> products
-    await waitForNextUpdate(); // loading product -> products
 
-    expect(result.current.data).toEqual([
+    testAllExpectedRequestAndNoMore(env);
+
+    expect(result.current.query.loading).toBeFalsy();
+    expect(result.current.query.ok).toBeTruthy();
+
+    expect(result.current.query.data).toEqual([
       {
         id: "1234",
         price: "ARS:33",
diff --git 
a/packages/merchant-backoffice/tests/hooks/swr/product-delete.test.tsx 
b/packages/merchant-backoffice/tests/hooks/swr/product-delete.test.tsx
index b7df9b6..4c67ec1 100644
--- a/packages/merchant-backoffice/tests/hooks/swr/product-delete.test.tsx
+++ b/packages/merchant-backoffice/tests/hooks/swr/product-delete.test.tsx
@@ -20,22 +20,21 @@
  */
 
 import { renderHook } from "@testing-library/preact-hooks";
+import { act } from "preact/test-utils";
 import * as backend from "../../../src/context/backend";
 import * as instance from "../../../src/context/instance";
-import { MerchantBackend } from "../../../src/declaration";
 import { useInstanceProducts, useProductAPI } from 
"../../../src/hooks/product";
-import { simulateBackendResponse } from "../../util";
-
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-import * as axios from "axios";
-import { setAxiosRequestAsTestingEnvironment } from 
"../../../src/hooks/backend";
-
-setAxiosRequestAsTestingEnvironment();
+import {
+  API_LIST_PRODUCTS,
+  API_GET_PRODUCT_BY_ID,
+  AxiosMockEnvironment,
+  testAllExpectedRequestAndNoMore,
+  testOneRequestToBackend,
+} from "../../axiosMock";
 
 jest.mock("axios");
-axios.default;
 
-describe("product api", () => {
+describe("product delete api", () => {
   beforeEach(() => {
     jest
       .spyOn(backend, "useBackendContext")
@@ -50,89 +49,78 @@ describe("product api", () => {
     // console.log("CLEAR")
   });
 
-  it.skip("should not have problem with cache after a delete", async () => {
-    simulateBackendResponse<
-      unknown,
-      MerchantBackend.Products.InventorySummaryResponse
-    >({
-      get: "http://backend/instances/default/private/products";,
+  it("should not have problem with cache after a delete", async () => {
+    const env = new AxiosMockEnvironment();
+
+    env.addRequestExpectation(API_LIST_PRODUCTS, {
       response: {
         products: [{ product_id: "1234" }, { product_id: "2345" }],
       },
     });
 
-    simulateBackendResponse<
-      unknown,
-      Partial<MerchantBackend.Products.ProductDetail>
-    >({
-      get: "http://backend/instances/default/private/products/1234";,
+    env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
       response: { price: "ARS:12" },
     });
 
-    simulateBackendResponse<
-      unknown,
-      Partial<MerchantBackend.Products.ProductDetail>
-    >({
-      get: "http://backend/instances/default/private/products/2345";,
+    env.addRequestExpectation(API_GET_PRODUCT_BY_ID("2345"), {
       response: { price: "ARS:23" },
     });
 
-    const { result, waitForNextUpdate } = renderHook(() =>
-      useInstanceProducts()
-    ); // get products -> loading
+    // env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
+    //   response: { price: "ARS:12" },
+    // });
+
+    // env.addRequestExpectation(API_GET_PRODUCT_BY_ID("2345"), {
+    //   response: { price: "ARS:23" },
+    // });
+
+    const { result, waitForNextUpdate } = renderHook(() => {
+      const query = useInstanceProducts();
+      const api = useProductAPI();
+      return { query, api };
+    }); // get products -> loading
 
     await waitForNextUpdate(); // get info of every product, -> loading
     await waitForNextUpdate(); // loading product -> products
-    await waitForNextUpdate(); // loading product -> products
+    testAllExpectedRequestAndNoMore(env);
+    // await waitForNextUpdate(); // loading product -> products
 
-    expect(result.current?.ok).toBeTruthy();
-    if (!result.current?.ok) return;
+    expect(result.current?.query.ok).toBeTruthy();
+    if (!result.current?.query.ok) return;
 
-    expect(result.current.data).toEqual([
-      {
-        id: "1234",
-        price: "ARS:12",
-      },
-      {
-        id: "2345",
-        price: "ARS:23",
-      },
+    expect(result.current.query.data).toEqual([
+      { id: "1234", price: "ARS:12" },
+      { id: "2345", price: "ARS:23" },
     ]);
 
-    const { deleteProduct } = useProductAPI();
-
-    simulateBackendResponse({
+    env.addRequestExpectation({
       delete: "http://backend/instances/default/private/products/1234";,
     });
 
-    simulateBackendResponse<
-      unknown,
-      MerchantBackend.Products.InventorySummaryResponse
-    >({
-      get: "http://backend/instances/default/private/products";,
+    env.addRequestExpectation(API_LIST_PRODUCTS, {
       response: {
-        products: [
-          {
-            product_id: "2345",
-          },
-        ],
+        products: [{ product_id: "2345" }],
       },
     });
 
-    simulateBackendResponse<
-      unknown,
-      Partial<MerchantBackend.Products.ProductDetail>
-    >({
-      get: "http://backend/instances/default/private/products/2345";,
+    env.addRequestExpectation(API_GET_PRODUCT_BY_ID("2345"), {
       response: { price: "ARS:23" },
     });
 
-    await deleteProduct("1234");
+    // env.addRequestExpectation(API_GET_PRODUCT_BY_ID("2345"), {
+    //   response: { price: "ARS:23" },
+    // });
+
+    act(async () => {
+      await result.current?.api.deleteProduct("1234");
+    });
 
+    testOneRequestToBackend(env);
     await waitForNextUpdate();
     await waitForNextUpdate();
+    testAllExpectedRequestAndNoMore(env);
 
-    expect(result.current.data).toEqual([
+    expect(result.current.query.data).toEqual([
       {
         id: "2345",
         price: "ARS:23",
diff --git 
a/packages/merchant-backoffice/tests/hooks/swr/product-details-update.test.tsx 
b/packages/merchant-backoffice/tests/hooks/swr/product-details-update.test.tsx
index 45b33a8..f5941f3 100644
--- 
a/packages/merchant-backoffice/tests/hooks/swr/product-details-update.test.tsx
+++ 
b/packages/merchant-backoffice/tests/hooks/swr/product-details-update.test.tsx
@@ -19,20 +19,23 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { renderHook } from "@testing-library/preact-hooks";
-import * as axios from "axios";
+import { renderHook, act } from "@testing-library/preact-hooks";
+
 import * as backend from "../../../src/context/backend";
 import * as instance from "../../../src/context/instance";
 import { MerchantBackend } from "../../../src/declaration";
-import { setAxiosRequestAsTestingEnvironment } from 
"../../../src/hooks/backend";
 import { useProductAPI, useProductDetails } from "../../../src/hooks/product";
-import { simulateBackendResponse } from "../../util";
-
-setAxiosRequestAsTestingEnvironment();
+import {
+  API_GET_PRODUCT_BY_ID,
+  API_UPDATE_PRODUCT_BY_ID,
+  AxiosMockEnvironment,
+  testAllExpectedRequestAndNoMore,
+  testOneRequestToBackend,
+} from "../../axiosMock";
 
 jest.mock("axios");
 
-describe("product api", () => {
+describe("product details api", () => {
   beforeEach(() => {
     jest
       .spyOn(backend, "useBackendContext")
@@ -47,50 +50,59 @@ describe("product api", () => {
   });
 
   it("should not have problem with cache after an update", async () => {
-    simulateBackendResponse<
-      unknown,
-      Partial<MerchantBackend.Products.ProductDetail>
-    >({
-      get: "http://backend/instances/default/private/products/12";,
+    const env = new AxiosMockEnvironment();
+
+    env.addRequestExpectation(API_GET_PRODUCT_BY_ID("12"), {
       response: { description: "this is a description" },
     });
 
-    const { result, waitForNextUpdate } = renderHook(() =>
-      useProductDetails("12")
-    ); // get products -> loading
+    const { result, waitForNextUpdate } = renderHook(() => {
+      const query = useProductDetails("12");
+      const api = useProductAPI();
+      return { query, api };
+    });
 
+    if (!result.current) {
+      expect(result.current).toBeDefined();
+      return;
+    }
+    expect(result.current.query.loading).toBeTruthy();
     await waitForNextUpdate();
 
-    expect(result.current?.ok).toBeTruthy();
-    if (!result.current?.ok) return;
+    testAllExpectedRequestAndNoMore(env);
+
+    expect(result.current.query.loading).toBeFalsy();
+    expect(result.current?.query.ok).toBeTruthy();
+    if (!result.current?.query.ok) return;
 
-    expect(result.current.data).toEqual({
+    expect(result.current.query.data).toEqual({
       description: "this is a description",
     });
 
-    const { updateProduct } = useProductAPI();
-
-    simulateBackendResponse<
-      unknown,
-      MerchantBackend.Products.InventorySummaryResponse
-    >({
-      patch: "http://backend/instances/default/private/products/12";,
+    env.addRequestExpectation(API_UPDATE_PRODUCT_BY_ID("12"), {
       request: { description: "other description" },
     });
 
-    simulateBackendResponse<
-      unknown,
-      Partial<MerchantBackend.Products.ProductDetail>
-    >({
-      get: "http://backend/instances/default/private/products/12";,
+    env.addRequestExpectation(API_GET_PRODUCT_BY_ID("12"), {
       response: { description: "other description" },
     });
 
-    await updateProduct("12", { description: "other description" } as any);
+    act(async () => {
+      return await result.current?.api.updateProduct("12", {
+        description: "other description",
+      } as any);
+    });
+
+    testOneRequestToBackend(env);
+    await waitForNextUpdate();
+
+    testAllExpectedRequestAndNoMore(env);
 
-    // await waitForNextUpdate();
+    expect(result.current.query.loading).toBeFalsy();
+    expect(result.current?.query.ok).toBeTruthy();
+    if (!result.current?.query.ok) return;
 
-    expect(result.current.data).toEqual({
+    expect(result.current.query.data).toEqual({
       description: "other description",
     });
   });
diff --git 
a/packages/merchant-backoffice/tests/hooks/swr/product-update.test.tsx 
b/packages/merchant-backoffice/tests/hooks/swr/product-update.test.tsx
index 940852c..d3a106b 100644
--- a/packages/merchant-backoffice/tests/hooks/swr/product-update.test.tsx
+++ b/packages/merchant-backoffice/tests/hooks/swr/product-update.test.tsx
@@ -20,19 +20,22 @@
  */
 
 import { renderHook } from "@testing-library/preact-hooks";
-import * as axios from "axios";
+import { act } from "preact/test-utils";
 import * as backend from "../../../src/context/backend";
 import * as instance from "../../../src/context/instance";
-import { MerchantBackend } from "../../../src/declaration";
-import { setAxiosRequestAsTestingEnvironment } from 
"../../../src/hooks/backend";
 import { useInstanceProducts, useProductAPI } from 
"../../../src/hooks/product";
-import { simulateBackendResponse } from "../../util";
-
-setAxiosRequestAsTestingEnvironment();
+import {
+  API_GET_PRODUCT_BY_ID,
+  API_LIST_PRODUCTS,
+  API_UPDATE_PRODUCT_BY_ID,
+  AxiosMockEnvironment,
+  testAllExpectedRequestAndNoMore,
+  testOneRequestToBackend,
+} from "../../axiosMock";
 
 jest.mock("axios");
 
-describe("product api", () => {
+describe("product list api", () => {
   beforeEach(() => {
     jest
       .spyOn(backend, "useBackendContext")
@@ -44,66 +47,88 @@ describe("product api", () => {
       .mockImplementation(
         () => ({ token: "token", id: "default", admin: true } as any)
       );
-    // console.log("CLEAR")
   });
 
   it("should not have problem with cache after an update", async () => {
-    simulateBackendResponse<
-      unknown,
-      MerchantBackend.Products.InventorySummaryResponse
-    >({
-      get: "http://backend/instances/default/private/products";,
+    const env = new AxiosMockEnvironment();
+
+    env.addRequestExpectation(API_LIST_PRODUCTS, {
       response: {
         products: [{ product_id: "1234" }],
       },
     });
 
-    simulateBackendResponse<
-      unknown,
-      Partial<MerchantBackend.Products.ProductDetail>
-    >({
-      get: "http://backend/instances/default/private/products/1234";,
+    env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
       response: { price: "ARS:12" },
     });
 
-    const { result, waitForNextUpdate, waitFor } = renderHook(() =>
-      useInstanceProducts()
-    ); // get products -> loading
+    const { result, waitForNextUpdate } = renderHook(() => {
+      const query = useInstanceProducts();
+      const api = useProductAPI();
+      return { api, query };
+    }); // get products -> loading
 
-    await waitForNextUpdate(); // get info of every product, -> loading
-    await waitForNextUpdate(); // loading product -> products
+    if (!result.current) {
+      expect(result.current).toBeDefined();
+      return;
+    }
+    expect(result.current.query.loading).toBeTruthy();
+    await waitForNextUpdate();
 
-    expect(result.current?.ok).toBeTruthy();
-    if (!result.current?.ok) return;
+    await waitForNextUpdate();
+    testAllExpectedRequestAndNoMore(env);
 
-    expect(result.current.data).toEqual([
-      {
-        id: "1234",
-        price: "ARS:12",
-      },
-    ]);
+    expect(result.current.query.loading).toBeFalsy();
+    expect(result.current.query.ok).toBeTruthy();
+    if (!result.current?.query.ok) return;
 
-    const { updateProduct } = useProductAPI();
+    expect(result.current.query.data).toEqual([
+      { id: "1234", price: "ARS:12" },
+    ]);
 
-    simulateBackendResponse<
-      unknown,
-      MerchantBackend.Products.InventorySummaryResponse
-    >({
-      patch: "http://backend/instances/default/private/products/1234";,
+    env.addRequestExpectation(API_UPDATE_PRODUCT_BY_ID("1234"), {
       request: { price: "ARS:13" },
     });
 
-    simulateBackendResponse<
-      unknown,
-      Partial<MerchantBackend.Products.ProductDetail>
-    >({
-      get: "http://backend/instances/default/private/products/1234";,
+    env.addRequestExpectation(API_LIST_PRODUCTS, {
+      response: {
+        products: [{ product_id: "1234" }],
+      },
+    });
+    env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
       response: { price: "ARS:13" },
     });
+    // env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
+    //   request: { price: "ARS:13" },
+    // });
+    // env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
+    //   request: { price: "ARS:13" },
+    // });
+    // env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
+    //   request: { price: "ARS:13" },
+    // });
+    // env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), {
+    //   request: { price: "ARS:13" },
+    // });
+
+    act(async () => {
+      await result.current?.api.updateProduct("1234", {
+        price: "ARS:13",
+      } as any);
+    });
+
+    testOneRequestToBackend(env);
+    await waitForNextUpdate();
+    // await waitForNextUpdate();
+
+    // testAllExpectedRequestAndNoMore(env);
+    // await waitForNextUpdate();
 
-    await updateProduct("1234", { price: "ARS:13" } as any);
+    expect(result.current.query.loading).toBeFalsy();
+    expect(result.current?.query.ok).toBeTruthy();
+    if (!result.current?.query.ok) return;
 
-    expect(result.current.data).toEqual([
+    expect(result.current.query.data).toEqual([
       {
         id: "1234",
         price: "ARS:13",
diff --git a/packages/merchant-backoffice/tests/stories.test.tsx 
b/packages/merchant-backoffice/tests/stories.test.tsx
index 0b48cd7..5fb3483 100644
--- a/packages/merchant-backoffice/tests/stories.test.tsx
+++ b/packages/merchant-backoffice/tests/stories.test.tsx
@@ -18,10 +18,11 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
-import { mount } from "enzyme";
-import { h } from "preact";
+import { h, VNode } from "preact";
 import * as config from "../src/context/config";
 import * as i18n from "../src/context/translation";
+import { cleanup, render as originalRender } from "@testing-library/preact";
+import { SWRConfig } from "swr";
 
 import fs from "fs";
 
@@ -38,39 +39,51 @@ function getFiles(dir: string, files_: string[] = []) {
   return files_;
 }
 
-const re = RegExp(".*.stories.tsx");
+const STORIES_NAME_REGEX = RegExp(".*.stories.tsx");
+
+function render(vnode: VNode) {
+  return originalRender(
+    <SWRConfig
+      value={{
+        provider: () => new Map(),
+      }}
+    >
+      {vnode}
+    </SWRConfig>
+  );
+}
 
 import * as jedLib from "jed";
 const handler = new jedLib.Jed("en");
 
-it("render every story", () => {
-  jest
-    .spyOn(config, "useConfigContext")
-    .mockImplementation(() => ({ version: "1.0.0", currency: "EUR" }));
-  jest
-    .spyOn(i18n, "useTranslationContext")
-    .mockImplementation(() => ({
+describe("storybook testing", () => {
+  it("render every story", () => {
+    jest
+      .spyOn(config, "useConfigContext")
+      .mockImplementation(() => ({ version: "1.0.0", currency: "EUR" }));
+    jest.spyOn(i18n, "useTranslationContext").mockImplementation(() => ({
       changeLanguage: () => null,
       handler,
       lang: "en",
     }));
 
-  getFiles("./src")
-    .filter((f) => re.test(f))
-    .map((f) => {
-      // const f = "./src/paths/instance/transfers/list/List.stories.tsx";
-      // eslint-disable-next-line @typescript-eslint/no-var-requires
-      const s = require(`../${f}`);
+    getFiles("./src")
+      .filter((f) => STORIES_NAME_REGEX.test(f))
+      .map((f) => {
+        // const f = "./src/paths/instance/transfers/list/List.stories.tsx";
+        // eslint-disable-next-line @typescript-eslint/no-var-requires
+        const s = require(`../${f}`);
 
-      delete s.default;
-      Object.keys(s).forEach((k) => {
-        const Component = s[k];
-        expect(() => {
-          mount(<Component {...Component.args} />)
-            .mount()
-            .unmount()
-            .mount();
-        }).not.toThrow(); //`problem rendering ${f} example ${k}`
+        delete s.default;
+        Object.keys(s).forEach((k) => {
+          const Component = s[k];
+          const vdom = <Component {...Component.args} />;
+          expect(() => {
+            const { unmount } = render(vdom);
+            unmount();
+          }).not.toThrow(); //`problem rendering ${f} example ${k}`
+          cleanup();
+        });
       });
-    });
+  });
 });
diff --git a/packages/merchant-backoffice/tests/util.ts 
b/packages/merchant-backoffice/tests/util.ts
deleted file mode 100644
index 14b82b5..0000000
--- a/packages/merchant-backoffice/tests/util.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- 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 * as axios from 'axios';
-
-type Query<Req, Res> = (GetQuery | PostQuery | DeleteQuery | PatchQuery) & 
RequestResponse<Req, Res>
-
-interface RequestResponse<Req, Res> {
-  request?: Req,
-  params?: any,
-  response?: Res,
-}
-interface GetQuery { get: string }
-interface PostQuery { post: string }
-interface DeleteQuery { delete: string }
-interface PatchQuery { patch: string }
-
-export function simulateBackendResponse<R, T>(query: Query<R, T>): void {
-  (axios.default as 
jest.MockedFunction<axios.AxiosStatic>).mockImplementationOnce(function (opt?: 
axios.AxiosRequestConfig): axios.AxiosPromise {
-      // console.log(opt, JSON.stringify(query,undefined,2))
-      expect(opt).toBeDefined();
-      if (!opt)
-        return Promise.reject();
-
-      // expect(query.request).toStrictEqual(opt.data);
-      // expect(query.params).toStrictEqual(opt.params);
-      if ('get' in query) {
-        expect(opt.method).toBe('get');
-        expect(opt.url).toBe(query.get);
-      }
-      if ('post' in query) {
-        expect(opt.method).toBe('post');
-        expect(opt.url).toBe(query.post);
-      }
-      if ('delete' in query) {
-        expect(opt.method).toBe('delete');
-        expect(opt.url).toBe(query.delete);
-      }
-      if ('patch' in query) {
-        expect(opt.method).toBe('patch');
-        expect(opt.url).toBe(query.patch);
-      }
-      return ({ data: query.response, config: {} } as any);
-    } as any)
-}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]