Repository: ViacomInc/data-point
Pull request #382 (2019-08-21 15:39:13)

refactor(data-point): add ReducerList tests
<!--
Thanks for your interest in the project. We appreciate bugs filed and PRs submitted!

Please make sure that you are familiar with and follow the Code of Conduct for
this project (found in the CODE_OF_CONDUCT.md file).

Also, please make sure you're familiar with and follow the instructions in the
contributing guidelines (found in the CONTRIBUTING.md file).

Please fill out the information below to expedite the review and (hopefully)
merge of your pull request!

BREAKING CHANGES:

If your PR includes a breaking change, please submit it with a codemod under
data-point-codemods that will help users upgrade their codebase.

Breaking changes without a codemod will not be accepted unless a codemod is not
viable or does not apply to the specific situation.
-->

<!-- What changes are being made? (What feature/bug is being fixed here?) -->
**What**:

Adds tests to ReducerList Module

re  #357

<!-- Have you done all of these things?  -->
**Checklist**:
<!-- add "N/A" to the end of each line that's irrelevant to your changes -->
<!-- to check an item, place an "x" in the box like so: "- [x] Documentation" -->
- [ ] Has Breaking changes
- [ ] Documentation
- [x] Tests
- [x] Ready to be merged <!-- In your opinion, is this ready to be merged as soon as it's reviewed? -->
- [ ] Added username to **all-contributors** list

<!-- feel free to add additional comments -->

