remove old testing function, use web-utils
This commit is contained in:
parent
7fe5f3767e
commit
dbb3529b49
@ -10,7 +10,7 @@
|
|||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rimraf dist lib tsconfig.tsbuildinfo",
|
"clean": "rimraf dist lib tsconfig.tsbuildinfo",
|
||||||
"test": "pnpm compile && mocha --require source-map-support/register 'dist/**/*.test.js' 'dist/**/test.js'",
|
"test": "mocha --require source-map-support/register 'dist/**/*.test.js' 'dist/**/test.js'",
|
||||||
"test:coverage": "nyc pnpm test",
|
"test:coverage": "nyc pnpm test",
|
||||||
"compile": "tsc && ./build-fast-with-linaria.mjs",
|
"compile": "tsc && ./build-fast-with-linaria.mjs",
|
||||||
"prepare": "tsc",
|
"prepare": "tsc",
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
* @author Sebastian Javier Marchano (sebasjm)
|
* @author Sebastian Javier Marchano (sebasjm)
|
||||||
*/
|
*/
|
||||||
import { setupI18n } from "@gnu-taler/taler-util";
|
import { setupI18n } from "@gnu-taler/taler-util";
|
||||||
import { parseGroupImport } from "@gnu-taler/web-util/lib/index.browser";
|
import { parseGroupImport, tests } from "@gnu-taler/web-util/lib/index.browser";
|
||||||
import chromeAPI from "./platform/chrome.js";
|
import chromeAPI from "./platform/chrome.js";
|
||||||
import { setupPlatform } from "./platform/foreground.js";
|
import { setupPlatform } from "./platform/foreground.js";
|
||||||
|
|
||||||
@ -28,8 +28,8 @@ import * as cta from "./cta/index.stories.js";
|
|||||||
import * as mui from "./mui/index.stories.js";
|
import * as mui from "./mui/index.stories.js";
|
||||||
import * as popup from "./popup/index.stories.js";
|
import * as popup from "./popup/index.stories.js";
|
||||||
import * as wallet from "./wallet/index.stories.js";
|
import * as wallet from "./wallet/index.stories.js";
|
||||||
import { renderNodeOrBrowser } from "./test-utils.js";
|
// import { renderNodeOrBrowser } from "./test-utils.js";
|
||||||
import { h, VNode } from "preact";
|
import { h, VNode, ComponentChildren } from "preact";
|
||||||
import { AlertProvider } from "./context/alert.js";
|
import { AlertProvider } from "./context/alert.js";
|
||||||
|
|
||||||
setupI18n("en", { en: {} });
|
setupI18n("en", { en: {} });
|
||||||
@ -43,19 +43,22 @@ describe("All the examples:", () => {
|
|||||||
describe(`Component ${component.name}:`, () => {
|
describe(`Component ${component.name}:`, () => {
|
||||||
component.examples.forEach((example) => {
|
component.examples.forEach((example) => {
|
||||||
it(`should render example: ${example.name}`, () => {
|
it(`should render example: ${example.name}`, () => {
|
||||||
function C(): VNode {
|
tests.renderUI(example.render, DefaultTestingContext);
|
||||||
const B = h(example.render.component, example.render.props);
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
function DefaultTestingContext({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: ComponentChildren;
|
||||||
|
}): VNode {
|
||||||
//FIXME:
|
//FIXME:
|
||||||
//some components push the alter in the UI function
|
//some components push the alter in the UI function
|
||||||
//that's not correct, should be moved into the state function
|
//that's not correct, should be moved into the state function
|
||||||
// until then, we ran the tests with the alert provider
|
// until then, we ran the tests with the alert provider
|
||||||
return h(AlertProvider, { children: B }, B);
|
return h(AlertProvider, { children });
|
||||||
}
|
}
|
||||||
renderNodeOrBrowser(C, {});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
@ -21,204 +21,18 @@ import {
|
|||||||
WalletCoreRequestType,
|
WalletCoreRequestType,
|
||||||
WalletCoreResponseType,
|
WalletCoreResponseType,
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
|
import { TranslationProvider } from "@gnu-taler/web-util/lib/index.browser";
|
||||||
import {
|
import {
|
||||||
ComponentChildren,
|
ComponentChildren,
|
||||||
Fragment,
|
|
||||||
FunctionalComponent,
|
FunctionalComponent,
|
||||||
VNode,
|
VNode,
|
||||||
h as create,
|
h as create,
|
||||||
options,
|
|
||||||
render as renderIntoDom,
|
|
||||||
} from "preact";
|
} from "preact";
|
||||||
import { render as renderToString } from "preact-render-to-string";
|
|
||||||
import { AlertProvider } from "./context/alert.js";
|
import { AlertProvider } from "./context/alert.js";
|
||||||
import { BackendProvider } from "./context/backend.js";
|
import { BackendProvider } from "./context/backend.js";
|
||||||
|
import { strings } from "./i18n/strings.js";
|
||||||
import { nullFunction } from "./mui/handlers.js";
|
import { nullFunction } from "./mui/handlers.js";
|
||||||
import { BackgroundApiClient, wxApi } from "./wxApi.js";
|
import { BackgroundApiClient, wxApi } from "./wxApi.js";
|
||||||
import { TranslationProvider } from "@gnu-taler/web-util/lib/index.browser";
|
|
||||||
import { strings } from "./i18n/strings.js";
|
|
||||||
|
|
||||||
// When doing tests we want the requestAnimationFrame to be as fast as possible.
|
|
||||||
// without this option the RAF will timeout after 100ms making the tests slower
|
|
||||||
options.requestAnimationFrame = (fn: () => void) => {
|
|
||||||
return fn();
|
|
||||||
};
|
|
||||||
|
|
||||||
export function createExample<Props>(
|
|
||||||
Component: FunctionalComponent<Props>,
|
|
||||||
props: Partial<Props> | (() => Partial<Props>),
|
|
||||||
): ComponentChildren {
|
|
||||||
//FIXME: props are evaluated on build time
|
|
||||||
// in some cases we want to evaluated the props on render time so we can get some relative timestamp
|
|
||||||
// check how we can build evaluatedProps in render time
|
|
||||||
const evaluatedProps = typeof props === "function" ? props() : props;
|
|
||||||
const Render = (args: any): VNode => create(Component, args);
|
|
||||||
// Render.args = evaluatedProps;
|
|
||||||
|
|
||||||
return {
|
|
||||||
component: Render,
|
|
||||||
props: evaluatedProps,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createExampleWithCustomContext<Props, ContextProps>(
|
|
||||||
Component: FunctionalComponent<Props>,
|
|
||||||
props: Partial<Props> | (() => Partial<Props>),
|
|
||||||
ContextProvider: FunctionalComponent<ContextProps>,
|
|
||||||
contextProps: Partial<ContextProps>,
|
|
||||||
): ComponentChildren {
|
|
||||||
const evaluatedProps = typeof props === "function" ? props() : props;
|
|
||||||
const Render = (args: any): VNode => create(Component, args);
|
|
||||||
const WithContext = (args: any): VNode =>
|
|
||||||
create(ContextProvider, {
|
|
||||||
...contextProps,
|
|
||||||
children: [Render(args)],
|
|
||||||
} as any);
|
|
||||||
|
|
||||||
return {
|
|
||||||
component: WithContext,
|
|
||||||
props: evaluatedProps,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function NullLink({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children?: ComponentChildren;
|
|
||||||
}): VNode {
|
|
||||||
return create("a", { children, href: "javascript:void(0);" });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function renderNodeOrBrowser(Component: any, args: any): void {
|
|
||||||
const vdom = create(Component, args);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type RecursiveState<S> = S | (() => RecursiveState<S>);
|
|
||||||
|
|
||||||
interface Mounted<T> {
|
|
||||||
unmount: () => void;
|
|
||||||
pullLastResultOrThrow: () => Exclude<T, VoidFunction>;
|
|
||||||
assertNoPendingUpdate: () => void;
|
|
||||||
// waitNextUpdate: (s?: string) => Promise<void>;
|
|
||||||
waitForStateUpdate: () => Promise<boolean>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isNode = typeof window === "undefined";
|
|
||||||
|
|
||||||
export function mountHook<T extends object>(
|
|
||||||
callback: () => RecursiveState<T>,
|
|
||||||
Context?: ({ children }: { children: any }) => VNode,
|
|
||||||
): Mounted<T> {
|
|
||||||
let lastResult: Exclude<T, VoidFunction> | Error | null = null;
|
|
||||||
|
|
||||||
const listener: Array<() => void> = [];
|
|
||||||
|
|
||||||
// component that's going to hold the hook
|
|
||||||
function Component(): VNode {
|
|
||||||
try {
|
|
||||||
let componentOrResult = callback();
|
|
||||||
while (typeof componentOrResult === "function") {
|
|
||||||
componentOrResult = componentOrResult();
|
|
||||||
}
|
|
||||||
//typecheck fails here
|
|
||||||
const l: Exclude<T, () => void> = componentOrResult as any;
|
|
||||||
lastResult = l;
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof Error) {
|
|
||||||
lastResult = e;
|
|
||||||
} else {
|
|
||||||
lastResult = new Error(`mounting the hook throw an exception: ${e}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// notify to everyone waiting for an update and clean the queue
|
|
||||||
listener.splice(0, listener.length).forEach((cb) => cb());
|
|
||||||
return create(Fragment, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the vdom with context if required
|
|
||||||
const vdom = !Context
|
|
||||||
? create(Component, {})
|
|
||||||
: create(Context, { children: [create(Component, {})] });
|
|
||||||
|
|
||||||
const customElement = {} as Element;
|
|
||||||
const parentElement = isNode ? customElement : document.createElement("div");
|
|
||||||
if (!isNode) {
|
|
||||||
document.body.appendChild(parentElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderIntoDom(vdom, parentElement);
|
|
||||||
|
|
||||||
// clean up callback
|
|
||||||
function unmount(): void {
|
|
||||||
if (!isNode) {
|
|
||||||
document.body.removeChild(parentElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function pullLastResult(): Exclude<T | Error | null, VoidFunction> {
|
|
||||||
const copy: Exclude<T | Error | null, VoidFunction> = lastResult;
|
|
||||||
lastResult = null;
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
function pullLastResultOrThrow(): Exclude<T, VoidFunction> {
|
|
||||||
const r = pullLastResult();
|
|
||||||
if (r instanceof Error) throw r;
|
|
||||||
if (!r) throw Error("there was no last result");
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function assertNoPendingUpdate(): Promise<void> {
|
|
||||||
await new Promise((res, rej) => {
|
|
||||||
const tid = setTimeout(() => {
|
|
||||||
res(undefined);
|
|
||||||
}, 10);
|
|
||||||
|
|
||||||
listener.push(() => {
|
|
||||||
clearTimeout(tid);
|
|
||||||
rej(
|
|
||||||
Error(`Expecting no pending result but the hook got updated.
|
|
||||||
If the update was not intended you need to check the hook dependencies
|
|
||||||
(or dependencies of the internal state) but otherwise make
|
|
||||||
sure to consume the result before ending the test.`),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const r = pullLastResult();
|
|
||||||
if (r)
|
|
||||||
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> {
|
|
||||||
return await new Promise((res, rej) => {
|
|
||||||
const tid = setTimeout(() => {
|
|
||||||
res(false);
|
|
||||||
}, 10);
|
|
||||||
|
|
||||||
listener.push(() => {
|
|
||||||
clearTimeout(tid);
|
|
||||||
res(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
unmount,
|
|
||||||
pullLastResultOrThrow,
|
|
||||||
waitForStateUpdate,
|
|
||||||
assertNoPendingUpdate,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// export const nullFunction: any = () => null;
|
// export const nullFunction: any = () => null;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user