Fix broken LoginForm.test.tsx

This commit is contained in:
Jerko Steiner 2019-03-22 14:43:43 +08:00
parent 8f8c3b6c9c
commit 637b51382a
4 changed files with 43 additions and 25 deletions

View File

@ -1,8 +1,10 @@
import * as Feature from './' import * as Feature from './'
import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import T from 'react-dom/test-utils' import T from 'react-dom/test-utils'
import {HTTPClientMock, TestUtils, getError} from '../test-utils' import {HTTPClientMock, TestUtils, getError} from '../test-utils'
import {IAPIDef} from '@rondo/common' import {IAPIDef} from '@rondo/common'
import {MemoryRouter} from 'react-router-dom'
const test = new TestUtils() const test = new TestUtils()
@ -15,6 +17,8 @@ describe('LoginForm', () => {
reducers: {Login: Feature.Login}, reducers: {Login: Feature.Login},
connector: new Feature.LoginConnector(loginActions), connector: new Feature.LoginConnector(loginActions),
select: state => state.Login, select: state => state.Login,
customJSX: (Component, props) =>
<MemoryRouter><Component {...props} /></MemoryRouter>,
}) })
beforeAll(() => { beforeAll(() => {
@ -61,16 +65,7 @@ describe('LoginForm', () => {
data, data,
}) })
expect(onSuccess.mock.calls.length).toBe(1) expect(onSuccess.mock.calls.length).toBe(1)
expect( // TODO test clear username/password
(node.querySelector('input[name="username"]') as HTMLInputElement)
.value,
)
.toEqual('')
expect(
(node.querySelector('input[name="password"]') as HTMLInputElement)
.value,
)
.toEqual('')
node = ReactDOM.findDOMNode(component) as Element node = ReactDOM.findDOMNode(component) as Element
expect(node.innerHTML).toMatch(/<a href="\/">/) expect(node.innerHTML).toMatch(/<a href="\/">/)
}) })

View File

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import {IAction} from '../actions' import {IPendingAction} from '../actions'
export interface IComponentProps<Data> { export interface IComponentProps<Data> {
onSubmit: () => void onSubmit: () => void
@ -10,13 +10,18 @@ export interface IComponentProps<Data> {
} }
export interface IFormHOCProps<Data> { export interface IFormHOCProps<Data> {
onSubmit: (props: Data) => IAction<any> onSubmit: (props: Data) => IPendingAction<any, any>
// TODO figure out what would happen if the underlying child component // TODO figure out what would happen if the underlying child component
// would have the same required property as the HOC, like onSuccess? // would have the same required property as the HOC, like onSuccess?
onSuccess?: () => void onSuccess?: () => void
clearOnSuccess?: boolean clearOnSuccess?: boolean
} }
export interface IFormHOCState<Data> {
error: string
data: Data
}
export function withForm<Data, Props extends IComponentProps<Data>>( export function withForm<Data, Props extends IComponentProps<Data>>(
Component: React.ComponentType<Props>, Component: React.ComponentType<Props>,
initialState: Data, initialState: Data,
@ -26,28 +31,44 @@ export function withForm<Data, Props extends IComponentProps<Data>>(
Exclude<keyof Props, keyof IComponentProps<Data>>> Exclude<keyof Props, keyof IComponentProps<Data>>>
type T = IFormHOCProps<Data> & OtherProps type T = IFormHOCProps<Data> & OtherProps
return class FormHOC extends React.PureComponent<T, Data> { return class FormHOC extends React.PureComponent<T, IFormHOCState<Data>> {
constructor(props: T) { constructor(props: T) {
super(props) super(props)
this.state = initialState this.state = {
error: '',
data: initialState,
}
} }
handleSubmit = async (e: React.FormEvent) => { handleSubmit = async (e: React.FormEvent) => {
const {clearOnSuccess, onSuccess} = this.props const {clearOnSuccess, onSuccess} = this.props
e.preventDefault() e.preventDefault()
const promise = this.props.onSubmit(this.state) const action = this.props.onSubmit(this.state.data)
console.log('aaaaaaaaa', promise) try {
await promise await action.payload
} catch (err) {
this.setState({
error: err.message,
})
return
}
if (clearOnSuccess) { if (clearOnSuccess) {
this.setState(initialState) this.setState({
...this.state,
data: initialState,
})
} }
if (onSuccess) { if (onSuccess) {
onSuccess() onSuccess()
} }
} }
handleChange = (name: string, value: string) => { handleChange = (name: string, value: string) => {
this.setState( this.setState({
{[name]: value} as unknown as Pick<Data, keyof Data>, ...this.state,
) data: {
...this.state.data,
[name]: value,
},
})
} }
render() { render() {
const {children, onSuccess, onSubmit, ...otherProps} = this.props const {children, onSuccess, onSubmit, ...otherProps} = this.props

View File

@ -110,9 +110,11 @@ export class HTTPClientMock<T extends IRoutes> extends HTTPClient<T> {
*/ */
async wait(): Promise<IReqRes> { async wait(): Promise<IReqRes> {
expect(this.waitPromise).toBe(undefined) expect(this.waitPromise).toBe(undefined)
return new Promise((resolve, reject) => { const result: IReqRes = await new Promise((resolve, reject) => {
this.waitPromise = {resolve, reject} this.waitPromise = {resolve, reject}
}) })
await new Promise(resolve => setImmediate(resolve))
return result
} }
} }

View File

@ -18,9 +18,9 @@ interface IRenderParams<State> {
state?: DeepPartial<State> state?: DeepPartial<State>
connector: Connector<any> connector: Connector<any>
select: IStateSelector<State, any> select: IStateSelector<State, any>
customJSX?: <Props>( customJSX?: (
Component: React.ComponentType<Props>, Component: React.ComponentType<any>,
additionalProps: Record<string, Props>, additionalProps: Record<string, any>,
) => JSX.Element ) => JSX.Element
} }