From 80f3171751fafcd441c2fd208efc7acd99c1410b Mon Sep 17 00:00:00 2001 From: Robin Kneepkens Date: Fri, 17 Nov 2023 16:38:41 +0100 Subject: [PATCH] My solutions for the tutorial --- tutorial/1 - intro.test.ts | 9 ++-- tutorial/2 - deriving.test.ts | 34 +++++++----- tutorial/3 - reacting.test.ts | 41 ++++++++++----- tutorial/4 - inner workings.test.ts | 50 +++++++++--------- tutorial/5 - unresolved.test.ts | 32 ++++++------ tutorial/7 - utils.test.ts | 43 +++++++++++---- tutorial/8 - advanced.test.ts | 81 ++++++++++++++--------------- tutorial/9 - expert.test.ts | 53 ++++++++++--------- 8 files changed, 193 insertions(+), 150 deletions(-) diff --git a/tutorial/1 - intro.test.ts b/tutorial/1 - intro.test.ts index c7671f5..b199c68 100644 --- a/tutorial/1 - intro.test.ts +++ b/tutorial/1 - intro.test.ts @@ -43,7 +43,7 @@ describe('intro', () => { * This can also be indicated with the `__YOUR_TURN__` variable. * * It should be clear what to do here... */ - bool = __YOUR_TURN__; + bool = true; expect(bool).toBeTrue(); // We use expectations like this to verify the result. }); @@ -55,7 +55,7 @@ describe('intro', () => { * ** Your Turn ** * Remove the `.skip` so this part of the tutorial will run. */ -describe.skip('the basics', () => { +describe('the basics', () => { /** * The `Atom` is the basic building block of `@skunkteam/sherlock`. * It holds a value which you can `get()` and `set()`. @@ -73,6 +73,7 @@ describe.skip('the basics', () => { // ** Your Turn ** // Use the `.set()` method to change the value of the `Atom`. + myValue$.set(2); expect(myValue$.get()).toEqual(2); }); @@ -97,7 +98,7 @@ describe.skip('the basics', () => { * negative to a positive number and vice versa) of the original `Atom`. */ // Use `myValue$.derive(val => ...)` to implement `myInverse$`. - const myInverse$ = myValue$.derive(__YOUR_TURN__ => __YOUR_TURN__); + const myInverse$ = myValue$.derive(val => -val); expect(myInverse$.get()).toEqual(-1); // So if we set `myValue$` to -2: myValue$.set(-2); @@ -122,7 +123,7 @@ describe.skip('the basics', () => { * * Now react to `myCounter$`. In every `react()`. * Increase the `reacted` variable by one. */ - myCounter$.react(() => __YOUR_TURN__); + myCounter$.react(() => reacted++); expect(reacted).toEqual(1); // `react()` will react immediately, more on that later. diff --git a/tutorial/2 - deriving.test.ts b/tutorial/2 - deriving.test.ts index 5691dd9..248b448 100644 --- a/tutorial/2 - deriving.test.ts +++ b/tutorial/2 - deriving.test.ts @@ -37,7 +37,7 @@ describe.skip('deriving', () => { */ // We can combine txt with `repeat$.get()` here. - const lyric$ = text$.derive(txt => txt /* __YOUR_TURN__ */); + const lyric$ = text$.derive(txt => txt.repeat(repeat$.get())); expect(lyric$.get()).toEqual(`It won't be long`); @@ -74,12 +74,18 @@ describe.skip('deriving', () => { */ // Should return 'Fizz' when `myCounter$` is a multiple of 3 and '' otherwise. - const fizz$: Derivable = myCounter$.derive(__YOUR_TURN__); + const fizz$: Derivable = myCounter$.derive(value => { + return value % 3 ? '' : 'Fizz'; + }); // Should return 'Buzz' when `myCounter$` is a multiple of 5 and '' otherwise. - const buzz$: Derivable = myCounter$.derive(__YOUR_TURN__); + const buzz$: Derivable = myCounter$.derive(value => { + return value % 5 ? '' : 'Buzz'; + }); - const fizzBuzz$: Derivable = derive(__YOUR_TURN__); + const fizzBuzz$: Derivable = derive(() => { + return fizz$.get() + buzz$.get() || myCounter$.get(); + }); expect(fizz$.get()).toEqual(''); expect(buzz$.get()).toEqual(''); @@ -153,9 +159,9 @@ describe.skip('deriving', () => { const tweetCount = pastTweets.length; const lastTweet = pastTweets[tweetCount - 1]; - expect(tweetCount).toEqual(__YOUR_TURN__); // Is there a new tweet? - expect(lastTweet).toContain(__YOUR_TURN__); // Who sent it? Donald? Or Barack? - expect(lastTweet).toContain(__YOUR_TURN__); // What did he tweet? + expect(tweetCount).toEqual(3); // Is there a new tweet? + expect(lastTweet).toContain('Donald'); // Who sent it? Donald? Or Barack? + expect(lastTweet).toContain('politics'); // What did he tweet? /** * As you can see, this is something to look out for. @@ -200,17 +206,17 @@ describe.skip('deriving', () => { */ const fizz$ = myCounter$ .derive(count => count % 3) - .is(__YOUR_TURN__) - .and(__YOUR_TURN__) - .or(__YOUR_TURN__) as Derivable; + .is(0) + .and('Fizz') + .or('') as Derivable; const buzz$ = myCounter$ .derive(count => count % 5) - .is(__YOUR_TURN__) - .and(__YOUR_TURN__) - .or(__YOUR_TURN__) as Derivable; + .is(0) + .and('Buzz') + .or('') as Derivable; - const fizzBuzz$ = derive(() => fizz$.get() + buzz$.get()).or(__YOUR_TURN__); + const fizzBuzz$ = derive(() => fizz$.get() + buzz$.get()).or(myCounter$); for (let count = 1; count <= 100; count++) { // Set the value of the `Atom`, diff --git a/tutorial/3 - reacting.test.ts b/tutorial/3 - reacting.test.ts index 162d4d3..ece776d 100644 --- a/tutorial/3 - reacting.test.ts +++ b/tutorial/3 - reacting.test.ts @@ -69,11 +69,11 @@ describe.skip('reacting', () => { * Time to react to `myAtom$` with the `reactor()` function defined * above. */ - + myAtom$.react(reactor); expectReact(1, 'initial value'); // Now set a 'new value' to `myAtom$`. - + myAtom$.set('new value'); expectReact(2, 'new value'); }); @@ -99,7 +99,7 @@ describe.skip('reacting', () => { * * catch the returned `stopper` in a variable */ - myAtom$.react(reactor); + let stopper = myAtom$.react(reactor); expectReact(1, 'initial value'); @@ -108,7 +108,7 @@ describe.skip('reacting', () => { * * Call the `stopper`. */ - + stopper(); myAtom$.set('new value'); // And the reaction stopped. @@ -130,9 +130,9 @@ describe.skip('reacting', () => { * In the reaction below, use the stopper callback to stop the * reaction */ - myAtom$.react((val, __YOUR_TURN___) => { + myAtom$.react((val, stop) => { reactor(val); - __YOUR_TURN___; + stop(); }); expectReact(1, 'initial value'); @@ -185,7 +185,7 @@ describe.skip('reacting', () => { * * Try giving `boolean$` as `until` option. */ - string$.react(reactor, __YOUR_TURN__); + string$.react(reactor, { until: boolean$ }); // It should react directly as usual. expectReact(1, 'Value'); @@ -233,7 +233,13 @@ describe.skip('reacting', () => { * Use `!string$.get()` to return `true` when the `string` is * empty. */ - string$.react(reactor, __YOUR_TURN__); + let stringEmpty = function () { + return !string$.get(); + }; + + string$.react(reactor, { + until: stringEmpty, + }); // It should react as usual: string$.set('New value'); @@ -261,7 +267,7 @@ describe.skip('reacting', () => { * Try using the first parameter of the `until` function to do * the same as above. */ - string$.react(reactor, __YOUR_TURN__); + string$.react(reactor, { until: parent$ => !parent$.get() }); // It should react as usual. string$.set('New value'); @@ -305,7 +311,7 @@ describe.skip('reacting', () => { * * *Hint: remember the `.is()` method from tutorial 2?* */ - sherlock$.react(reactor, __YOUR_TURN__); + sherlock$.react(reactor, { from: parent$ => parent$.is('dear') }); expectReact(0); ['Elementary,', 'my', 'dear', 'Watson'].forEach(txt => sherlock$.set(txt)); @@ -334,7 +340,7 @@ describe.skip('reacting', () => { * Now, let's react to all even numbers. * Except 4, we don't want to make it too easy now. */ - count$.react(reactor, __YOUR_TURN__); + count$.react(reactor, { when: parent$ => parent$.derive(value => value % 2 == 0 && value != 4) }); expectReact(1, 0); @@ -361,7 +367,7 @@ describe.skip('reacting', () => { * * Say you want to react when `done$` is true. But not right away.. */ - done$.react(reactor, __YOUR_TURN__); + done$.react(reactor, { skipFirst: true }); expectReact(0); done$.set(true); @@ -387,7 +393,10 @@ describe.skip('reacting', () => { * * *Hint: you will need to combine `once` with another option* */ - finished$.react(reactor, __YOUR_TURN__); + finished$.react(reactor, { + once: true, + skipFirst: true, + }); expectReact(0); // When finished it should react once. @@ -414,7 +423,11 @@ describe.skip('reacting', () => { * `connected$` indicates the current connection status. * This should be possible with three simple ReactorOptions */ - connected$.react(reactor, __YOUR_TURN__); + connected$.react(reactor, { + skipFirst: true, + once: true, + from: parent$ => parent$, + }); // It starts as 'not connected' expectReact(0); diff --git a/tutorial/4 - inner workings.test.ts b/tutorial/4 - inner workings.test.ts index 9daf82b..7ba21ef 100644 --- a/tutorial/4 - inner workings.test.ts +++ b/tutorial/4 - inner workings.test.ts @@ -11,7 +11,7 @@ export const __YOUR_TURN__ = {} as any; /** * Time to dive a bit deeper into the inner workings of `@skunkteam/sherlock`. */ -describe.skip('inner workings', () => { +describe('inner workings', () => { /** * What if there is a derivation that reads from one of two `Derivable`s * dynamically? Will both of those `Derivable`s be tracked for changes? @@ -43,8 +43,8 @@ describe.skip('inner workings', () => { * * What do you expect? */ - expect(reacted).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(reacted).toHaveBeenLastCalledWith(__YOUR_TURN__, expect.toBeFunction()); + expect(reacted).toHaveBeenCalledTimes(1); + expect(reacted).toHaveBeenLastCalledWith(1, expect.toBeFunction()); // `switch$` is still set to true (number) number$.set(2); @@ -54,8 +54,8 @@ describe.skip('inner workings', () => { * * What do you expect? */ - expect(reacted).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(reacted).toHaveBeenLastCalledWith(__YOUR_TURN__, expect.toBeFunction()); + expect(reacted).toHaveBeenCalledTimes(2); + expect(reacted).toHaveBeenLastCalledWith(2, expect.toBeFunction()); // Now let's reset the mock function, so the call count should // be 0 again. @@ -71,8 +71,8 @@ describe.skip('inner workings', () => { * * What do you expect now? */ - expect(reacted).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(reacted).toHaveBeenLastCalledWith(__YOUR_TURN__, expect.toBeFunction()); + expect(reacted).toHaveBeenCalledTimes(1); + expect(reacted).toHaveBeenLastCalledWith('two', expect.toBeFunction()); }); /** @@ -98,17 +98,17 @@ describe.skip('inner workings', () => { */ // Well, what do you expect? - expect(hasDerived).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(hasDerived).toHaveBeenCalledTimes(0); myDerivation$.get(); // And after a `.get()`? - expect(hasDerived).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(hasDerived).toHaveBeenCalledTimes(1); myDerivation$.get(); // And after the second `.get()`? Is there an extra call? - expect(hasDerived).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(hasDerived).toHaveBeenCalledTimes(2); /** * The state of any `Derivable` can change at any moment. @@ -147,27 +147,27 @@ describe.skip('inner workings', () => { * * Ok, it's your turn to complete the expectations. */ - expect(hasDerived).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(hasDerived).toHaveBeenCalledTimes(1); myDerivation$.get(); - expect(hasDerived).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(hasDerived).toHaveBeenCalledTimes(1); myAtom$.set(false); - expect(hasDerived).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(hasDerived).toHaveBeenCalledTimes(2); myDerivation$.get(); - expect(hasDerived).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(hasDerived).toHaveBeenCalledTimes(2); stopper(); - expect(hasDerived).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(hasDerived).toHaveBeenCalledTimes(2); myDerivation$.get(); - expect(hasDerived).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(hasDerived).toHaveBeenCalledTimes(3); /** * Since the `.react()` already listens to the value(changes) there is @@ -212,23 +212,23 @@ describe.skip('inner workings', () => { // Note that this is the same value as it was initialized with myAtom$.set(1); - expect(first).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(second).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(first).toHaveBeenCalledTimes(1); + expect(second).toHaveBeenCalledTimes(1); myAtom$.set(2); - expect(first).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(second).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(first).toHaveBeenCalledTimes(2); + expect(second).toHaveBeenCalledTimes(1); myAtom$.set(3); - expect(first).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(second).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(first).toHaveBeenCalledTimes(3); + expect(second).toHaveBeenCalledTimes(2); myAtom$.set(4); - expect(first).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(second).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(first).toHaveBeenCalledTimes(4); + expect(second).toHaveBeenCalledTimes(2); /** * Can you explain the behavior above? @@ -265,7 +265,7 @@ describe.skip('inner workings', () => { * The `Atom` is set with exactly the same object as before. Will the * `.react()` fire? */ - expect(hasReacted).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(hasReacted).toHaveBeenCalledTimes(1); /** * But what if you use an object, that can be easily compared through a diff --git a/tutorial/5 - unresolved.test.ts b/tutorial/5 - unresolved.test.ts index 998b121..094c60e 100644 --- a/tutorial/5 - unresolved.test.ts +++ b/tutorial/5 - unresolved.test.ts @@ -17,7 +17,7 @@ export const __YOUR_TURN__ = {} as any; * state, called `unresolved`. This indicates that the data is not available * yet, but (probably) will be at some point. */ -describe.skip('unresolved', () => { +describe('unresolved', () => { /** * Let's start by creating an `unresolved` `Derivable`. */ @@ -27,14 +27,14 @@ describe.skip('unresolved', () => { // since it can't be inferred by TypeScript this way. const myAtom$ = atom.unresolved(); - expect(myAtom$.resolved).toEqual(__YOUR_TURN__); + expect(myAtom$.resolved).toEqual(false); /** * ** Your Turn ** * * Resolve the atom, it's pretty easy */ - + myAtom$.set(42); expect(myAtom$.resolved).toBeTrue(); }); @@ -48,7 +48,7 @@ describe.skip('unresolved', () => { * * Time to create an `unresolved` Atom.. */ - const myAtom$: DerivableAtom = __YOUR_TURN__; + const myAtom$: DerivableAtom = atom.unresolved(); expect(myAtom$.resolved).toBeFalse(); @@ -63,10 +63,10 @@ describe.skip('unresolved', () => { * * What do you expect? */ - expect(myAtom$.resolved).toEqual(__YOUR_TURN__); + expect(myAtom$.resolved).toEqual(true); // .toThrow() or .not.toThrow()? ↴ - expect(() => myAtom$.get()) /*__YOUR_TURN__*/; + expect(() => myAtom$.get()).not.toThrow(); }); /** @@ -86,14 +86,14 @@ describe.skip('unresolved', () => { * * What do you expect? */ - expect(hasReacted).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(hasReacted).toHaveBeenCalledTimes(0); /** * ** Your Turn ** * * Now make the last expect succeed */ - + myAtom$.set(`woohoow, I was called`); expect(myAtom$.resolved).toBeTrue(); expect(hasReacted).toHaveBeenCalledExactlyOnceWith(`woohoow, I was called`, expect.toBeFunction()); }); @@ -112,7 +112,7 @@ describe.skip('unresolved', () => { * * Set the value.. */ - + myAtom$.set("it's alive!"); expect(myAtom$.get()).toEqual(`it's alive!`); /** @@ -120,7 +120,7 @@ describe.skip('unresolved', () => { * * Unset the value.. (*Hint: TypeScript is your friend*) */ - + myAtom$.unset(); expect(myAtom$.resolved).toBeFalse(); }); @@ -140,14 +140,14 @@ describe.skip('unresolved', () => { * * Combine the two `Atom`s into one `Derivable` */ - const myDerivable$: Derivable = __YOUR_TURN__; + const myDerivable$: Derivable = myString$.and(myOtherString$).derive(_ => 'Both resolved'); /** * ** Your Turn ** * * Is `myDerivable$` expected to be `resolved`? */ - expect(myDerivable$.resolved).toEqual(__YOUR_TURN__); + expect(myDerivable$.resolved).toEqual(false); // Now let's set one of the two source `Atom`s myString$.set('some'); @@ -157,12 +157,12 @@ describe.skip('unresolved', () => { * * What do you expect to see in `myDerivable$`. */ - expect(myDerivable$.resolved).toEqual(__YOUR_TURN__); + expect(myDerivable$.resolved).toEqual(false); // And what if we set `myOtherString$`? myOtherString$.set('data'); - expect(myDerivable$.resolved).toEqual(__YOUR_TURN__); - expect(myDerivable$.get()).toEqual(__YOUR_TURN__); + expect(myDerivable$.resolved).toEqual(true); + expect(myDerivable$.get()).toEqual('Both resolved'); /** * ** Your Turn ** @@ -171,6 +171,6 @@ describe.skip('unresolved', () => { * What do you expect `myDerivable$` to be? */ myString$.unset(); - expect(myDerivable$.resolved).toEqual(__YOUR_TURN__); + expect(myDerivable$.resolved).toEqual(false); }); }); diff --git a/tutorial/7 - utils.test.ts b/tutorial/7 - utils.test.ts index 3e49e8e..8f7eb57 100644 --- a/tutorial/7 - utils.test.ts +++ b/tutorial/7 - utils.test.ts @@ -18,7 +18,7 @@ expect(struct).toBe(struct); * multiple values of a single `Derivable` or combine multiple `Derivable`s into * one. We will show a couple of those here. */ -describe.skip('utils', () => { +describe('utils', () => { /** * As the name suggests, `pairwise()` will call the given function with both * the current and the previous state. @@ -39,7 +39,13 @@ describe.skip('utils', () => { * *Hint: check the overloads of pairwise if you're struggling with * `oldVal`.* */ - myCounter$.derive(__YOUR_TURN__).react(reactSpy); + myCounter$ + .derive( + pairwise((newVal, oldVal) => { + return newVal - oldVal; + }, 0), + ) + .react(reactSpy); expect(reactSpy).toHaveBeenCalledExactlyOnceWith(1, expect.toBeFunction()); @@ -72,7 +78,13 @@ describe.skip('utils', () => { * * Now, use `scan()`, to add all the emitted values together */ - myCounter$.derive(__YOUR_TURN__).react(reactSpy); + myCounter$ + .derive( + scan((accumulator, currentValue) => { + return currentValue + accumulator; + }, 0), + ) + .react(reactSpy); expect(reactSpy).toHaveBeenCalledExactlyOnceWith(1, expect.toBeFunction()); @@ -92,7 +104,7 @@ describe.skip('utils', () => { */ }); - it.skip('pairwise - BONUS', () => { + it('pairwise - BONUS', () => { const myCounter$ = atom(1); let lastPairwiseResult = 0; @@ -107,7 +119,11 @@ describe.skip('utils', () => { * `lastPairwiseResult` instead. This is so the implementation can be * validated. */ - myCounter$.react(__YOUR_TURN__); + myCounter$.react( + pairwise((newVal, oldVal) => { + lastPairwiseResult = newVal - oldVal; + }, 0), + ); expect(lastPairwiseResult).toEqual(1); @@ -120,7 +136,7 @@ describe.skip('utils', () => { expect(lastPairwiseResult).toEqual(42); }); - it.skip('scan - BONUS', () => { + it('scan - BONUS', () => { const myCounter$ = atom(1); let lastScanResult = 0; @@ -135,7 +151,12 @@ describe.skip('utils', () => { * `lastScanResult` instead. This is so the implementation can be * validated. */ - myCounter$.react(__YOUR_TURN__); + myCounter$.react( + scan((accumulator, newVal) => { + lastScanResult = newVal + accumulator; + return lastScanResult; + }, 0), + ); expect(lastScanResult).toEqual(1); @@ -186,11 +207,11 @@ describe.skip('utils', () => { * expect? */ expect(myOneAtom$.get()).toEqual({ - regularProp: __YOUR_TURN__, - string: __YOUR_TURN__, - number: __YOUR_TURN__, + regularProp: 'new value', + string: 'my string', + number: 1, sub: { - string: __YOUR_TURN__, + string: 'my new substring', }, }); }); diff --git a/tutorial/8 - advanced.test.ts b/tutorial/8 - advanced.test.ts index 48e9944..c8d4e1f 100644 --- a/tutorial/8 - advanced.test.ts +++ b/tutorial/8 - advanced.test.ts @@ -8,7 +8,7 @@ import { Map as ImmutableMap } from 'immutable'; */ export const __YOUR_TURN__ = {} as any; -describe.skip('advanced', () => { +describe('advanced', () => { /** * In the case a `Derivable` is required, but the value is immutable. * You can use a `constant()`. @@ -29,11 +29,9 @@ describe.skip('advanced', () => { * What do you expect this `Derivable` to do on `.set()`, `.get()` etc? */ - // Remove this after taking your turn below. - expect(false).toBe(true); // .toThrow() or .not.toThrow()? ↴ (2x) - expect(() => c.get()) /* __YOUR_TURN__ */; - expect(() => c.set('new value')) /* __YOUR_TURN__ */; + expect(() => c.get()).not.toThrow(); + expect(() => c.set('new value')).toThrow(); }); /** @@ -57,12 +55,11 @@ describe.skip('advanced', () => { * * Rewrite the `.get()`/`.set()` combos below using `.swap()`. */ - expect(false).toBe(true); - // Remove this after taking your turn below. - myCounter$.set(plusOne(myCounter$.get())); + + myCounter$.swap(plusOne); expect(myCounter$.get()).toEqual(1); - myCounter$.set(plusOne(myCounter$.get())); + myCounter$.swap(plusOne); expect(myCounter$.get()).toEqual(2); }); @@ -83,14 +80,14 @@ describe.skip('advanced', () => { * * Use the `.value` accessor to get the current value. */ - expect(__YOUR_TURN__).toEqual('foo'); + expect(myAtom$.value).toEqual('foo'); /** * ** Your Turn ** * * Now use the `.value` accessor to set a 'new value'. */ - myAtom$.value = __YOUR_TURN__; + myAtom$.value = 'new value'; expect(myAtom$.get()).toEqual('new value'); }); @@ -105,7 +102,7 @@ describe.skip('advanced', () => { /** * ** Your Turn ** */ - expect(myAtom$.value).toEqual(__YOUR_TURN__); + expect(myAtom$.value).toEqual(undefined); }); /** @@ -128,11 +125,11 @@ describe.skip('advanced', () => { * We just created two `Derivable`s that are almost exactly the same. * But what happens when their source becomes `unresolved`? */ - expect(usingGet$.resolved).toEqual(__YOUR_TURN__); - expect(usingVal$.resolved).toEqual(__YOUR_TURN__); + expect(usingGet$.resolved).toEqual(true); + expect(usingVal$.resolved).toEqual(true); myAtom$.unset(); - expect(usingGet$.resolved).toEqual(__YOUR_TURN__); - expect(usingVal$.resolved).toEqual(__YOUR_TURN__); + expect(usingGet$.resolved).toEqual(false); + expect(usingVal$.resolved).toEqual(true); }); }); @@ -155,7 +152,7 @@ describe.skip('advanced', () => { * * Use the `.map()` method to create the expected output below */ - const mappedAtom$: Derivable = __YOUR_TURN__; + const mappedAtom$: Derivable = myAtom$.map(value => value.toString().repeat(value)); mappedAtom$.react(mapReactSpy); @@ -186,11 +183,11 @@ describe.skip('advanced', () => { * We changed`myRepeat$` to equal 3. * Do you expect both reactors to have fired? And with what? */ - expect(deriveReactSpy).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(deriveReactSpy).toHaveBeenLastCalledWith(__YOUR_TURN__, expect.toBeFunction()); + expect(deriveReactSpy).toHaveBeenCalledTimes(2); + expect(deriveReactSpy).toHaveBeenLastCalledWith('hohoho', expect.toBeFunction()); - expect(mapReactSpy).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(mapReactSpy).toHaveBeenLastCalledWith(__YOUR_TURN__, expect.toBeFunction()); + expect(mapReactSpy).toHaveBeenCalledTimes(2); + expect(mapReactSpy).toHaveBeenLastCalledWith('hohoho', expect.toBeFunction()); myString$.value = 'ha'; /** @@ -198,18 +195,18 @@ describe.skip('advanced', () => { * * And now that we have changed `myString$`? And when `myRepeat$` changed again? */ - expect(deriveReactSpy).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(deriveReactSpy).toHaveBeenLastCalledWith(__YOUR_TURN__, expect.toBeFunction()); + expect(deriveReactSpy).toHaveBeenCalledTimes(3); + expect(deriveReactSpy).toHaveBeenLastCalledWith('hahaha', expect.toBeFunction()); - expect(mapReactSpy).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(mapReactSpy).toHaveBeenLastCalledWith(__YOUR_TURN__, expect.toBeFunction()); + expect(mapReactSpy).toHaveBeenCalledTimes(2); + expect(mapReactSpy).toHaveBeenLastCalledWith('hohoho', expect.toBeFunction()); myRepeat$.value = 2; - expect(deriveReactSpy).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(deriveReactSpy).toHaveBeenLastCalledWith(__YOUR_TURN__, expect.toBeFunction()); + expect(deriveReactSpy).toHaveBeenCalledTimes(4); + expect(deriveReactSpy).toHaveBeenLastCalledWith('haha', expect.toBeFunction()); - expect(mapReactSpy).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(mapReactSpy).toHaveBeenLastCalledWith(__YOUR_TURN__, expect.toBeFunction()); + expect(mapReactSpy).toHaveBeenCalledTimes(3); + expect(mapReactSpy).toHaveBeenLastCalledWith('haha', expect.toBeFunction()); /** * As you can see, a change in `myString$` will not trigger an @@ -236,7 +233,7 @@ describe.skip('advanced', () => { // This first function is called when getting... n => -n, // ...and this second function is called when setting. - __YOUR_TURN__, + n => -n, ); // The original `atom` was set to 1, so we want the inverse to @@ -290,7 +287,7 @@ describe.skip('advanced', () => { * * * Hint: you'll have to cast the result from `.pluck()`. */ - firstProp$ = __YOUR_TURN__; + firstProp$ = myMap$.pluck('firstProp') as SettableDerivable; }); /** @@ -306,18 +303,18 @@ describe.skip('advanced', () => { * What do you expect the plucked `Derivable` to look like? And what * happens when we `.set()` it? */ - expect(firstProp$.get()).toEqual(__YOUR_TURN__); + expect(firstProp$.get()).toEqual('firstValue'); // the plucked `Derivable` should be settable firstProp$.set('other value'); // is the `Derivable` value the same as was set? - expect(firstProp$.get()).toEqual(__YOUR_TURN__); + expect(firstProp$.get()).toEqual('other value'); // How many times was the spy called? Note the `skipFirst`.. - expect(reactPropSpy).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(reactPropSpy).toHaveBeenCalledTimes(1); // ...and what was the value? - expect(reactPropSpy).toHaveBeenLastCalledWith(__YOUR_TURN__, expect.toBeFunction()); + expect(reactPropSpy).toHaveBeenLastCalledWith('other value', expect.toBeFunction()); }); /** @@ -339,7 +336,7 @@ describe.skip('advanced', () => { myMap$.swap(map => map.set('secondProp', 'new value')); // How many times was the spy called? Note the `skipFirst`. - expect(reactPropSpy).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(reactPropSpy).toHaveBeenCalledTimes(0); /** * ** Your Turn ** @@ -349,10 +346,10 @@ describe.skip('advanced', () => { myMap$.swap(map => map.set('firstProp', 'new value')); // How many times was the spy called? Note the `skipFirst`.. - expect(reactPropSpy).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(reactPropSpy).toHaveBeenCalledTimes(1); // ...and what was the value? - expect(reactPropSpy).toHaveBeenLastCalledWith(__YOUR_TURN__, expect.toBeFunction()); + expect(reactPropSpy).toHaveBeenLastCalledWith('new value', expect.toBeFunction()); }); /** @@ -372,10 +369,10 @@ describe.skip('advanced', () => { * So what if we set `firstProp$`? Does this propagate to the source * `Derivable`? */ - firstProp$.set(__YOUR_TURN__); - expect(reactSpy).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(myMap$.get().get('firstProp')).toEqual(__YOUR_TURN__); - expect(myMap$.get().get('secondProp')).toEqual(__YOUR_TURN__); + firstProp$.set('mysterious'); + expect(reactSpy).toHaveBeenCalledTimes(1); + expect(myMap$.get().get('firstProp')).toEqual('mysterious'); + expect(myMap$.get().get('secondProp')).toEqual('secondValue'); }); }); }); diff --git a/tutorial/9 - expert.test.ts b/tutorial/9 - expert.test.ts index 4f6b6fb..227c26f 100644 --- a/tutorial/9 - expert.test.ts +++ b/tutorial/9 - expert.test.ts @@ -8,7 +8,7 @@ import { derivableCache } from '@skunkteam/sherlock-utils'; */ export const __YOUR_TURN__ = {} as any; -describe.skip('expert', () => { +describe('expert', () => { describe('`.autoCache()`', () => { /** * If a `.get()` is called on a `Derivable` all derivations will be @@ -32,7 +32,7 @@ describe.skip('expert', () => { */ // `.toHaveBeenCalled()` or `.not.toHaveBeenCalled()`? ↴ - expect(hasDerived) /* Your Turn */; + expect(hasDerived).not.toHaveBeenCalled(); mySecondDerivation$.get(); @@ -44,7 +44,7 @@ describe.skip('expert', () => { * first `Derivable` actually executed its derivation? */ // how many times? - expect(hasDerived).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(hasDerived).toHaveBeenCalledTimes(3); }); /** @@ -68,7 +68,7 @@ describe.skip('expert', () => { * expectations pass. */ const myAtom$ = atom(true); - const myFirstDerivation$ = myAtom$.derive(firstHasDerived); + const myFirstDerivation$ = myAtom$.derive(firstHasDerived).autoCache(); const mySecondDerivation$ = myFirstDerivation$.derive(() => secondHasDerived(myFirstDerivation$.get() + myFirstDerivation$.get()), ); @@ -110,9 +110,9 @@ describe.skip('expert', () => { mySecondDerivation$.get(); // first after last .get() - expect(firstHasDerived).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(firstHasDerived).toHaveBeenCalledTimes(2); // second after last .get() - expect(secondHasDerived).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(secondHasDerived).toHaveBeenCalledTimes(3); }); }); @@ -182,9 +182,14 @@ describe.skip('expert', () => { * But does that apply here? * How many times has the setup run, for the price `Derivable`. */ - expect(stockPrice$).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(stockPrice$).toHaveBeenCalledTimes(2); /** Can you explain this behavior? */ + /** + * stockPrices$ is not a derivable itself, just the setup function. + * Because of that, we don't get the nice caching that the real + * derivables have. + */ }); /** @@ -229,19 +234,19 @@ describe.skip('expert', () => { */ // How often was the reactor on price$ called? - expect(reactSpy).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(reactSpy).toHaveBeenCalledTimes(0); // And how many times did the setup run? - expect(stockPrice$).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(stockPrice$).toHaveBeenCalledTimes(2); // What's the value of price$ now? - expect(price$.value).toEqual(__YOUR_TURN__); + expect(price$.value).toEqual(undefined); // And the value of googlPrice$? - expect(googlPrice$.value).toEqual(__YOUR_TURN__); + expect(googlPrice$.value).toEqual(1079.11); // Is googlPrice$ still even driving any reactors? - expect(googlPrice$.connected).toEqual(__YOUR_TURN__); + expect(googlPrice$.connected).toEqual(false); /** * Can you explain this behavior? @@ -324,8 +329,8 @@ describe.skip('expert', () => { * * So the value was increased. What do you think happened now? */ - expect(reactSpy).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(reactSpy).toHaveBeenLastCalledWith([__YOUR_TURN__]); + expect(reactSpy).toHaveBeenCalledTimes(2); + expect(reactSpy).toHaveBeenLastCalledWith([1079.11]); /** * So that worked, now let's try and add another company to the @@ -343,8 +348,8 @@ describe.skip('expert', () => { * * We had a price for 'GOOGL', but not for 'APPL'... */ - expect(reactSpy).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(reactSpy).toHaveBeenCalledWith([__YOUR_TURN__, __YOUR_TURN__]); + expect(reactSpy).toHaveBeenCalledTimes(3); + expect(reactSpy).toHaveBeenCalledWith([1079.11, undefined]); }); }); @@ -404,7 +409,7 @@ describe.skip('expert', () => { * * Has anything changed, by using the `derivableCache`? */ - expect(stockPrice$).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(stockPrice$).toHaveBeenCalledTimes(1); // Now let's resolve the price stockPrice$.mock.results[0].value.set(1079.11); @@ -417,10 +422,10 @@ describe.skip('expert', () => { * * What happens this time? Has the setup run again? */ - expect(stockPrice$).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(stockPrice$).toHaveBeenCalledTimes(1); // Ok, but did it update the HTML? - expect(reactSpy).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(lastEmittedHTMLs()[0]).toContain(__YOUR_TURN__); + expect(reactSpy).toHaveBeenCalledTimes(2); + expect(lastEmittedHTMLs()[0]).toContain('$ 1079.11'); // Last chance, what if we add a company companies$.swap(current => [...current, 'APPL']); @@ -433,12 +438,12 @@ describe.skip('expert', () => { * * But did it calculate 'GOOGL' again too? */ - expect(stockPrice$).toHaveBeenCalledTimes(__YOUR_TURN__); - expect(reactSpy).toHaveBeenCalledTimes(__YOUR_TURN__); + expect(stockPrice$).toHaveBeenCalledTimes(2); + expect(reactSpy).toHaveBeenCalledTimes(3); // The first should be the generated HTML for 'GOOGL'. - expect(lastEmittedHTMLs()[0]).toContain(__YOUR_TURN__); + expect(lastEmittedHTMLs()[0]).toContain('1079.11'); // The second should be the generated HTML for 'APPL'. - expect(lastEmittedHTMLs()[1]).toContain(__YOUR_TURN__); + expect(lastEmittedHTMLs()[1]).toContain('unknown'); }); }); });