import useProgramStore, {type ReducedProgram} from "../../src/utils/programStore.ts"; describe('useProgramStore', () => { beforeEach(() => { // Reset store before each test useProgramStore.setState({ currentProgram: { phases: [] }, }); }); const mockProgram: ReducedProgram = { phases: [ { id: 'phase-1', norms: [{ id: 'norm-1' }], goals: [{ id: 'goal-1' }], triggers: [{ id: 'trigger-1' }], }, { id: 'phase-2', norms: [{ id: 'norm-2' }], goals: [{ id: 'goal-2' }], triggers: [{ id: 'trigger-2' }], }, ], }; it('should set and get the program state', () => { useProgramStore.getState().setProgramState(mockProgram); const program = useProgramStore.getState().getProgramState(); expect(program).toEqual(mockProgram); }); it('should return the ids of all phases in the program', () => { useProgramStore.getState().setProgramState(mockProgram); const phaseIds = useProgramStore.getState().getPhaseIds(); expect(phaseIds).toEqual(['phase-1', 'phase-2']); }); it('should return all norms for a given phase', () => { useProgramStore.getState().setProgramState(mockProgram); const norms = useProgramStore.getState().getNormsInPhase('phase-1'); expect(norms).toEqual([{ id: 'norm-1' }]); }); it('should return all goals for a given phase', () => { useProgramStore.getState().setProgramState(mockProgram); const goals = useProgramStore.getState().getGoalsInPhase('phase-2'); expect(goals).toEqual([{ id: 'goal-2' }]); }); it('should return all triggers for a given phase', () => { useProgramStore.getState().setProgramState(mockProgram); const triggers = useProgramStore.getState().getTriggersInPhase('phase-1'); expect(triggers).toEqual([{ id: 'trigger-1' }]); }); it('throws if phase does not exist when getting norms', () => { useProgramStore.getState().setProgramState(mockProgram); expect(() => useProgramStore.getState().getNormsInPhase('missing-phase') ).toThrow('phase with id:"missing-phase" not found'); }); it('throws if phase does not exist when getting goals', () => { useProgramStore.getState().setProgramState(mockProgram); expect(() => useProgramStore.getState().getGoalsInPhase('missing-phase') ).toThrow('phase with id:"missing-phase" not found'); }); it('throws if phase does not exist when getting triggers', () => { useProgramStore.getState().setProgramState(mockProgram); expect(() => useProgramStore.getState().getTriggersInPhase('missing-phase') ).toThrow('phase with id:"missing-phase" not found'); }); it('should clone program state when setting it (no shared references should exist)', () => { const changeableMockProgram: ReducedProgram = { phases: [ { id: 'phase-1', norms: [{ id: 'norm-1' }], goals: [{ id: 'goal-1' }], triggers: [{ id: 'trigger-1' }], }, { id: 'phase-2', norms: [{ id: 'norm-2' }], goals: [{ id: 'goal-2' }], triggers: [{ id: 'trigger-2' }], }, ], }; useProgramStore.getState().setProgramState(changeableMockProgram); const storedProgram = useProgramStore.getState().getProgramState(); // mutate original (changeableMockProgram.phases[0].norms as any[]).push({ id: 'norm-mutated' }); // store should NOT change expect(storedProgram.phases[0]['norms']).toHaveLength(1); }); }); describe('getGoalsWithDepth', () => { const complexProgram: ReducedProgram = { phases: [ { id: 'phase-nested', goals: [ // Level 0: Root Goal 1 { id: 'root-1', name: 'Root Goal 1', plan: { steps: [ // This is an ACTION (no plan), should be ignored { id: 'action-1', type: 'speech' }, // Level 1: Child Goal { id: 'child-1', name: 'Child Goal', plan: { steps: [ // Level 2: Grandchild Goal { id: 'grandchild-1', name: 'Grandchild', plan: { steps: [] } // Empty plan is still a plan } ] } } ] } }, // Level 0: Root Goal 2 (Sibling) { id: 'root-2', name: 'Root Goal 2', plan: { steps: [] } } ] } ] }; it('should flatten nested goals and assign correct depth levels', () => { useProgramStore.getState().setProgramState(complexProgram); const goals = useProgramStore.getState().getGoalsWithDepth('phase-nested'); // logic: Root 1 -> Child 1 -> Grandchild 1 -> Root 2 expect(goals).toHaveLength(4); // Check Root 1 expect(goals[0]).toEqual(expect.objectContaining({ id: 'root-1', level: 0 })); // Check Child 1 expect(goals[1]).toEqual(expect.objectContaining({ id: 'child-1', level: 1 })); // Check Grandchild 1 expect(goals[2]).toEqual(expect.objectContaining({ id: 'grandchild-1', level: 2 })); // Check Root 2 expect(goals[3]).toEqual(expect.objectContaining({ id: 'root-2', level: 0 })); }); it('should ignore steps that are not goals (missing "plan" property)', () => { useProgramStore.getState().setProgramState(complexProgram); const goals = useProgramStore.getState().getGoalsWithDepth('phase-nested'); // The 'action-1' object should NOT be in the list const action = goals.find(g => g.id === 'action-1'); expect(action).toBeUndefined(); }); it('throws if phase does not exist', () => { useProgramStore.getState().setProgramState(complexProgram); expect(() => useProgramStore.getState().getGoalsWithDepth('missing-phase') ).toThrow('phase with id:"missing-phase" not found'); }); }); it('should return the names of all phases in the program', () => { // Define a program specifically with names for this test const programWithNames: ReducedProgram = { phases: [ { id: 'phase-1', name: 'Introduction Phase', // Assuming the property is 'name' norms: [], goals: [], triggers: [], }, { id: 'phase-2', name: 'Execution Phase', norms: [], goals: [], triggers: [], }, ], }; useProgramStore.getState().setProgramState(programWithNames); const phaseNames = useProgramStore.getState().getPhaseNames(); expect(phaseNames).toEqual(['Introduction Phase', 'Execution Phase']); });