From 5c847b94e648e797e8d34cf7a2855917bed7a574 Mon Sep 17 00:00:00 2001 From: Jerko Steiner Date: Sun, 20 Jan 2019 21:39:32 +0100 Subject: [PATCH] Add full test for LoginForm --- packages/client/src/components/index.ts | 1 - .../client/src/http/HTTPClientMock.test.ts | 26 ------ packages/client/src/http/HTTPClientMock.ts | 44 ---------- packages/client/src/login/LoginForm.tsx | 9 +- packages/client/src/login/index.test.tsx | 44 +++++++++- .../src/test-utils/HTTPClientMock.test.ts | 44 ++++++++++ .../client/src/test-utils/HTTPClientMock.ts | 82 +++++++++++++++++++ packages/client/src/test-utils/TestUtils.tsx | 6 +- packages/client/src/test-utils/index.ts | 1 + 9 files changed, 177 insertions(+), 80 deletions(-) delete mode 100644 packages/client/src/http/HTTPClientMock.test.ts delete mode 100644 packages/client/src/http/HTTPClientMock.ts create mode 100644 packages/client/src/test-utils/HTTPClientMock.test.ts create mode 100644 packages/client/src/test-utils/HTTPClientMock.ts diff --git a/packages/client/src/components/index.ts b/packages/client/src/components/index.ts index c0ed20c..543a729 100644 --- a/packages/client/src/components/index.ts +++ b/packages/client/src/components/index.ts @@ -1,4 +1,3 @@ export * from './Button' // export * from './Component' export * from './Input' -export * from './LoginForm' diff --git a/packages/client/src/http/HTTPClientMock.test.ts b/packages/client/src/http/HTTPClientMock.test.ts deleted file mode 100644 index a916907..0000000 --- a/packages/client/src/http/HTTPClientMock.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {HTTPClientMock} from './HTTPClientMock' -import {getError} from '../test-utils' - -describe('HTTPClientMock', () => { - - const http = new HTTPClientMock() - - describe('mockAdd and mockClear', () => { - it('adds a mock', async () => { - const value = {a: 1} - http.mockAdd({ - method: 'get', - url: '/test', - }, value) - - const result = await http.get('/test') - expect(result).toBe(value) - - http.mockClear() - - const error = await getError(http.get('/test')) - expect(error.message).toMatch(/mock/i) - }) - }) - -}) diff --git a/packages/client/src/http/HTTPClientMock.ts b/packages/client/src/http/HTTPClientMock.ts deleted file mode 100644 index a56449d..0000000 --- a/packages/client/src/http/HTTPClientMock.ts +++ /dev/null @@ -1,44 +0,0 @@ -import {HTTPClient} from './HTTPClient' -import {IRoutes} from '@rondo/common' -import {IRequest} from './IRequest' -import {IResponse} from './IResponse' - -export class HTTPClientMock extends HTTPClient { - mocks: {[key: string]: any} = {} - - constructor() { - super() - } - - createRequestor() { - return { - request: (r: IRequest): Promise => { - return new Promise((resolve, reject) => { - const key = this.serialize(r) - if (!this.mocks.hasOwnProperty(key)) { - setImmediate(() => { - reject(new Error('No mock for request: ' + key)) - }) - return - } - setImmediate(() => { - resolve({data: this.mocks[key]}) - }) - }) - }, - } - } - - serialize(r: IRequest) { - return JSON.stringify(r, null, ' ') - } - - mockAdd(r: IRequest, response: any) { - this.mocks[this.serialize(r)] = response - } - - mockClear() { - this.mocks = [] - } - -} diff --git a/packages/client/src/login/LoginForm.tsx b/packages/client/src/login/LoginForm.tsx index af2ac97..c325c8c 100644 --- a/packages/client/src/login/LoginForm.tsx +++ b/packages/client/src/login/LoginForm.tsx @@ -4,7 +4,8 @@ import {ICredentials} from '@rondo/common' export interface ILoginFormProps { error?: string - onSubmit: (credentials: ICredentials) => void + onSubmit: (credentials: ICredentials) => Promise + onSuccess: () => void } export interface ILoginFormState extends ICredentials {} @@ -21,8 +22,9 @@ export class LoginForm extends React.PureComponent< password: '', } } - handleSubmit = () => { - this.props.onSubmit(this.state) + handleSubmit = async () => { + await this.props.onSubmit(this.state) + this.props.onSuccess() } handleChange = (name: string, value: string) => { this.setState( @@ -32,6 +34,7 @@ export class LoginForm extends React.PureComponent< render() { return (
+

{this.props.error}

