import React from 'react' import ReactDOM from 'react-dom' import T from 'react-dom/test-utils' import {createStore, TStateSelector, WaitMiddleware} from '@rondo.dev/redux' import {Provider} from 'react-redux' import { Action, AnyAction, DeepPartial, Reducer, ReducersMapObject, combineReducers, Store as ReduxStore, Unsubscribe, } from 'redux' import { format } from 'util' interface IRenderParams { reducers: ReducersMapObject select: TStateSelector // getComponent: ( // select: TStateSelector) => React.ComponentType, // customJSX?: ( // Component: React.ComponentType, // props: Props, // ) => JSX.Element } export class TestUtils { /** * Create a redux store */ readonly createStore = createStore render(jsx: JSX.Element) { const $div = document.createElement('div') const component = ReactDOM.render(
{jsx}
, $div) as unknown as React.Component const node = (ReactDOM.findDOMNode(component) as Element).children[0] return { component, node, } } combineReducers(reducers: ReducersMapObject): Reducer combineReducers( reducers: ReducersMapObject, ): Reducer { return combineReducers(reducers) } /** * Creates a redux store, connects a component, and provides the `render` * method to render the connected component with a `Provider`. */ withProvider = AnyAction>( params: IRenderParams, ) { const {reducers, select} = params const waitMiddleware = new WaitMiddleware() let store = this.createStore({ reducer: this.combineReducers(reducers), extraMiddleware: [waitMiddleware.handle], })() const withState = (state: DeepPartial) => { store = this.createStore({ reducer: this.combineReducers(reducers), })(state) return {withComponent} } const withComponent = ( getComponent: (select: TStateSelector) => React.ComponentType, ) => { const Component = getComponent(select) type CreateJSX = ( Component: React.ComponentType, props: Props, ) => JSX.Element let createJSX: CreateJSX | undefined const render = (props: Props) => { const recorder = waitMiddleware.record() const jsx = createJSX ? createJSX(Component, props) : const result = this.render( {jsx} , ) return { ...result, async waitForActions(timeout = 2000) { await waitMiddleware.waitForRecorded(recorder) }, } } const withJSX = (localCreateJSX: CreateJSX) => { createJSX = localCreateJSX return self } const self: ISelf< Props, typeof store, typeof Component, CreateJSX > = { render, store, Component, withJSX, } return self } return {withState, withComponent} } } interface ISelf { render: (props: Props) => { component: React.Component node: Element waitForActions(timeout?: number): Promise } store: Store Component: Component withJSX: (localCreateJSX: CreateJSX) => ISelf }