From 9edecf11f6e68b6a945ea4748edb10daad150de7 Mon Sep 17 00:00:00 2001 From: liujuping Date: Thu, 25 Jan 2024 10:38:52 +0800 Subject: [PATCH] feat(utils): move checkPropTypes to utils module --- packages/renderer-core/src/renderer/base.tsx | 3 +- packages/renderer-core/src/utils/common.ts | 76 ------ .../renderer-core/tests/utils/common.test.ts | 219 ------------------ packages/utils/package.json | 1 + packages/utils/src/check-prop-types.ts | 68 ++++++ packages/utils/src/check-types/index.ts | 4 +- .../src/check-types/is-basic-prop-type.ts | 8 + .../src/check-types/is-required-prop-type.ts | 8 + packages/utils/src/context-menu.tsx | 2 +- packages/utils/src/index.ts | 1 + .../utils/test/src/check-prop-types.test.ts | 190 +++++++++++++++ .../check-types/is-basic-prop-type.test.ts | 11 + .../check-types/is-required-prop-type.test.ts | 13 ++ 13 files changed, 305 insertions(+), 299 deletions(-) create mode 100644 packages/utils/src/check-prop-types.ts create mode 100644 packages/utils/src/check-types/is-basic-prop-type.ts create mode 100644 packages/utils/src/check-types/is-required-prop-type.ts create mode 100644 packages/utils/test/src/check-prop-types.test.ts create mode 100644 packages/utils/test/src/check-types/is-basic-prop-type.test.ts create mode 100644 packages/utils/test/src/check-types/is-required-prop-type.test.ts diff --git a/packages/renderer-core/src/renderer/base.tsx b/packages/renderer-core/src/renderer/base.tsx index 9d9e88ab5..d24009560 100644 --- a/packages/renderer-core/src/renderer/base.tsx +++ b/packages/renderer-core/src/renderer/base.tsx @@ -4,7 +4,7 @@ import classnames from 'classnames'; import { create as createDataSourceEngine } from '@alilc/lowcode-datasource-engine/interpret'; import { IPublicTypeNodeSchema, IPublicTypeNodeData, IPublicTypeJSONValue, IPublicTypeCompositeValue } from '@alilc/lowcode-types'; -import { isI18nData, isJSExpression, isJSFunction } from '@alilc/lowcode-utils'; +import { checkPropTypes, isI18nData, isJSExpression, isJSFunction } from '@alilc/lowcode-utils'; import adapter from '../adapter'; import divFactory from '../components/Div'; import visualDomFactory from '../components/VisualDom'; @@ -21,7 +21,6 @@ import { isFileSchema, transformArrayToMap, transformStringToFunction, - checkPropTypes, getI18n, getFileCssName, capitalizeFirstLetter, diff --git a/packages/renderer-core/src/utils/common.ts b/packages/renderer-core/src/utils/common.ts index 894d2d8ca..80150f379 100644 --- a/packages/renderer-core/src/utils/common.ts +++ b/packages/renderer-core/src/utils/common.ts @@ -6,16 +6,11 @@ import { isI18nData, isJSExpression } from '@alilc/lowcode-utils'; import { isEmpty } from 'lodash'; import IntlMessageFormat from 'intl-messageformat'; import pkg from '../../package.json'; -import * as ReactIs from 'react-is'; -import { default as ReactPropTypesSecret } from 'prop-types/lib/ReactPropTypesSecret'; -import { default as factoryWithTypeCheckers } from 'prop-types/factoryWithTypeCheckers'; (window as any).sdkVersion = pkg.version; export { pick, isEqualWith as deepEqual, cloneDeep as clone, isEmpty, throttle, debounce } from 'lodash'; -const PropTypes2 = factoryWithTypeCheckers(ReactIs.isElement, true); - const EXPRESSION_TYPE = { JSEXPRESSION: 'JSExpression', JSFUNCTION: 'JSFunction', @@ -183,77 +178,6 @@ export function transformArrayToMap(arr: any[], key: string, overwrite = true) { return res; } -export function isBasicType(propType: IPublicTypePropType): propType is IPublicTypeBasicType { - if (!propType) { - return false; - } - return typeof propType === 'string'; -} - -export function isRequiredType(propType: IPublicTypePropType): propType is IPublicTypeRequiredType { - if (!propType) { - return false; - } - return typeof propType === 'object' && propType.type && ['array', 'bool', 'func', 'number', 'object', 'string', 'node', 'element', 'any'].includes(propType.type); -} - -export function transformPropTypesRuleToString(rule: IPublicTypePropType): string { - if (!rule) { - return 'PropTypes.any'; - } - - if (typeof rule === 'string') { - return `PropTypes.${rule}`; - } - - if (isRequiredType(rule)) { - const { type, isRequired } = rule; - return `PropTypes.${type}${isRequired ? '.isRequired' : ''}`; - } - - const { type, value } = rule; - switch (type) { - case 'oneOf': - return `PropTypes.oneOf([${value.map((item: any) => `"${item}"`).join(',')}])`; - case 'oneOfType': - return `PropTypes.oneOfType([${value.map((item: any) => transformPropTypesRuleToString(item)).join(', ')}])`; - case 'arrayOf': - case 'objectOf': - return `PropTypes.${type}(${transformPropTypesRuleToString(value)})`; - case 'shape': - case 'exact': - return `PropTypes.${type}({${value.map((item: any) => `${item.name}: ${transformPropTypesRuleToString(item.propType)}`).join(',')}})`; - } -} - -export function checkPropTypes(value: any, name: string, rule: any, componentName: string): boolean { - let ruleFunction = rule; - if (typeof rule === 'object') { - ruleFunction = new Function(`"use strict"; const PropTypes = arguments[0]; return ${transformPropTypesRuleToString(rule)}`)(PropTypes2); - } - if (typeof rule === 'string') { - ruleFunction = new Function(`"use strict"; const PropTypes = arguments[0]; return ${rule}`)(PropTypes2); - } - if (!ruleFunction || typeof ruleFunction !== 'function') { - logger.warn('checkPropTypes should have a function type rule argument'); - return true; - } - const err = ruleFunction( - { - [name]: value, - }, - name, - componentName, - 'prop', - null, - ReactPropTypesSecret, - ); - if (err) { - logger.warn(err); - } - return !err; -} - /** * transform string to a function * @param str function in string form diff --git a/packages/renderer-core/tests/utils/common.test.ts b/packages/renderer-core/tests/utils/common.test.ts index 45a8e33d7..13b6908d5 100644 --- a/packages/renderer-core/tests/utils/common.test.ts +++ b/packages/renderer-core/tests/utils/common.test.ts @@ -1,4 +1,3 @@ -import factoryWithTypeCheckers from 'prop-types/factoryWithTypeCheckers'; import { isSchema, isFileSchema, @@ -18,17 +17,9 @@ import { parseThisRequiredExpression, parseI18n, parseData, - checkPropTypes, - transformPropTypesRuleToString, - isRequiredType, - isBasicType, } from '../../src/utils/common'; import logger from '../../src/utils/logger'; -var ReactIs = require('react-is'); - -const PropTypes = factoryWithTypeCheckers(ReactIs.isElement, true); - describe('test isSchema', () => { it('should be false when empty value is passed', () => { expect(isSchema(null)).toBeFalsy(); @@ -470,213 +461,3 @@ describe('test parseData ', () => { }); }); - -describe('test isBasicType ', () => { - it('should work', () => { - expect(isBasicType(null)).toBeFalsy(); - expect(isBasicType(undefined)).toBeFalsy(); - expect(isBasicType({})).toBeFalsy(); - expect(isBasicType({ type: 'any other type' })).toBeFalsy(); - expect(isBasicType('string')).toBeTruthy(); - }); -}); - -describe('test isRequiredType', () => { - it('should work', () => { - expect(isRequiredType(null)).toBeFalsy(); - expect(isRequiredType(undefined)).toBeFalsy(); - expect(isRequiredType({})).toBeFalsy(); - expect(isRequiredType({ type: 'any other type' })).toBeFalsy(); - expect(isRequiredType('string')).toBeFalsy(); - expect(isRequiredType({ type: 'string' })).toBeTruthy(); - expect(isRequiredType({ type: 'string', isRequired: true })).toBeTruthy(); - }); -}) - -describe('checkPropTypes', () => { - it('should validate correctly with valid prop type', () => { - expect(checkPropTypes(123, 'age', PropTypes.number, 'TestComponent')).toBe(true); - expect(checkPropTypes('123', 'age', PropTypes.string, 'TestComponent')).toBe(true); - }); - - it('should log a warning and return false with invalid prop type', () => { - expect(checkPropTypes(123, 'age', PropTypes.string, 'TestComponent')).toBe(false); - expect(checkPropTypes('123', 'age', PropTypes.number, 'TestComponent')).toBe(false); - }); - - it('should handle custom rule functions correctly', () => { - const customRule = (props, propName) => { - if (props[propName] !== 123) { - return new Error('Invalid value'); - } - }; - const result = checkPropTypes(123, 'customProp', customRule, 'TestComponent'); - expect(result).toBe(true); - }); - - - it('should interpret and validate a rule given as a string', () => { - const result = checkPropTypes(123, 'age', 'PropTypes.number', 'TestComponent'); - expect(result).toBe(true); - }); - - it('should log a warning for invalid rule type', () => { - const result = checkPropTypes(123, 'age', 123, 'TestComponent'); - expect(result).toBe(true); - }); - - // oneOf - it('should validate correctly with valid oneOf prop type', () => { - const rule = { - type: 'oneOf', - value: ['News', 'Photos'], - } - expect(transformPropTypesRuleToString(rule)).toBe(`PropTypes.oneOf(["News","Photos"])`); - expect(checkPropTypes('News', 'type', rule, 'TestComponent')).toBe(true); - expect(checkPropTypes('Others', 'type', rule, 'TestComponent')).toBe(false); - }); - - // oneOfType - it('should validate correctly with valid oneOfType prop type', () => { - const rule = { - type: 'oneOfType', - value: ['string', 'number', { - type: 'array', - isRequired: true, - }], - }; - expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array.isRequired])'); - expect(checkPropTypes(['News', 'Photos'], 'type', rule, 'TestComponent')).toBe(true); - expect(checkPropTypes('News', 'type', rule, 'TestComponent')).toBe(true); - expect(checkPropTypes(123, 'type', rule, 'TestComponent')).toBe(true); - expect(checkPropTypes({}, 'type', rule, 'TestComponent')).toBe(false); - }); - - // arrayOf - it('should validate correctly with valid arrayOf prop type', () => { - const rule = { - type: 'arrayOf', - value: { - type: 'string', - isRequired: true, - }, - }; - expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.arrayOf(PropTypes.string.isRequired)'); - expect(checkPropTypes(['News', 'Photos'], 'type', rule, 'TestComponent')).toBe(true); - expect(checkPropTypes(['News', 123], 'type', rule, 'TestComponent')).toBe(false); - }); - - // objectOf - it('should validate correctly with valid objectOf prop type', () => { - const rule = { - type: 'objectOf', - value: { - type: 'string', - isRequired: true, - }, - }; - expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.objectOf(PropTypes.string.isRequired)'); - expect(checkPropTypes({ a: 'News', b: 'Photos' }, 'type', rule, 'TestComponent')).toBe(true); - expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule, 'TestComponent')).toBe(false); - }); - - // shape - it('should validate correctly with valid shape prop type', () => { - const rule = { - type: 'shape', - value: [ - { - name: 'a', - propType: { - type: 'string', - isRequired: true, - }, - }, - { - name: 'b', - propType: { - type: 'number', - isRequired: true, - }, - }, - ], - }; - expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.shape({a: PropTypes.string.isRequired,b: PropTypes.number.isRequired})'); - expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule, 'TestComponent')).toBe(true); - expect(checkPropTypes({ a: 'News', b: 'Photos' }, 'type', rule, 'TestComponent')).toBe(false); - - // isRequired - const rule2 = { - type: 'shape', - value: [ - { - name: 'a', - propType: { - type: 'string', - isRequired: true, - }, - }, - { - name: 'b', - propType: { - type: 'number', - isRequired: false, - }, - }, - ], - }; - expect(transformPropTypesRuleToString(rule2)).toBe('PropTypes.shape({a: PropTypes.string.isRequired,b: PropTypes.number})'); - expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule2, 'TestComponent')).toBe(true); - expect(checkPropTypes({ b: 123 }, 'type', rule2, 'TestComponent')).toBe(false); - }); - - // exact - it('should validate correctly with valid exact prop type', () => { - const rule = { - type: 'exact', - value: [ - { - name: 'a', - propType: { - type: 'string', - isRequired: true, - }, - }, - { - name: 'b', - propType: { - type: 'number', - isRequired: true, - }, - }, - ], - }; - expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.exact({a: PropTypes.string.isRequired,b: PropTypes.number.isRequired})'); - expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule, 'TestComponent')).toBe(true); - expect(checkPropTypes({ a: 'News', b: 'Photos' }, 'type', rule, 'TestComponent')).toBe(false); - - // isRequired - const rule2 = { - type: 'exact', - value: [ - { - name: 'a', - propType: { - type: 'string', - isRequired: true, - }, - }, - { - name: 'b', - propType: { - type: 'number', - isRequired: false, - }, - }, - ], - }; - expect(transformPropTypesRuleToString(rule2)).toBe('PropTypes.exact({a: PropTypes.string.isRequired,b: PropTypes.number})'); - expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule2, 'TestComponent')).toBe(true); - expect(checkPropTypes({ b: 123 }, 'type', rule2, 'TestComponent')).toBe(false); - }); -}); \ No newline at end of file diff --git a/packages/utils/package.json b/packages/utils/package.json index 12e3ebc1b..cc363a729 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -17,6 +17,7 @@ "@alilc/lowcode-types": "1.3.1", "lodash": "^4.17.21", "mobx": "^6.3.0", + "prop-types": "^15.8.1", "react": "^16" }, "devDependencies": { diff --git a/packages/utils/src/check-prop-types.ts b/packages/utils/src/check-prop-types.ts new file mode 100644 index 000000000..89555c70b --- /dev/null +++ b/packages/utils/src/check-prop-types.ts @@ -0,0 +1,68 @@ +import * as ReactIs from 'react-is'; +import { default as ReactPropTypesSecret } from 'prop-types/lib/ReactPropTypesSecret'; +import { default as factoryWithTypeCheckers } from 'prop-types/factoryWithTypeCheckers'; +import { IPublicTypePropType } from '@alilc/lowcode-types'; +import { isRequiredPropType } from './check-types/is-required-prop-type'; +import { Logger } from './logger'; + +const PropTypes2 = factoryWithTypeCheckers(ReactIs.isElement, true); +const logger = new Logger({ level: 'warn', bizName: 'utils' }); + +export function transformPropTypesRuleToString(rule: IPublicTypePropType): string { + if (!rule) { + return 'PropTypes.any'; + } + + if (typeof rule === 'string') { + return `PropTypes.${rule}`; + } + + if (isRequiredPropType(rule)) { + const { type, isRequired } = rule; + return `PropTypes.${type}${isRequired ? '.isRequired' : ''}`; + } + + const { type, value } = rule; + switch (type) { + case 'oneOf': + return `PropTypes.oneOf([${value.map((item: any) => `"${item}"`).join(',')}])`; + case 'oneOfType': + return `PropTypes.oneOfType([${value.map((item: any) => transformPropTypesRuleToString(item)).join(', ')}])`; + case 'arrayOf': + case 'objectOf': + return `PropTypes.${type}(${transformPropTypesRuleToString(value)})`; + case 'shape': + case 'exact': + return `PropTypes.${type}({${value.map((item: any) => `${item.name}: ${transformPropTypesRuleToString(item.propType)}`).join(',')}})`; + } +} + +export function checkPropTypes(value: any, name: string, rule: any, componentName: string): boolean { + let ruleFunction = rule; + if (typeof rule === 'object') { + // eslint-disable-next-line no-new-func + ruleFunction = new Function(`"use strict"; const PropTypes = arguments[0]; return ${transformPropTypesRuleToString(rule)}`)(PropTypes2); + } + if (typeof rule === 'string') { + // eslint-disable-next-line no-new-func + ruleFunction = new Function(`"use strict"; const PropTypes = arguments[0]; return ${rule}`)(PropTypes2); + } + if (!ruleFunction || typeof ruleFunction !== 'function') { + logger.warn('checkPropTypes should have a function type rule argument'); + return true; + } + const err = ruleFunction( + { + [name]: value, + }, + name, + componentName, + 'prop', + null, + ReactPropTypesSecret, + ); + if (err) { + logger.warn(err); + } + return !err; +} diff --git a/packages/utils/src/check-types/index.ts b/packages/utils/src/check-types/index.ts index 3155926ef..507259b2c 100644 --- a/packages/utils/src/check-types/index.ts +++ b/packages/utils/src/check-types/index.ts @@ -23,4 +23,6 @@ export * from './is-location-data'; export * from './is-setting-field'; export * from './is-lowcode-component-type'; export * from './is-lowcode-project-schema'; -export * from './is-component-schema'; \ No newline at end of file +export * from './is-component-schema'; +export * from './is-basic-prop-type'; +export * from './is-required-prop-type'; \ No newline at end of file diff --git a/packages/utils/src/check-types/is-basic-prop-type.ts b/packages/utils/src/check-types/is-basic-prop-type.ts new file mode 100644 index 000000000..fd3b1b1dc --- /dev/null +++ b/packages/utils/src/check-types/is-basic-prop-type.ts @@ -0,0 +1,8 @@ +import { IPublicTypeBasicType, IPublicTypePropType } from '@alilc/lowcode-types'; + +export function isBasicPropType(propType: IPublicTypePropType): propType is IPublicTypeBasicType { + if (!propType) { + return false; + } + return typeof propType === 'string'; +} \ No newline at end of file diff --git a/packages/utils/src/check-types/is-required-prop-type.ts b/packages/utils/src/check-types/is-required-prop-type.ts new file mode 100644 index 000000000..106da78a0 --- /dev/null +++ b/packages/utils/src/check-types/is-required-prop-type.ts @@ -0,0 +1,8 @@ +import { IPublicTypePropType, IPublicTypeRequiredType } from '@alilc/lowcode-types'; + +export function isRequiredPropType(propType: IPublicTypePropType): propType is IPublicTypeRequiredType { + if (!propType) { + return false; + } + return typeof propType === 'object' && propType.type && ['array', 'bool', 'func', 'number', 'object', 'string', 'node', 'element', 'any'].includes(propType.type); +} \ No newline at end of file diff --git a/packages/utils/src/context-menu.tsx b/packages/utils/src/context-menu.tsx index d783a9643..185abbb34 100644 --- a/packages/utils/src/context-menu.tsx +++ b/packages/utils/src/context-menu.tsx @@ -5,7 +5,7 @@ import classNames from 'classnames'; import React from 'react'; import './context-menu.scss'; -const logger = new Logger({ level: 'warn', bizName: 'designer' }); +const logger = new Logger({ level: 'warn', bizName: 'utils' }); const { Item, Divider, PopupItem } = Menu; const MAX_LEVEL = 2; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index b99ab0207..22bad0e36 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -32,3 +32,4 @@ export { transactionManager } from './transaction-manager'; export * from './check-types'; export * from './workspace'; export * from './context-menu'; +export { checkPropTypes } from './check-prop-types'; \ No newline at end of file diff --git a/packages/utils/test/src/check-prop-types.test.ts b/packages/utils/test/src/check-prop-types.test.ts new file mode 100644 index 000000000..c1902bec3 --- /dev/null +++ b/packages/utils/test/src/check-prop-types.test.ts @@ -0,0 +1,190 @@ +import { checkPropTypes, transformPropTypesRuleToString } from '../../src/check-prop-types'; +import PropTypes from 'prop-types'; + +describe('checkPropTypes', () => { + it('should validate correctly with valid prop type', () => { + expect(checkPropTypes(123, 'age', PropTypes.number, 'TestComponent')).toBe(true); + expect(checkPropTypes('123', 'age', PropTypes.string, 'TestComponent')).toBe(true); + }); + + it('should log a warning and return false with invalid prop type', () => { + expect(checkPropTypes(123, 'age', PropTypes.string, 'TestComponent')).toBe(false); + expect(checkPropTypes('123', 'age', PropTypes.number, 'TestComponent')).toBe(false); + }); + + it('should handle custom rule functions correctly', () => { + const customRule = (props, propName) => { + if (props[propName] !== 123) { + return new Error('Invalid value'); + } + }; + const result = checkPropTypes(123, 'customProp', customRule, 'TestComponent'); + expect(result).toBe(true); + }); + + + it('should interpret and validate a rule given as a string', () => { + const result = checkPropTypes(123, 'age', 'PropTypes.number', 'TestComponent'); + expect(result).toBe(true); + }); + + it('should log a warning for invalid rule type', () => { + const result = checkPropTypes(123, 'age', 123, 'TestComponent'); + expect(result).toBe(true); + }); + + // oneOf + it('should validate correctly with valid oneOf prop type', () => { + const rule = { + type: 'oneOf', + value: ['News', 'Photos'], + } + expect(transformPropTypesRuleToString(rule)).toBe(`PropTypes.oneOf(["News","Photos"])`); + expect(checkPropTypes('News', 'type', rule, 'TestComponent')).toBe(true); + expect(checkPropTypes('Others', 'type', rule, 'TestComponent')).toBe(false); + }); + + // oneOfType + it('should validate correctly with valid oneOfType prop type', () => { + const rule = { + type: 'oneOfType', + value: ['string', 'number', { + type: 'array', + isRequired: true, + }], + }; + expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array.isRequired])'); + expect(checkPropTypes(['News', 'Photos'], 'type', rule, 'TestComponent')).toBe(true); + expect(checkPropTypes('News', 'type', rule, 'TestComponent')).toBe(true); + expect(checkPropTypes(123, 'type', rule, 'TestComponent')).toBe(true); + expect(checkPropTypes({}, 'type', rule, 'TestComponent')).toBe(false); + }); + + // arrayOf + it('should validate correctly with valid arrayOf prop type', () => { + const rule = { + type: 'arrayOf', + value: { + type: 'string', + isRequired: true, + }, + }; + expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.arrayOf(PropTypes.string.isRequired)'); + expect(checkPropTypes(['News', 'Photos'], 'type', rule, 'TestComponent')).toBe(true); + expect(checkPropTypes(['News', 123], 'type', rule, 'TestComponent')).toBe(false); + }); + + // objectOf + it('should validate correctly with valid objectOf prop type', () => { + const rule = { + type: 'objectOf', + value: { + type: 'string', + isRequired: true, + }, + }; + expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.objectOf(PropTypes.string.isRequired)'); + expect(checkPropTypes({ a: 'News', b: 'Photos' }, 'type', rule, 'TestComponent')).toBe(true); + expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule, 'TestComponent')).toBe(false); + }); + + // shape + it('should validate correctly with valid shape prop type', () => { + const rule = { + type: 'shape', + value: [ + { + name: 'a', + propType: { + type: 'string', + isRequired: true, + }, + }, + { + name: 'b', + propType: { + type: 'number', + isRequired: true, + }, + }, + ], + }; + expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.shape({a: PropTypes.string.isRequired,b: PropTypes.number.isRequired})'); + expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule, 'TestComponent')).toBe(true); + expect(checkPropTypes({ a: 'News', b: 'Photos' }, 'type', rule, 'TestComponent')).toBe(false); + + // isRequired + const rule2 = { + type: 'shape', + value: [ + { + name: 'a', + propType: { + type: 'string', + isRequired: true, + }, + }, + { + name: 'b', + propType: { + type: 'number', + isRequired: false, + }, + }, + ], + }; + expect(transformPropTypesRuleToString(rule2)).toBe('PropTypes.shape({a: PropTypes.string.isRequired,b: PropTypes.number})'); + expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule2, 'TestComponent')).toBe(true); + expect(checkPropTypes({ b: 123 }, 'type', rule2, 'TestComponent')).toBe(false); + }); + + // exact + it('should validate correctly with valid exact prop type', () => { + const rule = { + type: 'exact', + value: [ + { + name: 'a', + propType: { + type: 'string', + isRequired: true, + }, + }, + { + name: 'b', + propType: { + type: 'number', + isRequired: true, + }, + }, + ], + }; + expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.exact({a: PropTypes.string.isRequired,b: PropTypes.number.isRequired})'); + expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule, 'TestComponent')).toBe(true); + expect(checkPropTypes({ a: 'News', b: 'Photos' }, 'type', rule, 'TestComponent')).toBe(false); + + // isRequired + const rule2 = { + type: 'exact', + value: [ + { + name: 'a', + propType: { + type: 'string', + isRequired: true, + }, + }, + { + name: 'b', + propType: { + type: 'number', + isRequired: false, + }, + }, + ], + }; + expect(transformPropTypesRuleToString(rule2)).toBe('PropTypes.exact({a: PropTypes.string.isRequired,b: PropTypes.number})'); + expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule2, 'TestComponent')).toBe(true); + expect(checkPropTypes({ b: 123 }, 'type', rule2, 'TestComponent')).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/utils/test/src/check-types/is-basic-prop-type.test.ts b/packages/utils/test/src/check-types/is-basic-prop-type.test.ts new file mode 100644 index 000000000..81a1bf0d3 --- /dev/null +++ b/packages/utils/test/src/check-types/is-basic-prop-type.test.ts @@ -0,0 +1,11 @@ +import { isBasicPropType } from '../../../src'; + +describe('test isBasicPropType ', () => { + it('should work', () => { + expect(isBasicPropType(null)).toBeFalsy(); + expect(isBasicPropType(undefined)).toBeFalsy(); + expect(isBasicPropType({})).toBeFalsy(); + expect(isBasicPropType({ type: 'any other type' })).toBeFalsy(); + expect(isBasicPropType('string')).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/packages/utils/test/src/check-types/is-required-prop-type.test.ts b/packages/utils/test/src/check-types/is-required-prop-type.test.ts new file mode 100644 index 000000000..25515f9aa --- /dev/null +++ b/packages/utils/test/src/check-types/is-required-prop-type.test.ts @@ -0,0 +1,13 @@ +import { isRequiredPropType } from '../../../src'; + +describe('test isRequiredType', () => { + it('should work', () => { + expect(isRequiredPropType(null)).toBeFalsy(); + expect(isRequiredPropType(undefined)).toBeFalsy(); + expect(isRequiredPropType({})).toBeFalsy(); + expect(isRequiredPropType({ type: 'any other type' })).toBeFalsy(); + expect(isRequiredPropType('string')).toBeFalsy(); + expect(isRequiredPropType({ type: 'string' })).toBeTruthy(); + expect(isRequiredPropType({ type: 'string', isRequired: true })).toBeTruthy(); + }); +})