Repository: ViacomInc/data-point (js)

Issue:
Falsy values are treated as reducers
**Problem description**:

Falsy values are treated as valid reducers.

```js
const input = [{ a: 1 }]

dataPoint.resolve(0, input) // => [{ a: 1 }]
dataPoint.resolve('', input) // => [{ a: 1 }]
dataPoint.resolve(false, input) // => [{ a: 1 }]
dataPoint.resolve(undefined, input) // => [{ a: 1 }]
```

**Suggested solution**:

The return value of `ReducerList#parse` should be changed to this:

```js
return parseFromArray(_.castArray(source))
```

https://github.com/ViacomInc/data-point/blob/master/packages/data-point/lib/reducer-types/reducer-list/factory.js#L72

We might also need to remove this `_.compact`:

https://github.com/ViacomInc/data-point/blob/master/packages/data-point/lib/reducer-types/reducer-list/factory.js#L59

Pull request description:
fix(reducer-list): ReducerList factory now throws an error for falsy input
BREAKING CHANGE: Anything relying on putting a falsy value into ReducerList will now
throw an error

fix #188

<!--
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.
-->

<!-- feel free to add additional comments -->


Patch:
diff --git a/packages/data-point/lib/reducer-types/reducer-helpers/reducer-stub.js b/packages/data-point/lib/reducer-types/reducer-helpers/reducer-stub.js
index 49cebca..397d300 100644
--- a/packages/data-point/lib/reducer-types/reducer-helpers/reducer-stub.js
+++ b/packages/data-point/lib/reducer-types/reducer-helpers/reducer-stub.js
@@ -20,7 +20,7 @@ module.exports.Constructor = ReducerStub
  * @returns {boolean}
  */
 function isType (source) {
-  return source[REDUCER_STUB_SYMBOL] === true
+  return !!source && source[REDUCER_STUB_SYMBOL] === true
 }
 
 module.exports.isType = isType
diff --git a/packages/data-point/lib/reducer-types/reducer-list/factory.js b/packages/data-point/lib/reducer-types/reducer-list/factory.js
index fbcd3eb..9e1954a 100644
--- a/packages/data-point/lib/reducer-types/reducer-list/factory.js
+++ b/packages/data-point/lib/reducer-types/reducer-list/factory.js
@@ -33,7 +33,7 @@ module.exports.isType = isType
  */
 function parseFromString (source) {
   const reducerSource = _.defaultTo(source, '')
-  const tokens = _.compact(reducerSource.split(' | '))
+  const tokens = reducerSource.split(' | ')
   return tokens
 }
 
