From 325915e97adc45961d622a660c0310a8b0d60fdf Mon Sep 17 00:00:00 2001 From: Nikita Zolotykh Date: Wed, 7 May 2025 13:05:25 +0200 Subject: [PATCH] test(unstable): update tests --- src/lib/unstable/__tests__/helpers.test.ts | 240 ------ .../core/Entity/__tests__/utils.test.ts | 740 +++++++++++++++--- .../__tests__/utils.test.ts | 104 +-- .../core/SchemaRendererServiceField/utils.ts | 8 +- .../__tests__/async-validation.test.ts | 370 +++++---- .../set-errors/__tests__/set-errors.test.ts | 431 +++++----- src/lib/unstable/core/types/schema.ts | 6 +- .../core/utils/__tests__/common.test.ts | 115 +-- src/stories/Unstable.stories.tsx | 2 - 9 files changed, 1193 insertions(+), 823 deletions(-) delete mode 100644 src/lib/unstable/__tests__/helpers.test.ts diff --git a/src/lib/unstable/__tests__/helpers.test.ts b/src/lib/unstable/__tests__/helpers.test.ts deleted file mode 100644 index 9a3c10e8..00000000 --- a/src/lib/unstable/__tests__/helpers.test.ts +++ /dev/null @@ -1,240 +0,0 @@ -import type {InternalFormState, MutableState, Tools} from 'final-form'; - -import {JsonSchemaType, SchemaRendererMode} from '../core/constants'; -import type { - IndependentView, - JsonSchema, - JsonSchemaArray, - JsonSchemaBoolean, - JsonSchemaNumber, - JsonSchemaObject, - JsonSchemaString, - SchemaRendererConfig, - SimpleView, - Wrapper, -} from '../core/types'; - -export const MockArrayFormComponent: SimpleView = () => null; -export const MockBooleanFormComponent: SimpleView = () => null; -export const MockNumberFormComponent: SimpleView = () => null; -export const MockObjectFormComponent: SimpleView = () => null; -export const MockStringFormComponent: SimpleView = () => null; - -export const MockArrayOverviewComponent: SimpleView = () => null; -export const MockBooleanOverviewComponent: SimpleView = () => null; -export const MockNumberOverviewComponent: SimpleView = () => null; -export const MockObjectOverviewComponent: SimpleView = () => null; -export const MockStringOverviewComponent: SimpleView = () => null; - -export const MockIndependentArrayFormComponent: IndependentView = () => null; -export const MockIndependentBooleanFormComponent: IndependentView = () => null; -export const MockIndependentNumberFormComponent: IndependentView = () => null; -export const MockIndependentObjectFormComponent: IndependentView = () => null; -export const MockIndependentStringFormComponent: IndependentView = () => null; - -export const MockIndependentArrayOverviewComponent: IndependentView = () => null; -export const MockIndependentBooleanOverviewComponent: IndependentView = () => - null; -export const MockIndependentNumberOverviewComponent: IndependentView = () => null; -export const MockIndependentObjectOverviewComponent: IndependentView = () => null; -export const MockIndependentStringOverviewComponent: IndependentView = () => null; - -export const MockArrayWrapper: Wrapper = () => null; -export const MockBooleanWrapper: Wrapper = () => null; -export const MockNumberWrapper: Wrapper = () => null; -export const MockObjectWrapper: Wrapper = () => null; -export const MockStringWrapper: Wrapper = () => null; - -export const mockSchemaRendererConfig: SchemaRendererConfig = { - [JsonSchemaType.Array]: { - views: { - base: { - [SchemaRendererMode.Form]: { - Component: MockArrayFormComponent, - }, - [SchemaRendererMode.Overview]: { - Component: MockArrayOverviewComponent, - }, - }, - custom: { - [SchemaRendererMode.Form]: { - Component: MockIndependentArrayFormComponent, - independent: true, - }, - [SchemaRendererMode.Overview]: { - Component: MockIndependentArrayOverviewComponent, - independent: true, - }, - }, - }, - wrappers: { - default: MockArrayWrapper, - }, - validators: {}, - }, - [JsonSchemaType.Boolean]: { - views: { - base: { - [SchemaRendererMode.Form]: { - Component: MockBooleanFormComponent, - }, - [SchemaRendererMode.Overview]: { - Component: MockBooleanOverviewComponent, - }, - }, - custom: { - [SchemaRendererMode.Form]: { - Component: MockIndependentBooleanFormComponent, - independent: true, - }, - [SchemaRendererMode.Overview]: { - Component: MockIndependentBooleanOverviewComponent, - independent: true, - }, - }, - }, - wrappers: { - default: MockBooleanWrapper, - }, - validators: {}, - }, - [JsonSchemaType.Number]: { - views: { - base: { - [SchemaRendererMode.Form]: { - Component: MockNumberFormComponent, - }, - [SchemaRendererMode.Overview]: { - Component: MockNumberOverviewComponent, - }, - }, - custom: { - [SchemaRendererMode.Form]: { - Component: MockIndependentNumberFormComponent, - independent: true, - }, - [SchemaRendererMode.Overview]: { - Component: MockIndependentNumberOverviewComponent, - independent: true, - }, - }, - }, - wrappers: { - default: MockNumberWrapper, - }, - validators: {}, - }, - [JsonSchemaType.Object]: { - views: { - base: { - [SchemaRendererMode.Form]: { - Component: MockObjectFormComponent, - }, - [SchemaRendererMode.Overview]: { - Component: MockObjectOverviewComponent, - }, - }, - custom: { - [SchemaRendererMode.Form]: { - Component: MockIndependentObjectFormComponent, - independent: true, - }, - [SchemaRendererMode.Overview]: { - Component: MockIndependentObjectOverviewComponent, - independent: true, - }, - }, - }, - wrappers: { - default: MockObjectWrapper, - }, - validators: {}, - }, - [JsonSchemaType.String]: { - views: { - base: { - [SchemaRendererMode.Form]: { - Component: MockStringFormComponent, - }, - [SchemaRendererMode.Overview]: { - Component: MockStringOverviewComponent, - }, - }, - custom: { - [SchemaRendererMode.Form]: { - Component: MockIndependentStringFormComponent, - independent: true, - }, - [SchemaRendererMode.Overview]: { - Component: MockIndependentStringOverviewComponent, - independent: true, - }, - }, - }, - wrappers: { - default: MockStringWrapper, - }, - validators: {}, - }, -}; - -export const createMockArraySchema = (items: JsonSchema | JsonSchema[]): JsonSchema => ({ - type: JsonSchemaType.Array, - items, -}); - -export const createMockBooleanSchema = (): JsonSchemaBoolean => ({ - type: JsonSchemaType.Boolean, -}); - -export const createMockNumberSchema = (): JsonSchemaNumber => ({ - type: JsonSchemaType.Number, -}); - -export const createMockStringSchema = (): JsonSchemaString => ({ - type: JsonSchemaType.String, -}); - -export const createMockObjectSchema = ( - properties: Record, -): JsonSchemaObject => ({ - type: JsonSchemaType.Object, - properties, -}); - -export function createMockSchema( - type: JsonSchemaType, - viewType = 'base', - wrapperType = 'default', - viewProps: Record = {}, - wrapperProps: Record = {}, -): T { - return { - type, - entityParameters: { - viewType, - wrapperType, - viewProps, - wrapperProps, - }, - } as T; -} - -export const mockTools = {} as Tools<{}, {}>; -export const mockServiceFieldName = 'mockServiceFieldName'; - -export const createMockMutableState = (fields: Record = {}): MutableState<{}, {}> => { - const mockFormState = {} as InternalFormState; - - return { - fields, - formState: mockFormState, - fieldSubscribers: {}, - }; -}; - -describe('helpers', () => { - test('just empty test', () => { - expect(true).toBe(true); - }); -}); diff --git a/src/lib/unstable/core/Entity/__tests__/utils.test.ts b/src/lib/unstable/core/Entity/__tests__/utils.test.ts index dd9480f8..232053e7 100644 --- a/src/lib/unstable/core/Entity/__tests__/utils.test.ts +++ b/src/lib/unstable/core/Entity/__tests__/utils.test.ts @@ -1,142 +1,704 @@ -import { - MockIndependentStringFormComponent, - MockIndependentStringOverviewComponent, - MockObjectFormComponent, - MockObjectWrapper, - MockStringFormComponent, - MockStringOverviewComponent, - MockStringWrapper, - createMockSchema, - mockSchemaRendererConfig, -} from '../../../__tests__/helpers.test'; import {JsonSchemaType, SchemaRendererMode} from '../../constants'; -import type {JsonSchema, JsonSchemaString} from '../../types'; +import type { + IndependentView, + JsonSchemaArray, + JsonSchemaBoolean, + JsonSchemaNumber, + JsonSchemaObject, + JsonSchemaString, + SchemaRendererConfig, + SimpleView, + Wrapper, +} from '../../types'; import {getRenderKit} from '../utils'; -describe('Entity/utils', () => { +const SimpleArrayFormComponent: SimpleView = () => null; +const SimpleBooleanFormComponent: SimpleView = () => null; +const SimpleNumberFormComponent: SimpleView = () => null; +const SimpleObjectFormComponent: SimpleView = () => null; +const SimpleStringFormComponent: SimpleView = () => null; + +const SimpleArrayOverviewComponent: SimpleView = () => null; +const SimpleBooleanOverviewComponent: SimpleView = () => null; +const SimpleNumberOverviewComponent: SimpleView = () => null; +const SimpleObjectOverviewComponent: SimpleView = () => null; +const SimpleStringOverviewComponent: SimpleView = () => null; + +const IndependentArrayFormComponent: IndependentView = () => null; +const IndependentBooleanFormComponent: IndependentView = () => null; +const IndependentNumberFormComponent: IndependentView = () => null; +const IndependentObjectFormComponent: IndependentView = () => null; +const IndependentStringFormComponent: IndependentView = () => null; + +const IndependentArrayOverviewComponent: IndependentView = () => null; +const IndependentBooleanOverviewComponent: IndependentView = () => null; +const IndependentNumberOverviewComponent: IndependentView = () => null; +const IndependentObjectOverviewComponent: IndependentView = () => null; +const IndependentStringOverviewComponent: IndependentView = () => null; + +const ArrayWrapper: Wrapper = () => null; +const BooleanWrapper: Wrapper = () => null; +const NumberWrapper: Wrapper = () => null; +const ObjectWrapper: Wrapper = () => null; +const StringWrapper: Wrapper = () => null; + +const schemaRendererConfig: SchemaRendererConfig = { + [JsonSchemaType.Array]: { + views: { + simple: { + [SchemaRendererMode.Form]: { + Component: SimpleArrayFormComponent, + }, + [SchemaRendererMode.Overview]: { + Component: SimpleArrayOverviewComponent, + }, + }, + independent: { + [SchemaRendererMode.Form]: { + Component: IndependentArrayFormComponent, + independent: true, + }, + [SchemaRendererMode.Overview]: { + Component: IndependentArrayOverviewComponent, + independent: true, + }, + }, + }, + wrappers: { + base: ArrayWrapper, + }, + validators: {}, + }, + [JsonSchemaType.Boolean]: { + views: { + simple: { + [SchemaRendererMode.Form]: { + Component: SimpleBooleanFormComponent, + }, + [SchemaRendererMode.Overview]: { + Component: SimpleBooleanOverviewComponent, + }, + }, + independent: { + [SchemaRendererMode.Form]: { + Component: IndependentBooleanFormComponent, + independent: true, + }, + [SchemaRendererMode.Overview]: { + Component: IndependentBooleanOverviewComponent, + independent: true, + }, + }, + }, + wrappers: { + base: BooleanWrapper, + }, + validators: {}, + }, + [JsonSchemaType.Number]: { + views: { + simple: { + [SchemaRendererMode.Form]: { + Component: SimpleNumberFormComponent, + }, + [SchemaRendererMode.Overview]: { + Component: SimpleNumberOverviewComponent, + }, + }, + independent: { + [SchemaRendererMode.Form]: { + Component: IndependentNumberFormComponent, + independent: true, + }, + [SchemaRendererMode.Overview]: { + Component: IndependentNumberOverviewComponent, + independent: true, + }, + }, + }, + wrappers: { + base: NumberWrapper, + }, + validators: {}, + }, + [JsonSchemaType.Object]: { + views: { + simple: { + [SchemaRendererMode.Form]: { + Component: SimpleObjectFormComponent, + }, + [SchemaRendererMode.Overview]: { + Component: SimpleObjectOverviewComponent, + }, + }, + independent: { + [SchemaRendererMode.Form]: { + Component: IndependentObjectFormComponent, + independent: true, + }, + [SchemaRendererMode.Overview]: { + Component: IndependentObjectOverviewComponent, + independent: true, + }, + }, + }, + wrappers: { + base: ObjectWrapper, + }, + validators: {}, + }, + [JsonSchemaType.String]: { + views: { + simple: { + [SchemaRendererMode.Form]: { + Component: SimpleStringFormComponent, + }, + [SchemaRendererMode.Overview]: { + Component: SimpleStringOverviewComponent, + }, + }, + independent: { + [SchemaRendererMode.Form]: { + Component: IndependentStringFormComponent, + independent: true, + }, + [SchemaRendererMode.Overview]: { + Component: IndependentStringOverviewComponent, + independent: true, + }, + }, + }, + wrappers: { + base: StringWrapper, + }, + validators: {}, + }, +}; + +describe('core/Entity/utils', () => { describe('getRenderKit', () => { - test('should return render kit with simple string form component', () => { - const schema = createMockSchema( - JsonSchemaType.String, - 'base', - 'default', - {viewProp: 'viewProp'}, - {wrapperProp: 'wrapperProp'}, - ); + test('should return render kit with simple array form component', () => { + const schema: JsonSchemaArray = { + type: JsonSchemaType.Array, + entityParameters: { + viewType: 'simple', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; const result = getRenderKit({ - config: mockSchemaRendererConfig, + config: schemaRendererConfig, mode: SchemaRendererMode.Form, schema, }); expect(result).toEqual({ - View: MockStringFormComponent, + View: SimpleArrayFormComponent, viewProps: {viewProp: 'viewProp'}, - Wrapper: MockStringWrapper, + Wrapper: ArrayWrapper, wrapperProps: {wrapperProp: 'wrapperProp'}, independent: undefined, }); }); - test('should return render kit with simple string overview component', () => { - const schema = createMockSchema( - JsonSchemaType.String, - 'base', - 'default', - {viewProp: 'viewProp'}, - {wrapperProp: 'wrapperProp'}, - ); + test('should return render kit with simple array overview component', () => { + const schema: JsonSchemaArray = { + type: JsonSchemaType.Array, + entityParameters: { + viewType: 'simple', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Overview, + schema, + }); + + expect(result).toEqual({ + View: SimpleArrayOverviewComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: ArrayWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: undefined, + }); + }); + + test('should return render kit with independent array form component', () => { + const schema: JsonSchemaArray = { + type: JsonSchemaType.Array, + entityParameters: { + viewType: 'independent', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Form, + schema, + }); + + expect(result).toEqual({ + View: IndependentArrayFormComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: ArrayWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: true, + }); + }); + + test('should return render kit with independent array overview component', () => { + const schema: JsonSchemaArray = { + type: JsonSchemaType.Array, + entityParameters: { + viewType: 'independent', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Overview, + schema, + }); + + expect(result).toEqual({ + View: IndependentArrayOverviewComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: ArrayWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: true, + }); + }); + + test('should return render kit with simple boolean form component', () => { + const schema: JsonSchemaBoolean = { + type: JsonSchemaType.Boolean, + entityParameters: { + viewType: 'simple', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Form, + schema, + }); + + expect(result).toEqual({ + View: SimpleBooleanFormComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: BooleanWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: undefined, + }); + }); + + test('should return render kit with simple boolean overview component', () => { + const schema: JsonSchemaBoolean = { + type: JsonSchemaType.Boolean, + entityParameters: { + viewType: 'simple', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Overview, + schema, + }); + + expect(result).toEqual({ + View: SimpleBooleanOverviewComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: BooleanWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: undefined, + }); + }); + + test('should return render kit with independent boolean form component', () => { + const schema: JsonSchemaBoolean = { + type: JsonSchemaType.Boolean, + entityParameters: { + viewType: 'independent', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Form, + schema, + }); + + expect(result).toEqual({ + View: IndependentBooleanFormComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: BooleanWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: true, + }); + }); + + test('should return render kit with independent boolean overview component', () => { + const schema: JsonSchemaBoolean = { + type: JsonSchemaType.Boolean, + entityParameters: { + viewType: 'independent', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; const result = getRenderKit({ - config: mockSchemaRendererConfig, + config: schemaRendererConfig, mode: SchemaRendererMode.Overview, schema, }); expect(result).toEqual({ - View: MockStringOverviewComponent, + View: IndependentBooleanOverviewComponent, viewProps: {viewProp: 'viewProp'}, - Wrapper: MockStringWrapper, + Wrapper: BooleanWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: true, + }); + }); + + test('should return render kit with simple number form component', () => { + const schema: JsonSchemaNumber = { + type: JsonSchemaType.Number, + entityParameters: { + viewType: 'simple', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Form, + schema, + }); + + expect(result).toEqual({ + View: SimpleNumberFormComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: NumberWrapper, wrapperProps: {wrapperProp: 'wrapperProp'}, independent: undefined, }); }); + test('should return render kit with simple number overview component', () => { + const schema: JsonSchemaNumber = { + type: JsonSchemaType.Number, + entityParameters: { + viewType: 'simple', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Overview, + schema, + }); + + expect(result).toEqual({ + View: SimpleNumberOverviewComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: NumberWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: undefined, + }); + }); + + test('should return render kit with independent number form component', () => { + const schema: JsonSchemaNumber = { + type: JsonSchemaType.Number, + entityParameters: { + viewType: 'independent', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Form, + schema, + }); + + expect(result).toEqual({ + View: IndependentNumberFormComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: NumberWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: true, + }); + }); + + test('should return render kit with independent number overview component', () => { + const schema: JsonSchemaNumber = { + type: JsonSchemaType.Number, + entityParameters: { + viewType: 'independent', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Overview, + schema, + }); + + expect(result).toEqual({ + View: IndependentNumberOverviewComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: NumberWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: true, + }); + }); + test('should return render kit with simple object form component', () => { - const schema = createMockSchema( - JsonSchemaType.Object, - 'base', - 'default', - {viewProp: 'viewProp'}, - {wrapperProp: 'wrapperProp'}, - ); + const schema: JsonSchemaObject = { + type: JsonSchemaType.Object, + entityParameters: { + viewType: 'simple', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; const result = getRenderKit({ - config: mockSchemaRendererConfig, + config: schemaRendererConfig, mode: SchemaRendererMode.Form, schema, }); expect(result).toEqual({ - View: MockObjectFormComponent, + View: SimpleObjectFormComponent, viewProps: {viewProp: 'viewProp'}, - Wrapper: MockObjectWrapper, + Wrapper: ObjectWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: undefined, + }); + }); + + test('should return render kit with simple object overview component', () => { + const schema: JsonSchemaObject = { + type: JsonSchemaType.Object, + entityParameters: { + viewType: 'simple', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Overview, + schema, + }); + + expect(result).toEqual({ + View: SimpleObjectOverviewComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: ObjectWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: undefined, + }); + }); + + test('should return render kit with independent object form component', () => { + const schema: JsonSchemaObject = { + type: JsonSchemaType.Object, + entityParameters: { + viewType: 'independent', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Form, + schema, + }); + + expect(result).toEqual({ + View: IndependentObjectFormComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: ObjectWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: true, + }); + }); + + test('should return render kit with independent object overview component', () => { + const schema: JsonSchemaObject = { + type: JsonSchemaType.Object, + entityParameters: { + viewType: 'independent', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Overview, + schema, + }); + + expect(result).toEqual({ + View: IndependentObjectOverviewComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: ObjectWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: true, + }); + }); + + test('should return render kit with simple string form component', () => { + const schema: JsonSchemaString = { + type: JsonSchemaType.String, + entityParameters: { + viewType: 'simple', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Form, + schema, + }); + + expect(result).toEqual({ + View: SimpleStringFormComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: StringWrapper, + wrapperProps: {wrapperProp: 'wrapperProp'}, + independent: undefined, + }); + }); + + test('should return render kit with simple string overview component', () => { + const schema: JsonSchemaString = { + type: JsonSchemaType.String, + entityParameters: { + viewType: 'simple', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; + + const result = getRenderKit({ + config: schemaRendererConfig, + mode: SchemaRendererMode.Overview, + schema, + }); + + expect(result).toEqual({ + View: SimpleStringOverviewComponent, + viewProps: {viewProp: 'viewProp'}, + Wrapper: StringWrapper, wrapperProps: {wrapperProp: 'wrapperProp'}, independent: undefined, }); }); test('should return render kit with independent string form component', () => { - const schema = createMockSchema( - JsonSchemaType.String, - 'custom', - 'default', - {viewProp: 'viewProp'}, - {wrapperProp: 'wrapperProp'}, - ); + const schema: JsonSchemaString = { + type: JsonSchemaType.String, + entityParameters: { + viewType: 'independent', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; const result = getRenderKit({ - config: mockSchemaRendererConfig, + config: schemaRendererConfig, mode: SchemaRendererMode.Form, schema, }); expect(result).toEqual({ - View: MockIndependentStringFormComponent, + View: IndependentStringFormComponent, viewProps: {viewProp: 'viewProp'}, - Wrapper: MockStringWrapper, + Wrapper: StringWrapper, wrapperProps: {wrapperProp: 'wrapperProp'}, independent: true, }); }); test('should return render kit with independent string overview component', () => { - const schema = createMockSchema( - JsonSchemaType.String, - 'custom', - 'default', - {viewProp: 'viewProp'}, - {wrapperProp: 'wrapperProp'}, - ); + const schema: JsonSchemaString = { + type: JsonSchemaType.String, + entityParameters: { + viewType: 'independent', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; const result = getRenderKit({ - config: mockSchemaRendererConfig, + config: schemaRendererConfig, mode: SchemaRendererMode.Overview, schema, }); expect(result).toEqual({ - View: MockIndependentStringOverviewComponent, + View: IndependentStringOverviewComponent, viewProps: {viewProp: 'viewProp'}, - Wrapper: MockStringWrapper, + Wrapper: StringWrapper, wrapperProps: {wrapperProp: 'wrapperProp'}, independent: true, }); }); test('should return render kit with undefined components when viewType/wrapperType are missing', () => { - const schema: JsonSchema = { + const schema: JsonSchemaString = { type: JsonSchemaType.String, entityParameters: { viewProps: {viewProp: 'viewProp'}, @@ -145,7 +707,7 @@ describe('Entity/utils', () => { }; const result = getRenderKit({ - config: mockSchemaRendererConfig, + config: schemaRendererConfig, mode: SchemaRendererMode.Form, schema, }); @@ -160,16 +722,17 @@ describe('Entity/utils', () => { }); test('should return render kit with undefined components when viewType/wrapperType are unknown', () => { - const schema = createMockSchema( - JsonSchemaType.String, - 'unknown', - 'unknown', - {viewProp: 'viewProp'}, - {wrapperProp: 'wrapperProp'}, - ); - + const schema: JsonSchemaString = { + type: JsonSchemaType.String, + entityParameters: { + viewType: 'unknown', + wrapperType: 'unknown', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; const result = getRenderKit({ - config: mockSchemaRendererConfig, + config: schemaRendererConfig, mode: SchemaRendererMode.Form, schema, }); @@ -184,12 +747,12 @@ describe('Entity/utils', () => { }); test('should return render kit with undefined components when entityParameters is empty', () => { - const schema: JsonSchema = { + const schema: JsonSchemaString = { type: JsonSchemaType.String, }; const result = getRenderKit({ - config: mockSchemaRendererConfig, + config: schemaRendererConfig, mode: SchemaRendererMode.Form, schema, }); @@ -204,13 +767,13 @@ describe('Entity/utils', () => { }); test('should return render kit with undefined components when entityParameters is null', () => { - const schema: JsonSchema = { + const schema: JsonSchemaString = { type: JsonSchemaType.String, entityParameters: null as any, }; const result = getRenderKit({ - config: mockSchemaRendererConfig, + config: schemaRendererConfig, mode: SchemaRendererMode.Form, schema, }); @@ -225,14 +788,15 @@ describe('Entity/utils', () => { }); test('should return render kit with undefined components when config is empty', () => { - const schema = createMockSchema( - JsonSchemaType.String, - 'base', - 'default', - {viewProp: 'viewProp'}, - {wrapperProp: 'wrapperProp'}, - ); - + const schema: JsonSchemaString = { + type: JsonSchemaType.String, + entityParameters: { + viewType: 'simple', + wrapperType: 'base', + viewProps: {viewProp: 'viewProp'}, + wrapperProps: {wrapperProp: 'wrapperProp'}, + }, + }; const emptyConfig = { [JsonSchemaType.String]: { views: {}, diff --git a/src/lib/unstable/core/SchemaRendererServiceField/__tests__/utils.test.ts b/src/lib/unstable/core/SchemaRendererServiceField/__tests__/utils.test.ts index aeffdef3..b7633124 100644 --- a/src/lib/unstable/core/SchemaRendererServiceField/__tests__/utils.test.ts +++ b/src/lib/unstable/core/SchemaRendererServiceField/__tests__/utils.test.ts @@ -1,9 +1,10 @@ -import { - createMockArraySchema, - createMockNumberSchema, - createMockObjectSchema, - createMockStringSchema, -} from '../../../__tests__/helpers.test'; +import {JsonSchemaType} from '../../constants'; +import type { + JsonSchemaArray, + JsonSchemaNumber, + JsonSchemaObject, + JsonSchemaString, +} from '../../types'; import { getSchemaByInstancePath, getSchemaBySchemaPath, @@ -12,22 +13,32 @@ import { parseSchemaPath, } from '../utils'; -const nameSchema = createMockStringSchema(); -const streetSchema = createMockStringSchema(); -const citySchema = createMockStringSchema(); -const addressSchema = createMockObjectSchema({street: streetSchema, city: citySchema}); -const typeStringSchema = createMockStringSchema(); -const typeNumberSchema = createMockNumberSchema(); -const tagsSchema = createMockArraySchema([typeStringSchema, typeNumberSchema]); -const labelsSchema = createMockArraySchema(typeStringSchema); -const specialFieldSchema = createMockNumberSchema(); -const testSchema = createMockObjectSchema({ - name: nameSchema, - address: addressSchema, - tags: tagsSchema, - labels: labelsSchema, - 'special/field': specialFieldSchema, -}); +const nameSchema: JsonSchemaString = {type: JsonSchemaType.String}; +const streetSchema: JsonSchemaString = {type: JsonSchemaType.String}; +const citySchema: JsonSchemaString = {type: JsonSchemaType.String}; +const addressSchema: JsonSchemaObject = { + type: JsonSchemaType.Object, + properties: {street: streetSchema, city: citySchema}, +}; +const stringTagSchema: JsonSchemaString = {type: JsonSchemaType.String}; +const numberTagSchema: JsonSchemaNumber = {type: JsonSchemaType.Number}; +const tagsSchema: JsonSchemaArray = { + type: JsonSchemaType.Array, + items: [stringTagSchema, numberTagSchema], +}; +const labelSchema: JsonSchemaString = {type: JsonSchemaType.String}; +const labelsSchema: JsonSchemaArray = {type: JsonSchemaType.Array, items: labelSchema}; +const specialFieldSchema: JsonSchemaNumber = {type: JsonSchemaType.Number}; +const mainSchema: JsonSchemaObject = { + type: JsonSchemaType.Object, + properties: { + name: nameSchema, + address: addressSchema, + tags: tagsSchema, + labels: labelsSchema, + 'special/field': specialFieldSchema, + }, +}; describe('SchemaRendererServiceField/utils', () => { describe('parseSchemaPath', () => { @@ -146,13 +157,13 @@ describe('SchemaRendererServiceField/utils', () => { describe('getSchemaBySchemaPath', () => { it('should return the main schema for empty path array', () => { - const result = getSchemaBySchemaPath('#/', testSchema); + const result = getSchemaBySchemaPath('#/', mainSchema); - expect(result).toBe(testSchema); + expect(result).toBe(mainSchema); }); it('should return the schema for a simple path', () => { - const result = getSchemaBySchemaPath('#/properties/name/minLength', testSchema); + const result = getSchemaBySchemaPath('#/properties/name/minLength', mainSchema); expect(result).toBe(nameSchema); }); @@ -160,26 +171,26 @@ describe('SchemaRendererServiceField/utils', () => { it('should return the schema for a nested path', () => { const result = getSchemaBySchemaPath( '#/properties/address/properties/street/minLength', - testSchema, + mainSchema, ); expect(result).toBe(streetSchema); }); it('should return the schema for a path with array indexes', () => { - const result = getSchemaBySchemaPath('#/properties/tags/items/1/maximum', testSchema); + const result = getSchemaBySchemaPath('#/properties/tags/items/1/maximum', mainSchema); - expect(result).toBe(typeNumberSchema); + expect(result).toBe(numberTagSchema); }); it('should handle array with single schema for items', () => { - const result = getSchemaBySchemaPath('#/properties/labels/items/minLength', testSchema); + const result = getSchemaBySchemaPath('#/properties/labels/items/minLength', mainSchema); - expect(result).toBe(typeStringSchema); + expect(result).toBe(labelSchema); }); it('should return undefined for a path that does not exist', () => { - const result = getSchemaBySchemaPath('#/properties/nonexistent/minLength', testSchema); + const result = getSchemaBySchemaPath('#/properties/nonexistent/minLength', mainSchema); expect(result).toBeUndefined(); }); @@ -187,7 +198,7 @@ describe('SchemaRendererServiceField/utils', () => { it('should handle paths with special characters', () => { const result = getSchemaBySchemaPath( '#/properties/special~1field/maxLength', - testSchema, + mainSchema, ); expect(result).toBe(specialFieldSchema); @@ -196,55 +207,55 @@ describe('SchemaRendererServiceField/utils', () => { describe('getSchemaByInstancePath', () => { it('should return the main schema for empty instance path', () => { - const result = getSchemaByInstancePath('', testSchema); + const result = getSchemaByInstancePath('', mainSchema); - expect(result).toBe(testSchema); + expect(result).toBe(mainSchema); }); it('should return the schema for a simple object property path', () => { - const result = getSchemaByInstancePath('/name', testSchema); + const result = getSchemaByInstancePath('/name', mainSchema); expect(result).toBe(nameSchema); }); it('should return the schema for a nested object property path', () => { - const result = getSchemaByInstancePath('/address/street', testSchema); + const result = getSchemaByInstancePath('/address/street', mainSchema); expect(result).toBe(streetSchema); }); it('should return the schema for an array item path with specific index', () => { - const result = getSchemaByInstancePath('/tags/1', testSchema); + const result = getSchemaByInstancePath('/tags/1', mainSchema); - expect(result).toBe(typeNumberSchema); + expect(result).toBe(numberTagSchema); }); it('should handle array with single schema for items', () => { - const result = getSchemaByInstancePath('/labels/0', testSchema); + const result = getSchemaByInstancePath('/labels/0', mainSchema); - expect(result).toBe(typeStringSchema); + expect(result).toBe(labelSchema); }); it('should return undefined for a path that does not exist', () => { - const result = getSchemaByInstancePath('/nonexistent', testSchema); + const result = getSchemaByInstancePath('/nonexistent', mainSchema); expect(result).toBeUndefined(); }); it('should handle paths with special characters', () => { - const result = getSchemaByInstancePath('/special~1field', testSchema); + const result = getSchemaByInstancePath('/special~1field', mainSchema); expect(result).toBe(specialFieldSchema); }); it('should return undefined for a path that starts valid but ends invalid', () => { - const result = getSchemaByInstancePath('/address/nonexistent', testSchema); + const result = getSchemaByInstancePath('/address/nonexistent', mainSchema); expect(result).toBeUndefined(); }); it('should return undefined when traversing non-object and non-array schemas', () => { - const result = getSchemaByInstancePath('/name/invalid', testSchema); + const result = getSchemaByInstancePath('/name/invalid', mainSchema); expect(result).toBeUndefined(); }); @@ -253,26 +264,31 @@ describe('SchemaRendererServiceField/utils', () => { describe('getValuePaths', () => { it('should return a path for a primitive value', () => { const result = getValuePaths('test'); + expect(result).toEqual([]); }); it('should return a path for a number value', () => { const result = getValuePaths(42); + expect(result).toEqual([]); }); it('should return a path for a boolean value', () => { const result = getValuePaths(true); + expect(result).toEqual([]); }); it('should return a path for null', () => { const result = getValuePaths(null); + expect(result).toEqual([]); }); it('should return a path for undefined', () => { const result = getValuePaths(undefined); + expect(result).toEqual([]); }); diff --git a/src/lib/unstable/core/SchemaRendererServiceField/utils.ts b/src/lib/unstable/core/SchemaRendererServiceField/utils.ts index ef341596..4851804e 100644 --- a/src/lib/unstable/core/SchemaRendererServiceField/utils.ts +++ b/src/lib/unstable/core/SchemaRendererServiceField/utils.ts @@ -191,7 +191,7 @@ export const getValuePaths = (value: unknown, path: string[] = []) => { return result; }; -const processEntityParametersError = ({ +export const processEntityParametersError = ({ allValues, error, headName, @@ -226,7 +226,7 @@ const processEntityParametersError = ({ } }; -const getAjvErrorMessage = ({ +export const getAjvErrorMessage = ({ ajvErrorMessage, errorMessages = EMPTY_OBJECT, instancePath, @@ -256,7 +256,7 @@ const getAjvErrorMessage = ({ ); }; -const processAjvError = ({ +export const processAjvError = ({ error, errorMessages, headName, @@ -275,7 +275,7 @@ const processAjvError = ({ } onError({ - path: [...parseFinalFormPath(headName), ...parseInstancePath(error.instancePath)], + path: [...parseFinalFormPath(headName), ...parseInstancePath(instancePath)], error: getAjvErrorMessage({ ajvErrorMessage: error.message, errorMessages, diff --git a/src/lib/unstable/core/mutators/async-validation/__tests__/async-validation.test.ts b/src/lib/unstable/core/mutators/async-validation/__tests__/async-validation.test.ts index 62c26590..dac80398 100644 --- a/src/lib/unstable/core/mutators/async-validation/__tests__/async-validation.test.ts +++ b/src/lib/unstable/core/mutators/async-validation/__tests__/async-validation.test.ts @@ -1,294 +1,336 @@ -import { - createMockMutableState, - mockServiceFieldName, - mockTools, -} from '../../../../__tests__/helpers.test'; +import type {InternalFieldState, InternalFormState, MutableState, Tools} from 'final-form'; + import {JsonSchemaType} from '../../../constants'; import {setValidationCache, setValidationWaiters} from '../async-validation'; import type {ValidationCache, ValidationWaiter} from '../types'; -const createMockWaiterAndCache = (prefix = '') => { - const fieldName = `fieldName${prefix}`; - const waiter: ValidationWaiter = { - schema: {type: JsonSchemaType.String}, - validator: jest.fn(), - value: `value${prefix}`, - }; - const cache: ValidationCache = {...waiter, result: `result${prefix}`}; - - return {cache, fieldName, waiter}; -}; - describe('async-validation', () => { describe('setValidationWaiters', () => { - it('should not modify state if service field does not exist', () => { - const {fieldName, waiter} = createMockWaiterAndCache(); - const mutableState = createMockMutableState(); + const fieldName = 'fieldName'; + const serviceFieldName = 'serviceFieldName'; + const tools = {} as Tools<{}, {}>; + + let serviceField: InternalFieldState; + let field: InternalFieldState; + let mutableState: MutableState<{}, {}>; + let waiter: ValidationWaiter; + + beforeEach(() => { + serviceField = {data: {}} as InternalFieldState; + field = {} as InternalFieldState; + mutableState = { + fields: {}, + formState: {} as InternalFormState, + fieldSubscribers: {}, + }; + waiter = { + schema: {type: JsonSchemaType.String}, + validator: jest.fn(), + value: 'value', + }; + }); + it('should not modify state if service field does not exist', () => { setValidationWaiters( - [{serviceFieldName: mockServiceFieldName, waiters: {[fieldName]: waiter}}], + [{serviceFieldName, waiters: {[fieldName]: waiter}}], mutableState, - mockTools, + tools, ); expect(mutableState.fields).toEqual({}); }); it('should not modify state if waiters are not provided', () => { - const mutableState = createMockMutableState({ - [mockServiceFieldName]: {data: {}}, - }); + mutableState.fields = {[serviceFieldName]: serviceField}; setValidationWaiters( - [{serviceFieldName: mockServiceFieldName, waiters: undefined as any}], + [{serviceFieldName, waiters: undefined as any}], mutableState, - mockTools, + tools, ); - expect(mutableState.fields[mockServiceFieldName].data).toEqual({}); + expect(mutableState.fields[serviceFieldName].data).toEqual({}); }); it('should add waiter to the validation state', () => { - const {fieldName, waiter} = createMockWaiterAndCache(); - const mutableState = createMockMutableState({ - [mockServiceFieldName]: {data: {}}, - [fieldName]: {}, - }); + mutableState.fields = { + [serviceFieldName]: serviceField, + [fieldName]: field, + }; setValidationWaiters( - [{serviceFieldName: mockServiceFieldName, waiters: {[fieldName]: waiter}}], + [{serviceFieldName, waiters: {[fieldName]: waiter}}], mutableState, - mockTools, + tools, ); - expect(mutableState.fields[mockServiceFieldName].data.waiters).toEqual({ + expect(mutableState.fields[serviceFieldName].data.waiters).toEqual({ [fieldName]: waiter, }); expect(mutableState.fields[fieldName].validating).toBe(true); }); it('should add waiters to the validation state', () => { - const waiterAndCacheKit = createMockWaiterAndCache(); - const waiterAndCacheKit2 = createMockWaiterAndCache('2'); - - const mutableState = createMockMutableState({ - [mockServiceFieldName]: {data: {}}, - [waiterAndCacheKit.fieldName]: {validating: false}, - [waiterAndCacheKit2.fieldName]: {validating: false}, - }); + const anotherFieldName = 'anotherFieldName'; + const anotherField = {} as InternalFieldState; + const anotherWaiter = { + schema: {type: JsonSchemaType.String}, + validator: jest.fn(), + value: 'anotherValue', + }; + + mutableState.fields = { + [serviceFieldName]: serviceField, + [fieldName]: {...field, validating: false}, + [anotherFieldName]: {...anotherField, validating: false}, + }; setValidationWaiters( [ { - serviceFieldName: mockServiceFieldName, + serviceFieldName, waiters: { - [waiterAndCacheKit.fieldName]: waiterAndCacheKit.waiter, - [waiterAndCacheKit2.fieldName]: waiterAndCacheKit2.waiter, + [fieldName]: waiter, + [anotherFieldName]: anotherWaiter, }, }, ], mutableState, - mockTools, + tools, ); - expect(mutableState.fields[mockServiceFieldName].data.waiters).toEqual({ - [waiterAndCacheKit.fieldName]: waiterAndCacheKit.waiter, - [waiterAndCacheKit2.fieldName]: waiterAndCacheKit2.waiter, + expect(mutableState.fields[serviceFieldName].data.waiters).toEqual({ + [fieldName]: waiter, + [anotherFieldName]: anotherWaiter, }); - expect(mutableState.fields[waiterAndCacheKit.fieldName].validating).toBe(true); - expect(mutableState.fields[waiterAndCacheKit2.fieldName].validating).toBe(true); + expect(mutableState.fields[fieldName].validating).toBe(true); + expect(mutableState.fields[anotherFieldName].validating).toBe(true); }); it('should merge waiters with existing waiters', () => { - const waiterAndCacheKit = createMockWaiterAndCache(); - const existingWaiterAndCacheKit = createMockWaiterAndCache('existing'); - const mutableState = createMockMutableState({ - [mockServiceFieldName]: { - data: { - waiters: { - [existingWaiterAndCacheKit.fieldName]: existingWaiterAndCacheKit.waiter, - }, - }, + const anotherFieldName = 'anotherFieldName'; + const anotherWaiter = { + schema: {type: JsonSchemaType.String}, + validator: jest.fn(), + value: 'anotherValue', + }; + + serviceField.data = { + waiters: { + [anotherFieldName]: anotherWaiter, }, - [waiterAndCacheKit.fieldName]: {}, - }); + }; + mutableState.fields = { + [serviceFieldName]: serviceField, + [fieldName]: field, + }; setValidationWaiters( - [ - { - serviceFieldName: mockServiceFieldName, - waiters: {[waiterAndCacheKit.fieldName]: waiterAndCacheKit.waiter}, - }, - ], + [{serviceFieldName, waiters: {[fieldName]: waiter}}], mutableState, - mockTools, + tools, ); - expect(mutableState.fields[mockServiceFieldName].data.waiters).toEqual({ - [existingWaiterAndCacheKit.fieldName]: existingWaiterAndCacheKit.waiter, - [waiterAndCacheKit.fieldName]: waiterAndCacheKit.waiter, + expect(mutableState.fields[serviceFieldName].data.waiters).toEqual({ + [anotherFieldName]: anotherWaiter, + [fieldName]: waiter, }); - expect(mutableState.fields[waiterAndCacheKit.fieldName].validating).toBe(true); + expect(mutableState.fields[fieldName].validating).toBe(true); }); }); describe('setValidationCache', () => { - it('should not modify state if field does not exist', () => { - const {cache, fieldName} = createMockWaiterAndCache(); - const mutableState = createMockMutableState(); + const fieldName = 'fieldName'; + const serviceFieldName = 'serviceFieldName'; + const tools = {} as Tools<{}, {}>; + + let serviceField: InternalFieldState; + let field: InternalFieldState; + let mutableState: MutableState<{}, {}>; + let waiter: ValidationWaiter; + let cache: ValidationCache; + + beforeEach(() => { + serviceField = { + data: {}, + } as InternalFieldState; + field = {} as InternalFieldState; + mutableState = { + fields: {}, + formState: {} as InternalFormState, + fieldSubscribers: {}, + }; + waiter = { + schema: {type: JsonSchemaType.String}, + validator: jest.fn(), + value: 'value', + }; + cache = { + ...waiter, + result: 'result', + }; + }); + it('should not modify state if field does not exist', () => { setValidationCache( - [{serviceFieldName: mockServiceFieldName, cache: {[fieldName]: cache}}], + [{serviceFieldName, cache: {[fieldName]: cache}}], mutableState, - mockTools, + tools, ); expect(mutableState.fields).toEqual({}); }); it('should not modify state if cache is not provided', () => { - const mutableState = createMockMutableState({ - [mockServiceFieldName]: {data: {}}, - }); + mutableState.fields = {[serviceFieldName]: serviceField}; - setValidationCache( - [{serviceFieldName: mockServiceFieldName, cache: undefined as any}], - mutableState, - mockTools, - ); + setValidationCache([{serviceFieldName, cache: undefined as any}], mutableState, tools); - expect(mutableState.fields[mockServiceFieldName].data).toEqual({}); + expect(mutableState.fields[serviceFieldName].data).toEqual({}); }); it('should add cache to the validation state', () => { - const {cache, fieldName} = createMockWaiterAndCache(); - const mutableState = createMockMutableState({ - [mockServiceFieldName]: {data: {}}, - [fieldName]: {}, - }); + mutableState.fields = { + [serviceFieldName]: serviceField, + [fieldName]: field, + }; setValidationCache( - [{serviceFieldName: mockServiceFieldName, cache: {[fieldName]: cache}}], + [{serviceFieldName, cache: {[fieldName]: cache}}], mutableState, - mockTools, + tools, ); - expect(mutableState.fields[mockServiceFieldName].data.cache).toEqual({ + expect(mutableState.fields[serviceFieldName].data.cache).toEqual({ [fieldName]: [cache], }); }); it('should append to existing cache', () => { - const fieldName = 'fieldName'; - const existingWaiterAndCacheKit = createMockWaiterAndCache('existing'); - const waiterAndCacheKit = createMockWaiterAndCache(); - const mutableState = createMockMutableState({ - [mockServiceFieldName]: { - data: { - cache: { - [fieldName]: [existingWaiterAndCacheKit.cache], - }, - }, + const existingCache: ValidationCache = { + schema: {type: JsonSchemaType.String}, + validator: jest.fn(), + value: 'existingValue', + result: 'existingResult', + }; + + serviceField.data = { + cache: { + [fieldName]: [existingCache], }, - [fieldName]: {}, - }); + }; + mutableState.fields = { + [serviceFieldName]: serviceField, + [fieldName]: field, + }; setValidationCache( - [ - { - serviceFieldName: mockServiceFieldName, - cache: {[fieldName]: waiterAndCacheKit.cache}, - }, - ], + [{serviceFieldName, cache: {[fieldName]: cache}}], mutableState, - mockTools, + tools, ); - expect(mutableState.fields[mockServiceFieldName].data.cache).toEqual({ - [fieldName]: [existingWaiterAndCacheKit.cache, waiterAndCacheKit.cache], + expect(mutableState.fields[serviceFieldName].data.cache).toEqual({ + [fieldName]: [existingCache, cache], }); }); it('should clear waiter and set validating to false when waiter matches cache', () => { - const fieldName = 'fieldName'; - const {cache, waiter} = createMockWaiterAndCache(); - const mutableState = createMockMutableState({ - [mockServiceFieldName]: {data: {waiters: {[fieldName]: waiter}}}, - [fieldName]: {validating: true}, - }); + serviceField.data = { + waiters: { + [fieldName]: waiter, + }, + }; + mutableState.fields = { + [serviceFieldName]: serviceField, + [fieldName]: {...field, validating: true}, + }; setValidationCache( - [{serviceFieldName: mockServiceFieldName, cache: {[fieldName]: cache}}], + [{serviceFieldName, cache: {[fieldName]: cache}}], mutableState, - mockTools, + tools, ); - expect(mutableState.fields[mockServiceFieldName].data.waiters).toEqual({}); + expect(mutableState.fields[serviceFieldName].data.waiters).toEqual({}); expect(mutableState.fields[fieldName].validating).toBe(false); }); it('should not clear waiter when waiter does not match cache', () => { - const fieldName = 'fieldName'; - const {waiter} = createMockWaiterAndCache('1'); - const {cache} = createMockWaiterAndCache('2'); - - const mutableState = createMockMutableState({ - [mockServiceFieldName]: {data: {waiters: {[fieldName]: waiter}}}, - [fieldName]: {validating: true}, - }); + const anotherCache: ValidationCache = { + schema: {type: JsonSchemaType.String}, + validator: jest.fn(), + value: 'anotherValue', + result: 'anotherResult', + }; + + serviceField.data = { + waiters: { + [fieldName]: waiter, + }, + }; + mutableState.fields = { + [serviceFieldName]: serviceField, + [fieldName]: {...field, validating: true}, + }; setValidationCache( - [{serviceFieldName: mockServiceFieldName, cache: {[fieldName]: cache}}], + [{serviceFieldName, cache: {[fieldName]: anotherCache}}], mutableState, - mockTools, + tools, ); - expect(mutableState.fields[mockServiceFieldName].data.waiters).toEqual({ + expect(mutableState.fields[serviceFieldName].data.waiters).toEqual({ [fieldName]: waiter, }); expect(mutableState.fields[fieldName].validating).toBe(true); }); it('should handle multiple cache entries', () => { - const fieldName = 'fieldName'; - const waiterAndCacheKit = createMockWaiterAndCache(fieldName); - - const fieldName2 = 'fieldName2'; - const waiterAndCacheKit2 = createMockWaiterAndCache(fieldName2); - - const mutableState = createMockMutableState({ - [mockServiceFieldName]: { - data: { - waiters: { - [fieldName]: waiterAndCacheKit.waiter, - [fieldName2]: waiterAndCacheKit2.waiter, - }, - }, + const anotherFieldName = 'anotherFieldName'; + const anotherField = {} as InternalFieldState; + const anotherWaiter = { + schema: {type: JsonSchemaType.String}, + validator: jest.fn(), + value: 'anotherValue', + }; + const anotherCache = { + ...anotherWaiter, + result: 'anotherResult', + }; + + serviceField.data = { + waiters: { + [fieldName]: waiter, + [anotherFieldName]: anotherWaiter, }, - [fieldName]: {validating: true}, - [fieldName2]: {validating: true}, - }); + }; + mutableState.fields = { + [serviceFieldName]: serviceField, + [fieldName]: {...field, validating: true}, + [anotherFieldName]: {...anotherField, validating: true}, + }; setValidationCache( [ { - serviceFieldName: mockServiceFieldName, + serviceFieldName, cache: { - [fieldName]: waiterAndCacheKit.cache, - [fieldName2]: waiterAndCacheKit2.cache, + [fieldName]: cache, + [anotherFieldName]: anotherCache, }, }, ], mutableState, - mockTools, + tools, ); - expect(mutableState.fields[mockServiceFieldName].data.waiters).toEqual({}); + expect(mutableState.fields[serviceFieldName].data.waiters).toEqual({}); expect(mutableState.fields[fieldName].validating).toBe(false); - expect(mutableState.fields[fieldName2].validating).toBe(false); - expect(mutableState.fields[mockServiceFieldName].data.cache).toEqual({ - [fieldName]: [waiterAndCacheKit.cache], - [fieldName2]: [waiterAndCacheKit2.cache], + expect(mutableState.fields[anotherFieldName].validating).toBe(false); + expect(mutableState.fields[serviceFieldName].data.cache).toEqual({ + [fieldName]: [cache], + [anotherFieldName]: [anotherCache], }); }); }); diff --git a/src/lib/unstable/core/mutators/set-errors/__tests__/set-errors.test.ts b/src/lib/unstable/core/mutators/set-errors/__tests__/set-errors.test.ts index 4de925f8..56474985 100644 --- a/src/lib/unstable/core/mutators/set-errors/__tests__/set-errors.test.ts +++ b/src/lib/unstable/core/mutators/set-errors/__tests__/set-errors.test.ts @@ -1,324 +1,307 @@ +import type {InternalFieldState, InternalFormState, MutableState, Tools} from 'final-form'; import omit from 'lodash/omit'; -import { - createMockMutableState, - mockServiceFieldName, - mockTools, -} from '../../../../__tests__/helpers.test'; import {removeErrors, setErrors} from '../set-errors'; import type {ErrorsState} from '../types'; -const createMockErrorsKit = (errorPrefix = '', fieldNamePrefix = '') => { - const fieldName = `fieldName${fieldNamePrefix}`; - - const errorsState: ErrorsState = { - priorityErrors: { - [fieldName]: `priorityError${errorPrefix}`, - }, - regularErrors: { - [fieldName]: `regularError${errorPrefix}`, - }, - }; - - return {errorsState, fieldName}; -}; - describe('set-errors', () => { describe('setErrors', () => { - it('should not modify state if service field does not exist', () => { - const errorsKit = createMockErrorsKit(); - const mutableState = createMockMutableState(); + const fieldName = 'fieldName'; + const serviceFieldName = 'serviceFieldName'; + const priorityErrors: Exclude = { + [fieldName]: 'priorityError', + }; + const regularErrors: Exclude = { + [fieldName]: 'regularErrors', + }; + const tools = {} as Tools<{}, {}>; + + let serviceField: InternalFieldState; + let mutableState: MutableState<{}, {}>; + + beforeEach(() => { + serviceField = {data: {}} as InternalFieldState; + mutableState = { + fields: {}, + formState: {} as InternalFormState, + fieldSubscribers: {}, + }; + }); - setErrors( - [{serviceFieldName: mockServiceFieldName, ...errorsKit.errorsState}], - mutableState, - mockTools, - ); + it('should not modify state if service field does not exist', () => { + setErrors([{serviceFieldName, priorityErrors, regularErrors}], mutableState, tools); expect(mutableState.fields).toEqual({}); }); it('should not modify state if errors are not provided', () => { - const mutableState = createMockMutableState({[mockServiceFieldName]: {data: {}}}); + mutableState.fields = {[serviceFieldName]: serviceField}; - setErrors([{serviceFieldName: mockServiceFieldName}], mutableState, mockTools); + setErrors( + [{serviceFieldName, priorityErrors: undefined, regularErrors: undefined}], + mutableState, + tools, + ); - expect(mutableState.fields[mockServiceFieldName].data).toEqual({}); + expect(mutableState.fields[serviceFieldName].data).toEqual({}); }); it('should add priority errors to the errors state', () => { - const existingErrorsKit = createMockErrorsKit(); - const errorsKit = createMockErrorsKit('2', '2'); - const mutableState = createMockMutableState({ - [mockServiceFieldName]: {data: existingErrorsKit.errorsState}, - }); + const anotherFieldName = 'anotherFieldName'; + const existingErrorsState = { + priorityErrors: {[anotherFieldName]: 'anotherPriorityError'}, + regularErrors: {[anotherFieldName]: 'anotherRegularError'}, + }; - setErrors( - [ - { - serviceFieldName: mockServiceFieldName, - priorityErrors: errorsKit.errorsState.priorityErrors, - }, - ], - mutableState, - mockTools, - ); + serviceField.data = existingErrorsState; + mutableState.fields = { + [serviceFieldName]: serviceField, + }; - expect(mutableState.fields[mockServiceFieldName].data.priorityErrors).toEqual({ - ...existingErrorsKit.errorsState.priorityErrors, - ...errorsKit.errorsState.priorityErrors, + setErrors([{serviceFieldName, priorityErrors}], mutableState, tools); + + expect(mutableState.fields[serviceFieldName].data.priorityErrors).toEqual({ + ...existingErrorsState.priorityErrors, + ...priorityErrors, }); - expect(mutableState.fields[mockServiceFieldName].data.regularErrors).toEqual( - existingErrorsKit.errorsState.regularErrors, + expect(mutableState.fields[serviceFieldName].data.regularErrors).toEqual( + existingErrorsState.regularErrors, ); }); it('should add regular errors to the errors state', () => { - const existingErrorsKit = createMockErrorsKit(); - const errorsKit = createMockErrorsKit('2', '2'); - const mutableState = createMockMutableState({ - [mockServiceFieldName]: {data: existingErrorsKit.errorsState}, - }); + const anotherFieldName = 'anotherFieldName'; + const existingErrorsState = { + priorityErrors: {[anotherFieldName]: 'anotherPriorityError'}, + regularErrors: {[anotherFieldName]: 'anotherRegularError'}, + }; - setErrors( - [ - { - serviceFieldName: mockServiceFieldName, - regularErrors: errorsKit.errorsState.regularErrors, - }, - ], - mutableState, - mockTools, - ); + serviceField.data = existingErrorsState; + mutableState.fields = { + [serviceFieldName]: serviceField, + }; - expect(mutableState.fields[mockServiceFieldName].data.priorityErrors).toEqual( - existingErrorsKit.errorsState.priorityErrors, + setErrors([{serviceFieldName, regularErrors}], mutableState, tools); + + expect(mutableState.fields[serviceFieldName].data.priorityErrors).toEqual( + existingErrorsState.priorityErrors, ); - expect(mutableState.fields[mockServiceFieldName].data.regularErrors).toEqual({ - ...existingErrorsKit.errorsState.regularErrors, - ...errorsKit.errorsState.regularErrors, + expect(mutableState.fields[serviceFieldName].data.regularErrors).toEqual({ + ...existingErrorsState.regularErrors, + ...regularErrors, }); }); it('should add both priority and regular errors to the errors state', () => { - const existingErrorsKit = createMockErrorsKit(); - const errorsKit = createMockErrorsKit('2', '2'); - const mutableState = createMockMutableState({ - [mockServiceFieldName]: {data: existingErrorsKit.errorsState}, - }); + const anotherFieldName = 'anotherFieldName'; + const existingErrorsState = { + priorityErrors: {[anotherFieldName]: 'anotherPriorityError'}, + regularErrors: {[anotherFieldName]: 'anotherRegularError'}, + }; - setErrors( - [ - { - serviceFieldName: mockServiceFieldName, - ...errorsKit.errorsState, - }, - ], - mutableState, - mockTools, - ); + serviceField.data = existingErrorsState; + mutableState.fields = { + [serviceFieldName]: serviceField, + }; - expect(mutableState.fields[mockServiceFieldName].data.priorityErrors).toEqual({ - ...existingErrorsKit.errorsState.priorityErrors, - ...errorsKit.errorsState.priorityErrors, + setErrors([{serviceFieldName, priorityErrors, regularErrors}], mutableState, tools); + + expect(mutableState.fields[serviceFieldName].data.priorityErrors).toEqual({ + ...existingErrorsState.priorityErrors, + ...priorityErrors, }); - expect(mutableState.fields[mockServiceFieldName].data.regularErrors).toEqual({ - ...existingErrorsKit.errorsState.regularErrors, - ...errorsKit.errorsState.regularErrors, + expect(mutableState.fields[serviceFieldName].data.regularErrors).toEqual({ + ...existingErrorsState.regularErrors, + ...regularErrors, }); }); it('should override existing errors with the same field name', () => { - const existingErrorsKit = createMockErrorsKit(); - const errorsKit = createMockErrorsKit('2'); - const mutableState = createMockMutableState({ - [mockServiceFieldName]: {data: existingErrorsKit.errorsState}, - }); + const existingErrorsState = { + priorityErrors: {[fieldName]: 'anotherPriorityError'}, + regularErrors: {[fieldName]: 'anotherRegularError'}, + }; - setErrors( - [ - { - serviceFieldName: mockServiceFieldName, - ...errorsKit.errorsState, - }, - ], - mutableState, - mockTools, - ); + serviceField.data = existingErrorsState; + mutableState.fields = { + [serviceFieldName]: serviceField, + }; - expect(mutableState.fields[mockServiceFieldName].data.priorityErrors).toEqual({ - ...errorsKit.errorsState.priorityErrors, - }); - expect(mutableState.fields[mockServiceFieldName].data.regularErrors).toEqual({ - ...errorsKit.errorsState.regularErrors, - }); + setErrors([{serviceFieldName, priorityErrors, regularErrors}], mutableState, tools); + + expect(mutableState.fields[serviceFieldName].data.priorityErrors).toEqual( + priorityErrors, + ); + expect(mutableState.fields[serviceFieldName].data.regularErrors).toEqual(regularErrors); }); }); describe('removeErrors', () => { + const fieldName = 'fieldName'; + const serviceFieldName = 'serviceFieldName'; + const tools = {} as Tools<{}, {}>; + + let serviceField: InternalFieldState; + let mutableState: MutableState<{}, {}>; + let errorsState: ErrorsState; + + beforeEach(() => { + serviceField = {data: {}} as InternalFieldState; + mutableState = { + fields: {}, + formState: {} as InternalFormState, + fieldSubscribers: {}, + }; + errorsState = { + priorityErrors: {[fieldName]: 'priorityError'}, + regularErrors: {[fieldName]: 'regularError'}, + }; + }); + it('should not modify state if service field does not exist', () => { - const mutableState = createMockMutableState(); + const anotherFieldName = 'anotherFieldName'; removeErrors( - [ - { - serviceFieldName: mockServiceFieldName, - removeFunctionOrNames: ['fieldName', 'fieldName2'], - }, - ], + [{serviceFieldName, removeFunctionOrNames: [fieldName, anotherFieldName]}], mutableState, - mockTools, + tools, ); expect(mutableState.fields).toEqual({}); }); it('should not modify state if removeFunctionOrNames is not provided', () => { - const existingErrorsKit = createMockErrorsKit(); - const mutableState = createMockMutableState({ - [mockServiceFieldName]: {data: existingErrorsKit.errorsState}, - }); + serviceField.data = errorsState; + mutableState.fields = { + [serviceFieldName]: serviceField, + }; removeErrors( - [{serviceFieldName: mockServiceFieldName}] as any, + [{serviceFieldName, removeFunctionOrNames: undefined as any}], mutableState, - mockTools, + tools, ); - expect(mutableState.fields[mockServiceFieldName].data).toEqual( - existingErrorsKit.errorsState, - ); + expect(mutableState.fields[serviceFieldName].data).toEqual(errorsState); }); - it('should not modify state if field names array is empty ', () => { - const existingErrorsKit = createMockErrorsKit(); - const mutableState = createMockMutableState({ - [mockServiceFieldName]: {data: existingErrorsKit.errorsState}, - }); + it('should not modify state if field names array is empty', () => { + serviceField.data = errorsState; + mutableState.fields = { + [serviceFieldName]: serviceField, + }; - removeErrors( - [{serviceFieldName: mockServiceFieldName, removeFunctionOrNames: []}], - mutableState, - mockTools, - ); + removeErrors([{serviceFieldName, removeFunctionOrNames: []}], mutableState, tools); - expect(mutableState.fields[mockServiceFieldName].data).toEqual( - existingErrorsKit.errorsState, - ); + expect(mutableState.fields[serviceFieldName].data).toEqual(errorsState); }); it('should remove errors by field names', () => { - const errorsKit1 = createMockErrorsKit('1', '1'); - const errorsKit2 = createMockErrorsKit('2', '2'); - const errorsKit3 = createMockErrorsKit('3', '3'); - - const mutableState = createMockMutableState({ - [mockServiceFieldName]: { - data: { - priorityErrors: { - ...errorsKit1.errorsState.priorityErrors, - ...errorsKit2.errorsState.priorityErrors, - ...errorsKit3.errorsState.priorityErrors, - }, - regularErrors: { - ...errorsKit1.errorsState.regularErrors, - ...errorsKit2.errorsState.regularErrors, - ...errorsKit3.errorsState.regularErrors, - }, - }, + const anotherFieldName1 = 'anotherFieldName1'; + const anotherPriorityErrors1 = {[anotherFieldName1]: 'anotherPriorityError1'}; + const anotherRegularErrors1 = {[anotherFieldName1]: 'anotherRegularError1'}; + const anotherFieldName2 = 'anotherFieldName2'; + const anotherPriorityErrors2 = {[anotherFieldName2]: 'anotherPriorityError2'}; + const anotherRegularErrors2 = {[anotherFieldName2]: 'anotherRegularError2'}; + const anotherFieldName3 = 'anotherFieldName3'; + const anotherPriorityErrors3 = {[anotherFieldName3]: 'anotherPriorityError3'}; + const anotherRegularErrors3 = {[anotherFieldName3]: 'anotherRegularError3'}; + + serviceField.data = { + priorityErrors: { + ...anotherPriorityErrors1, + ...anotherPriorityErrors2, + ...anotherPriorityErrors3, }, - }); + regularErrors: { + ...anotherRegularErrors1, + ...anotherRegularErrors2, + ...anotherRegularErrors3, + }, + }; + mutableState.fields = { + [serviceFieldName]: serviceField, + }; removeErrors( - [ - { - serviceFieldName: mockServiceFieldName, - removeFunctionOrNames: [errorsKit1.fieldName, errorsKit2.fieldName], - }, - ], + [{serviceFieldName, removeFunctionOrNames: [anotherFieldName1, anotherFieldName2]}], mutableState, - mockTools, + tools, ); - expect(mutableState.fields[mockServiceFieldName].data.priorityErrors).toEqual({ - ...errorsKit3.errorsState.priorityErrors, - }); - expect(mutableState.fields[mockServiceFieldName].data.regularErrors).toEqual({ - ...errorsKit3.errorsState.regularErrors, - }); + expect(mutableState.fields[serviceFieldName].data.priorityErrors).toEqual( + anotherPriorityErrors3, + ); + expect(mutableState.fields[serviceFieldName].data.regularErrors).toEqual( + anotherRegularErrors3, + ); }); it('should remove errors using a custom function', () => { - const errorsKit1 = createMockErrorsKit('1', '1'); - const errorsKit2 = createMockErrorsKit('2', '2'); - const errorsKit3 = createMockErrorsKit('3', '3'); - - const mutableState = createMockMutableState({ - [mockServiceFieldName]: { - data: { - priorityErrors: { - ...errorsKit1.errorsState.priorityErrors, - ...errorsKit2.errorsState.priorityErrors, - ...errorsKit3.errorsState.priorityErrors, - }, - regularErrors: { - ...errorsKit1.errorsState.regularErrors, - ...errorsKit2.errorsState.regularErrors, - ...errorsKit3.errorsState.regularErrors, - }, - }, + const anotherFieldName1 = 'anotherFieldName1'; + const anotherPriorityErrors1 = {[anotherFieldName1]: 'anotherPriorityError1'}; + const anotherRegularErrors1 = {[anotherFieldName1]: 'anotherRegularError1'}; + const anotherFieldName2 = 'anotherFieldName2'; + const anotherPriorityErrors2 = {[anotherFieldName2]: 'anotherPriorityError2'}; + const anotherRegularErrors2 = {[anotherFieldName2]: 'anotherRegularError2'}; + const anotherFieldName3 = 'anotherFieldName3'; + const anotherPriorityErrors3 = {[anotherFieldName3]: 'anotherPriorityError3'}; + const anotherRegularErrors3 = {[anotherFieldName3]: 'anotherRegularError3'}; + + serviceField.data = { + priorityErrors: { + ...anotherPriorityErrors1, + ...anotherPriorityErrors2, + ...anotherPriorityErrors3, }, - }); + regularErrors: { + ...anotherRegularErrors1, + ...anotherRegularErrors2, + ...anotherRegularErrors3, + }, + }; + mutableState.fields = { + [serviceFieldName]: serviceField, + }; const customRemoveFunction = (state: ErrorsState): ErrorsState => { return { - priorityErrors: omit(state.priorityErrors, [errorsKit1.fieldName]), - regularErrors: omit(state.regularErrors, [errorsKit2.fieldName]), + priorityErrors: omit(state.priorityErrors, [anotherFieldName1]), + regularErrors: omit(state.regularErrors, [anotherFieldName2]), }; }; removeErrors( - [ - { - serviceFieldName: mockServiceFieldName, - removeFunctionOrNames: customRemoveFunction, - }, - ], + [{serviceFieldName, removeFunctionOrNames: customRemoveFunction}], mutableState, - mockTools, + tools, ); - expect(mutableState.fields[mockServiceFieldName].data.priorityErrors).toEqual({ - ...errorsKit2.errorsState.priorityErrors, - ...errorsKit3.errorsState.priorityErrors, + expect(mutableState.fields[serviceFieldName].data.priorityErrors).toEqual({ + ...anotherPriorityErrors2, + ...anotherPriorityErrors3, }); - expect(mutableState.fields[mockServiceFieldName].data.regularErrors).toEqual({ - ...errorsKit1.errorsState.regularErrors, - ...errorsKit3.errorsState.regularErrors, + expect(mutableState.fields[serviceFieldName].data.regularErrors).toEqual({ + ...anotherRegularErrors1, + ...anotherRegularErrors3, }); }); it('should handle non-existent field names', () => { - const nonExistentFieldName = 'nonExistentFieldName'; - const existingErrorsKit = createMockErrorsKit(); - const mutableState = createMockMutableState({ - [mockServiceFieldName]: {data: existingErrorsKit.errorsState}, - }); + const anotherFieldName = 'anotherFieldName'; + + serviceField.data = errorsState; + mutableState.fields = { + [serviceFieldName]: serviceField, + }; removeErrors( - [ - { - serviceFieldName: mockServiceFieldName, - removeFunctionOrNames: [nonExistentFieldName], - }, - ], + [{serviceFieldName, removeFunctionOrNames: [anotherFieldName]}], mutableState, - mockTools, + tools, ); - expect(mutableState.fields[mockServiceFieldName].data).toEqual( - existingErrorsKit.errorsState, - ); + expect(mutableState.fields[serviceFieldName].data).toEqual(errorsState); }); }); }); diff --git a/src/lib/unstable/core/types/schema.ts b/src/lib/unstable/core/types/schema.ts index 7afcfed7..b1ff0725 100644 --- a/src/lib/unstable/core/types/schema.ts +++ b/src/lib/unstable/core/types/schema.ts @@ -20,9 +20,9 @@ interface EntityParameters< enumDescription?: { [key: string]: string; }; - errorMessages?: ErrorMessages & { - dependencies?: Record; - required?: Record; + errorMessages?: Omit & { + dependencies?: string | Record; + required?: string | Record; }; // todo validatorType?: string; viewType?: ViewKey; diff --git a/src/lib/unstable/core/utils/__tests__/common.test.ts b/src/lib/unstable/core/utils/__tests__/common.test.ts index 21ce1b1e..12e2acd3 100644 --- a/src/lib/unstable/core/utils/__tests__/common.test.ts +++ b/src/lib/unstable/core/utils/__tests__/common.test.ts @@ -1,13 +1,46 @@ -import { - createMockArraySchema, - createMockBooleanSchema, - createMockNumberSchema, - createMockObjectSchema, - createMockStringSchema, -} from '../../../__tests__/helpers.test'; -import type {JsonSchema} from '../../types'; +import {JsonSchemaType} from '../../constants'; +import type { + JsonSchema, + JsonSchemaArray, + JsonSchemaNumber, + JsonSchemaObject, + JsonSchemaString, +} from '../../types'; import {getSchemaByFinalFormPath, parseFinalFormPath} from '../common'; +const nameSchema: JsonSchemaString = {type: JsonSchemaType.String}; +const surnameSchema: JsonSchemaString = {type: JsonSchemaType.String}; +const hobbySchema: JsonSchemaString = {type: JsonSchemaType.String}; +const hobbiesSchema: JsonSchemaArray = {type: JsonSchemaType.Array, items: hobbySchema}; +const personalDataSchema: JsonSchemaObject = { + type: JsonSchemaType.Object, + properties: {name: nameSchema, surname: surnameSchema, hobbies: hobbiesSchema}, +}; +const aboutSchema: JsonSchemaString = {type: JsonSchemaType.String}; +const friendsSchema: JsonSchemaArray = { + type: JsonSchemaType.Array, + items: personalDataSchema, +}; +const stringTagSchema: JsonSchemaString = {type: JsonSchemaType.String}; +const numberTagSchema: JsonSchemaNumber = {type: JsonSchemaType.Number}; +const tagsSchema: JsonSchemaArray = { + type: JsonSchemaType.Array, + items: [stringTagSchema, numberTagSchema], +}; +const labelSchema: JsonSchemaString = {type: JsonSchemaType.String}; +const labelsSchema: JsonSchemaArray = {type: JsonSchemaType.Array, items: labelSchema}; + +const mainSchema: JsonSchemaObject = { + type: JsonSchemaType.Object, + properties: { + personalData: personalDataSchema, + about: aboutSchema, + friends: friendsSchema, + tags: tagsSchema, + labels: labelsSchema, + }, +}; + describe('core/utils/common', () => { describe('parseFinalFormPath', () => { it('should parse a simple path', () => { @@ -49,9 +82,8 @@ describe('core/utils/common', () => { describe('getSchemaByFinalFormPath', () => { it('should return the main schema when path is empty', () => { - const mainSchema = createMockObjectSchema({foo: createMockStringSchema()}); - const path = ''; const headPath = ''; + const path = ''; const result = getSchemaByFinalFormPath(path, headPath, mainSchema); @@ -59,75 +91,53 @@ describe('core/utils/common', () => { }); it('should navigate through object properties', () => { - const stringSchema = createMockStringSchema(); - const objectSchema = createMockObjectSchema({bar: stringSchema}); - const mainSchema = createMockObjectSchema({foo: objectSchema}); - const path = 'foo.bar'; const headPath = ''; + const path = 'personalData.name'; const result = getSchemaByFinalFormPath(path, headPath, mainSchema); - expect(result).toBe(stringSchema); + expect(result).toBe(nameSchema); }); it('should navigate through array items (when items is a single schema)', () => { - const stringSchema = createMockStringSchema(); - const arraySchema = createMockArraySchema(stringSchema); - const mainSchema = createMockObjectSchema({foo: arraySchema}); - const path = 'foo[0]'; const headPath = ''; + const path = 'labels[0]'; const result = getSchemaByFinalFormPath(path, headPath, mainSchema); - expect(result).toBe(stringSchema); + expect(result).toBe(labelSchema); }); it('should navigate through array items (when items is an array of schemas)', () => { - const stringSchema = createMockStringSchema(); - const numberSchema = createMockNumberSchema(); - const booleanSchema = createMockBooleanSchema(); - const arraySchema = createMockArraySchema([stringSchema, numberSchema, booleanSchema]); - const mainSchema = createMockObjectSchema({foo: arraySchema}); - const path = 'foo[1]'; const headPath = ''; + const path = 'tags[1]'; const result = getSchemaByFinalFormPath(path, headPath, mainSchema); - expect(result).toBe(numberSchema); + expect(result).toBe(numberTagSchema); }); it('should handle complex nested paths', () => { - const booleanSchema = createMockBooleanSchema(); - const objectSchema = createMockObjectSchema({qux: booleanSchema}); - const arraySchema = createMockArraySchema(objectSchema); - const objectSchema2 = createMockObjectSchema({baz: arraySchema}); - const arraySchema2 = createMockArraySchema(objectSchema2); - const mainSchema = createMockObjectSchema({foo: arraySchema2}); - const path = 'foo[0].baz[0].qux'; const headPath = ''; + const path = 'friends[0].hobbies[0]'; const result = getSchemaByFinalFormPath(path, headPath, mainSchema); - expect(result).toBe(booleanSchema); + expect(result).toBe(hobbySchema); }); it('should handle finalFormHeadPath correctly', () => { - const stringSchema = createMockStringSchema(); - const objectSchema = createMockObjectSchema({bar: stringSchema}); - const mainSchema = createMockObjectSchema({foo: objectSchema}); - const path = 'baz.foo.bar'; - const headPath = 'baz'; + const headPath = 'headPath'; + const path = `${headPath}.personalData.name`; const result = getSchemaByFinalFormPath(path, headPath, mainSchema); - expect(result).toBe(stringSchema); + expect(result).toBe(nameSchema); }); it('should return undefined for invalid path', () => { - const stringSchema = createMockStringSchema(); - const mainSchema = createMockObjectSchema({foo: stringSchema}); - const path = 'bar.baz'; const headPath = 'foo'; + const path = 'personalData.name'; const result = getSchemaByFinalFormPath(path, headPath, mainSchema); @@ -135,10 +145,8 @@ describe('core/utils/common', () => { }); it('should handle array path with string schema', () => { - const stringSchema = createMockStringSchema(); - const mainSchema = createMockObjectSchema({foo: stringSchema}); - const path = 'foo[0]'; const headPath = ''; + const path = 'personalData[0]'; const result = getSchemaByFinalFormPath(path, headPath, mainSchema); @@ -146,20 +154,17 @@ describe('core/utils/common', () => { }); it('should accept path as array of segments', () => { - const stringSchema = createMockStringSchema(); - const objectSchema = createMockObjectSchema({bar: stringSchema}); - const mainSchema = createMockObjectSchema({foo: objectSchema}); - const path = ['foo', 'bar']; const headPath = ''; + const path = ['personalData', 'name']; const result = getSchemaByFinalFormPath(path, headPath, mainSchema); - expect(result).toBe(stringSchema); + expect(result).toBe(nameSchema); }); it('should handle undefined schema gracefully', () => { - const path = 'foo.bar'; const headPath = ''; + const path = 'personalData.name'; const result = getSchemaByFinalFormPath( path, @@ -171,9 +176,11 @@ describe('core/utils/common', () => { }); it('should handle schema without type gracefully', () => { + const headPath = ''; + const path = 'personalData.name'; const invalidSchema = {} as JsonSchema; - const result = getSchemaByFinalFormPath('foo', '', invalidSchema); + const result = getSchemaByFinalFormPath(path, headPath, invalidSchema); expect(result).toBeUndefined(); }); diff --git a/src/stories/Unstable.stories.tsx b/src/stories/Unstable.stories.tsx index f08a2340..6811cfa3 100644 --- a/src/stories/Unstable.stories.tsx +++ b/src/stories/Unstable.stories.tsx @@ -400,8 +400,6 @@ const objectDependencies: JsonSchemaObject = { }, errorMessages: { // dependencies: 'objectDependencies', - // todo - // @ts-expect-error dependencies: { stringEnum: 'objectDependencies/stringEnum', numberMinimum: 'objectDependencies/numberMinimum',