181 lines
5.4 KiB
TypeScript
181 lines
5.4 KiB
TypeScript
import { describe, expect, it } from "@suaveplan/testing/runner";
|
|
import { FSM_CODES, FSMError } from "../errors/fsm-error.js";
|
|
import { validateDefinition } from "./definition-validator.js";
|
|
|
|
describe("validateDefinition", () => {
|
|
it("accepts a valid minimal definition", { timeout: 5000 }, () => {
|
|
expect(() =>
|
|
validateDefinition({
|
|
initial: "idle",
|
|
context: {},
|
|
states: { idle: {} },
|
|
})
|
|
).not.toThrow();
|
|
});
|
|
|
|
it("accepts a valid multi-state definition", { timeout: 5000 }, () => {
|
|
expect(() =>
|
|
validateDefinition({
|
|
initial: "red",
|
|
context: {},
|
|
states: {
|
|
red: { on: { NEXT: { target: "green" } } },
|
|
green: { on: { NEXT: { target: "yellow" } } },
|
|
yellow: { on: { NEXT: { target: "red" } } },
|
|
},
|
|
})
|
|
).not.toThrow();
|
|
});
|
|
|
|
it("throws INVALID_DEFINITION when initial state does not exist", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
let err: FSMError | undefined;
|
|
try {
|
|
validateDefinition({
|
|
initial: "ghost",
|
|
context: {},
|
|
states: { idle: {} },
|
|
});
|
|
} catch (e) {
|
|
err = e as FSMError;
|
|
}
|
|
expect(err).toBeInstanceOf(FSMError);
|
|
expect(err?.code).toBe(FSM_CODES.INVALID_DEFINITION);
|
|
expect(err?.message).toContain("ghost");
|
|
});
|
|
|
|
it("throws INVALID_DEFINITION when transition targets non-existent state", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
let err: FSMError | undefined;
|
|
try {
|
|
validateDefinition({
|
|
initial: "idle",
|
|
context: {},
|
|
states: {
|
|
idle: { on: { GO: { target: "nowhere" } } },
|
|
},
|
|
});
|
|
} catch (e) {
|
|
err = e as FSMError;
|
|
}
|
|
expect(err).toBeInstanceOf(FSMError);
|
|
expect(err?.code).toBe(FSM_CODES.INVALID_DEFINITION);
|
|
expect(err?.message).toContain("nowhere");
|
|
});
|
|
|
|
it("accepts multiple transition candidates for the same event", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
expect(() =>
|
|
validateDefinition({
|
|
initial: "idle",
|
|
context: { count: 0 },
|
|
states: {
|
|
idle: {
|
|
on: {
|
|
GO: [{ target: "active" }, { target: "inactive" }],
|
|
},
|
|
},
|
|
active: {},
|
|
inactive: {},
|
|
},
|
|
})
|
|
).not.toThrow();
|
|
});
|
|
|
|
it("throws when nested initial child does not exist", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
let err: FSMError | undefined;
|
|
try {
|
|
validateDefinition({
|
|
initial: "form",
|
|
context: {},
|
|
states: {
|
|
form: {
|
|
initial: "step999",
|
|
states: {
|
|
step1: {},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
} catch (e) {
|
|
err = e as FSMError;
|
|
}
|
|
expect(err).toBeInstanceOf(FSMError);
|
|
expect(err?.code).toBe(FSM_CODES.INVALID_DEFINITION);
|
|
expect(err?.message).toContain("step999");
|
|
});
|
|
|
|
it("throws when nested states exist but no initial is declared", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
let err: FSMError | undefined;
|
|
try {
|
|
validateDefinition({
|
|
initial: "form",
|
|
context: {},
|
|
states: {
|
|
form: {
|
|
states: {
|
|
step1: {},
|
|
},
|
|
// initial deliberately omitted
|
|
},
|
|
},
|
|
});
|
|
} catch (e) {
|
|
err = e as FSMError;
|
|
}
|
|
expect(err).toBeInstanceOf(FSMError);
|
|
expect(err?.code).toBe(FSM_CODES.INVALID_DEFINITION);
|
|
});
|
|
|
|
it("accepts a valid nested definition with correct initial", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
expect(() =>
|
|
validateDefinition({
|
|
initial: "form",
|
|
context: {},
|
|
states: {
|
|
form: {
|
|
initial: "step1",
|
|
states: {
|
|
step1: { on: { NEXT: { target: "step2" } } },
|
|
step2: {},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
).not.toThrow();
|
|
});
|
|
|
|
it("throws when a transition target in nested states is unknown", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
let err: FSMError | undefined;
|
|
try {
|
|
validateDefinition({
|
|
initial: "form",
|
|
context: {},
|
|
states: {
|
|
form: {
|
|
initial: "step1",
|
|
states: {
|
|
step1: { on: { GO: { target: "ghost" } } },
|
|
},
|
|
},
|
|
},
|
|
});
|
|
} catch (e) {
|
|
err = e as FSMError;
|
|
}
|
|
expect(err).toBeInstanceOf(FSMError);
|
|
expect(err?.code).toBe(FSM_CODES.INVALID_DEFINITION);
|
|
});
|
|
});
|