@@ -55,7 +55,6 @@ module.exports.parseTokenExpression = parseTokenExpression
  */
 function parseFromArray (source) {
   return _.chain(source)
-    .compact()
     .map(parseTokenExpression)
     .flatten()
     .value()
@@ -68,7 +67,7 @@ module.exports.parseFromArray = parseFromArray
  * @returns {Array}
  */
 function parse (source) {
-  return parseFromArray(source ? _.castArray(source) : [])
+  return parseFromArray(_.castArray(source))
 }
 
 module.exports.parse = parse


Test patch:
diff --git a/packages/data-point/lib/reducer-types/reducer-list/__snapshots__/factory.test.js.snap b/packages/data-point/lib/reducer-types/reducer-list/__snapshots__/factory.test.js.snap
new file mode 100644
index 0000000..4cf1782
--- /dev/null
+++ b/packages/data-point/lib/reducer-types/reducer-list/__snapshots__/factory.test.js.snap
@@ -0,0 +1,49 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`factory#create should throw error if one of two reducers is false 1`] = `
+"Invalid reducer type. Could not find a matching reducer type while parsing the value:
+ false
+Try using an Array, String, Object, or Function.
+More info: https://github.com/ViacomInc/data-point/tree/master/packages/data-point#reducers
+"
+`;
+
+exports[`factory#create should throw error if reducer is empty string 1`] = `
+"Invalid reducer type. Could not find a matching reducer type while parsing the value:
+ ''
+Try using an Array, String, Object, or Function.
+More info: https://github.com/ViacomInc/data-point/tree/master/packages/data-point#reducers
+"
+`;
+
+exports[`factory#create should throw error if reducer is false 1`] = `
+"Invalid reducer type. Could not find a matching reducer type while parsing the value:
+ false
+Try using an Array, String, Object, or Function.
+More info: https://github.com/ViacomInc/data-point/tree/master/packages/data-point#reducers
+"
+`;
+
+exports[`factory#create should throw error if reducer is non-empty string with no matching reducer 1`] = `
+"Invalid reducer type. Could not find a matching reducer type while parsing the value:
+ 'asdf'
+Try using an Array, String, Object, or Function.
+More info: https://github.com/ViacomInc/data-point/tree/master/packages/data-point#reducers
+"
+`;
+
+exports[`factory#create should throw error if reducer is undefined 1`] = `
+"Invalid reducer type. Could not find a matching reducer type while parsing the value:
+ undefined
+Try using an Array, String, Object, or Function.
+More info: https://github.com/ViacomInc/data-point/tree/master/packages/data-point#reducers
+"
+`;
+
+exports[`factory#create should throw error if reducer is zero 1`] = `
+"Invalid reducer type. Could not find a matching reducer type while parsing the value:
+ 0
+Try using an Array, String, Object, or Function.
+More info: https://github.com/ViacomInc/data-point/tree/master/packages/data-point#reducers
+"
+`;
diff --git a/packages/data-point/lib/reducer-types/reducer-list/factory.test.js b/packages/data-point/lib/reducer-types/reducer-list/factory.test.js
index 12e3952..0e682e0 100644
--- a/packages/data-point/lib/reducer-types/reducer-list/factory.test.js
+++ b/packages/data-point/lib/reducer-types/reducer-list/factory.test.js
@@ -5,14 +5,6 @@ const factory = require('./factory')
 const createReducer = require('../index').create
 
 describe('factory#create', () => {
-  test('factory#create default', () => {
-    const result = factory.create(createReducer)
-
-    expect(result).toBeInstanceOf(factory.Constructor)
-    expect(result.context).toBeUndefined()
-    expect(result.reducers).toHaveLength(0)
-  })
-
   test('factory#create only path', () => {
     const result = factory.create(createReducer, '$foo.bar')
     expect(result.reducers).toHaveLength(1)
@@ -105,4 +97,40 @@ describe('factory#create', () => {
     expect(result.reducers[2].type).toBe('ReducerPath')
     expect(result.reducers[3].type).toBe('ReducerFunction')
   })
+
+  test('should throw error if reducer is false', () => {
+    expect(() =>
+      factory.create(createReducer, [false])
+    ).toThrowErrorMatchingSnapshot()
+  })
+
+  test('should throw error if reducer is empty string', () => {
+    expect(() =>
+      factory.create(createReducer, '')
+    ).toThrowErrorMatchingSnapshot()
+  })
+
+  test('should throw error if reducer is non-empty string with no matching reducer', () => {
+    expect(() =>
+      factory.create(createReducer, 'asdf')
+    ).toThrowErrorMatchingSnapshot()
+  })
+
+  test('should throw error if one of two reducers is false', () => {
+    expect(() =>
+      factory.create(createReducer, ['$foo.bar', false])
+    ).toThrowErrorMatchingSnapshot()
+  })
+
+  test('should throw error if reducer is undefined', () => {
+    expect(() =>
+      factory.create(createReducer, [undefined])
+    ).toThrowErrorMatchingSnapshot()
+  })
+
+  test('should throw error if reducer is zero', () => {
+    expect(() =>
+      factory.create(createReducer, [0])
+    ).toThrowErrorMatchingSnapshot()
+  })
 })