diff --git a/core/src/activity-utils/makeActivityReducer.spec.ts b/core/src/activity-utils/makeActivityReducer.spec.ts new file mode 100644 index 000000000..f81adf8ff --- /dev/null +++ b/core/src/activity-utils/makeActivityReducer.spec.ts @@ -0,0 +1,58 @@ +import { makeEvent } from "../event-utils"; +import type { Activity } from "../Stack"; +import { makeActivityReducer } from "./makeActivityReducer"; + +function makeActivity(): Activity { + const pushedEvent = makeEvent("Pushed", { + activityId: "a1", + activityName: "sample", + activityParams: { page: "1" }, + }); + + const stepPushedEvent = makeEvent("StepPushed", { + stepId: "s2", + stepParams: { page: "2" }, + }); + + return { + id: "a1", + name: "sample", + transitionState: "enter-done", + params: { page: "2" }, + enteredBy: pushedEvent, + steps: [ + { + id: "a1", + params: { page: "1" }, + enteredBy: pushedEvent, + zIndex: -1, + }, + { + id: "s2", + params: { page: "2" }, + enteredBy: stepPushedEvent, + zIndex: -1, + }, + ], + isTop: true, + isActive: true, + isRoot: true, + zIndex: 0, + }; +} + +test("makeActivityReducer - StepPopped는 activity.steps를 변경하지 않고 새 배열을 반환합니다", () => { + const activity = makeActivity(); + const originalSteps = activity.steps; + + const reducer = makeActivityReducer({ transitionDuration: 300, now: 0 }); + const stepPoppedEvent = makeEvent("StepPopped", {}); + + const nextActivity = reducer(activity, stepPoppedEvent); + + expect(activity.steps).toBe(originalSteps); + expect(activity.steps.length).toBe(2); + expect(nextActivity.steps).not.toBe(originalSteps); + expect(nextActivity.steps.length).toBe(1); + expect(nextActivity.params).toEqual({ page: "1" }); +}); diff --git a/core/src/activity-utils/makeActivityReducer.ts b/core/src/activity-utils/makeActivityReducer.ts index fac85e2d7..1bdb0e989 100644 --- a/core/src/activity-utils/makeActivityReducer.ts +++ b/core/src/activity-utils/makeActivityReducer.ts @@ -108,13 +108,14 @@ export function makeActivityReducer(context: { * If there are params in the previous step, set them as the new params */ StepPopped: (activity: Activity, event: StepPoppedEvent): Activity => { - activity.steps.pop(); + const steps = activity.steps.slice(0, -1); - const beforeActivityParams = last(activity.steps)?.params; + const beforeActivityParams = last(steps)?.params; return { ...activity, params: beforeActivityParams ?? activity.params, + steps, }; }, diff --git a/core/src/activity-utils/makeReducer.spec.ts b/core/src/activity-utils/makeReducer.spec.ts new file mode 100644 index 000000000..62c57fbf0 --- /dev/null +++ b/core/src/activity-utils/makeReducer.spec.ts @@ -0,0 +1,50 @@ +import type { DomainEvent } from "../event-types"; +import { makeReducer } from "./makeReducer"; + +test("makeReducer - 등록된 이벤트는 해당 reducer로 처리됩니다", () => { + const reducer = makeReducer({ + Initialized: (state) => state + 1, + ActivityRegistered: (state) => state, + Pushed: (state) => state, + Replaced: (state) => state, + Popped: (state) => state, + StepPushed: (state) => state, + StepReplaced: (state) => state, + StepPopped: (state) => state, + Paused: (state) => state, + Resumed: (state) => state, + }); + + const event = { + id: "e1", + name: "Initialized", + eventDate: 0, + } as unknown as DomainEvent; + + expect(reducer(0, event)).toBe(1); +}); + +test("makeReducer - 등록되지 않은 이벤트는 명확한 에러를 던집니다", () => { + const reducer = makeReducer({ + Initialized: (state) => state, + ActivityRegistered: (state) => state, + Pushed: (state) => state, + Replaced: (state) => state, + Popped: (state) => state, + StepPushed: (state) => state, + StepReplaced: (state) => state, + StepPopped: (state) => state, + Paused: (state) => state, + Resumed: (state) => state, + }); + + const unknownEvent = { + id: "e1", + name: "Unknown", + eventDate: 0, + } as unknown as DomainEvent; + + expect(() => reducer(0, unknownEvent)).toThrow( + `No reducer for event ${JSON.stringify(unknownEvent)}`, + ); +}); diff --git a/core/src/activity-utils/makeReducer.ts b/core/src/activity-utils/makeReducer.ts index 5c5c2d285..d6c7f9a91 100644 --- a/core/src/activity-utils/makeReducer.ts +++ b/core/src/activity-utils/makeReducer.ts @@ -11,10 +11,10 @@ export function makeReducer( }, ) { return (target: T, event: DomainEvent) => { - const reducer = (reducerMap[event.name] as Reducer).bind(reducerMap); - if (reducer) { - return reducer(target, event); + const rawReducer = reducerMap[event.name] as Reducer | undefined; + if (!rawReducer) { + throw new Error(`No reducer for event ${JSON.stringify(event)}`); } - throw new Error(`No reducer for event ${JSON.stringify(event)}`); + return rawReducer.bind(reducerMap)(target, event); }; }