Skip to content

Commit a323c9a

Browse files
committed
WIP
1 parent d45889b commit a323c9a

File tree

2 files changed

+470
-49
lines changed

2 files changed

+470
-49
lines changed
Lines changed: 398 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,398 @@
1+
// This file was copied and modified from https://github.com/heath-freenome/react-jsonschema-form/blob/3f33fe3a0275c5ac355a90f8ac0179eee8dec1f8/packages/utils/test/schema/getClosestMatchingOptionTest.ts
2+
// Licensed under the Apache License, Version 2.0.
3+
// Modifications made by Roman Krasilnikov.
4+
5+
import {
6+
oneOfData,
7+
oneOfSchema,
8+
ONE_OF_SCHEMA_DATA,
9+
OPTIONAL_ONE_OF_DATA,
10+
OPTIONAL_ONE_OF_SCHEMA,
11+
ONE_OF_SCHEMA_OPTIONS,
12+
OPTIONAL_ONE_OF_SCHEMA_ONEOF,
13+
} from "./fixtures/test-data.js";
14+
15+
import type { Schema } from "./schema.js";
16+
import { calculateIndexScore, getClosestMatchingOption } from "./matching.js";
17+
import { beforeEach, describe, expect, it } from "vitest";
18+
import type { Validator } from "./validator.js";
19+
import { makeTestValidator } from "./test-validator.js";
20+
21+
const firstOption = oneOfSchema.definitions!.first_option_def as Schema;
22+
const secondOption = oneOfSchema.definitions!.second_option_def as Schema;
23+
24+
let testValidator: Validator;
25+
26+
beforeEach(() => {
27+
testValidator = makeTestValidator();
28+
});
29+
30+
describe("calculateIndexScore", () => {
31+
it("returns 0 when schema is not specified", () => {
32+
expect(calculateIndexScore(testValidator, OPTIONAL_ONE_OF_SCHEMA)).toEqual(
33+
0
34+
);
35+
});
36+
it("returns 0 when schema.properties is undefined", () => {
37+
expect(
38+
calculateIndexScore(testValidator, OPTIONAL_ONE_OF_SCHEMA, {})
39+
).toEqual(0);
40+
});
41+
it("returns 0 when schema.properties is not an object", () => {
42+
expect(
43+
calculateIndexScore(testValidator, OPTIONAL_ONE_OF_SCHEMA, {
44+
properties: "foo",
45+
} as unknown as Schema)
46+
).toEqual(0);
47+
});
48+
it("returns 0 when properties type is boolean", () => {
49+
expect(
50+
calculateIndexScore(testValidator, OPTIONAL_ONE_OF_SCHEMA, {
51+
properties: { foo: true },
52+
})
53+
).toEqual(0);
54+
});
55+
it("returns 0 when formData is empty object", () => {
56+
expect(
57+
calculateIndexScore(testValidator, oneOfSchema, firstOption, {})
58+
).toEqual(0);
59+
});
60+
it.todo("returns 1 for first option in oneOf schema", () => {
61+
expect(
62+
calculateIndexScore(
63+
testValidator,
64+
oneOfSchema,
65+
firstOption,
66+
ONE_OF_SCHEMA_DATA
67+
)
68+
).toEqual(1);
69+
});
70+
it("returns 8 for second option in oneOf schema", () => {
71+
expect(
72+
calculateIndexScore(
73+
testValidator,
74+
oneOfSchema,
75+
secondOption,
76+
ONE_OF_SCHEMA_DATA
77+
)
78+
).toEqual(9);
79+
});
80+
it("returns 1 for a schema that has a type matching the formData type", () => {
81+
expect(
82+
calculateIndexScore(testValidator, oneOfSchema, { type: "boolean" }, true)
83+
).toEqual(1);
84+
});
85+
it("returns 2 for a schema that has a const matching the formData value", () => {
86+
expect(
87+
calculateIndexScore(
88+
testValidator,
89+
oneOfSchema,
90+
{ properties: { foo: { type: "string", const: "constValue" } } },
91+
{ foo: "constValue" }
92+
)
93+
).toEqual(2);
94+
});
95+
it("returns 0 for a schema that has a const that does not match the formData value", () => {
96+
expect(
97+
calculateIndexScore(
98+
testValidator,
99+
oneOfSchema,
100+
{ properties: { foo: { type: "string", const: "constValue" } } },
101+
{ foo: "aValue" }
102+
)
103+
).toEqual(0);
104+
});
105+
});
106+
describe("oneOfMatchingOption", () => {
107+
it("oneOfSchema, oneOfData data, no options, returns -1", () => {
108+
expect(
109+
getClosestMatchingOption(testValidator, oneOfSchema, oneOfData, [])
110+
).toEqual(-1);
111+
});
112+
it("oneOfSchema, no data, 2 options, returns -1", () => {
113+
expect(
114+
getClosestMatchingOption(testValidator, oneOfSchema, undefined, [
115+
{ type: "string" },
116+
{ type: "number" },
117+
])
118+
).toEqual(-1);
119+
});
120+
it("oneOfSchema, oneOfData, no options, selectedOption 2, returns 2", () => {
121+
expect(
122+
getClosestMatchingOption(testValidator, oneOfSchema, oneOfData, [], 2)
123+
).toEqual(2);
124+
});
125+
it("oneOfSchema, no data, 2 options, returns -1", () => {
126+
expect(
127+
getClosestMatchingOption(
128+
testValidator,
129+
oneOfSchema,
130+
undefined,
131+
[{ type: "string" }, { type: "number" }],
132+
2
133+
)
134+
).toEqual(2);
135+
});
136+
it.only("returns the first option, which kind of matches the data", () => {
137+
expect(
138+
getClosestMatchingOption(
139+
testValidator,
140+
oneOfSchema,
141+
{ flag: true },
142+
ONE_OF_SCHEMA_OPTIONS
143+
)
144+
).toEqual(0);
145+
});
146+
it("returns the second option, which exactly matches the data", () => {
147+
// First 3 are mocked false, with the fourth being true for the real second option
148+
testValidator = makeTestValidator({ isValid: [false, false, false, true] });
149+
expect(
150+
getClosestMatchingOption(
151+
testValidator,
152+
oneOfSchema,
153+
ONE_OF_SCHEMA_DATA,
154+
ONE_OF_SCHEMA_OPTIONS
155+
)
156+
).toEqual(1);
157+
});
158+
it("returns the first matching option (i.e. second index) when data is ambiguous", () => {
159+
testValidator = makeTestValidator({
160+
isValid: [false, false, false, true, false, true],
161+
});
162+
const formData = { flag: false };
163+
expect(
164+
getClosestMatchingOption(
165+
testValidator,
166+
OPTIONAL_ONE_OF_SCHEMA,
167+
formData,
168+
OPTIONAL_ONE_OF_SCHEMA_ONEOF
169+
)
170+
).toEqual(1);
171+
});
172+
it("returns the third index when data is clear", () => {
173+
testValidator = makeTestValidator({
174+
isValid: [false, false, false, false, false, true],
175+
});
176+
expect(
177+
getClosestMatchingOption(
178+
testValidator,
179+
OPTIONAL_ONE_OF_SCHEMA,
180+
OPTIONAL_ONE_OF_DATA,
181+
OPTIONAL_ONE_OF_SCHEMA_ONEOF
182+
)
183+
).toEqual(2);
184+
});
185+
it("returns the second option when data matches for oneOf", () => {
186+
// From https://github.com/rjsf-team/react-jsonschema-form/issues/2944
187+
const oneOf: Schema[] = [
188+
{
189+
properties: {
190+
lorem: {
191+
type: "string",
192+
},
193+
},
194+
required: ["lorem"],
195+
},
196+
{
197+
properties: {
198+
ipsum: {
199+
oneOf: [
200+
{
201+
properties: {
202+
day: {
203+
type: "string",
204+
},
205+
},
206+
},
207+
{
208+
properties: {
209+
night: {
210+
type: "string",
211+
},
212+
},
213+
},
214+
],
215+
},
216+
},
217+
required: ["ipsum"],
218+
},
219+
];
220+
const schema = {
221+
type: "array",
222+
items: {
223+
oneOf,
224+
},
225+
} satisfies Schema;
226+
const formData = { ipsum: { night: "nicht" } };
227+
// Mock to return true for the last of the second one-ofs
228+
testValidator = makeTestValidator({
229+
isValid: [false, false, false, false, false, false, false, true],
230+
});
231+
expect(
232+
getClosestMatchingOption(
233+
testValidator,
234+
schema as unknown as Schema,
235+
formData,
236+
oneOf
237+
)
238+
).toEqual(1);
239+
});
240+
it("returns the second option when data matches for anyOf", () => {
241+
const anyOf: Schema[] = [
242+
{
243+
properties: {
244+
lorem: {
245+
type: "string",
246+
},
247+
},
248+
required: ["lorem"],
249+
},
250+
{
251+
properties: {
252+
ipsum: {
253+
anyOf: [
254+
{
255+
properties: {
256+
day: {
257+
type: "string",
258+
},
259+
},
260+
},
261+
{
262+
properties: {
263+
night: {
264+
type: "string",
265+
},
266+
},
267+
},
268+
],
269+
},
270+
},
271+
required: ["ipsum"],
272+
},
273+
];
274+
const schema: Schema = {
275+
type: "array",
276+
items: {
277+
anyOf,
278+
},
279+
};
280+
const formData = { ipsum: { night: "nicht" } };
281+
// Mock to return true for the last of the second anyOfs
282+
testValidator = makeTestValidator({
283+
isValid: [false, false, false, false, false, false, false, true],
284+
});
285+
expect(
286+
getClosestMatchingOption(testValidator, schema, formData, anyOf)
287+
).toEqual(1);
288+
});
289+
it("should return 0 when schema has discriminator but no matching data", () => {
290+
// Mock isValid to fail both values
291+
testValidator = makeTestValidator({
292+
isValid: [false, false, false, false],
293+
});
294+
const schema: Schema = {
295+
type: "object",
296+
definitions: {
297+
Foo: {
298+
title: "Foo",
299+
type: "object",
300+
properties: {
301+
code: {
302+
title: "Code",
303+
default: "foo_coding",
304+
enum: ["foo_coding"],
305+
type: "string",
306+
},
307+
},
308+
},
309+
Bar: {
310+
title: "Bar",
311+
type: "object",
312+
properties: {
313+
code: {
314+
title: "Code",
315+
default: "bar_coding",
316+
enum: ["bar_coding"],
317+
type: "string",
318+
},
319+
},
320+
},
321+
},
322+
//@ts-expect-error Discriminator field
323+
discriminator: {
324+
propertyName: "code",
325+
},
326+
oneOf: [{ $ref: "#/definitions/Foo" }, { $ref: "#/definitions/Bar" }],
327+
};
328+
const options = [
329+
schema.definitions!.Foo,
330+
schema.definitions!.Bar,
331+
] as Schema[];
332+
expect(
333+
getClosestMatchingOption(
334+
testValidator,
335+
schema,
336+
undefined,
337+
options,
338+
-1,
339+
"code"
340+
)
341+
).toEqual(-1);
342+
});
343+
it("should return Bar when schema has discriminator for bar", () => {
344+
// Mock isValid to pass the second value
345+
testValidator = makeTestValidator({ isValid: [false, false, false, true] });
346+
const schema: Schema = {
347+
type: "object",
348+
definitions: {
349+
Foo: {
350+
title: "Foo",
351+
type: "object",
352+
properties: {
353+
code: {
354+
title: "Code",
355+
default: "foo_coding",
356+
enum: ["foo_coding"],
357+
type: "string",
358+
},
359+
},
360+
},
361+
Bar: {
362+
title: "Bar",
363+
type: "object",
364+
properties: {
365+
code: {
366+
title: "Code",
367+
default: "bar_coding",
368+
enum: ["bar_coding"],
369+
type: "string",
370+
},
371+
},
372+
},
373+
},
374+
// @ts-expect-error Discriminator field
375+
discriminator: {
376+
propertyName: "code",
377+
},
378+
oneOf: [{ $ref: "#/definitions/Foo" }, { $ref: "#/definitions/Bar" }],
379+
};
380+
const formData = { code: "bar_coding" };
381+
const options = [
382+
schema.definitions!.Foo,
383+
schema.definitions!.Bar,
384+
] as Schema[];
385+
// Use the schemaUtils to verify the discriminator prop gets passed
386+
// const schemaUtils = createSchemaUtils(testValidator, schema);
387+
expect(
388+
getClosestMatchingOption(
389+
testValidator,
390+
oneOfSchema,
391+
formData,
392+
options,
393+
0,
394+
"code"
395+
)
396+
).toEqual(1);
397+
});
398+
});

0 commit comments

Comments
 (0)