@idiosync/react-observable
Version:
State management control layer for React projects
231 lines (230 loc) • 10.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const create_command_stream_1 = require("./create-command-stream");
const observable_1 = require("./observable");
describe('createCommandStream', () => {
describe('Basic functionality', () => {
it('should create a command stream function', () => {
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.stream((value) => value);
});
expect(typeof command).toBe('function');
expect(command.exit$).toBeDefined();
});
it('should execute and return a promise', async () => {
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.stream((value) => value);
});
const result = command('test');
expect(result).toBeInstanceOf(Promise);
const [data, error] = await result;
expect(data).toBe('test');
expect(error).toBeUndefined();
});
it('should handle async operations', async () => {
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.streamAsync(async (value) => {
await new Promise((resolve) => setTimeout(resolve, 10));
return value + ' processed';
});
});
const [data, error] = await command('test');
expect(data).toBe('test processed');
expect(error).toBeUndefined();
});
});
describe('Error handling', () => {
it('should handle errors in stream', async () => {
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.streamAsync(async (value) => {
throw new Error('Test error');
});
});
const [data, error] = await command('test');
expect(data).toBeUndefined();
expect(error).toBeInstanceOf(Error);
expect(error.message).toBe('Test error');
});
it('should call onError callback when provided', async () => {
const onError = jest.fn();
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.streamAsync(async (value) => {
throw new Error('Test error');
});
}, { onError });
await command('test');
expect(onError).toHaveBeenCalledWith(expect.any(Error), expect.any(Array));
});
});
describe('Execution tracking', () => {
it('should track multiple concurrent executions', async () => {
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.streamAsync(async (value) => {
await new Promise((resolve) => setTimeout(resolve, 50));
return value;
});
});
const execution1 = command('first');
const execution2 = command('second');
const [result1, error1] = await execution1;
const [result2, error2] = await execution2;
expect(result1).toBe('first');
expect(result2).toBe('second');
expect(error1).toBeUndefined();
expect(error2).toBeUndefined();
});
it('should isolate execution results', async () => {
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.stream((value) => value);
});
const promise1 = command('value1');
const promise2 = command('value2');
const [data1] = await promise1;
const [data2] = await promise2;
expect(data1).toBe('value1');
expect(data2).toBe('value2');
});
});
describe('Store integration', () => {
it('should provide store to stream', async () => {
const mockStore = {
test: {
value$: (0, observable_1.createObservable)({ initialValue: 'store value' }),
},
};
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.stream((value) => {
return `${value} - ${store.test.value$.get()}`;
});
});
const [data] = await command('input');
expect(data).toBe('input - store value');
});
it('should handle store updates', async () => {
const mockStore = {
counter: {
count$: (0, observable_1.createObservable)({ initialValue: 0 }),
},
};
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.stream((value) => {
store.counter.count$.set(store.counter.count$.get() + 1);
return store.counter.count$.get();
});
});
const [data] = await command('increment');
expect(data).toBe(1);
});
});
describe('Exit observable', () => {
it('should provide exit observable', () => {
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.stream((value) => value);
});
expect(command.exit$).toBeDefined();
expect(typeof command.exit$.subscribe).toBe('function');
});
it('should emit results to exit observable', async () => {
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.stream((value) => value);
});
const listener = jest.fn();
command.exit$.subscribe(listener);
await command('test');
expect(listener).toHaveBeenCalledWith('test', expect.any(Array));
});
it('should handle initial value in exit observable', () => {
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.stream((value) => value);
}, { initialValue: 'initial' });
expect(command.exit$.get()).toBe('initial');
});
});
describe('Result observable integration', () => {
it('should integrate with external result observable', async () => {
const result$ = (0, observable_1.createObservable)();
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.stream((value) => value);
}, { result$ });
await command('test');
expect(result$.get()).toBe('test');
});
});
describe('Stream operations', () => {
it('should handle stream with latest from', async () => {
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.withLatestFrom((0, observable_1.createObservable)({ initialValue: 'context' })).stream(([value, context]) => `${value} with ${context}`);
});
const [data] = await command('input');
expect(data).toBe('input with context');
});
it('should handle combine latest from', async () => {
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.combineLatestFrom((0, observable_1.createObservable)({ initialValue: 'a' }), (0, observable_1.createObservable)({ initialValue: 'b' })).stream(([value, a, b]) => `${value}-${a}-${b}`);
});
const [data] = await command('input');
expect(data).toBe('input-a-b');
});
it('should handle tap operations', async () => {
const sideEffect = jest.fn();
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.tap(sideEffect).stream((value) => value);
});
await command('test');
expect(sideEffect).toHaveBeenCalledWith('test');
});
it('should handle catch error operations', async () => {
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.streamAsync(async (value) => {
throw new Error('Stream error');
}).catchError((error, currentValue, setter) => {
setter('recovered');
});
});
const [data, error] = await command('test');
expect(data).toBe('recovered');
expect(error).toBeUndefined();
});
});
describe('Complex scenarios', () => {
it('should handle chained operations', async () => {
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.stream((value) => value)
.stream((value) => value.toUpperCase())
.stream((value) => `${value}!`);
});
const [data] = await command('hello');
expect(data).toBe('HELLO!');
});
it('should handle async chained operations', async () => {
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.streamAsync(async (value) => {
await new Promise((resolve) => setTimeout(resolve, 10));
return value + '1';
}).streamAsync(async (value) => {
await new Promise((resolve) => setTimeout(resolve, 10));
return value + '2';
});
});
const [data] = await command('test');
expect(data).toBe('test12');
});
it('should handle store updates in complex scenarios', async () => {
const mockStore = {
user: {
name$: (0, observable_1.createObservable)({ initialValue: 'John' }),
age$: (0, observable_1.createObservable)({ initialValue: 25 }),
},
};
const command = (0, create_command_stream_1.createCommandStream)(({ $, store }) => {
return $.stream((value) => {
const name = store.user.name$.get();
const age = store.user.age$.get();
return `${value} by ${name} (${age})`;
});
});
const [data] = await command('action');
expect(data).toBe('action by John (25)');
});
});
});