Add captcha to main site, fix tests of styled-components

This commit is contained in:
Jerko Steiner 2019-11-04 11:07:46 -04:00
parent 262ba2c2af
commit c27e66a8e2
11 changed files with 43 additions and 11 deletions

View File

@ -13,5 +13,6 @@ module.exports = {
'jsx', 'jsx',
], ],
setupFiles: ['<rootDir>/jest.setup.js'], setupFiles: ['<rootDir>/jest.setup.js'],
setupFilesAfterEnv: ['<rootDir>/src/jest.env.ts'],
verbose: false, verbose: false,
} }

View File

@ -0,0 +1,3 @@
import { TestUtils } from '@rondo.dev/react-test'
import { theme } from './theme'
TestUtils.defaultTheme = theme

View File

@ -36,7 +36,7 @@ export class LoginActions {
) )
} }
register = (profile: NewUser) => { register = (profile: NewUser & { captcha: string }) => {
return createPendingAction( return createPendingAction(
this.http.post('/auth/register', profile), this.http.post('/auth/register', profile),
'LOGIN_REGISTER', 'LOGIN_REGISTER',

View File

@ -5,12 +5,14 @@ import { Link } from 'react-router-dom'
import { Input } from '../components/Input' import { Input } from '../components/Input'
import { Redirect } from '../components/Redirect' import { Redirect } from '../components/Redirect'
import { Button } from '../components' import { Button } from '../components'
import { Captcha } from '@rondo.dev/react-captcha'
export interface RegisterFormProps { export interface RegisterFormProps {
baseUrl: string
error?: string error?: string
onSubmit: () => void onSubmit: () => void
onChange: (name: string, value: string) => void onChange: (name: string, value: string) => void
data: NewUser data: NewUser & { captcha: string }
user?: UserProfile user?: UserProfile
redirectTo: string redirectTo: string
} }
@ -74,6 +76,19 @@ export class RegisterForm extends React.PureComponent<RegisterFormProps> {
value={this.props.data.lastName} value={this.props.data.lastName}
placeholder='Last name' placeholder='Last name'
/> />
<Captcha
imageUrl={this.props.baseUrl + '/api/auth/captcha.svg'}
audioUrl={this.props.baseUrl + '/api/auth/captcha.wav'}
/>
<Input
Icon={undefined}
label='Captcha'
name='captcha'
type='text'
onChange={this.props.onChange}
value={this.props.data.captcha}
placeholder='Captcha'
/>
<div className='text-center'> <div className='text-center'>
<Button name='submit' type='submit'> <Button name='submit' type='submit'>
Register Register

View File

@ -20,7 +20,7 @@ describe('configureRegister', () => {
select: state => state.Login, select: state => state.Login,
}) })
.withComponent( .withComponent(
select => configureRegister(select, loginActions), select => configureRegister(select, loginActions, '/app'),
) )
.withJSX((Component, props) => .withJSX((Component, props) =>
<MemoryRouter><Component {...props} /></MemoryRouter>, <MemoryRouter><Component {...props} /></MemoryRouter>,
@ -41,6 +41,7 @@ describe('configureRegister', () => {
password: 'pass', password: 'pass',
firstName: '', firstName: '',
lastName: '', lastName: '',
captcha: '',
} }
const onSuccess = jest.fn() const onSuccess = jest.fn()
let node: Element let node: Element

View File

@ -6,16 +6,18 @@ import { LoginState } from './LoginReducer'
import { RegisterForm } from './RegisterForm' import { RegisterForm } from './RegisterForm'
import { withForm } from './withForm' import { withForm } from './withForm'
const defaultCredentials: NewUser = { const defaultCredentials: NewUser & { captcha: string }= {
username: '', username: '',
password: '', password: '',
firstName: '', firstName: '',
lastName: '', lastName: '',
captcha: '',
} }
export function configureRegister<State>( export function configureRegister<State>(
getLocalState: SelectState<State, LoginState>, getLocalState: SelectState<State, LoginState>,
loginActions: LoginActions, loginActions: LoginActions,
baseUrl: string,
) { ) {
return pack( return pack(
getLocalState, getLocalState,
@ -25,6 +27,7 @@ export function configureRegister<State>(
redirectTo: state.redirectTo, redirectTo: state.redirectTo,
}), }),
dispatch => ({ dispatch => ({
baseUrl,
onSubmit: bindActionCreators(loginActions.register, dispatch), onSubmit: bindActionCreators(loginActions.register, dispatch),
clearOnSuccess: true, clearOnSuccess: true,
}), }),

View File

@ -10,7 +10,7 @@ export interface APIDef {
} }
'/auth/register': { '/auth/register': {
'post': { 'post': {
body: NewUser body: NewUser & { captcha: string }
response: UserProfile response: UserProfile
} }
} }

View File

@ -13,9 +13,6 @@ export interface CaptchaState {
} }
export class Captcha extends React.PureComponent<CaptchaProps, CaptchaState> { export class Captcha extends React.PureComponent<CaptchaProps, CaptchaState> {
static defaultProps = {
name: 'captcha',
}
state: CaptchaState = { state: CaptchaState = {
type: 'image', type: 'image',
attempt: 1, attempt: 1,
@ -46,7 +43,7 @@ export class Captcha extends React.PureComponent<CaptchaProps, CaptchaState> {
</a> </a>
{this.props.audioUrl && ( {this.props.audioUrl && (
<a className='action-audio' onClick={this.changeToAudio}> <a className='action-audio' onClick={this.changeToAudio}>
Click here for image version Click here for audio version
</a> </a>
)} )}
</> </>
@ -58,7 +55,7 @@ export class Captcha extends React.PureComponent<CaptchaProps, CaptchaState> {
Refresh Refresh
</a> </a>
<a className='action-image' onClick={this.changeToImage}> <a className='action-image' onClick={this.changeToImage}>
Click here for audio version Click here for image version
</a> </a>
</> </>
)} )}

View File

@ -2,8 +2,10 @@
import { createStore, SelectState, WaitMiddleware } from '@rondo.dev/redux' import { createStore, SelectState, WaitMiddleware } from '@rondo.dev/redux'
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import T from 'react-dom/test-utils'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import { Action, AnyAction, combineReducers, Reducer, ReducersMapObject } from 'redux' import { Action, AnyAction, combineReducers, Reducer, ReducersMapObject } from 'redux'
import { ThemeProvider, DefaultTheme } from 'styled-components'
interface RenderParams<State, LocalState> { interface RenderParams<State, LocalState> {
reducers: ReducersMapObject<State, any> reducers: ReducersMapObject<State, any>
@ -18,15 +20,24 @@ export class TestContainer extends React.Component<{}> {
} }
export class TestUtils { export class TestUtils {
static defaultTheme?: DefaultTheme
/** /**
* Create a redux store * Create a redux store
*/ */
readonly createStore = createStore readonly createStore = createStore
readonly Utils = T
render(jsx: JSX.Element) { render(jsx: JSX.Element) {
const $div = document.createElement('div') const $div = document.createElement('div')
const component = ReactDOM.render( const component = ReactDOM.render(
<TestContainer>{jsx}</TestContainer>, $div) as unknown as TestContainer <TestContainer>
<ThemeProvider theme={TestUtils.defaultTheme}>
{jsx}
</ThemeProvider>
</TestContainer>,
$div,
) as unknown as TestContainer
const node = component.ref.current!.children[0] const node = component.ref.current!.children[0]
return { return {
component, component,

View File

@ -13,4 +13,5 @@ module.exports = {
'jsx', 'jsx',
], ],
setupFiles: ['<rootDir>/jest.setup.js'], setupFiles: ['<rootDir>/jest.setup.js'],
setupFilesAfterEnv: ['<rootDir>/src/jest.env.ts'],
} }