add captcha component
This commit is contained in:
parent
375fcb38b4
commit
1b8c335cac
@ -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",
|
||||
|
3
resources/assets/src/components/Captcha.module.scss
Normal file
3
resources/assets/src/components/Captcha.module.scss
Normal file
@ -0,0 +1,3 @@
|
||||
.captcha {
|
||||
cursor: pointer;
|
||||
}
|
102
resources/assets/src/components/Captcha.tsx
Normal file
102
resources/assets/src/components/Captcha.tsx
Normal 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
|
16
resources/assets/tests/__mocks__/reaptcha.tsx
Normal file
16
resources/assets/tests/__mocks__/reaptcha.tsx
Normal 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
|
65
resources/assets/tests/components/Captcha.test.tsx
Normal file
65
resources/assets/tests/components/Captcha.test.tsx
Normal 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()
|
||||
})
|
||||
})
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user