Repository: char0n/ramda-adjunct
Pull request #1167 (2019-11-10 09:54:43)

Update conventional-changelog-cli to the latest version 🚀

## The devDependency [conventional-changelog-cli](https://github.com/conventional-changelog/conventional-changelog) was updated from `2.0.25` to `2.0.27`.
This version is **not covered** by your **current version range**.

If you don’t accept this pull request, your project will work just like it did before. However, you might be missing out on a bunch of new features, fixes and/or performance improvements from the dependency update.

---

**Publisher:** [tommywo](https://www.npmjs.com/~tommywo)
**License:** MIT

[Find out more about this release](https://github.com/conventional-changelog/conventional-changelog).

---

<details>
  <summary>FAQ and help</summary>

  There is a collection of [frequently asked questions](https://greenkeeper.io/faq.html). If those don’t help, you can always [ask the humans behind Greenkeeper](https://github.com/greenkeeperio/greenkeeper/issues/new).
</details>

---


Your [Greenkeeper](https://greenkeeper.io) bot :palm_tree:




Patch:
diff --git a/package-lock.json b/package-lock.json
index 99dac76ddd..ddbb483e4f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3639,28 +3639,28 @@
       "dev": true
     },
     "conventional-changelog": {
-      "version": "3.1.12",
-      "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.12.tgz",
-      "integrity": "sha512-zyGKwii8Z5zOq1nGFm5jn9Ou1jQ6UBoRT0+nqBIU8fEzh64+AcVxrY97tVuK77Ati0xwpBiFHpDXAW7pkq1jEw==",
+      "version": "3.1.14",
+      "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.14.tgz",
+      "integrity": "sha512-utfeyIrLKBjIhxFW64W425tVjnu/rqpVrI4/ylje2figIEfGHQ1YoMkkT8ZwX65MxzTtqQR1SCl7CX1y0/0JrA==",
       "dev": true,
       "requires": {
-        "conventional-changelog-angular": "^5.0.5",
+        "conventional-changelog-angular": "^5.0.6",
         "conventional-changelog-atom": "^2.0.3",
         "conventional-changelog-codemirror": "^2.0.3",
-        "conventional-changelog-conventionalcommits": "^4.2.1",
-        "conventional-changelog-core": "^4.0.2",
+        "conventional-changelog-conventionalcommits": "^4.2.3",
+        "conventional-changelog-core": "^4.1.0",
         "conventional-changelog-ember": "^2.0.4",
         "conventional-changelog-eslint": "^3.0.4",
         "conventional-changelog-express": "^2.0.1",
         "conventional-changelog-jquery": "^3.0.6",
         "conventional-changelog-jshint": "^2.0.3",
-        "conventional-changelog-preset-loader": "^2.2.0"
+        "conventional-changelog-preset-loader": "^2.3.0"
       },
       "dependencies": {
         "conventional-changelog-angular": {
-          "version": "5.0.5",
-          "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.5.tgz",
-          "integrity": "sha512-RrkdWnL/TVyWV1ayWmSsrWorsTDqjL/VwG5ZSEneBQrd65ONcfeA1cW7FLtNweQyMiKOyriCMTKRSlk18DjTrw==",
+          "version": "5.0.6",
+          "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.6.tgz",
+          "integrity": "sha512-QDEmLa+7qdhVIv8sFZfVxU1VSyVvnXPsxq8Vam49mKUcO1Z8VTLEJk9uI21uiJUsnmm0I4Hrsdc9TgkOQo9WSA==",
           "dev": true,
           "requires": {
             "compare-func": "^1.3.1",
@@ -3689,18 +3689,24 @@
       }
     },
     "conventional-changelog-cli": {
-      "version": "2.0.25",
-      "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-2.0.25.tgz",
-      "integrity": "sha512-b0zNygHrFI4HjNmgPzDLSqoltIwo3pIfilWZFNH/1w6QIZoZHeA4SxojRgih9aCc0Eeg1RH7lXIPaRgHa9cv7A==",
+      "version": "2.0.27",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-2.0.27.tgz",
+      "integrity": "sha512-SK71zMVyAlfYua44WFCLRZqWO76He57NLsp9wfTAsHkCgbrWtWGbXA/UuGq49H5lAlWMcaqYR6OYcssqmFw6aQ==",
       "dev": true,
       "requires": {
         "add-stream": "^1.0.0",
-        "conventional-changelog": "^3.1.12",
-        "lodash": "^4.14.14",
+        "conventional-changelog": "^3.1.14",
+        "lodash": "^4.17.15",
         "meow": "^4.0.0",
         "tempfile": "^3.0.0"
       },
       "dependencies": {
+        "lodash": {
+          "version": "4.17.15",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+          "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+          "dev": true
+        },
         "meow": {
           "version": "4.0.1",
           "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz",
@@ -3736,30 +3742,38 @@
       }
     },
     "conventional-changelog-conventionalcommits": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.2.1.tgz",
-      "integrity": "sha512-vC02KucnkNNap+foDKFm7BVUSDAXktXrUJqGszUuYnt6T0J2azsbYz/w9TDc3VsrW2v6JOtiQWVcgZnporHr4Q==",
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.2.3.tgz",
+      "integrity": "sha512-atGa+R4vvEhb8N/8v3IoW59gCBJeeFiX6uIbPu876ENAmkMwsenyn0R21kdDHJFLQdy6zW4J6b4xN8KI3b9oww==",
       "dev": true,
       "requires": {
         "compare-func": "^1.3.1",
-        "lodash": "^4.2.1",
+        "lodash": "^4.17.15",
         "q": "^1.5.1"
+      },
+      "dependencies": {
+        "lodash": {
+          "version": "4.17.15",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+          "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+          "dev": true
+        }
       }
     },
     "conventional-changelog-core": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.0.2.tgz",
-      "integrity": "sha512-vQh7J+emZlcIA5alvI2xGikID2/iYKyk39dHmHEyU7/xvB9L9kq+3BzqbCXVstu7SD7isDfem27m/Qzu7R0BRA==",
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.1.0.tgz",
+      "integrity": "sha512-2m2udIRAPbQL5It0ABNjsQZKJQJPNuf3anxLhcOLkEW+M4z3C343BMDUyJy9Y7tKAweqK458xxW81bG8FYrBwA==",
       "dev": true,
       "requires": {
-        "conventional-changelog-writer": "^4.0.9",
-        "conventional-commits-parser": "^3.0.5",
+        "conventional-changelog-writer": "^4.0.10",
+        "conventional-commits-parser": "^3.0.7",
         "dateformat": "^3.0.0",
         "get-pkg-repo": "^1.0.0",
         "git-raw-commits": "2.0.0",
         "git-remote-origin-url": "^2.0.0",
         "git-semver-tags": "^3.0.0",
-        "lodash": "^4.2.1",
+        "lodash": "^4.17.15",
         "normalize-package-data": "^2.3.5",
         "q": "^1.5.1",
         "read-pkg": "^3.0.0",
@@ -3768,14 +3782,14 @@
       },
       "dependencies": {
         "conventional-commits-parser": {
-          "version": "3.0.5",
-          "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.5.tgz",
-          "integrity": "sha512-qVz9+5JwdJzsbt7JbJ6P7NOXBGt8CyLFJYSjKAuPSgO+5UGfcsbk9EMR+lI8Unlvx6qwIc2YDJlrGIfay2ehNA==",
+          "version": "3.0.7",
+          "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.7.tgz",
+          "integrity": "sha512-4mx/FRC92z0yIiXGyRVYQFhn0jWDwvxnj2UuLaUi3hJSG4Thall6GXA8YOPHQK2qvotciJandJIVmuSvLgDLbQ==",
           "dev": true,
           "requires": {
             "JSONStream": "^1.0.4",
-            "is-text-path": "^2.0.0",
-            "lodash": "^4.2.1",
+            "is-text-path": "^1.0.1",
+            "lodash": "^4.17.15",
             "meow": "^4.0.0",
             "split2": "^2.0.0",
             "through2": "^3.0.0",
@@ -3807,14 +3821,11 @@
             }
           }
         },
-        "is-text-path": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz",
-          "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==",
-          "dev": true,
-          "requires": {
-            "text-extensions": "^2.0.0"
-          }
+        "lodash": {
+          "version": "4.17.15",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+          "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+          "dev": true
         },
         "meow": {
           "version": "4.0.1",
@@ -3839,12 +3850,6 @@
           "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
           "dev": true
         },
-        "text-extensions": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.0.0.tgz",
-          "integrity": "sha512-F91ZqLgvi1E0PdvmxMgp+gcf6q8fMH7mhdwWfzXnl1k+GbpQDmi8l7DzLC5JTASKbwpY3TfxajAUzAXcv2NmsQ==",
-          "dev": true
-        },
         "through2": {
           "version": "3.0.1",
           "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz",
@@ -3903,15 +3908,15 @@
       }
     },
     "conventional-changelog-preset-loader": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.2.0.tgz",
-      "integrity": "sha512-zXB+5vF7D5Y3Cb/rJfSyCCvFphCVmF8mFqOdncX3BmjZwAtGAPfYrBcT225udilCKvBbHgyzgxqz2GWDB5xShQ==",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.0.tgz",
+      "integrity": "sha512-/rHb32J2EJnEXeK4NpDgMaAVTFZS3o1ExmjKMtYVgIC4MQn0vkNSbYpdGRotkfGGRWiqk3Ri3FBkiZGbAfIfOQ==",
       "dev": true
     },
     "conventional-changelog-writer": {
-      "version": "4.0.9",
-      "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.9.tgz",
-      "integrity": "sha512-2Y3QfiAM37WvDMjkVNaRtZgxVzWKj73HE61YQ/95T53yle+CRwTVSl6Gbv/lWVKXeZcM5af9n9TDVf0k7Xh+cw==",
+      "version": "4.0.10",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.10.tgz",
+      "integrity": "sha512-vtO9vBAVh7XnSpGLTB1BOGgsGTz1MdvFjzbSXLrtapWCHWwuVOZFgwdLhlS0MaXwlF1dksWdEb6tnr42Ie2INw==",
       "dev": true,
       "requires": {
         "compare-func": "^1.3.1",
@@ -3919,24 +3924,18 @@
         "dateformat": "^3.0.0",
         "handlebars": "^4.4.0",
         "json-stringify-safe": "^5.0.1",
-        "lodash": "^4.2.1",
+        "lodash": "^4.17.15",
         "meow": "^4.0.0",
         "semver": "^6.0.0",
         "split": "^1.0.0",
         "through2": "^3.0.0"
       },
       "dependencies": {
-        "handlebars": {
-          "version": "4.5.1",
-          "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz",
-          "integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==",
-          "dev": true,
-          "requires": {
-            "neo-async": "^2.6.0",
-            "optimist": "^0.6.1",
-            "source-map": "^0.6.1",
-            "uglify-js": "^3.1.4"
-          }
+        "lodash": {
+          "version": "4.17.15",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+          "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+          "dev": true
         },
         "meow": {
           "version": "4.0.1",
@@ -3967,12 +3966,6 @@
           "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
           "dev": true
         },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        },
         "through2": {
           "version": "3.0.1",
           "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz",
@@ -6813,7 +6806,7 @@
         },
         "camelcase-keys": {
           "version": "2.1.0",
-          "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+          "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
           "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
           "dev": true,
           "requires": {
@@ -6848,7 +6841,7 @@
         },
         "load-json-file": {
           "version": "1.1.0",
-          "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
           "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
           "dev": true,
           "requires": {
@@ -6867,7 +6860,7 @@
         },
         "meow": {
           "version": "3.7.0",
-          "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+          "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
           "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
           "dev": true,
           "requires": {
@@ -6920,7 +6913,7 @@
         },
         "pify": {
           "version": "2.3.0",
-          "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
           "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
           "dev": true
         },
@@ -7061,7 +7054,7 @@
       "dependencies": {
         "pify": {
           "version": "2.3.0",
-          "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
           "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
           "dev": true
         }
diff --git a/package.json b/package.json
index ed6c470d9d..68165a33b1 100644
--- a/package.json
+++ b/package.json
@@ -146,7 +146,7 @@
     "better-npm-run": "0.1.1",
     "chai": "4.2.0",
     "codecov": "3.6.1",
-    "conventional-changelog-cli": "2.0.25",
+    "conventional-changelog-cli": "2.0.27",
     "docdash": "git+https://github.com/char0n/docdash.git#534b44382138a55dd8d93642c979e51e46471185",
     "dtslint": "2.0.0",
     "eslint": "6.6.0",
diff --git a/src/index.js b/src/index.js
index 930713f11b..336d88a5ca 100644
--- a/src/index.js
+++ b/src/index.js
@@ -110,6 +110,7 @@ export { default as dispatch } from './dispatch';
 export { default as async } from './async';
 export { default as anyP } from './anyP';
 export { default as firstP } from './anyP'; // alias of anyP
+export { default as lastP } from './lastP';
 // List
 export { default as mapIndexed } from './mapIndexed';
 export { default as reduceIndexed } from './reduceIndexed';
diff --git a/src/lastP.js b/src/lastP.js
new file mode 100644
index 0000000000..eb17b71d0f
--- /dev/null
+++ b/src/lastP.js
@@ -0,0 +1,58 @@
+import { bind, last, map } from 'ramda';
+import curry1 from 'ramda/src/internal/_curry1';
+
+import allP from './allP';
+import lengthEq from './lengthEq';
+import lengthGte from './lengthGte';
+import rejectP from './rejectP';
+import resolveP from './resolveP';
+
+/**
+ * Returns a promise that is fulfilled by the last given promise to be fulfilled,
+ * or rejected with an array of rejection reasons if all of the given promises are rejected.
+ *
+ * @func lastP
+ * @memberOf RA
+ * @category Function
+ * @since {@link https://char0n.github.io/ramda-adjunct/2.23.0|v2.23.0}
+ * @sig [Promise a] -> Promise a
+ * @param {Iterable.<*>} iterable An iterable object such as an Array or String
+ * @return {Promise} A promise that is fulfilled by the last given promise to be fulfilled, or rejected with an array of rejection reasons if all of the given promises are rejected.
+ * @see {@link RA.anyP|anyP}
+ * @example
+ *
+ * const delayP = timeout => new Promise(resolve => setTimeout(() => resolve(timeout), timeout));
+ * delayP.reject = timeout => new Promise((resolve, reject) => setTimeout(() => reject(timeout), timeout));
+ * RA.lastP([
+ *   1,
+ *   delayP(10),
+ *   delayP(100),
+ *   delayP.reject(1000),
+ * ]); //=> Promise(100)
+ */
+const lastP = curry1(iterable => {
+  const fulfilled = [];
+  const rejected = [];
+  const onFulfill = bind(fulfilled.push, fulfilled);
+  const onReject = bind(rejected.push, rejected);
+
+  const listOfPromises = map(
+    p =>
+      resolveP(p)
+        .then(onFulfill)
+        .catch(onReject),
+    [...iterable]
+  );
+
+  return allP(listOfPromises).then(() => {
+    if (lengthEq(0, fulfilled) && lengthEq(0, rejected)) {
+      return undefined;
+    }
+    if (lengthGte(1, fulfilled)) {
+      return last(fulfilled);
+    }
+    return rejectP(rejected);
+  });
+});
+
+export default lastP;
diff --git a/types/index.d.ts b/types/index.d.ts
index 838fab8933..cc14e9be9a 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -867,6 +867,12 @@ declare namespace RamdaAdjunct {
         anyP<T>(iterable: Iterable<T>): Promise<T>;
         firstP<T>(iterable: Iterable<T>): Promise<T>; // alias
 
+        /**
+         * Returns a promise that is fulfilled by the last given promise to be fulfilled,
+         * or rejected with an array of rejection reasons if all of the given promises are rejected.
+         */
+        lastP<T>(iterable: Iterable<T>): Promise<T>;
+
         /**
          * Composable shortcut for `Promise.resolve`.
          *


Test patch:
diff --git a/test/isNotPlainObj.js b/test/isNotPlainObj.js
index 03d46287ba..c7e8c6178e 100644
--- a/test/isNotPlainObj.js
+++ b/test/isNotPlainObj.js
@@ -1,5 +1,6 @@
+import { assert } from 'chai';
+
 import * as RA from '../src';
-import eq from './shared/eq';
 import element from './shared/element';
 import args from './shared/arguments';
 import Symbol from './shared/Symbol';
@@ -12,53 +13,53 @@ class Bar {
 
 describe('isNotPlainObj', function() {
   it('tests a value for POJO', function() {
-    eq(RA.isNotPlainObj({}), false);
-    eq(RA.isNotPlainObj({ prop: 'value' }), false);
-    eq(RA.isNotPlainObj({ constructor: Bar }), false);
-    eq(RA.isNotPlainObj(new Bar()), true);
-    eq(RA.isNotPlainObj(['a', 'b', 'c']), true);
+    assert.isFalse(RA.isNotPlainObj({}));
+    assert.isFalse(RA.isNotPlainObj({ prop: 'value' }));
+    assert.isFalse(RA.isNotPlainObj({ constructor: Bar }));
+    assert.isTrue(RA.isNotPlainObj(new Bar()));
+    assert.isTrue(RA.isNotPlainObj(['a', 'b', 'c']));
   });
 
   it('tests a value with prototype of null', function() {
-    eq(RA.isNotPlainObj(Object.create(null)), false);
+    assert.isFalse(RA.isNotPlainObj(Object.create(null)));
 
     const object = Object.create(null);
     object.constructor = Object.prototype.constructor;
 
-    eq(RA.isNotPlainObj(object), false);
+    assert.isFalse(RA.isNotPlainObj(object));
   });
 
   it('tests a value with `valueOf` property', function() {
-    eq(RA.isNotPlainObj({ valueOf: 1 }), false);
+    assert.isFalse(RA.isNotPlainObj({ valueOf: 1 }));
   });
 
   it('test a value with custom prototype', function() {
-    eq(RA.isNotPlainObj(Object.create({ a: 3 })), false);
+    assert.isFalse(RA.isNotPlainObj(Object.create({ a: 3 })));
   });
 
   it('test a value that is DOM element', function() {
     if (element) {
-      eq(RA.isNotPlainObj(element), true);
+      assert.isTrue(RA.isNotPlainObj(element));
     }
   });
 
   it('test a value for non-objects', function() {
-    eq(RA.isNotPlainObj(args), true);
-    eq(RA.isNotPlainObj(Error), true);
-    eq(RA.isNotPlainObj(Math), true);
-    eq(RA.isNotPlainObj(true), true);
-    eq(RA.isNotPlainObj('abc'), true);
+    assert.isTrue(RA.isNotPlainObj(args));
+    assert.isTrue(RA.isNotPlainObj(Error));
+    assert.isTrue(RA.isNotPlainObj(Math));
+    assert.isTrue(RA.isNotPlainObj(true));
+    assert.isTrue(RA.isNotPlainObj('abc'));
   });
 
   it('test a value for Symbol', function() {
     if (Symbol) {
-      eq(RA.isNotPlainObj(Symbol.for('symbol')), true);
+      assert.isTrue(RA.isNotPlainObj(Symbol.for('symbol')));
     }
   });
 });
 
 describe('isNotPlainObject', function() {
   it('tests an alias', function() {
-    eq(RA.isNotPlainObj === RA.isNotPlainObject, true);
+    assert.strictEqual(RA.isNotPlainObj, RA.isNotPlainObject);
   });
 });
diff --git a/test/isNotRegExp.js b/test/isNotRegExp.js
index 930bcc2c2c..b28a59c286 100644
--- a/test/isNotRegExp.js
+++ b/test/isNotRegExp.js
@@ -1,32 +1,33 @@
+import { assert } from 'chai';
+
 import * as RA from '../src';
-import eq from './shared/eq';
 import args from './shared/arguments';
 import Symbol from './shared/Symbol';
 
 describe('isNotRegExp', function() {
   it('tests a value for complement of `RegExp`', function() {
-    eq(RA.isNotRegExp('a'), true);
-    eq(RA.isNotRegExp(1), true);
-    eq(RA.isNotRegExp([]), true);
-    eq(RA.isNotRegExp({}), true);
-    eq(RA.isNotRegExp(true), true);
-    eq(RA.isNotRegExp(false), true);
-    eq(RA.isNotRegExp(new Error()), true);
-    eq(RA.isNotRegExp(new Date()), true);
-    eq(RA.isNotRegExp(RA), true);
-    eq(RA.isNotRegExp(function() {}), true);
-    eq(RA.isNotRegExp(Object(0)), true);
-    eq(RA.isNotRegExp(Object('a')), true);
-    eq(RA.isNotRegExp(args), true);
+    assert.isTrue(RA.isNotRegExp('a'));
+    assert.isTrue(RA.isNotRegExp(1));
+    assert.isTrue(RA.isNotRegExp([]));
+    assert.isTrue(RA.isNotRegExp({}));
+    assert.isTrue(RA.isNotRegExp(true));
+    assert.isTrue(RA.isNotRegExp(false));
+    assert.isTrue(RA.isNotRegExp(new Error()));
+    assert.isTrue(RA.isNotRegExp(new Date()));
+    assert.isTrue(RA.isNotRegExp(RA));
+    assert.isTrue(RA.isNotRegExp(function() {}));
+    assert.isTrue(RA.isNotRegExp(Object(0)));
+    assert.isTrue(RA.isNotRegExp(Object('a')));
+    assert.isTrue(RA.isNotRegExp(args));
 
     if (Symbol !== 'undefined') {
-      eq(RA.isNotRegExp(Symbol), true);
+      assert.isTrue(RA.isNotRegExp(Symbol));
     }
 
-    eq(RA.isNotRegExp(null), true);
-    eq(RA.isNotRegExp(undefined), true);
+    assert.isTrue(RA.isNotRegExp(null));
+    assert.isTrue(RA.isNotRegExp(undefined));
 
-    eq(RA.isNotRegExp(new RegExp()), false);
-    eq(RA.isNotRegExp(/(?:)/), false);
+    assert.isFalse(RA.isNotRegExp(new RegExp()));
+    assert.isFalse(RA.isNotRegExp(/(?:)/));
   });
 });
diff --git a/test/isNotString.js b/test/isNotString.js
index 2a68f117b8..27d2396d5f 100644
--- a/test/isNotString.js
+++ b/test/isNotString.js
@@ -1,22 +1,23 @@
+import { assert } from 'chai';
+
 import * as RA from '../src';
-import eq from './shared/eq';
 import Symbol from './shared/Symbol';
 import args from './shared/arguments';
 
 describe('isNotString', function() {
   it('tests a value for complement of `String`', function() {
-    eq(RA.isNotString('abc'), false);
-    eq(RA.isNotString(Object('abc')), false);
+    assert.isFalse(RA.isNotString('abc'));
+    assert.isFalse(RA.isNotString(Object('abc')));
 
-    eq(RA.isNotString(args), true);
-    eq(RA.isNotString([1, 2, 3]), true);
-    eq(RA.isNotString(true), true);
-    eq(RA.isNotString(new Date()), true);
-    eq(RA.isNotString(new Error()), true);
-    eq(RA.isNotString(Array.prototype.slice), true);
-    eq(RA.isNotString({ 0: 1, length: 1 }), true);
-    eq(RA.isNotString(1), true);
-    eq(RA.isNotString(/x/), true);
-    eq(RA.isNotString(Symbol), true);
+    assert.isTrue(RA.isNotString(args));
+    assert.isTrue(RA.isNotString([1, 2, 3]));
+    assert.isTrue(RA.isNotString(true));
+    assert.isTrue(RA.isNotString(new Date()));
+    assert.isTrue(RA.isNotString(new Error()));
+    assert.isTrue(RA.isNotString(Array.prototype.slice));
+    assert.isTrue(RA.isNotString({ 0: 1, length: 1 }));
+    assert.isTrue(RA.isNotString(1));
+    assert.isTrue(RA.isNotString(/x/));
+    assert.isTrue(RA.isNotString(Symbol));
   });
 });
diff --git a/test/isNotUndefined.js b/test/isNotUndefined.js
index 65b78f2691..4254a0570e 100644
--- a/test/isNotUndefined.js
+++ b/test/isNotUndefined.js
@@ -1,14 +1,15 @@
+import { assert } from 'chai';
+
 import * as RA from '../src';
-import eq from './shared/eq';
 
 describe('isNotUndefined', function() {
   it('tests a value for complement of `undefined`', function() {
-    eq(RA.isNotUndefined(void 0), false);
-    eq(RA.isNotUndefined(undefined), false);
-    eq(RA.isNotUndefined(null), true);
-    eq(RA.isNotUndefined([]), true);
-    eq(RA.isNotUndefined({}), true);
-    eq(RA.isNotUndefined(0), true);
-    eq(RA.isNotUndefined(''), true);
+    assert.isFalse(RA.isNotUndefined(void 0));
+    assert.isFalse(RA.isNotUndefined(undefined));
+    assert.isTrue(RA.isNotUndefined(null));
+    assert.isTrue(RA.isNotUndefined([]));
+    assert.isTrue(RA.isNotUndefined({}));
+    assert.isTrue(RA.isNotUndefined(0));
+    assert.isTrue(RA.isNotUndefined(''));
   });
 });
diff --git a/test/isNotValidDate.js b/test/isNotValidDate.js
index 80988c3ff0..c13a1316e6 100644
--- a/test/isNotValidDate.js
+++ b/test/isNotValidDate.js
@@ -1,36 +1,37 @@
+import { assert } from 'chai';
+
 import * as RA from '../src';
-import eq from './shared/eq';
 import args from './shared/arguments';
 import Symbol from './shared/Symbol';
 
 describe('isNotValidDate', function() {
   it('tests a value for complement of valid `Date`', function() {
-    eq(RA.isNotValidDate(new Date()), false);
+    assert.isFalse(RA.isNotValidDate(new Date()));
 
-    eq(RA.isNotValidDate(new Date('a')), true);
-    eq(RA.isNotValidDate(Date.now()), true);
-    eq(RA.isNotValidDate(args), true);
-    eq(RA.isNotValidDate([1, 2, 3]), true);
-    eq(RA.isNotValidDate(Object(true)), true);
-    eq(RA.isNotValidDate(new Error()), true);
-    eq(RA.isNotValidDate(RA), true);
-    eq(RA.isNotValidDate(Array.prototype.slice), true);
-    eq(RA.isNotValidDate({ a: 1 }), true);
-    eq(RA.isNotValidDate(Object(0)), true);
-    eq(RA.isNotValidDate(/x/), true);
-    eq(RA.isNotValidDate(Object('a')), true);
+    assert.isTrue(RA.isNotValidDate(new Date('a')));
+    assert.isTrue(RA.isNotValidDate(Date.now()));
+    assert.isTrue(RA.isNotValidDate(args));
+    assert.isTrue(RA.isNotValidDate([1, 2, 3]));
+    assert.isTrue(RA.isNotValidDate(Object(true)));
+    assert.isTrue(RA.isNotValidDate(new Error()));
+    assert.isTrue(RA.isNotValidDate(RA));
+    assert.isTrue(RA.isNotValidDate(Array.prototype.slice));
+    assert.isTrue(RA.isNotValidDate({ a: 1 }));
+    assert.isTrue(RA.isNotValidDate(Object(0)));
+    assert.isTrue(RA.isNotValidDate(/x/));
+    assert.isTrue(RA.isNotValidDate(Object('a')));
 
     if (Symbol !== 'undefined') {
-      eq(RA.isNotValidDate(Symbol), true);
+      assert.isTrue(RA.isNotValidDate(Symbol));
     }
 
-    eq(RA.isNotValidDate(null), true);
-    eq(RA.isNotValidDate(undefined), true);
+    assert.isTrue(RA.isNotValidDate(null));
+    assert.isTrue(RA.isNotValidDate(undefined));
   });
 });
 
 describe('isInvalidDate', function() {
   it('tests an alias', function() {
-    eq(RA.isNotValidDate === RA.isInvalidDate, true);
+    assert.strictEqual(RA.isNotValidDate, RA.isInvalidDate);
   });
 });
diff --git a/test/isNotValidNumber.js b/test/isNotValidNumber.js
index 02b5e3bb64..b81a2c9c3d 100644
--- a/test/isNotValidNumber.js
+++ b/test/isNotValidNumber.js
@@ -1,41 +1,42 @@
+import { assert } from 'chai';
+
 import * as RA from '../src';
 import MAX_SAFE_INTEGER from '../src/internal/polyfills/Number.MAX_SAFE_INTEGER';
 import MIN_SAFE_INTEGER from '../src/internal/polyfills/Number.MIN_SAFE_INTEGER';
-import eq from './shared/eq';
 import args from './shared/arguments';
 import Symbol from './shared/Symbol';
 
 describe('isNotValidNumber', function() {
   it('tests a value for complement of valid `Number`', function() {
-    eq(RA.isNotValidNumber(0), false);
-    eq(RA.isNotValidNumber(0.1), false);
-    eq(RA.isNotValidNumber(-0.1), false);
-    eq(RA.isNotValidNumber(1), false);
-    eq(RA.isNotValidNumber(-1), false);
-    eq(RA.isNotValidNumber(MAX_SAFE_INTEGER), false);
-    eq(RA.isNotValidNumber(MIN_SAFE_INTEGER), false);
-    eq(RA.isNotValidNumber(Number.MAX_VALUE), false);
-    eq(RA.isNotValidNumber(Number.MIN_VALUE), false);
+    assert.isFalse(RA.isNotValidNumber(0));
+    assert.isFalse(RA.isNotValidNumber(0.1));
+    assert.isFalse(RA.isNotValidNumber(-0.1));
+    assert.isFalse(RA.isNotValidNumber(1));
+    assert.isFalse(RA.isNotValidNumber(-1));
+    assert.isFalse(RA.isNotValidNumber(MAX_SAFE_INTEGER));
+    assert.isFalse(RA.isNotValidNumber(MIN_SAFE_INTEGER));
+    assert.isFalse(RA.isNotValidNumber(Number.MAX_VALUE));
+    assert.isFalse(RA.isNotValidNumber(Number.MIN_VALUE));
 
-    eq(RA.isNotValidNumber(NaN), true);
-    eq(RA.isNotValidNumber(Infinity), true);
-    eq(RA.isNotValidNumber(-Infinity), true);
-    eq(RA.isNotValidNumber(new Date()), true);
-    eq(RA.isNotValidNumber(args), true);
-    eq(RA.isNotValidNumber([1, 2, 3]), true);
-    eq(RA.isNotValidNumber(Object(true)), true);
-    eq(RA.isNotValidNumber(new Error()), true);
-    eq(RA.isNotValidNumber(RA), true);
-    eq(RA.isNotValidNumber(Array.prototype.slice), true);
-    eq(RA.isNotValidNumber({ a: 1 }), true);
-    eq(RA.isNotValidNumber(/x/), true);
-    eq(RA.isNotValidNumber(Object('a')), true);
+    assert.isTrue(RA.isNotValidNumber(NaN));
+    assert.isTrue(RA.isNotValidNumber(Infinity));
+    assert.isTrue(RA.isNotValidNumber(-Infinity));
+    assert.isTrue(RA.isNotValidNumber(new Date()));
+    assert.isTrue(RA.isNotValidNumber(args));
+    assert.isTrue(RA.isNotValidNumber([1, 2, 3]));
+    assert.isTrue(RA.isNotValidNumber(Object(true)));
+    assert.isTrue(RA.isNotValidNumber(new Error()));
+    assert.isTrue(RA.isNotValidNumber(RA));
+    assert.isTrue(RA.isNotValidNumber(Array.prototype.slice));
+    assert.isTrue(RA.isNotValidNumber({ a: 1 }));
+    assert.isTrue(RA.isNotValidNumber(/x/));
+    assert.isTrue(RA.isNotValidNumber(Object('a')));
 
     if (Symbol !== 'undefined') {
-      eq(RA.isNotValidNumber(Symbol), true);
+      assert.isTrue(RA.isNotValidNumber(Symbol));
     }
 
-    eq(RA.isNotValidNumber(null), true);
-    eq(RA.isNotValidNumber(undefined), true);
+    assert.isTrue(RA.isNotValidNumber(null));
+    assert.isTrue(RA.isNotValidNumber(undefined));
   });
 });
diff --git a/test/isNull.js b/test/isNull.js
index 6131d61d75..b70b09fc84 100644
--- a/test/isNull.js
+++ b/test/isNull.js
@@ -1,13 +1,14 @@
+import { assert } from 'chai';
+
 import * as RA from '../src';
-import eq from './shared/eq';
 
 describe('isNull', function() {
   it('tests a value for `null`', function() {
-    eq(RA.isNull(void 0), false);
-    eq(RA.isNull(null), true);
-    eq(RA.isNull([]), false);
-    eq(RA.isNull({}), false);
-    eq(RA.isNull(0), false);
-    eq(RA.isNull(''), false);
+    assert.isFalse(RA.isNull(void 0));
+    assert.isTrue(RA.isNull(null));
+    assert.isFalse(RA.isNull([]));
+    assert.isFalse(RA.isNull({}));
+    assert.isFalse(RA.isNull(0));
+    assert.isFalse(RA.isNull(''));
   });
 });
diff --git a/test/lastP.js b/test/lastP.js
new file mode 100644
index 0000000000..cfe057c260
--- /dev/null
+++ b/test/lastP.js
@@ -0,0 +1,113 @@
+import { assert } from 'chai';
+import * as R from 'ramda';
+
+import * as RA from '../src';
+
+describe('lastP', function() {
+  context('given list of fulfilled promises', function() {
+    specify(
+      'should return the result of the last fulfilled promise',
+      async function() {
+        const p1 = new Promise(resolve => setTimeout(resolve, 20, 'one'));
+        const p2 = new Promise(resolve => setTimeout(resolve, 40, 'two'));
+        const p3 = RA.resolveP(3);
+        const p4 = new Promise(resolve => setTimeout(resolve, 10, 'four'));
+        const iterable = [p1, p2, p3, p4];
+        const slowest = 'two';
+
+        assert.strictEqual(await RA.lastP(iterable), slowest);
+      }
+    );
+  });
+
+  context('given list of rejected promises', function() {
+    specify('should fail with a list of rejected reasons', async function() {
+      const p1 = RA.rejectP(1);
+      const p2 = RA.rejectP(2);
+      const p3 = RA.rejectP(3);
+      const iterable = [p1, p2, p3];
+      const rejections = [1, 2, 3];
+
+      try {
+        await RA.lastP(iterable);
+        throw new Error('resolving should fail');
+      } catch (e) {
+        assert.notStrictEqual(e.message, 'resolving should fail');
+        assert.deepEqual(e, rejections);
+      }
+    });
+  });
+
+  context('given list of fulfilled and rejected promises', function() {
+    specify(
+      'should return the result of the last fulfilled promise',
+      async function() {
+        const p1 = new Promise(resolve => setTimeout(resolve, 20, 1));
+        const p2 = RA.rejectP(2);
+        const p3 = RA.resolveP(3);
+        const p4 = RA.rejectP(4);
+        const iterable = [p1, p2, p3, p4];
+        const slowest = 1;
+
+        assert.strictEqual(await RA.lastP(iterable), slowest);
+      }
+    );
+  });
+
+  context('given list of values', function() {
+    specify('should return the last value', async function() {
+      const v1 = 1;
+      const v2 = 2;
+      const v3 = 3;
+      const iterable = [v1, v2, v3];
+      const last = 3;
+
+      assert.strictEqual(await RA.lastP(iterable), last);
+    });
+  });
+
+  context('given large number of promises', function() {
+    specify('should handle them properly', async function() {
+      const slowest = 300;
+      const list = R.range(1, 1000);
+      const iterable = list.map(
+        R.ifElse(
+          R.equals(slowest),
+          () => new Promise(resolve => setTimeout(resolve, 10, slowest)),
+          RA.resolveP
+        )
+      );
+
+      assert.strictEqual(await RA.lastP(iterable), slowest);
+    });
+  });
+
+  context('given an empty iterable', function() {
+    specify('should resolve undefined', async function() {
+      const iterable = [];
+      assert.isUndefined(await RA.lastP(iterable));
+    });
+  });
+
+  context('given a custom iterable', function() {
+    specify('should return the last value', async function() {
+      const myCustomIterable = {
+        *[Symbol.iterator]() {
+          yield new Promise(resolve => setTimeout(resolve, 20, 'one'));
+          yield new Promise(resolve => setTimeout(resolve, 40, 'two'));
+          yield RA.resolveP(3);
+          yield new Promise(resolve => setTimeout(resolve, 10, 'four'));
+        },
+      };
+      const slowest = 'two';
+
+      assert.strictEqual(await RA.lastP(myCustomIterable), slowest);
+    });
+  });
+
+  it('should support placeholder to specify "gaps"', async function() {
+    const lastP = RA.lastP(R.__);
+
+    assert.strictEqual(await lastP([1]), 1);
+  });
+});
diff --git a/types/test/lastP.ts b/types/test/lastP.ts
new file mode 100644
index 0000000000..c82cd46203
--- /dev/null
+++ b/types/test/lastP.ts
@@ -0,0 +1,12 @@
+import * as RA from 'ramda-adjunct';
+
+import { customIterable } from './helpers';
+
+RA.lastP([1, 2]); // $ExpectType Promise<number>
+RA.lastP(['a', 'b']); // $ExpectType Promise<string>
+RA.lastP([1, 'a']); // $ExpectType Promise<string | number>
+RA.lastP([]); // $ExpectType Promise<never>
+RA.lastP(customIterable); // $ExpectType Promise<number>
+
+RA.lastP({}); // $ExpectError
+RA.lastP(1); // $ExpectError