import { describe, it, expect } from 'vitest'; import { Flowful, actionCreator, createHandler, createActionMap, ActionMap, IAction } from '../src/index'; import { skipDevMessages } from '../src/dev'; import { MODE_DEV, setGlobalMode } from '../src/mode'; setGlobalMode(MODE_DEV); skipDevMessages(true); describe('Sort DFS', () => { it('very simple #1', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [], run: () => null }); const handlers = [L1, L2]; flowful.addHandlers(handlers); const actions = [A1(), A2()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2]); // ok }); it('simple #1', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const A5 = actionCreator('A5'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A4], run: () => null }); const L4 = createHandler({ inputs: [A4], outputs: [A5], run: () => null }); const L5 = createHandler({ inputs: [A5], outputs: [], run: () => null }); const handlers = [L2, L3, L5, L4, L1]; flowful.addHandlers(handlers); const actions = [A1(), A2(), A3(), A4(), A5()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3, L4, L5]); // ok }); it('simple #2', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const A5 = actionCreator('A5'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A4], run: () => null }); const L4 = createHandler({ inputs: [A4], outputs: [A5], run: () => null }); const L5 = createHandler({ inputs: [A5], outputs: [], run: () => null }); const handlers = [L3, L4, L1, L5, L2]; flowful.addHandlers(handlers); const actions = [A1(), A2(), A3(), A4(), A5()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3, L4, L5]); // ok }); it('simple #3', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const A5 = actionCreator('A5'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A4], run: () => null }); const L4 = createHandler({ inputs: [A4], outputs: [A5], run: () => null }); const L5 = createHandler({ inputs: [A5], outputs: [], run: () => null }); const handlers = [L1, L2, L3, L5, L4]; flowful.addHandlers(handlers); const actions = [A1(), A2(), A3(), A4(), A5()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3, L4, L5]); // ok }); it('simple #4', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const A5 = actionCreator('A5'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A4], run: () => null }); const L4 = createHandler({ inputs: [A4], outputs: [A5], run: () => null }); const L5 = createHandler({ inputs: [A5], outputs: [], run: () => null }); const handlers = [L1, L2, L3, L4, L5]; flowful.addHandlers(handlers); const actions = [A1(), A2(), A3(), A4(), A5()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3, L4, L5]); // ok }); it('simple #4 (reversed actions)', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const A5 = actionCreator('A5'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A4], run: () => null }); const L4 = createHandler({ inputs: [A4], outputs: [A5], run: () => null }); const L5 = createHandler({ inputs: [A5], outputs: [], run: () => null }); const handlers = [L1, L2, L3, L4, L5]; flowful.addHandlers(handlers); const actions = [A1(), A2(), A3(), A4(), A5()].reverse(); const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3, L4, L5]); // ok }); it('simple #5', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const A5 = actionCreator('A5'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A4], run: () => null }); const L4 = createHandler({ inputs: [A4], outputs: [A5], run: () => null }); const L5 = createHandler({ inputs: [A5], outputs: [], run: () => null }); flowful.addHandlers([L3, L5, L2, L1, L4]); const actions = [A1(), A2(), A3(), A4(), A5()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3, L4, L5]); // ok }); it('simple #5 (reversed actions)', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const A5 = actionCreator('A5'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A4], run: () => null }); const L4 = createHandler({ inputs: [A4], outputs: [A5], run: () => null }); const L5 = createHandler({ inputs: [A5], outputs: [], run: () => null }); flowful.addHandlers([L3, L5, L2, L1, L4]); const actions = [A1(), A2(), A3(), A4(), A5()].reverse(); const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3, L4, L5]); // ok }); it('simple #6', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const A5 = actionCreator('A5'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2_v1 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L2_v2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L2_v3 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A4], run: () => null }); const L4 = createHandler({ inputs: [A4], outputs: [A5], run: () => null }); const L5 = createHandler({ inputs: [A5], outputs: [], run: () => null }); flowful.addHandlers([L3, L5, L2_v1, L2_v2, L2_v3, L1, L4]); const actions = [A1(), A2(), A3(), A4(), A5()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2_v3, L2_v2, L2_v1, L3, L4, L5]); // ok }); it('multi input simple #1', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3, A2], outputs: [], run: () => null }); flowful.addHandlers([L1, L2, L3]); const actions = [A1(), A2(), A3()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3]); // ok }); it('multi input simple #2', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3, A2], outputs: [], run: () => null }); flowful.addHandlers([L3, L2, L1]); const actions = [A1(), A2(), A3()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3]); // ok }); it('multi input simple #3', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3, A2], outputs: [], run: () => null }); flowful.addHandlers([L2, L3, L1]); const actions = [A1(), A2(), A3()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3]); // ok }); it('multi input simple #4', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3, A2], outputs: [], run: () => null }); flowful.addHandlers([L1, L3, L2]); const actions = [A1(), A2(), A3()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3]); // ok }); it('multi input simple #5', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3, A2], outputs: [], run: () => null }); flowful.addHandlers([L2, L1, L3]); const actions = [A1(), A2(), A3()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3]); // ok }); it('multi output simple #1', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L1 = createHandler({ inputs: [A1], outputs: [A2, A3], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [], run: () => null }); flowful.addHandlers([L1, L2, L3]); const actions = [A1(), A2(), A3()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3]); // ok }); it('multi output simple #2', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L1 = createHandler({ inputs: [A1], outputs: [A2, A3], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [], run: () => null }); flowful.addHandlers([L3, L2, L1]); const actions = [A1(), A2(), A3()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3]); // ok }); it('multi output simple #3', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L1 = createHandler({ inputs: [A1], outputs: [A2, A3], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [], run: () => null }); flowful.addHandlers([L2, L3, L1]); const actions = [A1(), A2(), A3()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3]); // ok }); it('multi output simple #4', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L1 = createHandler({ inputs: [A1], outputs: [A2, A3], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [], run: () => null }); flowful.addHandlers([L2, L1, L3]); const actions = [A1(), A2(), A3()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3]); // ok }); it('multi output simple #5', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L1 = createHandler({ inputs: [A1], outputs: [A2, A3], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [], run: () => null }); flowful.addHandlers([L3, L1, L2]); const actions = [A1(), A2(), A3()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3]); // ok }); it('multi output #1 (cyclic)', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L1 = createHandler({ inputs: [A1], outputs: [A2, A3], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3, A1], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [], run: () => null }); expect(() => flowful.addHandlers([L1, L2, L3])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L1, L2, L3]); // ok }); it('multi output #2 (cyclic)', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L1 = createHandler({ inputs: [A1], outputs: [A2, A3], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3, A1], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [], run: () => null }); expect(() => flowful.addHandlers([L3, L2, L1])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L2, L1, L3]); // ok - it doesn't matter if L1 or L2 is first (dfs will start from L2 so it will place L2 before L1) }); it('multi output #3 (cyclic)', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L2 = createHandler({ inputs: [A2], outputs: [A3, A1], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [], run: () => null }); const L1 = createHandler({ inputs: [A1], outputs: [A2, A3], run: () => null }); expect(() => flowful.addHandlers([L2, L3, L1])).toThrow(); // const actionMap = createActionMap(...actions); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L2, L1, L3]); // ok }); it('multi output #3v2 (cyclic)', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L2 = createHandler({ inputs: [A2], outputs: [A1, A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [], run: () => null }); const L1 = createHandler({ inputs: [A1], outputs: [A2, A3], run: () => null }); expect(() => flowful.addHandlers([L2, L3, L1])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L2, L1, L3]); // ok }); it('multi output #4 (cyclic)', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L1 = createHandler({ inputs: [A1], outputs: [A2, A3], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3, A1], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [], run: () => null }); expect(() => flowful.addHandlers([L3, L1, L2])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L1, L2, L3]); // ok }); it('multi output #5 (cyclic)', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const L2 = createHandler({ inputs: [A2], outputs: [A3, A1], run: () => null }); const L1 = createHandler({ inputs: [A1], outputs: [A2, A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [], run: () => null }); expect(() => flowful.addHandlers([L2, L1, L3])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L2, L1, L3]); // ok }); it('multi output #2 - #1 (cyclic)', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const L3 = createHandler({ inputs: [A3], outputs: [A1, A4], run: () => null }); const L4 = createHandler({ inputs: [A4], outputs: [A1], run: () => null }); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); expect(() => flowful.addHandlers([L3, L1, L2, L4])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L3, L4, L1, L2]); // ok }); it('multi output #2 - #2 (cyclic)', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L4 = createHandler({ inputs: [A4], outputs: [A1], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A1, A4], run: () => null }); expect(() => flowful.addHandlers([L1, L2, L4, L3])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L1, L2, L3, L4]); // ok }); it('final #1 (cyclic)', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2, A4], outputs: [A1], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A4], run: () => null }); const L4 = createHandler({ inputs: [A4], outputs: [], run: () => null }); expect(() => flowful.addHandlers([L1, L2, L3, L4])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L3, L4, L1, L2]); // ok }); it('order not important', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const X1 = actionCreator('X1'); const X2 = actionCreator('X2'); const X3 = actionCreator('X3'); const X4 = actionCreator('X4'); const L1 = createHandler({ inputs: [A1], outputs: [X1], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [X2], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [X3], run: () => null }); const L4 = createHandler({ inputs: [A4], outputs: [X4], run: () => null }); flowful.addHandlers([L1, L2, L3, L4]); const actions = [A1(), A2(), A3(), A4()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L4, L3, L2, L1]); // ok // dfs will reverse the order but it doesn't matter }); it('cyclic #1', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const A5 = actionCreator('A5'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A4, A1], run: () => null }); const L4 = createHandler({ inputs: [A4], outputs: [A5], run: () => null }); const L5 = createHandler({ inputs: [A5], outputs: [], run: () => null }); expect(() => flowful.addHandlers([L1, L2, L3, L4, L5])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L1, L2, L3, L4, L5]); // ok }); it('couple movements', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const A5 = actionCreator('A5'); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A4], run: () => null }); const L4 = createHandler({ inputs: [A4], outputs: [A5], run: () => null }); const L1 = createHandler({ inputs: [A1], outputs: [A4, A2], run: () => null }); const L5 = createHandler({ inputs: [A5], outputs: [], run: () => null }); flowful.addHandlers([L2, L3, L4, L1, L5]); const actions = [A1(), A2(), A3(), A4(), A5()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L1, L2, L3, L4, L5]); // ok }); it('two branches', () => { const flowful = new Flowful(); const Branch1 = actionCreator('Branch1'); const Branch1_1 = actionCreator('Branch1_1'); const Branch2 = actionCreator('Branch2'); const Branch2_1 = actionCreator('Branch2_1'); const L1 = createHandler({ inputs: [Branch1], outputs: [Branch1_1], run: () => null }); // 2,1,4,3 const L2 = createHandler({ inputs: [Branch1_1], outputs: [], run: () => null }); const L3 = createHandler({ inputs: [Branch2], outputs: [Branch2_1], run: () => null }); const L4 = createHandler({ inputs: [Branch2_1], outputs: [], run: () => null }); flowful.addHandlers([L1, L2, L3, L4]); const actions = [Branch1(), Branch1_1(), Branch2(), Branch2_1()]; const actionMap = createActionMap(...actions); // @ts-expect-error _collectHandlers is protected const sorted = flowful._collectHandlers(actionMap); expect(sorted).toEqual([L3, L4, L1, L2]); // ok - it could be L1, L2, L3, L4 also but it doesn't matter }); it('two branches (cyclic)', () => { const flowful = new Flowful(); const Branch1 = actionCreator('Branch1'); const Branch1_1 = actionCreator('Branch1_1'); const Branch2 = actionCreator('Branch2'); const Branch2_1 = actionCreator('Branch2_1'); const L1 = createHandler({ inputs: [Branch1], outputs: [Branch1_1], run: () => null }); const L2 = createHandler({ inputs: [Branch1_1], outputs: [], run: () => null }); const L3 = createHandler({ inputs: [Branch1_1], outputs: [Branch1], run: () => null }); // cyclic const L4 = createHandler({ inputs: [Branch2], outputs: [Branch2_1], run: () => null }); const L5 = createHandler({ inputs: [Branch2_1], outputs: [], run: () => null }); expect(() => flowful.addHandlers([L1, L2, L5, L3, L4])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L4, L5, L1, L3, L2]); // ok - it could be L1, L2, L3, L4, L5 also but it doesn't matter }); it('cycle and couple of independent', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const B1 = actionCreator('B1'); const X1 = actionCreator('X1'); const C1 = actionCreator('C1'); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A1], run: () => null }); const L4 = createHandler({ inputs: [B1], outputs: [X1], run: () => null }); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L5 = createHandler({ inputs: [C1], outputs: [], run: () => null }); expect(() => flowful.addHandlers([L2, L3, L4, L1, L5])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // @ts-expect-error _collectHandlers is protected // expect(sorted).toEqual([L5, L4, L2, L3, L1]); // ok - it could be L1, L2, L3, L4, L5 also but it doesn't matter }); it('cycle and couple of independent (at different loop point)', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const B1 = actionCreator('B1'); const X1 = actionCreator('X1'); const C1 = actionCreator('C1'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A1], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [], run: () => null }); const L4 = createHandler({ inputs: [B1], outputs: [X1], run: () => null }); const L5 = createHandler({ inputs: [C1], outputs: [], run: () => null }); expect(() => flowful.addHandlers([L2, L3, L4, L1, L5])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L5, L4, L3, L2, L1]); // ok - it could be L1, L2, L3, L4, L5 also but it doesn't matter }); it('cycle and couple of independent (with multiple loop points)', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const B1 = actionCreator('B1'); const X1 = actionCreator('X1'); const C1 = actionCreator('C1'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3, A1], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A1], run: () => null }); const L4 = createHandler({ inputs: [B1], outputs: [X1], run: () => null }); const L5 = createHandler({ inputs: [C1], outputs: [], run: () => null }); expect(() => flowful.addHandlers([L2, L3, L4, L1, L5])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L5, L4, L2, L3, L1]); // ok }); it('two cycling loops (loops starts later)', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const B1 = actionCreator('B1'); const B2 = actionCreator('B2'); const B3 = actionCreator('B3'); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [A2], run: () => null }); const L4 = createHandler({ inputs: [B1], outputs: [B2], run: () => null }); const L5 = createHandler({ inputs: [B2], outputs: [B3], run: () => null }); const L6 = createHandler({ inputs: [B3], outputs: [B1], run: () => null }); expect(() => flowful.addHandlers([L2, L3, L4, L1, L5, L6])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L1, L4, L5, L6, L2, L3]); // ok - looks ugly but will work }); it('two loops with one without emit', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const A5 = actionCreator('A5'); const B1 = actionCreator('B1'); const B2 = actionCreator('B2'); const B3 = actionCreator('B3'); const B4 = actionCreator('B4'); const B5 = actionCreator('B5'); const LA2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const LA3 = createHandler({ inputs: [A3], outputs: [A4], run: () => null }); const LA5 = createHandler({ inputs: [A5], outputs: [], run: () => null }); // 5,1,4,3,2 | 1,5,4,3,2 const LA4 = createHandler({ inputs: [A4], outputs: [A5, A1], run: () => null }); const LA1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const LB2 = createHandler({ inputs: [B2], outputs: [B3], run: () => null }); const LB3 = createHandler({ inputs: [B3], outputs: [B4], run: () => null }); const LB5 = createHandler({ inputs: [B5], outputs: [B1], run: () => null }); const LB4 = createHandler({ inputs: [B4], outputs: [B5], run: () => null }); const LB1 = createHandler({ inputs: [B1], outputs: [B2], run: () => null }); expect(() => flowful.addHandlers([LA2, LA3, LA5, LA4, LA1, LB2, LB3, LB5, LB4, LB1])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([LB2, LB3, LB4, LB5, LB1, LA2, LA3, LA4, LA1, LA5]); }); it('two branches (one looped) with one without emit', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const A4 = actionCreator('A4'); const A5 = actionCreator('A5'); const B1 = actionCreator('B1'); const B2 = actionCreator('B2'); const B3 = actionCreator('B3'); const B4 = actionCreator('B4'); const B5 = actionCreator('B5'); const LA2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const LA3 = createHandler({ inputs: [A3], outputs: [A4], run: () => null }); const LA5 = createHandler({ inputs: [A5], outputs: [], run: () => null }); const LA4 = createHandler({ inputs: [A4], outputs: [A5, A1], run: () => null }); const LA1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); const LB2 = createHandler({ inputs: [B2], outputs: [B3], run: () => null }); const LB3 = createHandler({ inputs: [B3], outputs: [B4], run: () => null }); const LB5 = createHandler({ inputs: [B5], outputs: [], run: () => null }); const LB4 = createHandler({ inputs: [B4], outputs: [B5], run: () => null }); const LB1 = createHandler({ inputs: [B1], outputs: [B2], run: () => null }); expect(() => flowful.addHandlers([LA2, LA3, LA5, LA4, LA1, LB2, LB3, LB5, LB4, LB1])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([LB1, LB2, LB3, LB4, LB5, LA2, LA3, LA4, LA1, LA5]); // ok }); it('cycled eventHandler calls a eventHandler from another branch', () => { const flowful = new Flowful(); const A1 = actionCreator('A1'); const A2 = actionCreator('A2'); const A3 = actionCreator('A3'); const B1 = actionCreator('B1'); const B2 = actionCreator('B2'); const B3 = actionCreator('B3'); const L2 = createHandler({ inputs: [A2], outputs: [A3], run: () => null }); const L3 = createHandler({ inputs: [A3], outputs: [], run: () => null }); const L4 = createHandler({ inputs: [B1], outputs: [B2], run: () => null }); const L1 = createHandler({ inputs: [A1], outputs: [A2], run: () => null }); // 3,2,6,5,4,1 const L5 = createHandler({ inputs: [B2], outputs: [B3, A2], run: () => null }); const L6 = createHandler({ inputs: [B3], outputs: [B1], run: () => null }); expect(() => flowful.addHandlers([L2, L3, L4, L1, L5, L6])).toThrow(); // const sorted = flowful._collectHandlers(actionMap); // expect(sorted).toEqual([L1, L4, L5, L6, L2, L3]); // ok }); });