Last active
August 7, 2022 14:43
-
-
Save tlimpanont/2cbbf369dccc902277e4b4704b837976 to your computer and use it in GitHub Desktop.
xState form submission machine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { describe, expect, it } from "vitest"; | |
import { interpret } from "xstate"; | |
import { formSubmissionMachine } from "../formSubmissionMachine"; | |
import { waitFor } from "xstate/lib/waitFor"; | |
describe("formSubmissionMachine", () => { | |
it("should validate name and save in DB", async () => { | |
const machine = formSubmissionMachine.withConfig({ | |
services: { | |
validateName: (context) => { | |
return Promise.resolve({ | |
config: { | |
params: { | |
name: "Henk", | |
}, | |
}, | |
}); | |
}, | |
postDB: (context) => { | |
return Promise.resolve(); | |
}, | |
}, | |
}); | |
const service = interpret(machine); | |
service.start(); | |
expect(service.state.value).toBe("enteringData"); | |
service.send({ type: "VALIDATE", value: { name: "Henk" } }); | |
expect(service.state.value).toBe("validating"); | |
await waitFor(service, (state) => state.matches("readyToSubmit")); | |
expect(service.state.context.name).toBe("Henk"); | |
service.send({ type: "SAVE_IN_DB", value: { name: "Henk" } }); | |
await waitFor(service, (state) => state.matches("dataSaved")); | |
expect(service.state.done).toBeTruthy(); | |
}); | |
it("should failed to validate name", async () => { | |
const machine = formSubmissionMachine.withConfig({ | |
services: { | |
validateName: (context) => { | |
return Promise.reject(); | |
}, | |
}, | |
}); | |
const service = interpret(machine); | |
service.start(); | |
expect(service.state.value).toBe("enteringData"); | |
service.send({ type: "VALIDATE", value: { name: "Henk" } }); | |
expect(service.state.value).toBe("validating"); | |
await waitFor(service, (state) => state.matches("enteringData")); | |
expect(service.state.context.name).toBeUndefined(); | |
}); | |
it("should failed to save to DB", async () => { | |
const machine = formSubmissionMachine.withConfig({ | |
services: { | |
validateName: (context) => { | |
return Promise.resolve({ | |
config: { | |
params: { | |
name: "Henk", | |
}, | |
}, | |
}); | |
}, | |
postDB: (context) => { | |
return Promise.reject(); | |
}, | |
}, | |
}); | |
const service = interpret(machine); | |
service.start(); | |
service.send({ type: "VALIDATE", value: { name: "Henk" } }); | |
await waitFor(service, (state) => state.matches("readyToSubmit")); | |
service.send({ type: "SAVE_IN_DB", value: { name: "Henk" } }); | |
await waitFor(service, (state) => state.matches("enteringData")); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { assign, createMachine } from "xstate"; | |
import { validateName } from "../services/validateName"; | |
import { postDB } from "../services/postDB"; | |
export type FormSubmissionContext = { | |
name?: string; | |
}; | |
export type FormSubmissionEvent = | |
| { type: "VALIDATE"; value: FormSubmissionContext } | |
| { type: "SAVE_IN_DB"; value: FormSubmissionContext } | |
| { type: "EDIT_DATA"; value: FormSubmissionContext }; | |
export type FormSubmissionTypeState = | |
| { | |
value: "enteringData"; | |
context: FormSubmissionContext; | |
} | |
| { | |
value: "validating"; | |
context: FormSubmissionContext; | |
} | |
| { | |
value: "readyToSubmit"; | |
context: FormSubmissionContext; | |
} | |
| { | |
value: "sendingToDB"; | |
context: FormSubmissionContext; | |
} | |
| { | |
value: "dataSaved"; | |
context: FormSubmissionContext; | |
} | |
| { | |
value: "dataFailedToSave"; | |
context: FormSubmissionContext; | |
}; | |
export const formSubmissionMachine = createMachine< | |
FormSubmissionContext, | |
FormSubmissionEvent, | |
FormSubmissionTypeState | |
>( | |
{ | |
predictableActionArguments: true, | |
initial: "enteringData", | |
context: { | |
name: "", | |
}, | |
states: { | |
enteringData: { | |
on: { | |
VALIDATE: { | |
target: "validating", | |
}, | |
}, | |
}, | |
validating: { | |
invoke: { | |
id: "validateName", | |
src: validateName, | |
onDone: { | |
target: "readyToSubmit", | |
actions: assign((context, event) => { | |
return { | |
name: event.data.config.params.name, | |
}; | |
}), | |
}, | |
onError: { | |
target: "enteringData", | |
actions: assign((context, event) => { | |
return { | |
name: undefined, | |
}; | |
}), | |
}, | |
}, | |
}, | |
readyToSubmit: { | |
on: { | |
SAVE_IN_DB: { | |
target: "sendingToDB", | |
cond: (context) => context.name !== undefined, | |
}, | |
VALIDATE: { | |
target: "validating", | |
}, | |
}, | |
}, | |
sendingToDB: { | |
invoke: { | |
id: "postDB", | |
src: postDB, | |
onDone: { | |
target: "dataSaved", | |
}, | |
onError: { | |
target: "enteringData", | |
}, | |
}, | |
}, | |
dataSaved: { | |
type: "final", | |
}, | |
}, | |
}, | |
{ | |
actions: {}, | |
services: {}, | |
guards: {}, | |
} | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment