From b8ececc491615b5c2186f7d5455fcaa35ef019dc Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Tue, 18 Jan 2022 15:45:22 +0100 Subject: [PATCH] feat: add validators utilities to utils package --- .../settings/backends/useBackendsForm.ts | 2 +- .../wallet/sign-verify/useSignVerifyForm.ts | 4 +- .../suite/src/reducers/suite/resizeReducer.ts | 12 +- .../suite/src/reducers/suite/suiteReducer.ts | 5 +- .../utils/suite/__tests__/validators.test.ts | 130 ------------------ packages/suite/src/utils/suite/validators.ts | 44 ------ packages/suite/src/utils/wallet/ethUtils.ts | 4 +- .../utils/src/getNumberFromPixelString.ts | 2 + packages/utils/src/getNumberFromPxString.ts | 1 - packages/utils/src/hasUppercaseLetter.ts | 3 + packages/utils/src/index.ts | 5 +- packages/utils/src/isAscii.ts | 4 + packages/utils/src/isUrl.ts | 4 + .../tests/getNumberFromPixelString.test.ts | 6 + .../utils/tests/getNumberFromPxString.test.ts | 6 - .../utils/tests/hasUppercaseLetter.test.ts | 14 ++ packages/utils/tests/isAscii.test.ts | 18 +++ 17 files changed, 69 insertions(+), 195 deletions(-) delete mode 100644 packages/suite/src/utils/suite/__tests__/validators.test.ts delete mode 100644 packages/suite/src/utils/suite/validators.ts create mode 100644 packages/utils/src/getNumberFromPixelString.ts delete mode 100644 packages/utils/src/getNumberFromPxString.ts create mode 100644 packages/utils/src/hasUppercaseLetter.ts create mode 100644 packages/utils/src/isAscii.ts create mode 100644 packages/utils/src/isUrl.ts create mode 100644 packages/utils/tests/getNumberFromPixelString.test.ts delete mode 100644 packages/utils/tests/getNumberFromPxString.test.ts create mode 100644 packages/utils/tests/hasUppercaseLetter.test.ts create mode 100644 packages/utils/tests/isAscii.test.ts diff --git a/packages/suite/src/hooks/settings/backends/useBackendsForm.ts b/packages/suite/src/hooks/settings/backends/useBackendsForm.ts index 93db612d9ff3..a7475f38a832 100644 --- a/packages/suite/src/hooks/settings/backends/useBackendsForm.ts +++ b/packages/suite/src/hooks/settings/backends/useBackendsForm.ts @@ -1,7 +1,7 @@ import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { useActions, useSelector, useTranslation } from '@suite-hooks'; -import { isUrl } from '@suite-utils/validators'; +import { isUrl } from '@trezor/utils'; import { setBackend as setBackendAction } from '@settings-actions/walletSettingsActions'; import type { Network } from '@wallet-types'; import type { BackendType } from '@wallet-reducers/settingsReducer'; diff --git a/packages/suite/src/hooks/wallet/sign-verify/useSignVerifyForm.ts b/packages/suite/src/hooks/wallet/sign-verify/useSignVerifyForm.ts index 4f5eb085975e..a5ef0f83d585 100644 --- a/packages/suite/src/hooks/wallet/sign-verify/useSignVerifyForm.ts +++ b/packages/suite/src/hooks/wallet/sign-verify/useSignVerifyForm.ts @@ -3,7 +3,7 @@ import { useForm, useController } from 'react-hook-form'; import { useTranslation, useSelector, useActions } from '@suite-hooks'; import * as protocolActions from '@suite-actions/protocolActions'; import { isHex } from '@wallet-utils/ethUtils'; -import { isASCII } from '@suite-utils/validators'; +import { isAscii } from '@trezor/utils'; import { isAddressValid } from '@wallet-utils/validation'; import type { Account } from '@wallet-types'; import type { AoppState } from '@suite-reducers/protocolReducer'; @@ -114,7 +114,7 @@ export const useSignVerifyForm = (page: 'sign' | 'verify', account?: Account) => ? translationString('DATA_NOT_VALID_HEX') : undefined, ascii: (message: string) => - !formValues.hex && !isASCII(message) + !formValues.hex && !isAscii(message) ? translationString('TR_ASCII_ONLY') : undefined, }, diff --git a/packages/suite/src/reducers/suite/resizeReducer.ts b/packages/suite/src/reducers/suite/resizeReducer.ts index 1d00dbc76868..606481b8f016 100644 --- a/packages/suite/src/reducers/suite/resizeReducer.ts +++ b/packages/suite/src/reducers/suite/resizeReducer.ts @@ -1,15 +1,15 @@ import produce from 'immer'; import * as variables from '@trezor/components/lib/config/variables'; // can't import from index cause it would import all UI components import { RESIZE } from '@suite-actions/constants'; -import { getNumberFromPxString } from '@trezor/utils'; +import { getNumberFromPixelString } from '@trezor/utils'; import { Action } from '@suite-types'; const sizes = { - UNAVAILABLE: getNumberFromPxString(variables.SCREEN_SIZE.UNAVAILABLE), - SMALL: getNumberFromPxString(variables.SCREEN_SIZE.SM), - MEDIUM: getNumberFromPxString(variables.SCREEN_SIZE.MD), - LARGE: getNumberFromPxString(variables.SCREEN_SIZE.LG), - XLARGE: getNumberFromPxString(variables.SCREEN_SIZE.XL), + UNAVAILABLE: getNumberFromPixelString(variables.SCREEN_SIZE.UNAVAILABLE), + SMALL: getNumberFromPixelString(variables.SCREEN_SIZE.SM), + MEDIUM: getNumberFromPixelString(variables.SCREEN_SIZE.MD), + LARGE: getNumberFromPixelString(variables.SCREEN_SIZE.LG), + XLARGE: getNumberFromPixelString(variables.SCREEN_SIZE.XL), }; const getSize = (screenWidth: number | null): State['size'] => { diff --git a/packages/suite/src/reducers/suite/suiteReducer.ts b/packages/suite/src/reducers/suite/suiteReducer.ts index e8320fb62b77..a73ad42eee82 100644 --- a/packages/suite/src/reducers/suite/suiteReducer.ts +++ b/packages/suite/src/reducers/suite/suiteReducer.ts @@ -7,7 +7,7 @@ import { Action, TrezorDevice, Lock, SuiteThemeColors } from '@suite-types'; import type { Locale } from '@suite-config/languages'; import { isWeb, getWindowWidth } from '@suite-utils/env'; import { ensureLocale } from '@suite-utils/l10n'; -import { getNumberFromPxString } from '@trezor/utils'; +import { getNumberFromPixelString } from '@trezor/utils'; export interface DebugModeOptions { invityAPIUrl?: string; @@ -79,7 +79,8 @@ const initialState: SuiteState = { taprootBannerClosed: false, securityStepsHidden: false, dashboardGraphHidden: false, - dashboardAssetsGridMode: getWindowWidth() < getNumberFromPxString(variables.SCREEN_SIZE.SM), + dashboardAssetsGridMode: + getWindowWidth() < getNumberFromPixelString(variables.SCREEN_SIZE.SM), }, settings: { theme: { diff --git a/packages/suite/src/utils/suite/__tests__/validators.test.ts b/packages/suite/src/utils/suite/__tests__/validators.test.ts deleted file mode 100644 index 31a50979e940..000000000000 --- a/packages/suite/src/utils/suite/__tests__/validators.test.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { isEmail, isASCII, hasDecimals, hasUppercase, isNumber } from '../validators'; - -describe('utils/suite/validators', () => { - describe('isEmail', () => { - it('should return true for valid email', () => { - expect(isEmail('satoshi@nakamoto.io')).toEqual(true); - }); - - it('should return false for invalid email', () => { - const fooMails = ['', 'satoshi', 'satoshi@seznam', 'satoshi @foo.io']; - fooMails.forEach(mail => { - expect(isEmail(mail)).toEqual(false); - }); - }); - }); - - describe('isASCII', () => { - it('should return true for ASCII only string', () => { - expect(isASCII('this is only ascii')).toEqual(true); - }); - - it('should return true when called without parameter', () => { - expect(isASCII()).toEqual(true); - }); - - it('should return false strings with non ASCII chars', () => { - const fooStrings = ['¥', 'železniční přejezd']; - fooStrings.forEach(str => { - expect(isASCII(str)).toEqual(false); - }); - }); - }); - - it('hasDecimals', () => { - expect(hasDecimals('0', 18)).toBe(true); - expect(hasDecimals('0.0', 18)).toBe(true); - expect(hasDecimals('0.00000000', 18)).toBe(true); - expect(hasDecimals('0.00000001', 18)).toBe(true); - expect(hasDecimals('+0.0', 18)).toBe(false); - expect(hasDecimals('-0.0', 18)).toBe(false); - expect(hasDecimals('1', 18)).toBe(true); - expect(hasDecimals('+1', 18)).toBe(false); - expect(hasDecimals('+100000', 18)).toBe(false); - expect(hasDecimals('.', 18)).toBe(false); - expect(hasDecimals('-.1', 18)).toBe(false); - expect(hasDecimals('0.1', 18)).toBe(true); - expect(hasDecimals('0.12314841', 18)).toBe(true); - expect(hasDecimals('0.1381841848184814818391931933', 18)).toBe(false); // 28 decimals - expect(hasDecimals('0.100000000000000000', 18)).toBe(true); // 18s decimals - - expect(hasDecimals('100.', 18)).toBe(true); - expect(hasDecimals('.1', 18)).toBe(false); - expect(hasDecimals('.000000001', 18)).toBe(false); - expect(hasDecimals('.13134818481481841', 18)).toBe(false); - - expect(hasDecimals('001.12314841', 18)).toBe(false); - expect(hasDecimals('83819319391491949941', 18)).toBe(true); - expect(hasDecimals('-83819319391491949941', 18)).toBe(false); - expect(hasDecimals('+0.131831848184', 18)).toBe(false); - expect(hasDecimals('0.127373193981774718318371831731761626162613', 18)).toBe(false); - - expect(hasDecimals('0.131831848184a', 18)).toBe(false); - expect(hasDecimals('100a', 18)).toBe(false); - expect(hasDecimals('.100a', 18)).toBe(false); - expect(hasDecimals('a.100', 18)).toBe(false); - expect(hasDecimals('abc', 18)).toBe(false); - expect(hasDecimals('1abc0', 18)).toBe(false); - }); - - it('hasDecimals decimals=0', () => { - expect(hasDecimals('0', 0)).toBe(true); - expect(hasDecimals('0.1', 0)).toBe(false); - expect(hasDecimals('0.12345', 0)).toBe(false); - expect(hasDecimals('1', 0)).toBe(true); - expect(hasDecimals('1.1', 0)).toBe(false); - expect(hasDecimals('1000000', 0)).toBe(true); - expect(hasDecimals('-1000000', 0)).toBe(false); - expect(hasDecimals('.0', 0)).toBe(false); - expect(hasDecimals('0.', 0)).toBe(false); - expect(hasDecimals('.', 0)).toBe(false); - }); - - it('hasUppercase', () => { - expect(hasUppercase('0')).toBe(false); - expect(hasUppercase('abc')).toBe(false); - expect(hasUppercase('abcD')).toBe(true); - expect(hasUppercase('Abcd')).toBe(true); - expect(hasUppercase('aBcd')).toBe(true); - expect(hasUppercase('123abc123')).toBe(false); - expect(hasUppercase('0x123abc456')).toBe(false); - expect(hasUppercase('0x123aBc456')).toBe(true); - }); - - it('isNumber', () => { - expect(isNumber('0')).toBe(true); - expect(isNumber('0.0')).toBe(true); - expect(isNumber('0.00000000')).toBe(true); - expect(isNumber('0.00000001')).toBe(true); - expect(isNumber('+0.0')).toBe(false); - expect(isNumber('-0.0')).toBe(false); - expect(isNumber('1')).toBe(true); - expect(isNumber('+1')).toBe(false); - expect(isNumber('+100000')).toBe(false); - expect(isNumber('.')).toBe(false); - expect(isNumber('')).toBe(false); - expect(isNumber(' ')).toBe(false); - expect(isNumber('-.1')).toBe(false); - expect(isNumber('0.1')).toBe(true); - expect(isNumber('0.12314841')).toBe(true); - expect(isNumber('0.1381841848184814818391931933')).toBe(true); // 28 decimals - expect(isNumber('0.100000000000000000')).toBe(true); // 18s decimals - - expect(isNumber('100.')).toBe(true); - expect(isNumber('.1')).toBe(false); - expect(isNumber('.000000001')).toBe(false); - expect(isNumber('.13134818481481841')).toBe(false); - - expect(isNumber('001.12314841')).toBe(false); - expect(isNumber('83819319391491949941')).toBe(true); - expect(isNumber('-83819319391491949941')).toBe(false); - expect(isNumber('+0.131831848184')).toBe(false); - - expect(isNumber('0.131831848184a')).toBe(false); - expect(isNumber('100a')).toBe(false); - expect(isNumber('.100a')).toBe(false); - expect(isNumber('a.100')).toBe(false); - expect(isNumber('abc')).toBe(false); - expect(isNumber('1abc0')).toBe(false); - }); -}); diff --git a/packages/suite/src/utils/suite/validators.ts b/packages/suite/src/utils/suite/validators.ts deleted file mode 100644 index 0e4db4013a74..000000000000 --- a/packages/suite/src/utils/suite/validators.ts +++ /dev/null @@ -1,44 +0,0 @@ -// RFC 5322 - http://emailregex.com/ -const EMAIL_REGEX = - /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; -// Simple URL regex -const URL_REGEX = - /^(http|ws)s?:\/\/[a-z0-9]([a-z0-9.-]+)?(:[0-9]{1,5})?((\/)?(([a-z0-9-_])+(\/)?)+)$/i; - -export function isEmail(value?: string): boolean { - if (!value) return false; - return EMAIL_REGEX.test(value); -} - -export const isUrl = (value: string): boolean => URL_REGEX.test(value); - -export function isASCII(value?: string): boolean { - if (!value) return true; - return /^[\x00-\x7F]*$/.test(value); // eslint-disable-line -} - -export const hasUppercase = (value: string) => { - const UPPERCASE_RE = new RegExp('^(.*[A-Z].*)$'); - return UPPERCASE_RE.test(value); -}; - -export const isAbs = (value: string) => { - const ABS_RE = new RegExp('^[0-9]+$'); - return ABS_RE.test(value); -}; - -export const hasDecimals = (value: string, decimals: number) => { - if (decimals === 0) { - return isAbs(value); - } - - const ETH_DECIMALS_RE = new RegExp( - `^(0|0\\.([0-9]{0,${decimals}})?|[1-9][0-9]*\\.?([0-9]{0,${decimals}})?)$`, - ); - return ETH_DECIMALS_RE.test(value); -}; - -export const isNumber = (value: string) => { - const NUMBER_RE = new RegExp(`^(0|0\\.([0-9]+)?|[1-9][0-9]*\\.?([0-9]+)?)$`); - return NUMBER_RE.test(value); -}; diff --git a/packages/suite/src/utils/wallet/ethUtils.ts b/packages/suite/src/utils/wallet/ethUtils.ts index d1f200148c5e..eedac91b4119 100644 --- a/packages/suite/src/utils/wallet/ethUtils.ts +++ b/packages/suite/src/utils/wallet/ethUtils.ts @@ -1,4 +1,5 @@ import * as EthereumjsUtil from 'ethereumjs-util'; +import { hasUppercaseLetter } from '@trezor/utils'; import BigNumber from 'bignumber.js'; export const decimalToHex = (dec: number): string => new BigNumber(dec).toString(16); @@ -24,14 +25,13 @@ export const strip = (str: string): string => { }; export const validateAddress = (address: string): string | null => { - const hasUpperCase = new RegExp('^(.*[A-Z].*)$'); if (address.length < 1) { return 'Address is not set'; } if (!EthereumjsUtil.isValidAddress(address)) { return 'Address is not valid'; } - if (address.match(hasUpperCase) && !EthereumjsUtil.isValidChecksumAddress(address)) { + if (hasUppercaseLetter(address) && !EthereumjsUtil.isValidChecksumAddress(address)) { return 'Address is not a valid checksum'; } return null; diff --git a/packages/utils/src/getNumberFromPixelString.ts b/packages/utils/src/getNumberFromPixelString.ts new file mode 100644 index 000000000000..cf11f31566f8 --- /dev/null +++ b/packages/utils/src/getNumberFromPixelString.ts @@ -0,0 +1,2 @@ +export const getNumberFromPixelString = (size: string): number => + parseInt(size.replace('px', ''), 10); diff --git a/packages/utils/src/getNumberFromPxString.ts b/packages/utils/src/getNumberFromPxString.ts deleted file mode 100644 index 4b7d82c03b4b..000000000000 --- a/packages/utils/src/getNumberFromPxString.ts +++ /dev/null @@ -1 +0,0 @@ -export const getNumberFromPxString = (size: string): number => parseInt(size.replace('px', ''), 10); diff --git a/packages/utils/src/hasUppercaseLetter.ts b/packages/utils/src/hasUppercaseLetter.ts new file mode 100644 index 000000000000..c743ad8fe2e2 --- /dev/null +++ b/packages/utils/src/hasUppercaseLetter.ts @@ -0,0 +1,3 @@ +const HAS_UPPERCASE_LATER_REGEXP = new RegExp('^(.*[A-Z].*)$'); + +export const hasUppercaseLetter = (value: string) => HAS_UPPERCASE_LATER_REGEXP.test(value); diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 2d3b4959fb5e..8ccb6f3e9fbb 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -7,4 +7,7 @@ export * from './getRandomNumberInRange'; export * from './capitalizeFirstLetter'; export * from './countBytesInString'; export * from './truncateMiddle'; -export * from './getNumberFromPxString'; +export * from './getNumberFromPixelString'; +export * from './isUrl'; +export * from './hasUppercaseLetter'; +export * from './isAscii'; diff --git a/packages/utils/src/isAscii.ts b/packages/utils/src/isAscii.ts new file mode 100644 index 000000000000..31010e023dc5 --- /dev/null +++ b/packages/utils/src/isAscii.ts @@ -0,0 +1,4 @@ +export function isAscii(value?: string): boolean { + if (!value) return true; + return /^[\x00-\x7F]*$/.test(value); // eslint-disable-line +} diff --git a/packages/utils/src/isUrl.ts b/packages/utils/src/isUrl.ts new file mode 100644 index 000000000000..afe462c4142e --- /dev/null +++ b/packages/utils/src/isUrl.ts @@ -0,0 +1,4 @@ +const URL_REGEXP = + /^(http|ws)s?:\/\/[a-z0-9]([a-z0-9.-]+)?(:[0-9]{1,5})?((\/)?(([a-z0-9-_])+(\/)?)+)$/i; + +export const isUrl = (value: string): boolean => URL_REGEXP.test(value); diff --git a/packages/utils/tests/getNumberFromPixelString.test.ts b/packages/utils/tests/getNumberFromPixelString.test.ts new file mode 100644 index 000000000000..b76275a67893 --- /dev/null +++ b/packages/utils/tests/getNumberFromPixelString.test.ts @@ -0,0 +1,6 @@ +import { getNumberFromPixelString } from '../src/getNumberFromPixelString'; + +it('getNumberFromPixelString', () => { + expect(getNumberFromPixelString('1px')).toBe(1); + expect(getNumberFromPixelString('1')).toBe(1); +}); diff --git a/packages/utils/tests/getNumberFromPxString.test.ts b/packages/utils/tests/getNumberFromPxString.test.ts deleted file mode 100644 index becb276d652d..000000000000 --- a/packages/utils/tests/getNumberFromPxString.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { getNumberFromPxString } from '../src/getNumberFromPxString'; - -it('getNumberFromPxString', () => { - expect(getNumberFromPxString('1px')).toBe(1); - expect(getNumberFromPxString('1')).toBe(1); -}); diff --git a/packages/utils/tests/hasUppercaseLetter.test.ts b/packages/utils/tests/hasUppercaseLetter.test.ts new file mode 100644 index 000000000000..10a19d5fbb46 --- /dev/null +++ b/packages/utils/tests/hasUppercaseLetter.test.ts @@ -0,0 +1,14 @@ +import { hasUppercaseLetter } from '../src/hasUppercaseLetter'; + +describe('hasUppercaseLetter', () => { + it('hasUppercaseLetter', () => { + expect(hasUppercaseLetter('0')).toBe(false); + expect(hasUppercaseLetter('abc')).toBe(false); + expect(hasUppercaseLetter('abcD')).toBe(true); + expect(hasUppercaseLetter('Abcd')).toBe(true); + expect(hasUppercaseLetter('aBcd')).toBe(true); + expect(hasUppercaseLetter('123abc123')).toBe(false); + expect(hasUppercaseLetter('0x123abc456')).toBe(false); + expect(hasUppercaseLetter('0x123aBc456')).toBe(true); + }); +}); diff --git a/packages/utils/tests/isAscii.test.ts b/packages/utils/tests/isAscii.test.ts new file mode 100644 index 000000000000..50af1f16982e --- /dev/null +++ b/packages/utils/tests/isAscii.test.ts @@ -0,0 +1,18 @@ +import { isAscii } from '../src/isAscii'; + +describe('isAscii', () => { + describe('isAscii', () => { + it('should return true for ASCII only string', () => { + expect(isAscii('this is only ascii')).toEqual(true); + }); + + it('should return true when called without parameter', () => { + expect(isAscii()).toEqual(true); + }); + + it('should return false strings with non ASCII chars', () => { + expect(isAscii('¥')).toEqual(false); + expect(isAscii('železniční přejezd')).toEqual(false); + }); + }); +});