codecov[bot]: # [Codecov](https://codecov.io/gh/ViacomInc/data-point/pull/382?src=pr&el=h1) Report
> Merging [#382](https://codecov.io/gh/ViacomInc/data-point/pull/382?src=pr&el=desc) into [canary](https://codecov.io/gh/ViacomInc/data-point/commit/0d46d7c2042c0c3dde71cea5fd72828806ff0f27?src=pr&el=desc) will **decrease** coverage by `0.4%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/ViacomInc/data-point/pull/382/graphs/tree.svg?width=650&token=LYxItnrx4i&height=150&src=pr)](https://codecov.io/gh/ViacomInc/data-point/pull/382?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           canary     #382      +/-   ##
==========================================
- Coverage     100%   99.59%   -0.41%     
==========================================
  Files          11       13       +2     
  Lines         226      249      +23     
  Branches       35       37       +2     
==========================================
+ Hits          226      248      +22     
- Misses          0        1       +1
```


| [Impacted Files](https://codecov.io/gh/ViacomInc/data-point/pull/382?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [packages/data-point/src/ReducerList.js](https://codecov.io/gh/ViacomInc/data-point/pull/382/diff?src=pr&el=tree#diff-cGFja2FnZXMvZGF0YS1wb2ludC9zcmMvUmVkdWNlckxpc3QuanM=) | `100% <100%> (ø)` | |
| [packages/data-point/src/ReducerNative.js](https://codecov.io/gh/ViacomInc/data-point/pull/382/diff?src=pr&el=tree#diff-cGFja2FnZXMvZGF0YS1wb2ludC9zcmMvUmVkdWNlck5hdGl2ZS5qcw==) | `66.66% <0%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/ViacomInc/data-point/pull/382?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/ViacomInc/data-point/pull/382?src=pr&el=footer). Last update [0d46d7c...d0dac3f](https://codecov.io/gh/ViacomInc/data-point/pull/382?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/packages/data-point/src/ReducerList.js b/packages/data-point/src/ReducerList.js
index d904406..b3a27d5 100644
--- a/packages/data-point/src/ReducerList.js
+++ b/packages/data-point/src/ReducerList.js
@@ -1,10 +1,10 @@
-const { ReducerNative } = require("./ReducerNative");
+const { Reducer } = require("./Reducer");
 
-class ReducerList extends ReducerNative {
+class ReducerList extends Reducer {
   constructor(spec, createReducer) {
     super("list", undefined, spec);
 
-    this.reducerList = spec.map(token => createReducer(token));
+    this.reducerList = spec.map(reducerSource => createReducer(reducerSource));
   }
 
   static isType(spec) {
@@ -12,30 +12,28 @@ class ReducerList extends ReducerNative {
   }
 
   async resolve(accumulator, resolveReducer) {
-    const reducers = this.reducerList;
+    const reducerList = this.reducerList;
 
-    if (reducers.length === 0) {
+    if (reducerList.length === 0) {
       return undefined;
     }
 
-    const initialValue =
-      accumulator.value === undefined ? null : accumulator.value;
+    let value = accumulator.value;
+    let acc = accumulator;
 
-    let index = 0;
+    const reducerMaxIndex = reducerList.length - 1;
 
-    let value = initialValue;
-
-    while (index < reducers.length) {
-      const reducer = reducers[index];
-
-      const acc = Object.create(accumulator);
-      acc.value = value;
+    for (let index = 0; index <= reducerMaxIndex; index += 1) {
+      const reducer = reducerList[index];
 
       // we do purposely want to wait for each reducer to execute
       // eslint-disable-next-line no-await-in-loop
       value = await resolveReducer(acc, reducer);
 
-      index += 1;
+      // only create new accumulator if there are more reducers to process
+      if (index < reducerMaxIndex) {
+        acc = acc.set("value", value);
+      }
     }
 
     return value;


Test patch:
diff --git a/packages/data-point/src/ReducerList.test.js b/packages/data-point/src/ReducerList.test.js
new file mode 100644
index 0000000..e35dd84
--- /dev/null
+++ b/packages/data-point/src/ReducerList.test.js
@@ -0,0 +1,101 @@
+const { ReducerList } = require("./ReducerList");
+const { Accumulator } = require("./Accumulator");
+
+const mockCreateReducer = jest.fn(spec => {
+  return (value, acc) => spec(value, acc);
+});
+
+const mockResolveReducer = jest.fn((acc, reducer) => {
+  return reducer(acc.value, acc);
+});
+
+const reducer1 = val => `${val}-reducer1`;
+const reducer2 = val => `${val}-reducer2`;
+const reducer3 = val => `${val}-reducer3`;
+
+afterEach(() => {
+  jest.clearAllMocks();
+});
+
+describe("ReducerList", () => {
+  describe("constructor", () => {
+    const overSimplifiedCreateReducer = source => source;
+
+    it("should set type to list", () => {
+      const reducer = new ReducerList([], overSimplifiedCreateReducer);
+      expect(reducer.type).toEqual("list");
+    });
+
+    it("should have no name defined", () => {
+      const reducer = new ReducerList([], overSimplifiedCreateReducer);
+      expect(reducer.name).toEqual(undefined);
+    });
+
+    it("should set reducerList as a parsed set of reducers", () => {
+      const reducer = new ReducerList([reducer1], overSimplifiedCreateReducer);
+      expect(reducer.reducerList).toEqual([reducer1]);
+    });
+  });
+
+  describe("isType", () => {
+    it("should return true if source is array, false for non", () => {
+      // kind of silly these tests since we are using native Array.isArray
+      expect(ReducerList.isType([])).toEqual(true);
+      expect(ReducerList.isType({})).toEqual(false);
+      expect(ReducerList.isType("notArray")).toEqual(false);
+    });
+  });
+
+  describe("resolve", () => {
+    it("should resolve to undefined if no reducers provided", async () => {
+      const reducer = new ReducerList([], mockCreateReducer);
+      const acc = new Accumulator();
+
+      expect(await reducer.resolve(acc)).toEqual(undefined);
+    });
+
+    it("should resolve single reducer", async () => {
+      const reducer = new ReducerList([reducer1], mockCreateReducer);
+      const acc = new Accumulator({
+        value: "value"
+      });
+
+      expect(await reducer.resolve(acc, mockResolveReducer)).toEqual(
+        "value-reducer1"
+      );
+
+      // should be called with original accumulator
+      expect(mockResolveReducer).toBeCalledWith(acc, reducer.reducerList[0]);
+    });
+
+    it("should resolve multiple reducers", async () => {
+      const reducer = new ReducerList(
+        [reducer1, reducer2, reducer3],
+        mockCreateReducer
+      );
+      const acc = new Accumulator({
+        value: "value"
+      });
+
+      const spySet = jest.spyOn(acc, "set");
+
+      expect(await reducer.resolve(acc, mockResolveReducer)).toEqual(
+        "value-reducer1-reducer2-reducer3"
+      );
+
+      // should be called with original accumulator
+      expect(mockResolveReducer).toBeCalledTimes(3);
+      // get information from second call to mockResolveReducer
+      const [arg1] = mockResolveReducer.mock.calls[1];
+      // accumulator is a copy, not original
+      expect(arg1).not.toEqual(acc);
+      // new accumulator.value must be updated
+      expect(arg1).toMatchObject({
+        value: "value-reducer1"
+      });
+
+      // should only clone object when necessary
+      expect(spySet).toBeCalledTimes(2);
+    });
+  });
+});