gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 07/13: render hook and render ui are not the same f


From: gnunet
Subject: [taler-wallet-core] 07/13: render hook and render ui are not the same function (node and browser)
Date: Fri, 21 Apr 2023 16:07:04 +0200

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

sebasjm pushed a commit to branch master
in repository wallet-core.

commit 3772ff85db61fd2260659e370e882d08d698f981
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Fri Apr 21 10:43:59 2023 -0300

    render hook and render ui  are not the same function (node and browser)
---
 packages/web-util/src/tests/hook.ts | 124 +++++++++++++++++++++++-------------
 1 file changed, 80 insertions(+), 44 deletions(-)

diff --git a/packages/web-util/src/tests/hook.ts 
b/packages/web-util/src/tests/hook.ts
index fb9f979e5..58ac7d8c0 100644
--- a/packages/web-util/src/tests/hook.ts
+++ b/packages/web-util/src/tests/hook.ts
@@ -15,14 +15,16 @@
  */
 
 import {
-  ComponentChildren,
   Fragment,
+  FunctionComponent,
   FunctionalComponent,
+  VNode,
   h as create,
   options,
   render as renderIntoDom,
-  VNode
 } from "preact";
+import { render as renderToString } from "preact-render-to-string";
+import { ExampleItem, ExampleItemSetup } from "../stories.js";
 
 // This library is expected to be included in testing environment only
 // When doing tests we want the requestAnimationFrame to be as fast as 
possible.
@@ -31,51 +33,83 @@ options.requestAnimationFrame = (fn: () => void) => {
   return fn();
 };
 
