import { expect, tap } from '@push.rocks/tapbundle'; import * as smartstate from '../ts/index.js'; type TTestStateParts = 'initTest' | 'persistTest' | 'forceTest'; interface ITestState { value: number; nested: { data: string; }; } tap.test('should handle soft init mode (default)', async () => { const state = new smartstate.Smartstate(); // First creation const statePart1 = await state.getStatePart('initTest', { value: 1, nested: { data: 'initial' } }); expect(statePart1.getState()).toEqual({ value: 1, nested: { data: 'initial' } }); // Second call should return existing const statePart2 = await state.getStatePart('initTest'); expect(statePart1).toEqual(statePart2); }); tap.test('should handle mandatory init mode', async () => { const state = new smartstate.Smartstate(); // First creation should succeed const statePart1 = await state.getStatePart('initTest', { value: 1, nested: { data: 'initial' } }, 'mandatory'); expect(statePart1).toBeInstanceOf(smartstate.StatePart); // Second call with mandatory should fail let error: Error | null = null; try { await state.getStatePart('initTest', { value: 2, nested: { data: 'second' } }, 'mandatory'); } catch (e) { error = e as Error; } expect(error).not.toBeNull(); expect(error?.message).toMatch(/already exists.*mandatory/); }); tap.test('should handle force init mode', async () => { const state = new smartstate.Smartstate(); // First creation const statePart1 = await state.getStatePart('forceTest', { value: 1, nested: { data: 'initial' } }); expect(statePart1.getState()?.value).toEqual(1); // Force should create new state part const statePart2 = await state.getStatePart('forceTest', { value: 2, nested: { data: 'forced' } }, 'force'); expect(statePart2.getState()?.value).toEqual(2); expect(statePart1).not.toEqual(statePart2); }); tap.test('should handle missing initial state error', async () => { const state = new smartstate.Smartstate(); let error: Error | null = null; try { await state.getStatePart('initTest'); } catch (e) { error = e as Error; } expect(error).not.toBeNull(); expect(error?.message).toMatch(/does not exist.*no initial state/); }); tap.test('should handle state validation', async () => { const state = new smartstate.Smartstate(); const statePart = await state.getStatePart('initTest', { value: 1, nested: { data: 'initial' } }); // Setting null should fail validation let error: Error | null = null; try { await statePart.setState(null as any); } catch (e) { error = e as Error; } expect(error).not.toBeNull(); expect(error?.message).toMatch(/Invalid state structure/); }); tap.test('should handle undefined state in select', async () => { const state = new smartstate.Smartstate(); const statePart = new smartstate.StatePart('initTest'); // Select should filter out undefined states const values: (ITestState | undefined)[] = []; statePart.select().subscribe(val => values.push(val)); // Initially undefined, should not emit expect(values).toHaveLength(0); // After setting state, should emit await statePart.setState({ value: 1, nested: { data: 'test' } }); expect(values).toHaveLength(1); expect(values[0]).toEqual({ value: 1, nested: { data: 'test' } }); }); tap.test('should not notify on duplicate state', async () => { const state = new smartstate.Smartstate(); const statePart = await state.getStatePart('initTest', { value: 1, nested: { data: 'initial' } }); let notificationCount = 0; // Use select() to get initial value + changes statePart.select().subscribe(() => notificationCount++); // Should have received initial state expect(notificationCount).toEqual(1); // Set same state multiple times await statePart.setState({ value: 1, nested: { data: 'initial' } }); await statePart.setState({ value: 1, nested: { data: 'initial' } }); await statePart.setState({ value: 1, nested: { data: 'initial' } }); // Should still be 1 (no new notifications for duplicate state) expect(notificationCount).toEqual(1); // Change state should notify await statePart.setState({ value: 2, nested: { data: 'changed' } }); expect(notificationCount).toEqual(2); }); export default tap.start();