🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@sjsf/form

Package Overview
Dependencies
Maintainers
1
Versions
88
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sjsf/form - npm Package Compare versions

Comparing version
0.0.2
to
0.0.3
LICENSE-APACHE

Sorry, the diff of this file is not supported yet

+0
-2
import FormComponent from "./form-component.svelte";
import ButtonComponent from "./button-component.svelte";
import LayoutComponent from "./layout-component.svelte";
import AlertComponent from "./alert-component.svelte";
import TitleComponent from "./title-component.svelte";

@@ -13,3 +12,2 @@ import DescriptionComponent from "./description-component.svelte";

layout: LayoutComponent,
alert: AlertComponent,
title: TitleComponent,

@@ -16,0 +14,0 @@ description: DescriptionComponent,

@@ -56,10 +56,2 @@ import type { Snippet, Component as SvelteComponent } from "svelte";

}
export interface AlertType {
error: {};
}
export interface AlertComponentProps {
type: keyof AlertType;
title?: string;
children: Snippet;
}
export interface ParentTemplateType {

@@ -90,3 +82,2 @@ field: {};

layout: LayoutComponentProps;
alert: AlertComponentProps;
title: TitleComponentProps;

@@ -101,3 +92,2 @@ description: DescriptionComponentProps;

layout: "";
alert: "";
title: "";

@@ -104,0 +94,0 @@ description: "";

@@ -17,2 +17,2 @@ <script lang="ts" module>

<p style="color: red;">{message}</p>
<pre style="color: red;">{message}</pre>

@@ -7,2 +7,3 @@ <script lang="ts">

import { isDisabledOrReadonly } from "../../is-disabled-or-readonly.js";
import ErrorMessage from "../../error-message.svelte";

@@ -63,5 +64,2 @@ import { getField, type FieldProps } from "../model.js";

const [arrayField, field] = $derived.by(() => {
if (config.schema.items === undefined) {
return ["unsupportedArray", undefined] as const;
}
if (isMultiSelect(ctx, config.schema)) {

@@ -85,2 +83,6 @@ return ["anotherFieldArray", "enum"] as const;

<ArrayField bind:value {config} {field} />
{#if config.schema.items === undefined}
<ErrorMessage message={ctx.translation("array-schema-missing-items")} />
{:else}
<ArrayField bind:value {config} {field} />
{/if}
export * from './context.js';
export { default as UnsupportedArrayField } from './unsupported-array-field.svelte';
export { default as AnotherFieldArrayField } from './another-field-array-field.svelte';

@@ -4,0 +3,0 @@ export { default as FixedArrayField } from './fixed-array-field.svelte';

export * from './context.js';
export { default as UnsupportedArrayField } from './unsupported-array-field.svelte';
export { default as AnotherFieldArrayField } from './another-field-array-field.svelte';

@@ -4,0 +3,0 @@ export { default as FixedArrayField } from './fixed-array-field.svelte';

@@ -1,2 +0,2 @@

import { ArrayField, AnotherFieldArrayField, NormalArrayField, FixedArrayField, UnsupportedArrayField, ArrayItemField, } from "./array/index.js";
import { ArrayField, AnotherFieldArrayField, NormalArrayField, FixedArrayField, ArrayItemField, } from "./array/index.js";
import { ObjectField, ObjectPropertyField } from "./object/index.js";

@@ -9,3 +9,2 @@ import RootField from "./root-field.svelte";

import BooleanField from "./boolean-field.svelte";
import UnsupportedField from "./unsupported-field.svelte";
import EnumField from "./enum-field.svelte";

@@ -24,3 +23,2 @@ import HiddenField from "./hidden-field.svelte";

boolean: BooleanField,
unsupported: UnsupportedField,
string: StringField,

@@ -30,3 +28,2 @@ object: ObjectField,

array: ArrayField,
unsupportedArray: UnsupportedArrayField,
anotherFieldArray: AnotherFieldArrayField,

@@ -33,0 +30,0 @@ fixedArray: FixedArrayField,

@@ -25,3 +25,2 @@ import type { Component as SvelteComponent } from "svelte";

array: FieldCommonProps<V>;
unsupportedArray: FieldCommonProps<V>;
anotherFieldArray: FieldCommonProps<V> & {

@@ -47,3 +46,2 @@ field: "enum" | "file";

hidden: FieldCommonProps<V>;
unsupported: FieldCommonProps<V>;
}

@@ -60,3 +58,2 @@ export interface FieldBindings {

array: "value";
unsupportedArray: "value";
anotherFieldArray: "value";

@@ -70,3 +67,2 @@ fixedArray: "value";

hidden: "value";
unsupported: "value";
}

@@ -83,3 +79,2 @@ export interface FieldValue {

array: SchemaArrayValue;
unsupportedArray: SchemaArrayValue;
anotherFieldArray: SchemaArrayValue;

@@ -93,3 +88,2 @@ fixedArray: SchemaArrayValue;

hidden: SchemaValue;
unsupported: SchemaValue;
}

@@ -96,0 +90,0 @@ export type FieldType = keyof FieldsAndProps<SchemaValue>;

@@ -15,4 +15,3 @@ import { createMessage } from "../error-message.svelte";

return (getFieldInternal(ctx, type, config) ??
ctx.fields("unsupported", config) ??
createMessage(`Field "${config.uiSchema["ui:field"] ?? type}" not found`));
}
export interface Labels {
submit: [];
"unsupported-field-type": [type: string];
"array-schema-missing-items": [];

@@ -5,0 +4,0 @@ yes: [];

import { createTranslation } from "../form/translation.js";
export const translation = createTranslation({
submit: "Submit",
"unsupported-field-type": (type) => `Unsupported field type: ${type}`,
"array-schema-missing-items": "Missing items definition",

@@ -6,0 +5,0 @@ yes: "Yes",

import { createTranslation } from "../form/translation.js";
export const translation = createTranslation({
submit: "Продолжить",
"unsupported-field-type": (type) => `Неподдерживаемый тип поля: ${type}`,
"array-schema-missing-items": "Отсутствует опция `items`",

@@ -6,0 +5,0 @@ yes: "Да",

@@ -8,1 +8,3 @@ Copyright (c) 2024 Roman Krasilnikov

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Portions of this project are licensed under the Apache License, Version 2.0. See the LICENSE-APACHE file for more details.
{
"name": "@sjsf/form",
"version": "0.0.2",
"version": "0.0.3",
"description": "Svelte 5 library for creating forms based on JSON schema.",

@@ -13,3 +13,6 @@ "license": "(MIT AND Apache-2.0)",

"files": [
"dist"
"dist",
"LICENSE-APACHE",
"!dist/**/*.test.*",
"!dist/**/*.spec.*"
],

@@ -16,0 +19,0 @@ "publishConfig": {

@@ -67,3 +67,3 @@ # @sjsf/form

See [LICENSE-MIT](LICENSE-MIT) and [LICENSE-APACHE](LICENSE-APACHE) for details.
See [LICENSE-MIT](LICENSE) and [LICENSE-APACHE](LICENSE-APACHE) for details.

@@ -70,0 +70,0 @@ ## See also

<script lang="ts">
import type { ComponentProps } from '../../form/index.js';
const { title, children }: ComponentProps<"alert"> = $props();
</script>
<div>
{#if title}
<h3>{title}</h3>
{/if}
<div>
<pre style="width: 0"><code>{@render children?.()}</code></pre>
</div>
</div>
import type { ComponentProps } from '../../form/index.js';
declare const AlertComponent: import("svelte").Component<ComponentProps<"alert">, {}, "">;
export default AlertComponent;

Sorry, the diff of this file is too big to display

// This file was copied and modified from https://github.com/rjsf-team/react-jsonschema-form/blob/f4229bf6e067d31b24de3ef9d3ca754ee52529ac/packages/utils/test/findSchemaDefinition.test.ts
// Licensed under the Apache License, Version 2.0.
// Modifications made by Roman Krasilnikov.
import { describe, expect, it } from 'vitest';
import { findSchemaDefinition } from './definitions.js';
// import { findSchemaDefinitionRecursive } from './definitions';
const schema = {
type: 'object',
definitions: {
stringRef: {
type: 'string',
},
nestedRef: {
$ref: '#/definitions/stringRef',
},
extraNestedRef: {
$ref: '#/definitions/stringRef',
title: 'foo',
},
// Reference accidentally pointing to itself.
badCircularNestedRef: {
$ref: '#/definitions/badCircularNestedRef',
},
// Reference accidentally pointing to a chain of references which ultimately
// point back to the original reference.
badCircularDeepNestedRef: {
$ref: '#/definitions/badCircularDeeperNestedRef',
},
badCircularDeeperNestedRef: {
$ref: '#/definitions/badCircularDeepestNestedRef',
},
badCircularDeepestNestedRef: {
$ref: '#/definitions/badCircularDeepNestedRef',
},
},
};
const EXTRA_EXPECTED = { type: 'string', title: 'foo' };
describe('findSchemaDefinition()', () => {
it('throws error when ref is malformed', () => {
expect(() => findSchemaDefinition('definitions/missing', {})).toThrowError('Invalid reference: definitions/missing');
});
it('throws error when ref does not exist', () => {
expect(() => findSchemaDefinition('#/definitions/missing', schema)).toThrowError('Could not find a definition for #/definitions/missing');
});
it('returns the string ref from its definition', () => {
expect(findSchemaDefinition('#/definitions/stringRef', schema)).toBe(schema.definitions.stringRef);
});
it('returns the string ref from its nested definition', () => {
expect(findSchemaDefinition('#/definitions/nestedRef', schema)).toBe(schema.definitions.stringRef);
});
it('returns a combined schema made from its nested definition with the extra props', () => {
expect(findSchemaDefinition('#/definitions/extraNestedRef', schema)).toEqual(EXTRA_EXPECTED);
});
it('throws error when ref is a circular reference', () => {
expect(() => findSchemaDefinition('#/definitions/badCircularNestedRef', schema)).toThrowError('Definition for #/definitions/badCircularNestedRef is a circular reference');
});
it('throws error when ref is a deep circular reference', () => {
expect(() => findSchemaDefinition('#/definitions/badCircularDeepNestedRef', schema)).toThrowError('Definition for #/definitions/badCircularDeepNestedRef contains a circular reference through #/definitions/badCircularDeepNestedRef -> #/definitions/badCircularDeeperNestedRef -> #/definitions/badCircularDeepestNestedRef -> #/definitions/badCircularDeepNestedRef');
});
});
// describe('findSchemaDefinitionRecursive()', () => {
// it('throws error when ref is missing', () => {
// expect(() => findSchemaDefinitionRecursive()).toThrowError('Could not find a definition for undefined');
// });
// it('throws error when ref is malformed', () => {
// expect(() => findSchemaDefinitionRecursive('definitions/missing')).toThrowError(
// 'Could not find a definition for definitions/missing'
// );
// });
// it('throws error when ref does not exist', () => {
// expect(() => findSchemaDefinitionRecursive('#/definitions/missing', schema)).toThrowError(
// 'Could not find a definition for #/definitions/missing'
// );
// });
// it('returns the string ref from its definition', () => {
// expect(findSchemaDefinitionRecursive('#/definitions/stringRef', schema)).toBe(schema.definitions!.stringRef);
// });
// it('returns the string ref from its nested definition', () => {
// expect(findSchemaDefinitionRecursive('#/definitions/nestedRef', schema)).toBe(schema.definitions!.stringRef);
// });
// it('returns a combined schema made from its nested definition with the extra props', () => {
// expect(findSchemaDefinitionRecursive('#/definitions/extraNestedRef', schema)).toEqual(EXTRA_EXPECTED);
// });
// it('throws error when ref is a circular reference', () => {
// expect(() => findSchemaDefinitionRecursive('#/definitions/badCircularNestedRef', schema)).toThrowError(
// 'Definition for #/definitions/badCircularNestedRef is a circular reference'
// );
// });
// it('throws error when ref is a deep circular reference', () => {
// expect(() => findSchemaDefinitionRecursive('#/definitions/badCircularDeepNestedRef', schema)).toThrowError(
// 'Definition for #/definitions/badCircularDeepNestedRef contains a circular reference through #/definitions/badCircularDeeperNestedRef -> #/definitions/badCircularDeepestNestedRef -> #/definitions/badCircularDeepNestedRef'
// );
// });
// });
// This file was copied and modified from https://github.com/rjsf-team/react-jsonschema-form/blob/f4229bf6e067d31b24de3ef9d3ca754ee52529ac/packages/utils/test/getOptionMatchingSimpleDiscriminator.test.ts and https://github.com/rjsf-team/react-jsonschema-form/blob/f4229bf6e067d31b24de3ef9d3ca754ee52529ac/packages/utils/test/getDiscriminatorFieldFromSchema.test.ts
// Licensed under the Apache License, Version 2.0.
// Modifications made by Roman Krasilnikov.
import { afterAll, beforeAll, describe, expect, it, test, vi } from 'vitest';
import { getDiscriminatorFieldFromSchema, getOptionMatchingSimpleDiscriminator } from './discriminator.js';
const PROPERTY_NAME = 'testProp';
// @ts-expect-error
const BAD_DISCRIMINATOR = { discriminator: { propertyName: 5 } };
// @ts-expect-error
const GOOD_DISCRIMINATOR = { discriminator: { propertyName: PROPERTY_NAME } };
describe('getDiscriminatorFieldFromSchema()', () => {
it('returns undefined when no discriminator is present', () => {
expect(getDiscriminatorFieldFromSchema({})).toBeUndefined();
});
it('returns the propertyName when discriminator is present', () => {
expect(getDiscriminatorFieldFromSchema(GOOD_DISCRIMINATOR)).toEqual(PROPERTY_NAME);
});
describe('bad discriminator', () => {
let consoleWarnSpy;
beforeAll(() => {
consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => { }); // mock this to avoid actually warning in the tests
});
afterAll(() => {
consoleWarnSpy.mockRestore();
});
it('returns undefined when discriminator is present, but not a string', () => {
expect(getDiscriminatorFieldFromSchema(BAD_DISCRIMINATOR)).toBeUndefined();
});
it('it also warns about the bad discriminator', () => {
expect(consoleWarnSpy).toHaveBeenCalledWith('Expecting discriminator to be a string, got "number" instead');
});
});
});
describe('getOptionMatchingSimpleDiscriminator()', () => {
describe('returns undefined if no option matches discriminator', () => {
test('no options with no data', () => {
expect(getOptionMatchingSimpleDiscriminator({}, [], 'id')).toEqual(undefined);
});
test('no options with data', () => {
expect(getOptionMatchingSimpleDiscriminator({ foo: 'foo' }, [], 'id')).toEqual(undefined);
});
test('options with no data', () => {
expect(getOptionMatchingSimpleDiscriminator({}, [{ type: 'object', properties: { foo: { const: 'foo' } } }], 'id')).toEqual(undefined);
});
test('matching property, but no discriminatorField', () => {
expect(getOptionMatchingSimpleDiscriminator({ foo: 'foo' }, [
{ type: 'object', properties: { foo: { const: 'foo' } } },
])).toEqual(undefined);
});
test('matching property different from discriminatorField', () => {
expect(getOptionMatchingSimpleDiscriminator({ foo: 'foo' }, [{ type: 'object', properties: { foo: { const: 'foo' } } }], 'bar')).toEqual(undefined);
});
});
describe('returns option index if option matches discriminator', () => {
test('const discriminator', () => {
expect(getOptionMatchingSimpleDiscriminator({ foo: 'foo' }, [{}, { type: 'object', properties: { foo: { const: 'foo' } } }], 'foo')).toEqual(1);
});
test('enum discriminator', () => {
expect(getOptionMatchingSimpleDiscriminator({ foo: 'foo' }, [{}, { type: 'object', properties: { foo: { enum: ['bar', 'foo'] } } }], 'foo')).toEqual(1);
});
});
describe('unsupported (non-simple) discriminator returns undefined', () => {
test('object discriminator', () => {
expect(getOptionMatchingSimpleDiscriminator({ foo: 'foo' }, [{}, { type: 'object', properties: { foo: { type: 'object' } } }], 'foo')).toEqual(undefined);
});
test('array discriminator', () => {
expect(getOptionMatchingSimpleDiscriminator({ foo: 'foo' }, [{}, { type: 'object', properties: { foo: { type: 'array' } } }], 'foo')).toEqual(undefined);
});
});
});
// This file was copied and modified from https://github.com/rjsf-team/react-jsonschema-form/blob/f4229bf6e067d31b24de3ef9d3ca754ee52529ac/packages/utils/test/schema/isSelectTest.ts and https://github.com/rjsf-team/react-jsonschema-form/blob/f4229bf6e067d31b24de3ef9d3ca754ee52529ac/packages/utils/test/schema/isMultiSelectTest.ts
// Licensed under the Apache License, Version 2.0.
// Modifications made by Roman Krasilnikov.
import { beforeEach, describe, it, expect } from "vitest";
import { isMultiSelect, isSelect } from "./is-select.js";
import { makeTestValidator } from "./test-validator.js";
let testValidator;
beforeEach(() => {
testValidator = makeTestValidator();
});
describe("isSelect()", () => {
it("should be false if items is undefined", () => {
const schema = {};
expect(isSelect(testValidator, schema, schema)).toBe(false);
});
describe("schema items enum is not an array", () => {
it("should be false if oneOf/anyOf schemas are not all constants", () => {
const schema = {
anyOf: [{ type: "string", enum: ["Foo"] }, { type: "string" }],
};
expect(isSelect(testValidator, schema, schema)).toBe(false);
});
it("should be true if oneOf/anyOf schemas are all constants", () => {
const schema = {
oneOf: [
{ type: "string", enum: ["Foo"] },
{ type: "string", enum: ["Foo"] },
],
};
expect(isSelect(testValidator, schema, schema)).toBe(true);
});
});
it("should retrieve reference schema definitions", () => {
const schema = {
definitions: {
FooItem: { type: "string", enum: ["foo"] },
},
$ref: "#/definitions/FooItem",
};
expect(isSelect(testValidator, schema, schema)).toBe(true);
});
});
describe("isMultiSelect()", () => {
describe("uniqueItems is true", () => {
describe("schema items enum is an array", () => {
it("should be true", () => {
const schema = {
items: { enum: ["foo", "bar"] },
uniqueItems: true,
};
expect(isMultiSelect(testValidator, schema, schema)).toBe(true);
});
});
it("should be false if items is undefined", () => {
const schema = {};
expect(isMultiSelect(testValidator, schema, schema)).toBe(false);
});
describe("schema items enum is not an array", () => {
it("should be false if oneOf/anyOf is not in items schema", () => {
const schema = { items: {}, uniqueItems: true };
expect(isMultiSelect(testValidator, schema, schema)).toBe(false);
});
it("should be false if oneOf/anyOf schemas are not all constants", () => {
const schema = {
items: {
oneOf: [{ type: "string", enum: ["Foo"] }, { type: "string" }],
},
uniqueItems: true,
};
expect(isMultiSelect(testValidator, schema, schema)).toBe(false);
});
it("should be true if oneOf/anyOf schemas are all constants", () => {
const schema = {
items: {
oneOf: [
{ type: "string", enum: ["Foo"] },
{ type: "string", enum: ["Foo"] },
],
},
uniqueItems: true,
};
expect(isMultiSelect(testValidator, schema, schema)).toBe(true);
});
});
it("should retrieve reference schema definitions", () => {
const schema = {
definitions: {
FooItem: { type: "string", enum: ["foo"] },
},
items: { $ref: "#/definitions/FooItem" },
uniqueItems: true,
};
expect(isMultiSelect(testValidator, schema, schema)).toBe(true);
});
});
it("should be false if uniqueItems is false", () => {
const schema = {
items: { enum: ["foo", "bar"] },
uniqueItems: false,
};
expect(isMultiSelect(testValidator, schema, schema)).toBe(false);
});
});
// This file was copied and modified from https://github.com/rjsf-team/react-jsonschema-form/blob/f4229bf6e067d31b24de3ef9d3ca754ee52529ac/packages/utils/test/mergeObjects.test.ts and https://github.com/rjsf-team/react-jsonschema-form/blob/f4229bf6e067d31b24de3ef9d3ca754ee52529ac/packages/utils/test/mergeSchemas.test.ts
// Licensed under the Apache License, Version 2.0.
// Modifications made by Roman Krasilnikov.
import { describe, expect, it } from "vitest";
import { mergeSchemaObjects, mergeSchemas } from "./merge.js";
describe("mergeSchemas()", () => {
it("shouldn`t mutate the provided objects", () => {
const obj1 = { const: 1 };
mergeSchemas(obj1, { const: 2 });
expect(obj1).toEqual({ const: 1 });
});
it("should merge two one-level deep objects", () => {
expect(mergeSchemas({ const: 1 }, { examples: 2 })).toEqual({
const: 1,
examples: 2,
});
});
it("should override the first object with the values from the second", () => {
expect(mergeSchemas({ const: 1 }, { const: 2 })).toEqual({ const: 2 });
});
// it('should override non-existing values of the first object with the values from the second', () => {
// expect(mergeSchemas({ properties: { b: undefined } }, { a: { b: { c: 1 } } })).toEqual({ a: { b: { c: 1 } } });
// });
// it('should recursively merge deeply nested objects', () => {
// const obj1 = {
// a: 1,
// b: {
// c: 3,
// d: [1, 2, 3],
// e: { f: { g: 1 } },
// },
// c: 2,
// };
// const obj2 = {
// a: 1,
// b: {
// d: [3, 2, 1],
// e: { f: { h: 2 } },
// g: 1,
// },
// c: 3,
// };
// const expected = {
// a: 1,
// b: {
// c: 3,
// d: [3, 2, 1],
// e: { f: { g: 1, h: 2 } },
// g: 1,
// },
// c: 3,
// };
// expect(mergeSchemas(obj1, obj2)).toEqual(expected);
// });
// it('should recursively merge File objects', () => {
// const file = new File(['test'], 'test.txt');
// const obj1 = {
// a: {},
// };
// const obj2 = {
// a: file,
// };
// expect(mergeSchemas(obj1, obj2).a).toBeInstanceOf(File);
// });
describe("arrays", () => {
it("should not concat arrays", () => {
const obj1 = { enum: [1] };
const obj2 = { enum: [2] };
expect(mergeSchemas(obj1, obj2)).toEqual({ enum: [2] });
});
it("should concat arrays under `required` keyword", () => {
const obj1 = { type: "object", required: ["1"] };
const obj2 = { type: "object", required: ["2"] };
expect(mergeSchemas(obj1, obj2)).toEqual({
type: "object",
required: ["1", "2"],
});
});
it("should concat arrays under `required` keyword when one of the schemas is an object type", () => {
const obj1 = { type: "object", required: ["1"] };
const obj2 = { required: ["2"] };
expect(mergeSchemas(obj1, obj2)).toEqual({
type: "object",
required: ["1", "2"],
});
});
it("should concat nested arrays under `required` keyword", () => {
const obj1 = { items: { type: "object", required: ["1"] } };
const obj2 = { items: { type: "object", required: ["2"] } };
expect(mergeSchemas(obj1, obj2)).toEqual({
items: { type: "object", required: ["1", "2"] },
});
});
it("should not include duplicate values when concatting arrays under `required` keyword", () => {
const obj1 = { type: "object", required: ["1"] };
const obj2 = { type: "object", required: ["1"] };
expect(mergeSchemas(obj1, obj2)).toEqual({
type: "object",
required: ["1"],
});
});
it("should not concat arrays under `required` keyword that are not under an object type", () => {
const obj1 = { dependencies: { required: ["1"] } };
const obj2 = { dependencies: { required: ["2"] } };
expect(mergeSchemas(obj1, obj2)).toEqual({
dependencies: { required: ["2"] },
});
});
});
});
describe("mergeSchemaObjects()", () => {
it("shouldn`t mutate the provided objects", () => {
const obj1 = { a: 1 };
mergeSchemaObjects(obj1, { b: 2 });
expect(obj1).toEqual({ a: 1 });
});
it("should merge two one-level deep objects", () => {
expect(mergeSchemaObjects({ a: 1 }, { b: 2 })).toEqual({ a: 1, b: 2 });
});
it("should override the first object with the values from the second", () => {
expect(mergeSchemaObjects({ a: 1 }, { a: 2 })).toEqual({ a: 2 });
});
it("should override non-existing values of the first object with the values from the second", () => {
expect(mergeSchemaObjects({ a: { b: undefined } }, { a: { b: { c: 1 } } })).toEqual({ a: { b: { c: 1 } } });
});
it("should recursively merge deeply nested objects", () => {
const obj1 = {
a: 1,
b: {
c: 3,
d: [1, 2, 3],
e: { f: { g: 1 } },
},
c: 2,
};
const obj2 = {
a: 1,
b: {
d: [3, 2, 1],
e: { f: { h: 2 } },
g: 1,
},
c: 3,
};
const expected = {
a: 1,
b: {
c: 3,
d: [3, 2, 1],
e: { f: { g: 1, h: 2 } },
g: 1,
},
c: 3,
};
expect(mergeSchemaObjects(obj1, obj2)).toEqual(expected);
});
// NOTE: File is not supported
// it('should recursively merge File objects', () => {
// const file = new File(['test'], 'test.txt');
// const obj1 = {
// a: {},
// };
// const obj2 = {
// a: file,
// };
// expect(mergeSchemaObjects(obj1, obj2).a).toBeInstanceOf(File);
// });
describe("concatArrays option", () => {
it("should not concat arrays by default", () => {
const obj1 = { a: [1] };
const obj2 = { a: [2] };
expect(mergeSchemaObjects(obj1, obj2)).toEqual({ a: [2] });
});
it("should concat arrays when concatArrays is true", () => {
const obj1 = { a: [1] };
const obj2 = { a: [2] };
expect(mergeSchemaObjects(obj1, obj2, true)).toEqual({ a: [1, 2] });
});
it("should concat nested arrays when concatArrays is true", () => {
const obj1 = { a: { b: [1] } };
const obj2 = { a: { b: [2] } };
expect(mergeSchemaObjects(obj1, obj2, true)).toEqual({
a: { b: [1, 2] },
});
});
it("should not concat duplicate values in arrays when concatArrays is 'preventDuplicates'", () => {
const obj1 = { a: [1] };
const obj2 = { a: [1, 2] };
expect(mergeSchemaObjects(obj1, obj2, "preventDuplicates")).toEqual({
a: [1, 2],
});
});
it("should not concat duplicate values in nested arrays when concatArrays is 'preventDuplicates'", () => {
const obj1 = { a: { b: [1] } };
const obj2 = { a: { b: [1, 2] } };
expect(mergeSchemaObjects(obj1, obj2, "preventDuplicates")).toEqual({
a: { b: [1, 2] },
});
});
});
});
// This file was copied and modified from https://github.com/rjsf-team/react-jsonschema-form/blob/f4229bf6e067d31b24de3ef9d3ca754ee52529ac/packages/utils/test/orderProperties.test.ts
// Licensed under the Apache License, Version 2.0.
// Modifications made by Roman Krasilnikov.
import { describe, expect, it } from 'vitest';
import { orderProperties } from './order-properties.js';
function fix(keys) {
return Object.fromEntries(keys.map((key) => [key, {}]));
}
describe('orderProperties()', () => {
it('returns properties when order array is not specified', () => {
const keys = ['foo', 'baz'];
const properties = fix(keys);
expect(orderProperties(properties, undefined)).toEqual(keys);
});
it('should remove from order elements that are not in properties', () => {
const properties = fix(['foo', 'baz']);
const order = ['foo', 'bar', 'baz', 'qux'];
expect(orderProperties(properties, order)).toEqual(['foo', 'baz']);
});
it('should order properties according to the order', () => {
const properties = fix(['bar', 'foo']);
const order = ['foo', 'bar'];
expect(orderProperties(properties, order)).toEqual(['foo', 'bar']);
});
it('should replace * with properties that are absent in order', () => {
const properties = fix(['foo', 'bar', 'baz']);
const order = ['*', 'foo'];
expect(orderProperties(properties, order)).toEqual(['bar', 'baz', 'foo']);
});
it('should handle more complex ordering case correctly', () => {
const properties = fix(['foo', 'baz', 'qux', 'bar']);
const order = ['quux', 'foo', '*', 'corge', 'baz'];
expect(orderProperties(properties, order)).toEqual(['foo', 'qux', 'bar', 'baz']);
});
it('throws error when * is missing and there is one more prop than ordered', () => {
const properties = fix(['foo', 'bar', 'baz']);
const order = ['foo', 'baz'];
expect(() => orderProperties(properties, order)).toThrowError("uiSchema order list does not contain property 'bar'");
});
it('throws error when * is missing and there are a few more props than ordered', () => {
const properties = fix(['foo', 'bar', 'baz']);
const order = ['foo'];
expect(() => orderProperties(properties, order)).toThrowError("uiSchema order list does not contain properties 'bar', 'baz'");
});
it('throws error when there are multiple *s in order', () => {
const properties = fix(['foo', 'bar', 'baz']);
const order = ['*', 'foo', '*'];
expect(() => orderProperties(properties, order)).toThrowError('uiSchema order list contains more than one wildcard item');
});
});
// This file was copied and modified from https://github.com/rjsf-team/react-jsonschema-form/blob/f4229bf6e067d31b24de3ef9d3ca754ee52529ac/packages/utils/test/withIdRef.test.ts
// Licensed under the Apache License, Version 2.0.
// Modifications made by Roman Krasilnikov.
import { describe, expect, it } from 'vitest';
import { ROOT_SCHEMA_PREFIX } from './schema.js';
import { prefixSchemaRefs } from './prefix-schema-refs.js';
describe('prefixSchemaRefs()', () => {
it('should recursively add id prefix to all refs', () => {
const schema = {
anyOf: [{ $ref: '#/defs/foo' }],
};
const expected = {
anyOf: [{ $ref: '__sjsf_rootSchema#/defs/foo' }],
};
expect(prefixSchemaRefs(schema, ROOT_SCHEMA_PREFIX)).toEqual(expected);
});
it('shouldn`t mutate the schema', () => {
const schema = {
anyOf: [{ $ref: '#/defs/foo' }],
};
prefixSchemaRefs(schema, ROOT_SCHEMA_PREFIX);
expect(schema).toEqual({
anyOf: [{ $ref: '#/defs/foo' }],
});
});
it('should not change a property named `$ref`', () => {
const schema = {
title: 'A registration form',
description: 'A simple form example.',
type: 'object',
properties: {
$ref: { type: 'string', title: 'First name', default: 'Chuck' },
},
};
expect(prefixSchemaRefs(schema, ROOT_SCHEMA_PREFIX)).toEqual(schema);
});
// it('should handle null schemaNode', () => {
// const schemaNode = null;
// expect(prefixSchemaRefs(schemaNode, ROOT_SCHEMA_PREFIX)).toEqual(schemaNode);
// });
});
// This file was copied and modified from https://github.com/rjsf-team/react-jsonschema-form/blob/f4229bf6e067d31b24de3ef9d3ca754ee52529ac/packages/utils/test/schema/retrieveSchemaTest.ts
// Licensed under the Apache License, Version 2.0.
// Modifications made by Roman Krasilnikov.
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi, } from "vitest";
import { getValueByPath } from "../lib/object.js";
import { ADDITIONAL_PROPERTY_FLAG, PROPERTIES_KEY, } from "./schema.js";
import { getAllPermutationsOfXxxOf, resolveAnyOrOneOfSchemas, resolveCondition, retrieveSchema, retrieveSchemaInternal, stubExistingAdditionalProperties, withExactlyOneSubSchema, } from "./resolve.js";
import { PROPERTY_DEPENDENCIES, RECURSIVE_REF, RECURSIVE_REF_ALLOF, SCHEMA_DEPENDENCIES, SCHEMA_AND_REQUIRED_DEPENDENCIES, SCHEMA_AND_ONEOF_REF_DEPENDENCIES, SCHEMA_WITH_ONEOF_NESTED_DEPENDENCIES, SCHEMA_WITH_SINGLE_CONDITION, SCHEMA_WITH_MULTIPLE_CONDITIONS, SCHEMA_WITH_NESTED_CONDITIONS, SUPER_SCHEMA, } from "./fixtures/test-data.js";
import { makeTestValidator } from "./test-validator.js";
let testValidator;
beforeEach(() => {
testValidator = makeTestValidator();
});
describe("retrieveSchema()", () => {
let consoleWarnSpy;
beforeAll(() => {
consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => { }); // mock this to avoid actually warning in the tests
});
afterAll(() => {
consoleWarnSpy.mockRestore();
});
afterEach(() => {
consoleWarnSpy.mockClear();
});
it("should `resolve` a schema which contains definitions", () => {
const schema = { $ref: "#/definitions/address" };
const address = {
type: "object",
properties: {
street_address: { type: "string" },
city: { type: "string" },
state: { type: "string" },
},
required: ["street_address", "city", "state"],
};
const rootSchema = { definitions: { address } };
expect(retrieveSchema(testValidator, schema, rootSchema)).toEqual(address);
});
it("should `resolve` a schema which contains definitions not in `#/definitions`", () => {
const address = {
type: "object",
properties: {
street_address: { type: "string" },
city: { type: "string" },
state: { type: "string" },
},
required: ["street_address", "city", "state"],
};
const schema = {
$ref: "#/definitions/address",
definitions: { address },
};
expect(retrieveSchema(testValidator, schema, schema)).toEqual({
definitions: { address },
...address,
});
});
it("should give an error when JSON pointer is not in a URI encoded format", () => {
const address = {
type: "object",
properties: {
street_address: { type: "string" },
city: { type: "string" },
state: { type: "string" },
},
required: ["street_address", "city", "state"],
};
const schema = {
$ref: "/definitions/schemas/address",
definitions: { address },
};
expect(() => retrieveSchema(testValidator, schema, schema)).toThrowError("Invalid reference: ");
});
it("should give an error when JSON pointer does not point to anything", () => {
const schema = {
$ref: "#/definitions/schemas/address",
definitions: { schemas: {} },
};
expect(() => retrieveSchema(testValidator, schema, schema)).toThrowError("Could not find a definition");
});
it("should `resolve` escaped JSON Pointers", () => {
const schema = { $ref: "#/definitions/a~0complex~1name" };
const address = { type: "string" };
const rootSchema = {
definitions: { "a~complex/name": address },
};
expect(retrieveSchema(testValidator, schema, rootSchema)).toEqual(address);
});
it("should `resolve` and stub out a schema which contains an `additionalProperties` with a definition", () => {
const schema = {
type: "object",
additionalProperties: {
$ref: "#/definitions/address",
},
};
const address = {
type: "object",
properties: {
street_address: { type: "string" },
city: { type: "string" },
state: { type: "string" },
},
required: ["street_address", "city", "state"],
};
const rootSchema = { definitions: { address } };
const formData = { newKey: {} };
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
...schema,
properties: {
newKey: {
...address,
[ADDITIONAL_PROPERTY_FLAG]: true,
},
},
});
});
it("should `resolve` and stub out a schema which contains an `additionalProperties` with a type and definition", () => {
const schema = {
type: "string",
additionalProperties: {
$ref: "#/definitions/number",
},
};
const number = {
type: "number",
};
const rootSchema = { definitions: { number } };
const formData = { newKey: {} };
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
...schema,
properties: {
newKey: {
...number,
[ADDITIONAL_PROPERTY_FLAG]: true,
},
},
});
});
it("should `resolve` and stub out a schema which contains an `additionalProperties` with oneOf", () => {
const oneOf = [
{
type: "string",
},
{
type: "number",
},
];
const schema = {
additionalProperties: {
oneOf,
},
type: "object",
};
const formData = { newKey: {} };
expect(retrieveSchema(testValidator, schema, {}, formData)).toEqual({
...schema,
properties: {
newKey: {
type: "object",
oneOf,
[ADDITIONAL_PROPERTY_FLAG]: true,
},
},
});
});
it("should `resolve` and stub out a schema which contains an `additionalProperties` with anyOf", () => {
const anyOf = [
{
type: "string",
},
{
type: "number",
},
];
const schema = {
additionalProperties: {
anyOf,
},
type: "object",
};
const formData = { newKey: {} };
expect(retrieveSchema(testValidator, schema, {}, formData)).toEqual({
...schema,
properties: {
newKey: {
type: "object",
anyOf,
[ADDITIONAL_PROPERTY_FLAG]: true,
},
},
});
});
it("should handle null formData for schema which contains additionalProperties", () => {
const schema = {
additionalProperties: {
type: "string",
},
type: "object",
};
const formData = null;
expect(retrieveSchema(testValidator, schema, {}, formData)).toEqual({
...schema,
properties: {},
});
});
it("should prioritize local definitions over foreign ones", () => {
const schema = {
$ref: "#/definitions/address",
title: "foo",
};
const address = {
type: "string",
title: "bar",
};
const rootSchema = { definitions: { address } };
expect(retrieveSchema(testValidator, schema, rootSchema)).toEqual({
...address,
title: "foo",
});
});
it("recursive ref should resolve once", () => {
const result = retrieveSchema(testValidator, RECURSIVE_REF, RECURSIVE_REF);
expect(result).toEqual({
definitions: RECURSIVE_REF.definitions,
...RECURSIVE_REF.definitions["@enum"],
});
});
it("recursive allof ref should resolve once", () => {
const result = retrieveSchema(testValidator, getValueByPath(RECURSIVE_REF_ALLOF, [
PROPERTIES_KEY,
"value",
"items",
]), RECURSIVE_REF_ALLOF);
expect(result).toEqual({
...RECURSIVE_REF_ALLOF.definitions["@enum"],
});
});
it("should `resolve` refs inside of a properties key with bad property", () => {
const schema = {
type: "object",
properties: {
firstName: "some mame",
},
};
const rootSchema = {
type: "object",
};
expect(retrieveSchema(testValidator, schema, rootSchema)).toEqual(schema);
});
it("should `resolve` refs inside of a properties key", () => {
const entity = {
type: "string",
title: "Entity",
};
const schema = {
type: "object",
properties: {
entity: {
$ref: "#/definitions/entity",
},
},
};
const rootSchema = {
type: "object",
definitions: {
entity,
},
};
expect(retrieveSchema(testValidator, schema, rootSchema)).toEqual({
type: "object",
properties: {
entity: {
...entity,
},
},
});
});
it("should `resolve` refs inside of an items key", () => {
const entity = {
type: "string",
title: "Entity",
};
const schema = {
type: "array",
items: {
$ref: "#/definitions/entity",
},
};
const rootSchema = {
type: "object",
definitions: {
entity,
},
};
expect(retrieveSchema(testValidator, schema, rootSchema)).toEqual({
type: "array",
items: {
...entity,
},
});
});
it("should `resolve` sibling refs to the same definition", () => {
const schema = {
definitions: {
address: {
type: "object",
properties: {
street_address: { type: "string" },
city: { type: "string" },
state: { type: "string" },
},
required: ["street_address", "city", "state"],
},
},
type: "object",
properties: {
billing_address: {
title: "Billing address",
$ref: "#/definitions/address",
},
shipping_address: {
title: "Shipping address",
$ref: "#/definitions/address",
},
},
};
expect(retrieveSchema(testValidator, schema, schema)).toEqual({
...schema,
properties: {
billing_address: {
...schema.definitions.address,
title: "Billing address",
},
shipping_address: {
...schema.definitions.address,
title: "Shipping address",
},
},
});
});
describe("property dependencies", () => {
describe("false condition", () => {
it("should not add required properties", () => {
const rootSchema = { definitions: {} };
const formData = {};
expect(retrieveSchema(testValidator, PROPERTY_DEPENDENCIES, rootSchema, formData)).toEqual({
type: "object",
properties: {
a: { type: "string" },
b: { type: "integer" },
},
required: ["a"],
});
});
});
describe("true condition", () => {
describe("when required is not defined", () => {
it("should define required properties", () => {
const schema = {
...PROPERTY_DEPENDENCIES,
required: undefined,
};
const rootSchema = { definitions: {} };
const formData = { a: "1" };
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "object",
properties: {
a: { type: "string" },
b: { type: "integer" },
},
required: ["b"],
});
});
it("should not define required properties, when the dependency is a boolean", () => {
const schema = {
...PROPERTY_DEPENDENCIES,
required: undefined,
dependencies: {
a: true,
},
};
const rootSchema = { definitions: {} };
const formData = { a: "1" };
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "object",
properties: {
a: { type: "string" },
b: { type: "integer" },
},
});
});
});
describe("when required is defined", () => {
it("should concat required properties", () => {
const rootSchema = { definitions: {} };
const formData = { a: "1" };
expect(retrieveSchema(testValidator, PROPERTY_DEPENDENCIES, rootSchema, formData)).toEqual({
type: "object",
properties: {
a: { type: "string" },
b: { type: "integer" },
},
required: ["a", "b"],
});
});
});
});
});
describe("schema dependencies", () => {
describe("conditional", () => {
describe("false condition", () => {
it("should not modify properties", () => {
const rootSchema = { definitions: {} };
const formData = {};
expect(retrieveSchema(testValidator, SCHEMA_DEPENDENCIES, rootSchema, formData)).toEqual({
type: "object",
properties: {
a: { type: "string" },
},
});
});
});
describe("true condition", () => {
it("should add properties", () => {
const rootSchema = { definitions: {} };
const formData = { a: "1" };
expect(retrieveSchema(testValidator, SCHEMA_DEPENDENCIES, rootSchema, formData)).toEqual({
type: "object",
properties: {
a: { type: "string" },
b: { type: "integer" },
},
});
});
it("should concat required properties", () => {
const rootSchema = { definitions: {} };
const formData = { a: "1" };
expect(retrieveSchema(testValidator, SCHEMA_AND_REQUIRED_DEPENDENCIES, rootSchema, formData)).toEqual({
type: "object",
properties: {
a: { type: "string" },
b: { type: "integer" },
},
required: ["a", "b"],
});
});
it("should not concat enum properties, but should concat `required` properties", () => {
const schema = {
type: "object",
properties: {
a: { type: "string", enum: ["FOO", "BAR", "BAZ"] },
b: { type: "string", enum: ["GREEN", "BLUE", "RED"] },
},
required: ["a"],
dependencies: {
a: {
properties: {
a: { enum: ["FOO"] },
b: { enum: ["BLUE"] },
},
required: ["a", "b"],
},
},
};
const rootSchema = { definitions: {} };
const formData = { a: "FOO" };
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "object",
properties: {
a: { type: "string", enum: ["FOO"] },
b: { type: "string", enum: ["BLUE"] },
},
required: ["a", "b"],
});
});
});
describe("with $ref in dependency", () => {
it("should retrieve referenced schema", () => {
const schema = {
type: "object",
properties: {
a: { type: "string" },
},
dependencies: {
a: {
$ref: "#/definitions/needsB",
},
},
};
const rootSchema = {
definitions: {
needsB: {
properties: {
b: { type: "integer" },
},
},
},
};
const formData = { a: "1" };
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "object",
properties: {
a: { type: "string" },
b: { type: "integer" },
},
});
});
});
describe("with $ref in oneOf", () => {
it("should retrieve referenced schemas", () => {
// Mock isValid so that withExactlyOneSubschema works as expected
testValidator = makeTestValidator({
isValid: [
false, // First oneOf... second !== first
true, // Second oneOf... second === second
],
});
const schema = {
type: "object",
properties: {
a: { enum: ["typeA", "typeB"] },
},
dependencies: {
a: {
oneOf: [
{ $ref: "#/definitions/needsA" },
{ $ref: "#/definitions/needsB" },
],
},
},
};
const rootSchema = {
definitions: {
needsA: {
properties: {
a: { enum: ["typeA"] },
b: { type: "number" },
},
},
needsB: {
properties: {
a: { enum: ["typeB"] },
c: { type: "boolean" },
},
},
},
};
const formData = { a: "typeB" };
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "object",
properties: {
a: { enum: ["typeA", "typeB"] },
c: { type: "boolean" },
},
});
});
});
});
describe("dynamic", () => {
describe("false condition", () => {
it("should not modify properties", () => {
const schema = {
...SCHEMA_AND_ONEOF_REF_DEPENDENCIES,
properties: {
a: { type: "string" },
},
definitions: undefined,
};
const formData = {};
expect(retrieveSchema(testValidator, schema, SCHEMA_AND_ONEOF_REF_DEPENDENCIES, formData)).toEqual({
type: "object",
properties: {
a: { type: "string" },
},
});
});
});
describe("true condition", () => {
it("should add `first` properties given `first` data", () => {
// Mock isValid so that withExactlyOneSubschema works as expected
testValidator = makeTestValidator({
isValid: [
true, // First dependency... first === first
false, // Second dependency... second !== first
],
});
const schema = {
...SCHEMA_AND_ONEOF_REF_DEPENDENCIES,
definitions: undefined,
};
const formData = { a: "int" };
expect(retrieveSchema(testValidator, schema, SCHEMA_AND_ONEOF_REF_DEPENDENCIES, formData)).toEqual({
type: "object",
properties: {
a: { type: "string", enum: ["int", "bool"] },
b: { type: "integer" },
},
});
});
it("should add `second` properties given `second` data", () => {
// Mock isValid so that withExactlyOneSubschema works as expected
testValidator = makeTestValidator({
isValid: [
false, // First dependency... first !== second
true, // Second dependency... second === second
],
});
const schema = {
...SCHEMA_AND_ONEOF_REF_DEPENDENCIES,
definitions: undefined,
};
const formData = { a: "bool" };
expect(retrieveSchema(testValidator, schema, SCHEMA_AND_ONEOF_REF_DEPENDENCIES, formData)).toEqual({
type: "object",
properties: {
a: { type: "string", enum: ["int", "bool"] },
b: { type: "boolean" },
},
});
});
describe("showing/hiding nested dependencies", () => {
let schema;
let rootSchema;
beforeAll(() => {
schema = SCHEMA_WITH_ONEOF_NESTED_DEPENDENCIES;
rootSchema = { definitions: {} };
});
it("should not include nested dependencies that should be hidden", () => {
// Mock isValid so that withExactlyOneSubschema works as expected
testValidator = makeTestValidator({
isValid: [
false, // employee_accounts oneOf ... - fail
true, // update_absences first oneOf... success
false, // update_absences second oneOf... fail
false, // update_absences third oneOf... fail
],
});
const formData = {
employee_accounts: false,
update_absences: "BOTH",
};
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "object",
properties: {
employee_accounts: {
type: "boolean",
title: "Employee Accounts",
},
},
});
expect(consoleWarnSpy).toHaveBeenCalledWith("ignoring oneOf in dependencies because there isn't exactly one subschema that is valid");
});
it("should include nested dependencies that should not be hidden", () => {
// Mock isValid so that withExactlyOneSubschema works as expected
testValidator = makeTestValidator({
isValid: [
true, // employee_accounts oneOf... success
true, // update_absences first oneOf... success
false, // update_absences second oneOf... fail
false, // update_absences third oneOf... fail
],
});
const formData = {
employee_accounts: true,
update_absences: "BOTH",
};
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "object",
properties: {
employee_accounts: {
type: "boolean",
title: "Employee Accounts",
},
permitted_extension: {
title: "Permitted Extension",
type: "integer",
},
update_absences: {
title: "Update Absences",
type: "string",
oneOf: [
{
title: "Both",
const: "BOTH",
},
],
},
},
});
});
});
});
describe("with $ref in dependency", () => {
it("should retrieve the referenced schema", () => {
// Mock isValid so that withExactlyOneSubschema works as expected
testValidator = makeTestValidator({
isValid: [
false, // First oneOf... fail
true, // Second oneOf... success
],
});
const schema = {
type: "object",
properties: {
a: { type: "string", enum: ["int", "bool"] },
},
dependencies: {
a: {
$ref: "#/definitions/typedInput",
},
},
};
const rootSchema = {
definitions: {
typedInput: {
oneOf: [
{
properties: {
a: { enum: ["int"] },
b: { type: "integer" },
},
},
{
properties: {
a: { enum: ["bool"] },
b: { type: "boolean" },
},
},
],
},
},
};
const formData = { a: "bool" };
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "object",
properties: {
a: { type: "string", enum: ["int", "bool"] },
b: { type: "boolean" },
},
});
});
});
});
});
describe("allOf", () => {
it("should merge types", () => {
const schema = {
allOf: [{ type: ["string", "number", "null"] }, { type: "string" }],
};
const rootSchema = { definitions: {} };
const formData = {};
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "string",
});
});
it("should not merge `allOf.contains` schemas", () => {
// https://github.com/rjsf-team/react-jsonschema-form/issues/2923#issuecomment-1946034240
const schema = {
type: "array",
items: {
type: "object",
properties: {
a: {
type: "string",
},
},
},
allOf: [
{
maxItems: 5,
},
{
contains: {
type: "object",
properties: {
a: {
pattern: "1",
},
},
},
},
{
contains: {
type: "object",
properties: {
a: {
pattern: "2",
},
},
},
},
],
};
const rootSchema = { definitions: {} };
const formData = {};
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "array",
items: {
type: "object",
properties: {
a: {
type: "string",
},
},
},
maxItems: 5,
allOf: [
{
contains: {
type: "object",
properties: {
a: {
pattern: "1",
},
},
},
},
{
contains: {
type: "object",
properties: {
a: {
pattern: "2",
},
},
},
},
],
});
});
it("should not merge incompatible types", () => {
const schema = {
allOf: [{ type: "string" }, { type: "boolean" }],
};
const rootSchema = { definitions: {} };
const formData = {};
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({});
expect(consoleWarnSpy).toBeCalledWith(expect.stringMatching(/could not merge subschemas in allOf/), expect.any(Error));
});
it("should return allOf and top level schemas when expand all", () => {
const schema = {
properties: { test: { type: "string" } },
allOf: [{ type: "string" }, { type: "boolean" }],
};
const rootSchema = { definitions: {} };
const formData = {};
const { allOf, ...restOfSchema } = schema;
expect(retrieveSchemaInternal(testValidator, schema, rootSchema, formData, true)).toEqual([...allOf, restOfSchema]);
});
it("should merge types with $ref in them", () => {
const schema = {
allOf: [{ $ref: "#/definitions/1" }, { $ref: "#/definitions/2" }],
};
const rootSchema = {
definitions: {
"1": { type: "string" },
"2": { minLength: 5 },
},
};
const formData = {};
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "string",
minLength: 5,
});
});
it("should properly merge schemas with nested allOf`s", () => {
const schema = {
allOf: [
{
type: "string",
allOf: [{ minLength: 2 }, { maxLength: 5 }],
},
{
type: "string",
allOf: [{ default: "hi" }, { minLength: 4 }],
},
],
};
const rootSchema = { definitions: {} };
const formData = {};
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "string",
minLength: 4,
maxLength: 5,
default: "hi",
});
});
});
describe("Conditional schemas (If, Then, Else)", () => {
it("should resolve if, then", () => {
// Mock errors so that resolveCondition works as expected
testValidator = makeTestValidator({
isValid: [
true, // First condition Country... USA pass
false, // Second condition Countery... Canada fail
],
});
const rootSchema = { definitions: {} };
const formData = {
country: "United States of America",
postal_code: "20500",
};
expect(retrieveSchema(testValidator, SCHEMA_WITH_SINGLE_CONDITION, rootSchema, formData)).toEqual({
type: "object",
properties: {
country: {
default: "United States of America",
enum: ["United States of America", "Canada"],
},
postal_code: { pattern: "[0-9]{5}(-[0-9]{4})?" },
},
});
});
it("should resolve if, else", () => {
// Mock errors so that resolveCondition works as expected
testValidator = makeTestValidator({
isValid: [
false, // First condition Country... USA fail
],
});
const rootSchema = { definitions: {} };
const formData = {
country: "Canada",
postal_code: "K1M 1M4",
};
expect(retrieveSchema(testValidator, SCHEMA_WITH_SINGLE_CONDITION, rootSchema, formData)).toEqual({
type: "object",
properties: {
country: {
default: "United States of America",
enum: ["United States of America", "Canada"],
},
postal_code: { pattern: "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]" },
},
});
});
it("should resolve multiple conditions", () => {
// Mock errors so that resolveCondition works as expected
testValidator = makeTestValidator({
isValid: [
true, // First condition animal... Cat pass
false, // Second condition animal... Fish fail
],
});
const schema = {
type: "object",
properties: {
animal: {
enum: ["Cat", "Fish"],
},
},
allOf: [
{
if: {
properties: { animal: { const: "Cat" } },
},
then: {
properties: {
food: { type: "string", enum: ["meat", "grass", "fish"] },
},
},
required: ["food"],
},
{
if: {
properties: { animal: { const: "Fish" } },
},
then: {
properties: {
food: {
type: "string",
enum: ["insect", "worms"],
},
water: {
type: "string",
enum: ["lake", "sea"],
},
},
required: ["food", "water"],
},
},
{
required: ["animal"],
},
],
};
const rootSchema = { definitions: {} };
const formData = {
animal: "Cat",
};
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "object",
properties: {
animal: {
enum: ["Cat", "Fish"],
},
food: { type: "string", enum: ["meat", "grass", "fish"] },
},
required: ["animal", "food"],
});
});
it("should resolve multiple conditions in nested allOf blocks", () => {
// Mock errors so that resolveCondition works as expected
testValidator = makeTestValidator({
isValid: [
false, // First condition Animal... Cat fail
true, // Second condition Animal... Dog pass
false, // Third condition Breed... Alsatian fail
true, // Fourth condition Breed... Dalmation pass
],
});
const rootSchema = { definitions: {} };
const formData = {
Animal: "Dog",
Breed: {
BreedName: "Dalmation",
},
};
expect(retrieveSchema(testValidator, SCHEMA_WITH_MULTIPLE_CONDITIONS, rootSchema, formData)).toEqual({
type: "object",
properties: {
Animal: {
default: "Cat",
enum: ["Cat", "Dog"],
title: "Animal",
type: "string",
},
Breed: {
properties: {
BreedName: {
default: "Alsatian",
enum: ["Alsatian", "Dalmation"],
title: "Breed name",
type: "string",
},
},
allOf: [
{
if: {
required: ["BreedName"],
properties: {
BreedName: {
const: "Alsatian",
},
},
},
then: {
properties: {
Fur: {
default: "brown",
enum: ["black", "brown"],
title: "Fur",
type: "string",
},
},
required: ["Fur"],
},
},
{
if: {
required: ["BreedName"],
properties: {
BreedName: {
const: "Dalmation",
},
},
},
then: {
properties: {
Spots: {
default: "small",
enum: ["large", "small"],
title: "Spots",
type: "string",
},
},
required: ["Spots"],
},
},
],
required: ["BreedName"],
title: "Breed",
},
},
required: ["Animal"],
});
});
it("should resolve $ref", () => {
// Mock errors so that resolveCondition works as expected
testValidator = makeTestValidator({
isValid: [
true, // First condition animal... Cat pass
false, // Second condition animal... Fish fail
],
});
const schema = {
type: "object",
properties: {
animal: {
enum: ["Cat", "Fish"],
},
},
allOf: [
{
if: {
properties: { animal: { const: "Cat" } },
},
then: {
$ref: "#/definitions/cat",
},
required: ["food"],
},
{
if: {
properties: { animal: { const: "Fish" } },
},
then: {
$ref: "#/definitions/fish",
},
},
{
required: ["animal"],
},
],
};
const rootSchema = {
definitions: {
cat: {
properties: {
food: { type: "string", enum: ["meat", "grass", "fish"] },
},
},
fish: {
properties: {
food: {
type: "string",
enum: ["insect", "worms"],
},
water: {
type: "string",
enum: ["lake", "sea"],
},
},
required: ["food", "water"],
},
},
};
const formData = {
animal: "Cat",
};
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "object",
properties: {
animal: {
enum: ["Cat", "Fish"],
},
food: { type: "string", enum: ["meat", "grass", "fish"] },
},
required: ["animal", "food"],
});
});
it("handles nested if then else", () => {
const rootSchema = {};
const formData = {
country: "USA",
state: "New York",
};
expect(retrieveSchema(testValidator, SCHEMA_WITH_NESTED_CONDITIONS, rootSchema, formData)).toEqual({
type: "object",
properties: {
country: {
enum: ["USA"],
},
state: { type: "string", enum: ["California", "New York"] },
city: {
type: "string",
enum: ["New York City", "Buffalo", "Rochester"],
},
},
required: ["country", "state"],
});
});
it("overrides the base schema with a conditional branch when merged", () => {
const schema = {
type: "object",
properties: {
myString: {
type: "string",
minLength: 5,
},
},
if: true,
then: {
properties: {
myString: {
minLength: 10, // This value of minLength should override the original value
},
},
},
};
const rootSchema = { definitions: {} };
const formData = {};
expect(retrieveSchema(testValidator, schema, rootSchema, formData)).toEqual({
type: "object",
properties: {
myString: {
type: "string",
minLength: 10,
},
},
});
});
});
describe("withExactlyOneSubschema()", () => {
it("Handle conditions with falsy subschema, subschema.properties, or condition schema", () => {
const schema = {
type: "integer",
};
const oneOf = [
true,
{ properties: undefined },
{ properties: { foo: { type: "string" } } },
];
expect(withExactlyOneSubSchema(testValidator, schema, schema, "bar", oneOf, false, new Set())).toEqual([schema]);
});
});
describe("stubExistingAdditionalProperties()", () => {
it("deals with undefined formData", () => {
const schema = { type: "string" };
expect(stubExistingAdditionalProperties(testValidator, schema)).toEqual({
...schema,
properties: {},
});
});
it("deals with non-object formData", () => {
const schema = { type: "string" };
expect(stubExistingAdditionalProperties(testValidator, schema, undefined, [])).toEqual({
...schema,
properties: {},
});
});
it("has property keys that match formData, additionalProperties is boolean", () => {
const schema = {
additionalProperties: true,
};
const formData = { bar: 1, baz: false, foo: "str" };
expect(stubExistingAdditionalProperties(testValidator, schema, undefined, formData)).toEqual({
...schema,
properties: {
bar: {
type: "number",
[ADDITIONAL_PROPERTY_FLAG]: true,
},
baz: {
type: "boolean",
[ADDITIONAL_PROPERTY_FLAG]: true,
},
foo: {
type: "string",
[ADDITIONAL_PROPERTY_FLAG]: true,
},
},
});
});
it("has property keys that match schema AND formData, additionalProperties is boolean", () => {
const schema = {
properties: {
foo: { type: "string" },
bar: { type: "number" },
},
additionalProperties: true,
};
const formData = { foo: "blah", bar: 1, baz: true };
expect(stubExistingAdditionalProperties(testValidator, schema, undefined, formData)).toEqual({
...schema,
properties: {
...schema.properties,
baz: {
type: "boolean",
[ADDITIONAL_PROPERTY_FLAG]: true,
},
},
});
});
it("has additionalProperties of type number", () => {
const schema = {
additionalProperties: { type: "number" },
};
const formData = { bar: 1 };
expect(stubExistingAdditionalProperties(testValidator, schema, undefined, formData)).toEqual({
...schema,
properties: {
bar: {
...schema.additionalProperties,
[ADDITIONAL_PROPERTY_FLAG]: true,
},
},
});
});
it("has additionalProperties of empty object", () => {
const schema = {
additionalProperties: {},
};
const formData = { foo: "blah", bar: 1, baz: true };
expect(stubExistingAdditionalProperties(testValidator, schema, undefined, formData)).toEqual({
...schema,
properties: {
foo: {
type: "string",
[ADDITIONAL_PROPERTY_FLAG]: true,
},
bar: {
type: "number",
[ADDITIONAL_PROPERTY_FLAG]: true,
},
baz: {
type: "boolean",
[ADDITIONAL_PROPERTY_FLAG]: true,
},
},
});
});
it("has additionalProperties with a ref", () => {
const schema = {
additionalProperties: { $ref: "#/definitions/foo" },
};
const rootSchema = {
definitions: {
foo: { type: "string" },
},
};
const formData = { bar: "blah" };
expect(stubExistingAdditionalProperties(testValidator, schema, rootSchema, formData)).toEqual({
...schema,
properties: {
bar: {
type: "string",
[ADDITIONAL_PROPERTY_FLAG]: true,
},
},
});
});
});
describe("getAllPermutationsOfXxxOf()", () => {
it("returns a single permutation when there are only one version of each row", () => {
const oneOfs = [
[{ title: "A", type: "string" }],
[{ title: "B", type: "number" }],
[{ title: "C", type: "boolean" }],
];
expect(getAllPermutationsOfXxxOf(oneOfs)).toEqual([
[
{ title: "A", type: "string" },
{ title: "B", type: "number" },
{ title: "C", type: "boolean" },
],
]);
});
it("returns 2 permutations when there are 2 versions in one row and one in another", () => {
const oneOfs = [
[{ title: "A", type: "string" }],
[
{ title: "B1", type: "number" },
{ title: "B2", type: "boolean" },
],
];
expect(getAllPermutationsOfXxxOf(oneOfs)).toEqual([
[
{ title: "A", type: "string" },
{ title: "B1", type: "number" },
],
[
{ title: "A", type: "string" },
{ title: "B2", type: "boolean" },
],
]);
});
it("returns 6 permutations when there are 3 in row 1, 2 in row 2 and one in row 3", () => {
const oneOfs = [
[{ title: "A", type: "string" }],
[
{ title: "B1", type: "number" },
{ title: "B2", type: "boolean" },
],
[
{ title: "C1", type: "string" },
{ title: "C2", type: "number" },
{ title: "C3", type: "boolean" },
],
];
expect(getAllPermutationsOfXxxOf(oneOfs)).toEqual([
[
{ title: "A", type: "string" },
{ title: "B1", type: "number" },
{ title: "C1", type: "string" },
],
[
{ title: "A", type: "string" },
{ title: "B2", type: "boolean" },
{ title: "C1", type: "string" },
],
[
{ title: "A", type: "string" },
{ title: "B1", type: "number" },
{ title: "C2", type: "number" },
],
[
{ title: "A", type: "string" },
{ title: "B2", type: "boolean" },
{ title: "C2", type: "number" },
],
[
{ title: "A", type: "string" },
{ title: "B1", type: "number" },
{ title: "C3", type: "boolean" },
],
[
{ title: "A", type: "string" },
{ title: "B2", type: "boolean" },
{ title: "C3", type: "boolean" },
],
]);
});
});
describe("resolveAnyOrOneOfSchemas()", () => {
it("resolves anyOf with $ref for single element, merging schemas", () => {
const anyOfSchema = SUPER_SCHEMA.properties?.multi;
expect(resolveAnyOrOneOfSchemas(testValidator, anyOfSchema, SUPER_SCHEMA, false, [])).toEqual([
{
...SUPER_SCHEMA.definitions?.foo,
title: "multi",
},
]);
});
it("resolves oneOf with $ref for expandedAll elements, merging schemas", () => {
const oneOfSchema = SUPER_SCHEMA.properties?.single;
expect(resolveAnyOrOneOfSchemas(testValidator, oneOfSchema, SUPER_SCHEMA, true, [])).toEqual([
{
...SUPER_SCHEMA.definitions?.choice1,
required: ["choice", "more"],
},
{
...SUPER_SCHEMA.definitions?.choice2,
required: ["choice"],
},
]);
});
it("resolves oneOf with multiple $refs", () => {
const schema = {
oneOf: [
{
type: "object",
properties: {
field: {
$ref: "#/definitions/aObject",
},
},
},
{
type: "array",
items: {
$ref: "#/definitions/bObject",
},
},
],
};
const rootSchema = {
definitions: {
aObject: {
properties: {
a: { enum: ["typeA"] },
b: { type: "number" },
},
},
bObject: {
properties: {
a: { enum: ["typeB"] },
c: { type: "boolean" },
},
},
},
};
expect(resolveAnyOrOneOfSchemas(testValidator, schema, rootSchema, true, [])).toEqual([
{
type: "object",
properties: {
field: {
properties: {
a: { enum: ["typeA"] },
b: { type: "number" },
},
},
},
},
{
type: "array",
items: {
properties: {
a: { enum: ["typeB"] },
c: { type: "boolean" },
},
},
},
]);
});
});
describe("resolveCondition()", () => {
it("returns both conditions with expandAll", () => {
expect(resolveCondition(testValidator, SCHEMA_WITH_SINGLE_CONDITION, SCHEMA_WITH_SINGLE_CONDITION, true, new Set())).toEqual([
{
type: "object",
properties: {
...SCHEMA_WITH_SINGLE_CONDITION.properties,
...SCHEMA_WITH_SINGLE_CONDITION.then.properties,
},
},
{
type: "object",
properties: {
...SCHEMA_WITH_SINGLE_CONDITION.properties,
...SCHEMA_WITH_SINGLE_CONDITION.else.properties,
},
},
]);
});
it("returns neither condition with expandAll, using boolean based then/else", () => {
const schema = {
type: "object",
properties: {
country: {
default: "United States of America",
enum: ["United States of America", "Canada"],
},
},
if: {
properties: { country: { const: "United States of America" } },
},
then: false,
else: true,
};
expect(resolveCondition(testValidator, schema, schema, true, new Set())).toEqual([
{
type: "object",
properties: {
...SCHEMA_WITH_SINGLE_CONDITION.properties,
},
},
]);
});
});
});
// This file was copied and modified from https://github.com/rjsf-team/react-jsonschema-form/blob/f4229bf6e067d31b24de3ef9d3ca754ee52529ac/packages/utils/test/schema/sanitizeDataForNewSchemaTest.ts
// Licensed under the Apache License, Version 2.0.
// Modifications made by Roman Krasilnikov.
import { beforeEach, describe, expect, it } from "vitest";
import { FIRST_ONE_OF, oneOfData, oneOfSchema, SECOND_ONE_OF, } from "./fixtures/test-data.js";
import { sanitizeDataForNewSchema } from "./sanitize-data-for-new-schema.js";
import { retrieveSchema } from "./resolve.js";
import { makeTestValidator } from "./test-validator.js";
let testValidator;
beforeEach(() => {
testValidator = makeTestValidator();
});
describe("sanitizeDataForNewSchema", () => {
it('returns undefined when the new schema does not contain a "property" object', () => {
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, {}, {}, undefined)).toBeUndefined();
});
// it('returns input formData when the old schema is not an object', () => {
// const newSchema = retrieveSchema(testValidator, SECOND_ONE_OF, oneOfSchema);
// expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, undefined, oneOfData)).toEqual(oneOfData);
// });
it('returns input formData when the old schema does not contain a "property" object', () => {
const newSchema = retrieveSchema(testValidator, SECOND_ONE_OF, oneOfSchema);
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, {}, oneOfData)).toEqual(oneOfData);
});
it("returns input formData when the new schema matches the data for the new schema rather than the old", () => {
const newSchema = retrieveSchema(testValidator, SECOND_ONE_OF, oneOfSchema);
const oldSchema = structuredClone(retrieveSchema(testValidator, FIRST_ONE_OF, oneOfSchema));
// Change the type of name to trigger a fall-thru
// @ts-expect-error
oldSchema.properties.name.type = "boolean";
// By changing the type, the name will be marked as undefined
const expected = { ...oneOfData, name: undefined };
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, oneOfData)).toEqual(expected);
});
it("returns input formData when the new schema and old schema match on a default", () => {
const oldSchema = {
type: "object",
properties: {
defaultField: {
type: "string",
default: "myData",
},
anotherField: {
type: "boolean",
},
},
};
const newSchema = {
type: "object",
properties: {
defaultField: {
type: "string",
default: "myData",
},
anotherField: {
type: "string",
},
},
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, {
notInEitherSchema: "keep",
defaultField: "myData",
anotherField: true,
})).toEqual({ notInEitherSchema: "keep", defaultField: "myData" });
});
it("returns new schema const in formData when the old schema default matches in the formData", () => {
const oldSchema = {
type: "object",
properties: {
defaultField: {
type: "string",
default: "myData",
},
anotherField: {
type: "boolean",
},
},
};
const newSchema = {
type: "object",
properties: {
defaultField: {
type: "string",
default: "yourData",
},
anotherField: {
type: "string",
},
},
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, {
defaultField: "myData",
anotherField: true,
})).toEqual({ defaultField: "yourData" });
});
it("returns input formData when the old schema default does not match in the formData", () => {
const oldSchema = {
type: "object",
properties: {
defaultField: {
type: "string",
default: "myData",
},
anotherField: {
type: "boolean",
},
},
};
const newSchema = {
type: "object",
properties: {
defaultField: {
type: "string",
default: "yourData",
},
anotherField: {
type: "string",
},
},
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, {
defaultField: "fooData",
anotherField: true,
})).toEqual({ defaultField: "fooData" });
});
it("returns empty formData when the old schema default does not match in the formData, and new schema default is readOnly", () => {
const oldSchema = {
type: "object",
properties: {
defaultField: {
type: "string",
default: "myData",
},
anotherField: {
type: "boolean",
},
},
};
const newSchema = {
type: "object",
properties: {
defaultField: {
type: "string",
default: "yourData",
readOnly: true,
},
anotherField: {
type: "string",
},
},
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, {
defaultField: "fooData",
anotherField: true,
})).toEqual({});
});
it("returns input formData when the new schema and old schema match on a const", () => {
const oldSchema = {
type: "object",
properties: {
constField: {
type: "string",
const: "myData",
},
anotherField: {
type: "boolean",
},
},
};
const newSchema = {
type: "object",
properties: {
constField: {
type: "string",
const: "myData",
},
anotherField: {
type: "string",
},
},
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, {
notInEitherSchema: "keep",
constField: "myData",
anotherField: true,
})).toEqual({ notInEitherSchema: "keep", constField: "myData" });
});
it("returns new schema const in formData when the old schema const matches in the formData", () => {
const oldSchema = {
type: "object",
properties: {
constField: {
type: "string",
const: "myData",
},
anotherField: {
type: "boolean",
},
},
};
const newSchema = {
type: "object",
properties: {
constField: {
type: "string",
const: "yourData",
},
anotherField: {
type: "string",
},
},
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, {
constField: "myData",
anotherField: true,
})).toEqual({ constField: "yourData" });
});
it("returns empty formData when the old schema const does not match in the formData", () => {
const oldSchema = {
type: "object",
properties: {
constField: {
type: "string",
const: "myData",
},
anotherField: {
type: "boolean",
},
},
};
const newSchema = {
type: "object",
properties: {
constField: {
type: "string",
const: "yourData",
},
anotherField: {
type: "string",
},
},
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, {
constField: "fooData",
anotherField: true,
})).toEqual({});
});
it("returns empty formData after resolving schema refs", () => {
const rootSchema = {
definitions: {
string_def: {
type: "string",
},
},
};
const oldSchema = {
type: "object",
properties: {
field: {
$ref: "#/definitions/string_def",
},
oldField: {
type: "string",
},
},
};
const newSchema = {
type: "object",
properties: {
field: {
$ref: "#/definitions/string_def",
},
},
};
expect(sanitizeDataForNewSchema(testValidator, rootSchema, newSchema, oldSchema, { oldField: "test" })).toEqual({});
});
it("returns data when two arrays have same boolean items", () => {
const oldSchema = {
type: "array",
items: true,
};
const newSchema = {
type: "array",
items: true,
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, [1])).toEqual([1]);
});
it("returns undefined when two arrays have differing boolean items", () => {
const oldSchema = {
type: "array",
items: false,
};
const newSchema = {
type: "array",
items: true,
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, [1])).toBeUndefined();
});
it("returns undefined when one array has boolean items", () => {
const oldSchema = {
type: "array",
items: false,
};
const newSchema = {
type: "array",
items: { type: "string" },
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, [1])).toBeUndefined();
});
it("returns undefined when both arrays has array items", () => {
const oldSchema = {
type: "array",
items: [true],
};
const newSchema = {
type: "array",
items: [{ type: "string" }],
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, [1])).toBeUndefined();
});
it("returns undefined when one arrays has array items", () => {
const oldSchema = {
type: "array",
items: { type: "number" },
};
const newSchema = {
type: "array",
items: [{ type: "string" }],
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, [1])).toBeUndefined();
});
it("returns undefined when the arrays has array items of different types", () => {
const oldSchema = {
type: "array",
items: { type: "number" },
};
const newSchema = {
type: "array",
items: { type: "string" },
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, [1])).toBeUndefined();
});
it("returns trimmed array when the new schema has maxItems < size for simple type", () => {
const oldSchema = {
type: "array",
items: { type: "string" },
};
const newSchema = {
type: "array",
maxItems: 1,
items: { type: "string" },
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, ["1", "2"])).toEqual(["1"]);
});
it("returns whole array when the new schema does not have maxItems for simple type", () => {
const rootSchema = {
definitions: {
string_def: {
type: "string",
},
},
};
const oldSchema = {
type: "array",
maxItems: 2,
items: { $ref: "#/definitions/string_def" },
};
const newSchema = {
type: "array",
items: { $ref: "#/definitions/string_def" },
};
expect(sanitizeDataForNewSchema(testValidator, rootSchema, newSchema, oldSchema, ["1", "2"])).toEqual(["1", "2"]);
});
it("returns trimmed array when the new schema has maxItems < size for object type", () => {
const oldSchema = {
type: "array",
items: { type: "object", properties: { foo: { type: "string" } } },
};
const newSchema = {
type: "array",
maxItems: 1,
items: { type: "object", properties: { foo: { type: "string" } } },
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, [{ foo: "1" }, { foo: "2" }])).toEqual([{ foo: "1" }]);
});
it("returns whole array when the new schema does not have maxItems for object type", () => {
const oldSchema = {
type: "array",
maxItems: 2,
items: { type: "object", properties: { foo: { type: "string" } } },
};
const newSchema = {
type: "array",
items: { type: "object", properties: { foo: { type: "string" } } },
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, [{ foo: "1" }, { foo: "2" }])).toEqual([{ foo: "1" }, { foo: "2" }]);
});
it("returns undefined object values when the new schema has different object type", () => {
const oldSchema = {
type: "array",
items: { type: "object", properties: { foo: { type: "string" } } },
};
const newSchema = {
type: "array",
items: { type: "object", properties: { foo: { type: "number" } } },
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, [{ foo: "1" }, { foo: "2" }])).toEqual([{ foo: undefined }, { foo: undefined }]);
});
it("returns undefined object values when the new schema has array with different object types", () => {
const oldSchema = {
type: "object",
properties: { foo: { type: "array", items: { type: "string" } } },
};
const newSchema = {
type: "object",
properties: { foo: { type: "array", items: { type: "number" } } },
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, {
foo: ["1"],
})).toEqual({ foo: undefined });
});
it("returns formData when the new schema has field that is not in the old schema", () => {
const oldSchema = {
type: "object",
properties: {},
};
const newSchema = {
type: "object",
properties: { foo: { type: "object" } },
};
const formData = { foo: "1" };
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, formData)).toEqual(formData);
});
it('returns empty object when the old schema is of type string and the new contains "property" field', () => {
const oldSchema = { type: "string" };
const newSchema = {
properties: {
foo: {
type: "string",
},
},
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, "qwerty")).toEqual({});
});
it('returns empty object when the old schema is of type array and the new contains "property" field', () => {
const oldSchema = {
type: "array",
items: {
type: "string",
},
};
const newSchema = {
properties: {
foo: {
type: "string",
},
},
};
expect(sanitizeDataForNewSchema(testValidator, oneOfSchema, newSchema, oldSchema, ["qwerty", "asdfg"])).toEqual({});
});
});
import { expect, describe, it } from 'vitest';
import { typeOfSchema } from './type.js';
describe('typeOfSchema', () => {
it("Should allow empty schema", () => {
expect(typeOfSchema({})).toBe("null");
});
});
<script lang="ts">
import { getComponent } from "../../component.js";
import { getFormContext } from "../../context.js";
import type { FieldProps } from "../model.js";
import { getArrayContext } from "./context.js";
const { config, value: _ = $bindable() }: FieldProps<"unsupportedArray"> = $props();
const ctx = getFormContext();
const arrayCtx = getArrayContext();
const Alert = $derived(getComponent(ctx, "alert", config));
</script>
<Alert
{config}
errors={arrayCtx.errors}
type="error"
title={ctx.translation("array-schema-missing-items")}
>
{JSON.stringify(config, null, 2)}
</Alert>
declare const UnsupportedArrayField: import("svelte").Component<import("../model.js").FieldCommonProps<import("../../../core/schema.js").SchemaArrayValue>, {}, "value">;
export default UnsupportedArrayField;
<script lang="ts">
import { getSimpleSchemaType } from "../../core/index.js";
import { getFormContext } from "../context.js";
import { getComponent } from "../component.js";
import { getErrors } from '../utils.js';
import type { FieldProps } from "./model.js";
const ctx = getFormContext();
const { value: _ = $bindable(), config }: FieldProps<"unsupported"> =
$props();
const Alert = $derived(getComponent(ctx, "alert", config));
const errors = $derived(getErrors(ctx, config.idSchema));
</script>
<Alert
errors={errors}
type="error"
title={ctx.translation(
"unsupported-field-type",
getSimpleSchemaType(config.schema)
)}
{config}
>
{JSON.stringify(config, null, 2)}
</Alert>
declare const UnsupportedField: import("svelte").Component<import("./model.js").FieldCommonProps<import("../../core/index.js").SchemaValue>, {}, "value">;
export default UnsupportedField;