-export function createExample<Props>(
+/**
+ *
+ * @param Component component to be tested
+ * @param props allow partial props for easier example setup
+ * @param contextProps if the context requires params for this example
+ * @returns
+ */
+export function createExample<T extends object, Props extends object>(
   Component: FunctionalComponent<Props>,
   props: Partial<Props> | (() => Partial<Props>),
-): ComponentChildren {
+  contextProps?: T | (() => T),
+): ExampleItemSetup<Props> {
   const evaluatedProps = typeof props === "function" ? props() : props;
   const Render = (args: any): VNode => create(Component, args);
-
+  const evaluatedContextProps =
+    typeof contextProps === "function" ? contextProps() : contextProps;
   return {
     component: Render,
-    props: evaluatedProps,
+    props: evaluatedProps as Props,
+    contextProps: !evaluatedContextProps ? {} : evaluatedContextProps,
   };
 }
 
-const isNode = typeof window === "undefined";
-
 /**
- * To be used on automated unit test.
- * So test will run under node or browser
+ * Should render HTML on node and browser
+ * Browser: mount update and unmount
+ * Node: render to string
+ *
  * @param Component
  * @param args
  */
-export function renderNodeOrBrowser(
-  Component: any,
-  args: any,
-  Context?: any,
-): void {
+export function renderUI(example: ExampleItemSetup<any>, Context?: any): void {
   const vdom = !Context
-    ? create(Component, args)
-    : create(Context, { children: [create(Component, args)] });
+    ? create(example.component, example.props)
+    : create(Context, {
+        ...example.contextProps,
+        children: [create(example.component, example.props)],
+      });
 
-  const customElement = {} as Element;
-  const parentElement = isNode ? customElement : document.createElement("div");
-  if (!isNode) {
-    document.body.appendChild(parentElement);
-  }
-  // renderIntoDom works also in nodejs
-  // if the VirtualDOM is composed only by functional components
-  // then no called is going to be made to the DOM api.
-  // vdom should not have any 'div' or other html component
-  renderIntoDom(vdom, parentElement);
-
-  if (!isNode) {
-    document.body.removeChild(parentElement);
+  if (typeof window === "undefined") {
+    renderToString(vdom);
+  } else {
+    const div = document.createElement("div");
+    document.body.appendChild(div);
+    renderIntoDom(vdom, div);
+    renderIntoDom(null, div);
+    document.body.removeChild(div);
   }
 }
+
+/**
+ * No need to render.
+ * Should mount, update and run effects.
+ *
+ * Browser: mount update and unmount
+ * Node: mount on a mock virtual dom
+ *
+ * Mounting hook doesn't use DOM api so is
+ * safe to use normal mounting api in node
+ *
+ * @param Component
+ * @param props
+ * @param Context
+ */
+function renderHook(
+  Component: FunctionComponent,
+  Context?: ({ children }: { children: any }) => VNode | null,
+): void {
+  const vdom = !Context
+    ? create(Component, {})
+    : create(Context, { children: [create(Component, {})] });
+
+  //use normal mounting API since we expect
+  //useEffect to be called ( and similar APIs )
+  renderIntoDom(vdom, {} as Element);
+}
+
 type RecursiveState<S> = S | (() => RecursiveState<S>);
 
 interface Mounted<T> {
@@ -89,14 +123,13 @@ interface Mounted<T> {
 /**
  * Manual API mount the hook and return testing API
  * Consider using hookBehaveLikeThis() function
- * 
+ *
  * @param hookToBeTested
  * @param Context
- * 
+ *
  * @returns testing API
  */
-// eslint-disable-next-line @typescript-eslint/ban-types
-export function mountHook<T extends object>(
+function mountHook<T extends object>(
   hookToBeTested: () => RecursiveState<T>,
   Context?: ({ children }: { children: any }) => VNode | null,
 ): Mounted<T> {
@@ -108,6 +141,11 @@ export function mountHook<T extends object>(
   function Component(): VNode {
     try {
       let componentOrResult = hookToBeTested();
+
+      // special loop
+      // since Taler use a special type of hook that can return
+      // a function and it will be treated as a composed component
+      // then tests should be aware of it and reproduce the same behavior
       while (typeof componentOrResult === "function") {
         componentOrResult = componentOrResult();
       }
@@ -127,7 +165,7 @@ export function mountHook<T extends object>(
     return create(Fragment, {});
   }
 
-  renderNodeOrBrowser(Component, {}, Context);
+  renderHook(Component, Context);
 
   function pullLastResult(): Exclude<T | Error | null, VoidFunction> {
     const copy: Exclude<T | Error | null, VoidFunction> = lastResult;
@@ -165,7 +203,6 @@ export function mountHook<T extends object>(
       return Promise.resolve(false);
     }
     return Promise.resolve(true);
-    // throw Error(`There are still pending results.
     //  This may happen because the hook did a new update but the test didn't 
consume the result using pullLastResult`);
   }
   async function waitForStateUpdate(): Promise<boolean> {
@@ -182,7 +219,6 @@ export function mountHook<T extends object>(
   }
 
   return {
-    // unmount,
     pullLastResultOrThrow,
     waitForStateUpdate,
     assertNoPendingUpdate,
@@ -209,13 +245,13 @@ interface HookTestResultError {
 
 /**
  * Main testing driver.
- * It will assert that there are no more and no less hook updates than 
expected. 
- * 
+ * It will assert that there are no more and no less hook updates than 
expected.
+ *
  * @param hookFunction hook function to be tested
  * @param props initial props for the hook
  * @param checks step by step state validation
  * @param Context additional testing context for overrides
- * 
+ *
  * @returns testing result, should also be checked to be "ok"
  */
 // eslint-disable-next-line @typescript-eslint/ban-types
@@ -228,7 +264,7 @@ export async function hookBehaveLikeThis<T extends object, 
PropsType>(
   const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } =
     mountHook<T>(() => hookFunction(props), Context);
 
-  const [firstCheck, ...resultOfTheChecks] = checks;
+  const [firstCheck, ...restOfTheChecks] = checks;
   {
     const state = pullLastResultOrThrow();
     const checkError = firstCheck(state);
@@ -236,13 +272,13 @@ export async function hookBehaveLikeThis<T extends 
object, PropsType>(
       return {
         result: "fail",
         index: 0,
-        error: `Check return not undefined error: ${checkError}`,
+        error: `First check returned with error: ${checkError}`,
       };
     }
   }
 
   let index = 1;
-  for (const check of resultOfTheChecks) {
+  for (const check of restOfTheChecks) {
     const hasNext = await waitForStateUpdate();
     if (!hasNext) {
       return {
@@ -257,7 +293,7 @@ export async function hookBehaveLikeThis<T extends object, 
PropsType>(
       return {
         result: "fail",
         index,
-        error: `Check return not undefined error: ${checkError}`,
+        error: `Check returned with error: ${checkError}`,
       };
     }
     index++;

-- 
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]