Merge branch 'v2/main' into v2/fix/1942-tui-coverage-flake
This commit is contained in:
commit
2f0aa24726
1 changed files with 10868 additions and 1605 deletions
|
|
@ -213,4 +213,173 @@ describe("schemaToForm", () => {
|
|||
);
|
||||
expect(form.sections[0]!.fields[0]).toMatchObject({ initialValue: "hi" });
|
||||
});
|
||||
|
||||
// #2015 (the TUI twin of #1928): an argument declared with Zod's `.nullish()`
|
||||
// has no top-level `type`/`enum` — they sit on the surviving `anyOf` branch —
|
||||
// so before normalization every one of these degraded to a plain text field.
|
||||
describe("nullable unions", () => {
|
||||
it("renders a select for an anyOf string-enum|null argument", () => {
|
||||
const form = schemaToForm(
|
||||
{
|
||||
properties: {
|
||||
direction: {
|
||||
anyOf: [
|
||||
{ type: "string", enum: ["envio", "recebimento"] },
|
||||
{ type: "null" },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
"nullableEnum",
|
||||
);
|
||||
expect(form.sections[0]!.fields[0]).toMatchObject({
|
||||
name: "direction",
|
||||
type: "select",
|
||||
options: [
|
||||
{ label: "envio", value: "envio" },
|
||||
{ label: "recebimento", value: "recebimento" },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("maps the remaining nullable scalar branches to their typed fields", () => {
|
||||
const form = schemaToForm(
|
||||
{
|
||||
properties: {
|
||||
reference: { anyOf: [{ type: "string" }, { type: "null" }] },
|
||||
quantity: {
|
||||
anyOf: [
|
||||
{ type: "integer", minimum: 1, maximum: 9 },
|
||||
{ type: "null" },
|
||||
],
|
||||
},
|
||||
ratio: { anyOf: [{ type: "number" }, { type: "null" }] },
|
||||
express: { anyOf: [{ type: "boolean" }, { type: "null" }] },
|
||||
},
|
||||
},
|
||||
"nullableScalars",
|
||||
);
|
||||
const fields = form.sections[0]!.fields;
|
||||
expect(fields.map((f) => ({ name: f.name, type: f.type }))).toEqual([
|
||||
{ name: "reference", type: "string" },
|
||||
{ name: "quantity", type: "integer" },
|
||||
{ name: "ratio", type: "float" },
|
||||
{ name: "express", type: "boolean" },
|
||||
]);
|
||||
// The branch's own constraints are hoisted along with its type.
|
||||
expect(fields[1]).toMatchObject({ min: 1, max: 9 });
|
||||
});
|
||||
|
||||
it("renders a select for a nullable array-of-enum argument", () => {
|
||||
const form = schemaToForm(
|
||||
{
|
||||
properties: {
|
||||
tags: {
|
||||
anyOf: [
|
||||
{ type: "array", items: { enum: ["a", "b"] } },
|
||||
{ type: "null" },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
"nullableArrayEnum",
|
||||
);
|
||||
expect(form.sections[0]!.fields[0]).toMatchObject({
|
||||
type: "select",
|
||||
options: [
|
||||
{ label: "a", value: "a" },
|
||||
{ label: "b", value: "b" },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
// With the null left in the list, the all-strings check would reject the
|
||||
// enum and this would degrade to a plain text field.
|
||||
it("renders a select for a type: [string, null] enum, null stripped", () => {
|
||||
const form = schemaToForm(
|
||||
{
|
||||
properties: {
|
||||
direction: {
|
||||
type: ["string", "null"],
|
||||
enum: ["envio", "recebimento", null],
|
||||
},
|
||||
},
|
||||
},
|
||||
"typeArrayNullableEnum",
|
||||
);
|
||||
expect(form.sections[0]!.fields[0]).toMatchObject({
|
||||
type: "select",
|
||||
options: [
|
||||
{ label: "envio", value: "envio" },
|
||||
{ label: "recebimento", value: "recebimento" },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("collapses the type: [T, null] encoding too", () => {
|
||||
const form = schemaToForm(
|
||||
{
|
||||
properties: {
|
||||
note: { type: ["string", "null"] },
|
||||
flag: { type: ["boolean", "null"] },
|
||||
},
|
||||
},
|
||||
"typeArrayNull",
|
||||
);
|
||||
expect(
|
||||
form.sections[0]!.fields.map((f) => ({ name: f.name, type: f.type })),
|
||||
).toEqual([
|
||||
{ name: "note", type: "string" },
|
||||
{ name: "flag", type: "boolean" },
|
||||
]);
|
||||
});
|
||||
|
||||
// ink-form hands a select's stringified value straight back, so a numeric
|
||||
// enum routed to a select would submit "1" for 1. The typed field loses the
|
||||
// enum constraint but keeps the value's type — the safer loss.
|
||||
it("routes a nullable numeric enum to its typed field, not a select", () => {
|
||||
const form = schemaToForm(
|
||||
{
|
||||
properties: {
|
||||
level: {
|
||||
anyOf: [{ type: "integer", enum: [1, 2] }, { type: "null" }],
|
||||
},
|
||||
ratio: {
|
||||
anyOf: [{ type: "number", enum: [0.5, 1.5] }, { type: "null" }],
|
||||
},
|
||||
},
|
||||
},
|
||||
"numericEnum",
|
||||
);
|
||||
expect(
|
||||
form.sections[0]!.fields.map((f) => ({ name: f.name, type: f.type })),
|
||||
).toEqual([
|
||||
{ name: "level", type: "integer" },
|
||||
{ name: "ratio", type: "float" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("routes a plain numeric enum to its typed field too", () => {
|
||||
const form = schemaToForm(
|
||||
{ properties: { level: { type: "integer", enum: [1, 2] } } },
|
||||
"plainNumericEnum",
|
||||
);
|
||||
expect(form.sections[0]!.fields[0]).toMatchObject({ type: "integer" });
|
||||
});
|
||||
|
||||
it("leaves a union of two real types as a plain string field", () => {
|
||||
const form = schemaToForm(
|
||||
{
|
||||
properties: {
|
||||
mixed: { anyOf: [{ type: "string" }, { type: "number" }] },
|
||||
},
|
||||
},
|
||||
"realUnion",
|
||||
);
|
||||
expect(form.sections[0]!.fields[0]).toMatchObject({
|
||||
name: "mixed",
|
||||
type: "string",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue