import { expect, test, describe } from 'vitest'; import { actionCreator, createHandler, ActionMap, ActionHandler, IActionMapInternal } from '../src/index'; import { clone } from './helpers'; import { skipDevMessages } from '../src/dev'; import { MODE_DEV, setGlobalMode } from '../src/mode'; import { vi } from 'vitest'; skipDevMessages(true); setGlobalMode(MODE_DEV); describe('ActionHandler', () => { const inputAction1 = actionCreator('input1'); const inputAction2 = actionCreator('input2'); const outputAction1 = actionCreator('output1'); const outputAction2 = actionCreator('output2'); const mockCallback = vi.fn(); const mockInject = { dependency: 'test' }; test('constructor initializes correctly with only inputs', () => { const handler = new ActionHandler({ inputs: [inputAction1, inputAction2], run: mockCallback, }); expect(handler.getName()).toBe('ActionHandler(Unnamed)'); expect(handler.getInputs()).toEqual([inputAction1, inputAction2]); expect(handler.getInputSet()).toEqual(new Set([inputAction1, inputAction2])); expect(handler.getOutputs()).toBeUndefined(); expect(handler.getOutputSet()).toBeUndefined(); expect(handler.getInputsAndOutputs()).toEqual([inputAction1, inputAction2]); expect(handler.getInputsAndOutputsSet()).toEqual(new Set([inputAction1, inputAction2])); expect(handler.getInjected()).toBeUndefined(); // @ts-expect-error - accessing protected property for test expect(handler._callback).toBe(mockCallback); }); test('constructor initializes correctly with inputs and outputs', () => { const handler = new ActionHandler({ inputs: [inputAction1], outputs: [outputAction1, outputAction2], run: mockCallback, }); expect(handler.getName()).toBe('ActionHandler(Unnamed)'); expect(handler.getInputs()).toEqual([inputAction1]); expect(handler.getInputSet()).toEqual(new Set([inputAction1])); expect(handler.getOutputs()).toEqual([outputAction1, outputAction2]); expect(handler.getOutputSet()).toEqual(new Set([outputAction1, outputAction2])); expect(handler.getInputsAndOutputs()).toEqual([inputAction1, outputAction1, outputAction2]); expect(handler.getInputsAndOutputsSet()).toEqual(new Set([inputAction1, outputAction1, outputAction2])); expect(handler.getInjected()).toBeUndefined(); // @ts-expect-error - accessing protected property for test expect(handler._callback).toBe(mockCallback); }); test('constructor initializes correctly with inputs, outputs, name, and inject', () => { const handler = new ActionHandler({ inputs: [inputAction1], outputs: [outputAction1], run: mockCallback, name: 'TestHandler', inject: mockInject, }); expect(handler.getName()).toBe('TestHandler'); expect(handler.getInputs()).toEqual([inputAction1]); expect(handler.getInputSet()).toEqual(new Set([inputAction1])); expect(handler.getOutputs()).toEqual([outputAction1]); expect(handler.getOutputSet()).toEqual(new Set([outputAction1])); expect(handler.getInputsAndOutputs()).toEqual([inputAction1, outputAction1]); expect(handler.getInputsAndOutputsSet()).toEqual(new Set([inputAction1, outputAction1])); expect(handler.getInjected()).toBe(mockInject); // @ts-expect-error - accessing protected property for test expect(handler._callback).toBe(mockCallback); }); test('constructor handles duplicate inputs/outputs correctly', () => { const handler = new ActionHandler({ inputs: [inputAction1, inputAction1], outputs: [outputAction1, outputAction1], run: mockCallback, }); expect(handler.getInputs()).toEqual([inputAction1]); expect(handler.getInputSet()).toEqual(new Set([inputAction1])); expect(handler.getOutputs()).toEqual([outputAction1]); expect(handler.getOutputSet()).toEqual(new Set([outputAction1])); expect(handler.getInputsAndOutputs()).toEqual([inputAction1, outputAction1]); expect(handler.getInputsAndOutputsSet()).toEqual(new Set([inputAction1, outputAction1])); }); test('getName returns the correct name', () => { const handlerWithDefaultName = new ActionHandler({ inputs: [inputAction1], run: mockCallback }); const handlerWithCustomName = new ActionHandler({ inputs: [inputAction1], run: mockCallback, name: 'CustomName' }); expect(handlerWithDefaultName.getName()).toBe('ActionHandler(Unnamed)'); expect(handlerWithCustomName.getName()).toBe('CustomName'); }); test('getInputs returns the correct inputs array', () => { const handler = new ActionHandler({ inputs: [inputAction1, inputAction2], run: mockCallback }); expect(handler.getInputs()).toEqual([inputAction1, inputAction2]); }); test('getInputSet returns the correct input set', () => { const handler = new ActionHandler({ inputs: [inputAction1, inputAction2], run: mockCallback }); expect(handler.getInputSet()).toEqual(new Set([inputAction1, inputAction2])); }); test('getOutputs returns the correct outputs array or undefined', () => { const handlerWithOutputs = new ActionHandler({ inputs: [inputAction1], outputs: [outputAction1], run: mockCallback, }); const handlerWithoutOutputs = new ActionHandler({ inputs: [inputAction1], run: mockCallback }); expect(handlerWithOutputs.getOutputs()).toEqual([outputAction1]); expect(handlerWithoutOutputs.getOutputs()).toBeUndefined(); }); test('getOutputSet returns the correct output set or undefined', () => { const handlerWithOutputs = new ActionHandler({ inputs: [inputAction1], outputs: [outputAction1], run: mockCallback, }); const handlerWithoutOutputs = new ActionHandler({ inputs: [inputAction1], run: mockCallback }); expect(handlerWithOutputs.getOutputSet()).toEqual(new Set([outputAction1])); expect(handlerWithoutOutputs.getOutputSet()).toBeUndefined(); }); test('getInputsAndOutputs returns the combined array', () => { const handlerWithOutputs = new ActionHandler({ inputs: [inputAction1], outputs: [outputAction1], run: mockCallback, }); const handlerWithoutOutputs = new ActionHandler({ inputs: [inputAction1, inputAction2], run: mockCallback }); expect(handlerWithOutputs.getInputsAndOutputs()).toEqual([inputAction1, outputAction1]); expect(handlerWithoutOutputs.getInputsAndOutputs()).toEqual([inputAction1, inputAction2]); }); test('getInputsAndOutputsSet returns the combined set', () => { const handlerWithOutputs = new ActionHandler({ inputs: [inputAction1], outputs: [outputAction1], run: mockCallback, }); const handlerWithoutOutputs = new ActionHandler({ inputs: [inputAction1, inputAction2], run: mockCallback }); expect(handlerWithOutputs.getInputsAndOutputsSet()).toEqual(new Set([inputAction1, outputAction1])); expect(handlerWithoutOutputs.getInputsAndOutputsSet()).toEqual(new Set([inputAction1, inputAction2])); }); test('inject updates the injected value', () => { const handler = new ActionHandler({ inputs: [inputAction1], run: mockCallback, inject: { value: 1 }, }); expect(handler.getInjected()).toEqual({ value: 1 }); handler.inject({ value: 2 }); expect(handler.getInjected()).toEqual({ value: 2 }); }); test('getInjected returns the current injected value', () => { const handler = new ActionHandler({ inputs: [inputAction1], run: mockCallback, inject: mockInject }); expect(handler.getInjected()).toBe(mockInject); const newInject = { dependency: 'new' }; handler.inject(newInject); expect(handler.getInjected()).toBe(newInject); }); test('invoke calls the callback with correct arguments and returns its result', () => { const callback = vi.fn((map, injected) => { expect(injected).toBe(mockInject); return 'callback result'; }); const handler = new ActionHandler({ inputs: [inputAction1], run: callback, inject: mockInject, }); const mockActionMap = { _setIsHandlerExecuted: vi.fn(), // Add other necessary properties/methods if ActionMapInternal requires them } as unknown as IActionMapInternal; const result = handler.invoke(mockActionMap); expect(callback).toHaveBeenCalledTimes(1); expect(callback).toHaveBeenCalledWith(mockActionMap, mockInject); expect(result).toBe('callback result'); expect(mockActionMap._setIsHandlerExecuted).toHaveBeenCalledTimes(2); expect(mockActionMap._setIsHandlerExecuted).toHaveBeenNthCalledWith(1, true); expect(mockActionMap._setIsHandlerExecuted).toHaveBeenNthCalledWith(2, false); }); test('actionHandler factory function creates an ActionHandler instance', () => { const config = { inputs: [inputAction1], outputs: [outputAction1], run: mockCallback, name: 'FactoryHandler', inject: mockInject, }; const handler = createHandler(config); expect(handler).toBeInstanceOf(ActionHandler); expect(handler.getName()).toBe('FactoryHandler'); expect(handler.getInputs()).toEqual([inputAction1]); expect(handler.getOutputs()).toEqual([outputAction1]); expect(handler.getInjected()).toBe(mockInject); // @ts-expect-error - accessing protected property for test expect(handler._callback).toBe(mockCallback); }); });