diff --git a/src/components/CreateWallet.test.tsx b/src/components/CreateWallet.test.tsx
index 6330deea..49c0eafe 100644
--- a/src/components/CreateWallet.test.tsx
+++ b/src/components/CreateWallet.test.tsx
@@ -19,7 +19,8 @@ jest.mock('../libs/JmWalletApi', () => ({
const NOOP = () => {}
describe('', () => {
- const testWalletName = 'wallet'
+ const testWalletName = 'test_wallet21'
+ const invalidTestWalletName = 'invalid_wallet_name!'
const testWalletPassword = 'correct horse battery staple'
const setup = ({
@@ -56,12 +57,14 @@ describe('', () => {
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()
@@ -69,11 +72,32 @@ describe('', () => {
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(() => {
@@ -82,12 +106,10 @@ describe('', () => {
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()
})
@@ -114,8 +136,7 @@ describe('', () => {
})
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/))
})
diff --git a/src/components/WalletCreationForm.tsx b/src/components/WalletCreationForm.tsx
index baf34414..db7ba6e5 100644
--- a/src/components/WalletCreationForm.tsx
+++ b/src/components/WalletCreationForm.tsx
@@ -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 {
@@ -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
@@ -38,7 +41,7 @@ const WalletCreationForm = ({
const validate = useCallback(
(values: CreateWalletFormValues) => {
const errors = {} as FormikErrors
- if (!values.walletName) {
+ if (!values.walletName || !validateWalletName(values.walletName)) {
errors.walletName = t('create_wallet.feedback_invalid_wallet_name')
}
if (!values.password) {
@@ -75,6 +78,7 @@ const WalletCreationForm = ({
isValid={touched.walletName && !errors.walletName}
isInvalid={touched.walletName && !!errors.walletName}
className={styles.input}
+ maxLength={MAX_WALLET_NAME_LENGTH}
/>
{t('create_wallet.feedback_valid')}
{errors.walletName}
diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json
index 4f50da28..f75448b5 100644
--- a/src/i18n/locales/en/translation.json
+++ b/src/i18n/locales/en/translation.json
@@ -125,7 +125,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.",
diff --git a/src/utils.ts b/src/utils.ts
index 9c68b985..30590cb8 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -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(' ')