diff --git a/src/spread/spread.fork.test.ts b/src/spread/spread.fork.test.ts index 4afac567..cbb17d1b 100644 --- a/src/spread/spread.fork.test.ts +++ b/src/spread/spread.fork.test.ts @@ -4,84 +4,64 @@ import { spread } from './index'; test('works in forked scope', async () => { const app = createDomain(); - const source = app.createEvent<{ first: string; second: number; third: string }>(); + const source = app.createEvent<{ first: string; second: number }>(); const first = app.createEvent(); const second = app.createEvent(); - const $thirdA = app.createStore(''); - const $thirdB = app.createStore(''); - - const $first = app.createStore('').on(first, (_, p) => p); - const $second = restore(second, 0); + const _$first = app.createStore('').on(first, (_, p) => p); + const _$second = restore(second, 0); spread({ source, - targets: { first, second, third: [$thirdA, $thirdB] }, + targets: { first, second }, }); const scope = fork(); - await allSettled(source, { - scope, - params: { first: 'sergey', second: 26, third: '30' }, - }); - - expect(scope.getState($first)).toBe('sergey'); - expect(scope.getState($second)).toBe(26); - expect(scope.getState($thirdA)).toBe('30'); - expect(scope.getState($thirdB)).toBe('30'); + await allSettled(source, { scope, params: { first: 'sergey', second: 26 } }); + expect(serialize(scope)).toMatchInlineSnapshot(` + { + "-xihhjw": 26, + "nln5hw": "sergey", + } + `); }); test('do not affects original store state', async () => { const app = createDomain(); - const source = app.createEvent<{ first: string; second: number; third: string }>(); + const source = app.createEvent<{ first: string; second: number }>(); const first = app.createEvent(); const second = app.createEvent(); - const $thirdA = app.createStore(''); - const $thirdB = app.createStore(''); - const $first = app.createStore('').on(first, (_, p) => p); const $second = restore(second, 0); spread({ source, - targets: { first, second, third: [$thirdA, $thirdB] }, + targets: { first, second }, }); const scope = fork(); - await allSettled(source, { - scope, - params: { first: 'sergey', second: 26, third: '30' }, - }); - + await allSettled(source, { scope, params: { first: 'sergey', second: 26 } }); expect(scope.getState($first)).toBe('sergey'); expect(scope.getState($second)).toBe(26); - expect(scope.getState($thirdA)).toBe('30'); - expect(scope.getState($thirdB)).toBe('30'); - expect($first.getState()).toBe(''); expect($second.getState()).toBe(0); - expect($thirdA.getState()).toBe(''); - expect($thirdB.getState()).toBe(''); }); test('do not affects another scope', async () => { const app = createDomain(); - const source = app.createEvent<{ first: string; second: number; third: string }>(); + const source = app.createEvent<{ first: string; second: number }>(); const first = app.createEvent(); const second = app.createEvent(); - const $thirdA = app.createStore(''); - const $thirdB = app.createStore(''); - - const $first = app.createStore('').on(first, (_, p) => p); - const $second = restore(second, 0); + const _$first = app.createStore('').on(first, (_, p) => p); + const _$second = restore(second, 0); spread({ source, - targets: { first, second, third: [$thirdA, $thirdB] }, + targets: { first, second }, }); const scope1 = fork(); @@ -90,21 +70,137 @@ test('do not affects another scope', async () => { await Promise.all([ allSettled(source, { scope: scope1, - params: { first: 'sergey', second: 26, third: '30' }, + params: { first: 'sergey', second: 26 }, }), allSettled(source, { scope: scope2, - params: { first: 'Anon', second: 90, third: '154' }, + params: { first: 'Anon', second: 90 }, }), ]); + expect(serialize(scope1)).toMatchInlineSnapshot(` + { + "-w3pd79": 26, + "f2h7kg": "sergey", + } + `); + expect(serialize(scope2)).toMatchInlineSnapshot(` + { + "-w3pd79": 90, + "f2h7kg": "Anon", + } + `); +}); + +describe('targets: array of units', () => { + test('works in forked scope', async () => { + const app = createDomain(); + const source = app.createEvent<{ + first: string; + second: number; + third: string; + }>(); + const first = app.createEvent(); + const second = app.createEvent(); + + const $thirdA = app.createStore(''); + const $thirdB = app.createStore(''); - expect(scope1.getState($first)).toBe('sergey'); - expect(scope1.getState($second)).toBe(26); - expect(scope1.getState($thirdA)).toBe('30'); - expect(scope1.getState($thirdB)).toBe('30'); + const $first = app.createStore('').on(first, (_, p) => p); + const $second = restore(second, 0); - expect(scope2.getState($first)).toBe('Anon'); - expect(scope2.getState($second)).toBe(90); - expect(scope2.getState($thirdA)).toBe('154'); - expect(scope2.getState($thirdB)).toBe('154'); + spread({ + source, + targets: { first, second, third: [$thirdA, $thirdB] }, + }); + + const scope = fork(); + + await allSettled(source, { + scope, + params: { first: 'sergey', second: 26, third: '30' }, + }); + + expect(scope.getState($first)).toBe('sergey'); + expect(scope.getState($second)).toBe(26); + expect(scope.getState($thirdA)).toBe('30'); + expect(scope.getState($thirdB)).toBe('30'); + }); + + test('does not affect original store state', async () => { + const app = createDomain(); + const source = app.createEvent<{ + first: string; + second: number; + third: string; + }>(); + const first = app.createEvent(); + const second = app.createEvent(); + + const $thirdA = app.createStore(''); + const $thirdB = app.createStore(''); + + const $first = app.createStore('').on(first, (_, p) => p); + const $second = restore(second, 0); + + spread({ + source, + targets: { first, second, third: [$thirdA, $thirdB] }, + }); + + const scope = fork(); + + await allSettled(source, { + scope, + params: { first: 'sergey', second: 26, third: '30' }, + }); + + expect(scope.getState($first)).toBe('sergey'); + expect(scope.getState($second)).toBe(26); + expect(scope.getState($thirdA)).toBe('30'); + expect(scope.getState($thirdB)).toBe('30'); + + expect($first.getState()).toBe(''); + expect($second.getState()).toBe(0); + expect($thirdA.getState()).toBe(''); + expect($thirdB.getState()).toBe(''); + }); + + test('do not affects another scope', async () => { + const app = createDomain(); + const source = app.createEvent<{ + first: string; + second: number; + third: string; + }>(); + const first = app.createEvent(); + const second = app.createEvent(); + + const $thirdA = app.createStore(''); + const $thirdB = app.createStore(''); + + const $first = app.createStore('').on(first, (_, p) => p); + const $second = restore(second, 0); + + spread({ + source, + targets: { first, second, third: [$thirdA, $thirdB] }, + }); + + const scope1 = fork(); + const scope2 = fork(); + + await Promise.all([ + allSettled(source, { + scope: scope1, + params: { first: 'sergey', second: 26, third: '30' }, + }), + allSettled(source, { + scope: scope2, + params: { first: 'Anon', second: 90, third: '154' }, + }), + ]); + + expect(scope1.getState($first)).toBe('sergey'); + expect(scope1.getState($second)).toBe(26); + }); }); diff --git a/test-typings/spread.ts b/test-typings/spread.ts index cc90bd01..25b36029 100644 --- a/test-typings/spread.ts +++ b/test-typings/spread.ts @@ -11,13 +11,23 @@ import { import { spread } from '../dist/spread'; { - const $source = createStore({ first: '', last: '', foo: 1 }); + const $source = createStore({ first: '', last: '' }); const first = createEvent(); const last = createEvent(); - expectType>( + expectType>( spread({ source: $source, + targets: { + first, + last, + }, + }), + ); + + expectType>( + spread({ + source: createStore({ first: '', last: '', foo: 1 }), targets: { first, last, @@ -99,6 +109,39 @@ import { spread } from '../dist/spread'; } // Check input source type with output +{ + expectType>( + spread({ + source: createEvent<{ foo: string; bar: number }>(), + targets: { + foo: createEvent(), + bar: createEvent(), + }, + }), + ); + + expectType>( + spread({ + source: createStore({ random: '', bar: 5 }), + targets: { + random: createEvent(), + bar: createEvent(), + }, + }), + ); + + expectType>( + spread({ + source: createEffect<{ foo: string; bar: number }, void>(), + targets: { + foo: createEvent(), + bar: createEvent(), + }, + }), + ); +} + +// Check input source type with output (targets: array of units) { expectType>( spread({ @@ -135,6 +178,39 @@ import { spread } from '../dist/spread'; } // Check target different units +{ + expectType>( + spread({ + source: createEvent<{ foo: string; bar: number }>(), + targets: { + foo: createStore(''), + bar: createEffect(), + }, + }), + ); + + expectType>( + spread({ + source: createStore({ foo: '', bar: 5 }), + targets: { + foo: createStore(''), + bar: createEffect(), + }, + }), + ); + + expectType>( + spread({ + source: createEffect<{ foo: string; bar: number }, void>(), + targets: { + foo: createStore(''), + bar: createEffect(), + }, + }), + ); +} + +// Check target different units (targets: array of units) { expectType>( spread({ @@ -170,33 +246,85 @@ import { spread } from '../dist/spread'; ); } -// Check target is prepended +// Check target is prepended if type is given to generic or callback { - const foo = createEvent(); - - /** - * prepend arg is unknown now, but should be payload[K] type - * TODO: target prepend type should be inferred from source - */ - - // expectType>( - // spread({ - // source: createEvent<{ foo: string; bar: number; baz: boolean }>(), - // targets: { - // foo: foo.prepend((string) => string.length), - // bar: createEvent(), - // baz: [ - // createEvent().prepend((bool) => (bool ? 'true' : 'false')), - // createEvent().prepend((bool) => (bool ? 1 : 0)), - // ], - // }, - // }), - // ); + expectType>( + spread({ + source: createEvent<{ foo: string; bar: number; baz: string }>(), + targets: { + foo: createEvent().prepend((string) => string.length), + bar: createEvent(), + baz: createEvent().prepend((str: string) => Boolean(str)), + }, + }), + ); + + expectType>( + // @ts-expect-error + spread({ + source: createEvent<{ foo: string; bar: number; baz: string }>(), + targets: { + // @ts-expect-error (Type 'boolean' is not assignable to type 'number') + foo: createEvent().prepend((str) => str), + bar: createEvent(), + // @ts-expect-error (Type 'string' is not assignable to type 'boolean') + baz: createEvent().prepend((str: string) => str), + }, + }), + ); + + expectType>( + spread({ + source: createEvent<{ foo: string; bar: number; baz: boolean; zoo: string }>(), + targets: { + foo: createEvent().prepend((string: string) => string.length), + bar: createEvent(), + baz: [ + createEvent().prepend((bool) => + Boolean(expectType(bool)) ? 'true' : 'false', + ), + createEvent().prepend((bool: boolean) => + Boolean(expectType(bool)) ? 1 : 0, + ), + ], + zoo: [createEvent()], + }, + }), + ); + + expectType>( + // @ts-expect-error + spread({ + source: createEvent<{ foo: string; bar: number; baz: boolean; zoo: string }>(), + targets: { + // @ts-expect-error + foo: createEvent().prepend((str) => str), + bar: createEvent(), + baz: [ + // @ts-expect-error + createEvent().prepend((bool) => bool), + // @ts-expect-error + createEvent().prepend((bool: boolean) => bool), + ], + zoo: [createEvent()], + }, + }), + ); } // Check target different units without source { const spreadToStores = spread({ + targets: { + foo: createStore(''), + bar: createEffect(), + baz: createEvent(), + }, + }); + + expectType>(spreadToStores); + + const spreadToUnits = spread({ targets: { foo: createStore(''), bar: createEffect(), @@ -206,18 +334,26 @@ import { spread } from '../dist/spread'; }); expectType>( - spreadToStores, + spreadToUnits, ); } { const spreadToStores = spread({ + foo: createStore(''), + bar: createEffect(), + baz: createEvent(), + }); + + expectType>(spreadToStores); + + const spreadToUnits = spread({ foo: createStore(''), baz: createEvent(), last: [createEvent(), createStore(null)], }); expectType>( - spreadToStores, + spreadToUnits, ); } @@ -284,7 +420,11 @@ import { spread } from '../dist/spread'; // allows nested { - const $source = createStore({ first: '', last: { nested: '', other: '', arr: 1 } }); + const $source = createStore({ first: '', last: { nested: '', other: '' } }); + const $secondSource = createStore({ + first: '', + last: { nested: '', other: '', arr: 1 }, + }); const first = createEvent(); const nested = createEvent(); const other = createEvent(); @@ -293,6 +433,19 @@ import { spread } from '../dist/spread'; // nested full match spread({ source: $source, + targets: { + first, + last: spread({ + targets: { + nested, + other, + }, + }), + }, + }); + + spread({ + source: $secondSource, targets: { first, last: spread({ @@ -333,7 +486,6 @@ import { spread } from '../dist/spread'; targets: { nested, other, - arr: arrayOfUnits, }, }); @@ -345,6 +497,22 @@ import { spread } from '../dist/spread'; }, }); + const secondOut = spread({ + targets: { + nested, + other, + arr: arrayOfUnits, + }, + }); + + spread({ + source: $secondSource, + targets: { + first, + last: secondOut, + }, + }); + // nested partial match outer const outPart = spread({ targets: { @@ -373,6 +541,21 @@ import { spread } from '../dist/spread'; // sample full match sample({ clock: $source, + target: spread({ + targets: { + first, + last: spread({ + targets: { + nested, + other, + }, + }), + }, + }), + }); + + sample({ + clock: $secondSource, target: spread({ targets: { first, @@ -400,7 +583,11 @@ import { spread } from '../dist/spread'; }); } { - const $source = createStore({ first: '', last: { nested: '', other: '', arr: 1 } }); + const $source = createStore({ first: '', last: { nested: '', other: '' } }); + const $secondSource = createStore({ + first: '', + last: { nested: '', other: '', arr: 1 }, + }); const first = createEvent(); const nested = createEvent(); const other = createEvent(); @@ -409,6 +596,17 @@ import { spread } from '../dist/spread'; // nested full match spread({ source: $source, + targets: { + first, + last: spread({ + nested, + other, + }), + }, + }); + + spread({ + source: $secondSource, targets: { first, last: spread({ @@ -442,7 +640,6 @@ import { spread } from '../dist/spread'; const out = spread({ nested, other, - arr: arrayOfUnits, }); spread({ @@ -453,6 +650,20 @@ import { spread } from '../dist/spread'; }, }); + const secondOut = spread({ + nested, + other, + arr: arrayOfUnits, + }); + + spread({ + source: $secondSource, + targets: { + first, + last: secondOut, + }, + }); + // nested partial match outer const outPart = spread({ nested }); @@ -473,6 +684,17 @@ import { spread } from '../dist/spread'; // sample full match sample({ clock: $source, + target: spread({ + first, + last: spread({ + nested, + other, + }), + }), + }); + + sample({ + clock: $secondSource, target: spread({ first, last: spread({