alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
72 lines (64 loc) • 2.28 kB
text/typescript
import { Alepha, z } from "alepha";
import { AlephaLogger } from "alepha/logger";
import { describe, it } from "vitest";
import { FormModel } from "../services/FormModel.ts";
/**
* Repro for: a form whose submit fails TypeBox validation (missing field)
* leaves the submit button stuck in its loading state forever.
*
* The button's loading is driven by the `form:submit:begin` / `form:submit:end`
* event pair (see useFormState). So the invariant that matters is: every
* `form:submit:begin` is followed by a `form:submit:end`, even when validation
* throws AND even when a downstream error listener (e.g. the action-error
* toaster) itself throws.
*/
describe("FormModel.submit loading pairing", () => {
const makeForm = (alepha: Alepha) =>
alepha.inject(FormModel as any, {
lifetime: "transient",
args: [
"f1",
{
id: "f1",
schema: z.object({ email: z.text(), password: z.text() }),
handler: () => {},
initialValues: {}, // both required fields missing → decode throws
},
],
}) as FormModel<any>;
it("emits form:submit:end on a validation error (intrinsic)", async ({
expect,
}) => {
const alepha = Alepha.create().with(AlephaLogger);
await alepha.start();
const form = makeForm(alepha);
let begun = false;
let ended = false;
alepha.events.on("form:submit:begin", () => {
begun = true;
});
alepha.events.on("form:submit:end", () => {
ended = true;
});
await form.submit().catch(() => {});
expect(begun).toBe(true);
expect(ended).toBe(true); // pairing must hold
});
it("emits form:submit:end even when a react:action:error listener throws", async ({
expect,
}) => {
const alepha = Alepha.create().with(AlephaLogger);
await alepha.start();
const form = makeForm(alepha);
let ended = false;
alepha.events.on("form:submit:end", () => {
ended = true;
});
// Simulates a globally-mounted listener (e.g. ActionErrorToaster) throwing.
alepha.events.on("react:action:error", () => {
throw new Error("toaster boom");
});
await form.submit().catch(() => {});
expect(ended).toBe(true); // currently FAILS: end is skipped → button stuck
});
});