{ - const loginActions = new Feature.LoginActions({} as any) + const http = new HTTPClientMock() + const loginActions = new Feature.LoginActions(http) const t = test.withProvider({ reducers: {Login: Feature.Login}, @@ -19,4 +21,40 @@ describe('Login', () => { t.render() }) + describe('submit', () => { + + const data = {username: 'user', password: 'pass'} + const onSuccess = jest.fn() + let node: Element + beforeEach(() => { + http.mockAdd({ + method: 'post', + url: '/auth/login', + data, + }, { id: 123 }) + + node = t.render({onSuccess}).node + T.Simulate.change( + node.querySelector('input[name="username"]')!, + {target: {value: 'user'}} as any, + ) + T.Simulate.change( + node.querySelector('input[name="password"]')!, + {target: {value: 'pass'}} as any, + ) + }) + + it('should submit a form', async () => { + T.Simulate.submit(node) + const {req} = await http.wait() + expect(req).toEqual({ + method: 'post', + url: '/auth/login', + data, + }) + expect(onSuccess.mock.calls.length).toBe(1) + }) + + }) + }) diff --git a/packages/client/src/test-utils/HTTPClientMock.test.ts b/packages/client/src/test-utils/HTTPClientMock.test.ts new file mode 100644 index 0000000..d901d8f --- /dev/null +++ b/packages/client/src/test-utils/HTTPClientMock.test.ts @@ -0,0 +1,44 @@ +import {HTTPClientMock} from './HTTPClientMock' +import {getError} from './getError' + +describe('HTTPClientMock', () => { + + const http = new HTTPClientMock() + + const value = {a: 1} + beforeEach(() => { + http.mockClear() + http.mockAdd({ + method: 'get', + url: '/test', + }, value) + }) + + describe('mockAdd and mockClear', () => { + it('adds a mock', async () => { + const result = await http.get('/test') + expect(result).toBe(value) + + http.mockClear() + + const error = await getError(http.get('/test')) + expect(error.message).toMatch(/mock/i) + }) + }) + + describe('await wait', () => { + it('waits for a request', async () => { + const promise = http.get('/test') + const result1 = await http.wait() + const result2 = await promise + expect(result1.res.data).toBe(result2) + expect(result2).toBe(value) + expect(result1.req).toBe(http.requests[0]) + expect(result1.req).toEqual({ + method: 'get', + url: '/test', + }) + }) + }) + +}) diff --git a/packages/client/src/test-utils/HTTPClientMock.ts b/packages/client/src/test-utils/HTTPClientMock.ts new file mode 100644 index 0000000..f114375 --- /dev/null +++ b/packages/client/src/test-utils/HTTPClientMock.ts @@ -0,0 +1,82 @@ +import {HTTPClient} from '../http/HTTPClient' +import {IRoutes} from '@rondo/common' +import {IRequest} from '../http/IRequest' +import {IResponse} from '../http/IResponse' + +interface IReqRes { + req: IRequest + res: IResponse +} + +export class HTTPClientMock extends HTTPClient { + mocks: {[key: string]: any} = {} + requests: IRequest[] = [] + + constructor() { + super() + } + + createRequestor() { + return { + request: (req: IRequest): Promise => { + this.requests.push(req) + return new Promise((resolve, reject) => { + const key = this.serialize(req) + if (!this.mocks.hasOwnProperty(key)) { + setImmediate(() => { + const err = new Error('No mock for request: ' + key) + reject(err) + this.notify(err) + }) + return + } + const data = this.mocks[key] + const res: IResponse = {data} + setImmediate(() => { + resolve(res) + this.notify({req, res}) + }) + }) + }, + } + } + + serialize(req: IRequest) { + return JSON.stringify(req, null, ' ') + } + + mockAdd(req: IRequest, res: any) { + this.mocks[this.serialize(req)] = res + } + + mockClear() { + this.requests = [] + this.mocks = {} + } + + protected waitPromise?: { + resolve: (r: IReqRes) => void + reject: (err: Error) => void + } + + protected notify(r: IReqRes | Error) { + if (!this.waitPromise) { + return + } + const waitPromise = this.waitPromise + this.waitPromise = undefined + if (r instanceof Error) { + waitPromise.reject(r) + return + } + waitPromise.resolve(r) + } + + async wait(): Promise { + expect(this.waitPromise).toBe(undefined) + return new Promise((resolve, reject) => { + this.waitPromise = {resolve, reject} + }) + } + +} diff --git a/packages/client/src/test-utils/TestUtils.tsx b/packages/client/src/test-utils/TestUtils.tsx index 02c1c97..d3ea043 100644 --- a/packages/client/src/test-utils/TestUtils.tsx +++ b/packages/client/src/test-utils/TestUtils.tsx @@ -33,7 +33,7 @@ interface IRenderParams { export class TestUtils { render(jsx: JSX.Element) { const component = T.renderIntoDocument(jsx) as React.Component - const node = ReactDOM.findDOMNode(component) + const node = ReactDOM.findDOMNode(component) as Element return {component, node} } @@ -64,10 +64,10 @@ export class TestUtils { }) const Component = params.connector.connect(params.select) - const render = () => { + const render = (additionalProps: {[key: string]: any} = {}) => { return this.render( - + , ) } diff --git a/packages/client/src/test-utils/index.ts b/packages/client/src/test-utils/index.ts index 4567837..a9191e9 100644 --- a/packages/client/src/test-utils/index.ts +++ b/packages/client/src/test-utils/index.ts @@ -1,2 +1,3 @@ +export * from './HTTPClientMock' export * from './TestUtils' export * from './getError'