diff --git a/packages/utils/test/schema/getDefaultFormStateTest.ts b/packages/utils/test/schema/getDefaultFormStateTest.ts index 6a75006d92..9f7c4a9f76 100644 --- a/packages/utils/test/schema/getDefaultFormStateTest.ts +++ b/packages/utils/test/schema/getDefaultFormStateTest.ts @@ -1,4 +1,4 @@ -import { createSchemaUtils, getDefaultFormState, RJSFSchema } from '../../src'; +import { createSchemaUtils, Experimental_DefaultFormStateBehavior, getDefaultFormState, RJSFSchema } from '../../src'; import { AdditionalItemsHandling, computeDefaults, @@ -9,2087 +9,1945 @@ import { ensureFormDataMatchingSchema, } from '../../src/schema/getDefaultFormState'; import { RECURSIVE_REF, RECURSIVE_REF_ALLOF } from '../testUtils/testData'; -import { IExpectType, TestValidatorType } from './types'; +import { TestValidatorType } from './types'; import { resolveDependencies } from '../../src/schema/retrieveSchema'; -/** - * Validate the expected value based on the index of the expectList - * @param index = index of the expectList - * @param expectList = list of expected values - * @param schema = schema - * @param options = optional arguments - */ -const validateBasedOnIndex = (index: number, expectList: IExpectType[], schema: RJSFSchema, options?: any) => { - const { expectedCB, toEqual } = expectList[index]; - expect(expectedCB(schema, options)).toEqual(toEqual); -}; - -type ObjectDefaultExpectList = [ - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType, - IExpectType -]; - -/** - * This function tests schema with type object default values with expectedList, which has a generic callback to get expected data and toEqual data. It is used in multiple places with different methods. This will then test all object default values across different methods. - * - * Important: when adding a new test, please make sure to add the test at the end of the list to avoid breaking the existing tests. Also update the 'ObjectDefaultExpectList' type and add one or more 'IExpectType' to it. This will let typescript show you an error if you didn't update all the 'testObjectDefault' methods accordingly. - * @param {TestValidatorType} testValidator - * @param {IExpectType[]} expectList - */ -const testObjectDefault = (testValidator: TestValidatorType, expectList: ObjectDefaultExpectList) => { - describe('object default test ', () => { - let schema: RJSFSchema; - it('test a schema with a ref', () => { - schema = { - definitions: { - foo: { - type: 'number', - default: 42, - }, - testdef: { - type: 'object', - properties: { - foo: { - $ref: '#/definitions/foo', - }, - }, - }, - }, - $ref: '#/definitions/testdef', - }; - validateBasedOnIndex(0, expectList, schema); - }); - it('test a schema with a const property', () => { - schema = { - type: 'object', - properties: { - test: { - type: 'string', - const: 'test', - }, - }, - }; - validateBasedOnIndex(1, expectList, schema); - }); - it('test a schema with a const property and constAsDefaults is never', () => { - validateBasedOnIndex(2, expectList, schema, { - experimental_defaultFormStateBehavior: { constAsDefaults: 'never' }, - }); - }); - it('test an object with an optional property that has a nested required property', () => { - schema = { - type: 'object', - properties: { - optionalProperty: { - type: 'object', - properties: { - nestedRequiredProperty: { - type: 'string', - }, - }, - required: ['nestedRequiredProperty'], - }, - requiredProperty: { - type: 'string', - default: 'foo', - }, - }, - required: ['requiredProperty'], - }; - validateBasedOnIndex(3, expectList, schema); - }); - it('test an object with an optional property that has a nested required property with default', () => { - schema = { - type: 'object', - properties: { - optionalProperty: { - type: 'object', - properties: { - nestedRequiredProperty: { - type: 'string', - default: '', - }, - }, - required: ['nestedRequiredProperty'], - }, - requiredProperty: { - type: 'string', - default: 'foo', - }, - }, - required: ['requiredProperty'], - }; - validateBasedOnIndex(4, expectList, schema); - }); - it('test an object with an optional property that has a nested required property and includeUndefinedValues', () => { - schema = { - type: 'object', - properties: { - optionalProperty: { - type: 'object', - properties: { - nestedRequiredProperty: { - type: 'object', - properties: { - undefinedProperty: { - type: 'string', - }, - }, - }, - }, - required: ['nestedRequiredProperty'], - }, - requiredProperty: { - type: 'string', - default: 'foo', - }, - }, - required: ['requiredProperty'], - }; - validateBasedOnIndex(5, expectList, schema, { includeUndefinedValues: true }); - }); - it("test an object with an optional property that has a nested required property and includeUndefinedValues is 'excludeObjectChildren'", () => { - schema = { - type: 'object', - properties: { - optionalNumberProperty: { - type: 'number', - }, - optionalObjectProperty: { - type: 'object', - properties: { - nestedRequiredProperty: { - type: 'object', - properties: { - undefinedProperty: { - type: 'string', - }, - }, - }, - }, - required: ['nestedRequiredProperty'], - }, - requiredProperty: { - type: 'string', - default: 'foo', - }, - }, - required: ['requiredProperty'], - }; - validateBasedOnIndex(6, expectList, schema, { includeUndefinedValues: 'excludeObjectChildren' }); +export default function getDefaultFormStateTest(testValidator: TestValidatorType) { + describe('getDefaultFormState()', () => { + let consoleWarnSpy: jest.SpyInstance; + beforeAll(() => { + consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); // mock this to avoid actually warning in the tests }); - it('test an object with an additionalProperties', () => { - schema = { - type: 'object', - properties: { - requiredProperty: { - type: 'string', - default: 'foo', - }, - }, - additionalProperties: true, - required: ['requiredProperty'], - default: { - foo: 'bar', - }, - }; - validateBasedOnIndex(7, expectList, schema); + afterAll(() => { + consoleWarnSpy.mockRestore(); }); - it('test an object with an additionalProperties and includeUndefinedValues', () => { - schema = { - type: 'object', - properties: { - requiredProperty: { - type: 'string', - default: 'foo', - }, - }, - additionalProperties: { - type: 'string', - }, - required: ['requiredProperty'], - default: { - foo: 'bar', - }, - }; - validateBasedOnIndex(8, expectList, schema, { includeUndefinedValues: true }); + it('throws error when schema is not an object', () => { + expect(() => getDefaultFormState(testValidator, null as unknown as RJSFSchema)).toThrowError('Invalid schema:'); }); - it('test an object with additionalProperties type object with defaults and formdata', () => { - schema = { - type: 'object', - properties: { - test: { - title: 'Test', - type: 'object', - properties: { - foo: { - type: 'string', - }, + + describe('object schemas', () => { + describe('schema with a ref', () => { + const schema: RJSFSchema = { + definitions: { + foo: { + type: 'number', + default: 42, }, - additionalProperties: { + testdef: { type: 'object', properties: { - host: { - title: 'Host', - type: 'string', - default: 'localhost', - }, - port: { - title: 'Port', - type: 'integer', - default: 389, + foo: { + $ref: '#/definitions/foo', }, }, }, }, - }, - }; - validateBasedOnIndex(9, expectList, schema, { rawFormData: { test: { foo: 'x', newKey: {} } } }); - }); - it('test an object with additionalProperties type object with formdata and no defaults', () => { - schema = { - type: 'object', - properties: { - test: { - title: 'Test', - type: 'object', - properties: { - foo: { - type: 'string', - }, + $ref: '#/definitions/testdef', + }; + const expected = { + foo: 42, + }; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined, schema)).toEqual(expected); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect(getDefaultBasedOnSchemaType(testValidator, schema, { rootSchema: schema }, expected)).toBe(undefined); + }); + + test('getObjectDefaults', () => { + expect(getObjectDefaults(testValidator, schema, { rootSchema: schema }, expected)).toEqual({}); + }); + }); + + describe('schema with a const property', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + test: { + type: 'string', + const: 'test', }, - additionalProperties: { + }, + }; + const expected = { + test: 'test', + }; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined, schema)).toEqual(expected); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { + rootSchema: schema, + }) + ).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect(getObjectDefaults(testValidator, schema, { rootSchema: schema })).toEqual(expected); + }); + + describe('constAsDefaults is never', () => { + const experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior = { + constAsDefaults: 'never', + }; + const expected = {}; + + test('getDefaultFormState', () => { + expect( + getDefaultFormState( + testValidator, + schema, + undefined, + schema, + undefined, + experimental_defaultFormStateBehavior + ) + ).toEqual(expected); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { + rootSchema: schema, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults(testValidator, schema, { + rootSchema: schema, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + }); + }); + + describe('an object with an optional property that has a nested required property', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + optionalProperty: { type: 'object', properties: { - host: { - title: 'Host', + nestedRequiredProperty: { type: 'string', }, - port: { - title: 'Port', - type: 'integer', - }, }, + required: ['nestedRequiredProperty'], }, - }, - }, - }; - validateBasedOnIndex(10, expectList, schema, { rawFormData: { test: { foo: 'x', newKey: {} } } }); - }); - it('test an object with additionalProperties type object with no defaults and non-object formdata', () => { - schema = { - type: 'object', - properties: { - test: { - title: 'Test', - type: 'object', - properties: { - foo: { - type: 'string', - }, + requiredProperty: { + type: 'string', + default: 'foo', }, - additionalProperties: { + }, + required: ['requiredProperty'], + }; + const expected = { + requiredProperty: 'foo', + }; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined, schema)).toEqual(expected); + }); + + test('computeDefaults', () => { + expect(computeDefaults(testValidator, schema, { rootSchema: schema })).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { + rootSchema: schema, + }) + ).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect(getObjectDefaults(testValidator, schema, { rootSchema: schema })).toEqual(expected); + }); + }); + + describe('an object with an optional property that has a nested required property with default', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + optionalProperty: { type: 'object', properties: { - host: { - title: 'Host', + nestedRequiredProperty: { type: 'string', - }, - port: { - title: 'Port', - type: 'integer', + default: '', }, }, + required: ['nestedRequiredProperty'], }, - }, - }, - }; - validateBasedOnIndex(11, expectList, schema, { rawFormData: {} }); - }); - it('test an object with deep nested dependencies with formData', () => { - schema = { - type: 'object', - properties: { - nestedObject: { - type: 'object', - properties: { - first: { - type: 'string', - enum: ['no', 'yes'], - default: 'no', - }, + requiredProperty: { + type: 'string', + default: 'foo', }, - dependencies: { - first: { - oneOf: [ - { - properties: { - first: { - enum: ['yes'], - }, - second: { - type: 'object', - properties: { - deeplyNestedThird: { - type: 'string', - enum: ['before', 'after'], - default: 'before', - }, - }, - }, - }, - }, - { - properties: { - first: { - enum: ['no'], - }, + }, + required: ['requiredProperty'], + }; + const expected = { + requiredProperty: 'foo', + optionalProperty: { nestedRequiredProperty: '' }, + }; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined, schema)).toEqual(expected); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + }) + ); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect(getDefaultBasedOnSchemaType(testValidator, schema, { rootSchema: schema })).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect(getObjectDefaults(testValidator, schema, { rootSchema: schema })).toEqual(expected); + }); + }); + + describe('an object with an optional property that has a nested required property and includeUndefinedValues', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + optionalProperty: { + type: 'object', + properties: { + nestedRequiredProperty: { + type: 'object', + properties: { + undefinedProperty: { + type: 'string', }, }, - ], + }, }, + required: ['nestedRequiredProperty'], + }, + requiredProperty: { + type: 'string', + default: 'foo', }, }, - }, - }; + required: ['requiredProperty'], + }; + const includeUndefinedValues = true; + const expected = { + optionalProperty: { + nestedRequiredProperty: { + undefinedProperty: undefined, + }, + }, + requiredProperty: 'foo', + }; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined, schema, includeUndefinedValues)).toEqual( + expected + ); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + includeUndefinedValues, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { rootSchema: schema, includeUndefinedValues }) + ).toEqual(expected); + }); - // Mock isValid so that withExactlyOneSubschema works as expected - testValidator.setReturnValues({ - isValid: [ - true, // First oneOf... first === first - false, // Second oneOf... second !== first - ], + test('getObjectDefaults', () => { + expect(getObjectDefaults(testValidator, schema, { rootSchema: schema, includeUndefinedValues })).toEqual( + expected + ); + }); }); - validateBasedOnIndex(12, expectList, schema, { - rawFormData: { - nestedObject: { - first: 'yes', + describe("an object with an optional property that has a nested required property and includeUndefinedValues is 'excludeObjectChildren'", () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + optionalNumberProperty: { + type: 'number', + }, + optionalObjectProperty: { + type: 'object', + properties: { + nestedRequiredProperty: { + type: 'object', + properties: { + undefinedProperty: { + type: 'string', + }, + }, + }, + }, + required: ['nestedRequiredProperty'], + }, + requiredProperty: { + type: 'string', + default: 'foo', + }, }, - }, - testValidator, - }); - }); - it('test handling an invalid property schema', () => { - schema = { - type: 'object', - properties: { - invalidProperty: 'not a valid property value', - }, - } as RJSFSchema; + required: ['requiredProperty'], + }; + const includeUndefinedValues = 'excludeObjectChildren'; + const expected = { + optionalNumberProperty: undefined, + optionalObjectProperty: { + nestedRequiredProperty: {}, + }, + requiredProperty: 'foo', + }; - validateBasedOnIndex(13, expectList, schema, { - includeUndefinedValues: 'excludeObjectChildren', - }); - }); - it('test with a recursive schema', () => { - validateBasedOnIndex(14, expectList, RECURSIVE_REF, { - includeUndefinedValues: 'excludeObjectChildren', + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined, schema, includeUndefinedValues)).toEqual( + expected + ); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + includeUndefinedValues, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { rootSchema: schema, includeUndefinedValues }) + ).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect(getObjectDefaults(testValidator, schema, { rootSchema: schema, includeUndefinedValues })).toEqual( + expected + ); + }); }); - }); - it('test with a recursive allof schema', () => { - validateBasedOnIndex(15, expectList, RECURSIVE_REF_ALLOF); - }); - it('test returns undefined with simple schema and no optional args', () => { - schema = { type: 'string' }; - validateBasedOnIndex(16, expectList, schema); - }); - it('test an object const value merge with formData', () => { - schema = { - type: 'object', - properties: { - localConst: { - type: 'string', - const: 'local', + + describe('an object with an additionalProperties', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + requiredProperty: { + type: 'string', + default: 'foo', + }, }, - RootConst: { - type: 'object', - properties: { - attr1: { - type: 'number', + additionalProperties: true, + required: ['requiredProperty'], + default: { + foo: 'bar', + }, + }; + const expected = { + requiredProperty: 'foo', + foo: 'bar', + }; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined, schema)).toEqual(expected); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType( + testValidator, + schema, + { + rootSchema: schema, }, - attr2: { - type: 'boolean', + { foo: 'bar' } + ) + ).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults( + testValidator, + schema, + { + rootSchema: schema, }, - }, - const: { - attr1: 1, - attr2: true, + { foo: 'bar' } + ) + ).toEqual(expected); + }); + }); + + describe('an object with an additionalProperties and includeUndefinedValues', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + requiredProperty: { + type: 'string', + default: 'foo', }, }, - RootAndLocalConst: { + additionalProperties: { type: 'string', - const: 'FromLocal', }, - fromFormData: { - type: 'string', + required: ['requiredProperty'], + default: { + foo: 'bar', }, - }, - const: { - RootAndLocalConst: 'FromRoot', - }, - }; + }; + const includeUndefinedValues = true; + const expected = { + requiredProperty: 'foo', + foo: 'bar', + }; - validateBasedOnIndex(17, expectList, schema, { - rawFormData: { - fromFormData: 'fromFormData', - }, - experimental_defaultFormStateBehavior: { - emptyObjectFields: 'skipDefaults', - }, - }); - }); - it('test an object const value merge with formData and constAsDefault is never', () => { - validateBasedOnIndex(18, expectList, schema, { - rawFormData: { - fromFormData: 'fromFormData', - }, - experimental_defaultFormStateBehavior: { - emptyObjectFields: 'skipDefaults', - constAsDefaults: 'never', - }, - testValidator, + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined, schema, includeUndefinedValues)).toEqual( + expected + ); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + includeUndefinedValues, + }) + ); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType( + testValidator, + schema, + { rootSchema: schema, includeUndefinedValues }, + { foo: 'bar' } + ) + ).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults(testValidator, schema, { rootSchema: schema, includeUndefinedValues }, { foo: 'bar' }) + ).toEqual(expected); + }); }); - }); - it('test an object with non valid formData for enum properties', () => { - schema = { - type: 'object', - properties: { - animal: { - enum: ['Cat', 'Fish'], - }, - }, - dependencies: { - animal: { - oneOf: [ - { - properties: { - animal: { - enum: ['Cat'], - }, - food: { - type: 'string', - enum: ['meat', 'grass', 'fish'], - default: 'meat', - }, + + describe('an object with additionalProperties type object with defaults and formdata', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + test: { + title: 'Test', + type: 'object', + properties: { + foo: { + type: 'string', }, }, - { + additionalProperties: { + type: 'object', properties: { - animal: { - enum: ['Fish'], - }, - food: { + host: { + title: 'Host', type: 'string', - enum: ['insect', 'worms'], - default: 'worms', + default: 'localhost', }, - water: { - type: 'string', - enum: ['lake', 'sea'], - default: 'sea', + port: { + title: 'Port', + type: 'integer', + default: 389, }, }, }, - ], + }, }, - }, - }; + }; + const rawFormData = { test: { foo: 'x', newKey: {} } }; - // Mock isValid so that withExactlyOneSubschema works as expected - testValidator.setReturnValues({ - isValid: [false, true], - }); + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, rawFormData, schema)).toEqual({ + test: { + foo: 'x', + newKey: { + host: 'localhost', + port: 389, + }, + }, + }); + }); - validateBasedOnIndex(19, expectList, schema, { - rawFormData: { - animal: 'Fish', - food: 'meat', - water: null, - }, - shouldMergeDefaultsIntoFormData: true, - testValidator, - }); - }); - it('test an object with non valid formData for enum properties with mergeDefaultsIntoFormData set to "useDefaultIfFormDataUndefined"', () => { - // Mock isValid so that withExactlyOneSubschema works as expected - testValidator.setReturnValues({ - isValid: [false, true], - }); + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + rawFormData, + }) + ).toEqual({ + test: { + newKey: { + host: 'localhost', + port: 389, + }, + }, + }); + }); - validateBasedOnIndex(20, expectList, schema, { - rawFormData: { - animal: 'Fish', - food: 'meat', - water: null, - }, - shouldMergeDefaultsIntoFormData: true, - experimental_defaultFormStateBehavior: { - mergeDefaultsIntoFormData: 'useDefaultIfFormDataUndefined', - }, - testValidator, - }); - - // Reset the testValidator - if (typeof testValidator.reset === 'function') { - testValidator?.reset(); - } - }); - it('test oneOf with const values and constAsDefaults is always', () => { - schema = { - type: 'object', - properties: { - oneOfField: { - title: 'One Of Field', - type: 'string', - oneOf: [ - { - const: 'username', - title: 'Username and password', - }, - { - const: 'secret', - title: 'SSO', - }, - ], - }, - }, - required: ['oneOfField'], - }; - validateBasedOnIndex(21, expectList, schema, { - experimental_defaultFormStateBehavior: { - constAsDefaults: 'always', - }, - }); - }); - it('test oneOf with const values and constAsDefaults is skipOneOf', () => { - validateBasedOnIndex(22, expectList, schema, { - experimental_defaultFormStateBehavior: { - constAsDefaults: 'skipOneOf', - }, - }); - }); - it('test oneOf with const values and constAsDefaults is never', () => { - validateBasedOnIndex(23, expectList, schema, { - experimental_defaultFormStateBehavior: { - constAsDefaults: 'never', - }, - }); - }); - it('Test an object with invalid formData const and constAsDefault set to always', () => { - schema = { - type: 'object', - properties: { - stringField: { - type: 'string', - const: 'fromConst', - }, - }, - }; - - validateBasedOnIndex(24, expectList, schema, { - rawFormData: { - stringField: 'fromFormData', - }, - experimental_defaultFormStateBehavior: { - constAsDefaults: 'always', - }, - }); - }); - }); -}; - -type ArrayDefaultExpectList = [IExpectType, IExpectType, IExpectType, IExpectType, IExpectType, IExpectType]; - -/** - * This function tests schema with type array default values with expectedList, which has a generic callback to get expected data and toEqual data. It is used in multiple places with different methods. This will then test all array default values across different methods. - * - * Important: when adding a new test, please make sure to add the test at the end of the list to avoid breaking the existing tests. Also update the 'ArrayDefaultExpectList' type and add one or more 'IExpectType' to it. This will let typescript show you an error if you didn't update all the 'testArrayDefault' methods accordingly. - * @param {TestValidatorType} testValidator - * @param {IExpectType[]} expectList - */ -const testArrayDefault = (testValidator: TestValidatorType, expectList: ArrayDefaultExpectList) => { - describe('test array default', () => { - // Reset the testValidator - if (typeof testValidator.reset === 'function') { - testValidator?.reset(); - } - - it('test an array with defaults with no formData', () => { - const schema: RJSFSchema = { - type: 'array', - minItems: 4, - default: ['Raphael', 'Michaelangelo'], - items: { - type: 'string', - default: 'Unknown', - }, - }; - - validateBasedOnIndex(0, expectList, schema, { - includeUndefinedValues: 'excludeObjectChildren', - }); - }); - it('test an array with defaults with empty array as formData', () => { - const schema: RJSFSchema = { - type: 'array', - minItems: 4, - default: ['Raphael', 'Michaelangelo'], - items: { - type: 'string', - default: 'Unknown', - }, - }; - - validateBasedOnIndex(1, expectList, schema, { - rawFormData: [], - includeUndefinedValues: 'excludeObjectChildren', - experimental_defaultFormStateBehavior: { - arrayMinItems: { - mergeExtraDefaults: true, - populate: 'all', - }, - }, - }); - }); - it('test an array with no defaults', () => { - const schema: RJSFSchema = { - type: 'array', - minItems: 4, - items: { - type: 'string', - }, - }; - - validateBasedOnIndex(2, expectList, schema, { - includeUndefinedValues: 'excludeObjectChildren', - }); - }); - it('test an array const value populate as defaults', () => { - const schema: RJSFSchema = { - type: 'array', - minItems: 4, - const: ['ConstFromRoot', 'ConstFromRoot'], - items: { - type: 'string', - const: 'Constant', - }, - }; - - validateBasedOnIndex(3, expectList, schema, { - includeUndefinedValues: 'excludeObjectChildren', - }); - }); - it('test handling an invalid array schema', () => { - const schema: RJSFSchema = { - type: 'array', - items: 'not a valid item value', - } as RJSFSchema; - - validateBasedOnIndex(4, expectList, schema, { - includeUndefinedValues: 'excludeObjectChildren', - }); - }); - it('test returns undefined with simple schema and no optional args', () => { - const schema: RJSFSchema = { type: 'array' }; - validateBasedOnIndex(5, expectList, schema); - }); - }); -}; - -export default function getDefaultFormStateTest(testValidator: TestValidatorType) { - describe('getDefaultFormState()', () => { - let consoleWarnSpy: jest.SpyInstance; - beforeAll(() => { - consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); // mock this to avoid actually warning in the tests - }); - afterAll(() => { - consoleWarnSpy.mockRestore(); - }); - it('throws error when schema is not an object', () => { - expect(() => getDefaultFormState(testValidator, null as unknown as RJSFSchema)).toThrowError('Invalid schema:'); - }); - // test object defaults - testObjectDefault(testValidator, [ - { - expectedCB: (schema) => getDefaultFormState(testValidator, schema, undefined, schema), - toEqual: { - foo: 42, - }, - }, - { - expectedCB: (schema) => getDefaultFormState(testValidator, schema, undefined, schema), - toEqual: { - test: 'test', - }, - }, - { - expectedCB: (schema, options) => - getDefaultFormState( - testValidator, - schema, - undefined, - schema, - undefined, - options.experimental_defaultFormStateBehavior - ), - toEqual: {}, - }, - { - expectedCB: (schema) => getDefaultFormState(testValidator, schema, undefined, schema), - toEqual: { requiredProperty: 'foo' }, - }, - { - expectedCB: (schema) => getDefaultFormState(testValidator, schema, undefined, schema), - toEqual: { - requiredProperty: 'foo', - optionalProperty: { nestedRequiredProperty: '' }, - }, - }, - { - expectedCB: (schema, options) => - getDefaultFormState(testValidator, schema, undefined, schema, options?.includeUndefinedValues), - toEqual: { - optionalProperty: { - nestedRequiredProperty: { - undefinedProperty: undefined, - }, - }, - requiredProperty: 'foo', - }, - }, - { - expectedCB: (schema, options) => - getDefaultFormState(testValidator, schema, undefined, schema, options?.includeUndefinedValues), - toEqual: { - optionalNumberProperty: undefined, - optionalObjectProperty: { - nestedRequiredProperty: {}, - }, - requiredProperty: 'foo', - }, - }, - { - expectedCB: (schema) => getDefaultFormState(testValidator, schema, undefined, schema), - toEqual: { - requiredProperty: 'foo', - foo: 'bar', - }, - }, - { - expectedCB: (schema, options) => - getDefaultFormState(testValidator, schema, undefined, schema, options?.includeUndefinedValues), - toEqual: { - requiredProperty: 'foo', - foo: 'bar', - }, - }, - { - expectedCB: (schema, options) => getDefaultFormState(testValidator, schema, options.rawFormData, schema), - toEqual: { - test: { - foo: 'x', - newKey: { - host: 'localhost', - port: 389, - }, - }, - }, - }, - { - expectedCB: (schema, options) => getDefaultFormState(testValidator, schema, options.rawFormData, schema), - toEqual: { - test: { - foo: 'x', - newKey: {}, - }, - }, - }, - { - expectedCB: (schema, options) => getDefaultFormState(testValidator, schema, options.rawFormData, schema), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - getDefaultFormState(options.testValidator, schema, options.rawFormData, schema, false, { - emptyObjectFields: 'populateAllDefaults', - allOf: 'skipDefaults', - arrayMinItems: { - populate: 'populate' as any, - mergeExtraDefaults: false, - }, - mergeDefaultsIntoFormData: 'useFormDataIfPresent', - }), - toEqual: { - nestedObject: { - first: 'yes', - second: { - deeplyNestedThird: 'before', - }, - }, - }, - }, - { - expectedCB: (schema, options) => - getDefaultFormState(testValidator, schema, undefined, schema, options.includeUndefinedValues), - toEqual: {}, - }, - { - expectedCB: (schema) => getDefaultFormState(testValidator, schema, undefined, schema), - toEqual: { - children: { - name: '', - }, - name: '', - }, - }, - { - expectedCB: (schema) => getDefaultFormState(testValidator, schema, undefined, schema), - toEqual: { - value: [undefined], - }, - }, - { - expectedCB: (schema) => getDefaultFormState(testValidator, schema, undefined), - toEqual: undefined, - }, - { - expectedCB: (schema, options) => - getDefaultFormState( - testValidator, - schema, - options.rawFormData, - schema, - false, - options.experimental_defaultFormStateBehavior - ), - toEqual: { - localConst: 'local', - RootConst: { - attr1: 1, - attr2: true, - }, - RootAndLocalConst: 'FromLocal', - fromFormData: 'fromFormData', - }, - }, - { - expectedCB: (schema, options) => - getDefaultFormState( - testValidator, - schema, - options.rawFormData, - schema, - false, - options.experimental_defaultFormStateBehavior - ), - toEqual: { - fromFormData: 'fromFormData', - }, - }, - { - expectedCB: (schema, options) => - getDefaultFormState(options.testValidator, schema, options.rawFormData, schema), - toEqual: { - animal: 'Fish', - food: 'worms', - water: null, - }, - }, - { - expectedCB: (schema, options) => - getDefaultFormState( - options.testValidator, - schema, - options.rawFormData, - schema, - undefined, - options.experimental_defaultFormStateBehavior - ), - toEqual: { - animal: 'Fish', - food: 'worms', - water: 'sea', - }, - }, - { - expectedCB: (schema, options) => - getDefaultFormState( - testValidator, - schema, - undefined, - schema, - undefined, - options.experimental_defaultFormStateBehavior - ), - toEqual: { - oneOfField: 'username', - }, - }, - { - expectedCB: (schema, options) => - getDefaultFormState( - testValidator, - schema, - undefined, - schema, - undefined, - options.experimental_defaultFormStateBehavior - ), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - getDefaultFormState( - testValidator, - schema, - undefined, - schema, - undefined, - options.experimental_defaultFormStateBehavior - ), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - getDefaultFormState( - testValidator, - schema, - options.rawFormData, - schema, - undefined, - options.experimental_defaultFormStateBehavior - ), - toEqual: { - stringField: 'fromConst', - }, - }, - ]); - // test array defaults - testArrayDefault(testValidator, [ - { - expectedCB: (schema, options) => - getDefaultFormState(testValidator, schema, undefined, schema, options.includeUndefinedValues), - toEqual: ['Raphael', 'Michaelangelo', 'Unknown', 'Unknown'], - }, - { - expectedCB: (schema, options) => - getDefaultFormState( - testValidator, - schema, - undefined, - schema, - options.includeUndefinedValues, - options.experimental_defaultFormStateBehavior - ), - toEqual: ['Raphael', 'Michaelangelo', 'Unknown', 'Unknown'], - }, - { - expectedCB: (schema, options) => - getDefaultFormState(testValidator, schema, undefined, schema, options.includeUndefinedValues), - toEqual: [], - }, - { - expectedCB: (schema, options) => - getDefaultFormState(testValidator, schema, undefined, schema, options.includeUndefinedValues), - toEqual: ['ConstFromRoot', 'ConstFromRoot', 'Constant', 'Constant'], - }, - { - expectedCB: (schema, options) => - getDefaultFormState(testValidator, schema, undefined, schema, options.includeUndefinedValues), - toEqual: [], - }, - { - expectedCB: (schema) => getDefaultFormState(testValidator, schema), - toEqual: [], - }, - ]); - it('getInnerSchemaForArrayItem() item of type boolean returns empty schema', () => { - expect(getInnerSchemaForArrayItem({ items: [true] }, AdditionalItemsHandling.Ignore, 0)).toEqual({}); - }); - describe('resolveDependencies()', () => { - it('test an object with dependencies', () => { - const schema: RJSFSchema = { - type: 'object', - properties: { - first: { - type: 'string', - enum: ['no', 'yes'], - default: 'no', - }, - }, - dependencies: { - first: { - oneOf: [ - { - properties: { - first: { - enum: ['yes'], - }, - second: { - type: 'object', - properties: { - deeplyNestedThird: { - type: 'string', - enum: ['before', 'after'], - default: 'before', - }, - }, - }, - }, - }, - { - properties: { - first: { - enum: ['no'], - }, - }, - }, - ], - }, - }, - }; - - // Mock isValid so that withExactlyOneSubschema works as expected - testValidator.setReturnValues({ - isValid: [ - true, // First oneOf... first === first - false, // Second oneOf... second !== first - ], - }); - expect( - resolveDependencies( - testValidator, - schema, - schema, - false, - [], - { - first: 'yes', - }, - undefined - ) - ).toEqual([ - { - type: 'object', - properties: { - first: { - type: 'string', - enum: ['no', 'yes'], - default: 'no', - }, - second: { - type: 'object', - properties: { - deeplyNestedThird: { - type: 'string', - enum: ['before', 'after'], - default: 'before', - }, - }, - }, - }, - }, - ]); - }); - }); - describe('computeDefaults()', () => { - // test object defaults - testObjectDefault(testValidator, [ - { - expectedCB: (schema) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - }), - toEqual: { - foo: 42, - }, - }, - { - expectedCB: (schema) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - }), - toEqual: { - test: 'test', - }, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - }), - toEqual: { requiredProperty: 'foo' }, - }, - { - expectedCB: (schema) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - }), - toEqual: { - requiredProperty: 'foo', - optionalProperty: { nestedRequiredProperty: '' }, - }, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - optionalProperty: { - nestedRequiredProperty: { - undefinedProperty: undefined, - }, - }, - requiredProperty: 'foo', - }, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - optionalNumberProperty: undefined, - optionalObjectProperty: { - nestedRequiredProperty: {}, - }, - requiredProperty: 'foo', - }, - }, - { - expectedCB: (schema) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - }), - toEqual: { - requiredProperty: 'foo', - foo: 'bar', - }, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - requiredProperty: 'foo', - foo: 'bar', - }, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - test: { - newKey: { - host: 'localhost', - port: 389, - }, - }, - }, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - test: { - newKey: {}, - }, - }, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - nestedObject: { - first: 'no', - second: { - deeplyNestedThird: 'before', - }, - }, - }, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - }), - toEqual: { - name: '', - }, - }, - { - expectedCB: (schema) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - }), - toEqual: { - value: [undefined], - }, - }, - { - expectedCB: (schema) => computeDefaults(testValidator, schema), - toEqual: undefined, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - localConst: 'local', - RootConst: { - attr1: 1, - attr2: true, - }, - RootAndLocalConst: 'FromLocal', - }, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - computeDefaults(options.testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - animal: 'Fish', - food: 'worms', - water: 'sea', - }, - }, - { - expectedCB: (schema, options) => - computeDefaults(options.testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - animal: 'Fish', - food: 'worms', - water: 'sea', - }, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - oneOfField: 'username', - }, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - stringField: 'fromConst', - }, - }, - ]); - // test array defaults - testArrayDefault(testValidator, [ - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: ['Raphael', 'Michaelangelo', 'Unknown', 'Unknown'], - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: ['Raphael', 'Michaelangelo', 'Unknown', 'Unknown'], - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: [], - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: ['ConstFromRoot', 'ConstFromRoot', 'Constant', 'Constant'], - }, - { - expectedCB: (schema, options) => - computeDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: [], - }, - { - expectedCB: (schema) => getArrayDefaults(testValidator, schema), - toEqual: [], - }, - ]); - }); - describe('getDefaultBasedOnSchemaType()', () => { - // test object defaults - testObjectDefault(testValidator, [ - { - expectedCB: (schema) => - getDefaultBasedOnSchemaType( - testValidator, - schema, - { - rootSchema: schema, - }, - { - foo: 42, - } - ), - toEqual: undefined, - }, - { - expectedCB: (schema) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - }), - toEqual: { - test: 'test', - }, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - }), - toEqual: { requiredProperty: 'foo' }, - }, - { - expectedCB: (schema) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - }), - toEqual: { - requiredProperty: 'foo', - optionalProperty: { nestedRequiredProperty: '' }, - }, - }, - { - expectedCB: (schema, options) => + test('getDefaultBasedOnSchemaType', () => { + expect( getDefaultBasedOnSchemaType(testValidator, schema, { rootSchema: schema, - ...options, - }), - toEqual: { - optionalProperty: { - nestedRequiredProperty: { - undefinedProperty: undefined, + rawFormData, + }) + ).toEqual({ + test: { + newKey: { + host: 'localhost', + port: 389, }, }, - requiredProperty: 'foo', - }, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { + }); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults(testValidator, schema, { rootSchema: schema, - ...options, - }), - toEqual: { - optionalNumberProperty: undefined, - optionalObjectProperty: { - nestedRequiredProperty: {}, + rawFormData, + }) + ).toEqual({ + test: { + newKey: { + host: 'localhost', + port: 389, + }, }, - requiredProperty: 'foo', - }, - }, - { - expectedCB: (schema) => - getDefaultBasedOnSchemaType( - testValidator, - schema, - { + }); + }); + + describe('an object with additionalProperties type object with formdata and no defaults', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + test: { + title: 'Test', + type: 'object', + properties: { + foo: { + type: 'string', + }, + }, + additionalProperties: { + type: 'object', + properties: { + host: { + title: 'Host', + type: 'string', + }, + port: { + title: 'Port', + type: 'integer', + }, + }, + }, + }, + }, + }; + const rawFormData = { test: { foo: 'x', newKey: {} } }; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, rawFormData, schema)).toEqual({ + test: { + foo: 'x', + newKey: {}, + }, + }); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { rootSchema: schema, + rawFormData, + }) + ).toEqual({ + test: { + newKey: {}, }, - { foo: 'bar' } - ), - toEqual: { - requiredProperty: 'foo', - foo: 'bar', - }, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType( - testValidator, - schema, - { + }); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { rootSchema: schema, - ...options, + rawFormData, + }) + ).toEqual({ + test: { + newKey: {}, }, - { foo: 'bar' } - ), - toEqual: { - requiredProperty: 'foo', - foo: 'bar', - }, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - test: { - newKey: { - host: 'localhost', - port: 389, + }); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults(testValidator, schema, { + rootSchema: schema, + rawFormData, + }) + ).toEqual({ + test: { + newKey: {}, + }, + }); + }); + }); + + describe('an object with additionalProperties type object with no defaults and non-object formdata', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + test: { + title: 'Test', + type: 'object', + properties: { + foo: { + type: 'string', + }, + }, + additionalProperties: { + type: 'object', + properties: { + host: { + title: 'Host', + type: 'string', + }, + port: { + title: 'Port', + type: 'integer', + }, + }, + }, }, }, - }, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - test: { - newKey: {}, + }; + const rawFormData = {}; + const expected = {}; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, rawFormData, schema)).toEqual(expected); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + rawFormData, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { + rootSchema: schema, + rawFormData, + }) + ).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults(testValidator, schema, { + rootSchema: schema, + rawFormData, + }) + ).toEqual(expected); + }); + }); + + describe('an object with deep nested dependencies with formData', () => { + beforeEach(() => { + // Mock isValid so that withExactlyOneSubschema works as expected + testValidator.setReturnValues({ + isValid: [ + true, // First oneOf... first === first + false, // Second oneOf... second !== first + ], + }); + }); + afterAll(() => { + // Reset the testValidator + if (typeof testValidator.reset === 'function') { + testValidator?.reset(); + } + }); + + const schema: RJSFSchema = { + type: 'object', + properties: { + nestedObject: { + type: 'object', + properties: { + first: { + type: 'string', + enum: ['no', 'yes'], + default: 'no', + }, + }, + dependencies: { + first: { + oneOf: [ + { + properties: { + first: { + enum: ['yes'], + }, + second: { + type: 'object', + properties: { + deeplyNestedThird: { + type: 'string', + enum: ['before', 'after'], + default: 'before', + }, + }, + }, + }, + }, + { + properties: { + first: { + enum: ['no'], + }, + }, + }, + ], + }, + }, + }, }, - }, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { + }; + const rawFormData = { nestedObject: { - first: 'no', - second: { - deeplyNestedThird: 'before', - }, + first: 'yes', }, - }, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - }), - toEqual: undefined, - }, - { - expectedCB: (schema) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - }), - toEqual: { - value: [undefined], - }, - }, - { - expectedCB: (schema) => getDefaultBasedOnSchemaType(testValidator, schema), - toEqual: undefined, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - localConst: 'local', - RootConst: { - attr1: 1, - attr2: true, + }; + + test('getDefaultFormState', () => { + expect( + getDefaultFormState(testValidator, schema, rawFormData, schema, false, { + emptyObjectFields: 'populateAllDefaults', + allOf: 'skipDefaults', + arrayMinItems: { + populate: 'populate' as any, + mergeExtraDefaults: false, + }, + mergeDefaultsIntoFormData: 'useFormDataIfPresent', + }) + ).toEqual({ + nestedObject: { + first: 'yes', + second: { + deeplyNestedThird: 'before', + }, + }, + }); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + rawFormData, + }) + ).toEqual({ + nestedObject: { + first: 'no', + second: { + deeplyNestedThird: 'before', + }, + }, + }); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { + rootSchema: schema, + rawFormData, + }) + ).toEqual({ + nestedObject: { + first: 'no', + second: { + deeplyNestedThird: 'before', + }, + }, + }); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults(testValidator, schema, { + rootSchema: schema, + rawFormData, + }) + ).toEqual({ + nestedObject: { + first: 'no', + second: { + deeplyNestedThird: 'before', + }, + }, + }); + }); + }); + + describe('handling an invalid property schema', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + invalidProperty: 'not a valid property value', }, - RootAndLocalConst: 'FromLocal', - }, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(options.testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - animal: 'Fish', - }, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(options.testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - animal: 'Fish', - }, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - oneOfField: 'username', - }, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - stringField: 'fromConst', - }, - }, - ]); - // test array defaults - testArrayDefault(testValidator, [ - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType( - testValidator, - schema, - { + } as RJSFSchema; + const includeUndefinedValues = 'excludeObjectChildren'; + const expected = {}; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined, schema, includeUndefinedValues)).toEqual( + expected + ); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { rootSchema: schema, - ...options, - }, - ['Raphael', 'Michaelangelo'] - ), - toEqual: ['Raphael', 'Michaelangelo', 'Unknown', 'Unknown'], - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType( - testValidator, - schema, - { + includeUndefinedValues, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { rootSchema: schema, - ...options, - }, - ['Raphael', 'Michaelangelo'] - ), - toEqual: ['Raphael', 'Michaelangelo', 'Unknown', 'Unknown'], - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: [], - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType( - testValidator, - schema, - { + includeUndefinedValues, + }) + ).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults(testValidator, schema, { rootSchema: schema, - ...options, + includeUndefinedValues, + }) + ).toEqual(expected); + }); + }); + + describe('a recursive schema', () => { + const schema = RECURSIVE_REF; + // NOTE: defined at L410 + // const includeUndefinedValues = 'excludeObjectChildren'; + + test('getDefaultFormState', () => { + // NOTE: `includeUndefinedValues` is not used L861 + expect(getDefaultFormState(testValidator, schema, undefined, schema)).toEqual({ + children: { + name: '', }, - ['ConstFromRoot', 'ConstFromRoot'] - ), - toEqual: ['ConstFromRoot', 'ConstFromRoot', 'Constant', 'Constant'], - }, - { - expectedCB: (schema, options) => - getDefaultBasedOnSchemaType(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: [], - }, - { - expectedCB: (schema) => getDefaultBasedOnSchemaType(testValidator, schema), - toEqual: [], - }, - ]); - }); - describe('getObjectDefaults()', () => { - // test object defaults - testObjectDefault(testValidator, [ - { - expectedCB: (schema) => - getObjectDefaults( - testValidator, - schema, - { + name: '', + }); + }); + + test('computeDefaults', () => { + // NOTE: `includeUndefinedValues` is not used L1275 + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + }) + ).toEqual({ + name: '', + }); + }); + + test('getDefaultBasedOnSchemaType', () => { + // NOTE: `includeUndefinedValues` is not used L1598 + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { + rootSchema: schema, + }) + ).toBe(undefined); + }); + + test('getObjectDefaults', () => { + // NOTE: `includeUndefinedValues` is not used L1930 + expect( + getObjectDefaults(testValidator, schema, { + rootSchema: schema, + }) + ).toEqual({}); + }); + }); + + describe('a recursive allof schema', () => { + const schema = RECURSIVE_REF_ALLOF; + const expected = { + value: [undefined], + }; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined, schema)).toEqual(expected); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { rootSchema: schema, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect(getDefaultBasedOnSchemaType(testValidator, schema, { rootSchema: schema })).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect(getObjectDefaults(testValidator, schema, { rootSchema: schema })).toEqual(expected); + }); + }); + + describe('a simple schema and no optional args', () => { + const schema: RJSFSchema = { type: 'string' }; + const expected = undefined; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined)).toEqual(expected); + }); + + test('computeDefaults', () => { + expect(computeDefaults(testValidator, schema)).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect(getDefaultBasedOnSchemaType(testValidator, schema)).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect(getObjectDefaults(testValidator, schema)).toEqual({}); + }); + }); + + describe('an object const value merge with formData', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + localConst: { + type: 'string', + const: 'local', }, - { - foo: 42, - } - ), - toEqual: {}, - }, - { - expectedCB: (schema) => - getObjectDefaults(testValidator, schema, { - rootSchema: schema, - }), - toEqual: { - test: 'test', - }, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema) => - getObjectDefaults(testValidator, schema, { - rootSchema: schema, - }), - toEqual: { requiredProperty: 'foo' }, - }, - { - expectedCB: (schema) => - getObjectDefaults(testValidator, schema, { - rootSchema: schema, - }), - toEqual: { - requiredProperty: 'foo', - optionalProperty: { nestedRequiredProperty: '' }, - }, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - optionalProperty: { - nestedRequiredProperty: { - undefinedProperty: undefined, + RootConst: { + type: 'object', + properties: { + attr1: { + type: 'number', + }, + attr2: { + type: 'boolean', + }, + }, + const: { + attr1: 1, + attr2: true, + }, + }, + RootAndLocalConst: { + type: 'string', + const: 'FromLocal', + }, + fromFormData: { + type: 'string', }, }, - requiredProperty: 'foo', - }, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - optionalNumberProperty: undefined, - optionalObjectProperty: { - nestedRequiredProperty: {}, - }, - requiredProperty: 'foo', - }, - }, - { - expectedCB: (schema) => - getObjectDefaults( - testValidator, - schema, - { + const: { + RootAndLocalConst: 'FromRoot', + }, + }; + const rawFormData = { + fromFormData: 'fromFormData', + }; + const includeUndefinedValues = false; + const experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior = { + emptyObjectFields: 'skipDefaults', + }; + + test('getDefaultFormState', () => { + expect( + getDefaultFormState( + testValidator, + schema, + rawFormData, + schema, + includeUndefinedValues, + experimental_defaultFormStateBehavior + ) + ).toEqual({ + localConst: 'local', + RootConst: { + attr1: 1, + attr2: true, + }, + RootAndLocalConst: 'FromLocal', + fromFormData: 'fromFormData', + }); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { rootSchema: schema, + rawFormData, + includeUndefinedValues, + experimental_defaultFormStateBehavior, + }) + ).toEqual({ + localConst: 'local', + RootConst: { + attr1: 1, + attr2: true, }, - { foo: 'bar' } - ), - toEqual: { - requiredProperty: 'foo', - foo: 'bar', - }, - }, - { - expectedCB: (schema, options) => - getObjectDefaults( - testValidator, - schema, - { + RootAndLocalConst: 'FromLocal', + }); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { rootSchema: schema, - ...options, + rawFormData, + includeUndefinedValues, + experimental_defaultFormStateBehavior, + }) + ).toEqual({ + localConst: 'local', + RootConst: { + attr1: 1, + attr2: true, }, - { foo: 'bar' } - ), - toEqual: { - requiredProperty: 'foo', - foo: 'bar', - }, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - test: { - newKey: { - host: 'localhost', - port: 389, + RootAndLocalConst: 'FromLocal', + }); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults(testValidator, schema, { + rootSchema: schema, + rawFormData, + includeUndefinedValues, + experimental_defaultFormStateBehavior, + }) + ).toEqual({ + localConst: 'local', + RootConst: { + attr1: 1, + attr2: true, }, + RootAndLocalConst: 'FromLocal', + }); + }); + + describe('constAsDefault is never', () => { + const experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior = { + emptyObjectFields: 'skipDefaults', + constAsDefaults: 'never', + }; + + test('getDefaultFormState', () => { + expect( + getDefaultFormState( + testValidator, + schema, + rawFormData, + schema, + includeUndefinedValues, + experimental_defaultFormStateBehavior + ) + ).toEqual({ + fromFormData: 'fromFormData', + }); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + rawFormData, + includeUndefinedValues, + experimental_defaultFormStateBehavior, + }) + ).toEqual({}); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { + rootSchema: schema, + experimental_defaultFormStateBehavior, + }) + ).toEqual({}); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults(testValidator, schema, { + rootSchema: schema, + experimental_defaultFormStateBehavior, + }) + ).toEqual({}); + }); + }); + }); + }); + + describe('an object with non valid formData for enum properties', () => { + beforeEach(() => { + // Mock isValid so that withExactlyOneSubschema works as expected + testValidator.setReturnValues({ + isValid: [false, true], + }); + }); + afterAll(() => { + // Reset the testValidator + if (typeof testValidator.reset === 'function') { + testValidator?.reset(); + } + }); + + const schema: RJSFSchema = { + type: 'object', + properties: { + animal: { + enum: ['Cat', 'Fish'], }, }, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - test: { - newKey: {}, - }, - }, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - nestedObject: { - first: 'no', - second: { - deeplyNestedThird: 'before', - }, + dependencies: { + animal: { + oneOf: [ + { + properties: { + animal: { + enum: ['Cat'], + }, + food: { + type: 'string', + enum: ['meat', 'grass', 'fish'], + default: 'meat', + }, + }, + }, + { + properties: { + animal: { + enum: ['Fish'], + }, + food: { + type: 'string', + enum: ['insect', 'worms'], + default: 'worms', + }, + water: { + type: 'string', + enum: ['lake', 'sea'], + default: 'sea', + }, + }, + }, + ], }, }, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(testValidator, schema, { + }; + const rawFormData = { + animal: 'Fish', + food: 'meat', + water: null, + }; + const shouldMergeDefaultsIntoFormData = true; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, rawFormData, schema)).toEqual({ + animal: 'Fish', + food: 'worms', + water: null, + }); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema) => - getObjectDefaults(testValidator, schema, { + rawFormData, + shouldMergeDefaultsIntoFormData, + }) + ).toEqual({ + animal: 'Fish', + food: 'worms', + water: 'sea', + }); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { rootSchema: schema, - }), - toEqual: {}, - }, - { - expectedCB: (schema) => + rawFormData, + shouldMergeDefaultsIntoFormData, + }) + ).toEqual({ + animal: 'Fish', + }); + }); + + test('getObjectDefaults', () => { + expect( getObjectDefaults(testValidator, schema, { rootSchema: schema, - }), - toEqual: { - value: [undefined], + rawFormData, + shouldMergeDefaultsIntoFormData, + }) + ).toEqual({ + animal: 'Fish', + }); + }); + + describe('mergeDefaultsIntoFormData set to "useDefaultIfFormDataUndefined"', () => { + const experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior = { + mergeDefaultsIntoFormData: 'useDefaultIfFormDataUndefined', + }; + const expected = { + animal: 'Fish', + food: 'worms', + water: 'sea', + }; + + test('getDefaultFormState', () => { + expect( + getDefaultFormState( + testValidator, + schema, + rawFormData, + schema, + undefined, + experimental_defaultFormStateBehavior + ) + ).toEqual(expected); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + rawFormData, + experimental_defaultFormStateBehavior, + shouldMergeDefaultsIntoFormData, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { + rootSchema: schema, + rawFormData, + shouldMergeDefaultsIntoFormData, + experimental_defaultFormStateBehavior, + }) + ).toEqual({ + animal: 'Fish', + }); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults(testValidator, schema, { + rootSchema: schema, + rawFormData, + shouldMergeDefaultsIntoFormData, + experimental_defaultFormStateBehavior, + }) + ).toEqual({ + animal: 'Fish', + }); + }); + }); + }); + + describe('oneOf with const values', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + oneOfField: { + title: 'One Of Field', + type: 'string', + oneOf: [ + { + const: 'username', + title: 'Username and password', + }, + { + const: 'secret', + title: 'SSO', + }, + ], + }, }, - }, - { - expectedCB: (schema) => getObjectDefaults(testValidator, schema), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: { - localConst: 'local', - RootConst: { - attr1: 1, - attr2: true, + required: ['oneOfField'], + }; + + describe('constAsDefaults is always', () => { + const experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior = { + constAsDefaults: 'always', + }; + const expected = { + oneOfField: 'username', + }; + + test('getDefaultFormState', () => { + expect( + getDefaultFormState( + testValidator, + schema, + undefined, + schema, + undefined, + experimental_defaultFormStateBehavior + ) + ).toEqual(expected); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { + rootSchema: schema, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults(testValidator, schema, { + rootSchema: schema, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + }); + + describe('constAsDefaults is skipOneOf', () => { + const experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior = { + constAsDefaults: 'skipOneOf', + }; + const expected = {}; + + test('getDefaultFormState', () => { + expect( + getDefaultFormState( + testValidator, + schema, + undefined, + schema, + undefined, + experimental_defaultFormStateBehavior + ) + ).toEqual(expected); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { + rootSchema: schema, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults(testValidator, schema, { + rootSchema: schema, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + }); + + describe('constAsDefaults is never', () => { + const experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior = { + constAsDefaults: 'never', + }; + const expected = {}; + + test('getDefaultFormState', () => { + expect( + getDefaultFormState( + testValidator, + schema, + undefined, + schema, + undefined, + experimental_defaultFormStateBehavior + ) + ).toEqual(expected); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { + rootSchema: schema, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults(testValidator, schema, { + rootSchema: schema, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + }); + }); + + describe('an object with invalid formData const and constAsDefault set to always', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + stringField: { + type: 'string', + const: 'fromConst', }, - RootAndLocalConst: 'FromLocal', }, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(options.testValidator, schema, { + }; + const rawFormData = { + stringField: 'fromFormData', + }; + const experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior = { + constAsDefaults: 'always', + }; + const expected = { + stringField: 'fromConst', + }; + + test('getDefaultFormState', () => { + expect( + getDefaultFormState( + testValidator, + schema, + rawFormData, + schema, + undefined, + experimental_defaultFormStateBehavior + ) + ).toEqual(expected); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { rootSchema: schema, - ...options, - }), - toEqual: { - animal: 'Fish', - }, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(options.testValidator, schema, { + rawFormData, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { rootSchema: schema, - ...options, - }), - toEqual: { - animal: 'Fish', - }, - }, - { - expectedCB: (schema, options) => + rawFormData, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + + test('getObjectDefaults', () => { + expect( getObjectDefaults(testValidator, schema, { rootSchema: schema, - ...options, - }), - toEqual: { - oneOfField: 'username', + rawFormData, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + }); + }); + + describe('array schemas', () => { + describe('array with defaults with no formData', () => { + const schema: RJSFSchema = { + type: 'array', + minItems: 4, + default: ['Raphael', 'Michaelangelo'], + items: { + type: 'string', + default: 'Unknown', }, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(testValidator, schema, { - rootSchema: schema, - ...options, - }), - toEqual: {}, - }, - { - expectedCB: (schema, options) => - getObjectDefaults(testValidator, schema, { + }; + const includeUndefinedValues = 'excludeObjectChildren'; + const expected = ['Raphael', 'Michaelangelo', 'Unknown', 'Unknown']; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined, schema, includeUndefinedValues)).toEqual( + expected + ); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { rootSchema: schema, - ...options, - }), - toEqual: { - stringField: 'fromConst', - }, - }, - ]); - }); - describe('getArrayDefaults()', () => { - // test array defaults - testArrayDefault(testValidator, [ - { - expectedCB: (schema, options) => - getArrayDefaults( + includeUndefinedValues, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType( testValidator, schema, { rootSchema: schema, - ...options, + includeUndefinedValues, }, ['Raphael', 'Michaelangelo'] - ), - toEqual: ['Raphael', 'Michaelangelo', 'Unknown', 'Unknown'], - }, - { - expectedCB: (schema, options) => + ) + ).toEqual(expected); + }); + + test('getArrayDefaults', () => { + expect( getArrayDefaults( testValidator, schema, { rootSchema: schema, - ...options, + includeUndefinedValues, }, ['Raphael', 'Michaelangelo'] - ), - toEqual: ['Raphael', 'Michaelangelo', 'Unknown', 'Unknown'], - }, - { - expectedCB: (schema, options) => + ) + ).toEqual(expected); + }); + + describe('with empty array as formData', () => { + const rawFormData: never[] = []; + const experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior = { + arrayMinItems: { + mergeExtraDefaults: true, + populate: 'all', + }, + }; + + test('getDefaultFormState', () => { + expect( + // NOTE: `rawFormData` is not used L1003 + getDefaultFormState( + testValidator, + schema, + undefined, + schema, + includeUndefinedValues, + experimental_defaultFormStateBehavior + ) + ).toEqual(expected); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + rawFormData, + includeUndefinedValues, + experimental_defaultFormStateBehavior, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType( + testValidator, + schema, + { + rootSchema: schema, + rawFormData, + includeUndefinedValues, + experimental_defaultFormStateBehavior, + }, + ['Raphael', 'Michaelangelo'] + ) + ).toEqual(expected); + }); + + test('getArrayDefaults', () => { + expect( + getArrayDefaults( + testValidator, + schema, + { + rootSchema: schema, + rawFormData, + includeUndefinedValues, + experimental_defaultFormStateBehavior, + }, + ['Raphael', 'Michaelangelo'] + ) + ).toEqual(expected); + }); + }); + }); + + describe('array with no defaults', () => { + const schema: RJSFSchema = { + type: 'array', + minItems: 4, + items: { + type: 'string', + }, + }; + const includeUndefinedValues = 'excludeObjectChildren'; + const expected: undefined[] = [undefined, undefined, undefined, undefined]; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined, schema, includeUndefinedValues)).toEqual( + expected + ); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + includeUndefinedValues, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { + rootSchema: schema, + includeUndefinedValues, + }) + ).toEqual(expected); + }); + + test('getArrayDefaults', () => { + expect( getArrayDefaults(testValidator, schema, { rootSchema: schema, - ...options, - }), - toEqual: [], - }, - { - expectedCB: (schema, options) => + includeUndefinedValues, + }) + ).toEqual(expected); + }); + }); + + describe('array const value populate as defaults', () => { + const schema: RJSFSchema = { + type: 'array', + minItems: 4, + const: ['ConstFromRoot', 'ConstFromRoot'], + items: { + type: 'string', + const: 'Constant', + }, + }; + const includeUndefinedValues = 'excludeObjectChildren'; + const expected = ['ConstFromRoot', 'ConstFromRoot', 'Constant', 'Constant']; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined, schema, includeUndefinedValues)).toEqual( + expected + ); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + includeUndefinedValues, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType( + testValidator, + schema, + { + rootSchema: schema, + includeUndefinedValues, + }, + ['ConstFromRoot', 'ConstFromRoot'] + ) + ).toEqual(expected); + }); + + test('getArrayDefaults', () => { + expect( getArrayDefaults( testValidator, schema, { rootSchema: schema, - ...options, + includeUndefinedValues, }, ['ConstFromRoot', 'ConstFromRoot'] - ), - toEqual: ['ConstFromRoot', 'ConstFromRoot', 'Constant', 'Constant'], - }, - { - expectedCB: (schema, options) => - getArrayDefaults(testValidator, schema, { + ) + ).toEqual(expected); + }); + }); + + describe('an invalid array schema', () => { + const schema: RJSFSchema = { + type: 'array', + items: 'not a valid item value', + } as RJSFSchema; + const includeUndefinedValues = 'excludeObjectChildren'; + const expected: never[] = []; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema, undefined, schema, includeUndefinedValues)).toEqual( + expected + ); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { rootSchema: schema, - ...options, - }), - toEqual: [], - }, - { - expectedCB: (schema) => getArrayDefaults(testValidator, schema), - toEqual: [], - }, - ]); + includeUndefinedValues, + }) + ).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { rootSchema: schema, includeUndefinedValues }) + ).toEqual(expected); + }); + + test('getArrayDefaults', () => { + expect(getArrayDefaults(testValidator, schema, { rootSchema: schema, includeUndefinedValues })).toEqual( + expected + ); + }); + }); + + describe('simple schema and no optional args', () => { + const schema: RJSFSchema = { type: 'array' }; + const expected: never[] = []; + + test('getDefaultFormState', () => { + expect(getDefaultFormState(testValidator, schema)).toEqual(expected); + }); + + test('computeDefaults', () => { + expect(computeDefaults(testValidator, schema)).toEqual(expected); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect(getDefaultBasedOnSchemaType(testValidator, schema)).toEqual(expected); + }); + + test('getArrayDefaults', () => { + expect(getArrayDefaults(testValidator, schema)).toEqual(expected); + }); + }); + }); + + it('getInnerSchemaForArrayItem() item of type boolean returns empty schema', () => { + expect(getInnerSchemaForArrayItem({ items: [true] }, AdditionalItemsHandling.Ignore, 0)).toEqual({}); + }); + describe('resolveDependencies()', () => { + it('test an object with dependencies', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + first: { + type: 'string', + enum: ['no', 'yes'], + default: 'no', + }, + }, + dependencies: { + first: { + oneOf: [ + { + properties: { + first: { + enum: ['yes'], + }, + second: { + type: 'object', + properties: { + deeplyNestedThird: { + type: 'string', + enum: ['before', 'after'], + default: 'before', + }, + }, + }, + }, + }, + { + properties: { + first: { + enum: ['no'], + }, + }, + }, + ], + }, + }, + }; + + // Mock isValid so that withExactlyOneSubschema works as expected + testValidator.setReturnValues({ + isValid: [ + true, // First oneOf... first === first + false, // Second oneOf... second !== first + ], + }); + expect( + resolveDependencies( + testValidator, + schema, + schema, + false, + [], + { + first: 'yes', + }, + undefined + ) + ).toEqual([ + { + type: 'object', + properties: { + first: { + type: 'string', + enum: ['no', 'yes'], + default: 'no', + }, + second: { + type: 'object', + properties: { + deeplyNestedThird: { + type: 'string', + enum: ['before', 'after'], + default: 'before', + }, + }, + }, + }, + }, + ]); + }); }); + describe('getValidFormData', () => { let schema: RJSFSchema; it('Test schema with non valid formData for enum property', () => { diff --git a/packages/utils/test/schema/types.ts b/packages/utils/test/schema/types.ts index 1c74a9d074..02136856bc 100644 --- a/packages/utils/test/schema/types.ts +++ b/packages/utils/test/schema/types.ts @@ -1,4 +1,4 @@ -import { RJSFSchema, RJSFValidationError, ValidationData, ValidatorType } from '../../src'; +import { RJSFValidationError, ValidationData, ValidatorType } from '../../src'; export interface TestValidatorParams { isValid?: boolean[]; @@ -6,12 +6,6 @@ export interface TestValidatorParams { errorList?: RJSFValidationError[][]; } -export interface IExpectType { - // eslint-disable-next-line no-unused-vars - expectedCB: (schema: RJSFSchema, options?: any) => unknown; - toEqual: any; -} - export interface TestValidatorType extends ValidatorType { // eslint-disable-next-line no-unused-vars setReturnValues(params?: TestValidatorParams): void;