diff --git a/packages/kata-for-gabriel/src/cast-die.ts b/packages/kata-for-gabriel/src/cast-die.ts new file mode 100644 index 0000000000..3b78a0fffb --- /dev/null +++ b/packages/kata-for-gabriel/src/cast-die.ts @@ -0,0 +1,9 @@ +import { getInjectable } from "@ogre-tools/injectable"; + +const castDieInjectable = getInjectable({ + id: "cast-die", + instantiate: () => () => Promise.resolve(0), + causesSideEffects: true, +}); + +export default castDieInjectable; diff --git a/packages/kata-for-gabriel/src/handle-attack-on-monster-for.ts b/packages/kata-for-gabriel/src/handle-attack-on-monster-for.ts index eb3648ac30..715e52868d 100644 --- a/packages/kata-for-gabriel/src/handle-attack-on-monster-for.ts +++ b/packages/kata-for-gabriel/src/handle-attack-on-monster-for.ts @@ -1,19 +1,19 @@ -import type { Monster } from "./handle-landed-hit-on-monster-for"; -import type { Dependencies } from "./monster-beatdown"; +import { getInjectable } from "@ogre-tools/injectable"; +import messageToPlayerInjectable from "./message-to-player"; +import castDieInjectable from "./cast-die"; +import monsterInjectable from "./monster"; +import handleLandedHitOnMonsterInjectable from "./handle-landed-hit-on-monster-for"; -export const handleAttackOnMonsterFor = - ({ - monster, - messageToPlayer, - handleLandedHitOnMonster, - castDie, - }: { - monster: Monster; - messageToPlayer: Dependencies["messageToPlayer"]; - handleLandedHitOnMonster: () => { monsterIsDead: boolean }; - castDie: () => Promise; - }) => - async () => { +const handleAttackOnMonsterInjectable = getInjectable({ + id: "handle-attack-on-monster", + + instantiate: (di) => { + const messageToPlayer = di.inject(messageToPlayerInjectable); + const castDie = di.inject(castDieInjectable); + const handleLandedHitOnMonster = di.inject(handleLandedHitOnMonsterInjectable); + const monster = di.inject(monsterInjectable); + + return async () => { messageToPlayer("You attack the monster."); const dieResult = await castDie(); @@ -32,3 +32,7 @@ export const handleAttackOnMonsterFor = return { gameIsOver: false }; }; + }, +}); + +export default handleAttackOnMonsterInjectable; diff --git a/packages/kata-for-gabriel/src/handle-attacking-the-monster-again-for.ts b/packages/kata-for-gabriel/src/handle-attacking-the-monster-again-for.ts index b8b37e8d88..7a90fe5a03 100644 --- a/packages/kata-for-gabriel/src/handle-attacking-the-monster-again-for.ts +++ b/packages/kata-for-gabriel/src/handle-attacking-the-monster-again-for.ts @@ -1,25 +1,30 @@ -import type { Dependencies } from "./monster-beatdown"; +import { getInjectable } from "@ogre-tools/injectable"; +import questionToPlayerInjectable from "./question-to-player"; +import messageToPlayerInjectable from "./message-to-player"; -export const handleAttackingTheMonsterAgainFor = - ({ - messageToPlayer, - questionToPlayer, - }: { - messageToPlayer: Dependencies["messageToPlayer"]; - questionToPlayer: Dependencies["questionToPlayer"]; - }) => - async () => { - const playerWantsToAttackAgain = await questionToPlayer("Do you want to attack again?"); +const handleAttackingTheMonsterAgainInjectable = getInjectable({ + id: "handle-attacking-the-monster-again", - if (playerWantsToAttackAgain) { - return { gameIsOver: false }; - } + instantiate: (di) => { + const questionToPlayer = di.inject(questionToPlayerInjectable); + const messageToPlayer = di.inject(messageToPlayerInjectable); - messageToPlayer( - "You lose your nerve mid-beat-down, and try to run away. You get eaten by a sad, disappointed monster.", - ); + return async () => { + const playerWantsToAttackAgain = await questionToPlayer("Do you want to attack again?"); - messageToPlayer("You lose. Game over!"); + if (playerWantsToAttackAgain) { + return { gameIsOver: false }; + } - return { gameIsOver: true }; - }; + messageToPlayer( + "You lose your nerve mid-beat-down, and try to run away. You get eaten by a sad, disappointed monster.", + ); + + messageToPlayer("You lose. Game over!"); + + return { gameIsOver: true }; + }; + }, +}); + +export default handleAttackingTheMonsterAgainInjectable; diff --git a/packages/kata-for-gabriel/src/handle-initial-monster-encounter-for.ts b/packages/kata-for-gabriel/src/handle-initial-monster-encounter-for.ts index 96e9106f6a..c6c63a56ac 100644 --- a/packages/kata-for-gabriel/src/handle-initial-monster-encounter-for.ts +++ b/packages/kata-for-gabriel/src/handle-initial-monster-encounter-for.ts @@ -1,17 +1,17 @@ -import type { Monster } from "./handle-landed-hit-on-monster-for"; -import type { Dependencies } from "./monster-beatdown"; +import { getInjectable } from "@ogre-tools/injectable"; +import messageToPlayerInjectable from "./message-to-player"; +import questionToPlayerInjectable from "./question-to-player"; +import monsterInjectable from "./monster"; -export const handleInitialMonsterEncounterFor = - ({ - monster, - messageToPlayer, - questionToPlayer, - }: { - monster: Monster; - messageToPlayer: Dependencies["messageToPlayer"]; - questionToPlayer: Dependencies["questionToPlayer"]; - }) => - async () => { +const handleInitialMonsterEncounterInjectable = getInjectable({ + id: "handle-initial-monster-encounter", + + instantiate: (di) => { + const messageToPlayer = di.inject(messageToPlayerInjectable); + const questionToPlayer = di.inject(questionToPlayerInjectable); + const monster = di.inject(monsterInjectable); + + return async () => { messageToPlayer(`You encounter a monster with ${monster.hitPoints} hit-points`); const playerWantsToAttack = await questionToPlayer("Attack the monster?"); @@ -27,3 +27,7 @@ export const handleInitialMonsterEncounterFor = return { gameIsOver: true }; }; + }, +}); + +export default handleInitialMonsterEncounterInjectable; diff --git a/packages/kata-for-gabriel/src/handle-landed-hit-on-monster-for.ts b/packages/kata-for-gabriel/src/handle-landed-hit-on-monster-for.ts index d0d244c15c..dede30aa16 100644 --- a/packages/kata-for-gabriel/src/handle-landed-hit-on-monster-for.ts +++ b/packages/kata-for-gabriel/src/handle-landed-hit-on-monster-for.ts @@ -1,18 +1,15 @@ -import type { Dependencies } from "./monster-beatdown"; +import { getInjectable } from "@ogre-tools/injectable"; +import monsterInjectable from "./monster"; +import messageToPlayerInjectable from "./message-to-player"; -export type Monster = { - hitPoints: number; -}; +const handleLandedHitOnMonsterInjectable = getInjectable({ + id: "handle-landed-hit-on-monster", -export const handleLandedHitOnMonsterFor = - ({ - monster, - messageToPlayer, - }: { - monster: Monster; - messageToPlayer: Dependencies["messageToPlayer"]; - }) => - () => { + instantiate: (di) => { + const monster = di.inject(monsterInjectable); + const messageToPlayer = di.inject(messageToPlayerInjectable); + + return () => { monster.hitPoints--; if (monster.hitPoints) { @@ -31,3 +28,7 @@ export const handleLandedHitOnMonsterFor = return { monsterIsDead: true }; }; + }, +}); + +export default handleLandedHitOnMonsterInjectable; diff --git a/packages/kata-for-gabriel/src/message-to-player.ts b/packages/kata-for-gabriel/src/message-to-player.ts new file mode 100644 index 0000000000..9830e04d0b --- /dev/null +++ b/packages/kata-for-gabriel/src/message-to-player.ts @@ -0,0 +1,9 @@ +import { getInjectable } from "@ogre-tools/injectable"; + +const messageToPlayerInjectable = getInjectable({ + id: "message-to-player", + instantiate: () => (message: string) => {}, + causesSideEffects: true, +}); + +export default messageToPlayerInjectable; diff --git a/packages/kata-for-gabriel/src/monster-beatdown.test.ts b/packages/kata-for-gabriel/src/monster-beatdown.test.ts index b52589aceb..0df9457da2 100644 --- a/packages/kata-for-gabriel/src/monster-beatdown.test.ts +++ b/packages/kata-for-gabriel/src/monster-beatdown.test.ts @@ -1,6 +1,15 @@ -import { createGame, Dependencies } from "./monster-beatdown"; +import { Dependencies, gameInjectable } from "./monster-beatdown"; import asyncFn, { AsyncFnMock } from "@async-fn/jest"; import { getPromiseStatus } from "@k8slens/test-utils"; +import { createContainer } from "@ogre-tools/injectable"; +import messageToPlayerInjectable from "./message-to-player"; +import castDieInjectable from "./cast-die"; +import questionToPlayerInjectable from "./question-to-player"; +import handleInitialMonsterEncounterInjectable from "./handle-initial-monster-encounter-for"; +import monsterInjectable from "./monster"; +import handleAttackOnMonsterInjectable from "./handle-attack-on-monster-for"; +import handleLandedHitOnMonsterInjectable from "./handle-landed-hit-on-monster-for"; +import handleAttackingTheMonsterAgainInjectable from "./handle-attacking-the-monster-again-for"; describe("monster-beatdown", () => { let game: { start: () => Promise }; @@ -10,15 +19,29 @@ describe("monster-beatdown", () => { let gamePromise: Promise; beforeEach(() => { + const di = createContainer("monster-beatdown"); + + di.register( + castDieInjectable, + gameInjectable, + handleAttackOnMonsterInjectable, + handleAttackingTheMonsterAgainInjectable, + handleInitialMonsterEncounterInjectable, + handleLandedHitOnMonsterInjectable, + messageToPlayerInjectable, + monsterInjectable, + questionToPlayerInjectable, + ); + messageToPlayerMock = jest.fn(); + di.override(messageToPlayerInjectable, () => messageToPlayerMock); + di.override(questionToPlayerInjectable, () => questionToPlayerMock); + di.override(castDieInjectable, () => castDieMock); + questionToPlayerMock = asyncFn(); castDieMock = asyncFn(); - game = createGame({ - messageToPlayer: messageToPlayerMock, - questionToPlayer: questionToPlayerMock, - castDie: castDieMock, - }); + game = di.inject(gameInjectable); }); describe("when game is not started", () => { diff --git a/packages/kata-for-gabriel/src/monster-beatdown.ts b/packages/kata-for-gabriel/src/monster-beatdown.ts index 3371250c5d..21b7519864 100644 --- a/packages/kata-for-gabriel/src/monster-beatdown.ts +++ b/packages/kata-for-gabriel/src/monster-beatdown.ts @@ -1,7 +1,7 @@ -import { handleLandedHitOnMonsterFor, Monster } from "./handle-landed-hit-on-monster-for"; -import { handleInitialMonsterEncounterFor } from "./handle-initial-monster-encounter-for"; -import { handleAttackOnMonsterFor } from "./handle-attack-on-monster-for"; -import { handleAttackingTheMonsterAgainFor } from "./handle-attacking-the-monster-again-for"; +import { getInjectable } from "@ogre-tools/injectable"; +import handleInitialMonsterEncounterInjectable from "./handle-initial-monster-encounter-for"; +import handleAttackOnMonsterInjectable from "./handle-attack-on-monster-for"; +import handleAttackingTheMonsterAgainInjectable from "./handle-attacking-the-monster-again-for"; export type Dependencies = { messageToPlayer: (message: string) => void; @@ -9,49 +9,33 @@ export type Dependencies = { castDie: () => Promise; }; -const createGame = ({ messageToPlayer, questionToPlayer, castDie }: Dependencies) => { - const monster: Monster = { hitPoints: 3 }; +export const gameInjectable = getInjectable({ + id: "game", - const handleLandedHitOnMonster = handleLandedHitOnMonsterFor({ messageToPlayer, monster }); + instantiate: (di) => { + const handleInitialMonsterEncounter = di.inject(handleInitialMonsterEncounterInjectable); + const handleAttackOnMonster = di.inject(handleAttackOnMonsterInjectable); + const handleAttackingTheMonsterAgain = di.inject(handleAttackingTheMonsterAgainInjectable); - const handleAttackOnMonster = handleAttackOnMonsterFor({ - monster, - messageToPlayer, - handleLandedHitOnMonster, - castDie, - }); - - const handleAttackingTheMonsterAgain = handleAttackingTheMonsterAgainFor({ - messageToPlayer, - questionToPlayer, - }); - - const handleInitialMonsterEncounter = handleInitialMonsterEncounterFor({ - messageToPlayer, - questionToPlayer, - monster, - }); - - return { - start: async () => { - const initialEncounterResult = await handleInitialMonsterEncounter(); - if (initialEncounterResult.gameIsOver) { - return; - } - - while (true) { - const attackResult = await handleAttackOnMonster(); - if (attackResult.gameIsOver) { + return { + start: async () => { + const initialEncounterResult = await handleInitialMonsterEncounter(); + if (initialEncounterResult.gameIsOver) { return; } - const attackAgainResult = await handleAttackingTheMonsterAgain(); - if (attackAgainResult.gameIsOver) { - return; - } - } - }, - }; -}; + while (true) { + const attackResult = await handleAttackOnMonster(); + if (attackResult.gameIsOver) { + return; + } -export { createGame }; + const attackAgainResult = await handleAttackingTheMonsterAgain(); + if (attackAgainResult.gameIsOver) { + return; + } + } + }, + }; + }, +}); diff --git a/packages/kata-for-gabriel/src/monster.ts b/packages/kata-for-gabriel/src/monster.ts new file mode 100644 index 0000000000..aa78edf6b2 --- /dev/null +++ b/packages/kata-for-gabriel/src/monster.ts @@ -0,0 +1,8 @@ +import { getInjectable } from "@ogre-tools/injectable"; + +const monsterInjectable = getInjectable({ + id: "monster", + instantiate: () => ({ hitPoints: 3 }), +}); + +export default monsterInjectable; diff --git a/packages/kata-for-gabriel/src/question-to-player.ts b/packages/kata-for-gabriel/src/question-to-player.ts new file mode 100644 index 0000000000..c6b3afdc69 --- /dev/null +++ b/packages/kata-for-gabriel/src/question-to-player.ts @@ -0,0 +1,9 @@ +import { getInjectable } from "@ogre-tools/injectable"; + +const questionToPlayerInjectable = getInjectable({ + id: "question-to-player", + instantiate: () => (question: string) => Promise.resolve(true), + causesSideEffects: true, +}); + +export default questionToPlayerInjectable;