Skip to content

Commit

Permalink
test(form-runtime): ensure that default values are correctly compiled
Browse files Browse the repository at this point in the history
  • Loading branch information
danielo515 committed Dec 7, 2023
1 parent 711865c commit afae1a0
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 37 deletions.
4 changes: 2 additions & 2 deletions src/FormModal.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { App, Modal, Platform, Setting } from "obsidian";
import MultiSelect from "./views/components/MultiSelect.svelte";
import FormResult, {
formDataFromFormOptions,
type ModalFormData,
} from "./core/FormResult";
import { formDataFromFormDefaults } from './core/formDataFromFormDefaults';
import { exhaustiveGuard } from "./safety";
import { get_tfiles_from_folder } from "./utils/files";
import type { FormDefinition, FormOptions } from "./core/formDefinition";
Expand Down Expand Up @@ -32,7 +32,7 @@ export class FormModal extends Modal {
options?: FormOptions,
) {
super(app);
this.initialFormValues = options?.values ? formDataFromFormOptions(options.values) : {};
this.initialFormValues = options?.values ? formDataFromFormDefaults(modalDefinition.fields, options.values) : {};
this.formEngine = makeFormEngine((result) => {
this.onSubmit(new FormResult(result, "ok"));
this.close();
Expand Down
18 changes: 10 additions & 8 deletions src/core/FormResult.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ isEmployed:: true`;
const expectedOutput = `name:: John Doe
age:: 30`;
expect(
result.asDataviewProperties({ pick: ["name", "age"] })
result.asDataviewProperties({ pick: ["name", "age"] }),
).toEqual(expectedOutput);
});

Expand All @@ -76,7 +76,9 @@ age:: 30`;
const expectedOutput = `name:: John Doe
age:: 30`;
expect(
result.asDataviewProperties({ omit: ["hobbies", "isEmployed"] })
result.asDataviewProperties({
omit: ["hobbies", "isEmployed"],
}),
).toEqual(expectedOutput);
});

Expand All @@ -88,7 +90,7 @@ age:: 30`;
result.asDataviewProperties({
pick: ["name", "age"],
omit: ["hobbies", "isEmployed"],
})
}),
).toEqual(expectedOutput);
});

Expand All @@ -100,7 +102,7 @@ age:: 30`;
result.asDataviewProperties({
omit: ["hobbies", "isEmployed"],
pick: ["name", "age"],
})
}),
).toEqual(expectedOutput);
});
});
Expand All @@ -110,7 +112,7 @@ age:: 30`;
const expectedOutput = `name: John Doe
age: 30`;
expect(
result.asFrontmatterString({ pick: ["name", "age"] }).trim()
result.asFrontmatterString({ pick: ["name", "age"] }).trim(),
).toEqual(expectedOutput);
});

Expand All @@ -121,7 +123,7 @@ age: 30`;
expect(
result
.asFrontmatterString({ omit: ["hobbies", "isEmployed"] })
.trim()
.trim(),
).toEqual(expectedOutput);
});

Expand All @@ -135,7 +137,7 @@ age: 30`;
pick: ["name", "age"],
omit: ["hobbies", "isEmployed"],
})
.trim()
.trim(),
).toEqual(expectedOutput);
});

Expand All @@ -149,7 +151,7 @@ age: 30`;
omit: ["hobbies", "isEmployed"],
pick: ["name", "age"],
})
.trim()
.trim(),
).toEqual(expectedOutput);
});
});
Expand Down
28 changes: 4 additions & 24 deletions src/core/FormResult.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,20 @@
import { objectSelect } from './objectSelect';
import { stringifyYaml } from "obsidian";
import { log_error } from "../utils/Log";
import { ModalFormError } from "../utils/ModalFormError";

type ResultStatus = "ok" | "cancelled";

export type Val = string | boolean | number | string[]
// We don't use FormData because that is builtin browser API
export type ModalFormData = { [key: string]: string | boolean | number | string[] };
export type ModalFormData = { [key: string]: Val };

