Skip to content

Commit

Permalink
chore: validate wallet name (#684)
Browse files Browse the repository at this point in the history
  • Loading branch information
theborakompanioni authored Nov 2, 2023
1 parent b6a568e commit 7375116
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 13 deletions.
39 changes: 30 additions & 9 deletions src/components/CreateWallet.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ jest.mock('../libs/JmWalletApi', () => ({
const NOOP = () => {}

describe('<CreateWallet />', () => {
const testWalletName = 'wallet'
const testWalletName = 'test_wallet21'
const invalidTestWalletName = 'invalid_wallet_name!'
const testWalletPassword = 'correct horse battery staple'

const setup = ({
Expand Down Expand Up @@ -56,24 +57,47 @@ describe('<CreateWallet />', () => {
it('should show validation messages to user if form is invalid', async () => {
act(() => setup({}))

expect(await screen.queryByText('create_wallet.feedback_invalid_wallet_name')).not.toBeInTheDocument()
expect(await screen.queryByText('create_wallet.feedback_invalid_password')).not.toBeInTheDocument()
expect(await screen.queryByText('create_wallet.feedback_invalid_password_confirm')).not.toBeInTheDocument()
expect(await screen.findByText('create_wallet.button_create')).toBeVisible()

act(() => {
// click on the "create" button without filling the form
const createWalletButton = screen.getByText('create_wallet.button_create')
user.click(createWalletButton)
user.click(screen.getByText('create_wallet.button_create'))
})

expect(await screen.findByText('create_wallet.feedback_invalid_wallet_name')).toBeVisible()
expect(await screen.findByText('create_wallet.feedback_invalid_password')).toBeVisible()
expect(await screen.findByText('create_wallet.feedback_invalid_password_confirm')).toBeVisible()
})

it('should not submit form if wallet name contains invalid characters', async () => {
act(() => setup({}))

expect(await screen.queryByText('create_wallet.feedback_invalid_wallet_name')).not.toBeInTheDocument()
expect(await screen.queryByText('create_wallet.feedback_invalid_password_confirm')).not.toBeInTheDocument()

act(() => {
user.type(screen.getByPlaceholderText('create_wallet.placeholder_wallet_name'), invalidTestWalletName)
user.type(screen.getByPlaceholderText('create_wallet.placeholder_password'), testWalletPassword)
user.type(screen.getByPlaceholderText('create_wallet.placeholder_password_confirm'), testWalletPassword)
})

act(() => user.click(screen.getByText('create_wallet.button_create')))

expect(await screen.findByText('create_wallet.button_create')).toBeVisible()
expect(await screen.findByText('create_wallet.feedback_invalid_wallet_name')).toBeVisible()
expect(await screen.queryByText('create_wallet.feedback_invalid_password_confirm')).not.toBeInTheDocument()
})

it('should not submit form if passwords do not match', async () => {
act(() => setup({}))

expect(await screen.findByPlaceholderText('create_wallet.placeholder_password')).toBeVisible()
expect(await screen.findByPlaceholderText('create_wallet.placeholder_password_confirm')).toBeVisible()
expect(await screen.queryByText('create_wallet.feedback_invalid_wallet_name')).not.toBeInTheDocument()
expect(await screen.queryByText('create_wallet.feedback_invalid_password_confirm')).not.toBeInTheDocument()
expect(await screen.findByText('create_wallet.button_create')).toBeVisible()

act(() => {
Expand All @@ -82,12 +106,10 @@ describe('<CreateWallet />', () => {
user.type(screen.getByPlaceholderText('create_wallet.placeholder_password_confirm'), 'a_mismatching_input')
})

act(() => {
const createWalletButton = screen.getByText('create_wallet.button_create')
user.click(createWalletButton)
})
act(() => user.click(screen.getByText('create_wallet.button_create')))

expect(await screen.findByText('create_wallet.button_create')).toBeVisible()
expect(await screen.queryByText('create_wallet.feedback_invalid_wallet_name')).not.toBeInTheDocument()
expect(await screen.findByText('create_wallet.feedback_invalid_password_confirm')).toBeVisible()
})

Expand All @@ -114,8 +136,7 @@ describe('<CreateWallet />', () => {
})

await act(async () => {
const createWalletButton = screen.getByText('create_wallet.button_create')
user.click(createWalletButton)
user.click(screen.getByText('create_wallet.button_create'))

await waitFor(() => screen.findByText(/create_wallet.button_creating/))
})
Expand Down
8 changes: 6 additions & 2 deletions src/components/WalletCreationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as rb from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { Formik, FormikErrors } from 'formik'
import Sprite from './Sprite'
import { sanitizeWalletName } from '../utils'
import { JM_WALLET_FILE_EXTENSION, sanitizeWalletName } from '../utils'
import styles from './WalletCreationForm.module.css'

export interface CreateWalletFormValues {
Expand All @@ -20,6 +20,9 @@ const initialCreateWalletFormValues: CreateWalletFormValues = {

export type WalletNameAndPassword = { name: string; password: string }

const MAX_WALLET_NAME_LENGTH = 240 - JM_WALLET_FILE_EXTENSION.length
const validateWalletName = (input: string) => input.length <= MAX_WALLET_NAME_LENGTH && /^[\w-]+$/.test(input)

interface WalletCreationFormProps {
initialValues?: CreateWalletFormValues
submitButtonText: (isSubmitting: boolean) => React.ReactNode | string
Expand All @@ -38,7 +41,7 @@ const WalletCreationForm = ({
const validate = useCallback(
(values: CreateWalletFormValues) => {
const errors = {} as FormikErrors<CreateWalletFormValues>
if (!values.walletName) {
if (!values.walletName || !validateWalletName(values.walletName)) {
errors.walletName = t('create_wallet.feedback_invalid_wallet_name')
}
if (!values.password) {
Expand Down Expand Up @@ -75,6 +78,7 @@ const WalletCreationForm = ({
isValid={touched.walletName && !errors.walletName}
isInvalid={touched.walletName && !!errors.walletName}
className={styles.input}
maxLength={MAX_WALLET_NAME_LENGTH}
/>
<rb.Form.Control.Feedback>{t('create_wallet.feedback_valid')}</rb.Form.Control.Feedback>
<rb.Form.Control.Feedback type="invalid">{errors.walletName}</rb.Form.Control.Feedback>
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
"feedback_valid": "Looks good!",
"label_wallet_name": "Wallet name",
"placeholder_wallet_name": "Your Wallet...",
"feedback_invalid_wallet_name": "Please set a wallet name.",
"feedback_invalid_wallet_name": "Please choose a valid wallet name: Use only letters, numbers, underscores or hyphens.",
"label_password": "Password to unlock the wallet",
"placeholder_password": "Choose a secure password...",
"feedback_invalid_password": "Please set a password.",
Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const SATS_FORMATTER = new Intl.NumberFormat('en-US', {
export const BTC: Unit = 'BTC'
export const SATS: Unit = 'sats'

const JM_WALLET_FILE_EXTENSION = '.jmdat'
export const JM_WALLET_FILE_EXTENSION = '.jmdat'

export const DUMMY_MNEMONIC_PHRASE: MnemonicPhrase =
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'.split(' ')
Expand Down

0 comments on commit 7375116

Please sign in to comment.