add captcha component

This commit is contained in:
Pig Fang 2020-03-26 17:54:01 +08:00
parent 375fcb38b4
commit 1b8c335cac
6 changed files with 192 additions and 0 deletions

View File

@ -31,6 +31,7 @@
"react-dom": "^16.12.0",
"react-draggable": "^4.2.0",
"react-hot-loader": "^4.12.18",
"reaptcha": "^1.7.2",
"rxjs": "^6.5.3",
"skinview-utils": "^0.2.1",
"skinview3d": "^1.2.1",

View File

@ -0,0 +1,3 @@
.captcha {
cursor: pointer;
}

View File

@ -0,0 +1,102 @@
import React from 'react'
import Reaptcha from 'reaptcha'
import { emit, on } from '@/scripts/event'
import { t } from '@/scripts/i18n'
import styles from './Captcha.module.scss'
const eventId = Symbol()
type State = {
value: string
time: number
sitekey: string
invisible: boolean
}
class Captcha extends React.Component<{}, State> {
state: State
ref: React.MutableRefObject<Reaptcha | null>
constructor(props: {}) {
super(props)
this.state = {
value: '',
time: Date.now(),
sitekey: blessing.extra.recaptcha,
invisible: blessing.extra.invisible,
}
this.ref = React.createRef()
}
execute = async () => {
const recaptcha = this.ref.current
if (recaptcha && this.state.invisible) {
return new Promise<string>(resolve => {
const off = on(eventId, (value: string) => {
resolve(value)
off()
})
recaptcha.execute()
})
}
return this.state.value
}
reset = () => {
const recaptcha = this.ref.current
if (recaptcha) {
recaptcha.reset()
} else {
this.setState({ time: Date.now() })
}
}
handleValueChange = (event: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ value: event.target.value })
}
handleVerify = (value: string) => {
emit(eventId, value)
this.setState({ value })
}
handleRefresh = () => {
this.setState({ time: Date.now() })
}
render() {
return this.state.sitekey ? (
<div className="mb-2">
<Reaptcha
ref={this.ref}
sitekey={this.state.sitekey}
size={this.state.invisible ? 'invisible' : 'normal'}
onVerify={this.handleVerify}
/>
</div>
) : (
<div className="d-flex">
<div className="form-group mb-3 mr-2">
<input
type="text"
className="form-control"
placeholder={t('auth.captcha')}
required
value={this.state.value}
onChange={this.handleValueChange}
/>
</div>
<img
src={`${blessing.base_url}/auth/captcha?v=${this.state.time}`}
alt={t('auth.captcha')}
className={styles.captcha}
height={34}
title={t('auth.change-captcha')}
onClick={this.handleRefresh}
/>
</div>
)
}
}
export default Captcha

View File

@ -0,0 +1,16 @@
import React from 'react'
import type { ReaptchaProps } from 'reaptcha'
class Reaptcha extends React.Component<ReaptchaProps, {}> {
execute() {
this.props.onVerify('token')
}
reset() {}
render() {
return <></>
}
}
export default Reaptcha

View File

@ -0,0 +1,65 @@
import React from 'react'
import { render, fireEvent } from '@testing-library/react'
import Reaptcha from 'reaptcha'
import { t } from '@/scripts/i18n'
import Captcha from '@/components/Captcha'
describe('picture captcha', () => {
it('retrieve value', async () => {
const ref = React.createRef<Captcha>()
const { getByPlaceholderText } = render(<Captcha ref={ref} />)
fireEvent.input(getByPlaceholderText(t('auth.captcha')), {
target: { value: 'abc' },
})
expect(await ref.current?.execute()).toBe('abc')
})
it('refresh on click', async () => {
const spy = jest.spyOn(Date, 'now')
const ref = React.createRef<Captcha>()
const { getByAltText } = render(<Captcha ref={ref} />)
fireEvent.click(getByAltText(t('auth.captcha')))
expect(spy).toBeCalled()
})
it('refresh programatically', async () => {
const spy = jest.spyOn(Date, 'now')
const ref = React.createRef<Captcha>()
render(<Captcha ref={ref} />)
ref.current?.reset()
expect(spy).toBeCalled()
})
})
describe('recaptcha', () => {
beforeEach(() => {
window.blessing.extra = { recaptcha: 'sitekey', invisible: false }
})
it('retrieve value', async () => {
window.blessing.extra.invisible = true
const spy = jest.spyOn(Reaptcha.prototype, 'execute')
const ref = React.createRef<Captcha>()
render(<Captcha ref={ref} />)
const value = await ref.current?.execute()
expect(spy).toBeCalled()
expect(value).toBe('token')
})
it('refresh programatically', async () => {
const spy = jest.spyOn(Reaptcha.prototype, 'reset')
const ref = React.createRef<Captcha>()
render(<Captcha ref={ref} />)
ref.current?.reset()
expect(spy).toBeCalled()
})
})

View File

@ -8571,6 +8571,11 @@ realpath-native@^1.1.0:
dependencies:
util.promisify "^1.0.0"
reaptcha@^1.7.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/reaptcha/-/reaptcha-1.7.2.tgz#d829f54270c241f46501e92a5a7badeb1fcf372d"
integrity sha512-/RXiPeMd+fPUGByv+kAaQlCXCsSflZ9bKX5Fcwv9IYGS1oyT2nntL/8zn9IaiUFHL66T1jBtOABcb92g2+3w8w==
redent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"