function isPrimitive(value: unknown): value is string | boolean | number {
export function isPrimitive(value: unknown): value is string | boolean | number {
return typeof value === 'string' || typeof value === 'boolean' || typeof value === 'number';
}

function isPrimitiveArray(value: unknown): value is string[] {
export function isPrimitiveArray(value: unknown): value is string[] {
return Array.isArray(value) && value.every(isPrimitive)
}


export function formDataFromFormOptions(values: Record<string, unknown>) {
const result: ModalFormData = {};
const invalidKeys = []
for (const [key, value] of Object.entries(values)) {
if (Array.isArray(value) && isPrimitiveArray(value)) {
result[key] = value;
} else if (isPrimitive(value)) {
result[key] = value;
} else {
invalidKeys.push(key)
}
}
if (invalidKeys.length > 0) {
log_error(new ModalFormError(`Invalid keys in form options: ${invalidKeys.join(', ')}`))
}
return result;
}

export default class FormResult {
constructor(private data: ModalFormData, public status: ResultStatus) { }
/**
Expand Down
89 changes: 89 additions & 0 deletions src/core/formDataFromDefaults.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { FormDefinition } from "./formDefinition";
import { formDataFromFormDefaults } from "./formDataFromFormDefaults";

describe("formDataFromFormOptions", () => {
const fields: FormDefinition["fields"] = [
{ name: "age", description: "", input: { type: "number" } },
{
name: "name",
description: "Enter your name",
input: { type: "text" },
},
{ name: "age", description: "", input: { type: "number" } },
{
name: "hobbies",
description: "Select your hobbies",
input: {
type: "multiselect",
source: "fixed",
multi_select_options: ["reading", "swimming", "running"],
},
},
{ name: "isEmployed", description: "", input: { type: "toggle" } },
];

it("should return the correct form data when all values are valid", () => {
const values = {
name: "John Doe",
age: 30,
hobbies: ["reading", "swimming"],
isEmployed: true,
};

const expectedFormData = {
name: "John Doe",
age: 30,
hobbies: ["reading", "swimming"],
isEmployed: true,
};

const result = formDataFromFormDefaults(fields, values);
expect(result).toEqual(expectedFormData);
});

it("should ignore invalid keys and return the correct form data", () => {
const values = {
name: "John Doe",
age: 30,
hobbies: ["reading", "swimming"],
isEmployed: true,
invalidKey: "invalidValue",
};

const expectedFormData = {
name: "John Doe",
age: 30,
hobbies: ["reading", "swimming"],
isEmployed: true,
};

const result = formDataFromFormDefaults(fields, values);
expect(result).toEqual(expectedFormData);
});

it("should ensure toggles always have avalue even when no values are provided", () => {
const values = {};

const expectedFormData = { isEmployed: false };

const result = formDataFromFormDefaults(fields, values);
expect(result).toEqual(expectedFormData);
});

it("should return the correct form data when some values are missing", () => {
const values = {
name: "John Doe",
age: 30,
isEmployed: true,
};

const expectedFormData = {
name: "John Doe",
age: 30,
isEmployed: true,
};

const result = formDataFromFormDefaults(fields, values);
expect(result).toEqual(expectedFormData);
});
});
43 changes: 43 additions & 0 deletions src/core/formDataFromFormDefaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as R from 'fp-ts/Record';
import * as O from 'fp-ts/Option';
import { log_error } from "../utils/Log";
import { ModalFormError } from "../utils/ModalFormError";
import { FormDefinition } from './formDefinition';
import { A, pipe } from '@std';
import { ModalFormData, isPrimitiveArray, isPrimitive, Val } from './FormResult';


/**
* Given a form definition fields and a record containing the expected default values
* for the form, return a record containing only the values that are present in the form definition
* and filters out invalid values
* */
export function formDataFromFormDefaults(fields: FormDefinition['fields'], values: Record<string, unknown>): ModalFormData {
const result: ModalFormData = {};
const invalidKeys = [];
for (const [key, value] of Object.entries(values)) {
if (Array.isArray(value) && isPrimitiveArray(value)) {
result[key] = value;
} else if (isPrimitive(value)) {
result[key] = value;
} else {
invalidKeys.push(key);
}
}
if (invalidKeys.length > 0) {
log_error(new ModalFormError(`Invalid keys in form options: ${invalidKeys.join(', ')}`));
}
return pipe(
fields,
A.map((field) => {
return pipe(
result[field.name],
O.fromNullable,
O.match(() => field.input.type === 'toggle' ? O.some(false) : O.none, O.some),
O.map((value) => [field.name, value] as [string, Val])
);
}),
A.compact,
R.fromEntries
);
}
3 changes: 2 additions & 1 deletion src/std/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { pipe as p, flow as f } from "fp-ts/function";
import { partitionMap, findFirst, findFirstMap, partition, map as mapArr, filter } from "fp-ts/Array";
import { partitionMap, findFirst, findFirstMap, partition, map as mapArr, filter, compact } from "fp-ts/Array";
import { map as mapO, getOrElse as getOrElseOpt, some, none, fromNullable as fromNullableOpt } from 'fp-ts/Option'
import { isLeft, isRight, tryCatchK, map, getOrElse, fromNullable, right, left, mapLeft, Either, bimap, tryCatch, flatMap } from "fp-ts/Either";
import { BaseSchema, Output, ValiError, parse as parseV } from "valibot";
Expand All @@ -12,6 +12,7 @@ export const pipe = p
export const A = {
partitionMap,
partition,
compact,
findFirst,
findFirstMap,
map: mapArr,
Expand Down
1 change: 0 additions & 1 deletion src/store/formStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ export function makeFormEngine<T extends FieldValue>(
),
triggerSubmit() {
const formState = get(formStore);
console.log("Form state", formState.fields);
pipe(formState.fields, parseForm, E.match(setErrors, onSubmit));
},
addField: (field) => {
Expand Down
20 changes: 19 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,25 @@
},
"NEA": {
"importPath": "fp-ts/NonEmptyArray"
}
},
"E": {
"importPath": "fp-ts/Either"
},
"A": {
"importPath": "fp-ts/Array"
},
"T": {
"importPath": "fp-ts/Task"
},
"TE": {
"importPath": "fp-ts/TaskEither"
},
"R": {
"importPath": "fp-ts/Record"
},
"RA": {
"importPath": "fp-ts/ReadonlyArray"
},
}
}
]
Expand Down

0 comments on commit afae1a0

Please sign in to comment.