commit be76ba655ce5e1fafb1294e07abcd081d8d2351e
Author: zhouganqing
Date: Fri Feb 17 11:33:40 2023 +0800
Import Upstream version 3.7.2+dfsg1
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..9d02467
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,14 @@
+# This file is for unifying the coding style for different editors and IDEs
+# editorconfig.org
+
+root = true
+
+[*]
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[**.js]
+indent_style = space
+indent_size = 4
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8c0cfdd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+docs/Gemfile.lock
+gh-pages
+node_modules
+gh-pages
+test/browser/bundle.js
+test/browser/worker_bundle.js
+js/*
+zalgo.js
+coverage/*
+.vscode
+.idea
diff --git a/.istanbul.yml b/.istanbul.yml
new file mode 100644
index 0000000..63e6eee
--- /dev/null
+++ b/.istanbul.yml
@@ -0,0 +1,49 @@
+verbose: false
+instrumentation:
+ root: .
+ default-excludes: true
+ excludes: []
+ embed-source: false
+ variable: __coverage__
+ compact: true
+ preserve-comments: false
+ complete-copy: false
+ save-baseline: false
+ baseline-file: ./coverage/coverage-baseline.json
+ include-all-sources: false
+reporting:
+ print: summary
+ reports:
+ - lcov
+ dir: ./coverage
+ watermarks:
+ statements: [50, 80]
+ lines: [50, 80]
+ functions: [50, 80]
+ branches: [50, 80]
+ report-config:
+ clover: {file: clover.xml}
+ cobertura: {file: cobertura-coverage.xml}
+ json: {file: coverage-final.json}
+ json-summary: {file: coverage-summary.json}
+ lcovonly: {file: lcov.info}
+ teamcity: {file: null}
+ text: {file: null, maxCols: 0}
+ text-summary: {file: null}
+hooks:
+ hook-run-in-context: false
+ post-require-hook: null
+ handle-sigint: false
+check:
+ global:
+ statements: 0
+ lines: 0
+ branches: 0
+ functions: 0
+ excludes: []
+ each:
+ statements: 0
+ lines: 0
+ branches: 0
+ functions: 0
+ excludes: []
diff --git a/.jshintignore b/.jshintignore
new file mode 100644
index 0000000..fbea002
--- /dev/null
+++ b/.jshintignore
@@ -0,0 +1 @@
+src/constants.js
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..bc14497
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,161 @@
+{
+ "bitwise": false,
+ "camelcase": true,
+ "curly": true,
+ "eqeqeq": true,
+ "es3": true,
+ "forin": true,
+ "immed": true,
+ "latedef": false,
+ "newcap": true,
+ "noarg": true,
+ "noempty": true,
+ "nonew": true,
+ "plusplus": false,
+ "quotmark": "double",
+ "undef": true,
+ "unused": true,
+ "strict": false,
+ "maxparams": 6,
+ "maxlen": 80,
+ "asi": false,
+ "boss": true,
+ "eqnull": true,
+ "evil": true,
+ "expr": false,
+ "funcscope": false,
+ "globalstrict": false,
+ "lastsemic": false,
+ "laxcomma": false,
+ "laxbreak": false,
+ "loopfunc": true,
+ "multistr": true,
+ "proto": false,
+ "scripturl": true,
+ "shadow": true,
+ "sub": true,
+ "supernew": false,
+ "validthis": true,
+ "browser": true,
+ "jquery": true,
+ "devel": true,
+ "-W014": true,
+ "-W116": true,
+ "-W106": true,
+ "-W064": true,
+ "-W097": true,
+ "globals": {
+ "Symbol": false,
+ "Map": false,
+ "JSON": false,
+ "Error": true,
+ "args": true,
+ "chrome": true,
+ "INLINE_SLICE": false,
+ "INLINE_SLICE_LEFT_PADDED": false,
+ "BIT_FIELD_CHECK": false,
+ "BIT_FIELD_READ": false,
+ "USE": false,
+ "global": true,
+ "setImmediate": true,
+ "Promise": true,
+ "WebKitMutationObserver": true,
+ "TypeError": true,
+ "RangeError": true,
+ "__DEBUG__": false,
+ "__BROWSER__": false,
+ "process": true,
+ "self": true,
+ "console": false,
+ "require": false,
+ "module": false,
+ "define": false,
+ "LATE_QUEUE_CAPACITY": false,
+ "NORMAL_QUEUE_CAPACITY": false,
+ "ERROR_HANDLED_KEY": false,
+ "OPERATIONAL_ERROR_KEY": false,
+ "DEFAULT_STATE": false,
+ "STACK_ATTACHED": false,
+ "ERROR_HANDLED": false,
+ "GENERATED_CLASS_COUNT": false,
+ "USE_BOUND": false,
+ "DONT_USE_BOUND": false,
+ "PROPAGATE_CANCEL": false,
+ "PROPAGATE_BIND": false,
+ "PROPAGATE_ALL": false,
+ "CALLBACK_FULFILL_OFFSET": false,
+ "CALLBACK_REJECT_OFFSET": false,
+ "CALLBACK_PROMISE_OFFSET": false,
+ "CALLBACK_RECEIVER_OFFSET": false,
+ "CALLBACK_SIZE": false,
+ "ASYNC_GUARANTEE_SHIFT": false,
+ "NO_STATE": false,
+ "NO_ASYNC_GUARANTEE": false,
+ "RETURNED_NON_UNDEFINED": false,
+ "IS_ASYNC_GUARANTEED": false,
+ "IS_FOLLOWING": false,
+ "IS_FULFILLED": false,
+ "IS_REJECTED": false,
+ "WILL_BE_CANCELLED": false,
+ "IS_FINAL": false,
+ "IS_BOUND": false,
+ "IS_REJECTION_UNHANDLED": false,
+ "IS_REJECTION_IGNORED": false,
+ "IS_UNHANDLED_REJECTION_NOTIFIED": false,
+ "IS_DISPOSABLE": false,
+ "IS_CANCELLED": false,
+ "IS_CANCELLED_OR_WILL_BE_CANCELLED": false,
+ "LENGTH_MASK": false,
+ "LENGTH_CLEAR_MASK": false,
+ "MAX_LENGTH": false,
+ "IS_REJECTED_OR_CANCELLED": false,
+ "IS_REJECTED_OR_FULFILLED": false,
+ "IS_REJECTED_OR_FULFILLED_OR_CANCELLED": false,
+ "IS_PENDING_AND_WAITING_NEG": false,
+ "IS_FATE_SEALED": false,
+ "AFTER_PROMISIFIED_SUFFIX": false,
+ "UNHANDLED_REJECTION_EVENT": false,
+ "REJECTION_HANDLED_EVENT": false,
+ "RESOLVE_UNDEFINED": false,
+ "RESOLVE_ARRAY": false,
+ "RESOLVE_OBJECT": false,
+ "RESOLVE_FOREVER_PENDING": false,
+ "RESOLVE_CALL_METHOD": false,
+ "RESOLVE_MAP": false,
+ "QUEUE_MAX_CAPACITY": false,
+ "QUEUE_MIN_CAPACITY": false,
+ "FROM_PREVIOUS_EVENT": false,
+ "NO_STACK_TRACE": false,
+ "ADDITIONAL_STACK_TRACE": false,
+ "UNHANDLED_REJECTION_HEADER": false,
+ "FINALLY_TYPE": false,
+ "TAP_TYPE": false,
+ "THROW": false,
+ "RETURN": false,
+ "MAX_PARAM_COUNT": false,
+ "PARAM_COUNTS_TO_TRY": false,
+ "BLUEBIRD_ERRORS": false,
+ "OBJECT_PROMISIFY_DEPRECATED": false,
+ "SPAWN_DEPRECATED": false,
+ "LATE_CANCELLATION_OBSERVER": false,
+ "TIMEOUT_ERROR": false,
+ "COLLECTION_ERROR": false,
+ "OBJECT_ERROR": false,
+ "FUNCTION_ERROR": false,
+ "CONSTRUCT_ERROR_INVOCATION": false,
+ "NOT_GENERATOR_ERROR": false,
+ "LONG_STACK_TRACES_ERROR": false,
+ "INSPECTION_VALUE_ERROR": false,
+ "INSPECTION_REASON_ERROR": false,
+ "PROMISIFY_TYPE_ERROR": false,
+ "CIRCULAR_RESOLUTION_ERROR": false,
+ "PROPS_TYPE_ERROR": false,
+ "POSITIVE_INTEGER_ERROR": false,
+ "YIELDED_NON_PROMISE_ERROR": false,
+ "FROM_COROUTINE_CREATED_AT": false,
+ "UNBOUND_RESOLVER_INVOCATION": false,
+ "PROMISIFICATION_NORMAL_METHODS_ERROR": false,
+ "SUFFIX_NOT_IDENTIFIER": false,
+ "NO_ASYNC_SCHEDULER": false
+ }
+}
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..7adc795
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,26 @@
+language: node_js
+sudo: false
+matrix:
+ include:
+ - node_js: "0.10"
+ - node_js: "0.12"
+ - node_js: "4"
+ - node_js: "5"
+ - node_js: "6"
+ - node_js: "7"
+ - node_js: "8"
+ - node_js: "10"
+ fast_finish: true
+git:
+ depth: 5
+env:
+ - "NODE_FLAGS='--expose-gc' SCRIPT_FLAGS=''"
+before_script:
+- git submodule update --init --recursive
+script: "node $NODE_FLAGS tools/test.js $SCRIPT_FLAGS"
+branches:
+ only:
+ - master
+cache:
+ directories:
+ - "$HOME/.npm"
diff --git a/API.md b/API.md
new file mode 100644
index 0000000..257d52c
--- /dev/null
+++ b/API.md
@@ -0,0 +1 @@
+[http://bluebirdjs.com/docs/api-reference.html](http://bluebirdjs.com/docs/api-reference.html)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..136bb7e
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,9 @@
+
+# Questions and issues
+
+Please see [The Support Page](http://bluebirdjs.com/docs/support.html)
+The [github issue tracker](https://github.com/petkaantonov/bluebird/issues) is **_only_** for bug reports and feature requests.
+
+# Contributing to the library
+
+Contributions are welcome and appreciated. See the [Contribution Page](http://bluebirdjs.com/docs/contribute.html) on bluebirdjs.com
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b24e635
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2018 Petka Antonov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7c1dd66
--- /dev/null
+++ b/README.md
@@ -0,0 +1,57 @@
+
+
+
+
+
+[![Build Status](https://travis-ci.org/petkaantonov/bluebird.svg?branch=master)](https://travis-ci.org/petkaantonov/bluebird)
+[![coverage-98%](https://img.shields.io/badge/coverage-98%25-brightgreen.svg?style=flat)](http://petkaantonov.github.io/bluebird/coverage/debug/index.html)
+
+**Got a question?** Join us on [stackoverflow](http://stackoverflow.com/questions/tagged/bluebird), the [mailing list](https://groups.google.com/forum/#!forum/bluebird-js) or chat on [IRC](https://webchat.freenode.net/?channels=#promises)
+
+# Introduction
+
+Bluebird is a fully featured promise library with focus on innovative features and performance
+
+See the [**bluebird website**](http://bluebirdjs.com/docs/getting-started.html) for further documentation, references and instructions. See the [**API reference**](http://bluebirdjs.com/docs/api-reference.html) here.
+
+For bluebird 2.x documentation and files, see the [2.x tree](https://github.com/petkaantonov/bluebird/tree/2.x).
+
+### Note
+
+Promises in Node.js 10 are significantly faster than before. Bluebird still includes a lot of features like cancellation, iteration methods and warnings that native promises don't. If you are using Bluebird for performance rather than for those - please consider giving native promises a shot and running the benchmarks yourself.
+
+# Questions and issues
+
+The [github issue tracker](https://github.com/petkaantonov/bluebird/issues) is **_only_** for bug reports and feature requests. Anything else, such as questions for help in using the library, should be posted in [StackOverflow](http://stackoverflow.com/questions/tagged/bluebird) under tags `promise` and `bluebird`.
+
+
+
+## Thanks
+
+Thanks to BrowserStack for providing us with a free account which lets us support old browsers like IE8.
+
+# License
+
+The MIT License (MIT)
+
+Copyright (c) 2013-2019 Petka Antonov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/bench b/bench
new file mode 100755
index 0000000..cfe344d
--- /dev/null
+++ b/bench
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+./build --release --no-debug
+benchmark=$1
+nodepath=${2:-node}
+shift 2;
+cwd=${PWD}
+
+export NODE_ENV=production
+
+trap 'cd "$cwd"' EXIT
+
+if [ "$benchmark" = "doxbee" ]; then
+ cd "$cwd/benchmark"
+ npm install
+ echo "Doxbee sequential"
+ $nodepath performance.js --n 10000 --t 1 ./doxbee-sequential/*.js --harmony "$@"
+ exit 0
+elif [ "$benchmark" = "doxbee-errors" ]; then
+ cd "$cwd/benchmark"
+ npm install
+ echo "Doxbee sequential with 10% errors"
+ $nodepath performance.js --n 10000 --t 1 --e 0.1 ./doxbee-sequential-errors/*.js --harmony "$@"
+ exit 0
+elif [ "$benchmark" = "parallel" ]; then
+ cd "$cwd/benchmark"
+ npm install
+ echo "Madeup parallel"
+ $nodepath performance.js --n 10000 --t 1 --p 25 ./madeup-parallel/*.js --harmony "$@"
+ exit 0
+elif [ "$benchmark" = "analysis" ]; then
+ cd "$cwd/benchmark"
+ npm install
+ echo "analysis"
+ $nodepath performance.js --n 10000 --t 1 ./analysis/*.js --harmony "$@"
+ exit 0
+else
+ echo "Invalid benchmark name $benchmark"
+ exit -1
+fi
diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000..b2eae30
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,34 @@
+{
+ "name": "bluebird",
+ "version": "3.7.2",
+ "homepage": "https://github.com/petkaantonov/bluebird",
+ "authors": [
+ "Petka Antonov "
+ ],
+ "description": "Bluebird is a full featured promise library with unmatched performance.",
+ "main": "js/browser/bluebird.js",
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "benchmark",
+ "bower_components",
+ "./browser",
+ "node_modules",
+ "test"
+ ],
+ "keywords": [
+ "promise",
+ "performance",
+ "promises",
+ "promises-a",
+ "promises-aplus",
+ "async",
+ "await",
+ "deferred",
+ "deferreds",
+ "future",
+ "flow control",
+ "dsl",
+ "fluent interface"
+ ]
+}
diff --git a/build b/build
new file mode 100755
index 0000000..3d26cbd
--- /dev/null
+++ b/build
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+node tools/build.js "$@"
diff --git a/changelog.md b/changelog.md
new file mode 100644
index 0000000..73b2eb6
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1 @@
+[http://bluebirdjs.com/docs/changelog.html](http://bluebirdjs.com/docs/changelog.html)
diff --git a/deprecated_apis.md b/deprecated_apis.md
new file mode 100644
index 0000000..30506e5
--- /dev/null
+++ b/deprecated_apis.md
@@ -0,0 +1 @@
+[http://bluebirdjs.com/docs/deprecated-apis.html](http://bluebirdjs.com/docs/deprecated-apis.html)
diff --git a/issue_template.md b/issue_template.md
new file mode 100644
index 0000000..35e5c6c
--- /dev/null
+++ b/issue_template.md
@@ -0,0 +1,12 @@
+(This issue tracker is only for bug reports or feature requests, if this is neither, please choose appropriate channel from http://bluebirdjs.com/docs/support.html)
+
+Please answer the questions the best you can:
+
+1) What version of bluebird is the issue happening on?
+
+2) What platform and version? (For example Node.js 0.12 or Google Chrome 32)
+
+3) Did this issue happen with earlier version of bluebird?
+
+(Write description of your issue here, stack traces from errors and code that reproduces the issue are helpful)
+
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..35328ca
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,3209 @@
+{
+ "name": "bluebird",
+ "version": "3.5.4",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "Base64": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz",
+ "integrity": "sha1-ujpCMHCOGGcFBl5mur3Uw1z2ACg=",
+ "dev": true
+ },
+ "JSONStream": {
+ "version": "0.8.4",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.4.tgz",
+ "integrity": "sha1-kWV9/m/4V0gwZhMrRhi2Lo9Ih70=",
+ "dev": true,
+ "requires": {
+ "jsonparse": "0.0.5",
+ "through": ">=2.2.7 <3"
+ }
+ },
+ "abbrev": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz",
+ "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=",
+ "dev": true
+ },
+ "acorn": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz",
+ "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==",
+ "dev": true
+ },
+ "acorn-dynamic-import": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz",
+ "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==",
+ "dev": true
+ },
+ "acorn-node": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.7.0.tgz",
+ "integrity": "sha512-XhahLSsCB6X6CJbe+uNu3Mn9sJBNFxtBN9NLgAOQovfS6Kh0lDUtmlclhjn9CvEK7A7YyRU13PXlNcpSiLI9Yw==",
+ "dev": true,
+ "requires": {
+ "acorn": "^6.1.1",
+ "acorn-dynamic-import": "^4.0.0",
+ "acorn-walk": "^6.1.1",
+ "xtend": "^4.0.1"
+ },
+ "dependencies": {
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+ "dev": true
+ }
+ }
+ },
+ "acorn-walk": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz",
+ "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==",
+ "dev": true
+ },
+ "amdefine": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz",
+ "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=",
+ "dev": true
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "asn1": {
+ "version": "0.1.11",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz",
+ "integrity": "sha1-VZvhg3bQik7E2+gId9J4GGObLfc=",
+ "dev": true
+ },
+ "asn1.js": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
+ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "assert": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-1.3.0.tgz",
+ "integrity": "sha1-A5OaYiWCqBLMICMgoLmlbJuBWEk=",
+ "dev": true,
+ "requires": {
+ "util": "0.10.3"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+ "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
+ "dev": true
+ },
+ "util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+ "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.1"
+ }
+ }
+ }
+ },
+ "assert-plus": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz",
+ "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=",
+ "dev": true,
+ "optional": true
+ },
+ "astw": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz",
+ "integrity": "sha1-e9QXhNMkk5h66yOba04cV6hzuRc=",
+ "dev": true,
+ "requires": {
+ "acorn": "^4.0.3"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "4.0.13",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
+ "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=",
+ "dev": true
+ }
+ }
+ },
+ "async": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
+ "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=",
+ "dev": true
+ },
+ "aws-sign": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/aws-sign/-/aws-sign-0.3.0.tgz",
+ "integrity": "sha1-PYHKabR0seFlGHKLUcJP8Lvtxuk=",
+ "dev": true
+ },
+ "aws-sign2": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz",
+ "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM=",
+ "dev": true,
+ "optional": true
+ },
+ "baconjs": {
+ "version": "0.7.95",
+ "resolved": "https://registry.npmjs.org/baconjs/-/baconjs-0.7.95.tgz",
+ "integrity": "sha512-3qp0GuAfEUlJybSVPQ2oai8VYO0aSTJf4wP/jYZpgaffEXi31VBcqSlVmD8ahmXXGzgdO+yFk9onDnt2ZJJXxA==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "base64-js": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
+ "integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg=",
+ "dev": true
+ },
+ "bluebird": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz",
+ "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=",
+ "dev": true
+ },
+ "bn.js": {
+ "version": "4.11.8",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
+ "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
+ "dev": true
+ },
+ "body-parser": {
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+ "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.1.0",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
+ "on-finished": "~2.3.0",
+ "qs": "6.7.0",
+ "raw-body": "2.4.0",
+ "type-is": "~1.6.17"
+ }
+ },
+ "boom": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz",
+ "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=",
+ "dev": true,
+ "requires": {
+ "hoek": "0.9.x"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
+ "dev": true
+ },
+ "browser-pack": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-3.2.0.tgz",
+ "integrity": "sha1-+qHLxBSHsazEdH43PhFIrf/Q4tk=",
+ "dev": true,
+ "requires": {
+ "JSONStream": "~0.8.4",
+ "combine-source-map": "~0.3.0",
+ "concat-stream": "~1.4.1",
+ "defined": "~0.0.0",
+ "through2": "~0.5.1",
+ "umd": "^2.1.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "1.0.34",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "through2": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz",
+ "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~1.0.17",
+ "xtend": "~3.0.0"
+ }
+ }
+ }
+ },
+ "browser-resolve": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz",
+ "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==",
+ "dev": true,
+ "requires": {
+ "resolve": "1.1.7"
+ },
+ "dependencies": {
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+ "dev": true
+ }
+ }
+ },
+ "browserify": {
+ "version": "8.1.3",
+ "resolved": "https://registry.npmjs.org/browserify/-/browserify-8.1.3.tgz",
+ "integrity": "sha1-8zpUmjpsNoIZsHX9z+bfGV4Empo=",
+ "dev": true,
+ "requires": {
+ "JSONStream": "~0.8.3",
+ "assert": "~1.3.0",
+ "browser-pack": "^3.2.0",
+ "browser-resolve": "^1.3.0",
+ "browserify-zlib": "~0.1.2",
+ "buffer": "^3.0.0",
+ "builtins": "~0.0.3",
+ "commondir": "0.0.1",
+ "concat-stream": "~1.4.1",
+ "console-browserify": "^1.1.0",
+ "constants-browserify": "~0.0.1",
+ "crypto-browserify": "^3.0.0",
+ "deep-equal": "~0.2.1",
+ "defined": "~0.0.0",
+ "deps-sort": "^1.3.5",
+ "domain-browser": "~1.1.0",
+ "duplexer2": "~0.0.2",
+ "events": "~1.0.0",
+ "glob": "^4.0.5",
+ "http-browserify": "^1.4.0",
+ "https-browserify": "~0.0.0",
+ "inherits": "~2.0.1",
+ "insert-module-globals": "^6.2.0",
+ "isarray": "0.0.1",
+ "labeled-stream-splicer": "^1.0.0",
+ "module-deps": "^3.6.3",
+ "os-browserify": "~0.1.1",
+ "parents": "^1.0.1",
+ "path-browserify": "~0.0.0",
+ "process": "^0.10.0",
+ "punycode": "~1.2.3",
+ "querystring-es3": "~0.2.0",
+ "readable-stream": "^1.0.33-1",
+ "resolve": "~0.7.1",
+ "shallow-copy": "0.0.1",
+ "shasum": "^1.0.0",
+ "shell-quote": "~0.0.1",
+ "stream-browserify": "^1.0.0",
+ "string_decoder": "~0.10.0",
+ "subarg": "^1.0.0",
+ "syntax-error": "^1.1.1",
+ "through2": "^1.0.0",
+ "timers-browserify": "^1.0.1",
+ "tty-browserify": "~0.0.0",
+ "umd": "~2.1.0",
+ "url": "~0.10.1",
+ "util": "~0.10.1",
+ "vm-browserify": "~0.0.1",
+ "xtend": "^3.0.0"
+ }
+ },
+ "browserify-aes": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+ "dev": true,
+ "requires": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "browserify-cipher": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+ "dev": true,
+ "requires": {
+ "browserify-aes": "^1.0.4",
+ "browserify-des": "^1.0.0",
+ "evp_bytestokey": "^1.0.0"
+ }
+ },
+ "browserify-des": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+ "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "des.js": "^1.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "browserify-rsa": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "randombytes": "^2.0.1"
+ }
+ },
+ "browserify-sign": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
+ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.1",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.2",
+ "elliptic": "^6.0.0",
+ "inherits": "^2.0.1",
+ "parse-asn1": "^5.0.0"
+ }
+ },
+ "browserify-zlib": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz",
+ "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=",
+ "dev": true,
+ "requires": {
+ "pako": "~0.2.0"
+ }
+ },
+ "buffer": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz",
+ "integrity": "sha1-pyyTb3e5a/UvX357RnGAYoVR3vs=",
+ "dev": true,
+ "requires": {
+ "base64-js": "0.0.8",
+ "ieee754": "^1.1.4",
+ "isarray": "^1.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ }
+ }
+ },
+ "buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
+ "dev": true
+ },
+ "builtins": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/builtins/-/builtins-0.0.7.tgz",
+ "integrity": "sha1-NVIZzWzxjb58Acx/0tznZc/cVJo=",
+ "dev": true
+ },
+ "buster-core": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/buster-core/-/buster-core-0.6.4.tgz",
+ "integrity": "sha1-J79rrWdCROpyDzEdkAoMoct4YFA=",
+ "dev": true
+ },
+ "buster-format": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/buster-format/-/buster-format-0.5.6.tgz",
+ "integrity": "sha1-K4bDIuz14bCubm55Bev884fSq5U=",
+ "dev": true,
+ "requires": {
+ "buster-core": "=0.6.4"
+ }
+ },
+ "bytes": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+ "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
+ "dev": true
+ },
+ "callsite": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+ "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+ "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz",
+ "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "~1.0.0",
+ "has-color": "~0.1.0",
+ "strip-ansi": "~0.1.0"
+ }
+ },
+ "cipher-base": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "cli": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
+ "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=",
+ "dev": true,
+ "requires": {
+ "exit": "0.1.2",
+ "glob": "^7.1.1"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
+ "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ }
+ }
+ },
+ "cli-table": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz",
+ "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=",
+ "dev": true,
+ "requires": {
+ "colors": "1.0.3"
+ }
+ },
+ "co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+ "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=",
+ "dev": true
+ },
+ "combine-source-map": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.3.0.tgz",
+ "integrity": "sha1-2edPWT2c1DgHMSy12EbUUe+qnrc=",
+ "dev": true,
+ "requires": {
+ "convert-source-map": "~0.3.0",
+ "inline-source-map": "~0.3.0",
+ "source-map": "~0.1.31"
+ }
+ },
+ "combined-stream": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz",
+ "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=",
+ "dev": true,
+ "requires": {
+ "delayed-stream": "0.0.5"
+ }
+ },
+ "commander": {
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+ "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
+ "dev": true,
+ "optional": true
+ },
+ "commondir": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-0.0.1.tgz",
+ "integrity": "sha1-ifAP3NUbUZxXhzP+xWPmptp/W+I=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "concat-stream": {
+ "version": "1.4.11",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.4.11.tgz",
+ "integrity": "sha512-X3JMh8+4je3U1cQpG87+f9lXHDrqcb2MVLg9L7o8b1UZ0DzhRrUpdn65ttzu10PpJPPI3MQNkis+oha6TSA9Mw==",
+ "dev": true,
+ "requires": {
+ "inherits": "~2.0.1",
+ "readable-stream": "~1.1.9",
+ "typedarray": "~0.0.5"
+ }
+ },
+ "console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+ "dev": true,
+ "requires": {
+ "date-now": "^0.1.4"
+ }
+ },
+ "constants-browserify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-0.0.1.tgz",
+ "integrity": "sha1-kld9tSe6bEzwpFaNhLwDH0QeIfI=",
+ "dev": true
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz",
+ "integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=",
+ "dev": true
+ },
+ "cookie-jar": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/cookie-jar/-/cookie-jar-0.3.0.tgz",
+ "integrity": "sha1-vJon1OK5fhhs1XyeIGPLmfpozMw=",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
+ },
+ "create-ecdh": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
+ "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "elliptic": "^6.0.0"
+ }
+ },
+ "create-hash": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "create-hmac": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "cross-spawn": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-0.2.9.tgz",
+ "integrity": "sha1-vWf5bAfvtjA7f+lMHpefiEeOCjk=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^2.5.0"
+ }
+ },
+ "cryptiles": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz",
+ "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=",
+ "dev": true,
+ "requires": {
+ "boom": "0.4.x"
+ }
+ },
+ "crypto-browserify": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+ "dev": true,
+ "requires": {
+ "browserify-cipher": "^1.0.0",
+ "browserify-sign": "^4.0.0",
+ "create-ecdh": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.0",
+ "diffie-hellman": "^5.0.0",
+ "inherits": "^2.0.1",
+ "pbkdf2": "^3.0.3",
+ "public-encrypt": "^4.0.0",
+ "randombytes": "^2.0.0",
+ "randomfill": "^1.0.3"
+ }
+ },
+ "ctype": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz",
+ "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=",
+ "dev": true,
+ "optional": true
+ },
+ "date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
+ },
+ "deep-equal": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.2.2.tgz",
+ "integrity": "sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0=",
+ "dev": true
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "defined": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz",
+ "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=",
+ "dev": true
+ },
+ "delayed-stream": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz",
+ "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=",
+ "dev": true
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
+ "dev": true
+ },
+ "deps-sort": {
+ "version": "1.3.9",
+ "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-1.3.9.tgz",
+ "integrity": "sha1-Kd//U+F7Nq7K51MK27v2IsLtGnE=",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.0.3",
+ "shasum": "^1.0.0",
+ "subarg": "^1.0.0",
+ "through2": "^1.0.0"
+ },
+ "dependencies": {
+ "JSONStream": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+ "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+ "dev": true,
+ "requires": {
+ "jsonparse": "^1.2.0",
+ "through": ">=2.2.7 <3"
+ }
+ },
+ "jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
+ "dev": true
+ }
+ }
+ },
+ "des.js": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
+ "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
+ "dev": true
+ },
+ "detective": {
+ "version": "4.7.1",
+ "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz",
+ "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==",
+ "dev": true,
+ "requires": {
+ "acorn": "^5.2.1",
+ "defined": "^1.0.0"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "5.7.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
+ "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
+ "dev": true
+ },
+ "defined": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
+ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
+ "dev": true
+ }
+ }
+ },
+ "diff": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.8.tgz",
+ "integrity": "sha1-NDJ2MI7Jkbe8giZ+1VvBQR+XFmY=",
+ "dev": true
+ },
+ "diffie-hellman": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "miller-rabin": "^4.0.0",
+ "randombytes": "^2.0.0"
+ }
+ },
+ "dom-serializer": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
+ "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^1.3.0",
+ "entities": "^1.1.1"
+ },
+ "dependencies": {
+ "entities": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
+ "dev": true
+ }
+ }
+ },
+ "domain-browser": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz",
+ "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=",
+ "dev": true
+ },
+ "domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+ "dev": true
+ },
+ "domhandler": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
+ "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=",
+ "dev": true,
+ "requires": {
+ "domelementtype": "1"
+ }
+ },
+ "domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "duplexer2": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz",
+ "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~1.1.9"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
+ "dev": true
+ },
+ "elliptic": {
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
+ "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.4.0",
+ "brorand": "^1.0.1",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.0"
+ }
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+ "dev": true
+ },
+ "entities": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
+ "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=",
+ "dev": true
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz",
+ "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=",
+ "dev": true
+ },
+ "escodegen": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.7.1.tgz",
+ "integrity": "sha1-MOz89mypjcZ80v0WKr626vqM5vw=",
+ "dev": true,
+ "requires": {
+ "esprima": "^1.2.2",
+ "estraverse": "^1.9.1",
+ "esutils": "^2.0.2",
+ "optionator": "^0.5.0",
+ "source-map": "~0.2.0"
+ },
+ "dependencies": {
+ "esprima": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz",
+ "integrity": "sha1-CZNQL+r2aBODJXVvMPmlH+7sEek=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz",
+ "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ }
+ }
+ },
+ "esprima": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.5.0.tgz",
+ "integrity": "sha1-84ekb9NEwbGjm6+MIL+0O20AWMw=",
+ "dev": true
+ },
+ "estraverse": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz",
+ "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+ "dev": true
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+ "dev": true
+ },
+ "events": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/events/-/events-1.0.2.tgz",
+ "integrity": "sha1-dYSdz+k9EPsFfDAFWv29UdBqjiQ=",
+ "dev": true
+ },
+ "evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "dev": true,
+ "requires": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.0.7.tgz",
+ "integrity": "sha1-AXjc3uAjuSkFGTrwlZ6KdjnP3Lk=",
+ "dev": true
+ },
+ "fileset": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fileset/-/fileset-0.2.1.tgz",
+ "integrity": "sha1-WI74lzxmI7KnbfRlEFaWuWqsgGc=",
+ "dev": true,
+ "requires": {
+ "glob": "5.x",
+ "minimatch": "2.x"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "5.0.15",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+ "dev": true,
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "forever-agent": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz",
+ "integrity": "sha1-bQ4JxJIflKJ/Y9O0nF/v8epMUTA=",
+ "dev": true
+ },
+ "form-data": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz",
+ "integrity": "sha1-kavXiKupcCsaq/qLwBAxoqyeOxI=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "async": "~0.9.0",
+ "combined-stream": "~0.0.4",
+ "mime": "~1.2.11"
+ },
+ "dependencies": {
+ "async": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
+ "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "glob": {
+ "version": "4.5.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz",
+ "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=",
+ "dev": true,
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^2.0.1",
+ "once": "^1.3.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz",
+ "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=",
+ "dev": true
+ },
+ "growl": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz",
+ "integrity": "sha1-Sy3sjZB+k9szZiTc7AGDUC+MlCg=",
+ "dev": true
+ },
+ "grunt-saucelabs": {
+ "version": "8.4.1",
+ "resolved": "https://registry.npmjs.org/grunt-saucelabs/-/grunt-saucelabs-8.4.1.tgz",
+ "integrity": "sha1-16F5wt24LRgIoZ2kWEvl1aoeKfU=",
+ "dev": true,
+ "requires": {
+ "colors": "~0.6.2",
+ "lodash": "~2.4.1",
+ "q": "~1.0.0",
+ "request": "~2.35.0",
+ "sauce-tunnel": "~2.1.1",
+ "saucelabs": "~0.1.1"
+ },
+ "dependencies": {
+ "colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
+ "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=",
+ "dev": true
+ }
+ }
+ },
+ "handlebars": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz",
+ "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==",
+ "dev": true,
+ "requires": {
+ "neo-async": "^2.6.0",
+ "optimist": "^0.6.1",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4"
+ },
+ "dependencies": {
+ "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
+ },
+ "uglify-js": {
+ "version": "3.5.15",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.15.tgz",
+ "integrity": "sha512-fe7aYFotptIddkwcm6YuA0HmknBZ52ZzOsUxZEdhhkSsz7RfjHDX2QDxwKTiv4JQ5t5NhfmpgAK+J7LiDhKSqg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "commander": "~2.20.0",
+ "source-map": "~0.6.1"
+ }
+ }
+ }
+ },
+ "has-color": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz",
+ "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
+ "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
+ "dev": true
+ },
+ "hash-base": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
+ "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "hawk": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz",
+ "integrity": "sha1-uQuxaYByhUEdp//LjdJZhQLTtS0=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "boom": "0.4.x",
+ "cryptiles": "0.2.x",
+ "hoek": "0.9.x",
+ "sntp": "0.2.x"
+ }
+ },
+ "highland": {
+ "version": "2.13.4",
+ "resolved": "https://registry.npmjs.org/highland/-/highland-2.13.4.tgz",
+ "integrity": "sha512-r+YlbnBhCTcrcVzBpzPcrvB0llVjeDWKuXSZVuNBe5WgQJtN2xGUUMZC9WzHCntNIx0rskVernxLoFJUCkmb/Q==",
+ "dev": true,
+ "requires": {
+ "util-deprecate": "^1.0.2"
+ }
+ },
+ "hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+ "dev": true,
+ "requires": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "hoek": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz",
+ "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU=",
+ "dev": true
+ },
+ "htmlparser2": {
+ "version": "3.8.3",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
+ "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=",
+ "dev": true,
+ "requires": {
+ "domelementtype": "1",
+ "domhandler": "2.3",
+ "domutils": "1.5",
+ "entities": "1.0",
+ "readable-stream": "1.1"
+ }
+ },
+ "http-browserify": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/http-browserify/-/http-browserify-1.7.0.tgz",
+ "integrity": "sha1-M3la3nLfiKz7/TZ3PO/tp2RzWyA=",
+ "dev": true,
+ "requires": {
+ "Base64": "~0.2.0",
+ "inherits": "~2.0.1"
+ }
+ },
+ "http-errors": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+ "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+ "dev": true,
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.1",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.0"
+ }
+ },
+ "http-signature": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz",
+ "integrity": "sha1-T72sEyVZqoMjEh5UB3nAoBKyfmY=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "asn1": "0.1.11",
+ "assert-plus": "^0.1.5",
+ "ctype": "0.5.3"
+ }
+ },
+ "https-browserify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz",
+ "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=",
+ "dev": true
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ieee754": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
+ "dev": true
+ },
+ "indexof": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "inline-source-map": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.3.1.tgz",
+ "integrity": "sha1-pSi1FOaJ/OkNswiehw2S9Sestes=",
+ "dev": true,
+ "requires": {
+ "source-map": "~0.3.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.3.0.tgz",
+ "integrity": "sha1-hYb7mloAXltQHiHNGLbyG0V60fk=",
+ "dev": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ }
+ }
+ },
+ "insert-module-globals": {
+ "version": "6.6.3",
+ "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-6.6.3.tgz",
+ "integrity": "sha1-IGOOKaMPntHKLjqCX7wsulJG3fw=",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.0.3",
+ "combine-source-map": "~0.6.1",
+ "concat-stream": "~1.4.1",
+ "is-buffer": "^1.1.0",
+ "lexical-scope": "^1.2.0",
+ "process": "~0.11.0",
+ "through2": "^1.0.0",
+ "xtend": "^4.0.0"
+ },
+ "dependencies": {
+ "JSONStream": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+ "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+ "dev": true,
+ "requires": {
+ "jsonparse": "^1.2.0",
+ "through": ">=2.2.7 <3"
+ }
+ },
+ "combine-source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.6.1.tgz",
+ "integrity": "sha1-m0oJwxYDPXaODxHgKfonMOB5rZY=",
+ "dev": true,
+ "requires": {
+ "convert-source-map": "~1.1.0",
+ "inline-source-map": "~0.5.0",
+ "lodash.memoize": "~3.0.3",
+ "source-map": "~0.4.2"
+ }
+ },
+ "convert-source-map": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz",
+ "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=",
+ "dev": true
+ },
+ "inline-source-map": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.5.0.tgz",
+ "integrity": "sha1-Skxd2OT7Xps82mDIIt+tyu5m4K8=",
+ "dev": true,
+ "requires": {
+ "source-map": "~0.4.0"
+ }
+ },
+ "jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
+ "dev": true
+ },
+ "process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+ "dev": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+ "dev": true
+ }
+ }
+ },
+ "ip-regex": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
+ "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
+ "dev": true,
+ "optional": true
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "istanbul": {
+ "version": "0.3.22",
+ "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.3.22.tgz",
+ "integrity": "sha1-PhZNhQIf4ZyYXR8OfvDD4i0BLrY=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1.0.x",
+ "async": "1.x",
+ "escodegen": "1.7.x",
+ "esprima": "2.5.x",
+ "fileset": "0.2.x",
+ "handlebars": "^4.0.1",
+ "js-yaml": "3.x",
+ "mkdirp": "0.5.x",
+ "nopt": "3.x",
+ "once": "1.x",
+ "resolve": "1.1.x",
+ "supports-color": "^3.1.0",
+ "which": "^1.1.1",
+ "wordwrap": "^1.0.0"
+ },
+ "dependencies": {
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+ "dev": true
+ },
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+ "dev": true
+ }
+ }
+ },
+ "jade": {
+ "version": "0.26.3",
+ "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz",
+ "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=",
+ "dev": true,
+ "requires": {
+ "commander": "0.6.1",
+ "mkdirp": "0.3.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz",
+ "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz",
+ "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=",
+ "dev": true
+ }
+ }
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "dependencies": {
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ }
+ }
+ },
+ "jshint": {
+ "version": "2.10.2",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.10.2.tgz",
+ "integrity": "sha512-e7KZgCSXMJxznE/4WULzybCMNXNAd/bf5TSrvVEq78Q/K8ZwFpmBqQeDtNiHc3l49nV4E/+YeHU/JZjSUIrLAA==",
+ "dev": true,
+ "requires": {
+ "cli": "~1.0.0",
+ "console-browserify": "1.1.x",
+ "exit": "0.1.x",
+ "htmlparser2": "3.8.x",
+ "lodash": "~4.17.11",
+ "minimatch": "~3.0.2",
+ "shelljs": "0.3.x",
+ "strip-json-comments": "1.0.x"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ }
+ }
+ },
+ "jshint-stylish": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/jshint-stylish/-/jshint-stylish-0.2.0.tgz",
+ "integrity": "sha1-newAJQrISXlgvk7tb1Bn+x1twH0=",
+ "dev": true,
+ "requires": {
+ "chalk": "~0.4.0",
+ "text-table": "~0.2.0"
+ }
+ },
+ "json-stable-stringify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz",
+ "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=",
+ "dev": true,
+ "requires": {
+ "jsonify": "~0.0.0"
+ }
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+ "dev": true
+ },
+ "jsonify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
+ "dev": true
+ },
+ "jsonparse": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz",
+ "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=",
+ "dev": true
+ },
+ "kefir": {
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/kefir/-/kefir-2.8.2.tgz",
+ "integrity": "sha1-zdnl6C+ymM+FOGWTYiY1SvpYnTE=",
+ "dev": true
+ },
+ "labeled-stream-splicer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-1.0.2.tgz",
+ "integrity": "sha1-RhUzFTd4SYHo/SZOHzpDTE4N3WU=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "isarray": "~0.0.1",
+ "stream-splicer": "^1.1.0"
+ }
+ },
+ "levn": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.2.5.tgz",
+ "integrity": "sha1-uo0znQykphDjo/FFucr0iAcVUFQ=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.0",
+ "type-check": "~0.3.1"
+ }
+ },
+ "lexical-scope": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz",
+ "integrity": "sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ=",
+ "dev": true,
+ "requires": {
+ "astw": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
+ "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
+ "dev": true
+ },
+ "lodash._arraypool": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._arraypool/-/lodash._arraypool-2.4.1.tgz",
+ "integrity": "sha1-6I7suS4ruEyQZWEv2VigcZzUf5Q=",
+ "dev": true
+ },
+ "lodash._basebind": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._basebind/-/lodash._basebind-2.4.1.tgz",
+ "integrity": "sha1-6UC5690nwyfgqNqxtVkWxTQelXU=",
+ "dev": true,
+ "requires": {
+ "lodash._basecreate": "~2.4.1",
+ "lodash._setbinddata": "~2.4.1",
+ "lodash._slice": "~2.4.1",
+ "lodash.isobject": "~2.4.1"
+ }
+ },
+ "lodash._basecreate": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-2.4.1.tgz",
+ "integrity": "sha1-+Ob1tXip405UEXm1a47uv0oofgg=",
+ "dev": true,
+ "requires": {
+ "lodash._isnative": "~2.4.1",
+ "lodash.isobject": "~2.4.1",
+ "lodash.noop": "~2.4.1"
+ }
+ },
+ "lodash._basecreatecallback": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._basecreatecallback/-/lodash._basecreatecallback-2.4.1.tgz",
+ "integrity": "sha1-fQsmdknLKeehOdAQO3wR+uhOSFE=",
+ "dev": true,
+ "requires": {
+ "lodash._setbinddata": "~2.4.1",
+ "lodash.bind": "~2.4.1",
+ "lodash.identity": "~2.4.1",
+ "lodash.support": "~2.4.1"
+ }
+ },
+ "lodash._basecreatewrapper": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._basecreatewrapper/-/lodash._basecreatewrapper-2.4.1.tgz",
+ "integrity": "sha1-TTHy595+E0+/KAN2K4FQsyUZZm8=",
+ "dev": true,
+ "requires": {
+ "lodash._basecreate": "~2.4.1",
+ "lodash._setbinddata": "~2.4.1",
+ "lodash._slice": "~2.4.1",
+ "lodash.isobject": "~2.4.1"
+ }
+ },
+ "lodash._basemerge": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._basemerge/-/lodash._basemerge-2.4.1.tgz",
+ "integrity": "sha1-B2Zrr7/AsZqp2vgm2Su+JZuy6RM=",
+ "dev": true,
+ "requires": {
+ "lodash.foreach": "~2.4.1",
+ "lodash.forown": "~2.4.1",
+ "lodash.isarray": "~2.4.1",
+ "lodash.isplainobject": "~2.4.1"
+ }
+ },
+ "lodash._createwrapper": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._createwrapper/-/lodash._createwrapper-2.4.1.tgz",
+ "integrity": "sha1-UdaVeXPaTtVW43KQ2MGhjFPeFgc=",
+ "dev": true,
+ "requires": {
+ "lodash._basebind": "~2.4.1",
+ "lodash._basecreatewrapper": "~2.4.1",
+ "lodash._slice": "~2.4.1",
+ "lodash.isfunction": "~2.4.1"
+ }
+ },
+ "lodash._getarray": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._getarray/-/lodash._getarray-2.4.1.tgz",
+ "integrity": "sha1-+vH3+BD6mFolHCGHQESBCUg55e4=",
+ "dev": true,
+ "requires": {
+ "lodash._arraypool": "~2.4.1"
+ }
+ },
+ "lodash._isnative": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz",
+ "integrity": "sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw=",
+ "dev": true
+ },
+ "lodash._maxpoolsize": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._maxpoolsize/-/lodash._maxpoolsize-2.4.1.tgz",
+ "integrity": "sha1-nUgvRjuOZq++WcLBTtsRcGAXIzQ=",
+ "dev": true
+ },
+ "lodash._objecttypes": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz",
+ "integrity": "sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE=",
+ "dev": true
+ },
+ "lodash._releasearray": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._releasearray/-/lodash._releasearray-2.4.1.tgz",
+ "integrity": "sha1-phOWMNdtFTawfdyAliiJsIL2pkE=",
+ "dev": true,
+ "requires": {
+ "lodash._arraypool": "~2.4.1",
+ "lodash._maxpoolsize": "~2.4.1"
+ }
+ },
+ "lodash._setbinddata": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._setbinddata/-/lodash._setbinddata-2.4.1.tgz",
+ "integrity": "sha1-98IAzRuS7yNrOZ7s9zxkjReqlNI=",
+ "dev": true,
+ "requires": {
+ "lodash._isnative": "~2.4.1",
+ "lodash.noop": "~2.4.1"
+ }
+ },
+ "lodash._shimisplainobject": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._shimisplainobject/-/lodash._shimisplainobject-2.4.1.tgz",
+ "integrity": "sha1-AeyTsu5j5Z8aqDiZrG+gkFrHWW8=",
+ "dev": true,
+ "requires": {
+ "lodash.forin": "~2.4.1",
+ "lodash.isfunction": "~2.4.1"
+ }
+ },
+ "lodash._shimkeys": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz",
+ "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=",
+ "dev": true,
+ "requires": {
+ "lodash._objecttypes": "~2.4.1"
+ }
+ },
+ "lodash._slice": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash._slice/-/lodash._slice-2.4.1.tgz",
+ "integrity": "sha1-dFz0GlNZexj2iImFREBe+isG2Q8=",
+ "dev": true
+ },
+ "lodash.bind": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-2.4.1.tgz",
+ "integrity": "sha1-XRn6AFyMTSNvr0dCx7eh/Kvikmc=",
+ "dev": true,
+ "requires": {
+ "lodash._createwrapper": "~2.4.1",
+ "lodash._slice": "~2.4.1"
+ }
+ },
+ "lodash.foreach": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-2.4.1.tgz",
+ "integrity": "sha1-/j/Do0yGyUyrb5UiVgKCdB4BYwk=",
+ "dev": true,
+ "requires": {
+ "lodash._basecreatecallback": "~2.4.1",
+ "lodash.forown": "~2.4.1"
+ }
+ },
+ "lodash.forin": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash.forin/-/lodash.forin-2.4.1.tgz",
+ "integrity": "sha1-gInq7X0lsIZyt8Zv0HrFXQYjIOs=",
+ "dev": true,
+ "requires": {
+ "lodash._basecreatecallback": "~2.4.1",
+ "lodash._objecttypes": "~2.4.1"
+ }
+ },
+ "lodash.forown": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash.forown/-/lodash.forown-2.4.1.tgz",
+ "integrity": "sha1-eLQer+FAX6lmRZ6kGT/VAtCEUks=",
+ "dev": true,
+ "requires": {
+ "lodash._basecreatecallback": "~2.4.1",
+ "lodash._objecttypes": "~2.4.1",
+ "lodash.keys": "~2.4.1"
+ }
+ },
+ "lodash.identity": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash.identity/-/lodash.identity-2.4.1.tgz",
+ "integrity": "sha1-ZpTP+mX++TH3wxzobHRZfPVg9PE=",
+ "dev": true
+ },
+ "lodash.isarray": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-2.4.1.tgz",
+ "integrity": "sha1-tSoybB9i9tfac6MdVAHfbvRPD6E=",
+ "dev": true,
+ "requires": {
+ "lodash._isnative": "~2.4.1"
+ }
+ },
+ "lodash.isfunction": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-2.4.1.tgz",
+ "integrity": "sha1-LP1XXHPkmKtX4xm3f6Aq3vE6lNE=",
+ "dev": true
+ },
+ "lodash.isobject": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz",
+ "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=",
+ "dev": true,
+ "requires": {
+ "lodash._objecttypes": "~2.4.1"
+ }
+ },
+ "lodash.isplainobject": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-2.4.1.tgz",
+ "integrity": "sha1-rHOF4uqawDIfMNw7gDKm0iMagBE=",
+ "dev": true,
+ "requires": {
+ "lodash._isnative": "~2.4.1",
+ "lodash._shimisplainobject": "~2.4.1"
+ }
+ },
+ "lodash.keys": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz",
+ "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=",
+ "dev": true,
+ "requires": {
+ "lodash._isnative": "~2.4.1",
+ "lodash._shimkeys": "~2.4.1",
+ "lodash.isobject": "~2.4.1"
+ }
+ },
+ "lodash.memoize": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz",
+ "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=",
+ "dev": true
+ },
+ "lodash.merge": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-2.4.1.tgz",
+ "integrity": "sha1-TJ/oQ/bXnhsFIhgbzeoEMIhn0h4=",
+ "dev": true,
+ "requires": {
+ "lodash._basecreatecallback": "~2.4.1",
+ "lodash._basemerge": "~2.4.1",
+ "lodash._getarray": "~2.4.1",
+ "lodash._releasearray": "~2.4.1",
+ "lodash._slice": "~2.4.1",
+ "lodash.isobject": "~2.4.1"
+ }
+ },
+ "lodash.noop": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-2.4.1.tgz",
+ "integrity": "sha1-T7VPgWZS5a4Q6PcvcXo4jHMmU4o=",
+ "dev": true
+ },
+ "lodash.support": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/lodash.support/-/lodash.support-2.4.1.tgz",
+ "integrity": "sha1-Mg4LZwMWc8KNeiu12eAzGkUkBRU=",
+ "dev": true,
+ "requires": {
+ "lodash._isnative": "~2.4.1"
+ }
+ },
+ "lru-cache": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
+ "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=",
+ "dev": true
+ },
+ "md5.js": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+ "dev": true
+ },
+ "miller-rabin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.0.0",
+ "brorand": "^1.0.1"
+ }
+ },
+ "mime": {
+ "version": "1.2.11",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz",
+ "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=",
+ "dev": true
+ },
+ "mime-db": {
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
+ "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.24",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
+ "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
+ "dev": true,
+ "requires": {
+ "mime-db": "1.40.0"
+ }
+ },
+ "minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "2.0.10",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz",
+ "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.0.0"
+ }
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ }
+ }
+ },
+ "mocha": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.1.0.tgz",
+ "integrity": "sha1-d3Uv5ZL7kJJ1aCevRs0+rhuDZxw=",
+ "dev": true,
+ "requires": {
+ "commander": "2.3.0",
+ "debug": "2.0.0",
+ "diff": "1.0.8",
+ "escape-string-regexp": "1.0.2",
+ "glob": "3.2.3",
+ "growl": "1.8.1",
+ "jade": "0.26.3",
+ "mkdirp": "0.5.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz",
+ "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.0.0.tgz",
+ "integrity": "sha1-ib2d9nMrUSVrxnBTQrugLtEhMe8=",
+ "dev": true,
+ "requires": {
+ "ms": "0.6.2"
+ }
+ },
+ "glob": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz",
+ "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "~2.0.0",
+ "inherits": "2",
+ "minimatch": "~0.2.11"
+ }
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+ "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz",
+ "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "ms": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz",
+ "integrity": "sha1-2JwhJMb9wTU9Zai3e/GqxLGTcIw=",
+ "dev": true
+ }
+ }
+ },
+ "module-deps": {
+ "version": "3.9.1",
+ "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-3.9.1.tgz",
+ "integrity": "sha1-6nXK+RmQkNJbDVUStaysuW5/h/M=",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.0.3",
+ "browser-resolve": "^1.7.0",
+ "concat-stream": "~1.4.5",
+ "defined": "^1.0.0",
+ "detective": "^4.0.0",
+ "duplexer2": "0.0.2",
+ "inherits": "^2.0.1",
+ "parents": "^1.0.0",
+ "readable-stream": "^1.1.13",
+ "resolve": "^1.1.3",
+ "stream-combiner2": "~1.0.0",
+ "subarg": "^1.0.0",
+ "through2": "^1.0.0",
+ "xtend": "^4.0.0"
+ },
+ "dependencies": {
+ "JSONStream": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+ "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+ "dev": true,
+ "requires": {
+ "jsonparse": "^1.2.0",
+ "through": ">=2.2.7 <3"
+ }
+ },
+ "defined": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
+ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
+ "dev": true
+ },
+ "jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz",
+ "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+ "dev": true
+ }
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "neo-async": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
+ "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
+ "dev": true
+ },
+ "node-uuid": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
+ "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=",
+ "dev": true
+ },
+ "nopt": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "oauth-sign": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz",
+ "integrity": "sha1-y1QPk7srIqfVlBaRoojWDo6pOG4=",
+ "dev": true
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "open": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz",
+ "integrity": "sha1-QsPhjslUZra/DcQvOilFw/DK2Pw=",
+ "dev": true
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+ "dev": true,
+ "requires": {
+ "minimist": "~0.0.1",
+ "wordwrap": "~0.0.2"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+ "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
+ "dev": true
+ }
+ }
+ },
+ "optionator": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.5.0.tgz",
+ "integrity": "sha1-t1qJlaLUF98ltuTjhi9QqohlE2g=",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.2",
+ "fast-levenshtein": "~1.0.0",
+ "levn": "~0.2.5",
+ "prelude-ls": "~1.1.1",
+ "type-check": "~0.3.1",
+ "wordwrap": "~0.0.2"
+ }
+ },
+ "os-browserify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.1.2.tgz",
+ "integrity": "sha1-ScoCk+CxlZCl9d4Qx/JlphfY/lQ=",
+ "dev": true
+ },
+ "pako": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+ "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=",
+ "dev": true
+ },
+ "parents": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
+ "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=",
+ "dev": true,
+ "requires": {
+ "path-platform": "~0.11.15"
+ }
+ },
+ "parse-asn1": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz",
+ "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==",
+ "dev": true,
+ "requires": {
+ "asn1.js": "^4.0.0",
+ "browserify-aes": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.0",
+ "pbkdf2": "^3.0.3",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true
+ },
+ "path-browserify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
+ "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "path-platform": {
+ "version": "0.11.15",
+ "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz",
+ "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=",
+ "dev": true
+ },
+ "pbkdf2": {
+ "version": "3.0.17",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
+ "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
+ "dev": true,
+ "requires": {
+ "create-hash": "^1.1.2",
+ "create-hmac": "^1.1.4",
+ "ripemd160": "^2.0.1",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
+ },
+ "process": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.10.1.tgz",
+ "integrity": "sha1-hCRXzFHP7XLcd1r+6vuMYDQ3JyU=",
+ "dev": true
+ },
+ "psl": {
+ "version": "1.1.31",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
+ "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==",
+ "dev": true,
+ "optional": true
+ },
+ "public-encrypt": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "parse-asn1": "^5.0.0",
+ "randombytes": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "punycode": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.2.4.tgz",
+ "integrity": "sha1-VACKyXKux0F13vnLpt9/qdORh0A=",
+ "dev": true
+ },
+ "q": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.0.1.tgz",
+ "integrity": "sha1-EYcq7t7okmgRCxCnGESP+xARKhQ=",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
+ "dev": true
+ },
+ "querystring": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+ "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+ "dev": true
+ },
+ "querystring-es3": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
+ "dev": true
+ },
+ "randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "randomfill": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.0.5",
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true
+ },
+ "raw-body": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+ "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.1.0",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "readable-wrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/readable-wrap/-/readable-wrap-1.0.0.tgz",
+ "integrity": "sha1-O1ohHGMeEjA6VJkcgGwX564ga/8=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^1.1.13-1"
+ }
+ },
+ "request": {
+ "version": "2.35.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.35.0.tgz",
+ "integrity": "sha1-DVwPKTR5oIDLpQj5Q0LNJBWg0pc=",
+ "dev": true,
+ "requires": {
+ "aws-sign2": "~0.5.0",
+ "forever-agent": "~0.5.0",
+ "form-data": "~0.1.0",
+ "hawk": "~1.0.0",
+ "http-signature": "~0.10.0",
+ "json-stringify-safe": "~5.0.0",
+ "lodash.merge": "~2.4.1",
+ "mime": "~1.2.9",
+ "node-uuid": "~1.4.0",
+ "oauth-sign": "~0.3.0",
+ "qs": "~0.6.0",
+ "tough-cookie": ">=0.12.0",
+ "tunnel-agent": "~0.4.0"
+ },
+ "dependencies": {
+ "qs": {
+ "version": "0.6.6",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz",
+ "integrity": "sha1-bgFQmP9RlouKPIGQAdXyyJvEsQc=",
+ "dev": true
+ }
+ }
+ },
+ "resolve": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.7.4.tgz",
+ "integrity": "sha1-OVqe+ehz+/4SvRRAi9kbuTYAPWk=",
+ "dev": true
+ },
+ "rfile": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/rfile/-/rfile-1.0.0.tgz",
+ "integrity": "sha1-WXCM+Qyh50xUw8/Fw2/bmBBDUmE=",
+ "dev": true,
+ "requires": {
+ "callsite": "~1.0.0",
+ "resolve": "~0.3.0"
+ },
+ "dependencies": {
+ "resolve": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.3.1.tgz",
+ "integrity": "sha1-NMY0R8ZkxwWY0cmxJvxDsqJDEKQ=",
+ "dev": true
+ }
+ }
+ },
+ "rimraf": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
+ "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=",
+ "dev": true
+ },
+ "ripemd160": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "ruglify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/ruglify/-/ruglify-1.0.0.tgz",
+ "integrity": "sha1-3Ikw4qlUSidDAcyZcldMDQmGtnU=",
+ "dev": true,
+ "requires": {
+ "rfile": "~1.0",
+ "uglify-js": "~2.2"
+ },
+ "dependencies": {
+ "optimist": {
+ "version": "0.3.7",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz",
+ "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=",
+ "dev": true,
+ "requires": {
+ "wordwrap": "~0.0.2"
+ }
+ },
+ "uglify-js": {
+ "version": "2.2.5",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz",
+ "integrity": "sha1-puAqcNg5eSuXgEiLe4sYTAlcmcc=",
+ "dev": true,
+ "requires": {
+ "optimist": "~0.3.5",
+ "source-map": "~0.1.7"
+ }
+ }
+ }
+ },
+ "rx": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/rx/-/rx-2.5.3.tgz",
+ "integrity": "sha1-Ia3H2A8CACr1Da6X/Z2/JIdV9WY=",
+ "dev": true
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "sauce-tunnel": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/sauce-tunnel/-/sauce-tunnel-2.1.1.tgz",
+ "integrity": "sha1-uKOuuCaZd84IS8LFZrf3nCqKb04=",
+ "dev": true,
+ "requires": {
+ "chalk": "~0.4.0",
+ "request": "~2.21.0"
+ },
+ "dependencies": {
+ "assert-plus": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz",
+ "integrity": "sha1-2T/9u2esVQd3m+MWp9ZRRkF77vg=",
+ "dev": true
+ },
+ "ctype": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.2.tgz",
+ "integrity": "sha1-/oCR1Gijc6Cwyf+Lv7NCXACXOh0=",
+ "dev": true
+ },
+ "form-data": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.0.8.tgz",
+ "integrity": "sha1-CJDNEAXFzOzAudJKiAUskkQtDbU=",
+ "dev": true,
+ "requires": {
+ "async": "~0.2.7",
+ "combined-stream": "~0.0.4",
+ "mime": "~1.2.2"
+ }
+ },
+ "hawk": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/hawk/-/hawk-0.13.1.tgz",
+ "integrity": "sha1-NheViCH1gxHk1/beKR/KZitBLvQ=",
+ "dev": true,
+ "requires": {
+ "boom": "0.4.x",
+ "cryptiles": "0.2.x",
+ "hoek": "0.8.x",
+ "sntp": "0.2.x"
+ }
+ },
+ "hoek": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.8.5.tgz",
+ "integrity": "sha1-Hp/XcO9+vgJ0rfy1sIBqAlpeTp8=",
+ "dev": true
+ },
+ "http-signature": {
+ "version": "0.9.11",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.9.11.tgz",
+ "integrity": "sha1-nognFFcjFeZ5Cl0KeVXv/x8Z5lM=",
+ "dev": true,
+ "requires": {
+ "asn1": "0.1.11",
+ "assert-plus": "0.1.2",
+ "ctype": "0.5.2"
+ }
+ },
+ "json-stringify-safe": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-4.0.0.tgz",
+ "integrity": "sha1-d8JxqupUMC5o7+rMtWq78GqbGlQ=",
+ "dev": true
+ },
+ "qs": {
+ "version": "0.6.6",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz",
+ "integrity": "sha1-bgFQmP9RlouKPIGQAdXyyJvEsQc=",
+ "dev": true
+ },
+ "request": {
+ "version": "2.21.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.21.0.tgz",
+ "integrity": "sha1-VyirnEXlqHyZ2szVMCmLZnOoaNc=",
+ "dev": true,
+ "requires": {
+ "aws-sign": "~0.3.0",
+ "cookie-jar": "~0.3.0",
+ "forever-agent": "~0.5.0",
+ "form-data": "0.0.8",
+ "hawk": "~0.13.0",
+ "http-signature": "~0.9.11",
+ "json-stringify-safe": "~4.0.0",
+ "mime": "~1.2.9",
+ "node-uuid": "~1.4.0",
+ "oauth-sign": "~0.3.0",
+ "qs": "~0.6.0",
+ "tunnel-agent": "~0.3.0"
+ }
+ },
+ "tunnel-agent": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz",
+ "integrity": "sha1-rWgbaPUyGtKCfEz7G31d8s/pQu4=",
+ "dev": true
+ }
+ }
+ },
+ "saucelabs": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-0.1.1.tgz",
+ "integrity": "sha1-Xg6hzz1zXW6hX96Utb2mvBXSwG0=",
+ "dev": true
+ },
+ "send": {
+ "version": "0.17.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+ "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.7.2",
+ "mime": "1.6.0",
+ "ms": "2.1.1",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.1",
+ "statuses": "~1.5.0"
+ },
+ "dependencies": {
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ }
+ }
+ },
+ "serve-static": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+ "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+ "dev": true,
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.17.1"
+ }
+ },
+ "setprototypeof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
+ "dev": true
+ },
+ "sha.js": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "shallow-copy": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz",
+ "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=",
+ "dev": true
+ },
+ "shasum": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz",
+ "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=",
+ "dev": true,
+ "requires": {
+ "json-stable-stringify": "~0.0.0",
+ "sha.js": "~2.4.4"
+ }
+ },
+ "shell-quote": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-0.0.1.tgz",
+ "integrity": "sha1-GkEZbzwDM8SCMjWT1ohuzxU92YY=",
+ "dev": true
+ },
+ "shelljs": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
+ "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=",
+ "dev": true
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
+ "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
+ "dev": true
+ },
+ "sinon": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.7.3.tgz",
+ "integrity": "sha1-emnWnNApRYbHQyVO7/G1g6UJl/I=",
+ "dev": true,
+ "requires": {
+ "buster-format": "~0.5"
+ }
+ },
+ "sntp": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz",
+ "integrity": "sha1-+4hfGLDzqtGJ+CSGJTa87ux1CQA=",
+ "dev": true,
+ "requires": {
+ "hoek": "0.9.x"
+ }
+ },
+ "source-map": {
+ "version": "0.1.43",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
+ "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
+ "dev": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+ "dev": true
+ },
+ "stream-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-1.0.0.tgz",
+ "integrity": "sha1-v5tKv7QrJ011FHnkTg/yZWtvEZM=",
+ "dev": true,
+ "requires": {
+ "inherits": "~2.0.1",
+ "readable-stream": "^1.0.27-1"
+ }
+ },
+ "stream-combiner2": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.0.2.tgz",
+ "integrity": "sha1-unKmtQy/q/qVD8i8h2BL0B62BnE=",
+ "dev": true,
+ "requires": {
+ "duplexer2": "~0.0.2",
+ "through2": "~0.5.1"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "1.0.34",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "through2": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz",
+ "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~1.0.17",
+ "xtend": "~3.0.0"
+ }
+ }
+ }
+ },
+ "stream-splicer": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-1.3.2.tgz",
+ "integrity": "sha1-PARBvhW5v04iYnXm3IOWR0VUZmE=",
+ "dev": true,
+ "requires": {
+ "indexof": "0.0.1",
+ "inherits": "^2.0.1",
+ "isarray": "~0.0.1",
+ "readable-stream": "^1.1.13-1",
+ "readable-wrap": "^1.0.0",
+ "through2": "^1.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz",
+ "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=",
+ "dev": true
+ },
+ "strip-json-comments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
+ "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=",
+ "dev": true
+ },
+ "subarg": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz",
+ "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.1.0"
+ }
+ },
+ "supports-color": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
+ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
+ "dev": true,
+ "requires": {
+ "has-flag": "^1.0.0"
+ }
+ },
+ "syntax-error": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz",
+ "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==",
+ "dev": true,
+ "requires": {
+ "acorn-node": "^1.2.0"
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+ "dev": true
+ },
+ "through2": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-1.1.1.tgz",
+ "integrity": "sha1-CEfLxESfNAVXTb3M2buEG4OsNUU=",
+ "dev": true,
+ "requires": {
+ "readable-stream": ">=1.1.13-1 <1.2.0-0",
+ "xtend": ">=4.0.0 <4.1.0-0"
+ },
+ "dependencies": {
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+ "dev": true
+ }
+ }
+ },
+ "timers-browserify": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz",
+ "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=",
+ "dev": true,
+ "requires": {
+ "process": "~0.11.0"
+ },
+ "dependencies": {
+ "process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
+ "dev": true
+ }
+ }
+ },
+ "toidentifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
+ "dev": true
+ },
+ "tough-cookie": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
+ "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ip-regex": "^2.1.0",
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "tty-browserify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz",
+ "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==",
+ "dev": true
+ },
+ "tunnel-agent": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz",
+ "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=",
+ "dev": true,
+ "optional": true
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ },
+ "type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dev": true,
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ }
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "dev": true
+ },
+ "uglify-js": {
+ "version": "2.4.24",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.24.tgz",
+ "integrity": "sha1-+tV1XB4Vd2WLsG/5q25UjJW+vW4=",
+ "dev": true,
+ "requires": {
+ "async": "~0.2.6",
+ "source-map": "0.1.34",
+ "uglify-to-browserify": "~1.0.0",
+ "yargs": "~3.5.4"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.1.34",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz",
+ "integrity": "sha1-p8/omux7FoLDsZjQrPtH19CQVms=",
+ "dev": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ }
+ }
+ },
+ "uglify-to-browserify": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
+ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
+ "dev": true
+ },
+ "umd": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/umd/-/umd-2.1.0.tgz",
+ "integrity": "sha1-SmMHt2LxfwLSAbX6FU5nM5bCY88=",
+ "dev": true,
+ "requires": {
+ "rfile": "~1.0.0",
+ "ruglify": "~1.0.0",
+ "through": "~2.3.4",
+ "uglify-js": "~2.4.0"
+ }
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+ "dev": true
+ },
+ "url": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
+ "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
+ "dev": true,
+ "requires": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+ "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
+ "dev": true
+ }
+ }
+ },
+ "util": {
+ "version": "0.10.4",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
+ "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "vm-browserify": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
+ "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
+ "dev": true,
+ "requires": {
+ "indexof": "0.0.1"
+ }
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "window-size": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
+ "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
+ "dev": true
+ },
+ "wordwrap": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "xtend": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz",
+ "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=",
+ "dev": true
+ },
+ "yargs": {
+ "version": "3.5.4",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz",
+ "integrity": "sha1-2K/49mXpTDS9JZvevRv68N3TU2E=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^1.0.2",
+ "decamelize": "^1.0.0",
+ "window-size": "0.1.0",
+ "wordwrap": "0.0.2"
+ },
+ "dependencies": {
+ "wordwrap": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+ "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+ "dev": true
+ }
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..33f696c
--- /dev/null
+++ b/package.json
@@ -0,0 +1,78 @@
+{
+ "name": "bluebird",
+ "description": "Full featured Promises/A+ implementation with exceptionally good performance",
+ "version": "3.7.2",
+ "keywords": [
+ "promise",
+ "performance",
+ "promises",
+ "promises-a",
+ "promises-aplus",
+ "async",
+ "await",
+ "deferred",
+ "deferreds",
+ "future",
+ "flow control",
+ "dsl",
+ "fluent interface"
+ ],
+ "scripts": {
+ "lint": "node scripts/jshint.js",
+ "test": "node --expose-gc tools/test.js",
+ "istanbul": "istanbul",
+ "prepublish": "npm run generate-browser-core && npm run generate-browser-full",
+ "generate-browser-full": "node tools/build.js --no-clean --no-debug --release --browser --minify",
+ "generate-browser-core": "node tools/build.js --features=core --no-debug --release --zalgo --browser --minify && mv js/browser/bluebird.js js/browser/bluebird.core.js && mv js/browser/bluebird.min.js js/browser/bluebird.core.min.js"
+ },
+ "homepage": "https://github.com/petkaantonov/bluebird",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/petkaantonov/bluebird.git"
+ },
+ "bugs": {
+ "url": "http://github.com/petkaantonov/bluebird/issues"
+ },
+ "license": "MIT",
+ "author": {
+ "name": "Petka Antonov",
+ "email": "petka_antonov@hotmail.com",
+ "url": "http://github.com/petkaantonov/"
+ },
+ "devDependencies": {
+ "acorn": "^6.0.2",
+ "acorn-walk": "^6.1.0",
+ "baconjs": "^0.7.43",
+ "bluebird": "^2.9.2",
+ "body-parser": "^1.10.2",
+ "browserify": "^8.1.1",
+ "cli-table": "~0.3.1",
+ "co": "^4.2.0",
+ "cross-spawn": "^0.2.3",
+ "glob": "^4.3.2",
+ "grunt-saucelabs": "~8.4.1",
+ "highland": "^2.3.0",
+ "istanbul": "^0.3.5",
+ "jshint": "^2.6.0",
+ "jshint-stylish": "~0.2.0",
+ "kefir": "^2.4.1",
+ "mkdirp": "~0.5.0",
+ "mocha": "~2.1",
+ "open": "~0.0.5",
+ "optimist": "~0.6.1",
+ "rimraf": "~2.2.6",
+ "rx": "^2.3.25",
+ "serve-static": "^1.7.1",
+ "sinon": "~1.7.3",
+ "uglify-js": "~2.4.16"
+ },
+ "readmeFilename": "README.md",
+ "main": "./js/release/bluebird.js",
+ "webpack": "./js/release/bluebird.js",
+ "browser": "./js/browser/bluebird.js",
+ "files": [
+ "js/browser",
+ "js/release",
+ "LICENSE"
+ ]
+}
diff --git a/src/any.js b/src/any.js
new file mode 100644
index 0000000..7b4d74b
--- /dev/null
+++ b/src/any.js
@@ -0,0 +1,25 @@
+"use strict";
+module.exports = function(Promise) {
+var SomePromiseArray = Promise._SomePromiseArray;
+var ASSERT = require("./assert");
+
+function any(promises) {
+ var ret = new SomePromiseArray(promises);
+ var promise = ret.promise();
+ ASSERT(promise.isPending());
+ ASSERT(ret instanceof SomePromiseArray);
+ ret.setHowMany(1);
+ ret.setUnwrap();
+ ret.init();
+ return promise;
+}
+
+Promise.any = function (promises) {
+ return any(promises);
+};
+
+Promise.prototype.any = function () {
+ return any(this);
+};
+
+};
diff --git a/src/assert.js b/src/assert.js
new file mode 100644
index 0000000..c865847
--- /dev/null
+++ b/src/assert.js
@@ -0,0 +1,55 @@
+"use strict";
+module.exports = (function(){
+var AssertionError = (function() {
+ function AssertionError(a) {
+ this.constructor$(a);
+ this.message = a;
+ this.name = "AssertionError";
+ }
+ AssertionError.prototype = new Error();
+ AssertionError.prototype.constructor = AssertionError;
+ AssertionError.prototype.constructor$ = Error;
+ return AssertionError;
+})();
+
+function getParams(args) {
+ var params = [];
+ for (var i = 0; i < args.length; ++i) params.push("arg" + i);
+ return params;
+}
+
+function nativeAssert(callName, args, expect) {
+ try {
+ var params = getParams(args);
+ var constructorArgs = params;
+ constructorArgs.push("return " +
+ callName + "("+ params.join(",") + ");");
+ var fn = Function.apply(null, constructorArgs);
+ return fn.apply(null, args);
+ } catch (e) {
+ if (!(e instanceof SyntaxError)) {
+ throw e;
+ } else {
+ return expect;
+ }
+ }
+}
+
+return function assert(boolExpr, message) {
+ if (boolExpr === true) return;
+
+ if (typeof boolExpr === "string" &&
+ boolExpr.charAt(0) === "%") {
+ var nativeCallName = boolExpr;
+ INLINE_SLICE(args, arguments, 2);
+ if (nativeAssert(nativeCallName, args, message) === message) return;
+ message = (nativeCallName + " !== " + message);
+ }
+
+ var ret = new AssertionError(message);
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(ret, assert);
+ }
+ throw ret;
+};
+})();
diff --git a/src/async.js b/src/async.js
new file mode 100644
index 0000000..98873ff
--- /dev/null
+++ b/src/async.js
@@ -0,0 +1,129 @@
+"use strict";
+var firstLineError;
+try {throw new Error(); } catch (e) {firstLineError = e;}
+var ASSERT = require("./assert");
+var schedule = require("./schedule");
+var Queue = require("./queue");
+
+function Async() {
+ this._customScheduler = false;
+ this._isTickUsed = false;
+ this._lateQueue = new Queue(LATE_QUEUE_CAPACITY);
+ this._normalQueue = new Queue(NORMAL_QUEUE_CAPACITY);
+ this._haveDrainedQueues = false;
+ var self = this;
+ this.drainQueues = function () {
+ self._drainQueues();
+ };
+ this._schedule = schedule;
+}
+
+Async.prototype.setScheduler = function(fn) {
+ var prev = this._schedule;
+ this._schedule = fn;
+ this._customScheduler = true;
+ return prev;
+};
+
+Async.prototype.hasCustomScheduler = function() {
+ return this._customScheduler;
+};
+
+Async.prototype.haveItemsQueued = function () {
+ return this._isTickUsed || this._haveDrainedQueues;
+};
+
+
+Async.prototype.fatalError = function(e, isNode) {
+ if (isNode) {
+ process.stderr.write("Fatal " + (e instanceof Error ? e.stack : e) +
+ "\n");
+ process.exit(2);
+ } else {
+ this.throwLater(e);
+ }
+};
+
+// Must be used if fn can throw
+Async.prototype.throwLater = function(fn, arg) {
+ if (arguments.length === 1) {
+ arg = fn;
+ fn = function () { throw arg; };
+ }
+ if (typeof setTimeout !== "undefined") {
+ setTimeout(function() {
+ fn(arg);
+ }, 0);
+ } else try {
+ this._schedule(function() {
+ fn(arg);
+ });
+ } catch (e) {
+ throw new Error(NO_ASYNC_SCHEDULER);
+ }
+};
+
+//When the fn absolutely needs to be called after
+//the queue has been completely flushed
+function AsyncInvokeLater(fn, receiver, arg) {
+ ASSERT(arguments.length === 3);
+ this._lateQueue.push(fn, receiver, arg);
+ this._queueTick();
+}
+
+function AsyncInvoke(fn, receiver, arg) {
+ ASSERT(arguments.length === 3);
+ this._normalQueue.push(fn, receiver, arg);
+ this._queueTick();
+}
+
+function AsyncSettlePromises(promise) {
+ this._normalQueue._pushOne(promise);
+ this._queueTick();
+}
+
+Async.prototype.invokeLater = AsyncInvokeLater;
+Async.prototype.invoke = AsyncInvoke;
+Async.prototype.settlePromises = AsyncSettlePromises;
+
+
+function _drainQueue(queue) {
+ while (queue.length() > 0) {
+ _drainQueueStep(queue);
+ }
+}
+
+// Shift the queue in a separate function to allow
+// garbage collection after each step
+function _drainQueueStep(queue) {
+ var fn = queue.shift();
+ if (typeof fn !== "function") {
+ fn._settlePromises();
+ } else {
+ var receiver = queue.shift();
+ var arg = queue.shift();
+ fn.call(receiver, arg);
+ }
+}
+
+Async.prototype._drainQueues = function () {
+ ASSERT(this._isTickUsed);
+ _drainQueue(this._normalQueue);
+ this._reset();
+ this._haveDrainedQueues = true;
+ _drainQueue(this._lateQueue);
+};
+
+Async.prototype._queueTick = function () {
+ if (!this._isTickUsed) {
+ this._isTickUsed = true;
+ this._schedule(this.drainQueues);
+ }
+};
+
+Async.prototype._reset = function () {
+ this._isTickUsed = false;
+};
+
+module.exports = Async;
+module.exports.firstLineError = firstLineError;
diff --git a/src/bind.js b/src/bind.js
new file mode 100644
index 0000000..0a5cf9f
--- /dev/null
+++ b/src/bind.js
@@ -0,0 +1,67 @@
+"use strict";
+module.exports = function(Promise, INTERNAL, tryConvertToPromise, debug) {
+var calledBind = false;
+var rejectThis = function(_, e) {
+ this._reject(e);
+};
+
+var targetRejected = function(e, context) {
+ context.promiseRejectionQueued = true;
+ context.bindingPromise._then(rejectThis, rejectThis, null, this, e);
+};
+
+var bindingResolved = function(thisArg, context) {
+ if (BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG, this._bitField)) {
+ this._resolveCallback(context.target);
+ }
+};
+
+var bindingRejected = function(e, context) {
+ if (!context.promiseRejectionQueued) this._reject(e);
+};
+
+Promise.prototype.bind = function (thisArg) {
+ if (!calledBind) {
+ calledBind = true;
+ Promise.prototype._propagateFrom = debug.propagateFromFunction();
+ Promise.prototype._boundValue = debug.boundValueFunction();
+ }
+ var maybePromise = tryConvertToPromise(thisArg);
+ var ret = new Promise(INTERNAL);
+ ret._propagateFrom(this, PROPAGATE_CANCEL);
+ var target = this._target();
+ ret._setBoundTo(maybePromise);
+ if (maybePromise instanceof Promise) {
+ var context = {
+ promiseRejectionQueued: false,
+ promise: ret,
+ target: target,
+ bindingPromise: maybePromise
+ };
+ target._then(INTERNAL, targetRejected, undefined, ret, context);
+ maybePromise._then(
+ bindingResolved, bindingRejected, undefined, ret, context);
+ ret._setOnCancel(maybePromise);
+ } else {
+ ret._resolveCallback(target);
+ }
+ return ret;
+};
+
+Promise.prototype._setBoundTo = function (obj) {
+ if (obj !== undefined) {
+ this._bitField = this._bitField | IS_BOUND;
+ this._boundTo = obj;
+ } else {
+ this._bitField = this._bitField & (~IS_BOUND);
+ }
+};
+
+Promise.prototype._isBound = function () {
+ return (this._bitField & IS_BOUND) === IS_BOUND;
+};
+
+Promise.bind = function (thisArg, value) {
+ return Promise.resolve(value).bind(thisArg);
+};
+};
diff --git a/src/bluebird.js b/src/bluebird.js
new file mode 100644
index 0000000..1c36cf3
--- /dev/null
+++ b/src/bluebird.js
@@ -0,0 +1,11 @@
+"use strict";
+var old;
+if (typeof Promise !== "undefined") old = Promise;
+function noConflict() {
+ try { if (Promise === bluebird) Promise = old; }
+ catch (e) {}
+ return bluebird;
+}
+var bluebird = require("./promise")();
+bluebird.noConflict = noConflict;
+module.exports = bluebird;
diff --git a/src/call_get.js b/src/call_get.js
new file mode 100644
index 0000000..2a5a2e1
--- /dev/null
+++ b/src/call_get.js
@@ -0,0 +1,123 @@
+"use strict";
+var cr = Object.create;
+if (cr) {
+ var callerCache = cr(null);
+ var getterCache = cr(null);
+ callerCache[" size"] = getterCache[" size"] = 0;
+}
+
+module.exports = function(Promise) {
+var util = require("./util");
+var canEvaluate = util.canEvaluate;
+var isIdentifier = util.isIdentifier;
+
+var getMethodCaller;
+var getGetter;
+if (!__BROWSER__) {
+var makeMethodCaller = function (methodName) {
+ return new Function("ensureMethod", " \n\
+ return function(obj) { \n\
+ 'use strict' \n\
+ var len = this.length; \n\
+ ensureMethod(obj, 'methodName'); \n\
+ switch(len) { \n\
+ case 1: return obj.methodName(this[0]); \n\
+ case 2: return obj.methodName(this[0], this[1]); \n\
+ case 3: return obj.methodName(this[0], this[1], this[2]); \n\
+ case 0: return obj.methodName(); \n\
+ default: \n\
+ return obj.methodName.apply(obj, this); \n\
+ } \n\
+ }; \n\
+ ".replace(/methodName/g, methodName))(ensureMethod);
+};
+
+var makeGetter = function (propertyName) {
+ return new Function("obj", " \n\
+ 'use strict'; \n\
+ return obj.propertyName; \n\
+ ".replace("propertyName", propertyName));
+};
+
+var getCompiled = function(name, compiler, cache) {
+ var ret = cache[name];
+ if (typeof ret !== "function") {
+ if (!isIdentifier(name)) {
+ return null;
+ }
+ ret = compiler(name);
+ cache[name] = ret;
+ cache[" size"]++;
+ if (cache[" size"] > 512) {
+ var keys = Object.keys(cache);
+ for (var i = 0; i < 256; ++i) delete cache[keys[i]];
+ cache[" size"] = keys.length - 256;
+ }
+ }
+ return ret;
+};
+
+getMethodCaller = function(name) {
+ return getCompiled(name, makeMethodCaller, callerCache);
+};
+
+getGetter = function(name) {
+ return getCompiled(name, makeGetter, getterCache);
+};
+}
+
+function ensureMethod(obj, methodName) {
+ var fn;
+ if (obj != null) fn = obj[methodName];
+ if (typeof fn !== "function") {
+ var message = "Object " + util.classString(obj) + " has no method '" +
+ util.toString(methodName) + "'";
+ throw new Promise.TypeError(message);
+ }
+ return fn;
+}
+
+function caller(obj) {
+ var methodName = this.pop();
+ var fn = ensureMethod(obj, methodName);
+ return fn.apply(obj, this);
+}
+Promise.prototype.call = function (methodName) {
+ INLINE_SLICE(args, arguments, 1);
+ if (!__BROWSER__) {
+ if (canEvaluate) {
+ var maybeCaller = getMethodCaller(methodName);
+ if (maybeCaller !== null) {
+ return this._then(
+ maybeCaller, undefined, undefined, args, undefined);
+ }
+ }
+ }
+ args.push(methodName);
+ return this._then(caller, undefined, undefined, args, undefined);
+};
+
+function namedGetter(obj) {
+ return obj[this];
+}
+function indexedGetter(obj) {
+ var index = +this;
+ if (index < 0) index = Math.max(0, index + obj.length);
+ return obj[index];
+}
+Promise.prototype.get = function (propertyName) {
+ var isIndex = (typeof propertyName === "number");
+ var getter;
+ if (!isIndex) {
+ if (canEvaluate) {
+ var maybeGetter = getGetter(propertyName);
+ getter = maybeGetter !== null ? maybeGetter : namedGetter;
+ } else {
+ getter = namedGetter;
+ }
+ } else {
+ getter = indexedGetter;
+ }
+ return this._then(getter, undefined, undefined, propertyName, undefined);
+};
+};
diff --git a/src/cancel.js b/src/cancel.js
new file mode 100644
index 0000000..2328b54
--- /dev/null
+++ b/src/cancel.js
@@ -0,0 +1,136 @@
+"use strict";
+module.exports = function(Promise, PromiseArray, apiRejection, debug) {
+var ASSERT = require("./assert");
+var util = require("./util");
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+var async = Promise._async;
+
+Promise.prototype["break"] = Promise.prototype.cancel = function() {
+ if (!debug.cancellation()) return this._warn("cancellation is disabled");
+
+ var promise = this;
+ var child = promise;
+ while (promise._isCancellable()) {
+ if (!promise._cancelBy(child)) {
+ if (child._isFollowing()) {
+ child._followee().cancel();
+ } else {
+ child._cancelBranched();
+ }
+ break;
+ }
+
+ var parent = promise._cancellationParent;
+ if (parent == null || !parent._isCancellable()) {
+ if (promise._isFollowing()) {
+ promise._followee().cancel();
+ } else {
+ promise._cancelBranched();
+ }
+ break;
+ } else {
+ if (promise._isFollowing()) promise._followee().cancel();
+ promise._setWillBeCancelled();
+ child = promise;
+ promise = parent;
+ }
+ }
+};
+
+Promise.prototype._branchHasCancelled = function() {
+ ASSERT(typeof this._branchesRemainingToCancel === "number");
+ this._branchesRemainingToCancel--;
+};
+
+Promise.prototype._enoughBranchesHaveCancelled = function() {
+ return this._branchesRemainingToCancel === undefined ||
+ this._branchesRemainingToCancel <= 0;
+};
+
+Promise.prototype._cancelBy = function(canceller) {
+ if (canceller === this) {
+ this._branchesRemainingToCancel = 0;
+ this._invokeOnCancel();
+ return true;
+ } else {
+ ASSERT(canceller._cancellationParent === this);
+ this._branchHasCancelled();
+ if (this._enoughBranchesHaveCancelled()) {
+ this._invokeOnCancel();
+ return true;
+ }
+ }
+ return false;
+};
+
+Promise.prototype._cancelBranched = function() {
+ if (this._enoughBranchesHaveCancelled()) {
+ this._cancel();
+ }
+};
+
+Promise.prototype._cancel = function() {
+ if (!this._isCancellable()) return;
+ ASSERT(!this._isFollowing());
+ this._setCancelled();
+ async.invoke(this._cancelPromises, this, undefined);
+};
+
+Promise.prototype._cancelPromises = function() {
+ if (this._length() > 0) this._settlePromises();
+};
+
+Promise.prototype._unsetOnCancel = function() {
+ ASSERT(this._isCancellable() || this._isCancelled());
+ this._onCancelField = undefined;
+};
+
+Promise.prototype._isCancellable = function() {
+ return this.isPending() && !this._isCancelled();
+};
+
+Promise.prototype.isCancellable = function() {
+ return this.isPending() && !this.isCancelled();
+};
+
+Promise.prototype._doInvokeOnCancel = function(onCancelCallback, internalOnly) {
+ if (util.isArray(onCancelCallback)) {
+ for (var i = 0; i < onCancelCallback.length; ++i) {
+ this._doInvokeOnCancel(onCancelCallback[i], internalOnly);
+ }
+ } else if (onCancelCallback !== undefined) {
+ if (typeof onCancelCallback === "function") {
+ if (!internalOnly) {
+ var e = tryCatch(onCancelCallback).call(this._boundValue());
+ if (e === errorObj) {
+ this._attachExtraTrace(e.e);
+ async.throwLater(e.e);
+ }
+ }
+ } else {
+ onCancelCallback._resultCancelled(this);
+ }
+ }
+};
+
+Promise.prototype._invokeOnCancel = function() {
+ var onCancelCallback = this._onCancel();
+ // The existence of onCancel handler on a promise signals that the handler
+ // has not been queued for invocation yet.
+ this._unsetOnCancel();
+ async.invoke(this._doInvokeOnCancel, this, onCancelCallback);
+};
+
+Promise.prototype._invokeInternalOnCancel = function() {
+ if (this._isCancellable()) {
+ this._doInvokeOnCancel(this._onCancel(), true);
+ this._unsetOnCancel();
+ }
+};
+
+Promise.prototype._resultCancelled = function() {
+ this.cancel();
+};
+
+};
diff --git a/src/catch_filter.js b/src/catch_filter.js
new file mode 100644
index 0000000..0f24ce2
--- /dev/null
+++ b/src/catch_filter.js
@@ -0,0 +1,42 @@
+"use strict";
+module.exports = function(NEXT_FILTER) {
+var util = require("./util");
+var getKeys = require("./es5").keys;
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+
+function catchFilter(instances, cb, promise) {
+ return function(e) {
+ var boundTo = promise._boundValue();
+ predicateLoop: for (var i = 0; i < instances.length; ++i) {
+ var item = instances[i];
+
+ if (item === Error ||
+ (item != null && item.prototype instanceof Error)) {
+ if (e instanceof item) {
+ return tryCatch(cb).call(boundTo, e);
+ }
+ } else if (typeof item === "function") {
+ var matchesPredicate = tryCatch(item).call(boundTo, e);
+ if (matchesPredicate === errorObj) {
+ return matchesPredicate;
+ } else if (matchesPredicate) {
+ return tryCatch(cb).call(boundTo, e);
+ }
+ } else if (util.isObject(e)) {
+ var keys = getKeys(item);
+ for (var j = 0; j < keys.length; ++j) {
+ var key = keys[j];
+ if (item[key] != e[key]) {
+ continue predicateLoop;
+ }
+ }
+ return tryCatch(cb).call(boundTo, e);
+ }
+ }
+ return NEXT_FILTER;
+ };
+}
+
+return catchFilter;
+};
diff --git a/src/constants.js b/src/constants.js
new file mode 100644
index 0000000..a011036
--- /dev/null
+++ b/src/constants.js
@@ -0,0 +1,154 @@
+//This is pretty lame but what you gonna do
+
+//async.js
+CONSTANT(LATE_QUEUE_CAPACITY, 16);
+CONSTANT(NORMAL_QUEUE_CAPACITY, 16);
+
+//errors.js
+CONSTANT(ERROR_HANDLED_KEY, "__promiseHandled__");
+CONSTANT(OPERATIONAL_ERROR_KEY, "isOperational");
+CONSTANT(DEFAULT_STATE, 0);
+CONSTANT(STACK_ATTACHED, 1);
+CONSTANT(ERROR_HANDLED, 2);
+
+//join.js
+CONSTANT(GENERATED_CLASS_COUNT, 8);
+
+//promise.js
+CONSTANT(USE_BOUND, true);
+CONSTANT(DONT_USE_BOUND, false);
+
+CONSTANT(PROPAGATE_CANCEL, 1);
+CONSTANT(PROPAGATE_BIND, 2);
+CONSTANT(PROPAGATE_ALL, PROPAGATE_CANCEL | PROPAGATE_BIND);
+
+CONSTANT(CALLBACK_FULFILL_OFFSET, 0);
+CONSTANT(CALLBACK_REJECT_OFFSET, 1);
+CONSTANT(CALLBACK_PROMISE_OFFSET, 2);
+CONSTANT(CALLBACK_RECEIVER_OFFSET, 3);
+CONSTANT(CALLBACK_SIZE, 4);
+//Layout for ._bitField
+//[RR]XO GWFN CTBH IUDE LLLL LLLL LLLL LLLL
+//[RR] = [Reserved] (Both bits are either on or off to represent
+// 1 bit due to 31-bit integers in 32-bit v8)
+//R = [Reserved]
+//X = noAsyncGuarantee
+//O = returnedNonUndefined
+//G = isAsyncGuaranteed
+//W = isFollowing (The promise that is being followed is not stored explicitly)
+//F = isFulfilled
+//N = isRejected
+//C = willBeCancelled
+//T = isFinal (used for .done() implementation)
+//B = isBound
+//I = isRejectionIgnored
+//H = isRejectionUnhandled
+//U = isUnhanldedRejectionNotified
+//D = isDisposable
+//E = isCancelled
+//L = Length, 16 bit unsigned
+CONSTANT(ASYNC_GUARANTEE_SHIFT, 2)
+CONSTANT(NO_STATE, 0x0|0);
+CONSTANT(NO_ASYNC_GUARANTEE, 0x20000000|0);
+CONSTANT(RETURNED_NON_UNDEFINED, 0x10000000|0);
+CONSTANT(IS_ASYNC_GUARANTEED, 0x8000000|0);
+CONSTANT(IS_FOLLOWING, 0x4000000|0);
+CONSTANT(IS_FULFILLED, 0x2000000|0);
+CONSTANT(IS_REJECTED, 0x1000000|0);
+CONSTANT(WILL_BE_CANCELLED, 0x800000|0);
+CONSTANT(IS_FINAL, 0x400000|0);
+CONSTANT(IS_BOUND, 0x200000|0);
+CONSTANT(IS_REJECTION_UNHANDLED, 0x100000|0);
+CONSTANT(IS_REJECTION_IGNORED, 0x80000|0);
+CONSTANT(IS_UNHANDLED_REJECTION_NOTIFIED, 0x40000|0);
+CONSTANT(IS_DISPOSABLE, 0x20000|0);
+CONSTANT(IS_CANCELLED, 0x10000|0);
+CONSTANT(IS_CANCELLED_OR_WILL_BE_CANCELLED, IS_CANCELLED | WILL_BE_CANCELLED)
+CONSTANT(LENGTH_MASK, 0xFFFF|0);
+CONSTANT(LENGTH_CLEAR_MASK, ~LENGTH_MASK);
+CONSTANT(MAX_LENGTH, LENGTH_MASK);
+CONSTANT(IS_REJECTED_OR_CANCELLED, IS_REJECTED |Â IS_CANCELLED);
+CONSTANT(IS_REJECTED_OR_FULFILLED, IS_REJECTED |Â IS_FULFILLED);
+CONSTANT(IS_REJECTED_OR_FULFILLED_OR_CANCELLED, IS_REJECTED |Â IS_FULFILLED| IS_CANCELLED);
+CONSTANT(IS_PENDING_AND_WAITING_NEG, IS_REJECTED_OR_FULFILLED_OR_CANCELLED);
+
+CONSTANT(IS_FATE_SEALED, IS_REJECTED | IS_FULFILLED | IS_FOLLOWING | IS_CANCELLED);
+
+CONSTANT(AFTER_PROMISIFIED_SUFFIX, "Async");
+CONSTANT(UNHANDLED_REJECTION_EVENT, "unhandledRejection");
+CONSTANT(REJECTION_HANDLED_EVENT, "rejectionHandled");
+
+//promise_array.js
+//MUST BE NEGATIVE NUMBERS
+CONSTANT(RESOLVE_UNDEFINED, -1);
+CONSTANT(RESOLVE_ARRAY, -2);
+CONSTANT(RESOLVE_OBJECT, -3);
+CONSTANT(RESOLVE_FOREVER_PENDING, -4);
+CONSTANT(RESOLVE_CALL_METHOD, -5);
+CONSTANT(RESOLVE_MAP, -6);
+
+//queue.js
+CONSTANT(QUEUE_MAX_CAPACITY, (1 << 30) | 0);
+CONSTANT(QUEUE_MIN_CAPACITY, 16);
+
+//captured_trace.js
+CONSTANT(FROM_PREVIOUS_EVENT, "From previous event:");
+CONSTANT(NO_STACK_TRACE, " (No stack trace)");
+CONSTANT(ADDITIONAL_STACK_TRACE, "^--- With additional stack trace: ");
+CONSTANT(UNHANDLED_REJECTION_HEADER, "Unhandled rejection ");
+
+//finally.js
+CONSTANT(FINALLY_TYPE, 0);
+CONSTANT(TAP_TYPE, 1);
+
+//direct_resolve.js
+CONSTANT(THROW, 1);
+CONSTANT(RETURN, 2);
+
+//promisify.js
+CONSTANT(MAX_PARAM_COUNT, 1023);
+CONSTANT(PARAM_COUNTS_TO_TRY, 3);
+
+//error.js
+CONSTANT(BLUEBIRD_ERRORS, "__BluebirdErrorTypes__");
+
+//deprecated
+CONSTANT(OBJECT_PROMISIFY_DEPRECATED, "Promise.promisify for promisifying entire objects is deprecated. Use Promise.promisifyAll instead.\n\n\
+ See http://goo.gl/MqrFmX");
+CONSTANT(SPAWN_DEPRECATED, "Promise.spawn is deprecated. Use Promise.coroutine instead.");
+
+//errors
+CONSTANT(LATE_CANCELLATION_OBSERVER, "late cancellation observer");
+CONSTANT(TIMEOUT_ERROR, "operation timed out");
+CONSTANT(COLLECTION_ERROR, "expecting an array or an iterable object but got ");
+CONSTANT(OBJECT_ERROR, "expecting an object but got ");
+CONSTANT(FUNCTION_ERROR, "expecting a function but got ");
+CONSTANT(CONSTRUCT_ERROR_INVOCATION, "the promise constructor cannot be invoked directly\n\n\
+ See http://goo.gl/MqrFmX\n");
+CONSTANT(NOT_GENERATOR_ERROR, "generatorFunction must be a function\n\n\
+ See http://goo.gl/MqrFmX\n");
+CONSTANT(LONG_STACK_TRACES_ERROR, "cannot enable long stack traces after promises have been created\n\n\
+ See http://goo.gl/MqrFmX\n");
+CONSTANT(INSPECTION_VALUE_ERROR, "cannot get fulfillment value of a non-fulfilled promise\n\n\
+ See http://goo.gl/MqrFmX\n");
+CONSTANT(INSPECTION_REASON_ERROR, "cannot get rejection reason of a non-rejected promise\n\n\
+ See http://goo.gl/MqrFmX\n");
+CONSTANT(PROMISIFY_TYPE_ERROR, "the target of promisifyAll must be an object or a function\n\n\
+ See http://goo.gl/MqrFmX\n");
+CONSTANT(CIRCULAR_RESOLUTION_ERROR, "circular promise resolution chain\n\n\
+ See http://goo.gl/MqrFmX\n");
+CONSTANT(PROPS_TYPE_ERROR, "cannot await properties of a non-object\n\n\
+ See http://goo.gl/MqrFmX\n");
+CONSTANT(POSITIVE_INTEGER_ERROR, "expecting a positive integer\n\n\
+ See http://goo.gl/MqrFmX\n");
+CONSTANT(YIELDED_NON_PROMISE_ERROR, "A value %s was yielded that could not be treated as a promise\n\n\
+ See http://goo.gl/MqrFmX\n\n");
+CONSTANT(FROM_COROUTINE_CREATED_AT, "From coroutine:\n");
+CONSTANT(UNBOUND_RESOLVER_INVOCATION, "Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead.\n\n\
+ See http://goo.gl/MqrFmX\n");
+CONSTANT(PROMISIFICATION_NORMAL_METHODS_ERROR, "Cannot promisify an API that has normal methods with '%s'-suffix\n\n\
+ See http://goo.gl/MqrFmX\n");
+CONSTANT(SUFFIX_NOT_IDENTIFIER, "suffix must be a valid identifier\n\n\
+ See http://goo.gl/MqrFmX\n");
+CONSTANT(NO_ASYNC_SCHEDULER, "No async scheduler available\n\n\
+ See http://goo.gl/MqrFmX\n");
diff --git a/src/context.js b/src/context.js
new file mode 100644
index 0000000..c307414
--- /dev/null
+++ b/src/context.js
@@ -0,0 +1,69 @@
+"use strict";
+module.exports = function(Promise) {
+var longStackTraces = false;
+var contextStack = [];
+
+Promise.prototype._promiseCreated = function() {};
+Promise.prototype._pushContext = function() {};
+Promise.prototype._popContext = function() {return null;};
+Promise._peekContext = Promise.prototype._peekContext = function() {};
+
+function Context() {
+ this._trace = new Context.CapturedTrace(peekContext());
+}
+Context.prototype._pushContext = function () {
+ if (this._trace !== undefined) {
+ this._trace._promiseCreated = null;
+ contextStack.push(this._trace);
+ }
+};
+
+Context.prototype._popContext = function () {
+ if (this._trace !== undefined) {
+ var trace = contextStack.pop();
+ var ret = trace._promiseCreated;
+ trace._promiseCreated = null;
+ return ret;
+ }
+ return null;
+};
+
+function createContext() {
+ if (longStackTraces) return new Context();
+}
+
+function peekContext() {
+ var lastIndex = contextStack.length - 1;
+ if (lastIndex >= 0) {
+ return contextStack[lastIndex];
+ }
+ return undefined;
+}
+Context.CapturedTrace = null;
+Context.create = createContext;
+Context.deactivateLongStackTraces = function() {};
+Context.activateLongStackTraces = function() {
+ var Promise_pushContext = Promise.prototype._pushContext;
+ var Promise_popContext = Promise.prototype._popContext;
+ var Promise_PeekContext = Promise._peekContext;
+ var Promise_peekContext = Promise.prototype._peekContext;
+ var Promise_promiseCreated = Promise.prototype._promiseCreated;
+ Context.deactivateLongStackTraces = function() {
+ Promise.prototype._pushContext = Promise_pushContext;
+ Promise.prototype._popContext = Promise_popContext;
+ Promise._peekContext = Promise_PeekContext;
+ Promise.prototype._peekContext = Promise_peekContext;
+ Promise.prototype._promiseCreated = Promise_promiseCreated;
+ longStackTraces = false;
+ };
+ longStackTraces = true;
+ Promise.prototype._pushContext = Context.prototype._pushContext;
+ Promise.prototype._popContext = Context.prototype._popContext;
+ Promise._peekContext = Promise.prototype._peekContext = peekContext;
+ Promise.prototype._promiseCreated = function() {
+ var ctx = this._peekContext();
+ if (ctx && ctx._promiseCreated == null) ctx._promiseCreated = this;
+ };
+};
+return Context;
+};
diff --git a/src/debuggability.js b/src/debuggability.js
new file mode 100644
index 0000000..ba49deb
--- /dev/null
+++ b/src/debuggability.js
@@ -0,0 +1,1043 @@
+"use strict";
+module.exports = function(Promise, Context,
+ enableAsyncHooks, disableAsyncHooks) {
+var async = Promise._async;
+var Warning = require("./errors").Warning;
+var util = require("./util");
+var es5 = require("./es5");
+var ASSERT = require("./assert");
+var canAttachTrace = util.canAttachTrace;
+var unhandledRejectionHandled;
+var possiblyUnhandledRejection;
+var bluebirdFramePattern =
+ /[\\\/]bluebird[\\\/]js[\\\/](release|debug|instrumented)/;
+var nodeFramePattern = /\((?:timers\.js):\d+:\d+\)/;
+var parseLinePattern = /[\/<\(](.+?):(\d+):(\d+)\)?\s*$/;
+var stackFramePattern = null;
+var formatStack = null;
+var indentStackFrames = false;
+var printWarning;
+var debugging = !!(util.env("BLUEBIRD_DEBUG") != 0 &&
+ (__DEBUG__ ||
+ util.env("BLUEBIRD_DEBUG") ||
+ util.env("NODE_ENV") === "development"));
+
+var warnings = !!(util.env("BLUEBIRD_WARNINGS") != 0 &&
+ (debugging || util.env("BLUEBIRD_WARNINGS")));
+
+var longStackTraces = !!(util.env("BLUEBIRD_LONG_STACK_TRACES") != 0 &&
+ (debugging || util.env("BLUEBIRD_LONG_STACK_TRACES")));
+
+var wForgottenReturn = util.env("BLUEBIRD_W_FORGOTTEN_RETURN") != 0 &&
+ (warnings || !!util.env("BLUEBIRD_W_FORGOTTEN_RETURN"));
+
+var deferUnhandledRejectionCheck;
+(function() {
+ var promises = [];
+
+ function unhandledRejectionCheck() {
+ for (var i = 0; i < promises.length; ++i) {
+ promises[i]._notifyUnhandledRejection();
+ }
+ unhandledRejectionClear();
+ }
+
+ function unhandledRejectionClear() {
+ promises.length = 0;
+ }
+
+ deferUnhandledRejectionCheck = function(promise) {
+ promises.push(promise);
+ setTimeout(unhandledRejectionCheck, 1);
+ };
+
+ es5.defineProperty(Promise, "_unhandledRejectionCheck", {
+ value: unhandledRejectionCheck
+ });
+ es5.defineProperty(Promise, "_unhandledRejectionClear", {
+ value: unhandledRejectionClear
+ });
+})();
+
+Promise.prototype.suppressUnhandledRejections = function() {
+ var target = this._target();
+ target._bitField = ((target._bitField & (~IS_REJECTION_UNHANDLED)) |
+ IS_REJECTION_IGNORED);
+};
+
+Promise.prototype._ensurePossibleRejectionHandled = function () {
+ if ((this._bitField & IS_REJECTION_IGNORED) !== 0) return;
+ this._setRejectionIsUnhandled();
+ deferUnhandledRejectionCheck(this);
+};
+
+Promise.prototype._notifyUnhandledRejectionIsHandled = function () {
+ fireRejectionEvent(REJECTION_HANDLED_EVENT,
+ unhandledRejectionHandled, undefined, this);
+};
+
+Promise.prototype._setReturnedNonUndefined = function() {
+ this._bitField = this._bitField | RETURNED_NON_UNDEFINED;
+};
+
+Promise.prototype._returnedNonUndefined = function() {
+ return (this._bitField & RETURNED_NON_UNDEFINED) !== 0;
+};
+
+Promise.prototype._notifyUnhandledRejection = function () {
+ if (this._isRejectionUnhandled()) {
+ var reason = this._settledValue();
+ this._setUnhandledRejectionIsNotified();
+ fireRejectionEvent(UNHANDLED_REJECTION_EVENT,
+ possiblyUnhandledRejection, reason, this);
+ }
+};
+
+Promise.prototype._setUnhandledRejectionIsNotified = function () {
+ this._bitField = this._bitField | IS_UNHANDLED_REJECTION_NOTIFIED;
+};
+
+Promise.prototype._unsetUnhandledRejectionIsNotified = function () {
+ this._bitField = this._bitField & (~IS_UNHANDLED_REJECTION_NOTIFIED);
+};
+
+Promise.prototype._isUnhandledRejectionNotified = function () {
+ return (this._bitField & IS_UNHANDLED_REJECTION_NOTIFIED) > 0;
+};
+
+Promise.prototype._setRejectionIsUnhandled = function () {
+ ASSERT(!this._isFollowing());
+ this._bitField = this._bitField | IS_REJECTION_UNHANDLED;
+};
+
+Promise.prototype._unsetRejectionIsUnhandled = function () {
+ ASSERT(!this._isFollowing());
+ this._bitField = this._bitField & (~IS_REJECTION_UNHANDLED);
+ if (this._isUnhandledRejectionNotified()) {
+ this._unsetUnhandledRejectionIsNotified();
+ this._notifyUnhandledRejectionIsHandled();
+ }
+};
+
+Promise.prototype._isRejectionUnhandled = function () {
+ ASSERT(!this._isFollowing());
+ return (this._bitField & IS_REJECTION_UNHANDLED) > 0;
+};
+
+Promise.prototype._warn = function(message, shouldUseOwnTrace, promise) {
+ return warn(message, shouldUseOwnTrace, promise || this);
+};
+
+Promise.onPossiblyUnhandledRejection = function (fn) {
+ var context = Promise._getContext();
+ possiblyUnhandledRejection = util.contextBind(context, fn);
+};
+
+Promise.onUnhandledRejectionHandled = function (fn) {
+ var context = Promise._getContext();
+ unhandledRejectionHandled = util.contextBind(context, fn);
+};
+
+var disableLongStackTraces = function() {};
+Promise.longStackTraces = function () {
+ if (async.haveItemsQueued() && !config.longStackTraces) {
+ throw new Error(LONG_STACK_TRACES_ERROR);
+ }
+ if (!config.longStackTraces && longStackTracesIsSupported()) {
+ var Promise_captureStackTrace = Promise.prototype._captureStackTrace;
+ var Promise_attachExtraTrace = Promise.prototype._attachExtraTrace;
+ var Promise_dereferenceTrace = Promise.prototype._dereferenceTrace;
+ config.longStackTraces = true;
+ disableLongStackTraces = function() {
+ if (async.haveItemsQueued() && !config.longStackTraces) {
+ throw new Error(LONG_STACK_TRACES_ERROR);
+ }
+ Promise.prototype._captureStackTrace = Promise_captureStackTrace;
+ Promise.prototype._attachExtraTrace = Promise_attachExtraTrace;
+ Promise.prototype._dereferenceTrace = Promise_dereferenceTrace;
+ Context.deactivateLongStackTraces();
+ config.longStackTraces = false;
+ };
+ Promise.prototype._captureStackTrace = longStackTracesCaptureStackTrace;
+ Promise.prototype._attachExtraTrace = longStackTracesAttachExtraTrace;
+ Promise.prototype._dereferenceTrace = longStackTracesDereferenceTrace;
+ Context.activateLongStackTraces();
+ }
+};
+
+Promise.hasLongStackTraces = function () {
+ return config.longStackTraces && longStackTracesIsSupported();
+};
+
+
+var legacyHandlers = {
+ unhandledrejection: {
+ before: function() {
+ var ret = util.global.onunhandledrejection;
+ util.global.onunhandledrejection = null;
+ return ret;
+ },
+ after: function(fn) {
+ util.global.onunhandledrejection = fn;
+ }
+ },
+ rejectionhandled: {
+ before: function() {
+ var ret = util.global.onrejectionhandled;
+ util.global.onrejectionhandled = null;
+ return ret;
+ },
+ after: function(fn) {
+ util.global.onrejectionhandled = fn;
+ }
+ }
+};
+
+var fireDomEvent = (function() {
+ var dispatch = function(legacy, e) {
+ if (legacy) {
+ var fn;
+ try {
+ fn = legacy.before();
+ return !util.global.dispatchEvent(e);
+ } finally {
+ legacy.after(fn);
+ }
+ } else {
+ return !util.global.dispatchEvent(e);
+ }
+ };
+ try {
+ if (typeof CustomEvent === "function") {
+ var event = new CustomEvent("CustomEvent");
+ util.global.dispatchEvent(event);
+ return function(name, event) {
+ name = name.toLowerCase();
+ var eventData = {
+ detail: event,
+ cancelable: true
+ };
+ var domEvent = new CustomEvent(name, eventData);
+ es5.defineProperty(
+ domEvent, "promise", {value: event.promise});
+ es5.defineProperty(
+ domEvent, "reason", {value: event.reason});
+
+ return dispatch(legacyHandlers[name], domEvent);
+ };
+ // In Firefox < 48 CustomEvent is not available in workers but
+ // Event is.
+ } else if (typeof Event === "function") {
+ var event = new Event("CustomEvent");
+ util.global.dispatchEvent(event);
+ return function(name, event) {
+ name = name.toLowerCase();
+ var domEvent = new Event(name, {
+ cancelable: true
+ });
+ domEvent.detail = event;
+ es5.defineProperty(domEvent, "promise", {value: event.promise});
+ es5.defineProperty(domEvent, "reason", {value: event.reason});
+ return dispatch(legacyHandlers[name], domEvent);
+ };
+ } else {
+ var event = document.createEvent("CustomEvent");
+ event.initCustomEvent("testingtheevent", false, true, {});
+ util.global.dispatchEvent(event);
+ return function(name, event) {
+ name = name.toLowerCase();
+ var domEvent = document.createEvent("CustomEvent");
+ domEvent.initCustomEvent(name, false, true,
+ event);
+ return dispatch(legacyHandlers[name], domEvent);
+ };
+ }
+ } catch (e) {}
+ return function() {
+ return false;
+ };
+})();
+
+var fireGlobalEvent = (function() {
+ if (util.isNode) {
+ return function() {
+ return process.emit.apply(process, arguments);
+ };
+ } else {
+ if (!util.global) {
+ return function() {
+ return false;
+ };
+ }
+ return function(name) {
+ var methodName = "on" + name.toLowerCase();
+ var method = util.global[methodName];
+ if (!method) return false;
+ method.apply(util.global, [].slice.call(arguments, 1));
+ return true;
+ };
+ }
+})();
+
+function generatePromiseLifecycleEventObject(name, promise) {
+ return {promise: promise};
+}
+
+var eventToObjectGenerator = {
+ promiseCreated: generatePromiseLifecycleEventObject,
+ promiseFulfilled: generatePromiseLifecycleEventObject,
+ promiseRejected: generatePromiseLifecycleEventObject,
+ promiseResolved: generatePromiseLifecycleEventObject,
+ promiseCancelled: generatePromiseLifecycleEventObject,
+ promiseChained: function(name, promise, child) {
+ return {promise: promise, child: child};
+ },
+ warning: function(name, warning) {
+ return {warning: warning};
+ },
+ unhandledRejection: function (name, reason, promise) {
+ return {reason: reason, promise: promise};
+ },
+ rejectionHandled: generatePromiseLifecycleEventObject
+};
+
+var activeFireEvent = function (name) {
+ var globalEventFired = false;
+ try {
+ globalEventFired = fireGlobalEvent.apply(null, arguments);
+ } catch (e) {
+ async.throwLater(e);
+ globalEventFired = true;
+ }
+
+ var domEventFired = false;
+ try {
+ domEventFired = fireDomEvent(name,
+ eventToObjectGenerator[name].apply(null, arguments));
+ } catch (e) {
+ async.throwLater(e);
+ domEventFired = true;
+ }
+
+ return domEventFired || globalEventFired;
+};
+
+Promise.config = function(opts) {
+ opts = Object(opts);
+ if ("longStackTraces" in opts) {
+ if (opts.longStackTraces) {
+ Promise.longStackTraces();
+ } else if (!opts.longStackTraces && Promise.hasLongStackTraces()) {
+ disableLongStackTraces();
+ }
+ }
+ if ("warnings" in opts) {
+ var warningsOption = opts.warnings;
+ config.warnings = !!warningsOption;
+ wForgottenReturn = config.warnings;
+
+ if (util.isObject(warningsOption)) {
+ if ("wForgottenReturn" in warningsOption) {
+ wForgottenReturn = !!warningsOption.wForgottenReturn;
+ }
+ }
+ }
+ if ("cancellation" in opts && opts.cancellation && !config.cancellation) {
+ if (async.haveItemsQueued()) {
+ throw new Error(
+ "cannot enable cancellation after promises are in use");
+ }
+ Promise.prototype._clearCancellationData =
+ cancellationClearCancellationData;
+ Promise.prototype._propagateFrom = cancellationPropagateFrom;
+ Promise.prototype._onCancel = cancellationOnCancel;
+ Promise.prototype._setOnCancel = cancellationSetOnCancel;
+ Promise.prototype._attachCancellationCallback =
+ cancellationAttachCancellationCallback;
+ Promise.prototype._execute = cancellationExecute;
+ propagateFromFunction = cancellationPropagateFrom;
+ config.cancellation = true;
+ }
+ if ("monitoring" in opts) {
+ if (opts.monitoring && !config.monitoring) {
+ config.monitoring = true;
+ Promise.prototype._fireEvent = activeFireEvent;
+ } else if (!opts.monitoring && config.monitoring) {
+ config.monitoring = false;
+ Promise.prototype._fireEvent = defaultFireEvent;
+ }
+ }
+ if ("asyncHooks" in opts && util.nodeSupportsAsyncResource) {
+ var prev = config.asyncHooks;
+ var cur = !!opts.asyncHooks;
+ if (prev !== cur) {
+ config.asyncHooks = cur;
+ if (cur) {
+ enableAsyncHooks();
+ } else {
+ disableAsyncHooks();
+ }
+ }
+ }
+ return Promise;
+};
+
+function defaultFireEvent() { return false; }
+
+Promise.prototype._fireEvent = defaultFireEvent;
+Promise.prototype._execute = function(executor, resolve, reject) {
+ try {
+ executor(resolve, reject);
+ } catch (e) {
+ return e;
+ }
+};
+Promise.prototype._onCancel = function () {};
+Promise.prototype._setOnCancel = function (handler) { USE(handler); };
+Promise.prototype._attachCancellationCallback = function(onCancel) {
+ USE(onCancel);
+};
+Promise.prototype._captureStackTrace = function () {};
+Promise.prototype._attachExtraTrace = function () {};
+Promise.prototype._dereferenceTrace = function () {};
+Promise.prototype._clearCancellationData = function() {};
+Promise.prototype._propagateFrom = function (parent, flags) {
+ USE(parent);
+ USE(flags);
+};
+
+function cancellationExecute(executor, resolve, reject) {
+ var promise = this;
+ try {
+ executor(resolve, reject, function(onCancel) {
+ if (typeof onCancel !== "function") {
+ throw new TypeError("onCancel must be a function, got: " +
+ util.toString(onCancel));
+ }
+ promise._attachCancellationCallback(onCancel);
+ });
+ } catch (e) {
+ return e;
+ }
+}
+
+function cancellationAttachCancellationCallback(onCancel) {
+ if (!this._isCancellable()) return this;
+
+ var previousOnCancel = this._onCancel();
+ if (previousOnCancel !== undefined) {
+ if (util.isArray(previousOnCancel)) {
+ previousOnCancel.push(onCancel);
+ } else {
+ this._setOnCancel([previousOnCancel, onCancel]);
+ }
+ } else {
+ this._setOnCancel(onCancel);
+ }
+}
+
+function cancellationOnCancel() {
+ ASSERT(this._isCancellable());
+ return this._onCancelField;
+}
+
+function cancellationSetOnCancel(onCancel) {
+ ASSERT(this._isCancellable());
+ this._onCancelField = onCancel;
+}
+
+function cancellationClearCancellationData() {
+ this._cancellationParent = undefined;
+ this._onCancelField = undefined;
+}
+
+function cancellationPropagateFrom(parent, flags) {
+ ASSERT(flags !== 0);
+ if ((flags & PROPAGATE_CANCEL) !== 0) {
+ this._cancellationParent = parent;
+ var branchesRemainingToCancel = parent._branchesRemainingToCancel;
+ if (branchesRemainingToCancel === undefined) {
+ branchesRemainingToCancel = 0;
+ }
+ parent._branchesRemainingToCancel = branchesRemainingToCancel + 1;
+ }
+ if ((flags & PROPAGATE_BIND) !== 0 && parent._isBound()) {
+ this._setBoundTo(parent._boundTo);
+ }
+}
+
+function bindingPropagateFrom(parent, flags) {
+ ASSERT(flags !== 0);
+ if ((flags & PROPAGATE_BIND) !== 0 && parent._isBound()) {
+ this._setBoundTo(parent._boundTo);
+ }
+}
+var propagateFromFunction = bindingPropagateFrom;
+
+function boundValueFunction() {
+ var ret = this._boundTo;
+ if (ret !== undefined) {
+ if (ret instanceof Promise) {
+ if (ret.isFulfilled()) {
+ return ret.value();
+ } else {
+ return undefined;
+ }
+ }
+ }
+ return ret;
+}
+
+function longStackTracesCaptureStackTrace() {
+ ASSERT(this._trace == null);
+ this._trace = new CapturedTrace(this._peekContext());
+}
+
+function longStackTracesAttachExtraTrace(error, ignoreSelf) {
+ if (canAttachTrace(error)) {
+ var trace = this._trace;
+ if (trace !== undefined) {
+ if (ignoreSelf) trace = trace._parent;
+ }
+ if (trace !== undefined) {
+ trace.attachExtraTrace(error);
+ } else if (!error.__stackCleaned__) {
+ var parsed = parseStackAndMessage(error);
+ util.notEnumerableProp(error, "stack",
+ parsed.message + "\n" + parsed.stack.join("\n"));
+ util.notEnumerableProp(error, "__stackCleaned__", true);
+ }
+ }
+}
+
+function longStackTracesDereferenceTrace() {
+ this._trace = undefined;
+}
+
+function checkForgottenReturns(returnValue, promiseCreated, name, promise,
+ parent) {
+ if (returnValue === undefined && promiseCreated !== null &&
+ wForgottenReturn) {
+ if (parent !== undefined && parent._returnedNonUndefined()) return;
+ if (BIT_FIELD_READ(LENGTH_MASK, promise._bitField) === 0) return;
+
+ if (name) name = name + " ";
+ var handlerLine = "";
+ var creatorLine = "";
+ if (promiseCreated._trace) {
+ var traceLines = promiseCreated._trace.stack.split("\n");
+ var stack = cleanStack(traceLines);
+ for (var i = stack.length - 1; i >= 0; --i) {
+ var line = stack[i];
+ if (!nodeFramePattern.test(line)) {
+ var lineMatches = line.match(parseLinePattern);
+ if (lineMatches) {
+ handlerLine = "at " + lineMatches[1] +
+ ":" + lineMatches[2] + ":" + lineMatches[3] + " ";
+ }
+ break;
+ }
+ }
+
+ if (stack.length > 0) {
+ var firstUserLine = stack[0];
+ for (var i = 0; i < traceLines.length; ++i) {
+
+ if (traceLines[i] === firstUserLine) {
+ if (i > 0) {
+ creatorLine = "\n" + traceLines[i - 1];
+ }
+ break;
+ }
+ }
+
+ }
+ }
+ var msg = "a promise was created in a " + name +
+ "handler " + handlerLine + "but was not returned from it, " +
+ "see http://goo.gl/rRqMUw" +
+ creatorLine;
+ promise._warn(msg, true, promiseCreated);
+ }
+}
+
+function deprecated(name, replacement) {
+ var message = name +
+ " is deprecated and will be removed in a future version.";
+ if (replacement) message += " Use " + replacement + " instead.";
+ return warn(message);
+}
+
+function warn(message, shouldUseOwnTrace, promise) {
+ if (!config.warnings) return;
+ var warning = new Warning(message);
+ var ctx;
+ if (shouldUseOwnTrace) {
+ promise._attachExtraTrace(warning);
+ } else if (config.longStackTraces && (ctx = Promise._peekContext())) {
+ ctx.attachExtraTrace(warning);
+ } else {
+ var parsed = parseStackAndMessage(warning);
+ warning.stack = parsed.message + "\n" + parsed.stack.join("\n");
+ }
+
+ if (!activeFireEvent("warning", warning)) {
+ formatAndLogError(warning, "", true);
+ }
+}
+
+function reconstructStack(message, stacks) {
+ for (var i = 0; i < stacks.length - 1; ++i) {
+ stacks[i].push(FROM_PREVIOUS_EVENT);
+ stacks[i] = stacks[i].join("\n");
+ }
+ if (i < stacks.length) {
+ stacks[i] = stacks[i].join("\n");
+ }
+ return message + "\n" + stacks.join("\n");
+}
+
+function removeDuplicateOrEmptyJumps(stacks) {
+ for (var i = 0; i < stacks.length; ++i) {
+ if (stacks[i].length === 0 ||
+ ((i + 1 < stacks.length) && stacks[i][0] === stacks[i+1][0])) {
+ stacks.splice(i, 1);
+ i--;
+ }
+ }
+}
+
+function removeCommonRoots(stacks) {
+ var current = stacks[0];
+ for (var i = 1; i < stacks.length; ++i) {
+ var prev = stacks[i];
+ var currentLastIndex = current.length - 1;
+ var currentLastLine = current[currentLastIndex];
+ var commonRootMeetPoint = -1;
+
+ for (var j = prev.length - 1; j >= 0; --j) {
+ if (prev[j] === currentLastLine) {
+ commonRootMeetPoint = j;
+ break;
+ }
+ }
+
+ for (var j = commonRootMeetPoint; j >= 0; --j) {
+ var line = prev[j];
+ if (current[currentLastIndex] === line) {
+ current.pop();
+ currentLastIndex--;
+ } else {
+ break;
+ }
+ }
+ current = prev;
+ }
+}
+
+function cleanStack(stack) {
+ var ret = [];
+ for (var i = 0; i < stack.length; ++i) {
+ var line = stack[i];
+ var isTraceLine = NO_STACK_TRACE === line ||
+ stackFramePattern.test(line);
+ var isInternalFrame = isTraceLine && shouldIgnore(line);
+ if (isTraceLine && !isInternalFrame) {
+ if (indentStackFrames && line.charAt(0) !== " ") {
+ // Make Firefox stack traces readable...it is almost
+ // impossible to see the event boundaries without
+ // indentation.
+ line = " " + line;
+ }
+ ret.push(line);
+ }
+ }
+ return ret;
+}
+
+function stackFramesAsArray(error) {
+ var stack = error.stack.replace(/\s+$/g, "").split("\n");
+ for (var i = 0; i < stack.length; ++i) {
+ var line = stack[i];
+ if (NO_STACK_TRACE === line || stackFramePattern.test(line)) {
+ break;
+ }
+ }
+ // Chrome and IE include the error message in the stack
+ if (i > 0 && error.name != "SyntaxError") {
+ stack = stack.slice(i);
+ }
+ return stack;
+}
+
+function parseStackAndMessage(error) {
+ var stack = error.stack;
+ var message = error.toString();
+ stack = typeof stack === "string" && stack.length > 0
+ ? stackFramesAsArray(error) : [NO_STACK_TRACE];
+ return {
+ message: message,
+ stack: error.name == "SyntaxError" ? stack : cleanStack(stack)
+ };
+}
+
+function formatAndLogError(error, title, isSoft) {
+ if (typeof console !== "undefined") {
+ var message;
+ if (util.isObject(error)) {
+ var stack = error.stack;
+ message = title + formatStack(stack, error);
+ } else {
+ message = title + String(error);
+ }
+ if (typeof printWarning === "function") {
+ printWarning(message, isSoft);
+ } else if (typeof console.log === "function" ||
+ typeof console.log === "object") {
+ console.log(message);
+ }
+ }
+}
+
+function fireRejectionEvent(name, localHandler, reason, promise) {
+ var localEventFired = false;
+ try {
+ if (typeof localHandler === "function") {
+ localEventFired = true;
+ if (name === REJECTION_HANDLED_EVENT) {
+ localHandler(promise);
+ } else {
+ localHandler(reason, promise);
+ }
+ }
+ } catch (e) {
+ async.throwLater(e);
+ }
+
+ if (name === UNHANDLED_REJECTION_EVENT) {
+ if (!activeFireEvent(name, reason, promise) && !localEventFired) {
+ formatAndLogError(reason, UNHANDLED_REJECTION_HEADER);
+ }
+ } else {
+ activeFireEvent(name, promise);
+ }
+}
+
+function formatNonError(obj) {
+ var str;
+ if (typeof obj === "function") {
+ str = "[function " +
+ (obj.name || "anonymous") +
+ "]";
+ } else {
+ str = obj && typeof obj.toString === "function"
+ ? obj.toString() : util.toString(obj);
+ var ruselessToString = /\[object [a-zA-Z0-9$_]+\]/;
+ if (ruselessToString.test(str)) {
+ try {
+ var newStr = JSON.stringify(obj);
+ str = newStr;
+ }
+ catch(e) {
+
+ }
+ }
+ if (str.length === 0) {
+ str = "(empty array)";
+ }
+ }
+ return ("(<" + snip(str) + ">, no stack trace)");
+}
+
+function snip(str) {
+ var maxChars = 41;
+ if (str.length < maxChars) {
+ return str;
+ }
+ return str.substr(0, maxChars - 3) + "...";
+}
+
+function longStackTracesIsSupported() {
+ return typeof captureStackTrace === "function";
+}
+
+// For filtering out internal calls from stack traces
+var shouldIgnore = function() { return false; };
+var parseLineInfoRegex = /[\/<\(]([^:\/]+):(\d+):(?:\d+)\)?\s*$/;
+function parseLineInfo(line) {
+ var matches = line.match(parseLineInfoRegex);
+ if (matches) {
+ return {
+ fileName: matches[1],
+ line: parseInt(matches[2], 10)
+ };
+ }
+}
+
+function setBounds(firstLineError, lastLineError) {
+ if (!longStackTracesIsSupported()) return;
+ var firstStackLines = (firstLineError.stack || "").split("\n");
+ var lastStackLines = (lastLineError.stack || "").split("\n");
+ var firstIndex = -1;
+ var lastIndex = -1;
+ var firstFileName;
+ var lastFileName;
+ for (var i = 0; i < firstStackLines.length; ++i) {
+ var result = parseLineInfo(firstStackLines[i]);
+ if (result) {
+ firstFileName = result.fileName;
+ firstIndex = result.line;
+ break;
+ }
+ }
+ for (var i = 0; i < lastStackLines.length; ++i) {
+ var result = parseLineInfo(lastStackLines[i]);
+ if (result) {
+ lastFileName = result.fileName;
+ lastIndex = result.line;
+ break;
+ }
+ }
+ if (firstIndex < 0 || lastIndex < 0 || !firstFileName || !lastFileName ||
+ firstFileName !== lastFileName || firstIndex >= lastIndex) {
+ return;
+ }
+
+ shouldIgnore = function(line) {
+ if (bluebirdFramePattern.test(line)) return true;
+ var info = parseLineInfo(line);
+ if (info) {
+ if (info.fileName === firstFileName &&
+ (firstIndex <= info.line && info.line <= lastIndex)) {
+ return true;
+ }
+ }
+ return false;
+ };
+}
+
+function CapturedTrace(parent) {
+ ASSERT(parent === undefined || parent instanceof CapturedTrace);
+ this._parent = parent;
+ this._promisesCreated = 0;
+ var length = this._length = 1 + (parent === undefined ? 0 : parent._length);
+ captureStackTrace(this, CapturedTrace);
+ // Unless the user manually nested > 32 indentation levels,
+ // there must be cycles
+ if (length > 32) this.uncycle();
+}
+util.inherits(CapturedTrace, Error);
+Context.CapturedTrace = CapturedTrace;
+
+CapturedTrace.prototype.uncycle = function() {
+ var length = this._length;
+ if (length < 2) return;
+ var nodes = [];
+ var stackToIndex = {};
+
+ for (var i = 0, node = this; node !== undefined; ++i) {
+ nodes.push(node);
+ node = node._parent;
+ }
+ // the node length is only used as heuristic to decide when to decycle, as
+ // there may be multiple linked lists that share members and decycling one
+ // will fail to update lenghts in the other. This is the correct length.
+ length = this._length = i;
+ ASSERT(nodes[0] === this);
+ ASSERT(nodes[nodes.length - 1] instanceof CapturedTrace);
+
+ for (var i = length - 1; i >= 0; --i) {
+ var stack = nodes[i].stack;
+ if (stackToIndex[stack] === undefined) {
+ stackToIndex[stack] = i;
+ }
+ }
+ for (var i = 0; i < length; ++i) {
+ var currentStack = nodes[i].stack;
+ var index = stackToIndex[currentStack];
+ ASSERT(currentStack === nodes[index].stack);
+
+ if (index !== undefined && index !== i) {
+ if (index > 0) {
+ ASSERT(nodes[index - 1]._parent === nodes[index]);
+ nodes[index - 1]._parent = undefined;
+ nodes[index - 1]._length = 1;
+ }
+ nodes[i]._parent = undefined;
+ nodes[i]._length = 1;
+ var cycleEdgeNode = i > 0 ? nodes[i - 1] : this;
+
+ if (index < length - 1) {
+ cycleEdgeNode._parent = nodes[index + 1];
+ cycleEdgeNode._parent.uncycle();
+ cycleEdgeNode._length =
+ cycleEdgeNode._parent._length + 1;
+ } else {
+ cycleEdgeNode._parent = undefined;
+ cycleEdgeNode._length = 1;
+ }
+ var currentChildLength = cycleEdgeNode._length + 1;
+ for (var j = i - 2; j >= 0; --j) {
+ nodes[j]._length = currentChildLength;
+ currentChildLength++;
+ }
+ return;
+ }
+ }
+};
+
+CapturedTrace.prototype.attachExtraTrace = function(error) {
+ if (error.__stackCleaned__) return;
+ this.uncycle();
+ var parsed = parseStackAndMessage(error);
+ var message = parsed.message;
+ var stacks = [parsed.stack];
+
+ var trace = this;
+ while (trace !== undefined) {
+ stacks.push(cleanStack(trace.stack.split("\n")));
+ trace = trace._parent;
+ }
+ removeCommonRoots(stacks);
+ removeDuplicateOrEmptyJumps(stacks);
+ util.notEnumerableProp(error, "stack", reconstructStack(message, stacks));
+ util.notEnumerableProp(error, "__stackCleaned__", true);
+};
+
+var captureStackTrace = (function stackDetection() {
+ var v8stackFramePattern = /^\s*at\s*/;
+ var v8stackFormatter = function(stack, error) {
+ ASSERT(error !== null);
+
+ if (typeof stack === "string") return stack;
+
+ if (error.name !== undefined &&
+ error.message !== undefined) {
+ return error.toString();
+ }
+ return formatNonError(error);
+ };
+
+ //V8
+ if (typeof Error.stackTraceLimit === "number" &&
+ typeof Error.captureStackTrace === "function") {
+ Error.stackTraceLimit += 6;
+ stackFramePattern = v8stackFramePattern;
+ formatStack = v8stackFormatter;
+ var captureStackTrace = Error.captureStackTrace;
+
+ // For node
+ shouldIgnore = function(line) {
+ return bluebirdFramePattern.test(line);
+ };
+ return function(receiver, ignoreUntil) {
+ Error.stackTraceLimit += 6;
+ captureStackTrace(receiver, ignoreUntil);
+ Error.stackTraceLimit -= 6;
+ };
+ }
+ var err = new Error();
+
+ //SpiderMonkey
+ if (typeof err.stack === "string" &&
+ err.stack.split("\n")[0].indexOf("stackDetection@") >= 0) {
+ stackFramePattern = /@/;
+ formatStack = v8stackFormatter;
+ indentStackFrames = true;
+ return function captureStackTrace(o) {
+ o.stack = new Error().stack;
+ };
+ }
+
+ var hasStackAfterThrow;
+ try { throw new Error(); }
+ catch(e) {
+ hasStackAfterThrow = ("stack" in e);
+ }
+ // IE 10+
+ if (!("stack" in err) && hasStackAfterThrow &&
+ typeof Error.stackTraceLimit === "number") {
+ stackFramePattern = v8stackFramePattern;
+ formatStack = v8stackFormatter;
+ return function captureStackTrace(o) {
+ Error.stackTraceLimit += 6;
+ try { throw new Error(); }
+ catch(e) { o.stack = e.stack; }
+ Error.stackTraceLimit -= 6;
+ };
+ }
+
+ formatStack = function(stack, error) {
+ if (typeof stack === "string") return stack;
+
+ if ((typeof error === "object" ||
+ typeof error === "function") &&
+ error.name !== undefined &&
+ error.message !== undefined) {
+ return error.toString();
+ }
+ return formatNonError(error);
+ };
+
+ return null;
+
+})([]);
+
+if (typeof console !== "undefined" && typeof console.warn !== "undefined") {
+ printWarning = function (message) {
+ console.warn(message);
+ };
+ if (util.isNode && process.stderr.isTTY) {
+ printWarning = function(message, isSoft) {
+ var color = isSoft ? "\u001b[33m" : "\u001b[31m";
+ console.warn(color + message + "\u001b[0m\n");
+ };
+ } else if (!util.isNode && typeof (new Error().stack) === "string") {
+ printWarning = function(message, isSoft) {
+ console.warn("%c" + message,
+ isSoft ? "color: darkorange" : "color: red");
+ };
+ }
+}
+
+var config = {
+ warnings: warnings,
+ longStackTraces: false,
+ cancellation: false,
+ monitoring: false,
+ asyncHooks: false
+};
+
+if (longStackTraces) Promise.longStackTraces();
+
+return {
+ asyncHooks: function() {
+ return config.asyncHooks;
+ },
+ longStackTraces: function() {
+ return config.longStackTraces;
+ },
+ warnings: function() {
+ return config.warnings;
+ },
+ cancellation: function() {
+ return config.cancellation;
+ },
+ monitoring: function() {
+ return config.monitoring;
+ },
+ propagateFromFunction: function() {
+ return propagateFromFunction;
+ },
+ boundValueFunction: function() {
+ return boundValueFunction;
+ },
+ checkForgottenReturns: checkForgottenReturns,
+ setBounds: setBounds,
+ warn: warn,
+ deprecated: deprecated,
+ CapturedTrace: CapturedTrace,
+ fireDomEvent: fireDomEvent,
+ fireGlobalEvent: fireGlobalEvent
+};
+};
diff --git a/src/direct_resolve.js b/src/direct_resolve.js
new file mode 100644
index 0000000..a890298
--- /dev/null
+++ b/src/direct_resolve.js
@@ -0,0 +1,46 @@
+"use strict";
+module.exports = function(Promise) {
+function returner() {
+ return this.value;
+}
+function thrower() {
+ throw this.reason;
+}
+
+Promise.prototype["return"] =
+Promise.prototype.thenReturn = function (value) {
+ if (value instanceof Promise) value.suppressUnhandledRejections();
+ return this._then(
+ returner, undefined, undefined, {value: value}, undefined);
+};
+
+Promise.prototype["throw"] =
+Promise.prototype.thenThrow = function (reason) {
+ return this._then(
+ thrower, undefined, undefined, {reason: reason}, undefined);
+};
+
+Promise.prototype.catchThrow = function (reason) {
+ if (arguments.length <= 1) {
+ return this._then(
+ undefined, thrower, undefined, {reason: reason}, undefined);
+ } else {
+ var _reason = arguments[1];
+ var handler = function() {throw _reason;};
+ return this.caught(reason, handler);
+ }
+};
+
+Promise.prototype.catchReturn = function (value) {
+ if (arguments.length <= 1) {
+ if (value instanceof Promise) value.suppressUnhandledRejections();
+ return this._then(
+ undefined, returner, undefined, {value: value}, undefined);
+ } else {
+ var _value = arguments[1];
+ if (_value instanceof Promise) _value.suppressUnhandledRejections();
+ var handler = function() {return _value;};
+ return this.caught(value, handler);
+ }
+};
+};
diff --git a/src/each.js b/src/each.js
new file mode 100644
index 0000000..e4f3d05
--- /dev/null
+++ b/src/each.js
@@ -0,0 +1,30 @@
+"use strict";
+module.exports = function(Promise, INTERNAL) {
+var PromiseReduce = Promise.reduce;
+var PromiseAll = Promise.all;
+
+function promiseAllThis() {
+ return PromiseAll(this);
+}
+
+function PromiseMapSeries(promises, fn) {
+ return PromiseReduce(promises, fn, INTERNAL, INTERNAL);
+}
+
+Promise.prototype.each = function (fn) {
+ return PromiseReduce(this, fn, INTERNAL, 0)
+ ._then(promiseAllThis, undefined, undefined, this, undefined);
+};
+
+Promise.prototype.mapSeries = function (fn) {
+ return PromiseReduce(this, fn, INTERNAL, INTERNAL);
+};
+
+Promise.each = function (promises, fn) {
+ return PromiseReduce(promises, fn, INTERNAL, 0)
+ ._then(promiseAllThis, undefined, undefined, promises, undefined);
+};
+
+Promise.mapSeries = PromiseMapSeries;
+};
+
diff --git a/src/errors.js b/src/errors.js
new file mode 100644
index 0000000..7762216
--- /dev/null
+++ b/src/errors.js
@@ -0,0 +1,117 @@
+"use strict";
+var es5 = require("./es5");
+var Objectfreeze = es5.freeze;
+var util = require("./util");
+var inherits = util.inherits;
+var notEnumerableProp = util.notEnumerableProp;
+
+function subError(nameProperty, defaultMessage) {
+ function SubError(message) {
+ if (!(this instanceof SubError)) return new SubError(message);
+ notEnumerableProp(this, "message",
+ typeof message === "string" ? message : defaultMessage);
+ notEnumerableProp(this, "name", nameProperty);
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, this.constructor);
+ } else {
+ Error.call(this);
+ }
+ }
+ inherits(SubError, Error);
+ return SubError;
+}
+
+var _TypeError, _RangeError;
+var Warning = subError("Warning", "warning");
+var CancellationError = subError("CancellationError", "cancellation error");
+var TimeoutError = subError("TimeoutError", "timeout error");
+var AggregateError = subError("AggregateError", "aggregate error");
+try {
+ _TypeError = TypeError;
+ _RangeError = RangeError;
+} catch(e) {
+ _TypeError = subError("TypeError", "type error");
+ _RangeError = subError("RangeError", "range error");
+}
+
+var methods = ("join pop push shift unshift slice filter forEach some " +
+ "every map indexOf lastIndexOf reduce reduceRight sort reverse").split(" ");
+
+for (var i = 0; i < methods.length; ++i) {
+ if (typeof Array.prototype[methods[i]] === "function") {
+ AggregateError.prototype[methods[i]] = Array.prototype[methods[i]];
+ }
+}
+
+es5.defineProperty(AggregateError.prototype, "length", {
+ value: 0,
+ configurable: false,
+ writable: true,
+ enumerable: true
+});
+AggregateError.prototype[OPERATIONAL_ERROR_KEY] = true;
+var level = 0;
+AggregateError.prototype.toString = function() {
+ var indent = Array(level * 4 + 1).join(" ");
+ var ret = "\n" + indent + "AggregateError of:" + "\n";
+ level++;
+ indent = Array(level * 4 + 1).join(" ");
+ for (var i = 0; i < this.length; ++i) {
+ var str = this[i] === this ? "[Circular AggregateError]" : this[i] + "";
+ var lines = str.split("\n");
+ for (var j = 0; j < lines.length; ++j) {
+ lines[j] = indent + lines[j];
+ }
+ str = lines.join("\n");
+ ret += str + "\n";
+ }
+ level--;
+ return ret;
+};
+
+function OperationalError(message) {
+ if (!(this instanceof OperationalError))
+ return new OperationalError(message);
+ notEnumerableProp(this, "name", "OperationalError");
+ notEnumerableProp(this, "message", message);
+ this.cause = message;
+ this[OPERATIONAL_ERROR_KEY] = true;
+
+ if (message instanceof Error) {
+ notEnumerableProp(this, "message", message.message);
+ notEnumerableProp(this, "stack", message.stack);
+ } else if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, this.constructor);
+ }
+
+}
+inherits(OperationalError, Error);
+
+//Ensure all copies of the library throw the same error types
+var errorTypes = Error[BLUEBIRD_ERRORS];
+if (!errorTypes) {
+ errorTypes = Objectfreeze({
+ CancellationError: CancellationError,
+ TimeoutError: TimeoutError,
+ OperationalError: OperationalError,
+ RejectionError: OperationalError,
+ AggregateError: AggregateError
+ });
+ es5.defineProperty(Error, BLUEBIRD_ERRORS, {
+ value: errorTypes,
+ writable: false,
+ enumerable: false,
+ configurable: false
+ });
+}
+
+module.exports = {
+ Error: Error,
+ TypeError: _TypeError,
+ RangeError: _RangeError,
+ CancellationError: errorTypes.CancellationError,
+ OperationalError: errorTypes.OperationalError,
+ TimeoutError: errorTypes.TimeoutError,
+ AggregateError: errorTypes.AggregateError,
+ Warning: Warning
+};
diff --git a/src/es5.js b/src/es5.js
new file mode 100644
index 0000000..ea41d5a
--- /dev/null
+++ b/src/es5.js
@@ -0,0 +1,80 @@
+var isES5 = (function(){
+ "use strict";
+ return this === undefined;
+})();
+
+if (isES5) {
+ module.exports = {
+ freeze: Object.freeze,
+ defineProperty: Object.defineProperty,
+ getDescriptor: Object.getOwnPropertyDescriptor,
+ keys: Object.keys,
+ names: Object.getOwnPropertyNames,
+ getPrototypeOf: Object.getPrototypeOf,
+ isArray: Array.isArray,
+ isES5: isES5,
+ propertyIsWritable: function(obj, prop) {
+ var descriptor = Object.getOwnPropertyDescriptor(obj, prop);
+ return !!(!descriptor || descriptor.writable || descriptor.set);
+ }
+ };
+} else {
+ var has = {}.hasOwnProperty;
+ var str = {}.toString;
+ var proto = {}.constructor.prototype;
+
+ var ObjectKeys = function (o) {
+ var ret = [];
+ for (var key in o) {
+ if (has.call(o, key)) {
+ ret.push(key);
+ }
+ }
+ return ret;
+ };
+
+ var ObjectGetDescriptor = function(o, key) {
+ return {value: o[key]};
+ };
+
+ var ObjectDefineProperty = function (o, key, desc) {
+ o[key] = desc.value;
+ return o;
+ };
+
+ var ObjectFreeze = function (obj) {
+ return obj;
+ };
+
+ var ObjectGetPrototypeOf = function (obj) {
+ try {
+ return Object(obj).constructor.prototype;
+ }
+ catch (e) {
+ return proto;
+ }
+ };
+
+ var ArrayIsArray = function (obj) {
+ try {
+ return str.call(obj) === "[object Array]";
+ }
+ catch(e) {
+ return false;
+ }
+ };
+
+ module.exports = {
+ isArray: ArrayIsArray,
+ keys: ObjectKeys,
+ names: ObjectKeys,
+ defineProperty: ObjectDefineProperty,
+ getDescriptor: ObjectGetDescriptor,
+ freeze: ObjectFreeze,
+ getPrototypeOf: ObjectGetPrototypeOf,
+ isES5: isES5,
+ propertyIsWritable: function() {
+ return true;
+ }
+ };
+}
diff --git a/src/filter.js b/src/filter.js
new file mode 100644
index 0000000..ed57bf0
--- /dev/null
+++ b/src/filter.js
@@ -0,0 +1,12 @@
+"use strict";
+module.exports = function(Promise, INTERNAL) {
+var PromiseMap = Promise.map;
+
+Promise.prototype.filter = function (fn, options) {
+ return PromiseMap(this, fn, options, INTERNAL);
+};
+
+Promise.filter = function (promises, fn, options) {
+ return PromiseMap(promises, fn, options, INTERNAL);
+};
+};
diff --git a/src/finally.js b/src/finally.js
new file mode 100644
index 0000000..f699573
--- /dev/null
+++ b/src/finally.js
@@ -0,0 +1,146 @@
+"use strict";
+module.exports = function(Promise, tryConvertToPromise, NEXT_FILTER) {
+var util = require("./util");
+var CancellationError = Promise.CancellationError;
+var errorObj = util.errorObj;
+var catchFilter = require("./catch_filter")(NEXT_FILTER);
+
+function PassThroughHandlerContext(promise, type, handler) {
+ this.promise = promise;
+ this.type = type;
+ this.handler = handler;
+ this.called = false;
+ this.cancelPromise = null;
+}
+
+PassThroughHandlerContext.prototype.isFinallyHandler = function() {
+ return this.type === FINALLY_TYPE;
+};
+
+function FinallyHandlerCancelReaction(finallyHandler) {
+ this.finallyHandler = finallyHandler;
+}
+
+FinallyHandlerCancelReaction.prototype._resultCancelled = function() {
+ checkCancel(this.finallyHandler);
+};
+
+function checkCancel(ctx, reason) {
+ if (ctx.cancelPromise != null) {
+ if (arguments.length > 1) {
+ ctx.cancelPromise._reject(reason);
+ } else {
+ ctx.cancelPromise._cancel();
+ }
+ ctx.cancelPromise = null;
+ return true;
+ }
+ return false;
+}
+
+function succeed() {
+ return finallyHandler.call(this, this.promise._target()._settledValue());
+}
+function fail(reason) {
+ if (checkCancel(this, reason)) return;
+ errorObj.e = reason;
+ return errorObj;
+}
+function finallyHandler(reasonOrValue) {
+ var promise = this.promise;
+ var handler = this.handler;
+
+ if (!this.called) {
+ this.called = true;
+ var ret = this.isFinallyHandler()
+ ? handler.call(promise._boundValue())
+ : handler.call(promise._boundValue(), reasonOrValue);
+ if (ret === NEXT_FILTER) {
+ return ret;
+ } else if (ret !== undefined) {
+ promise._setReturnedNonUndefined();
+ var maybePromise = tryConvertToPromise(ret, promise);
+ if (maybePromise instanceof Promise) {
+ if (this.cancelPromise != null) {
+ if (maybePromise._isCancelled()) {
+ var reason =
+ new CancellationError(LATE_CANCELLATION_OBSERVER);
+ promise._attachExtraTrace(reason);
+ errorObj.e = reason;
+ return errorObj;
+ } else if (maybePromise.isPending()) {
+ maybePromise._attachCancellationCallback(
+ new FinallyHandlerCancelReaction(this));
+ }
+ }
+ return maybePromise._then(
+ succeed, fail, undefined, this, undefined);
+ }
+ }
+ }
+
+ if (promise.isRejected()) {
+ checkCancel(this);
+ errorObj.e = reasonOrValue;
+ return errorObj;
+ } else {
+ checkCancel(this);
+ return reasonOrValue;
+ }
+}
+
+Promise.prototype._passThrough = function(handler, type, success, fail) {
+ if (typeof handler !== "function") return this.then();
+ return this._then(success,
+ fail,
+ undefined,
+ new PassThroughHandlerContext(this, type, handler),
+ undefined);
+};
+
+Promise.prototype.lastly =
+Promise.prototype["finally"] = function (handler) {
+ return this._passThrough(handler,
+ FINALLY_TYPE,
+ finallyHandler,
+ finallyHandler);
+};
+
+
+Promise.prototype.tap = function (handler) {
+ return this._passThrough(handler, TAP_TYPE, finallyHandler);
+};
+
+Promise.prototype.tapCatch = function (handlerOrPredicate) {
+ var len = arguments.length;
+ if(len === 1) {
+ return this._passThrough(handlerOrPredicate,
+ TAP_TYPE,
+ undefined,
+ finallyHandler);
+ } else {
+ var catchInstances = new Array(len - 1),
+ j = 0, i;
+ for (i = 0; i < len - 1; ++i) {
+ var item = arguments[i];
+ if (util.isObject(item)) {
+ catchInstances[j++] = item;
+ } else {
+ return Promise.reject(new TypeError(
+ "tapCatch statement predicate: "
+ + OBJECT_ERROR + util.classString(item)
+ ));
+ }
+ }
+ catchInstances.length = j;
+ var handler = arguments[i];
+ return this._passThrough(catchFilter(catchInstances, handler, this),
+ TAP_TYPE,
+ undefined,
+ finallyHandler);
+ }
+
+};
+
+return PassThroughHandlerContext;
+};
diff --git a/src/generators.js b/src/generators.js
new file mode 100644
index 0000000..2e6029c
--- /dev/null
+++ b/src/generators.js
@@ -0,0 +1,230 @@
+"use strict";
+module.exports = function(Promise,
+ apiRejection,
+ INTERNAL,
+ tryConvertToPromise,
+ Proxyable,
+ debug) {
+var errors = require("./errors");
+var TypeError = errors.TypeError;
+var ASSERT = require("./assert");
+var util = require("./util");
+var errorObj = util.errorObj;
+var tryCatch = util.tryCatch;
+var yieldHandlers = [];
+
+function promiseFromYieldHandler(value, yieldHandlers, traceParent) {
+ for (var i = 0; i < yieldHandlers.length; ++i) {
+ traceParent._pushContext();
+ var result = tryCatch(yieldHandlers[i])(value);
+ traceParent._popContext();
+ if (result === errorObj) {
+ traceParent._pushContext();
+ var ret = Promise.reject(errorObj.e);
+ traceParent._popContext();
+ return ret;
+ }
+ var maybePromise = tryConvertToPromise(result, traceParent);
+ if (maybePromise instanceof Promise) return maybePromise;
+ }
+ return null;
+}
+
+function PromiseSpawn(generatorFunction, receiver, yieldHandler, stack) {
+ if (debug.cancellation()) {
+ var internal = new Promise(INTERNAL);
+ var _finallyPromise = this._finallyPromise = new Promise(INTERNAL);
+ this._promise = internal.lastly(function() {
+ return _finallyPromise;
+ });
+ internal._captureStackTrace();
+ internal._setOnCancel(this);
+ } else {
+ var promise = this._promise = new Promise(INTERNAL);
+ promise._captureStackTrace();
+ }
+ this._stack = stack;
+ this._generatorFunction = generatorFunction;
+ this._receiver = receiver;
+ this._generator = undefined;
+ this._yieldHandlers = typeof yieldHandler === "function"
+ ? [yieldHandler].concat(yieldHandlers)
+ : yieldHandlers;
+ this._yieldedPromise = null;
+ this._cancellationPhase = false;
+}
+util.inherits(PromiseSpawn, Proxyable);
+
+PromiseSpawn.prototype._isResolved = function() {
+ return this._promise === null;
+};
+
+PromiseSpawn.prototype._cleanup = function() {
+ this._promise = this._generator = null;
+ if (debug.cancellation() && this._finallyPromise !== null) {
+ this._finallyPromise._fulfill();
+ this._finallyPromise = null;
+ }
+};
+
+PromiseSpawn.prototype._promiseCancelled = function() {
+ if (this._isResolved()) return;
+ var implementsReturn = typeof this._generator["return"] !== "undefined";
+
+ var result;
+ if (!implementsReturn) {
+ var reason = new Promise.CancellationError(
+ "generator .return() sentinel");
+ Promise.coroutine.returnSentinel = reason;
+ this._promise._attachExtraTrace(reason);
+ this._promise._pushContext();
+ result = tryCatch(this._generator["throw"]).call(this._generator,
+ reason);
+ this._promise._popContext();
+ } else {
+ this._promise._pushContext();
+ result = tryCatch(this._generator["return"]).call(this._generator,
+ undefined);
+ this._promise._popContext();
+ }
+ this._cancellationPhase = true;
+ this._yieldedPromise = null;
+ this._continue(result);
+};
+
+PromiseSpawn.prototype._promiseFulfilled = function(value) {
+ this._yieldedPromise = null;
+ this._promise._pushContext();
+ var result = tryCatch(this._generator.next).call(this._generator, value);
+ this._promise._popContext();
+ this._continue(result);
+};
+
+PromiseSpawn.prototype._promiseRejected = function(reason) {
+ this._yieldedPromise = null;
+ this._promise._attachExtraTrace(reason);
+ this._promise._pushContext();
+ var result = tryCatch(this._generator["throw"])
+ .call(this._generator, reason);
+ this._promise._popContext();
+ this._continue(result);
+};
+
+PromiseSpawn.prototype._resultCancelled = function() {
+ if (this._yieldedPromise instanceof Promise) {
+ var promise = this._yieldedPromise;
+ this._yieldedPromise = null;
+ promise.cancel();
+ }
+};
+
+PromiseSpawn.prototype.promise = function () {
+ return this._promise;
+};
+
+PromiseSpawn.prototype._run = function () {
+ this._generator = this._generatorFunction.call(this._receiver);
+ this._receiver =
+ this._generatorFunction = undefined;
+ this._promiseFulfilled(undefined);
+};
+
+PromiseSpawn.prototype._continue = function (result) {
+ ASSERT(this._yieldedPromise == null);
+ var promise = this._promise;
+ if (result === errorObj) {
+ this._cleanup();
+ if (this._cancellationPhase) {
+ return promise.cancel();
+ } else {
+ return promise._rejectCallback(result.e, false);
+ }
+ }
+
+ var value = result.value;
+ if (result.done === true) {
+ this._cleanup();
+ if (this._cancellationPhase) {
+ return promise.cancel();
+ } else {
+ return promise._resolveCallback(value);
+ }
+ } else {
+ var maybePromise = tryConvertToPromise(value, this._promise);
+ if (!(maybePromise instanceof Promise)) {
+ maybePromise =
+ promiseFromYieldHandler(maybePromise,
+ this._yieldHandlers,
+ this._promise);
+ ASSERT(maybePromise === null || maybePromise instanceof Promise);
+ if (maybePromise === null) {
+ this._promiseRejected(
+ new TypeError(
+ YIELDED_NON_PROMISE_ERROR.replace("%s", String(value)) +
+ FROM_COROUTINE_CREATED_AT +
+ this._stack.split("\n").slice(1, -7).join("\n")
+ )
+ );
+ return;
+ }
+ }
+ maybePromise = maybePromise._target();
+ var bitField = maybePromise._bitField;
+ USE(bitField);
+ if (BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) {
+ this._yieldedPromise = maybePromise;
+ maybePromise._proxy(this, null);
+ } else if (BIT_FIELD_CHECK(IS_FULFILLED)) {
+ Promise._async.invoke(
+ this._promiseFulfilled, this, maybePromise._value()
+ );
+ } else if (BIT_FIELD_CHECK(IS_REJECTED)) {
+ Promise._async.invoke(
+ this._promiseRejected, this, maybePromise._reason()
+ );
+ } else {
+ this._promiseCancelled();
+ }
+ }
+};
+
+Promise.coroutine = function (generatorFunction, options) {
+ //Throw synchronously because Promise.coroutine is semantically
+ //something you call at "compile time" to annotate static functions
+ if (typeof generatorFunction !== "function") {
+ throw new TypeError(NOT_GENERATOR_ERROR);
+ }
+ var yieldHandler = Object(options).yieldHandler;
+ var PromiseSpawn$ = PromiseSpawn;
+ var stack = new Error().stack;
+ return function () {
+ var generator = generatorFunction.apply(this, arguments);
+ var spawn = new PromiseSpawn$(undefined, undefined, yieldHandler,
+ stack);
+ var ret = spawn.promise();
+ spawn._generator = generator;
+ spawn._promiseFulfilled(undefined);
+ return ret;
+ };
+};
+
+Promise.coroutine.addYieldHandler = function(fn) {
+ if (typeof fn !== "function") {
+ throw new TypeError(FUNCTION_ERROR + util.classString(fn));
+ }
+ yieldHandlers.push(fn);
+};
+
+Promise.spawn = function (generatorFunction) {
+ debug.deprecated("Promise.spawn()", "Promise.coroutine()");
+ //Return rejected promise because Promise.spawn is semantically
+ //something that will be called at runtime with possibly dynamic values
+ if (typeof generatorFunction !== "function") {
+ return apiRejection(NOT_GENERATOR_ERROR);
+ }
+ var spawn = new PromiseSpawn(generatorFunction, this);
+ var ret = spawn.promise();
+ spawn._run(Promise.spawn);
+ return ret;
+};
+};
diff --git a/src/join.js b/src/join.js
new file mode 100644
index 0000000..b34af43
--- /dev/null
+++ b/src/join.js
@@ -0,0 +1,165 @@
+"use strict";
+module.exports =
+function(Promise, PromiseArray, tryConvertToPromise, INTERNAL, async) {
+var util = require("./util");
+var canEvaluate = util.canEvaluate;
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+var reject;
+
+if (!__BROWSER__) {
+if (canEvaluate) {
+ var thenCallback = function(i) {
+ return new Function("value", "holder", " \n\
+ 'use strict'; \n\
+ holder.pIndex = value; \n\
+ holder.checkFulfillment(this); \n\
+ ".replace(/Index/g, i));
+ };
+
+ var promiseSetter = function(i) {
+ return new Function("promise", "holder", " \n\
+ 'use strict'; \n\
+ holder.pIndex = promise; \n\
+ ".replace(/Index/g, i));
+ };
+
+ var generateHolderClass = function(total) {
+ var props = new Array(total);
+ for (var i = 0; i < props.length; ++i) {
+ props[i] = "this.p" + (i+1);
+ }
+ var assignment = props.join(" = ") + " = null;";
+ var cancellationCode= "var promise;\n" + props.map(function(prop) {
+ return " \n\
+ promise = " + prop + "; \n\
+ if (promise instanceof Promise) { \n\
+ promise.cancel(); \n\
+ } \n\
+ ";
+ }).join("\n");
+ var passedArguments = props.join(", ");
+ var name = "Holder$" + total;
+
+
+ var code = "return function(tryCatch, errorObj, Promise, async) { \n\
+ 'use strict'; \n\
+ function [TheName](fn) { \n\
+ [TheProperties] \n\
+ this.fn = fn; \n\
+ this.asyncNeeded = true; \n\
+ this.now = 0; \n\
+ } \n\
+ \n\
+ [TheName].prototype._callFunction = function(promise) { \n\
+ promise._pushContext(); \n\
+ var ret = tryCatch(this.fn)([ThePassedArguments]); \n\
+ promise._popContext(); \n\
+ if (ret === errorObj) { \n\
+ promise._rejectCallback(ret.e, false); \n\
+ } else { \n\
+ promise._resolveCallback(ret); \n\
+ } \n\
+ }; \n\
+ \n\
+ [TheName].prototype.checkFulfillment = function(promise) { \n\
+ var now = ++this.now; \n\
+ if (now === [TheTotal]) { \n\
+ if (this.asyncNeeded) { \n\
+ async.invoke(this._callFunction, this, promise); \n\
+ } else { \n\
+ this._callFunction(promise); \n\
+ } \n\
+ \n\
+ } \n\
+ }; \n\
+ \n\
+ [TheName].prototype._resultCancelled = function() { \n\
+ [CancellationCode] \n\
+ }; \n\
+ \n\
+ return [TheName]; \n\
+ }(tryCatch, errorObj, Promise, async); \n\
+ ";
+
+ code = code.replace(/\[TheName\]/g, name)
+ .replace(/\[TheTotal\]/g, total)
+ .replace(/\[ThePassedArguments\]/g, passedArguments)
+ .replace(/\[TheProperties\]/g, assignment)
+ .replace(/\[CancellationCode\]/g, cancellationCode);
+
+ return new Function("tryCatch", "errorObj", "Promise", "async", code)
+ (tryCatch, errorObj, Promise, async);
+ };
+
+ var holderClasses = [];
+ var thenCallbacks = [];
+ var promiseSetters = [];
+
+ for (var i = 0; i < GENERATED_CLASS_COUNT; ++i) {
+ holderClasses.push(generateHolderClass(i + 1));
+ thenCallbacks.push(thenCallback(i + 1));
+ promiseSetters.push(promiseSetter(i + 1));
+ }
+
+ reject = function (reason) {
+ this._reject(reason);
+ };
+}}
+
+Promise.join = function () {
+ var last = arguments.length - 1;
+ var fn;
+ if (last > 0 && typeof arguments[last] === "function") {
+ fn = arguments[last];
+ if (!__BROWSER__) {
+ if (last <= GENERATED_CLASS_COUNT && canEvaluate) {
+ var ret = new Promise(INTERNAL);
+ ret._captureStackTrace();
+ var HolderClass = holderClasses[last - 1];
+ var holder = new HolderClass(fn);
+ var callbacks = thenCallbacks;
+
+ for (var i = 0; i < last; ++i) {
+ var maybePromise = tryConvertToPromise(arguments[i], ret);
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ var bitField = maybePromise._bitField;
+ USE(bitField);
+ if (BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) {
+ maybePromise._then(callbacks[i], reject,
+ undefined, ret, holder);
+ promiseSetters[i](maybePromise, holder);
+ holder.asyncNeeded = false;
+ } else if (BIT_FIELD_CHECK(IS_FULFILLED)) {
+ callbacks[i].call(ret,
+ maybePromise._value(), holder);
+ } else if (BIT_FIELD_CHECK(IS_REJECTED)) {
+ ret._reject(maybePromise._reason());
+ } else {
+ ret._cancel();
+ }
+ } else {
+ callbacks[i].call(ret, maybePromise, holder);
+ }
+ }
+
+ if (!ret._isFateSealed()) {
+ if (holder.asyncNeeded) {
+ var context = Promise._getContext();
+ holder.fn = util.contextBind(context, holder.fn);
+ }
+ ret._setAsyncGuaranteed();
+ ret._setOnCancel(holder);
+ }
+ return ret;
+ }
+ }
+ }
+ INLINE_SLICE(args, arguments);
+ if (fn) args.pop();
+ var ret = new PromiseArray(args).promise();
+ return fn !== undefined ? ret.spread(fn) : ret;
+};
+
+};
diff --git a/src/map.js b/src/map.js
new file mode 100644
index 0000000..793210d
--- /dev/null
+++ b/src/map.js
@@ -0,0 +1,192 @@
+"use strict";
+module.exports = function(Promise,
+ PromiseArray,
+ apiRejection,
+ tryConvertToPromise,
+ INTERNAL,
+ debug) {
+var ASSERT = require("./assert");
+var util = require("./util");
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+var async = Promise._async;
+
+function MappingPromiseArray(promises, fn, limit, _filter) {
+ this.constructor$(promises);
+ this._promise._captureStackTrace();
+ var context = Promise._getContext();
+ this._callback = util.contextBind(context, fn);
+ this._preservedValues = _filter === INTERNAL
+ ? new Array(this.length())
+ : null;
+ this._limit = limit;
+ this._inFlight = 0;
+ this._queue = [];
+ async.invoke(this._asyncInit, this, undefined);
+ if (util.isArray(promises)) {
+ for (var i = 0; i < promises.length; ++i) {
+ var maybePromise = promises[i];
+ if (maybePromise instanceof Promise) {
+ maybePromise.suppressUnhandledRejections();
+ }
+ }
+ }
+}
+util.inherits(MappingPromiseArray, PromiseArray);
+
+MappingPromiseArray.prototype._asyncInit = function() {
+ this._init$(undefined, RESOLVE_ARRAY);
+};
+
+// The following hack is required because the super constructor
+// might call promiseFulfilled before this.callback = fn is set
+//
+// The super constructor call must always be first so that fields
+// are initialized in the same order so that the sub-class instances
+// will share same memory layout as the super class instances
+
+// Override
+MappingPromiseArray.prototype._init = function () {};
+
+// Override
+MappingPromiseArray.prototype._promiseFulfilled = function (value, index) {
+ ASSERT(!this._isResolved());
+ var values = this._values;
+ var length = this.length();
+ var preservedValues = this._preservedValues;
+ var limit = this._limit;
+
+ // Callback has been called for this index if it's negative
+ if (index < 0) {
+ // Restore the actual index value
+ index = (index * -1) - 1;
+ values[index] = value;
+ if (limit >= 1) {
+ this._inFlight--;
+ this._drainQueue();
+ if (this._isResolved()) return true;
+ }
+ } else {
+ if (limit >= 1 && this._inFlight >= limit) {
+ values[index] = value;
+ this._queue.push(index);
+ return false;
+ }
+ if (preservedValues !== null) preservedValues[index] = value;
+
+ var promise = this._promise;
+ var callback = this._callback;
+ var receiver = promise._boundValue();
+ promise._pushContext();
+ var ret = tryCatch(callback).call(receiver, value, index, length);
+ var promiseCreated = promise._popContext();
+ debug.checkForgottenReturns(
+ ret,
+ promiseCreated,
+ preservedValues !== null ? "Promise.filter" : "Promise.map",
+ promise
+ );
+ if (ret === errorObj) {
+ this._reject(ret.e);
+ return true;
+ }
+
+ // If the mapper function returned a promise we simply reuse
+ // The MappingPromiseArray as a PromiseArray for round 2.
+ // To mark an index as "round 2" its inverted by adding +1 and
+ // multiplying by -1
+ var maybePromise = tryConvertToPromise(ret, this._promise);
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ var bitField = maybePromise._bitField;
+ USE(bitField);
+ if (BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) {
+ if (limit >= 1) this._inFlight++;
+ values[index] = maybePromise;
+ maybePromise._proxy(this, (index + 1) * -1);
+ return false;
+ } else if (BIT_FIELD_CHECK(IS_FULFILLED)) {
+ ret = maybePromise._value();
+ } else if (BIT_FIELD_CHECK(IS_REJECTED)) {
+ this._reject(maybePromise._reason());
+ return true;
+ } else {
+ this._cancel();
+ return true;
+ }
+ }
+ values[index] = ret;
+ }
+ var totalResolved = ++this._totalResolved;
+ if (totalResolved >= length) {
+ if (preservedValues !== null) {
+ this._filter(values, preservedValues);
+ } else {
+ this._resolve(values);
+ }
+ return true;
+ }
+ return false;
+};
+
+MappingPromiseArray.prototype._drainQueue = function () {
+ var queue = this._queue;
+ var limit = this._limit;
+ var values = this._values;
+ while (queue.length > 0 && this._inFlight < limit) {
+ if (this._isResolved()) return;
+ var index = queue.pop();
+ this._promiseFulfilled(values[index], index);
+ }
+};
+
+MappingPromiseArray.prototype._filter = function (booleans, values) {
+ var len = values.length;
+ var ret = new Array(len);
+ var j = 0;
+ for (var i = 0; i < len; ++i) {
+ if (booleans[i]) ret[j++] = values[i];
+ }
+ ret.length = j;
+ this._resolve(ret);
+};
+
+MappingPromiseArray.prototype.preservedValues = function () {
+ return this._preservedValues;
+};
+
+function map(promises, fn, options, _filter) {
+ if (typeof fn !== "function") {
+ return apiRejection(FUNCTION_ERROR + util.classString(fn));
+ }
+
+ var limit = 0;
+ if (options !== undefined) {
+ if (typeof options === "object" && options !== null) {
+ if (typeof options.concurrency !== "number") {
+ return Promise.reject(
+ new TypeError("'concurrency' must be a number but it is " +
+ util.classString(options.concurrency)));
+ }
+ limit = options.concurrency;
+ } else {
+ return Promise.reject(new TypeError(
+ "options argument must be an object but it is " +
+ util.classString(options)));
+ }
+ }
+ limit = typeof limit === "number" &&
+ isFinite(limit) && limit >= 1 ? limit : 0;
+ return new MappingPromiseArray(promises, fn, limit, _filter).promise();
+}
+
+Promise.prototype.map = function (fn, options) {
+ return map(this, fn, options, null);
+};
+
+Promise.map = function (promises, fn, options, _filter) {
+ return map(promises, fn, options, _filter);
+};
+
+
+};
diff --git a/src/method.js b/src/method.js
new file mode 100644
index 0000000..5255f36
--- /dev/null
+++ b/src/method.js
@@ -0,0 +1,57 @@
+"use strict";
+module.exports =
+function(Promise, INTERNAL, tryConvertToPromise, apiRejection, debug) {
+var util = require("./util");
+var ASSERT = require("./assert");
+var tryCatch = util.tryCatch;
+
+Promise.method = function (fn) {
+ if (typeof fn !== "function") {
+ throw new Promise.TypeError(FUNCTION_ERROR + util.classString(fn));
+ }
+ return function () {
+ var ret = new Promise(INTERNAL);
+ ret._captureStackTrace();
+ ret._pushContext();
+ var value = tryCatch(fn).apply(this, arguments);
+ var promiseCreated = ret._popContext();
+ debug.checkForgottenReturns(
+ value, promiseCreated, "Promise.method", ret);
+ ret._resolveFromSyncValue(value);
+ return ret;
+ };
+};
+
+Promise.attempt = Promise["try"] = function (fn) {
+ if (typeof fn !== "function") {
+ return apiRejection(FUNCTION_ERROR + util.classString(fn));
+ }
+ var ret = new Promise(INTERNAL);
+ ret._captureStackTrace();
+ ret._pushContext();
+ var value;
+ if (arguments.length > 1) {
+ debug.deprecated("calling Promise.try with more than 1 argument");
+ var arg = arguments[1];
+ var ctx = arguments[2];
+ value = util.isArray(arg) ? tryCatch(fn).apply(ctx, arg)
+ : tryCatch(fn).call(ctx, arg);
+ } else {
+ value = tryCatch(fn)();
+ }
+ var promiseCreated = ret._popContext();
+ debug.checkForgottenReturns(
+ value, promiseCreated, "Promise.try", ret);
+ ret._resolveFromSyncValue(value);
+ return ret;
+};
+
+Promise.prototype._resolveFromSyncValue = function (value) {
+ ASSERT(!this._isFollowing());
+ if (value === util.errorObj) {
+ this._rejectCallback(value.e, false);
+ } else {
+ this._resolveCallback(value, true);
+ }
+};
+};
diff --git a/src/nodeback.js b/src/nodeback.js
new file mode 100644
index 0000000..e753c31
--- /dev/null
+++ b/src/nodeback.js
@@ -0,0 +1,51 @@
+"use strict";
+var util = require("./util");
+var maybeWrapAsError = util.maybeWrapAsError;
+var errors = require("./errors");
+var OperationalError = errors.OperationalError;
+var es5 = require("./es5");
+
+function isUntypedError(obj) {
+ return obj instanceof Error &&
+ es5.getPrototypeOf(obj) === Error.prototype;
+}
+
+var rErrorKey = /^(?:name|message|stack|cause)$/;
+function wrapAsOperationalError(obj) {
+ var ret;
+ if (isUntypedError(obj)) {
+ ret = new OperationalError(obj);
+ ret.name = obj.name;
+ ret.message = obj.message;
+ ret.stack = obj.stack;
+ var keys = es5.keys(obj);
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ if (!rErrorKey.test(key)) {
+ ret[key] = obj[key];
+ }
+ }
+ return ret;
+ }
+ util.markAsOriginatingFromRejection(obj);
+ return obj;
+}
+
+function nodebackForPromise(promise, multiArgs) {
+ return function(err, value) {
+ if (promise === null) return;
+ if (err) {
+ var wrapped = wrapAsOperationalError(maybeWrapAsError(err));
+ promise._attachExtraTrace(wrapped);
+ promise._reject(wrapped);
+ } else if (!multiArgs) {
+ promise._fulfill(value);
+ } else {
+ INLINE_SLICE(args, arguments, 1);
+ promise._fulfill(args);
+ }
+ promise = null;
+ };
+}
+
+module.exports = nodebackForPromise;
diff --git a/src/nodeify.js b/src/nodeify.js
new file mode 100644
index 0000000..787efce
--- /dev/null
+++ b/src/nodeify.js
@@ -0,0 +1,62 @@
+"use strict";
+module.exports = function(Promise) {
+var util = require("./util");
+var async = Promise._async;
+var ASSERT = require("./assert");
+var tryCatch = util.tryCatch;
+var errorObj = util.errorObj;
+
+function spreadAdapter(val, nodeback) {
+ var promise = this;
+ if (!util.isArray(val)) return successAdapter.call(promise, val, nodeback);
+ var ret =
+ tryCatch(nodeback).apply(promise._boundValue(), [null].concat(val));
+ if (ret === errorObj) {
+ async.throwLater(ret.e);
+ }
+}
+
+function successAdapter(val, nodeback) {
+ var promise = this;
+ var receiver = promise._boundValue();
+ ASSERT(typeof nodeback == "function");
+ var ret = val === undefined
+ ? tryCatch(nodeback).call(receiver, null)
+ : tryCatch(nodeback).call(receiver, null, val);
+ if (ret === errorObj) {
+ async.throwLater(ret.e);
+ }
+}
+function errorAdapter(reason, nodeback) {
+ var promise = this;
+ if (!reason) {
+ var newReason = new Error(reason + "");
+ newReason.cause = reason;
+ reason = newReason;
+ ASSERT(!!reason);
+ }
+ ASSERT(typeof nodeback == "function");
+ var ret = tryCatch(nodeback).call(promise._boundValue(), reason);
+ if (ret === errorObj) {
+ async.throwLater(ret.e);
+ }
+}
+
+Promise.prototype.asCallback = Promise.prototype.nodeify = function (nodeback,
+ options) {
+ if (typeof nodeback == "function") {
+ var adapter = successAdapter;
+ if (options !== undefined && Object(options).spread) {
+ adapter = spreadAdapter;
+ }
+ this._then(
+ adapter,
+ errorAdapter,
+ undefined,
+ this,
+ nodeback
+ );
+ }
+ return this;
+};
+};
diff --git a/src/promise.js b/src/promise.js
new file mode 100644
index 0000000..f511f48
--- /dev/null
+++ b/src/promise.js
@@ -0,0 +1,827 @@
+"use strict";
+module.exports = function() {
+var makeSelfResolutionError = function () {
+ return new TypeError(CIRCULAR_RESOLUTION_ERROR);
+};
+var reflectHandler = function() {
+ return new Promise.PromiseInspection(this._target());
+};
+var apiRejection = function(msg) {
+ return Promise.reject(new TypeError(msg));
+};
+function Proxyable() {}
+var UNDEFINED_BINDING = {};
+var ASSERT = require("./assert");
+var util = require("./util");
+util.setReflectHandler(reflectHandler);
+
+var getDomain = function() {
+ var domain = process.domain;
+ if (domain === undefined) {
+ return null;
+ }
+ return domain;
+};
+var getContextDefault = function() {
+ return null;
+};
+var getContextDomain = function() {
+ return {
+ domain: getDomain(),
+ async: null
+ };
+};
+var AsyncResource = util.isNode && util.nodeSupportsAsyncResource ?
+ require("async_hooks").AsyncResource : null;
+var getContextAsyncHooks = function() {
+ return {
+ domain: getDomain(),
+ async: new AsyncResource("Bluebird::Promise")
+ };
+};
+var getContext = util.isNode ? getContextDomain : getContextDefault;
+util.notEnumerableProp(Promise, "_getContext", getContext);
+var enableAsyncHooks = function() {
+ getContext = getContextAsyncHooks;
+ util.notEnumerableProp(Promise, "_getContext", getContextAsyncHooks);
+};
+var disableAsyncHooks = function() {
+ getContext = getContextDomain;
+ util.notEnumerableProp(Promise, "_getContext", getContextDomain);
+};
+
+var es5 = require("./es5");
+var Async = require("./async");
+var async = new Async();
+es5.defineProperty(Promise, "_async", {value: async});
+var errors = require("./errors");
+var TypeError = Promise.TypeError = errors.TypeError;
+Promise.RangeError = errors.RangeError;
+var CancellationError = Promise.CancellationError = errors.CancellationError;
+Promise.TimeoutError = errors.TimeoutError;
+Promise.OperationalError = errors.OperationalError;
+Promise.RejectionError = errors.OperationalError;
+Promise.AggregateError = errors.AggregateError;
+var INTERNAL = function(){};
+var APPLY = {};
+var NEXT_FILTER = {};
+var tryConvertToPromise = require("./thenables")(Promise, INTERNAL);
+var PromiseArray =
+ require("./promise_array")(Promise, INTERNAL,
+ tryConvertToPromise, apiRejection, Proxyable);
+var Context = require("./context")(Promise);
+ /*jshint unused:false*/
+var createContext = Context.create;
+
+var debug = require("./debuggability")(Promise, Context,
+ enableAsyncHooks, disableAsyncHooks);
+var CapturedTrace = debug.CapturedTrace;
+var PassThroughHandlerContext =
+ require("./finally")(Promise, tryConvertToPromise, NEXT_FILTER);
+var catchFilter = require("./catch_filter")(NEXT_FILTER);
+var nodebackForPromise = require("./nodeback");
+var errorObj = util.errorObj;
+var tryCatch = util.tryCatch;
+function check(self, executor) {
+ if (self == null || self.constructor !== Promise) {
+ throw new TypeError(CONSTRUCT_ERROR_INVOCATION);
+ }
+ if (typeof executor !== "function") {
+ throw new TypeError(FUNCTION_ERROR + util.classString(executor));
+ }
+
+}
+
+function Promise(executor) {
+ if (executor !== INTERNAL) {
+ check(this, executor);
+ }
+ this._bitField = NO_STATE;
+ this._fulfillmentHandler0 = undefined;
+ this._rejectionHandler0 = undefined;
+ this._promise0 = undefined;
+ this._receiver0 = undefined;
+ this._resolveFromExecutor(executor);
+ this._promiseCreated();
+ this._fireEvent("promiseCreated", this);
+}
+
+Promise.prototype.toString = function () {
+ return "[object Promise]";
+};
+
+Promise.prototype.caught = Promise.prototype["catch"] = function (fn) {
+ var len = arguments.length;
+ if (len > 1) {
+ var catchInstances = new Array(len - 1),
+ j = 0, i;
+ for (i = 0; i < len - 1; ++i) {
+ var item = arguments[i];
+ if (util.isObject(item)) {
+ catchInstances[j++] = item;
+ } else {
+ return apiRejection("Catch statement predicate: " +
+ OBJECT_ERROR + util.classString(item));
+ }
+ }
+ catchInstances.length = j;
+ fn = arguments[i];
+
+ if (typeof fn !== "function") {
+ throw new TypeError("The last argument to .catch() " +
+ "must be a function, got " + util.toString(fn));
+ }
+ return this.then(undefined, catchFilter(catchInstances, fn, this));
+ }
+ return this.then(undefined, fn);
+};
+
+Promise.prototype.reflect = function () {
+ return this._then(reflectHandler,
+ reflectHandler, undefined, this, undefined);
+};
+
+Promise.prototype.then = function (didFulfill, didReject) {
+ if (debug.warnings() && arguments.length > 0 &&
+ typeof didFulfill !== "function" &&
+ typeof didReject !== "function") {
+ var msg = ".then() only accepts functions but was passed: " +
+ util.classString(didFulfill);
+ if (arguments.length > 1) {
+ msg += ", " + util.classString(didReject);
+ }
+ this._warn(msg);
+ }
+ return this._then(didFulfill, didReject, undefined, undefined, undefined);
+};
+
+Promise.prototype.done = function (didFulfill, didReject) {
+ var promise =
+ this._then(didFulfill, didReject, undefined, undefined, undefined);
+ promise._setIsFinal();
+};
+
+Promise.prototype.spread = function (fn) {
+ if (typeof fn !== "function") {
+ return apiRejection(FUNCTION_ERROR + util.classString(fn));
+ }
+ return this.all()._then(fn, undefined, undefined, APPLY, undefined);
+};
+
+Promise.prototype.toJSON = function () {
+ var ret = {
+ isFulfilled: false,
+ isRejected: false,
+ fulfillmentValue: undefined,
+ rejectionReason: undefined
+ };
+ if (this.isFulfilled()) {
+ ret.fulfillmentValue = this.value();
+ ret.isFulfilled = true;
+ } else if (this.isRejected()) {
+ ret.rejectionReason = this.reason();
+ ret.isRejected = true;
+ }
+ return ret;
+};
+
+Promise.prototype.all = function () {
+ if (arguments.length > 0) {
+ this._warn(".all() was passed arguments but it does not take any");
+ }
+ return new PromiseArray(this).promise();
+};
+
+Promise.prototype.error = function (fn) {
+ return this.caught(util.originatesFromRejection, fn);
+};
+
+Promise.getNewLibraryCopy = module.exports;
+
+Promise.is = function (val) {
+ return val instanceof Promise;
+};
+
+Promise.fromNode = Promise.fromCallback = function(fn) {
+ var ret = new Promise(INTERNAL);
+ ret._captureStackTrace();
+ var multiArgs = arguments.length > 1 ? !!Object(arguments[1]).multiArgs
+ : false;
+ var result = tryCatch(fn)(nodebackForPromise(ret, multiArgs));
+ if (result === errorObj) {
+ ret._rejectCallback(result.e, true);
+ }
+ if (!ret._isFateSealed()) ret._setAsyncGuaranteed();
+ return ret;
+};
+
+Promise.all = function (promises) {
+ return new PromiseArray(promises).promise();
+};
+
+Promise.cast = function (obj) {
+ var ret = tryConvertToPromise(obj);
+ if (!(ret instanceof Promise)) {
+ ret = new Promise(INTERNAL);
+ ret._captureStackTrace();
+ ret._setFulfilled();
+ ret._rejectionHandler0 = obj;
+ }
+ return ret;
+};
+
+Promise.resolve = Promise.fulfilled = Promise.cast;
+
+Promise.reject = Promise.rejected = function (reason) {
+ var ret = new Promise(INTERNAL);
+ ret._captureStackTrace();
+ ret._rejectCallback(reason, true);
+ return ret;
+};
+
+Promise.setScheduler = function(fn) {
+ if (typeof fn !== "function") {
+ throw new TypeError(FUNCTION_ERROR + util.classString(fn));
+ }
+ return async.setScheduler(fn);
+};
+
+Promise.prototype._then = function (
+ didFulfill,
+ didReject,
+ _, // For fast-cast compatibility between bluebird versions
+ receiver,
+ internalData
+) {
+ ASSERT(arguments.length === 5);
+ var haveInternalData = internalData !== undefined;
+ var promise = haveInternalData ? internalData : new Promise(INTERNAL);
+ var target = this._target();
+ var bitField = target._bitField;
+
+ if (!haveInternalData) {
+ promise._propagateFrom(this, PROPAGATE_ALL);
+ promise._captureStackTrace();
+ if (receiver === undefined &&
+ BIT_FIELD_CHECK(IS_BOUND, this._bitField)) {
+ if (!BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) {
+ receiver = this._boundValue();
+ } else {
+ receiver = target === this ? undefined : this._boundTo;
+ }
+ }
+ this._fireEvent("promiseChained", this, promise);
+ }
+
+ var context = getContext();
+ if (!BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) {
+ var handler, value, settler = target._settlePromiseCtx;
+ if (BIT_FIELD_CHECK(IS_FULFILLED)) {
+ value = target._rejectionHandler0;
+ handler = didFulfill;
+ } else if (BIT_FIELD_CHECK(IS_REJECTED)) {
+ value = target._fulfillmentHandler0;
+ handler = didReject;
+ target._unsetRejectionIsUnhandled();
+ } else {
+ settler = target._settlePromiseLateCancellationObserver;
+ value = new CancellationError(LATE_CANCELLATION_OBSERVER);
+ target._attachExtraTrace(value);
+ handler = didReject;
+ }
+
+ async.invoke(settler, target, {
+ handler: util.contextBind(context, handler),
+ promise: promise,
+ receiver: receiver,
+ value: value
+ });
+ } else {
+ target._addCallbacks(didFulfill, didReject, promise,
+ receiver, context);
+ }
+
+ return promise;
+};
+
+Promise.prototype._length = function () {
+ ASSERT(arguments.length === 0);
+ return this._bitField & LENGTH_MASK;
+};
+
+Promise.prototype._isFateSealed = function () {
+ return (this._bitField & IS_FATE_SEALED) !== 0;
+};
+
+Promise.prototype._isFollowing = function () {
+ return (this._bitField & IS_FOLLOWING) === IS_FOLLOWING;
+};
+
+Promise.prototype._setLength = function (len) {
+ this._bitField = (this._bitField & LENGTH_CLEAR_MASK) |
+ (len & LENGTH_MASK);
+};
+
+Promise.prototype._setFulfilled = function () {
+ this._bitField = this._bitField | IS_FULFILLED;
+ this._fireEvent("promiseFulfilled", this);
+};
+
+Promise.prototype._setRejected = function () {
+ this._bitField = this._bitField | IS_REJECTED;
+ this._fireEvent("promiseRejected", this);
+};
+
+Promise.prototype._setFollowing = function () {
+ this._bitField = this._bitField | IS_FOLLOWING;
+ this._fireEvent("promiseResolved", this);
+};
+
+Promise.prototype._setIsFinal = function () {
+ this._bitField = this._bitField | IS_FINAL;
+};
+
+Promise.prototype._isFinal = function () {
+ return (this._bitField & IS_FINAL) > 0;
+};
+
+Promise.prototype._unsetCancelled = function() {
+ this._bitField = this._bitField & (~IS_CANCELLED);
+};
+
+Promise.prototype._setCancelled = function() {
+ this._bitField = this._bitField | IS_CANCELLED;
+ this._fireEvent("promiseCancelled", this);
+};
+
+Promise.prototype._setWillBeCancelled = function() {
+ this._bitField = this._bitField | WILL_BE_CANCELLED;
+};
+
+Promise.prototype._setAsyncGuaranteed = function() {
+ if (async.hasCustomScheduler()) return;
+ var bitField = this._bitField;
+ this._bitField = bitField |
+ (((bitField & NO_ASYNC_GUARANTEE) >> ASYNC_GUARANTEE_SHIFT) ^
+ IS_ASYNC_GUARANTEED);
+};
+
+Promise.prototype._setNoAsyncGuarantee = function() {
+ this._bitField = (this._bitField | NO_ASYNC_GUARANTEE) &
+ (~IS_ASYNC_GUARANTEED);
+};
+
+Promise.prototype._receiverAt = function (index) {
+ ASSERT(!this._isFollowing());
+ var ret = index === 0 ? this._receiver0 : this[
+ index * CALLBACK_SIZE - CALLBACK_SIZE + CALLBACK_RECEIVER_OFFSET];
+ //Only use the bound value when not calling internal methods
+ if (ret === UNDEFINED_BINDING) {
+ return undefined;
+ } else if (ret === undefined && this._isBound()) {
+ return this._boundValue();
+ }
+ return ret;
+};
+
+Promise.prototype._promiseAt = function (index) {
+ ASSERT(index > 0);
+ ASSERT(!this._isFollowing());
+ return this[
+ index * CALLBACK_SIZE - CALLBACK_SIZE + CALLBACK_PROMISE_OFFSET];
+};
+
+Promise.prototype._fulfillmentHandlerAt = function (index) {
+ ASSERT(!this._isFollowing());
+ ASSERT(index > 0);
+ return this[
+ index * CALLBACK_SIZE - CALLBACK_SIZE + CALLBACK_FULFILL_OFFSET];
+};
+
+Promise.prototype._rejectionHandlerAt = function (index) {
+ ASSERT(!this._isFollowing());
+ ASSERT(index > 0);
+ return this[
+ index * CALLBACK_SIZE - CALLBACK_SIZE + CALLBACK_REJECT_OFFSET];
+};
+
+Promise.prototype._boundValue = function() {};
+
+Promise.prototype._migrateCallback0 = function (follower) {
+ var bitField = follower._bitField;
+ var fulfill = follower._fulfillmentHandler0;
+ var reject = follower._rejectionHandler0;
+ var promise = follower._promise0;
+ var receiver = follower._receiverAt(0);
+ if (receiver === undefined) receiver = UNDEFINED_BINDING;
+ this._addCallbacks(fulfill, reject, promise, receiver, null);
+};
+
+Promise.prototype._migrateCallbackAt = function (follower, index) {
+ ASSERT(index > 0);
+ var fulfill = follower._fulfillmentHandlerAt(index);
+ var reject = follower._rejectionHandlerAt(index);
+ var promise = follower._promiseAt(index);
+ var receiver = follower._receiverAt(index);
+ if (receiver === undefined) receiver = UNDEFINED_BINDING;
+ this._addCallbacks(fulfill, reject, promise, receiver, null);
+};
+
+Promise.prototype._addCallbacks = function (
+ fulfill,
+ reject,
+ promise,
+ receiver,
+ context
+) {
+ ASSERT(typeof context === "object");
+ ASSERT(!this._isFateSealed());
+ ASSERT(!this._isFollowing());
+ var index = this._length();
+
+ if (index >= MAX_LENGTH - CALLBACK_SIZE) {
+ index = 0;
+ this._setLength(0);
+ }
+
+ if (index === 0) {
+ ASSERT(this._promise0 === undefined);
+ ASSERT(this._receiver0 === undefined);
+ ASSERT(this._fulfillmentHandler0 === undefined);
+ ASSERT(this._rejectionHandler0 === undefined);
+
+ this._promise0 = promise;
+ this._receiver0 = receiver;
+ if (typeof fulfill === "function") {
+ this._fulfillmentHandler0 = util.contextBind(context, fulfill);
+ }
+ if (typeof reject === "function") {
+ this._rejectionHandler0 = util.contextBind(context, reject);
+ }
+ } else {
+ ASSERT(this[base + CALLBACK_PROMISE_OFFSET] === undefined);
+ ASSERT(this[base + CALLBACK_RECEIVER_OFFSET] === undefined);
+ ASSERT(this[base + CALLBACK_FULFILL_OFFSET] === undefined);
+ ASSERT(this[base + CALLBACK_REJECT_OFFSET] === undefined);
+ var base = index * CALLBACK_SIZE - CALLBACK_SIZE;
+ this[base + CALLBACK_PROMISE_OFFSET] = promise;
+ this[base + CALLBACK_RECEIVER_OFFSET] = receiver;
+ if (typeof fulfill === "function") {
+ this[base + CALLBACK_FULFILL_OFFSET] =
+ util.contextBind(context, fulfill);
+ }
+ if (typeof reject === "function") {
+ this[base + CALLBACK_REJECT_OFFSET] =
+ util.contextBind(context, reject);
+ }
+ }
+ this._setLength(index + 1);
+ return index;
+};
+
+Promise.prototype._proxy = function (proxyable, arg) {
+ ASSERT(proxyable instanceof Proxyable);
+ ASSERT(!(arg instanceof Promise));
+ ASSERT(!this._isFollowing());
+ ASSERT(arguments.length === 2);
+ ASSERT(!this._isFateSealed());
+ this._addCallbacks(undefined, undefined, arg, proxyable, null);
+};
+
+Promise.prototype._resolveCallback = function(value, shouldBind) {
+ if (BIT_FIELD_CHECK(IS_FATE_SEALED, this._bitField)) return;
+ if (value === this)
+ return this._rejectCallback(makeSelfResolutionError(), false);
+ var maybePromise = tryConvertToPromise(value, this);
+ if (!(maybePromise instanceof Promise)) return this._fulfill(value);
+
+ if (shouldBind) this._propagateFrom(maybePromise, PROPAGATE_BIND);
+
+
+ var promise = maybePromise._target();
+
+ if (promise === this) {
+ this._reject(makeSelfResolutionError());
+ return;
+ }
+
+ var bitField = promise._bitField;
+ if (BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) {
+ var len = this._length();
+ if (len > 0) promise._migrateCallback0(this);
+ for (var i = 1; i < len; ++i) {
+ promise._migrateCallbackAt(this, i);
+ }
+ this._setFollowing();
+ this._setLength(0);
+ this._setFollowee(maybePromise);
+ } else if (BIT_FIELD_CHECK(IS_FULFILLED)) {
+ this._fulfill(promise._value());
+ } else if (BIT_FIELD_CHECK(IS_REJECTED)) {
+ this._reject(promise._reason());
+ } else {
+ var reason = new CancellationError(LATE_CANCELLATION_OBSERVER);
+ promise._attachExtraTrace(reason);
+ this._reject(reason);
+ }
+};
+
+Promise.prototype._rejectCallback =
+function(reason, synchronous, ignoreNonErrorWarnings) {
+ var trace = util.ensureErrorObject(reason);
+ var hasStack = trace === reason;
+ if (!hasStack && !ignoreNonErrorWarnings && debug.warnings()) {
+ var message = "a promise was rejected with a non-error: " +
+ util.classString(reason);
+ this._warn(message, true);
+ }
+ this._attachExtraTrace(trace, synchronous ? hasStack : false);
+ this._reject(reason);
+};
+
+Promise.prototype._resolveFromExecutor = function (executor) {
+ if (executor === INTERNAL) return;
+ ASSERT(typeof executor === "function");
+ var promise = this;
+ this._captureStackTrace();
+ this._pushContext();
+ var synchronous = true;
+ var r = this._execute(executor, function(value) {
+ promise._resolveCallback(value);
+ }, function (reason) {
+ promise._rejectCallback(reason, synchronous);
+ });
+ synchronous = false;
+ this._popContext();
+
+ if (r !== undefined) {
+ promise._rejectCallback(r, true);
+ }
+};
+
+Promise.prototype._settlePromiseFromHandler = function (
+ handler, receiver, value, promise
+) {
+ var bitField = promise._bitField;
+ if (BIT_FIELD_CHECK(IS_CANCELLED)) return;
+ promise._pushContext();
+ var x;
+ if (receiver === APPLY) {
+ if (!value || typeof value.length !== "number") {
+ x = errorObj;
+ x.e = new TypeError("cannot .spread() a non-array: " +
+ util.classString(value));
+ } else {
+ x = tryCatch(handler).apply(this._boundValue(), value);
+ }
+ } else {
+ x = tryCatch(handler).call(receiver, value);
+ }
+ var promiseCreated = promise._popContext();
+ bitField = promise._bitField;
+ if (BIT_FIELD_CHECK(IS_CANCELLED)) return;
+
+ ASSERT(!promise._isFateSealed());
+
+ if (x === NEXT_FILTER) {
+ promise._reject(value);
+ } else if (x === errorObj) {
+ promise._rejectCallback(x.e, false);
+ } else {
+ debug.checkForgottenReturns(x, promiseCreated, "", promise, this);
+ promise._resolveCallback(x);
+ }
+};
+
+Promise.prototype._target = function() {
+ var ret = this;
+ while (ret._isFollowing()) ret = ret._followee();
+ return ret;
+};
+
+Promise.prototype._followee = function() {
+ ASSERT(this._isFollowing());
+ ASSERT(this._rejectionHandler0 instanceof Promise);
+ return this._rejectionHandler0;
+};
+
+Promise.prototype._setFollowee = function(promise) {
+ ASSERT(this._isFollowing());
+ ASSERT(!(this._rejectionHandler0 instanceof Promise));
+ this._rejectionHandler0 = promise;
+};
+
+Promise.prototype._settlePromise = function(promise, handler, receiver, value) {
+ ASSERT(!this._isFollowing());
+ var isPromise = promise instanceof Promise;
+ var bitField = this._bitField;
+ var asyncGuaranteed = BIT_FIELD_CHECK(IS_ASYNC_GUARANTEED);
+ if (BIT_FIELD_CHECK(IS_CANCELLED)) {
+ if (isPromise) promise._invokeInternalOnCancel();
+
+ if (receiver instanceof PassThroughHandlerContext &&
+ receiver.isFinallyHandler()) {
+ receiver.cancelPromise = promise;
+ if (tryCatch(handler).call(receiver, value) === errorObj) {
+ promise._reject(errorObj.e);
+ }
+ } else if (handler === reflectHandler) {
+ promise._fulfill(reflectHandler.call(receiver));
+ } else if (receiver instanceof Proxyable) {
+ receiver._promiseCancelled(promise);
+ } else if (isPromise || promise instanceof PromiseArray) {
+ promise._cancel();
+ } else {
+ receiver.cancel();
+ }
+ } else if (typeof handler === "function") {
+ //if promise is not instanceof Promise
+ //it is internally smuggled data
+ if (!isPromise) {
+ handler.call(receiver, value, promise);
+ } else {
+ if (asyncGuaranteed) promise._setAsyncGuaranteed();
+ this._settlePromiseFromHandler(handler, receiver, value, promise);
+ }
+ } else if (receiver instanceof Proxyable) {
+ if (!receiver._isResolved()) {
+ if (BIT_FIELD_CHECK(IS_FULFILLED)) {
+ receiver._promiseFulfilled(value, promise);
+ } else {
+ receiver._promiseRejected(value, promise);
+ }
+ }
+ } else if (isPromise) {
+ if (asyncGuaranteed) promise._setAsyncGuaranteed();
+ if (BIT_FIELD_CHECK(IS_FULFILLED)) {
+ promise._fulfill(value);
+ } else {
+ promise._reject(value);
+ }
+ }
+};
+
+Promise.prototype._settlePromiseLateCancellationObserver = function(ctx) {
+ var handler = ctx.handler;
+ var promise = ctx.promise;
+ var receiver = ctx.receiver;
+ var value = ctx.value;
+ if (typeof handler === "function") {
+ if (!(promise instanceof Promise)) {
+ handler.call(receiver, value, promise);
+ } else {
+ this._settlePromiseFromHandler(handler, receiver, value, promise);
+ }
+ } else if (promise instanceof Promise) {
+ promise._reject(value);
+ }
+};
+
+Promise.prototype._settlePromiseCtx = function(ctx) {
+ this._settlePromise(ctx.promise, ctx.handler, ctx.receiver, ctx.value);
+};
+
+Promise.prototype._settlePromise0 = function(handler, value, bitField) {
+ var promise = this._promise0;
+ var receiver = this._receiverAt(0);
+ this._promise0 = undefined;
+ this._receiver0 = undefined;
+ this._settlePromise(promise, handler, receiver, value);
+};
+
+Promise.prototype._clearCallbackDataAtIndex = function(index) {
+ ASSERT(!this._isFollowing());
+ ASSERT(index > 0);
+ var base = index * CALLBACK_SIZE - CALLBACK_SIZE;
+ this[base + CALLBACK_PROMISE_OFFSET] =
+ this[base + CALLBACK_RECEIVER_OFFSET] =
+ this[base + CALLBACK_FULFILL_OFFSET] =
+ this[base + CALLBACK_REJECT_OFFSET] = undefined;
+};
+
+Promise.prototype._fulfill = function (value) {
+ var bitField = this._bitField;
+ if (BIT_FIELD_READ(IS_FATE_SEALED)) return;
+ if (value === this) {
+ var err = makeSelfResolutionError();
+ this._attachExtraTrace(err);
+ return this._reject(err);
+ }
+ this._setFulfilled();
+ this._rejectionHandler0 = value;
+
+ if (BIT_FIELD_READ(LENGTH_MASK) > 0) {
+ if (BIT_FIELD_CHECK(IS_ASYNC_GUARANTEED)) {
+ this._settlePromises();
+ } else {
+ async.settlePromises(this);
+ }
+ this._dereferenceTrace();
+ }
+};
+
+Promise.prototype._reject = function (reason) {
+ var bitField = this._bitField;
+ if (BIT_FIELD_READ(IS_FATE_SEALED)) return;
+ this._setRejected();
+ this._fulfillmentHandler0 = reason;
+
+ if (this._isFinal()) {
+ ASSERT(this._length() === 0);
+ return async.fatalError(reason, util.isNode);
+ }
+
+ if (BIT_FIELD_READ(LENGTH_MASK) > 0) {
+ async.settlePromises(this);
+ } else {
+ this._ensurePossibleRejectionHandled();
+ }
+};
+
+Promise.prototype._fulfillPromises = function (len, value) {
+ for (var i = 1; i < len; i++) {
+ var handler = this._fulfillmentHandlerAt(i);
+ var promise = this._promiseAt(i);
+ var receiver = this._receiverAt(i);
+ this._clearCallbackDataAtIndex(i);
+ this._settlePromise(promise, handler, receiver, value);
+ }
+};
+
+Promise.prototype._rejectPromises = function (len, reason) {
+ for (var i = 1; i < len; i++) {
+ var handler = this._rejectionHandlerAt(i);
+ var promise = this._promiseAt(i);
+ var receiver = this._receiverAt(i);
+ this._clearCallbackDataAtIndex(i);
+ this._settlePromise(promise, handler, receiver, reason);
+ }
+};
+
+Promise.prototype._settlePromises = function () {
+ var bitField = this._bitField;
+ var len = BIT_FIELD_READ(LENGTH_MASK);
+
+ if (len > 0) {
+ if (BIT_FIELD_CHECK(IS_REJECTED_OR_CANCELLED)) {
+ var reason = this._fulfillmentHandler0;
+ this._settlePromise0(this._rejectionHandler0, reason, bitField);
+ this._rejectPromises(len, reason);
+ } else {
+ var value = this._rejectionHandler0;
+ this._settlePromise0(this._fulfillmentHandler0, value, bitField);
+ this._fulfillPromises(len, value);
+ }
+ this._setLength(0);
+ }
+ this._clearCancellationData();
+};
+
+Promise.prototype._settledValue = function() {
+ ASSERT(!this._isFollowing());
+ ASSERT(this._isFateSealed());
+ var bitField = this._bitField;
+ if (BIT_FIELD_CHECK(IS_FULFILLED)) {
+ return this._rejectionHandler0;
+ } else if (BIT_FIELD_CHECK(IS_REJECTED)) {
+ return this._fulfillmentHandler0;
+ }
+ // Implicit undefined for cancelled promise.
+};
+
+if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
+ es5.defineProperty(Promise.prototype, Symbol.toStringTag, {
+ get: function () {
+ return "Object";
+ }
+ });
+}
+
+function deferResolve(v) {this.promise._resolveCallback(v);}
+function deferReject(v) {this.promise._rejectCallback(v, false);}
+
+Promise.defer = Promise.pending = function() {
+ debug.deprecated("Promise.defer", "new Promise");
+ var promise = new Promise(INTERNAL);
+ return {
+ promise: promise,
+ resolve: deferResolve,
+ reject: deferReject
+ };
+};
+
+util.notEnumerableProp(Promise,
+ "_makeSelfResolutionError",
+ makeSelfResolutionError);
+
+require("./method")(Promise, INTERNAL, tryConvertToPromise, apiRejection,
+ debug);
+require("./bind")(Promise, INTERNAL, tryConvertToPromise, debug);
+require("./cancel")(Promise, PromiseArray, apiRejection, debug);
+require("./direct_resolve")(Promise);
+require("./synchronous_inspection")(Promise);
+require("./join")(
+ Promise, PromiseArray, tryConvertToPromise, INTERNAL, async);
+Promise.Promise = Promise;
+Promise.version = "__VERSION__";
+};
diff --git a/src/promise_array.js b/src/promise_array.js
new file mode 100644
index 0000000..6256889
--- /dev/null
+++ b/src/promise_array.js
@@ -0,0 +1,203 @@
+"use strict";
+module.exports = function(Promise, INTERNAL, tryConvertToPromise,
+ apiRejection, Proxyable) {
+var ASSERT = require("./assert");
+var util = require("./util");
+var isArray = util.isArray;
+
+//To avoid eagerly allocating the objects
+//and also because undefined cannot be smuggled
+function toResolutionValue(val) {
+ switch(val) {
+ case RESOLVE_ARRAY: return [];
+ case RESOLVE_OBJECT: return {};
+ case RESOLVE_MAP: return new Map();
+ }
+ ASSERT(false);
+}
+
+function PromiseArray(values) {
+ ASSERT(arguments.length === 1);
+ var promise = this._promise = new Promise(INTERNAL);
+ if (values instanceof Promise) {
+ promise._propagateFrom(values, PROPAGATE_ALL);
+ values.suppressUnhandledRejections();
+ }
+ promise._setOnCancel(this);
+ this._values = values;
+ this._length = 0;
+ this._totalResolved = 0;
+ this._init(undefined, RESOLVE_ARRAY);
+}
+util.inherits(PromiseArray, Proxyable);
+
+PromiseArray.prototype.length = function () {
+ return this._length;
+};
+
+PromiseArray.prototype.promise = function () {
+ return this._promise;
+};
+
+PromiseArray.prototype._init = function init(_, resolveValueIfEmpty) {
+ var values = tryConvertToPromise(this._values, this._promise);
+ if (values instanceof Promise) {
+ values = values._target();
+ var bitField = values._bitField;
+ USE(bitField);
+ this._values = values;
+
+ if (BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) {
+ ASSERT(typeof resolveValueIfEmpty === "number");
+ ASSERT(resolveValueIfEmpty < 0);
+ this._promise._setAsyncGuaranteed();
+ return values._then(
+ init,
+ this._reject,
+ undefined,
+ this,
+ resolveValueIfEmpty
+ );
+ } else if (BIT_FIELD_CHECK(IS_FULFILLED)) {
+ values = values._value();
+ } else if (BIT_FIELD_CHECK(IS_REJECTED)) {
+ return this._reject(values._reason());
+ } else {
+ return this._cancel();
+ }
+ }
+ values = util.asArray(values);
+ if (values === null) {
+ var err = apiRejection(
+ COLLECTION_ERROR + util.classString(values)).reason();
+ this._promise._rejectCallback(err, false);
+ return;
+ }
+
+ if (values.length === 0) {
+ if (resolveValueIfEmpty === RESOLVE_CALL_METHOD) {
+ this._resolveEmptyArray();
+ }
+ else {
+ this._resolve(toResolutionValue(resolveValueIfEmpty));
+ }
+ return;
+ }
+ this._iterate(values);
+};
+
+PromiseArray.prototype._iterate = function(values) {
+ var len = this.getActualLength(values.length);
+ this._length = len;
+ this._values = this.shouldCopyValues() ? new Array(len) : this._values;
+ var result = this._promise;
+ var isResolved = false;
+ var bitField = null;
+ for (var i = 0; i < len; ++i) {
+ var maybePromise = tryConvertToPromise(values[i], result);
+
+ if (maybePromise instanceof Promise) {
+ maybePromise = maybePromise._target();
+ bitField = maybePromise._bitField;
+ } else {
+ bitField = null;
+ }
+
+ if (isResolved) {
+ if (bitField !== null) {
+ maybePromise.suppressUnhandledRejections();
+ }
+ } else if (bitField !== null) {
+ if (BIT_FIELD_CHECK(IS_PENDING_AND_WAITING_NEG)) {
+ // Optimized for just passing the updates through
+ maybePromise._proxy(this, i);
+ this._values[i] = maybePromise;
+ } else if (BIT_FIELD_CHECK(IS_FULFILLED)) {
+ isResolved = this._promiseFulfilled(maybePromise._value(), i);
+ } else if (BIT_FIELD_CHECK(IS_REJECTED)) {
+ isResolved = this._promiseRejected(maybePromise._reason(), i);
+ } else {
+ isResolved = this._promiseCancelled(i);
+ }
+ } else {
+ isResolved = this._promiseFulfilled(maybePromise, i);
+ }
+ ASSERT(typeof isResolved === "boolean");
+ }
+ if (!isResolved) result._setAsyncGuaranteed();
+};
+
+PromiseArray.prototype._isResolved = function () {
+ return this._values === null;
+};
+
+PromiseArray.prototype._resolve = function (value) {
+ ASSERT(!this._isResolved());
+ ASSERT(!(value instanceof Promise));
+ this._values = null;
+ this._promise._fulfill(value);
+};
+
+PromiseArray.prototype._cancel = function() {
+ if (this._isResolved() || !this._promise._isCancellable()) return;
+ this._values = null;
+ this._promise._cancel();
+};
+
+PromiseArray.prototype._reject = function (reason) {
+ ASSERT(!this._isResolved());
+ this._values = null;
+ this._promise._rejectCallback(reason, false);
+};
+
+PromiseArray.prototype._promiseFulfilled = function (value, index) {
+ ASSERT(!this._isResolved());
+ ASSERT(isArray(this._values));
+ ASSERT(typeof index === "number");
+ this._values[index] = value;
+ var totalResolved = ++this._totalResolved;
+ if (totalResolved >= this._length) {
+ this._resolve(this._values);
+ return true;
+ }
+ return false;
+};
+
+PromiseArray.prototype._promiseCancelled = function() {
+ this._cancel();
+ return true;
+};
+
+PromiseArray.prototype._promiseRejected = function (reason) {
+ ASSERT(!this._isResolved());
+ ASSERT(isArray(this._values));
+ this._totalResolved++;
+ this._reject(reason);
+ return true;
+};
+
+PromiseArray.prototype._resultCancelled = function() {
+ if (this._isResolved()) return;
+ var values = this._values;
+ this._cancel();
+ if (values instanceof Promise) {
+ values.cancel();
+ } else {
+ for (var i = 0; i < values.length; ++i) {
+ if (values[i] instanceof Promise) {
+ values[i].cancel();
+ }
+ }
+ }
+};
+
+PromiseArray.prototype.shouldCopyValues = function () {
+ return true;
+};
+
+PromiseArray.prototype.getActualLength = function (len) {
+ return len;
+};
+
+return PromiseArray;
+};
diff --git a/src/promisify.js b/src/promisify.js
new file mode 100644
index 0000000..a0bebf3
--- /dev/null
+++ b/src/promisify.js
@@ -0,0 +1,326 @@
+"use strict";
+module.exports = function(Promise, INTERNAL) {
+var THIS = {};
+var util = require("./util");
+var nodebackForPromise = require("./nodeback");
+var withAppended = util.withAppended;
+var maybeWrapAsError = util.maybeWrapAsError;
+var canEvaluate = util.canEvaluate;
+var ASSERT = require("./assert");
+var TypeError = require("./errors").TypeError;
+var defaultSuffix = AFTER_PROMISIFIED_SUFFIX;
+var defaultPromisified = {__isPromisified__: true};
+var noCopyProps = [
+ "arity", // Firefox 4
+ "length",
+ "name",
+ "arguments",
+ "caller",
+ "callee",
+ "prototype",
+ "__isPromisified__"
+];
+var noCopyPropsPattern = new RegExp("^(?:" + noCopyProps.join("|") + ")$");
+
+var defaultFilter = function(name) {
+ return util.isIdentifier(name) &&
+ name.charAt(0) !== "_" &&
+ name !== "constructor";
+};
+
+function propsFilter(key) {
+ return !noCopyPropsPattern.test(key);
+}
+
+function isPromisified(fn) {
+ try {
+ return fn.__isPromisified__ === true;
+ }
+ catch (e) {
+ return false;
+ }
+}
+
+function hasPromisified(obj, key, suffix) {
+ var val = util.getDataPropertyOrDefault(obj, key + suffix,
+ defaultPromisified);
+ return val ? isPromisified(val) : false;
+}
+function checkValid(ret, suffix, suffixRegexp) {
+ // Verify that in the list of methods to promisify there is no
+ // method that has a name ending in "Async"-suffix while
+ // also having a method with the same name but no Async suffix
+ for (var i = 0; i < ret.length; i += 2) {
+ var key = ret[i];
+ if (suffixRegexp.test(key)) {
+ var keyWithoutAsyncSuffix = key.replace(suffixRegexp, "");
+ for (var j = 0; j < ret.length; j += 2) {
+ if (ret[j] === keyWithoutAsyncSuffix) {
+ throw new TypeError(PROMISIFICATION_NORMAL_METHODS_ERROR
+ .replace("%s", suffix));
+ }
+ }
+ }
+ }
+}
+
+function promisifiableMethods(obj, suffix, suffixRegexp, filter) {
+ var keys = util.inheritedDataKeys(obj);
+ var ret = [];
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ var value = obj[key];
+ var passesDefaultFilter = filter === defaultFilter
+ ? true : defaultFilter(key, value, obj);
+ if (typeof value === "function" &&
+ !isPromisified(value) &&
+ !hasPromisified(obj, key, suffix) &&
+ filter(key, value, obj, passesDefaultFilter)) {
+ ret.push(key, value);
+ }
+ }
+ checkValid(ret, suffix, suffixRegexp);
+ return ret;
+}
+
+var escapeIdentRegex = function(str) {
+ return str.replace(/([$])/, "\\$");
+};
+
+var makeNodePromisifiedEval;
+if (!__BROWSER__) {
+//Gives an optimal sequence of argument count to try given a formal parameter
+//.length for a function
+var switchCaseArgumentOrder = function(likelyArgumentCount) {
+ var ret = [likelyArgumentCount];
+ var min = Math.max(0, likelyArgumentCount - 1 - PARAM_COUNTS_TO_TRY);
+ for(var i = likelyArgumentCount - 1; i >= min; --i) {
+ ret.push(i);
+ }
+ for(var i = likelyArgumentCount + 1; i <= PARAM_COUNTS_TO_TRY; ++i) {
+ ret.push(i);
+ }
+ return ret;
+};
+
+var argumentSequence = function(argumentCount) {
+ return util.filledRange(argumentCount, "_arg", "");
+};
+
+var parameterDeclaration = function(parameterCount) {
+ return util.filledRange(
+ Math.max(parameterCount, PARAM_COUNTS_TO_TRY), "_arg", "");
+};
+
+var parameterCount = function(fn) {
+ if (typeof fn.length === "number") {
+ return Math.max(Math.min(fn.length, MAX_PARAM_COUNT + 1), 0);
+ }
+ //Unsupported .length for functions
+ return 0;
+};
+
+makeNodePromisifiedEval =
+function(callback, receiver, originalName, fn, _, multiArgs) {
+ //-1 for the callback parameter
+ var newParameterCount = Math.max(0, parameterCount(fn) - 1);
+ var argumentOrder = switchCaseArgumentOrder(newParameterCount);
+ var shouldProxyThis = typeof callback === "string" || receiver === THIS;
+
+ function generateCallForArgumentCount(count) {
+ var args = argumentSequence(count).join(", ");
+ var comma = count > 0 ? ", " : "";
+ var ret;
+ if (shouldProxyThis) {
+ ret = "ret = callback.call(this, {{args}}, nodeback); break;\n";
+ } else {
+ ret = receiver === undefined
+ ? "ret = callback({{args}}, nodeback); break;\n"
+ : "ret = callback.call(receiver, {{args}}, nodeback); break;\n";
+ }
+ return ret.replace("{{args}}", args).replace(", ", comma);
+ }
+
+ function generateArgumentSwitchCase() {
+ var ret = "";
+ for (var i = 0; i < argumentOrder.length; ++i) {
+ ret += "case " + argumentOrder[i] +":" +
+ generateCallForArgumentCount(argumentOrder[i]);
+ }
+
+ ret += " \n\
+ default: \n\
+ var args = new Array(len + 1); \n\
+ var i = 0; \n\
+ for (var i = 0; i < len; ++i) { \n\
+ args[i] = arguments[i]; \n\
+ } \n\
+ args[i] = nodeback; \n\
+ [CodeForCall] \n\
+ break; \n\
+ ".replace("[CodeForCall]", (shouldProxyThis
+ ? "ret = callback.apply(this, args);\n"
+ : "ret = callback.apply(receiver, args);\n"));
+ return ret;
+ }
+
+ var getFunctionCode = typeof callback === "string"
+ ? ("this != null ? this['"+callback+"'] : fn")
+ : "fn";
+ var body = "'use strict'; \n\
+ var ret = function (Parameters) { \n\
+ 'use strict'; \n\
+ var len = arguments.length; \n\
+ var promise = new Promise(INTERNAL); \n\
+ promise._captureStackTrace(); \n\
+ var nodeback = nodebackForPromise(promise, " + multiArgs + "); \n\
+ var ret; \n\
+ var callback = tryCatch([GetFunctionCode]); \n\
+ switch(len) { \n\
+ [CodeForSwitchCase] \n\
+ } \n\
+ if (ret === errorObj) { \n\
+ promise._rejectCallback(maybeWrapAsError(ret.e), true, true);\n\
+ } \n\
+ if (!promise._isFateSealed()) promise._setAsyncGuaranteed(); \n\
+ return promise; \n\
+ }; \n\
+ notEnumerableProp(ret, '__isPromisified__', true); \n\
+ return ret; \n\
+ ".replace("[CodeForSwitchCase]", generateArgumentSwitchCase())
+ .replace("[GetFunctionCode]", getFunctionCode);
+ body = body.replace("Parameters", parameterDeclaration(newParameterCount));
+ return new Function("Promise",
+ "fn",
+ "receiver",
+ "withAppended",
+ "maybeWrapAsError",
+ "nodebackForPromise",
+ "tryCatch",
+ "errorObj",
+ "notEnumerableProp",
+ "INTERNAL",
+ body)(
+ Promise,
+ fn,
+ receiver,
+ withAppended,
+ maybeWrapAsError,
+ nodebackForPromise,
+ util.tryCatch,
+ util.errorObj,
+ util.notEnumerableProp,
+ INTERNAL);
+};
+}
+
+function makeNodePromisifiedClosure(callback, receiver, _, fn, __, multiArgs) {
+ var defaultThis = (function() {return this;})();
+ var method = callback;
+ if (typeof method === "string") {
+ callback = fn;
+ }
+ function promisified() {
+ var _receiver = receiver;
+ if (receiver === THIS) _receiver = this;
+ ASSERT(typeof callback === "function");
+ var promise = new Promise(INTERNAL);
+ promise._captureStackTrace();
+ var cb = typeof method === "string" && this !== defaultThis
+ ? this[method] : callback;
+ var fn = nodebackForPromise(promise, multiArgs);
+ try {
+ cb.apply(_receiver, withAppended(arguments, fn));
+ } catch(e) {
+ promise._rejectCallback(maybeWrapAsError(e), true, true);
+ }
+ if (!promise._isFateSealed()) promise._setAsyncGuaranteed();
+ return promise;
+ }
+ util.notEnumerableProp(promisified, "__isPromisified__", true);
+ return promisified;
+}
+
+var makeNodePromisified = canEvaluate
+ ? makeNodePromisifiedEval
+ : makeNodePromisifiedClosure;
+
+function promisifyAll(obj, suffix, filter, promisifier, multiArgs) {
+ ASSERT(typeof suffix === "string");
+ ASSERT(typeof filter === "function");
+ var suffixRegexp = new RegExp(escapeIdentRegex(suffix) + "$");
+ var methods =
+ promisifiableMethods(obj, suffix, suffixRegexp, filter);
+
+ for (var i = 0, len = methods.length; i < len; i+= 2) {
+ var key = methods[i];
+ var fn = methods[i+1];
+ var promisifiedKey = key + suffix;
+ if (promisifier === makeNodePromisified) {
+ obj[promisifiedKey] =
+ makeNodePromisified(key, THIS, key, fn, suffix, multiArgs);
+ } else {
+ var promisified = promisifier(fn, function() {
+ return makeNodePromisified(key, THIS, key,
+ fn, suffix, multiArgs);
+ });
+ util.notEnumerableProp(promisified, "__isPromisified__", true);
+ obj[promisifiedKey] = promisified;
+ }
+ }
+ util.toFastProperties(obj);
+ return obj;
+}
+
+function promisify(callback, receiver, multiArgs) {
+ return makeNodePromisified(callback, receiver, undefined,
+ callback, null, multiArgs);
+}
+
+Promise.promisify = function (fn, options) {
+ if (typeof fn !== "function") {
+ throw new TypeError(FUNCTION_ERROR + util.classString(fn));
+ }
+ if (isPromisified(fn)) {
+ return fn;
+ }
+ options = Object(options);
+ var receiver = options.context === undefined ? THIS : options.context;
+ var multiArgs = !!options.multiArgs;
+ var ret = promisify(fn, receiver, multiArgs);
+ util.copyDescriptors(fn, ret, propsFilter);
+ return ret;
+};
+
+Promise.promisifyAll = function (target, options) {
+ if (typeof target !== "function" && typeof target !== "object") {
+ throw new TypeError(PROMISIFY_TYPE_ERROR);
+ }
+ options = Object(options);
+ var multiArgs = !!options.multiArgs;
+ var suffix = options.suffix;
+ if (typeof suffix !== "string") suffix = defaultSuffix;
+ var filter = options.filter;
+ if (typeof filter !== "function") filter = defaultFilter;
+ var promisifier = options.promisifier;
+ if (typeof promisifier !== "function") promisifier = makeNodePromisified;
+
+ if (!util.isIdentifier(suffix)) {
+ throw new RangeError(SUFFIX_NOT_IDENTIFIER);
+ }
+
+ var keys = util.inheritedDataKeys(target);
+ for (var i = 0; i < keys.length; ++i) {
+ var value = target[keys[i]];
+ if (keys[i] !== "constructor" &&
+ util.isClass(value)) {
+ promisifyAll(value.prototype, suffix, filter, promisifier,
+ multiArgs);
+ promisifyAll(value, suffix, filter, promisifier, multiArgs);
+ }
+ }
+
+ return promisifyAll(target, suffix, filter, promisifier, multiArgs);
+};
+};
+
diff --git a/src/props.js b/src/props.js
new file mode 100644
index 0000000..ccbf22e
--- /dev/null
+++ b/src/props.js
@@ -0,0 +1,125 @@
+"use strict";
+module.exports = function(
+ Promise, PromiseArray, tryConvertToPromise, apiRejection) {
+var ASSERT = require("./assert");
+var util = require("./util");
+var isObject = util.isObject;
+var es5 = require("./es5");
+var Es6Map;
+if (typeof Map === "function") Es6Map = Map;
+
+var mapToEntries = (function() {
+ var index = 0;
+ var size = 0;
+
+ function extractEntry(value, key) {
+ this[index] = value;
+ this[index + size] = key;
+ index++;
+ }
+
+ return function mapToEntries(map) {
+ size = map.size;
+ index = 0;
+ var ret = new Array(map.size * 2);
+ map.forEach(extractEntry, ret);
+ return ret;
+ };
+})();
+
+var entriesToMap = function(entries) {
+ var ret = new Es6Map();
+ var length = entries.length / 2 | 0;
+ for (var i = 0; i < length; ++i) {
+ var key = entries[length + i];
+ var value = entries[i];
+ ret.set(key, value);
+ }
+ return ret;
+};
+
+function PropertiesPromiseArray(obj) {
+ var isMap = false;
+ var entries;
+ if (Es6Map !== undefined && obj instanceof Es6Map) {
+ entries = mapToEntries(obj);
+ isMap = true;
+ } else {
+ var keys = es5.keys(obj);
+ var len = keys.length;
+ entries = new Array(len * 2);
+ for (var i = 0; i < len; ++i) {
+ var key = keys[i];
+ entries[i] = obj[key];
+ entries[i + len] = key;
+ }
+ }
+ this.constructor$(entries);
+ this._isMap = isMap;
+ this._init$(undefined, isMap ? RESOLVE_MAP : RESOLVE_OBJECT);
+}
+util.inherits(PropertiesPromiseArray, PromiseArray);
+
+//Override
+PropertiesPromiseArray.prototype._init = function () {};
+
+//Override
+PropertiesPromiseArray.prototype._promiseFulfilled = function (value, index) {
+ ASSERT(!this._isResolved());
+ ASSERT(!(value instanceof Promise));
+ this._values[index] = value;
+ var totalResolved = ++this._totalResolved;
+ if (totalResolved >= this._length) {
+ var val;
+ if (this._isMap) {
+ val = entriesToMap(this._values);
+ } else {
+ val = {};
+ var keyOffset = this.length();
+ for (var i = 0, len = this.length(); i < len; ++i) {
+ val[this._values[i + keyOffset]] = this._values[i];
+ }
+ }
+ this._resolve(val);
+ return true;
+ }
+ return false;
+};
+
+// Override
+PropertiesPromiseArray.prototype.shouldCopyValues = function () {
+ return false;
+};
+
+// Override
+PropertiesPromiseArray.prototype.getActualLength = function (len) {
+ return len >> 1;
+};
+
+function props(promises) {
+ var ret;
+ var castValue = tryConvertToPromise(promises);
+
+ if (!isObject(castValue)) {
+ return apiRejection(PROPS_TYPE_ERROR);
+ } else if (castValue instanceof Promise) {
+ ret = castValue._then(
+ Promise.props, undefined, undefined, undefined, undefined);
+ } else {
+ ret = new PropertiesPromiseArray(castValue).promise();
+ }
+
+ if (castValue instanceof Promise) {
+ ret._propagateFrom(castValue, PROPAGATE_BIND);
+ }
+ return ret;
+}
+
+Promise.prototype.props = function () {
+ return props(this);
+};
+
+Promise.props = function (promises) {
+ return props(promises);
+};
+};
diff --git a/src/queue.js b/src/queue.js
new file mode 100644
index 0000000..15163b6
--- /dev/null
+++ b/src/queue.js
@@ -0,0 +1,79 @@
+"use strict";
+var ASSERT = require("./assert");
+function arrayMove(src, srcIndex, dst, dstIndex, len) {
+ for (var j = 0; j < len; ++j) {
+ dst[j + dstIndex] = src[j + srcIndex];
+ src[j + srcIndex] = void 0;
+ }
+}
+
+function Queue(capacity) {
+ this._capacity = capacity;
+ this._length = 0;
+ this._front = 0;
+}
+
+Queue.prototype._willBeOverCapacity = function (size) {
+ return this._capacity < size;
+};
+
+Queue.prototype._pushOne = function (arg) {
+ var length = this.length();
+ this._checkCapacity(length + 1);
+ var i = (this._front + length) & (this._capacity - 1);
+ this[i] = arg;
+ this._length = length + 1;
+};
+
+Queue.prototype.push = function (fn, receiver, arg) {
+ ASSERT(arguments.length === 3);
+ ASSERT(typeof fn === "function");
+ var length = this.length() + 3;
+ if (this._willBeOverCapacity(length)) {
+ //The fast array copies expect the
+ //underlying array to be filled completely
+ this._pushOne(fn);
+ this._pushOne(receiver);
+ this._pushOne(arg);
+ return;
+ }
+ var j = this._front + length - 3;
+ this._checkCapacity(length);
+ var wrapMask = this._capacity - 1;
+ this[(j + 0) & wrapMask] = fn;
+ this[(j + 1) & wrapMask] = receiver;
+ this[(j + 2) & wrapMask] = arg;
+ this._length = length;
+};
+
+Queue.prototype.shift = function () {
+ ASSERT(this.length() > 0);
+ var front = this._front,
+ ret = this[front];
+
+ this[front] = undefined;
+ this._front = (front + 1) & (this._capacity - 1);
+ this._length--;
+ return ret;
+};
+
+Queue.prototype.length = function () {
+ return this._length;
+};
+
+Queue.prototype._checkCapacity = function (size) {
+ if (this._capacity < size) {
+ this._resizeTo(this._capacity << 1);
+ }
+};
+
+Queue.prototype._resizeTo = function (capacity) {
+ var oldCapacity = this._capacity;
+ this._capacity = capacity;
+ var front = this._front;
+ var length = this._length;
+ var moveItemsCount = (front + length) & (oldCapacity - 1);
+ arrayMove(this, 0, this, oldCapacity, moveItemsCount);
+};
+
+module.exports = Queue;
diff --git a/src/race.js b/src/race.js
new file mode 100644
index 0000000..6f57c5b
--- /dev/null
+++ b/src/race.js
@@ -0,0 +1,50 @@
+"use strict";
+module.exports = function(
+ Promise, INTERNAL, tryConvertToPromise, apiRejection) {
+var util = require("./util");
+
+var raceLater = function (promise) {
+ return promise.then(function(array) {
+ return race(array, promise);
+ });
+};
+
+function race(promises, parent) {
+ var maybePromise = tryConvertToPromise(promises);
+
+ if (maybePromise instanceof Promise) {
+ return raceLater(maybePromise);
+ } else {
+ promises = util.asArray(promises);
+ if (promises === null)
+ return apiRejection(COLLECTION_ERROR + util.classString(promises));
+ }
+
+ var ret = new Promise(INTERNAL);
+ if (parent !== undefined) {
+ ret._propagateFrom(parent, PROPAGATE_ALL);
+ }
+ var fulfill = ret._fulfill;
+ var reject = ret._reject;
+ for (var i = 0, len = promises.length; i < len; ++i) {
+ var val = promises[i];
+
+ if (val === undefined && !(i in promises)) {
+ continue;
+ }
+
+ Promise.cast(val)._then(fulfill, reject, undefined, ret, null);
+ }
+ //Yes, if promises were empty, it will be forever pending :-)
+ return ret;
+}
+
+Promise.race = function (promises) {
+ return race(promises, undefined);
+};
+
+Promise.prototype.race = function () {
+ return race(this, undefined);
+};
+
+};
diff --git a/src/reduce.js b/src/reduce.js
new file mode 100644
index 0000000..51f86aa
--- /dev/null
+++ b/src/reduce.js
@@ -0,0 +1,191 @@
+"use strict";
+module.exports = function(Promise,
+ PromiseArray,
+ apiRejection,
+ tryConvertToPromise,
+ INTERNAL,
+ debug) {
+var util = require("./util");
+var tryCatch = util.tryCatch;
+
+function ReductionPromiseArray(promises, fn, initialValue, _each) {
+ this.constructor$(promises);
+ var context = Promise._getContext();
+ this._fn = util.contextBind(context, fn);
+ if (initialValue !== undefined) {
+ initialValue = Promise.resolve(initialValue);
+ initialValue._attachCancellationCallback(this);
+ }
+ this._initialValue = initialValue;
+ this._currentCancellable = null;
+ if(_each === INTERNAL) {
+ this._eachValues = Array(this._length);
+ } else if (_each === 0) {
+ this._eachValues = null;
+ } else {
+ this._eachValues = undefined;
+ }
+ this._promise._captureStackTrace();
+ this._init$(undefined, RESOLVE_CALL_METHOD);
+}
+util.inherits(ReductionPromiseArray, PromiseArray);
+
+ReductionPromiseArray.prototype._gotAccum = function(accum) {
+ if (this._eachValues !== undefined &&
+ this._eachValues !== null &&
+ accum !== INTERNAL) {
+ this._eachValues.push(accum);
+ }
+};
+
+ReductionPromiseArray.prototype._eachComplete = function(value) {
+ if (this._eachValues !== null) {
+ this._eachValues.push(value);
+ }
+ return this._eachValues;
+};
+
+// Override
+ReductionPromiseArray.prototype._init = function() {};
+
+// Override
+ReductionPromiseArray.prototype._resolveEmptyArray = function() {
+ this._resolve(this._eachValues !== undefined ? this._eachValues
+ : this._initialValue);
+};
+
+// Override
+ReductionPromiseArray.prototype.shouldCopyValues = function () {
+ return false;
+};
+
+// Override
+ReductionPromiseArray.prototype._resolve = function(value) {
+ this._promise._resolveCallback(value);
+ this._values = null;
+};
+
+// Override
+ReductionPromiseArray.prototype._resultCancelled = function(sender) {
+ if (sender === this._initialValue) return this._cancel();
+ if (this._isResolved()) return;
+ this._resultCancelled$();
+ if (this._currentCancellable instanceof Promise) {
+ this._currentCancellable.cancel();
+ }
+ if (this._initialValue instanceof Promise) {
+ this._initialValue.cancel();
+ }
+};
+
+// Override
+ReductionPromiseArray.prototype._iterate = function (values) {
+ this._values = values;
+ var value;
+ var i;
+ var length = values.length;
+ if (this._initialValue !== undefined) {
+ value = this._initialValue;
+ i = 0;
+ } else {
+ value = Promise.resolve(values[0]);
+ i = 1;
+ }
+
+ this._currentCancellable = value;
+
+ for (var j = i; j < length; ++j) {
+ var maybePromise = values[j];
+ if (maybePromise instanceof Promise) {
+ maybePromise.suppressUnhandledRejections();
+ }
+ }
+
+ if (!value.isRejected()) {
+ for (; i < length; ++i) {
+ var ctx = {
+ accum: null,
+ value: values[i],
+ index: i,
+ length: length,
+ array: this
+ };
+
+ value = value._then(gotAccum, undefined, undefined, ctx, undefined);
+
+ // Too many promises chained with asyncGuaranteed will result in
+ // stack overflow. Break up long chains to reset stack.
+ if ((i & 127) === 0) {
+ value._setNoAsyncGuarantee();
+ }
+ }
+ }
+
+ if (this._eachValues !== undefined) {
+ value = value
+ ._then(this._eachComplete, undefined, undefined, this, undefined);
+ }
+ value._then(completed, completed, undefined, value, this);
+};
+
+Promise.prototype.reduce = function (fn, initialValue) {
+ return reduce(this, fn, initialValue, null);
+};
+
+Promise.reduce = function (promises, fn, initialValue, _each) {
+ return reduce(promises, fn, initialValue, _each);
+};
+
+function completed(valueOrReason, array) {
+ if (this.isFulfilled()) {
+ array._resolve(valueOrReason);
+ } else {
+ array._reject(valueOrReason);
+ }
+}
+
+function reduce(promises, fn, initialValue, _each) {
+ if (typeof fn !== "function") {
+ return apiRejection(FUNCTION_ERROR + util.classString(fn));
+ }
+ var array = new ReductionPromiseArray(promises, fn, initialValue, _each);
+ return array.promise();
+}
+
+function gotAccum(accum) {
+ this.accum = accum;
+ this.array._gotAccum(accum);
+ var value = tryConvertToPromise(this.value, this.array._promise);
+ if (value instanceof Promise) {
+ this.array._currentCancellable = value;
+ return value._then(gotValue, undefined, undefined, this, undefined);
+ } else {
+ return gotValue.call(this, value);
+ }
+}
+
+function gotValue(value) {
+ var array = this.array;
+ var promise = array._promise;
+ var fn = tryCatch(array._fn);
+ promise._pushContext();
+ var ret;
+ if (array._eachValues !== undefined) {
+ ret = fn.call(promise._boundValue(), value, this.index, this.length);
+ } else {
+ ret = fn.call(promise._boundValue(),
+ this.accum, value, this.index, this.length);
+ }
+ if (ret instanceof Promise) {
+ array._currentCancellable = ret;
+ }
+ var promiseCreated = promise._popContext();
+ debug.checkForgottenReturns(
+ ret,
+ promiseCreated,
+ array._eachValues !== undefined ? "Promise.each" : "Promise.reduce",
+ promise
+ );
+ return ret;
+}
+};
diff --git a/src/schedule.js b/src/schedule.js
new file mode 100644
index 0000000..bcc5048
--- /dev/null
+++ b/src/schedule.js
@@ -0,0 +1,79 @@
+"use strict";
+var util = require("./util");
+var schedule;
+var noAsyncScheduler = function() {
+ throw new Error(NO_ASYNC_SCHEDULER);
+};
+var NativePromise = util.getNativePromise();
+// This file figures out which scheduler to use for Bluebird. It normalizes
+// async task scheduling across target platforms. Note that not all JS target
+// platforms come supported. The scheduler is overridable with `setScheduler`.
+
+// Our scheduler for Node.js/io.js is setImmediate for recent
+// versions of node because of macrotask semantics.
+// The `typeof` check is for an edge case with nw.js.
+if (util.isNode && typeof MutationObserver === "undefined") {
+ var GlobalSetImmediate = global.setImmediate;
+ var ProcessNextTick = process.nextTick;
+ schedule = util.isRecentNode
+ ? function(fn) { GlobalSetImmediate.call(global, fn); }
+ : function(fn) { ProcessNextTick.call(process, fn); };
+} else if (typeof NativePromise === "function" &&
+ typeof NativePromise.resolve === "function") {
+ var nativePromise = NativePromise.resolve();
+ schedule = function(fn) {
+ nativePromise.then(fn);
+ };
+// Outside of Node, we're using MutationObservers because they provide low
+// latency. The second check is to guard against iOS standalone apps which
+// do not fire DOM mutation events for some reason on iOS 8.3+ and cordova
+// apps which have the same bug but are not `.navigator.standalone`
+} else if ((typeof MutationObserver !== "undefined") &&
+ !(typeof window !== "undefined" &&
+ window.navigator &&
+ (window.navigator.standalone || window.cordova)) &&
+ ("classList" in document.documentElement)) {
+ schedule = (function() {
+ // Using 2 mutation observers to batch multiple updates into one.
+ var div = document.createElement("div");
+ var opts = {attributes: true};
+ var toggleScheduled = false;
+ var div2 = document.createElement("div");
+ var o2 = new MutationObserver(function() {
+ div.classList.toggle("foo");
+ toggleScheduled = false;
+ });
+ o2.observe(div2, opts);
+
+ var scheduleToggle = function() {
+ if (toggleScheduled) return;
+ toggleScheduled = true;
+ div2.classList.toggle("foo");
+ };
+
+ return function schedule(fn) {
+ var o = new MutationObserver(function() {
+ o.disconnect();
+ fn();
+ });
+ o.observe(div, opts);
+ scheduleToggle();
+ };
+ })();
+// setImmediate has higher latency but is still pretty good. This is useful for
+// cases where MutationObserver is not defined (older IE, for example).
+} else if (typeof setImmediate !== "undefined") {
+ schedule = function (fn) {
+ setImmediate(fn);
+ };
+// setTimeout also works, it has the most latency but it does the trick.
+} else if (typeof setTimeout !== "undefined") {
+ schedule = function (fn) {
+ setTimeout(fn, 0);
+ };
+} else {
+// Do __Not__ default to a sync scheduler, that would break Promises/A+
+// compliancy and cause race conditions.
+ schedule = noAsyncScheduler;
+}
+module.exports = schedule;
diff --git a/src/settle.js b/src/settle.js
new file mode 100644
index 0000000..becf211
--- /dev/null
+++ b/src/settle.js
@@ -0,0 +1,55 @@
+"use strict";
+module.exports =
+ function(Promise, PromiseArray, debug) {
+var ASSERT = require("./assert");
+var PromiseInspection = Promise.PromiseInspection;
+var util = require("./util");
+
+function SettledPromiseArray(values) {
+ this.constructor$(values);
+}
+util.inherits(SettledPromiseArray, PromiseArray);
+
+SettledPromiseArray.prototype._promiseResolved = function (index, inspection) {
+ ASSERT(typeof index === "number");
+ this._values[index] = inspection;
+ var totalResolved = ++this._totalResolved;
+ if (totalResolved >= this._length) {
+ this._resolve(this._values);
+ return true;
+ }
+ return false;
+};
+
+//override
+SettledPromiseArray.prototype._promiseFulfilled = function (value, index) {
+ ASSERT(!this._isResolved());
+ ASSERT(typeof index === "number");
+ var ret = new PromiseInspection();
+ ret._bitField = IS_FULFILLED;
+ ret._settledValueField = value;
+ return this._promiseResolved(index, ret);
+};
+//override
+SettledPromiseArray.prototype._promiseRejected = function (reason, index) {
+ ASSERT(!this._isResolved());
+ ASSERT(typeof index === "number");
+ var ret = new PromiseInspection();
+ ret._bitField = IS_REJECTED;
+ ret._settledValueField = reason;
+ return this._promiseResolved(index, ret);
+};
+
+Promise.settle = function (promises) {
+ debug.deprecated(".settle()", ".reflect()");
+ return new SettledPromiseArray(promises).promise();
+};
+
+Promise.allSettled = function (promises) {
+ return new SettledPromiseArray(promises).promise();
+};
+
+Promise.prototype.settle = function () {
+ return Promise.settle(this);
+};
+};
diff --git a/src/some.js b/src/some.js
new file mode 100644
index 0000000..b1661ae
--- /dev/null
+++ b/src/some.js
@@ -0,0 +1,159 @@
+"use strict";
+module.exports =
+function(Promise, PromiseArray, apiRejection) {
+var ASSERT = require("./assert");
+var util = require("./util");
+var RangeError = require("./errors").RangeError;
+var AggregateError = require("./errors").AggregateError;
+var isArray = util.isArray;
+var CANCELLATION = {};
+
+
+function SomePromiseArray(values) {
+ this.constructor$(values);
+ this._howMany = 0;
+ this._unwrap = false;
+ this._initialized = false;
+}
+util.inherits(SomePromiseArray, PromiseArray);
+
+SomePromiseArray.prototype._init = function () {
+ if (!this._initialized) {
+ return;
+ }
+ if (this._howMany === 0) {
+ this._resolve([]);
+ return;
+ }
+ this._init$(undefined, RESOLVE_CALL_METHOD);
+ var isArrayResolved = isArray(this._values);
+ if (!this._isResolved() &&
+ isArrayResolved &&
+ this._howMany > this._canPossiblyFulfill()) {
+ this._reject(this._getRangeError(this.length()));
+ }
+};
+
+SomePromiseArray.prototype.init = function () {
+ this._initialized = true;
+ this._init();
+};
+
+SomePromiseArray.prototype.setUnwrap = function () {
+ this._unwrap = true;
+};
+
+SomePromiseArray.prototype.howMany = function () {
+ return this._howMany;
+};
+
+SomePromiseArray.prototype.setHowMany = function (count) {
+ ASSERT(!this._isResolved());
+ this._howMany = count;
+};
+
+//override
+SomePromiseArray.prototype._promiseFulfilled = function (value) {
+ ASSERT(!this._isResolved());
+ this._addFulfilled(value);
+ if (this._fulfilled() === this.howMany()) {
+ this._values.length = this.howMany();
+ if (this.howMany() === 1 && this._unwrap) {
+ this._resolve(this._values[0]);
+ } else {
+ this._resolve(this._values);
+ }
+ return true;
+ }
+ return false;
+
+};
+//override
+SomePromiseArray.prototype._promiseRejected = function (reason) {
+ ASSERT(!this._isResolved());
+ this._addRejected(reason);
+ return this._checkOutcome();
+};
+
+//override
+SomePromiseArray.prototype._promiseCancelled = function () {
+ if (this._values instanceof Promise || this._values == null) {
+ return this._cancel();
+ }
+ ASSERT(!this._isResolved());
+ this._addRejected(CANCELLATION);
+ return this._checkOutcome();
+};
+
+SomePromiseArray.prototype._checkOutcome = function() {
+ if (this.howMany() > this._canPossiblyFulfill()) {
+ var e = new AggregateError();
+ for (var i = this.length(); i < this._values.length; ++i) {
+ if (this._values[i] !== CANCELLATION) {
+ e.push(this._values[i]);
+ }
+ }
+ if (e.length > 0) {
+ this._reject(e);
+ } else {
+ this._cancel();
+ }
+ return true;
+ }
+ return false;
+};
+
+SomePromiseArray.prototype._fulfilled = function () {
+ return this._totalResolved;
+};
+
+SomePromiseArray.prototype._rejected = function () {
+ return this._values.length - this.length();
+};
+
+//Use the same array past .length() to store rejection reasons
+SomePromiseArray.prototype._addRejected = function (reason) {
+ this._values.push(reason);
+};
+
+SomePromiseArray.prototype._addFulfilled = function (value) {
+ this._values[this._totalResolved++] = value;
+};
+
+SomePromiseArray.prototype._canPossiblyFulfill = function () {
+ return this.length() - this._rejected();
+};
+
+SomePromiseArray.prototype._getRangeError = function (count) {
+ var message = "Input array must contain at least " +
+ this._howMany + " items but contains only " + count + " items";
+ return new RangeError(message);
+};
+
+SomePromiseArray.prototype._resolveEmptyArray = function () {
+ this._reject(this._getRangeError(0));
+};
+
+function some(promises, howMany) {
+ if ((howMany | 0) !== howMany || howMany < 0) {
+ return apiRejection(POSITIVE_INTEGER_ERROR);
+ }
+ var ret = new SomePromiseArray(promises);
+ var promise = ret.promise();
+ ASSERT(promise.isPending());
+ ASSERT(ret instanceof SomePromiseArray);
+ ret.setHowMany(howMany);
+ ret.init();
+ return promise;
+}
+
+Promise.some = function (promises, howMany) {
+ return some(promises, howMany);
+};
+
+Promise.prototype.some = function (howMany) {
+ return some(this, howMany);
+};
+
+Promise._SomePromiseArray = SomePromiseArray;
+};
diff --git a/src/synchronous_inspection.js b/src/synchronous_inspection.js
new file mode 100644
index 0000000..4403797
--- /dev/null
+++ b/src/synchronous_inspection.js
@@ -0,0 +1,103 @@
+"use strict";
+module.exports = function(Promise) {
+function PromiseInspection(promise) {
+ if (promise !== undefined) {
+ promise = promise._target();
+ this._bitField = promise._bitField;
+ this._settledValueField = promise._isFateSealed()
+ ? promise._settledValue() : undefined;
+ }
+ else {
+ this._bitField = 0;
+ this._settledValueField = undefined;
+ }
+}
+
+PromiseInspection.prototype._settledValue = function() {
+ return this._settledValueField;
+};
+
+var value = PromiseInspection.prototype.value = function () {
+ if (!this.isFulfilled()) {
+ throw new TypeError(INSPECTION_VALUE_ERROR);
+ }
+ return this._settledValue();
+};
+
+var reason = PromiseInspection.prototype.error =
+PromiseInspection.prototype.reason = function () {
+ if (!this.isRejected()) {
+ throw new TypeError(INSPECTION_REASON_ERROR);
+ }
+ return this._settledValue();
+};
+
+var isFulfilled = PromiseInspection.prototype.isFulfilled = function() {
+ return (this._bitField & IS_FULFILLED) !== 0;
+};
+
+var isRejected = PromiseInspection.prototype.isRejected = function () {
+ return (this._bitField & IS_REJECTED) !== 0;
+};
+
+var isPending = PromiseInspection.prototype.isPending = function () {
+ return (this._bitField & IS_REJECTED_OR_FULFILLED_OR_CANCELLED) === 0;
+};
+
+var isResolved = PromiseInspection.prototype.isResolved = function () {
+ return (this._bitField & IS_REJECTED_OR_FULFILLED) !== 0;
+};
+
+PromiseInspection.prototype.isCancelled = function() {
+ return (this._bitField & IS_CANCELLED_OR_WILL_BE_CANCELLED) !== 0;
+};
+
+Promise.prototype.__isCancelled = function() {
+ return (this._bitField & IS_CANCELLED) === IS_CANCELLED;
+};
+
+Promise.prototype._isCancelled = function() {
+ return this._target().__isCancelled();
+};
+
+Promise.prototype.isCancelled = function() {
+ return (this._target()._bitField & IS_CANCELLED_OR_WILL_BE_CANCELLED) !== 0;
+};
+
+Promise.prototype.isPending = function() {
+ return isPending.call(this._target());
+};
+
+Promise.prototype.isRejected = function() {
+ return isRejected.call(this._target());
+};
+
+Promise.prototype.isFulfilled = function() {
+ return isFulfilled.call(this._target());
+};
+
+Promise.prototype.isResolved = function() {
+ return isResolved.call(this._target());
+};
+
+Promise.prototype.value = function() {
+ return value.call(this._target());
+};
+
+Promise.prototype.reason = function() {
+ var target = this._target();
+ target._unsetRejectionIsUnhandled();
+ return reason.call(target);
+};
+
+Promise.prototype._value = function() {
+ return this._settledValue();
+};
+
+Promise.prototype._reason = function() {
+ this._unsetRejectionIsUnhandled();
+ return this._settledValue();
+};
+
+Promise.PromiseInspection = PromiseInspection;
+};
diff --git a/src/thenables.js b/src/thenables.js
new file mode 100644
index 0000000..9572e5c
--- /dev/null
+++ b/src/thenables.js
@@ -0,0 +1,89 @@
+"use strict";
+module.exports = function(Promise, INTERNAL) {
+var ASSERT = require("./assert");
+var util = require("./util");
+var errorObj = util.errorObj;
+var isObject = util.isObject;
+
+function tryConvertToPromise(obj, context) {
+ if (isObject(obj)) {
+ if (obj instanceof Promise) return obj;
+ var then = getThen(obj);
+ if (then === errorObj) {
+ if (context) context._pushContext();
+ var ret = Promise.reject(then.e);
+ if (context) context._popContext();
+ return ret;
+ } else if (typeof then === "function") {
+ //Make casting from another bluebird fast
+ if (isAnyBluebirdPromise(obj)) {
+ var ret = new Promise(INTERNAL);
+ obj._then(
+ ret._fulfill,
+ ret._reject,
+ undefined,
+ ret,
+ null
+ );
+ return ret;
+ }
+ return doThenable(obj, then, context);
+ }
+ }
+ return obj;
+}
+
+function doGetThen(obj) {
+ return obj.then;
+}
+
+function getThen(obj) {
+ try {
+ return doGetThen(obj);
+ } catch (e) {
+ errorObj.e = e;
+ return errorObj;
+ }
+}
+
+var hasProp = {}.hasOwnProperty;
+function isAnyBluebirdPromise(obj) {
+ try {
+ return hasProp.call(obj, "_promise0");
+ } catch (e) {
+ return false;
+ }
+}
+
+function doThenable(x, then, context) {
+ ASSERT(typeof then === "function");
+ var promise = new Promise(INTERNAL);
+ var ret = promise;
+ if (context) context._pushContext();
+ promise._captureStackTrace();
+ if (context) context._popContext();
+ var synchronous = true;
+ var result = util.tryCatch(then).call(x, resolve, reject);
+ synchronous = false;
+
+ if (promise && result === errorObj) {
+ promise._rejectCallback(result.e, true, true);
+ promise = null;
+ }
+
+ function resolve(value) {
+ if (!promise) return;
+ promise._resolveCallback(value);
+ promise = null;
+ }
+
+ function reject(reason) {
+ if (!promise) return;
+ promise._rejectCallback(reason, synchronous, true);
+ promise = null;
+ }
+ return ret;
+}
+
+return tryConvertToPromise;
+};
diff --git a/src/timers.js b/src/timers.js
new file mode 100644
index 0000000..25d6454
--- /dev/null
+++ b/src/timers.js
@@ -0,0 +1,93 @@
+"use strict";
+module.exports = function(Promise, INTERNAL, debug) {
+var util = require("./util");
+var TimeoutError = Promise.TimeoutError;
+
+function HandleWrapper(handle) {
+ this.handle = handle;
+}
+
+HandleWrapper.prototype._resultCancelled = function() {
+ clearTimeout(this.handle);
+};
+
+var afterValue = function(value) { return delay(+this).thenReturn(value); };
+var delay = Promise.delay = function (ms, value) {
+ var ret;
+ var handle;
+ if (value !== undefined) {
+ ret = Promise.resolve(value)
+ ._then(afterValue, null, null, ms, undefined);
+ if (debug.cancellation() && value instanceof Promise) {
+ ret._setOnCancel(value);
+ }
+ } else {
+ ret = new Promise(INTERNAL);
+ handle = setTimeout(function() { ret._fulfill(); }, +ms);
+ if (debug.cancellation()) {
+ ret._setOnCancel(new HandleWrapper(handle));
+ }
+ ret._captureStackTrace();
+ }
+ ret._setAsyncGuaranteed();
+ return ret;
+};
+
+Promise.prototype.delay = function (ms) {
+ return delay(ms, this);
+};
+
+var afterTimeout = function (promise, message, parent) {
+ var err;
+ if (typeof message !== "string") {
+ if (message instanceof Error) {
+ err = message;
+ } else {
+ err = new TimeoutError(TIMEOUT_ERROR);
+ }
+ } else {
+ err = new TimeoutError(message);
+ }
+ util.markAsOriginatingFromRejection(err);
+ promise._attachExtraTrace(err);
+ promise._reject(err);
+
+ if (parent != null) {
+ parent.cancel();
+ }
+};
+
+function successClear(value) {
+ clearTimeout(this.handle);
+ return value;
+}
+
+function failureClear(reason) {
+ clearTimeout(this.handle);
+ throw reason;
+}
+
+Promise.prototype.timeout = function (ms, message) {
+ ms = +ms;
+ var ret, parent;
+
+ var handleWrapper = new HandleWrapper(setTimeout(function timeoutTimeout() {
+ if (ret.isPending()) {
+ afterTimeout(ret, message, parent);
+ }
+ }, ms));
+
+ if (debug.cancellation()) {
+ parent = this.then();
+ ret = parent._then(successClear, failureClear,
+ undefined, handleWrapper, undefined);
+ ret._setOnCancel(handleWrapper);
+ } else {
+ ret = this._then(successClear, failureClear,
+ undefined, handleWrapper, undefined);
+ }
+
+ return ret;
+};
+
+};
diff --git a/src/using.js b/src/using.js
new file mode 100644
index 0000000..9812b9f
--- /dev/null
+++ b/src/using.js
@@ -0,0 +1,226 @@
+"use strict";
+module.exports = function (Promise, apiRejection, tryConvertToPromise,
+ createContext, INTERNAL, debug) {
+ var util = require("./util");
+ var TypeError = require("./errors").TypeError;
+ var inherits = require("./util").inherits;
+ var errorObj = util.errorObj;
+ var tryCatch = util.tryCatch;
+ var NULL = {};
+
+ function thrower(e) {
+ setTimeout(function(){throw e;}, 0);
+ }
+
+ function castPreservingDisposable(thenable) {
+ var maybePromise = tryConvertToPromise(thenable);
+ if (maybePromise !== thenable &&
+ typeof thenable._isDisposable === "function" &&
+ typeof thenable._getDisposer === "function" &&
+ thenable._isDisposable()) {
+ maybePromise._setDisposable(thenable._getDisposer());
+ }
+ return maybePromise;
+ }
+ function dispose(resources, inspection) {
+ var i = 0;
+ var len = resources.length;
+ var ret = new Promise(INTERNAL);
+ function iterator() {
+ if (i >= len) return ret._fulfill();
+ var maybePromise = castPreservingDisposable(resources[i++]);
+ if (maybePromise instanceof Promise &&
+ maybePromise._isDisposable()) {
+ try {
+ maybePromise = tryConvertToPromise(
+ maybePromise._getDisposer().tryDispose(inspection),
+ resources.promise);
+ } catch (e) {
+ return thrower(e);
+ }
+ if (maybePromise instanceof Promise) {
+ return maybePromise._then(iterator, thrower,
+ null, null, null);
+ }
+ }
+ iterator();
+ }
+ iterator();
+ return ret;
+ }
+
+ function Disposer(data, promise, context) {
+ this._data = data;
+ this._promise = promise;
+ this._context = context;
+ }
+
+ Disposer.prototype.data = function () {
+ return this._data;
+ };
+
+ Disposer.prototype.promise = function () {
+ return this._promise;
+ };
+
+ Disposer.prototype.resource = function () {
+ if (this.promise().isFulfilled()) {
+ return this.promise().value();
+ }
+ return NULL;
+ };
+
+ Disposer.prototype.tryDispose = function(inspection) {
+ var resource = this.resource();
+ var context = this._context;
+ if (context !== undefined) context._pushContext();
+ var ret = resource !== NULL
+ ? this.doDispose(resource, inspection) : null;
+ if (context !== undefined) context._popContext();
+ this._promise._unsetDisposable();
+ this._data = null;
+ return ret;
+ };
+
+ Disposer.isDisposer = function (d) {
+ return (d != null &&
+ typeof d.resource === "function" &&
+ typeof d.tryDispose === "function");
+ };
+
+ function FunctionDisposer(fn, promise, context) {
+ this.constructor$(fn, promise, context);
+ }
+ inherits(FunctionDisposer, Disposer);
+
+ FunctionDisposer.prototype.doDispose = function (resource, inspection) {
+ var fn = this.data();
+ return fn.call(resource, resource, inspection);
+ };
+
+ function maybeUnwrapDisposer(value) {
+ if (Disposer.isDisposer(value)) {
+ this.resources[this.index]._setDisposable(value);
+ return value.promise();
+ }
+ return value;
+ }
+
+ function ResourceList(length) {
+ this.length = length;
+ this.promise = null;
+ this[length-1] = null;
+ }
+
+ ResourceList.prototype._resultCancelled = function() {
+ var len = this.length;
+ for (var i = 0; i < len; ++i) {
+ var item = this[i];
+ if (item instanceof Promise) {
+ item.cancel();
+ }
+ }
+ };
+
+ Promise.using = function () {
+ var len = arguments.length;
+ if (len < 2) return apiRejection(
+ "you must pass at least 2 arguments to Promise.using");
+ var fn = arguments[len - 1];
+ if (typeof fn !== "function") {
+ return apiRejection(FUNCTION_ERROR + util.classString(fn));
+ }
+ var input;
+ var spreadArgs = true;
+ if (len === 2 && Array.isArray(arguments[0])) {
+ input = arguments[0];
+ len = input.length;
+ spreadArgs = false;
+ } else {
+ input = arguments;
+ len--;
+ }
+ var resources = new ResourceList(len);
+ for (var i = 0; i < len; ++i) {
+ var resource = input[i];
+ if (Disposer.isDisposer(resource)) {
+ var disposer = resource;
+ resource = resource.promise();
+ resource._setDisposable(disposer);
+ } else {
+ var maybePromise = tryConvertToPromise(resource);
+ if (maybePromise instanceof Promise) {
+ resource =
+ maybePromise._then(maybeUnwrapDisposer, null, null, {
+ resources: resources,
+ index: i
+ }, undefined);
+ }
+ }
+ resources[i] = resource;
+ }
+
+ var reflectedResources = new Array(resources.length);
+ for (var i = 0; i < reflectedResources.length; ++i) {
+ reflectedResources[i] = Promise.resolve(resources[i]).reflect();
+ }
+
+ var resultPromise = Promise.all(reflectedResources)
+ .then(function(inspections) {
+ for (var i = 0; i < inspections.length; ++i) {
+ var inspection = inspections[i];
+ if (inspection.isRejected()) {
+ errorObj.e = inspection.error();
+ return errorObj;
+ } else if (!inspection.isFulfilled()) {
+ resultPromise.cancel();
+ return;
+ }
+ inspections[i] = inspection.value();
+ }
+ promise._pushContext();
+
+ fn = tryCatch(fn);
+ var ret = spreadArgs
+ ? fn.apply(undefined, inspections) : fn(inspections);
+ var promiseCreated = promise._popContext();
+ debug.checkForgottenReturns(
+ ret, promiseCreated, "Promise.using", promise);
+ return ret;
+ });
+
+ var promise = resultPromise.lastly(function() {
+ var inspection = new Promise.PromiseInspection(resultPromise);
+ return dispose(resources, inspection);
+ });
+ resources.promise = promise;
+ promise._setOnCancel(resources);
+ return promise;
+ };
+
+ Promise.prototype._setDisposable = function (disposer) {
+ this._bitField = this._bitField | IS_DISPOSABLE;
+ this._disposer = disposer;
+ };
+
+ Promise.prototype._isDisposable = function () {
+ return (this._bitField & IS_DISPOSABLE) > 0;
+ };
+
+ Promise.prototype._getDisposer = function () {
+ return this._disposer;
+ };
+
+ Promise.prototype._unsetDisposable = function () {
+ this._bitField = this._bitField & (~IS_DISPOSABLE);
+ this._disposer = undefined;
+ };
+
+ Promise.prototype.disposer = function (fn) {
+ if (typeof fn === "function") {
+ return new FunctionDisposer(fn, this, createContext());
+ }
+ throw new TypeError();
+ };
+
+};
diff --git a/src/util.js b/src/util.js
new file mode 100644
index 0000000..1ef4213
--- /dev/null
+++ b/src/util.js
@@ -0,0 +1,432 @@
+"use strict";
+var ASSERT = require("./assert");
+var es5 = require("./es5");
+// Assume CSP if browser
+var canEvaluate = typeof navigator == "undefined";
+
+//Try catch is not supported in optimizing
+//compiler, so it is isolated
+var errorObj = {e: {}};
+var tryCatchTarget;
+var globalObject = typeof self !== "undefined" ? self :
+ typeof window !== "undefined" ? window :
+ typeof global !== "undefined" ? global :
+ this !== undefined ? this : null;
+
+function tryCatcher() {
+ try {
+ var target = tryCatchTarget;
+ tryCatchTarget = null;
+ return target.apply(this, arguments);
+ } catch (e) {
+ errorObj.e = e;
+ return errorObj;
+ }
+}
+function tryCatch(fn) {
+ ASSERT(typeof fn === "function");
+ tryCatchTarget = fn;
+ return tryCatcher;
+}
+
+//Un-magical enough that using this doesn't prevent
+//extending classes from outside using any convention
+var inherits = function(Child, Parent) {
+ var hasProp = {}.hasOwnProperty;
+
+ function T() {
+ this.constructor = Child;
+ this.constructor$ = Parent;
+ for (var propertyName in Parent.prototype) {
+ if (hasProp.call(Parent.prototype, propertyName) &&
+ propertyName.charAt(propertyName.length-1) !== "$"
+ ) {
+ this[propertyName + "$"] = Parent.prototype[propertyName];
+ }
+ }
+ }
+ T.prototype = Parent.prototype;
+ Child.prototype = new T();
+ return Child.prototype;
+};
+
+
+function isPrimitive(val) {
+ return val == null || val === true || val === false ||
+ typeof val === "string" || typeof val === "number";
+
+}
+
+function isObject(value) {
+ return typeof value === "function" ||
+ typeof value === "object" && value !== null;
+}
+
+function maybeWrapAsError(maybeError) {
+ if (!isPrimitive(maybeError)) return maybeError;
+
+ return new Error(safeToString(maybeError));
+}
+
+function withAppended(target, appendee) {
+ var len = target.length;
+ var ret = new Array(len + 1);
+ var i;
+ for (i = 0; i < len; ++i) {
+ ret[i] = target[i];
+ }
+ ret[i] = appendee;
+ return ret;
+}
+
+function getDataPropertyOrDefault(obj, key, defaultValue) {
+ if (es5.isES5) {
+ var desc = Object.getOwnPropertyDescriptor(obj, key);
+
+ if (desc != null) {
+ return desc.get == null && desc.set == null
+ ? desc.value
+ : defaultValue;
+ }
+ } else {
+ return {}.hasOwnProperty.call(obj, key) ? obj[key] : undefined;
+ }
+}
+
+function notEnumerableProp(obj, name, value) {
+ if (isPrimitive(obj)) return obj;
+ var descriptor = {
+ value: value,
+ configurable: true,
+ enumerable: false,
+ writable: true
+ };
+ es5.defineProperty(obj, name, descriptor);
+ return obj;
+}
+
+function thrower(r) {
+ throw r;
+}
+
+var inheritedDataKeys = (function() {
+ var excludedPrototypes = [
+ Array.prototype,
+ Object.prototype,
+ Function.prototype
+ ];
+
+ var isExcludedProto = function(val) {
+ for (var i = 0; i < excludedPrototypes.length; ++i) {
+ if (excludedPrototypes[i] === val) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ if (es5.isES5) {
+ var getKeys = Object.getOwnPropertyNames;
+ return function(obj) {
+ var ret = [];
+ var visitedKeys = Object.create(null);
+ while (obj != null && !isExcludedProto(obj)) {
+ var keys;
+ try {
+ keys = getKeys(obj);
+ } catch (e) {
+ return ret;
+ }
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ if (visitedKeys[key]) continue;
+ visitedKeys[key] = true;
+ var desc = Object.getOwnPropertyDescriptor(obj, key);
+ if (desc != null && desc.get == null && desc.set == null) {
+ ret.push(key);
+ }
+ }
+ obj = es5.getPrototypeOf(obj);
+ }
+ return ret;
+ };
+ } else {
+ var hasProp = {}.hasOwnProperty;
+ return function(obj) {
+ if (isExcludedProto(obj)) return [];
+ var ret = [];
+
+ /*jshint forin:false */
+ enumeration: for (var key in obj) {
+ if (hasProp.call(obj, key)) {
+ ret.push(key);
+ } else {
+ for (var i = 0; i < excludedPrototypes.length; ++i) {
+ if (hasProp.call(excludedPrototypes[i], key)) {
+ continue enumeration;
+ }
+ }
+ ret.push(key);
+ }
+ }
+ return ret;
+ };
+ }
+
+})();
+
+var thisAssignmentPattern = /this\s*\.\s*\S+\s*=/;
+function isClass(fn) {
+ try {
+ if (typeof fn === "function") {
+ var keys = es5.names(fn.prototype);
+
+ var hasMethods = es5.isES5 && keys.length > 1;
+ var hasMethodsOtherThanConstructor = keys.length > 0 &&
+ !(keys.length === 1 && keys[0] === "constructor");
+ var hasThisAssignmentAndStaticMethods =
+ thisAssignmentPattern.test(fn + "") && es5.names(fn).length > 0;
+
+ if (hasMethods || hasMethodsOtherThanConstructor ||
+ hasThisAssignmentAndStaticMethods) {
+ return true;
+ }
+ }
+ return false;
+ } catch (e) {
+ return false;
+ }
+}
+
+function toFastProperties(obj) {
+ /*jshint -W027,-W055,-W031*/
+ function FakeConstructor() {}
+ FakeConstructor.prototype = obj;
+ var receiver = new FakeConstructor();
+ function ic() {
+ return typeof receiver.foo;
+ }
+ ic();
+ ic();
+ ASSERT("%HasFastProperties", true, obj);
+ return obj;
+ // Prevent the function from being optimized through dead code elimination
+ // or further optimizations. This code is never reached but even using eval
+ // in unreachable code causes v8 to not optimize functions.
+ eval(obj);
+}
+
+var rident = /^[a-z$_][a-z$_0-9]*$/i;
+function isIdentifier(str) {
+ return rident.test(str);
+}
+
+function filledRange(count, prefix, suffix) {
+ var ret = new Array(count);
+ for(var i = 0; i < count; ++i) {
+ ret[i] = prefix + i + suffix;
+ }
+ return ret;
+}
+
+function safeToString(obj) {
+ try {
+ return obj + "";
+ } catch (e) {
+ return "[no string representation]";
+ }
+}
+
+function isError(obj) {
+ return obj instanceof Error ||
+ (obj !== null &&
+ typeof obj === "object" &&
+ typeof obj.message === "string" &&
+ typeof obj.name === "string");
+}
+
+function markAsOriginatingFromRejection(e) {
+ try {
+ notEnumerableProp(e, OPERATIONAL_ERROR_KEY, true);
+ }
+ catch(ignore) {}
+}
+
+function originatesFromRejection(e) {
+ if (e == null) return false;
+ return ((e instanceof Error[BLUEBIRD_ERRORS].OperationalError) ||
+ e[OPERATIONAL_ERROR_KEY] === true);
+}
+
+function canAttachTrace(obj) {
+ return isError(obj) && es5.propertyIsWritable(obj, "stack");
+}
+
+var ensureErrorObject = (function() {
+ if (!("stack" in new Error())) {
+ return function(value) {
+ if (canAttachTrace(value)) return value;
+ try {throw new Error(safeToString(value));}
+ catch(err) {return err;}
+ };
+ } else {
+ return function(value) {
+ if (canAttachTrace(value)) return value;
+ return new Error(safeToString(value));
+ };
+ }
+})();
+
+function classString(obj) {
+ return {}.toString.call(obj);
+}
+
+function copyDescriptors(from, to, filter) {
+ var keys = es5.names(from);
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ if (filter(key)) {
+ try {
+ es5.defineProperty(to, key, es5.getDescriptor(from, key));
+ } catch (ignore) {}
+ }
+ }
+}
+
+var asArray = function(v) {
+ if (es5.isArray(v)) {
+ return v;
+ }
+ return null;
+};
+
+if (typeof Symbol !== "undefined" && Symbol.iterator) {
+ var ArrayFrom = typeof Array.from === "function" ? function(v) {
+ return Array.from(v);
+ } : function(v) {
+ var ret = [];
+ var it = v[Symbol.iterator]();
+ var itResult;
+ while (!((itResult = it.next()).done)) {
+ ret.push(itResult.value);
+ }
+ return ret;
+ };
+
+ asArray = function(v) {
+ if (es5.isArray(v)) {
+ return v;
+ } else if (v != null && typeof v[Symbol.iterator] === "function") {
+ return ArrayFrom(v);
+ }
+ return null;
+ };
+}
+
+var isNode = typeof process !== "undefined" &&
+ classString(process).toLowerCase() === "[object process]";
+
+var hasEnvVariables = typeof process !== "undefined" &&
+ typeof process.env !== "undefined";
+
+function env(key) {
+ return hasEnvVariables ? process.env[key] : undefined;
+}
+
+function getNativePromise() {
+ if (typeof Promise === "function") {
+ try {
+ var promise = new Promise(function(){});
+ if (classString(promise) === "[object Promise]") {
+ return Promise;
+ }
+ } catch (e) {}
+ }
+}
+
+var reflectHandler;
+function contextBind(ctx, cb) {
+ if (ctx === null ||
+ typeof cb !== "function" ||
+ cb === reflectHandler) {
+ return cb;
+ }
+
+ if (ctx.domain !== null) {
+ cb = ctx.domain.bind(cb);
+ }
+
+ var async = ctx.async;
+ if (async !== null) {
+ var old = cb;
+ cb = function() {
+ INLINE_SLICE_LEFT_PADDED(2, args, arguments);
+ args[0] = old;
+ args[1] = this;
+ return async.runInAsyncScope.apply(async, args);
+ };
+ }
+ return cb;
+}
+
+var ret = {
+ setReflectHandler: function(fn) {
+ reflectHandler = fn;
+ },
+ isClass: isClass,
+ isIdentifier: isIdentifier,
+ inheritedDataKeys: inheritedDataKeys,
+ getDataPropertyOrDefault: getDataPropertyOrDefault,
+ thrower: thrower,
+ isArray: es5.isArray,
+ asArray: asArray,
+ notEnumerableProp: notEnumerableProp,
+ isPrimitive: isPrimitive,
+ isObject: isObject,
+ isError: isError,
+ canEvaluate: canEvaluate,
+ errorObj: errorObj,
+ tryCatch: tryCatch,
+ inherits: inherits,
+ withAppended: withAppended,
+ maybeWrapAsError: maybeWrapAsError,
+ toFastProperties: toFastProperties,
+ filledRange: filledRange,
+ toString: safeToString,
+ canAttachTrace: canAttachTrace,
+ ensureErrorObject: ensureErrorObject,
+ originatesFromRejection: originatesFromRejection,
+ markAsOriginatingFromRejection: markAsOriginatingFromRejection,
+ classString: classString,
+ copyDescriptors: copyDescriptors,
+ isNode: isNode,
+ hasEnvVariables: hasEnvVariables,
+ env: env,
+ global: globalObject,
+ getNativePromise: getNativePromise,
+ contextBind: contextBind
+};
+ret.isRecentNode = ret.isNode && (function() {
+ var version;
+ if (process.versions && process.versions.node) {
+ version = process.versions.node.split(".").map(Number);
+ } else if (process.version) {
+ version = process.version.split(".").map(Number);
+ }
+ return (version[0] === 0 && version[1] > 10) || (version[0] > 0);
+})();
+ret.nodeSupportsAsyncResource = ret.isNode && (function() {
+ var supportsAsync = false;
+ try {
+ var res = require("async_hooks").AsyncResource;
+ supportsAsync = typeof res.prototype.runInAsyncScope === "function";
+ } catch (e) {
+ supportsAsync = false;
+ }
+ return supportsAsync;
+})();
+
+if (ret.isNode) ret.toFastProperties(process);
+
+try {throw new Error(); } catch (e) {ret.lastLineError = e;}
+module.exports = ret;
diff --git a/test/mocha/2.1.2.js b/test/mocha/2.1.2.js
new file mode 100644
index 0000000..eeaa32a
--- /dev/null
+++ b/test/mocha/2.1.2.js
@@ -0,0 +1,76 @@
+"use strict";
+
+var assert = require("assert");
+var testFulfilled = require("./helpers/testThreeCases").testFulfilled;
+
+var adapter = global.adapter;
+var pending = adapter.pending;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+
+describe("2.1.2.1: When fulfilled, a promise: must not transition to any other state.", function () {
+ testFulfilled(dummy, function (promise, done) {
+ var onFulfilledCalled = false;
+
+ promise.then(function onFulfilled() {
+ onFulfilledCalled = true;
+ }, function onRejected() {
+ assert.strictEqual(onFulfilledCalled, false);
+ done();
+ });
+
+ setTimeout(function(){done();}, 100);
+ });
+
+ specify("trying to fulfill then immediately reject", function (done) {
+ var tuple = pending();
+ var onFulfilledCalled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ onFulfilledCalled = true;
+ }, function onRejected() {
+ assert.strictEqual(onFulfilledCalled, false);
+ done();
+ });
+
+ tuple.fulfill(dummy);
+ tuple.reject(dummy);
+ setTimeout(function(){done();}, 100);
+ });
+
+ specify("trying to fulfill then reject, delayed", function (done) {
+ var tuple = pending();
+ var onFulfilledCalled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ onFulfilledCalled = true;
+ }, function onRejected() {
+ assert.strictEqual(onFulfilledCalled, false);
+ done();
+ });
+
+ setTimeout(function () {
+ tuple.fulfill(dummy);
+ tuple.reject(dummy);
+ }, 50);
+ setTimeout(function(){done();}, 100);
+ });
+
+ specify("trying to fulfill immediately then reject delayed", function (done) {
+ var tuple = pending();
+ var onFulfilledCalled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ onFulfilledCalled = true;
+ }, function onRejected() {
+ assert.strictEqual(onFulfilledCalled, false);
+ done();
+ });
+
+ tuple.fulfill(dummy);
+ setTimeout(function () {
+ tuple.reject(dummy);
+ }, 50);
+ setTimeout(function(){done();}, 100);
+ });
+});
diff --git a/test/mocha/2.1.3.js b/test/mocha/2.1.3.js
new file mode 100644
index 0000000..0013481
--- /dev/null
+++ b/test/mocha/2.1.3.js
@@ -0,0 +1,76 @@
+"use strict";
+
+var assert = require("assert");
+var testRejected = require("./helpers/testThreeCases").testRejected;
+
+var adapter = global.adapter;
+var pending = adapter.pending;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+
+describe("2.1.3.1: When rejected, a promise: must not transition to any other state.", function () {
+ testRejected(dummy, function (promise, done) {
+ var onRejectedCalled = false;
+
+ promise.then(function onFulfilled() {
+ assert.strictEqual(onRejectedCalled, false);
+ done();
+ }, function onRejected() {
+ onRejectedCalled = true;
+ });
+
+ setTimeout(function(){done();}, 100);
+ });
+
+ specify("trying to reject then immediately fulfill", function (done) {
+ var tuple = pending();
+ var onRejectedCalled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(onRejectedCalled, false);
+ done();
+ }, function onRejected() {
+ onRejectedCalled = true;
+ });
+
+ tuple.reject(dummy);
+ tuple.fulfill(dummy);
+ setTimeout(function(){done();}, 100);
+ });
+
+ specify("trying to reject then fulfill, delayed", function (done) {
+ var tuple = pending();
+ var onRejectedCalled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(onRejectedCalled, false);
+ done();
+ }, function onRejected() {
+ onRejectedCalled = true;
+ });
+
+ setTimeout(function () {
+ tuple.reject(dummy);
+ tuple.fulfill(dummy);
+ }, 50);
+ setTimeout(function(){done();}, 100);
+ });
+
+ specify("trying to reject immediately then fulfill delayed", function (done) {
+ var tuple = pending();
+ var onRejectedCalled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(onRejectedCalled, false);
+ done();
+ }, function onRejected() {
+ onRejectedCalled = true;
+ });
+
+ tuple.reject(dummy);
+ setTimeout(function () {
+ tuple.fulfill(dummy);
+ }, 50);
+ setTimeout(function(){done();}, 100);
+ });
+});
diff --git a/test/mocha/2.2.1.js b/test/mocha/2.2.1.js
new file mode 100644
index 0000000..cfe1012
--- /dev/null
+++ b/test/mocha/2.2.1.js
@@ -0,0 +1,41 @@
+"use strict";
+
+var adapter = global.adapter;
+var fulfilled = adapter.fulfilled;
+var rejected = adapter.rejected;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+
+describe("2.2.1: Both `onFulfilled` and `onRejected` are optional arguments.", function () {
+ describe("2.2.1.1: If `onFulfilled` is not a function, it must be ignored.", function () {
+ function testNonFunction(nonFunction, stringRepresentation) {
+ specify("`onFulfilled` is " + stringRepresentation, function (done) {
+ rejected(dummy).then(nonFunction, function () {
+ done();
+ });
+ });
+ }
+
+ testNonFunction(undefined, "`undefined`");
+ testNonFunction(null, "`null`");
+ testNonFunction(false, "`false`");
+ testNonFunction(5, "`5`");
+ testNonFunction({}, "an object");
+ });
+
+ describe("2.2.1.2: If `onRejected` is not a function, it must be ignored.", function () {
+ function testNonFunction(nonFunction, stringRepresentation) {
+ specify("`onRejected` is " + stringRepresentation, function (done) {
+ fulfilled(dummy).then(function () {
+ done();
+ }, nonFunction);
+ });
+ }
+
+ testNonFunction(undefined, "`undefined`");
+ testNonFunction(null, "`null`");
+ testNonFunction(false, "`false`");
+ testNonFunction(5, "`5`");
+ testNonFunction({}, "an object");
+ });
+});
diff --git a/test/mocha/2.2.2.js b/test/mocha/2.2.2.js
new file mode 100644
index 0000000..29683e5
--- /dev/null
+++ b/test/mocha/2.2.2.js
@@ -0,0 +1,151 @@
+"use strict";
+
+var assert = require("assert");
+var testFulfilled = require("./helpers/testThreeCases").testFulfilled;
+
+var adapter = global.adapter;
+var fulfilled = adapter.fulfilled;
+var pending = adapter.pending;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality
+
+describe("2.2.2: If `onFulfilled` is a function,", function () {
+ describe("2.2.2.1: it must be called after `promise` is fulfilled, with `promise`’s fulfillment value as its " +
+ "first argument.", function () {
+ testFulfilled(sentinel, function (promise, done) {
+ promise.then(function onFulfilled(value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("2.2.2.2: it must not be called before `promise` is fulfilled", function () {
+ specify("fulfilled after a delay", function (done) {
+ var tuple = pending();
+ var isFulfilled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(isFulfilled, true);
+ done();
+ });
+
+ setTimeout(function () {
+ tuple.fulfill(dummy);
+ isFulfilled = true;
+ }, 50);
+ });
+
+ specify("never fulfilled", function (done) {
+ var tuple = pending();
+ var onFulfilledCalled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ onFulfilledCalled = true;
+ done();
+ });
+
+ setTimeout(function () {
+ assert.strictEqual(onFulfilledCalled, false);
+ done();
+ }, 150);
+ });
+ });
+
+ describe("2.2.2.3: it must not be called more than once.", function () {
+ specify("already-fulfilled", function (done) {
+ var timesCalled = 0;
+
+ fulfilled(dummy).then(function onFulfilled() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+ });
+
+ specify("trying to fulfill a pending promise more than once, immediately", function (done) {
+ var tuple = pending();
+ var timesCalled = 0;
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+
+ tuple.fulfill(dummy);
+ tuple.fulfill(dummy);
+ });
+
+ specify("trying to fulfill a pending promise more than once, delayed", function (done) {
+ var tuple = pending();
+ var timesCalled = 0;
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+
+ setTimeout(function () {
+ tuple.fulfill(dummy);
+ tuple.fulfill(dummy);
+ }, 50);
+ });
+
+ specify("trying to fulfill a pending promise more than once, immediately then delayed", function (done) {
+ var tuple = pending();
+ var timesCalled = 0;
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+
+ tuple.fulfill(dummy);
+ setTimeout(function () {
+ tuple.fulfill(dummy);
+ }, 50);
+ });
+
+ specify("when multiple `then` calls are made, spaced apart in time", function (done) {
+ var tuple = pending();
+ var timesCalled = [0, 0, 0];
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled[0], 1);
+ });
+
+ setTimeout(function () {
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled[1], 1);
+ });
+ }, 50);
+
+ setTimeout(function () {
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled[2], 1);
+ done();
+ });
+ }, 100);
+
+ setTimeout(function () {
+ tuple.fulfill(dummy);
+ }, 150);
+ });
+
+ specify("when `then` is interleaved with fulfillment", function (done) {
+ var tuple = pending();
+ var timesCalled = [0, 0];
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled[0], 1);
+ });
+
+ tuple.fulfill(dummy);
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled[1], 1);
+ done();
+ });
+ });
+ });
+});
diff --git a/test/mocha/2.2.3.js b/test/mocha/2.2.3.js
new file mode 100644
index 0000000..d2a7109
--- /dev/null
+++ b/test/mocha/2.2.3.js
@@ -0,0 +1,151 @@
+"use strict";
+
+var assert = require("assert");
+var testRejected = require("./helpers/testThreeCases").testRejected;
+
+var adapter = global.adapter;
+var rejected = adapter.rejected;
+var pending = adapter.pending;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality
+
+describe("2.2.3: If `onRejected` is a function,", function () {
+ describe("2.2.3.1: it must be called after `promise` is rejected, with `promise`’s rejection reason as its " +
+ "first argument.", function () {
+ testRejected(sentinel, function (promise, done) {
+ promise.then(null, function onRejected(reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("2.2.3.2: it must not be called before `promise` is rejected", function () {
+ specify("rejected after a delay", function (done) {
+ var tuple = pending();
+ var isRejected = false;
+
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(isRejected, true);
+ done();
+ });
+
+ setTimeout(function () {
+ tuple.reject(dummy);
+ isRejected = true;
+ }, 50);
+ });
+
+ specify("never rejected", function (done) {
+ var tuple = pending();
+ var onRejectedCalled = false;
+
+ tuple.promise.then(null, function onRejected() {
+ onRejectedCalled = true;
+ done();
+ });
+
+ setTimeout(function () {
+ assert.strictEqual(onRejectedCalled, false);
+ done();
+ }, 150);
+ });
+ });
+
+ describe("2.2.3.3: it must not be called more than once.", function () {
+ specify("already-rejected", function (done) {
+ var timesCalled = 0;
+
+ rejected(dummy).then(null, function onRejected() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+ });
+
+ specify("trying to reject a pending promise more than once, immediately", function (done) {
+ var tuple = pending();
+ var timesCalled = 0;
+
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+
+ tuple.reject(dummy);
+ tuple.reject(dummy);
+ });
+
+ specify("trying to reject a pending promise more than once, delayed", function (done) {
+ var tuple = pending();
+ var timesCalled = 0;
+
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+
+ setTimeout(function () {
+ tuple.reject(dummy);
+ tuple.reject(dummy);
+ }, 50);
+ });
+
+ specify("trying to reject a pending promise more than once, immediately then delayed", function (done) {
+ var tuple = pending();
+ var timesCalled = 0;
+
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+
+ tuple.reject(dummy);
+ setTimeout(function () {
+ tuple.reject(dummy);
+ }, 50);
+ });
+
+ specify("when multiple `then` calls are made, spaced apart in time", function (done) {
+ var tuple = pending();
+ var timesCalled = [0, 0, 0];
+
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled[0], 1);
+ });
+
+ setTimeout(function () {
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled[1], 1);
+ });
+ }, 50);
+
+ setTimeout(function () {
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled[2], 1);
+ done();
+ });
+ }, 100);
+
+ setTimeout(function () {
+ tuple.reject(dummy);
+ }, 150);
+ });
+
+ specify("when `then` is interleaved with rejection", function (done) {
+ var tuple = pending();
+ var timesCalled = [0, 0];
+
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled[0], 1);
+ });
+
+ tuple.reject(dummy);
+
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled[1], 1);
+ done();
+ });
+ });
+ });
+});
diff --git a/test/mocha/2.2.4.js b/test/mocha/2.2.4.js
new file mode 100644
index 0000000..5f953f2
--- /dev/null
+++ b/test/mocha/2.2.4.js
@@ -0,0 +1,182 @@
+"use strict";
+
+var assert = require("assert");
+var testFulfilled = require("./helpers/testThreeCases").testFulfilled;
+var testRejected = require("./helpers/testThreeCases").testRejected;
+
+var adapter = global.adapter;
+var fulfilled = adapter.fulfilled;
+var rejected = adapter.rejected;
+var pending = adapter.pending;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+
+describe("2.2.4: `onFulfilled` or `onRejected` must not be called until the execution context stack contains only " +
+ "platform code.", function () {
+ describe("`then` returns before the promise becomes fulfilled or rejected", function () {
+ testFulfilled(dummy, function (promise, done) {
+ var thenHasReturned = false;
+
+ promise.then(function onFulfilled() {
+ assert.strictEqual(thenHasReturned, true);
+ done();
+ });
+
+ thenHasReturned = true;
+ });
+ testRejected(dummy, function (promise, done) {
+ var thenHasReturned = false;
+
+ promise.then(null, function onRejected() {
+ assert.strictEqual(thenHasReturned, true);
+ done();
+ });
+
+ thenHasReturned = true;
+ });
+ });
+
+ describe("Clean-stack execution ordering tests (fulfillment case)", function () {
+ specify("when `onFulfilled` is added immediately before the promise is fulfilled",
+ function () {
+ var tuple = pending();
+ var onFulfilledCalled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ onFulfilledCalled = true;
+ });
+
+ tuple.fulfill(dummy);
+
+ assert.strictEqual(onFulfilledCalled, false);
+ });
+
+ specify("when `onFulfilled` is added immediately after the promise is fulfilled",
+ function () {
+ var tuple = pending();
+ var onFulfilledCalled = false;
+
+ tuple.fulfill(dummy);
+
+ tuple.promise.then(function onFulfilled() {
+ onFulfilledCalled = true;
+ });
+
+ assert.strictEqual(onFulfilledCalled, false);
+ });
+
+ specify("when one `onFulfilled` is added inside another `onFulfilled`", function (done) {
+ var promise = fulfilled();
+ var firstOnFulfilledFinished = false;
+
+ promise.then(function () {
+ promise.then(function () {
+ assert.strictEqual(firstOnFulfilledFinished, true);
+ done();
+ });
+ firstOnFulfilledFinished = true;
+ });
+ });
+
+ specify("when `onFulfilled` is added inside an `onRejected`", function (done) {
+ var promise = rejected();
+ var promise2 = fulfilled();
+ var firstOnRejectedFinished = false;
+
+ promise.then(null, function () {
+ promise2.then(function () {
+ assert.strictEqual(firstOnRejectedFinished, true);
+ done();
+ });
+ firstOnRejectedFinished = true;
+ });
+ });
+
+ specify("when the promise is fulfilled asynchronously", function (done) {
+ var tuple = pending();
+ var firstStackFinished = false;
+
+ setTimeout(function () {
+ tuple.fulfill(dummy);
+ firstStackFinished = true;
+ }, 0);
+
+ tuple.promise.then(function () {
+ assert.strictEqual(firstStackFinished, true);
+ done();
+ });
+ });
+ });
+
+ describe("Clean-stack execution ordering tests (rejection case)", function () {
+ specify("when `onRejected` is added immediately before the promise is rejected",
+ function () {
+ var tuple = pending();
+ var onRejectedCalled = false;
+
+ tuple.promise.then(null, function onRejected() {
+ onRejectedCalled = true;
+ });
+
+ tuple.reject(dummy);
+
+ assert.strictEqual(onRejectedCalled, false);
+ });
+
+ specify("when `onRejected` is added immediately after the promise is rejected",
+ function () {
+ var tuple = pending();
+ var onRejectedCalled = false;
+
+ tuple.reject(dummy);
+
+ tuple.promise.then(null, function onRejected() {
+ onRejectedCalled = true;
+ });
+
+ assert.strictEqual(onRejectedCalled, false);
+ });
+
+ specify("when `onRejected` is added inside an `onFulfilled`", function (done) {
+ var promise = fulfilled();
+ var promise2 = rejected();
+ var firstOnFulfilledFinished = false;
+
+ promise.then(function () {
+ promise2.then(null, function () {
+ assert.strictEqual(firstOnFulfilledFinished, true);
+ done();
+ });
+ firstOnFulfilledFinished = true;
+ });
+ });
+
+ specify("when one `onRejected` is added inside another `onRejected`", function (done) {
+ var promise = rejected();
+ var firstOnRejectedFinished = false;
+
+ promise.then(null, function () {
+ promise.then(null, function () {
+ assert.strictEqual(firstOnRejectedFinished, true);
+ done();
+ });
+ firstOnRejectedFinished = true;
+ });
+ });
+
+ specify("when the promise is rejected asynchronously", function (done) {
+ var tuple = pending();
+ var firstStackFinished = false;
+
+ setTimeout(function () {
+ tuple.reject(dummy);
+ firstStackFinished = true;
+ }, 0);
+
+ tuple.promise.then(null, function () {
+ assert.strictEqual(firstStackFinished, true);
+ done();
+ });
+ });
+ });
+});
diff --git a/test/mocha/2.2.5.js b/test/mocha/2.2.5.js
new file mode 100644
index 0000000..d40aa8a
--- /dev/null
+++ b/test/mocha/2.2.5.js
@@ -0,0 +1,58 @@
+/*jshint strict: false */
+
+var assert = require("assert");
+
+var adapter = global.adapter;
+var fulfilled = adapter.fulfilled;
+var rejected = adapter.rejected;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+
+var undefinedThisStrict = (function() {
+ "use strict";
+ return this;
+})();
+
+var undefinedThisSloppy = (function() {
+ return this;
+})();
+
+describe("2.2.5 `onFulfilled` and `onRejected` must be called as functions (i.e. with no `this` value).", function () {
+ describe("strict mode", function () {
+ specify("fulfilled", function (done) {
+ fulfilled(dummy).then(function onFulfilled() {
+ "use strict";
+
+ assert(this === undefinedThisStrict ||
+ this === undefinedThisSloppy);
+ done();
+ });
+ });
+
+ specify("rejected", function (done) {
+ rejected(dummy).then(null, function onRejected() {
+ "use strict";
+
+ assert(this === undefinedThisStrict ||
+ this === undefinedThisSloppy);
+ done();
+ });
+ });
+ });
+
+ describe("sloppy mode", function () {
+ specify("fulfilled", function (done) {
+ fulfilled(dummy).then(function onFulfilled() {
+ assert.strictEqual(this, undefinedThisSloppy);
+ done();
+ });
+ });
+
+ specify("rejected", function (done) {
+ rejected(dummy).then(null, function onRejected() {
+ assert.strictEqual(this, undefinedThisSloppy);
+ done();
+ });
+ });
+ });
+});
diff --git a/test/mocha/2.2.6.js b/test/mocha/2.2.6.js
new file mode 100644
index 0000000..74b558c
--- /dev/null
+++ b/test/mocha/2.2.6.js
@@ -0,0 +1,257 @@
+"use strict";
+
+var assert = require("assert");
+var sinon = require("sinon");
+var testFulfilled = require("./helpers/testThreeCases").testFulfilled;
+var testRejected = require("./helpers/testThreeCases").testRejected;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+var other = { other: "other" }; // a value we don't want to be strict equal to
+var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality
+var sentinel2 = { sentinel2: "sentinel2" };
+var sentinel3 = { sentinel3: "sentinel3" };
+
+function callbackAggregator(times, ultimateCallback) {
+ var soFar = 0;
+ return function () {
+ if (++soFar === times) {
+ ultimateCallback();
+ }
+ };
+}
+
+describe("2.2.6: `then` may be called multiple times on the same promise.", function () {
+ describe("2.2.6.1: If/when `promise` is fulfilled, all respective `onFulfilled` callbacks must execute in the " +
+ "order of their originating calls to `then`.", function () {
+ describe("multiple boring fulfillment handlers", function () {
+ testFulfilled(sentinel, function (promise, done) {
+ var handler1 = sinon.stub().returns(other);
+ var handler2 = sinon.stub().returns(other);
+ var handler3 = sinon.stub().returns(other);
+
+ var spy = sinon.spy();
+ promise.then(handler1, spy);
+ promise.then(handler2, spy);
+ promise.then(handler3, spy);
+
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+
+ sinon.assert.calledWith(handler1, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler2, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler3, sinon.match.same(sentinel));
+ sinon.assert.notCalled(spy);
+
+ done();
+ });
+ });
+ });
+
+ describe("multiple fulfillment handlers, one of which throws", function () {
+ testFulfilled(sentinel, function (promise, done) {
+ var handler1 = sinon.stub().returns(other);
+ var handler2 = sinon.stub().throws(other);
+ var handler3 = sinon.stub().returns(other);
+
+ var spy = sinon.spy();
+ promise.then(handler1, spy);
+ promise.then(handler2, spy).caught(function(){});
+ promise.then(handler3, spy);
+
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+
+ sinon.assert.calledWith(handler1, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler2, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler3, sinon.match.same(sentinel));
+ sinon.assert.notCalled(spy);
+
+ done();
+ });
+ });
+ });
+
+ describe("results in multiple branching chains with their own fulfillment values", function () {
+ testFulfilled(dummy, function (promise, done) {
+ var semiDone = callbackAggregator(3, done);
+
+ promise.then(function () {
+ return sentinel;
+ }).then(function (value) {
+ assert.strictEqual(value, sentinel);
+ semiDone();
+ });
+
+ promise.then(function () {
+ throw sentinel2;
+ }).then(null, function (reason) {
+ assert.strictEqual(reason, sentinel2);
+ semiDone();
+ });
+
+ promise.then(function () {
+ return sentinel3;
+ }).then(function (value) {
+ assert.strictEqual(value, sentinel3);
+ semiDone();
+ });
+ });
+ });
+
+ describe("`onFulfilled` handlers are called in the original order", function () {
+ testFulfilled(dummy, function (promise, done) {
+ var handler1 = sinon.spy(function handler1() {});
+ var handler2 = sinon.spy(function handler2() {});
+ var handler3 = sinon.spy(function handler3() {});
+
+ promise.then(handler1);
+ promise.then(handler2);
+ promise.then(handler3);
+
+ promise.then(function () {
+ sinon.assert.callOrder(handler1, handler2, handler3);
+ done();
+ });
+ });
+
+ describe("even when one handler is added inside another handler", function () {
+ testFulfilled(dummy, function (promise, done) {
+ var handler1 = sinon.spy(function handler1() {});
+ var handler2 = sinon.spy(function handler2() {});
+ var handler3 = sinon.spy(function handler3() {});
+
+ promise.then(function () {
+ handler1();
+ promise.then(handler3);
+ });
+ promise.then(handler2);
+
+ promise.then(function () {
+ // Give implementations a bit of extra time to flush their internal queue, if necessary.
+ setTimeout(function () {
+ sinon.assert.callOrder(handler1, handler2, handler3);
+ done();
+ }, 15);
+ });
+ });
+ });
+ });
+ });
+
+ describe("2.2.6.2: If/when `promise` is rejected, all respective `onRejected` callbacks must execute in the " +
+ "order of their originating calls to `then`.", function () {
+ describe("multiple boring rejection handlers", function () {
+ testRejected(sentinel, function (promise, done) {
+ var handler1 = sinon.stub().returns(other);
+ var handler2 = sinon.stub().returns(other);
+ var handler3 = sinon.stub().returns(other);
+
+ var spy = sinon.spy();
+ promise.then(spy, handler1);
+ promise.then(spy, handler2);
+ promise.then(spy, handler3);
+
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+
+ sinon.assert.calledWith(handler1, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler2, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler3, sinon.match.same(sentinel));
+ sinon.assert.notCalled(spy);
+
+ done();
+ });
+ });
+ });
+
+ describe("multiple rejection handlers, one of which throws", function () {
+ testRejected(sentinel, function (promise, done) {
+ var handler1 = sinon.stub().returns(other);
+ var handler2 = sinon.stub().throws(other);
+ var handler3 = sinon.stub().returns(other);
+
+ var spy = sinon.spy();
+ promise.then(spy, handler1);
+ promise.then(spy, handler2).caught(function(){});
+ promise.then(spy, handler3);
+
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+
+ sinon.assert.calledWith(handler1, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler2, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler3, sinon.match.same(sentinel));
+ sinon.assert.notCalled(spy);
+
+ done();
+ });
+ });
+ });
+
+ describe("results in multiple branching chains with their own fulfillment values", function () {
+ testRejected(sentinel, function (promise, done) {
+ var semiDone = callbackAggregator(3, done);
+
+ promise.then(null, function () {
+ return sentinel;
+ }).then(function (value) {
+ assert.strictEqual(value, sentinel);
+ semiDone();
+ });
+
+ promise.then(null, function () {
+ throw sentinel2;
+ }).then(null, function (reason) {
+ assert.strictEqual(reason, sentinel2);
+ semiDone();
+ });
+
+ promise.then(null, function () {
+ return sentinel3;
+ }).then(function (value) {
+ assert.strictEqual(value, sentinel3);
+ semiDone();
+ });
+ });
+ });
+
+ describe("`onRejected` handlers are called in the original order", function () {
+ testRejected(dummy, function (promise, done) {
+ var handler1 = sinon.spy(function handler1() {});
+ var handler2 = sinon.spy(function handler2() {});
+ var handler3 = sinon.spy(function handler3() {});
+
+ promise.then(null, handler1);
+ promise.then(null, handler2);
+ promise.then(null, handler3);
+
+ promise.then(null, function () {
+ sinon.assert.callOrder(handler1, handler2, handler3);
+ done();
+ });
+ });
+
+ describe("even when one handler is added inside another handler", function () {
+ testRejected(dummy, function (promise, done) {
+ var handler1 = sinon.spy(function handler1() {});
+ var handler2 = sinon.spy(function handler2() {});
+ var handler3 = sinon.spy(function handler3() {});
+
+ promise.then(null, function () {
+ handler1();
+ promise.then(null, handler3);
+ });
+ promise.then(null, handler2);
+
+ promise.then(null, function () {
+ // Give implementations a bit of extra time to flush their internal queue, if necessary.
+ setTimeout(function () {
+ sinon.assert.callOrder(handler1, handler2, handler3);
+ done();
+ }, 15);
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/test/mocha/2.2.7.js b/test/mocha/2.2.7.js
new file mode 100644
index 0000000..a9ef1f6
--- /dev/null
+++ b/test/mocha/2.2.7.js
@@ -0,0 +1,109 @@
+"use strict";
+
+var assert = require("assert");
+var testFulfilled = require("./helpers/testThreeCases").testFulfilled;
+var testRejected = require("./helpers/testThreeCases").testRejected;
+var reasons = require("./helpers/reasons");
+
+var adapter = global.adapter;
+var pending = adapter.pending;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality
+var other = { other: "other" }; // a value we don't want to be strict equal to
+
+describe("2.2.7: `then` must return a promise: `promise2 = promise1.then(onFulfilled, onRejected)`", function () {
+ specify("is a promise", function () {
+ var promise1 = pending().promise;
+ var promise2 = promise1.then();
+
+ assert(typeof promise2 === "object" || typeof promise2 === "function");
+ assert.notStrictEqual(promise2, null);
+ assert.strictEqual(typeof promise2.then, "function");
+ });
+
+ describe("2.2.7.1: If either `onFulfilled` or `onRejected` returns a value `x`, run the Promise Resolution " +
+ "Procedure `[[Resolve]](promise2, x)`", function () {
+ specify("see separate 3.3 tests", function () { });
+ });
+
+ describe("2.2.7.2: If either `onFulfilled` or `onRejected` throws an exception `e`, `promise2` must be rejected " +
+ "with `e` as the reason.", function () {
+ function testReason(expectedReason, stringRepresentation) {
+ describe("The reason is " + stringRepresentation, function () {
+ testFulfilled(dummy, function (promise1, done) {
+ var promise2 = promise1.then(function onFulfilled() {
+ throw expectedReason;
+ });
+
+ promise2.then(null, function onPromise2Rejected(actualReason) {
+ assert.strictEqual(actualReason, expectedReason);
+ done();
+ });
+ });
+ testRejected(dummy, function (promise1, done) {
+ var promise2 = promise1.then(null, function onRejected() {
+ throw expectedReason;
+ });
+
+ promise2.then(null, function onPromise2Rejected(actualReason) {
+ assert.strictEqual(actualReason, expectedReason);
+ done();
+ });
+ });
+ });
+ }
+
+ Object.keys(reasons).forEach(function (stringRepresentation) {
+ testReason(reasons[stringRepresentation], stringRepresentation);
+ });
+ });
+
+ describe("2.2.7.3: If `onFulfilled` is not a function and `promise1` is fulfilled, `promise2` must be fulfilled " +
+ "with the same value.", function () {
+
+ function testNonFunction(nonFunction, stringRepresentation) {
+ describe("`onFulfilled` is " + stringRepresentation, function () {
+ testFulfilled(sentinel, function (promise1, done) {
+ var promise2 = promise1.then(nonFunction);
+
+ promise2.then(function onPromise2Fulfilled(value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+ }
+
+ testNonFunction(undefined, "`undefined`");
+ testNonFunction(null, "`null`");
+ testNonFunction(false, "`false`");
+ testNonFunction(5, "`5`");
+ testNonFunction({}, "an object");
+ testNonFunction([function () { return other; }], "an array containing a function");
+ });
+
+ describe("2.2.7.4: If `onRejected` is not a function and `promise1` is rejected, `promise2` must be rejected " +
+ "with the same reason.", function () {
+
+ function testNonFunction(nonFunction, stringRepresentation) {
+ describe("`onRejected` is " + stringRepresentation, function () {
+ testRejected(sentinel, function (promise1, done) {
+ var promise2 = promise1.then(null, nonFunction);
+
+ promise2.then(null, function onPromise2Rejected(reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+ }
+
+ testNonFunction(undefined, "`undefined`");
+ testNonFunction(null, "`null`");
+ testNonFunction(false, "`false`");
+ testNonFunction(5, "`5`");
+ testNonFunction({}, "an object");
+ testNonFunction([function () { return other; }], "an array containing a function");
+ });
+});
diff --git a/test/mocha/2.3.1.js b/test/mocha/2.3.1.js
new file mode 100644
index 0000000..6a1b8ff
--- /dev/null
+++ b/test/mocha/2.3.1.js
@@ -0,0 +1,34 @@
+"use strict";
+
+var assert = require("assert");
+
+var adapter = global.adapter;
+var fulfilled = adapter.fulfilled;
+var rejected = adapter.rejected;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+
+describe("2.3.1: If `promise` and `x` refer to the same object, reject `promise` with a `TypeError' as the reason.",
+ function () {
+ specify("via return from a fulfilled promise", function (done) {
+ var promise = fulfilled(dummy).then(function () {
+ return promise;
+ });
+
+ promise.then(null, function (reason) {
+ assert(reason instanceof adapter.TypeError);
+ done();
+ });
+ });
+
+ specify("via return from a rejected promise", function (done) {
+ var promise = rejected(dummy).then(null, function () {
+ return promise;
+ });
+
+ promise.then(null, function (reason) {
+ assert(reason instanceof adapter.TypeError);
+ done();
+ });
+ });
+});
diff --git a/test/mocha/2.3.2.js b/test/mocha/2.3.2.js
new file mode 100644
index 0000000..913af92
--- /dev/null
+++ b/test/mocha/2.3.2.js
@@ -0,0 +1,126 @@
+"use strict";
+
+var assert = require("assert");
+
+var adapter = global.adapter;
+var fulfilled = adapter.fulfilled;
+var rejected = adapter.rejected;
+var pending = adapter.pending;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality
+
+function testPromiseResolution(xFactory, test) {
+ specify("via return from a fulfilled promise", function (done) {
+ var promise = fulfilled(dummy).then(function onBasePromiseFulfilled() {
+ return xFactory();
+ });
+
+ test(promise, done);
+ });
+
+ specify("via return from a rejected promise", function (done) {
+ var promise = rejected(dummy).then(null, function onBasePromiseRejected() {
+ return xFactory();
+ });
+
+ test(promise, done);
+ });
+}
+
+describe("2.3.2: If `x` is a promise, adopt its state", function () {
+ describe("2.3.2.1: If `x` is pending, `promise` must remain pending until `x` is fulfilled or rejected.",
+ function () {
+ function xFactory() {
+ return pending().promise;
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ var wasFulfilled = false;
+ var wasRejected = false;
+
+ promise.then(
+ function onPromiseFulfilled() {
+ wasFulfilled = true;
+ },
+ function onPromiseRejected() {
+ wasRejected = true;
+ }
+ );
+
+ setTimeout(function () {
+ assert.strictEqual(wasFulfilled, false);
+ assert.strictEqual(wasRejected, false);
+ done();
+ }, 100);
+ });
+ });
+
+ describe("2.3.2.2: If/when `x` is fulfilled, fulfill `promise` with the same value.", function () {
+ describe("`x` is already-fulfilled", function () {
+ function xFactory() {
+ return fulfilled(sentinel);
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(function onPromiseFulfilled(value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("`x` is eventually-fulfilled", function () {
+ var tuple = null;
+
+ function xFactory() {
+ tuple = pending();
+ setTimeout(function () {
+ tuple.fulfill(sentinel);
+ }, 50);
+ return tuple.promise;
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(function onPromiseFulfilled(value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+ });
+
+ describe("2.3.2.3: If/when `x` is rejected, reject `promise` with the same reason.", function () {
+ describe("`x` is already-rejected", function () {
+ function xFactory() {
+ return rejected(sentinel);
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(null, function onPromiseRejected(reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("`x` is eventually-rejected", function () {
+ var tuple = null;
+
+ function xFactory() {
+ tuple = pending();
+ setTimeout(function () {
+ tuple.reject(sentinel);
+ }, 50);
+ return tuple.promise;
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(null, function onPromiseRejected(reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+ });
+});
diff --git a/test/mocha/2.3.3.js b/test/mocha/2.3.3.js
new file mode 100644
index 0000000..97c13a2
--- /dev/null
+++ b/test/mocha/2.3.3.js
@@ -0,0 +1,993 @@
+"use strict";
+
+var assert = require("assert");
+var thenables = require("./helpers/thenables");
+var reasons = require("./helpers/reasons");
+
+var adapter = global.adapter;
+var fulfilled = adapter.fulfilled;
+var rejected = adapter.rejected;
+var pending = adapter.pending;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality
+var other = { other: "other" }; // a value we don't want to be strict equal to
+var sentinelArray = [sentinel]; // a sentinel fulfillment value to test when we need an array
+
+function testPromiseResolution(xFactory, test) {
+ specify("via return from a fulfilled promise", function (done) {
+ var promise = fulfilled(dummy).then(function onBasePromiseFulfilled() {
+ return xFactory();
+ });
+
+ test(promise, done);
+ });
+
+ specify("via return from a rejected promise", function (done) {
+ var promise = rejected(dummy).then(null, function onBasePromiseRejected() {
+ return xFactory();
+ });
+
+ test(promise, done);
+ });
+}
+
+function testCallingResolvePromise(yFactory, stringRepresentation, test) {
+ describe("`y` is " + stringRepresentation, function () {
+ describe("`then` calls `resolvePromise` synchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise) {
+ resolvePromise(yFactory());
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, test);
+ });
+
+ describe("`then` calls `resolvePromise` asynchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise) {
+ setTimeout(function () {
+ resolvePromise(yFactory());
+ }, 0);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, test);
+ });
+ });
+}
+
+function testCallingRejectPromise(r, stringRepresentation, test) {
+ describe("`r` is " + stringRepresentation, function () {
+ describe("`then` calls `rejectPromise` synchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ rejectPromise(r);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, test);
+ });
+
+ describe("`then` calls `rejectPromise` asynchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ setTimeout(function () {
+ rejectPromise(r);
+ }, 0);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, test);
+ });
+ });
+}
+
+function testCallingResolvePromiseFulfillsWith(yFactory, stringRepresentation, fulfillmentValue) {
+ testCallingResolvePromise(yFactory, stringRepresentation, function (promise, done) {
+
+ promise.then(function onPromiseFulfilled(value) {
+ assert.strictEqual(value, fulfillmentValue);
+ done();
+ });
+ });
+}
+
+function testCallingResolvePromiseRejectsWith(yFactory, stringRepresentation, rejectionReason) {
+ testCallingResolvePromise(yFactory, stringRepresentation, function (promise, done) {
+
+ promise.then(null, function onPromiseRejected(reason) {
+ assert.strictEqual(reason, rejectionReason);
+ done();
+ });
+ });
+}
+
+function testCallingRejectPromiseRejectsWith(reason, stringRepresentation) {
+ testCallingRejectPromise(reason, stringRepresentation, function (promise, done) {
+
+ promise.then(null, function onPromiseRejected(rejectionReason) {
+ assert.strictEqual(rejectionReason, reason);
+ done();
+ });
+ });
+}
+
+describe("2.3.3: Otherwise, if `x` is an object or function,", function () {
+ describe("2.3.3.1: Let `then` be `x.then`", function () {
+ describe("`x` is an object with null prototype", function () {
+ var numberOfTimesThenWasRetrieved = null;
+
+ beforeEach(function () {
+ numberOfTimesThenWasRetrieved = 0;
+ });
+
+ function xFactory() {
+ return Object.create(null, {
+ then: {
+ get: function () {
+ ++numberOfTimesThenWasRetrieved;
+ return function thenMethodForX(onFulfilled) {
+ onFulfilled();
+ };
+ }
+ }
+ });
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(function () {
+ assert.strictEqual(numberOfTimesThenWasRetrieved, 1);
+ done();
+ });
+ });
+ });
+
+ describe("`x` is an object with normal Object.prototype", function () {
+ var numberOfTimesThenWasRetrieved = null;
+
+ beforeEach(function () {
+ numberOfTimesThenWasRetrieved = 0;
+ });
+
+ function xFactory() {
+ return Object.create(Object.prototype, {
+ then: {
+ get: function () {
+ ++numberOfTimesThenWasRetrieved;
+ return function thenMethodForX(onFulfilled) {
+ onFulfilled();
+ };
+ }
+ }
+ });
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(function () {
+ assert.strictEqual(numberOfTimesThenWasRetrieved, 1);
+ done();
+ });
+ });
+ });
+
+ describe("`x` is a function", function () {
+ var numberOfTimesThenWasRetrieved = null;
+
+ beforeEach(function () {
+ numberOfTimesThenWasRetrieved = 0;
+ });
+
+ function xFactory() {
+ function x() { }
+
+ Object.defineProperty(x, "then", {
+ get: function () {
+ ++numberOfTimesThenWasRetrieved;
+ return function thenMethodForX(onFulfilled) {
+ onFulfilled();
+ };
+ }
+ });
+
+ return x;
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(function () {
+ assert.strictEqual(numberOfTimesThenWasRetrieved, 1);
+ done();
+ });
+ });
+ });
+ });
+
+ describe("2.3.3.2: If retrieving the property `x.then` results in a thrown exception `e`, reject `promise` with " +
+ "`e` as the reason.", function () {
+ function testRejectionViaThrowingGetter(e, stringRepresentation) {
+ function xFactory() {
+ return Object.create(Object.prototype, {
+ then: {
+ get: function () {
+ throw e;
+ }
+ }
+ });
+ }
+
+ describe("`e` is " + stringRepresentation, function () {
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, e);
+ done();
+ });
+ });
+ });
+ }
+
+ Object.keys(reasons).forEach(function (stringRepresentation) {
+ testRejectionViaThrowingGetter(reasons[stringRepresentation], stringRepresentation);
+ });
+ });
+
+ describe("2.3.3.3: If `then` is a function, call it with `x` as `this`, first argument `resolvePromise`, and " +
+ "second argument `rejectPromise`", function () {
+ describe("Calls with `x` as `this` and two function arguments", function () {
+ function xFactory() {
+ var x = {
+ then: function (onFulfilled, onRejected) {
+ assert.strictEqual(this, x);
+ assert.strictEqual(typeof onFulfilled, "function");
+ assert.strictEqual(typeof onRejected, "function");
+ onFulfilled();
+ }
+ };
+ return x;
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(function () {
+ done();
+ });
+ });
+ });
+
+ describe("Uses the original value of `then`", function () {
+ var numberOfTimesThenWasRetrieved = null;
+
+ beforeEach(function () {
+ numberOfTimesThenWasRetrieved = 0;
+ });
+
+ function xFactory() {
+ return Object.create(Object.prototype, {
+ then: {
+ get: function () {
+ if (numberOfTimesThenWasRetrieved === 0) {
+ return function (onFulfilled) {
+ onFulfilled();
+ };
+ }
+ return null;
+ }
+ }
+ });
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(function () {
+ done();
+ });
+ });
+ });
+
+ describe("2.3.3.3.1: If/when `resolvePromise` is called with value `y`, run `[[Resolve]](promise, y)`",
+ function () {
+ describe("`y` is not a thenable", function () {
+ testCallingResolvePromiseFulfillsWith(function () { return undefined; }, "`undefined`", undefined);
+ testCallingResolvePromiseFulfillsWith(function () { return null; }, "`null`", null);
+ testCallingResolvePromiseFulfillsWith(function () { return false; }, "`false`", false);
+ testCallingResolvePromiseFulfillsWith(function () { return 5; }, "`5`", 5);
+ testCallingResolvePromiseFulfillsWith(function () { return sentinel; }, "an object", sentinel);
+ testCallingResolvePromiseFulfillsWith(function () { return sentinelArray; }, "an array", sentinelArray);
+ });
+
+ describe("`y` is a thenable", function () {
+ Object.keys(thenables.fulfilled).forEach(function (stringRepresentation) {
+ function yFactory() {
+ return thenables.fulfilled[stringRepresentation](sentinel);
+ }
+
+ testCallingResolvePromiseFulfillsWith(yFactory, stringRepresentation, sentinel);
+ });
+
+ Object.keys(thenables.rejected).forEach(function (stringRepresentation) {
+ function yFactory() {
+ return thenables.rejected[stringRepresentation](sentinel);
+ }
+
+ testCallingResolvePromiseRejectsWith(yFactory, stringRepresentation, sentinel);
+ });
+ });
+
+ describe("`y` is a thenable for a thenable", function () {
+ Object.keys(thenables.fulfilled).forEach(function (outerStringRepresentation) {
+ var outerThenableFactory = thenables.fulfilled[outerStringRepresentation];
+
+ Object.keys(thenables.fulfilled).forEach(function (innerStringRepresentation) {
+ var innerThenableFactory = thenables.fulfilled[innerStringRepresentation];
+
+ var stringRepresentation = outerStringRepresentation + " for " + innerStringRepresentation;
+
+ function yFactory() {
+ return outerThenableFactory(innerThenableFactory(sentinel));
+ }
+
+ testCallingResolvePromiseFulfillsWith(yFactory, stringRepresentation, sentinel);
+ });
+
+ Object.keys(thenables.rejected).forEach(function (innerStringRepresentation) {
+ var innerThenableFactory = thenables.rejected[innerStringRepresentation];
+
+ var stringRepresentation = outerStringRepresentation + " for " + innerStringRepresentation;
+
+ function yFactory() {
+ return outerThenableFactory(innerThenableFactory(sentinel));
+ }
+
+ testCallingResolvePromiseRejectsWith(yFactory, stringRepresentation, sentinel);
+ });
+ });
+ });
+ });
+
+ describe("2.3.3.3.2: If/when `rejectPromise` is called with reason `r`, reject `promise` with `r`",
+ function () {
+ Object.keys(reasons).forEach(function (stringRepresentation) {
+ testCallingRejectPromiseRejectsWith(reasons[stringRepresentation], stringRepresentation);
+ });
+ });
+
+ describe("2.3.3.3.3: If both `resolvePromise` and `rejectPromise` are called, or multiple calls to the same " +
+ "argument are made, the first call takes precedence, and any further calls are ignored.",
+ function () {
+ describe("calling `resolvePromise` then `rejectPromise`, both synchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ resolvePromise(sentinel);
+ rejectPromise(other);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `resolvePromise` synchronously then `rejectPromise` asynchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ resolvePromise(sentinel);
+
+ setTimeout(function () {
+ rejectPromise(other);
+ }, 0);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `resolvePromise` then `rejectPromise`, both asynchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ setTimeout(function () {
+ resolvePromise(sentinel);
+ }, 0);
+
+ setTimeout(function () {
+ rejectPromise(other);
+ }, 0);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `resolvePromise` with an asynchronously-fulfilled promise, then calling " +
+ "`rejectPromise`, both synchronously", function () {
+ function xFactory() {
+ var tuple = pending();
+ setTimeout(function () {
+ tuple.fulfill(sentinel);
+ }, 50);
+
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ resolvePromise(tuple.promise);
+ rejectPromise(other);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `resolvePromise` with an asynchronously-rejected promise, then calling " +
+ "`rejectPromise`, both synchronously", function () {
+ function xFactory() {
+ var tuple = pending();
+ setTimeout(function () {
+ tuple.reject(sentinel);
+ }, 50);
+
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ resolvePromise(tuple.promise);
+ rejectPromise(other);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `rejectPromise` then `resolvePromise`, both synchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ rejectPromise(sentinel);
+ resolvePromise(other);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `rejectPromise` synchronously then `resolvePromise` asynchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ rejectPromise(sentinel);
+
+ setTimeout(function () {
+ resolvePromise(other);
+ }, 0);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `rejectPromise` then `resolvePromise`, both asynchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ setTimeout(function () {
+ rejectPromise(sentinel);
+ }, 0);
+
+ setTimeout(function () {
+ resolvePromise(other);
+ }, 0);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `resolvePromise` twice synchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise) {
+ resolvePromise(sentinel);
+ resolvePromise(other);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `resolvePromise` twice, first synchronously then asynchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise) {
+ resolvePromise(sentinel);
+
+ setTimeout(function () {
+ resolvePromise(other);
+ }, 0);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `resolvePromise` twice, both times asynchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise) {
+ setTimeout(function () {
+ resolvePromise(sentinel);
+ }, 0);
+
+ setTimeout(function () {
+ resolvePromise(other);
+ }, 0);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `resolvePromise` with an asynchronously-fulfilled promise, then calling it again, both " +
+ "times synchronously", function () {
+ function xFactory() {
+ var tuple = pending();
+ setTimeout(function () {
+ tuple.fulfill(sentinel);
+ }, 50);
+
+ return {
+ then: function (resolvePromise) {
+ resolvePromise(tuple.promise);
+ resolvePromise(other);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `resolvePromise` with an asynchronously-rejected promise, then calling it again, both " +
+ "times synchronously", function () {
+ function xFactory() {
+ var tuple = pending();
+ setTimeout(function () {
+ tuple.reject(sentinel);
+ }, 50);
+
+ return {
+ then: function (resolvePromise) {
+ resolvePromise(tuple.promise);
+ resolvePromise(other);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `rejectPromise` twice synchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ rejectPromise(sentinel);
+ rejectPromise(other);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `rejectPromise` twice, first synchronously then asynchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ rejectPromise(sentinel);
+
+ setTimeout(function () {
+ resolvePromise(other);
+ }, 0);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("calling `rejectPromise` twice, both times asynchronously", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ setTimeout(function () {
+ rejectPromise(sentinel);
+ }, 0);
+
+ setTimeout(function () {
+ resolvePromise(other);
+ }, 0);
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("saving and abusing `resolvePromise` and `rejectPromise`", function () {
+ var savedResolvePromise, savedRejectPromise;
+
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ savedResolvePromise = resolvePromise;
+ savedRejectPromise = rejectPromise;
+ }
+ };
+ }
+
+ beforeEach(function () {
+ savedResolvePromise = null;
+ savedRejectPromise = null;
+ });
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ var timesFulfilled = 0;
+ var timesRejected = 0;
+
+ promise.then(
+ function () {
+ ++timesFulfilled;
+ },
+ function () {
+ ++timesRejected;
+ }
+ );
+
+ if (savedResolvePromise && savedRejectPromise) {
+ savedResolvePromise(dummy);
+ savedResolvePromise(dummy);
+ savedRejectPromise(dummy);
+ savedRejectPromise(dummy);
+ }
+
+ setTimeout(function () {
+ savedResolvePromise(dummy);
+ savedResolvePromise(dummy);
+ savedRejectPromise(dummy);
+ savedRejectPromise(dummy);
+ }, 4);
+
+ setTimeout(function () {
+ assert.strictEqual(timesFulfilled, 1);
+ assert.strictEqual(timesRejected, 0);
+ done();
+ }, 60);
+ });
+ });
+ });
+
+ describe("2.3.3.3.4: If calling `then` throws an exception `e`,", function () {
+ describe("2.3.3.3.4.1: If `resolvePromise` or `rejectPromise` have been called, ignore it.", function () {
+ describe("`resolvePromise` was called with a non-thenable", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise) {
+ resolvePromise(sentinel);
+ throw other;
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("`resolvePromise` was called with an asynchronously-fulfilled promise", function () {
+ function xFactory() {
+ var tuple = pending();
+ setTimeout(function () {
+ tuple.fulfill(sentinel);
+ }, 50);
+
+ return {
+ then: function (resolvePromise) {
+ resolvePromise(tuple.promise);
+ throw other;
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("`resolvePromise` was called with an asynchronously-rejected promise", function () {
+ function xFactory() {
+ var tuple = pending();
+ setTimeout(function () {
+ tuple.reject(sentinel);
+ }, 50);
+
+ return {
+ then: function (resolvePromise) {
+ resolvePromise(tuple.promise);
+ throw other;
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("`rejectPromise` was called", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ rejectPromise(sentinel);
+ throw other;
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("`resolvePromise` then `rejectPromise` were called", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ resolvePromise(sentinel);
+ rejectPromise(other);
+ throw other;
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("`rejectPromise` then `resolvePromise` were called", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ rejectPromise(sentinel);
+ resolvePromise(other);
+ throw other;
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+ });
+
+ describe("2.3.3.3.4.2: Otherwise, reject `promise` with `e` as the reason.", function () {
+ describe("straightforward case", function () {
+ function xFactory() {
+ return {
+ then: function () {
+ throw sentinel;
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("`resolvePromise` is called asynchronously before the `throw`", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise) {
+ setTimeout(function () {
+ resolvePromise(other);
+ }, 0);
+ throw sentinel;
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("`rejectPromise` is called asynchronously before the `throw`", function () {
+ function xFactory() {
+ return {
+ then: function (resolvePromise, rejectPromise) {
+ setTimeout(function () {
+ rejectPromise(other);
+ }, 0);
+ throw sentinel;
+ }
+ };
+ }
+
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+ });
+ });
+ });
+
+ describe("2.3.3.4: If `then` is not a function, fulfill promise with `x`", function () {
+ function testFulfillViaNonFunction(then, stringRepresentation) {
+ var x = null;
+
+ beforeEach(function () {
+ x = { then: then };
+ });
+
+ function xFactory() {
+ return x;
+ }
+
+ describe("`then` is " + stringRepresentation, function () {
+ testPromiseResolution(xFactory, function (promise, done) {
+ promise.then(function (value) {
+ assert.strictEqual(value, x);
+ done();
+ });
+ });
+ });
+ }
+
+ testFulfillViaNonFunction(5, "`5`");
+ testFulfillViaNonFunction({}, "an object");
+ testFulfillViaNonFunction([function () { }], "an array containing a function");
+ testFulfillViaNonFunction(/a-b/i, "a regular expression");
+ testFulfillViaNonFunction(Object.create(Function.prototype), "an object inheriting from `Function.prototype`");
+
+
+
+ });
+});
diff --git a/test/mocha/2.3.4.js b/test/mocha/2.3.4.js
new file mode 100644
index 0000000..e421eea
--- /dev/null
+++ b/test/mocha/2.3.4.js
@@ -0,0 +1,69 @@
+"use strict";
+
+var assert = require("assert");
+var testFulfilled = require("./helpers/testThreeCases").testFulfilled;
+var testRejected = require("./helpers/testThreeCases").testRejected;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+
+describe("2.3.4: If `x` is not an object or function, fulfill `promise` with `x`", function () {
+ function testValue(expectedValue, stringRepresentation, beforeEachHook, afterEachHook) {
+ describe("The value is " + stringRepresentation, function () {
+ if (typeof beforeEachHook === "function") {
+ beforeEach(beforeEachHook);
+ }
+ if (typeof afterEachHook === "function") {
+ afterEach(afterEachHook);
+ }
+
+ testFulfilled(dummy, function (promise1, done) {
+ var promise2 = promise1.then(function onFulfilled() {
+ return expectedValue;
+ });
+
+ promise2.then(function onPromise2Fulfilled(actualValue) {
+ assert.strictEqual(actualValue, expectedValue);
+ done();
+ });
+ });
+ testRejected(dummy, function (promise1, done) {
+ var promise2 = promise1.then(null, function onRejected() {
+ return expectedValue;
+ });
+
+ promise2.then(function onPromise2Fulfilled(actualValue) {
+ assert.strictEqual(actualValue, expectedValue);
+ done();
+ });
+ });
+ });
+ }
+
+ testValue(undefined, "`undefined`");
+ testValue(null, "`null`");
+ testValue(false, "`false`");
+ testValue(true, "`true`");
+ testValue(0, "`0`");
+
+ testValue(
+ true,
+ "`true` with `Boolean.prototype` modified to have a `then` method",
+ function () {
+ Boolean.prototype.then = function () {};
+ },
+ function () {
+ delete Boolean.prototype.then;
+ }
+ );
+
+ testValue(
+ 1,
+ "`1` with `Number.prototype` modified to have a `then` method",
+ function () {
+ Number.prototype.then = function () {};
+ },
+ function () {
+ delete Number.prototype.then;
+ }
+ );
+});
diff --git a/test/mocha/3.2.1.js b/test/mocha/3.2.1.js
new file mode 100644
index 0000000..1478a0f
--- /dev/null
+++ b/test/mocha/3.2.1.js
@@ -0,0 +1,41 @@
+"use strict";
+
+var adapter = global.adapter;
+var fulfilled = adapter.fulfilled;
+var rejected = adapter.rejected;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+
+describe("3.2.1: Both `onFulfilled` and `onRejected` are optional arguments.", function () {
+ describe("3.2.1.1: If `onFulfilled` is not a function, it must be ignored.", function () {
+ function testNonFunction(nonFunction, stringRepresentation) {
+ specify("`onFulfilled` is " + stringRepresentation, function (done) {
+ rejected(dummy).then(nonFunction, function () {
+ done();
+ });
+ });
+ }
+
+ testNonFunction(undefined, "`undefined`");
+ testNonFunction(null, "`null`");
+ testNonFunction(false, "`false`");
+ testNonFunction(5, "`5`");
+ testNonFunction({}, "an object");
+ });
+
+ describe("3.2.1.2: If `onRejected` is not a function, it must be ignored.", function () {
+ function testNonFunction(nonFunction, stringRepresentation) {
+ specify("`onRejected` is " + stringRepresentation, function (done) {
+ fulfilled(dummy).then(function () {
+ done();
+ }, nonFunction);
+ });
+ }
+
+ testNonFunction(undefined, "`undefined`");
+ testNonFunction(null, "`null`");
+ testNonFunction(false, "`false`");
+ testNonFunction(5, "`5`");
+ testNonFunction({}, "an object");
+ });
+});
diff --git a/test/mocha/3.2.2.js b/test/mocha/3.2.2.js
new file mode 100644
index 0000000..0e90048
--- /dev/null
+++ b/test/mocha/3.2.2.js
@@ -0,0 +1,187 @@
+"use strict";
+
+var assert = require("assert");
+var testFulfilled = require("./helpers/testThreeCases").testFulfilled;
+var testRejected = require("./helpers/testThreeCases").testRejected;
+
+var adapter = global.adapter;
+var fulfilled = adapter.fulfilled;
+var pending = adapter.pending;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality
+
+describe("3.2.2: If `onFulfilled` is a function,", function () {
+ describe("3.2.2.1: it must be called after `promise` is fulfilled, with `promise`’s fulfillment value as its " +
+ "first argument.", function () {
+ testFulfilled(sentinel, function (promise, done) {
+ promise.then(function onFulfilled(value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("3.2.2.2: it must not be called more than once.", function () {
+ specify("already-fulfilled", function (done) {
+ var timesCalled = 0;
+
+ fulfilled(dummy).then(function onFulfilled() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+ });
+
+ specify("trying to fulfill a pending promise more than once, immediately", function (done) {
+ var tuple = pending();
+ var timesCalled = 0;
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+
+ tuple.fulfill(dummy);
+ tuple.fulfill(dummy);
+ });
+
+ specify("trying to fulfill a pending promise more than once, delayed", function (done) {
+ var tuple = pending();
+ var timesCalled = 0;
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+
+ setTimeout(function () {
+ tuple.fulfill(dummy);
+ tuple.fulfill(dummy);
+ }, 50);
+ });
+
+ specify("trying to fulfill a pending promise more than once, immediately then delayed", function (done) {
+ var tuple = pending();
+ var timesCalled = 0;
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+
+ tuple.fulfill(dummy);
+ setTimeout(function () {
+ tuple.fulfill(dummy);
+ }, 50);
+ });
+
+ specify("when multiple `then` calls are made, spaced apart in time", function (done) {
+ var tuple = pending();
+ var timesCalled = [0, 0, 0];
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled[0], 1);
+ });
+
+ setTimeout(function () {
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled[1], 1);
+ });
+ }, 50);
+
+ setTimeout(function () {
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled[2], 1);
+ done();
+ });
+ }, 100);
+
+ setTimeout(function () {
+ tuple.fulfill(dummy);
+ }, 150);
+ });
+
+ specify("when `then` is interleaved with fulfillment", function (done) {
+ var tuple = pending();
+ var timesCalled = [0, 0];
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled[0], 1);
+ });
+
+ tuple.fulfill(dummy);
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(++timesCalled[1], 1);
+ done();
+ });
+ });
+ });
+
+ describe("3.2.2.3: it must not be called if `onRejected` has been called.", function () {
+ testRejected(dummy, function (promise, done) {
+ var onRejectedCalled = false;
+
+ promise.then(function onFulfilled() {
+ assert.strictEqual(onRejectedCalled, false);
+ done();
+ }, function onRejected() {
+ onRejectedCalled = true;
+ });
+
+ setTimeout(function(){done();}, 100);
+ });
+
+ specify("trying to reject then immediately fulfill", function (done) {
+ var tuple = pending();
+ var onRejectedCalled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(onRejectedCalled, false);
+ done();
+ }, function onRejected() {
+ onRejectedCalled = true;
+ });
+
+ tuple.reject(dummy);
+ tuple.fulfill(dummy);
+ setTimeout(function(){done();}, 100);
+ });
+
+ specify("trying to reject then fulfill, delayed", function (done) {
+ var tuple = pending();
+ var onRejectedCalled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(onRejectedCalled, false);
+ done();
+ }, function onRejected() {
+ onRejectedCalled = true;
+ });
+
+ setTimeout(function () {
+ tuple.reject(dummy);
+ tuple.fulfill(dummy);
+ }, 50);
+ setTimeout(function(){done();}, 100);
+ });
+
+ specify("trying to reject immediately then fulfill delayed", function (done) {
+ var tuple = pending();
+ var onRejectedCalled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ assert.strictEqual(onRejectedCalled, false);
+ done();
+ }, function onRejected() {
+ onRejectedCalled = true;
+ });
+
+ tuple.reject(dummy);
+ setTimeout(function () {
+ tuple.fulfill(dummy);
+ }, 50);
+ setTimeout(function(){done();}, 100);
+ });
+ });
+});
diff --git a/test/mocha/3.2.3.js b/test/mocha/3.2.3.js
new file mode 100644
index 0000000..3960d87
--- /dev/null
+++ b/test/mocha/3.2.3.js
@@ -0,0 +1,187 @@
+"use strict";
+
+var assert = require("assert");
+var testFulfilled = require("./helpers/testThreeCases").testFulfilled;
+var testRejected = require("./helpers/testThreeCases").testRejected;
+
+var adapter = global.adapter;
+var rejected = adapter.rejected;
+var pending = adapter.pending;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality
+
+describe("3.2.3: If `onRejected` is a function,", function () {
+ describe("3.2.3.1: it must be called after `promise` is rejected, with `promise`’s rejection reason as its " +
+ "first argument.", function () {
+ testRejected(sentinel, function (promise, done) {
+ promise.then(null, function onRejected(reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+
+ describe("3.2.3.2: it must not be called more than once.", function () {
+ specify("already-rejected", function (done) {
+ var timesCalled = 0;
+
+ rejected(dummy).then(null, function onRejected() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+ });
+
+ specify("trying to reject a pending promise more than once, immediately", function (done) {
+ var tuple = pending();
+ var timesCalled = 0;
+
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+
+ tuple.reject(dummy);
+ tuple.reject(dummy);
+ });
+
+ specify("trying to reject a pending promise more than once, delayed", function (done) {
+ var tuple = pending();
+ var timesCalled = 0;
+
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+
+ setTimeout(function () {
+ tuple.reject(dummy);
+ tuple.reject(dummy);
+ }, 50);
+ });
+
+ specify("trying to reject a pending promise more than once, immediately then delayed", function (done) {
+ var tuple = pending();
+ var timesCalled = 0;
+
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled, 1);
+ done();
+ });
+
+ tuple.reject(dummy);
+ setTimeout(function () {
+ tuple.reject(dummy);
+ }, 50);
+ });
+
+ specify("when multiple `then` calls are made, spaced apart in time", function (done) {
+ var tuple = pending();
+ var timesCalled = [0, 0, 0];
+
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled[0], 1);
+ });
+
+ setTimeout(function () {
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled[1], 1);
+ });
+ }, 50);
+
+ setTimeout(function () {
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled[2], 1);
+ done();
+ });
+ }, 100);
+
+ setTimeout(function () {
+ tuple.reject(dummy);
+ }, 150);
+ });
+
+ specify("when `then` is interleaved with rejection", function (done) {
+ var tuple = pending();
+ var timesCalled = [0, 0];
+
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled[0], 1);
+ });
+
+ tuple.reject(dummy);
+
+ tuple.promise.then(null, function onRejected() {
+ assert.strictEqual(++timesCalled[1], 1);
+ done();
+ });
+ });
+ });
+
+ describe("3.2.3.3: it must not be called if `onFulfilled` has been called.", function () {
+ testFulfilled(dummy, function (promise, done) {
+ var onFulfilledCalled = false;
+
+ promise.then(function onFulfilled() {
+ onFulfilledCalled = true;
+ }, function onRejected() {
+ assert.strictEqual(onFulfilledCalled, false);
+ done();
+ });
+
+ setTimeout(function(){done();}, 100);
+ });
+
+ specify("trying to fulfill then immediately reject", function (done) {
+ var tuple = pending();
+ var onFulfilledCalled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ onFulfilledCalled = true;
+ }, function onRejected() {
+ assert.strictEqual(onFulfilledCalled, false);
+ done();
+ });
+
+ tuple.fulfill(dummy);
+ tuple.reject(dummy);
+ setTimeout(function(){done();}, 100);
+ });
+
+ specify("trying to fulfill then reject, delayed", function (done) {
+ var tuple = pending();
+ var onFulfilledCalled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ onFulfilledCalled = true;
+ }, function onRejected() {
+ assert.strictEqual(onFulfilledCalled, false);
+ done();
+ });
+
+ setTimeout(function () {
+ tuple.fulfill(dummy);
+ tuple.reject(dummy);
+ }, 50);
+ setTimeout(function(){done();}, 100);
+ });
+
+ specify("trying to fulfill immediately then reject delayed", function (done) {
+ var tuple = pending();
+ var onFulfilledCalled = false;
+
+ tuple.promise.then(function onFulfilled() {
+ onFulfilledCalled = true;
+ }, function onRejected() {
+ assert.strictEqual(onFulfilledCalled, false);
+ done();
+ });
+
+ tuple.fulfill(dummy);
+ setTimeout(function () {
+ tuple.reject(dummy);
+ }, 50);
+ setTimeout(function(){done();}, 100);
+ });
+ });
+});
diff --git a/test/mocha/3.2.4.js b/test/mocha/3.2.4.js
new file mode 100644
index 0000000..5076d64
--- /dev/null
+++ b/test/mocha/3.2.4.js
@@ -0,0 +1,31 @@
+"use strict";
+
+var assert = require("assert");
+var testFulfilled = require("./helpers/testThreeCases").testFulfilled;
+var testRejected = require("./helpers/testThreeCases").testRejected;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+
+describe("3.2.4: `then` must return before `onFulfilled` or `onRejected` is called", function () {
+ testFulfilled(dummy, function (promise, done) {
+ var thenHasReturned = false;
+
+ promise.then(function onFulfilled() {
+ assert(thenHasReturned);
+ done();
+ });
+
+ thenHasReturned = true;
+ });
+
+ testRejected(dummy, function (promise, done) {
+ var thenHasReturned = false;
+
+ promise.then(null, function onRejected() {
+ assert(thenHasReturned);
+ done();
+ });
+
+ thenHasReturned = true;
+ });
+});
diff --git a/test/mocha/3.2.5.js b/test/mocha/3.2.5.js
new file mode 100644
index 0000000..ad83821
--- /dev/null
+++ b/test/mocha/3.2.5.js
@@ -0,0 +1,257 @@
+"use strict";
+
+var assert = require("assert");
+var sinon = require("sinon");
+var testFulfilled = require("./helpers/testThreeCases").testFulfilled;
+var testRejected = require("./helpers/testThreeCases").testRejected;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+var other = { other: "other" }; // a value we don't want to be strict equal to
+var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality
+var sentinel2 = { sentinel2: "sentinel2" };
+var sentinel3 = { sentinel3: "sentinel3" };
+
+function callbackAggregator(times, ultimateCallback) {
+ var soFar = 0;
+ return function () {
+ if (++soFar === times) {
+ ultimateCallback();
+ }
+ };
+}
+
+describe("3.2.5: `then` may be called multiple times on the same promise.", function () {
+ describe("3.2.5.1: If/when `promise` is fulfilled, respective `onFulfilled` callbacks must execute in the order " +
+ "of their originating calls to `then`.", function () {
+ describe("multiple boring fulfillment handlers", function () {
+ testFulfilled(sentinel, function (promise, done) {
+ var handler1 = sinon.stub().returns(other);
+ var handler2 = sinon.stub().returns(other);
+ var handler3 = sinon.stub().returns(other);
+
+ var spy = sinon.spy();
+ promise.then(handler1, spy);
+ promise.then(handler2, spy);
+ promise.then(handler3, spy);
+
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+
+ sinon.assert.calledWith(handler1, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler2, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler3, sinon.match.same(sentinel));
+ sinon.assert.notCalled(spy);
+
+ done();
+ });
+ });
+ });
+
+ describe("multiple fulfillment handlers, one of which throws", function () {
+ testFulfilled(sentinel, function (promise, done) {
+ var handler1 = sinon.stub().returns(other);
+ var handler2 = sinon.stub().throws(other);
+ var handler3 = sinon.stub().returns(other);
+
+ var spy = sinon.spy();
+ promise.then(handler1, spy);
+ promise.then(handler2, spy).caught(function(){});
+ promise.then(handler3, spy);
+
+ promise.then(function (value) {
+ assert.strictEqual(value, sentinel);
+
+ sinon.assert.calledWith(handler1, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler2, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler3, sinon.match.same(sentinel));
+ sinon.assert.notCalled(spy);
+
+ done();
+ });
+ });
+ });
+
+ describe("results in multiple branching chains with their own fulfillment values", function () {
+ testFulfilled(dummy, function (promise, done) {
+ var semiDone = callbackAggregator(3, done);
+
+ promise.then(function () {
+ return sentinel;
+ }).then(function (value) {
+ assert.strictEqual(value, sentinel);
+ semiDone();
+ });
+
+ promise.then(function () {
+ throw sentinel2;
+ }).then(null, function (reason) {
+ assert.strictEqual(reason, sentinel2);
+ semiDone();
+ });
+
+ promise.then(function () {
+ return sentinel3;
+ }).then(function (value) {
+ assert.strictEqual(value, sentinel3);
+ semiDone();
+ });
+ });
+ });
+
+ describe("`onFulfilled` handlers are called in the original order", function () {
+ testFulfilled(dummy, function (promise, done) {
+ var handler1 = sinon.spy(function handler1() {});
+ var handler2 = sinon.spy(function handler2() {});
+ var handler3 = sinon.spy(function handler3() {});
+
+ promise.then(handler1);
+ promise.then(handler2);
+ promise.then(handler3);
+
+ promise.then(function () {
+ sinon.assert.callOrder(handler1, handler2, handler3);
+ done();
+ });
+ });
+
+ describe("even when one handler is added inside another handler", function () {
+ testFulfilled(dummy, function (promise, done) {
+ var handler1 = sinon.spy(function handler1() {});
+ var handler2 = sinon.spy(function handler2() {});
+ var handler3 = sinon.spy(function handler3() {});
+
+ promise.then(function () {
+ handler1();
+ promise.then(handler3);
+ });
+ promise.then(handler2);
+
+ promise.then(function () {
+ // Give implementations a bit of extra time to flush their internal queue, if necessary.
+ setTimeout(function () {
+ sinon.assert.callOrder(handler1, handler2, handler3);
+ done();
+ }, 15);
+ });
+ });
+ });
+ });
+ });
+
+ describe("3.2.5.2: If/when `promise` is rejected, respective `onRejected` callbacks must execute in the order " +
+ "of their originating calls to `then`.", function () {
+ describe("multiple boring rejection handlers", function () {
+ testRejected(sentinel, function (promise, done) {
+ var handler1 = sinon.stub().returns(other);
+ var handler2 = sinon.stub().returns(other);
+ var handler3 = sinon.stub().returns(other);
+
+ var spy = sinon.spy();
+ promise.then(spy, handler1);
+ promise.then(spy, handler2);
+ promise.then(spy, handler3);
+
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+
+ sinon.assert.calledWith(handler1, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler2, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler3, sinon.match.same(sentinel));
+ sinon.assert.notCalled(spy);
+
+ done();
+ });
+ });
+ });
+
+ describe("multiple rejection handlers, one of which throws", function () {
+ testRejected(sentinel, function (promise, done) {
+ var handler1 = sinon.stub().returns(other);
+ var handler2 = sinon.stub().throws(other);
+ var handler3 = sinon.stub().returns(other);
+
+ var spy = sinon.spy();
+ promise.then(spy, handler1);
+ promise.then(spy, handler2).caught(function(){});
+ promise.then(spy, handler3);
+
+ promise.then(null, function (reason) {
+ assert.strictEqual(reason, sentinel);
+
+ sinon.assert.calledWith(handler1, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler2, sinon.match.same(sentinel));
+ sinon.assert.calledWith(handler3, sinon.match.same(sentinel));
+ sinon.assert.notCalled(spy);
+
+ done();
+ });
+ });
+ });
+
+ describe("results in multiple branching chains with their own fulfillment values", function () {
+ testRejected(sentinel, function (promise, done) {
+ var semiDone = callbackAggregator(3, done);
+
+ promise.then(null, function () {
+ return sentinel;
+ }).then(function (value) {
+ assert.strictEqual(value, sentinel);
+ semiDone();
+ });
+
+ promise.then(null, function () {
+ throw sentinel2;
+ }).then(null, function (reason) {
+ assert.strictEqual(reason, sentinel2);
+ semiDone();
+ });
+
+ promise.then(null, function () {
+ return sentinel3;
+ }).then(function (value) {
+ assert.strictEqual(value, sentinel3);
+ semiDone();
+ });
+ });
+ });
+
+ describe("`onRejected` handlers are called in the original order", function () {
+ testRejected(dummy, function (promise, done) {
+ var handler1 = sinon.spy(function handler1() {});
+ var handler2 = sinon.spy(function handler2() {});
+ var handler3 = sinon.spy(function handler3() {});
+
+ promise.then(null, handler1);
+ promise.then(null, handler2);
+ promise.then(null, handler3);
+
+ promise.then(null, function () {
+ sinon.assert.callOrder(handler1, handler2, handler3);
+ done();
+ });
+ });
+
+ describe("even when one handler is added inside another handler", function () {
+ testRejected(dummy, function (promise, done) {
+ var handler1 = sinon.spy(function handler1() {});
+ var handler2 = sinon.spy(function handler2() {});
+ var handler3 = sinon.spy(function handler3() {});
+
+ promise.then(null, function () {
+ handler1();
+ promise.then(null, handler3);
+ });
+ promise.then(null, handler2);
+
+ promise.then(null, function () {
+ // Give implementations a bit of extra time to flush their internal queue, if necessary.
+ setTimeout(function () {
+ sinon.assert.callOrder(handler1, handler2, handler3);
+ done();
+ }, 15);
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/test/mocha/3.2.6.js b/test/mocha/3.2.6.js
new file mode 100644
index 0000000..bf9b324
--- /dev/null
+++ b/test/mocha/3.2.6.js
@@ -0,0 +1,322 @@
+"use strict";
+
+var assert = require("assert");
+var testFulfilled = require("./helpers/testThreeCases").testFulfilled;
+var testRejected = require("./helpers/testThreeCases").testRejected;
+
+var adapter = global.adapter;
+var fulfilled = adapter.fulfilled;
+var rejected = adapter.rejected;
+var pending = adapter.pending;
+
+var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
+var sentinel = { sentinel: "sentinel" }; // a sentinel fulfillment value to test for with strict equality
+var other = { other: "other" }; // a value we don't want to be strict equal to
+
+describe("3.2.6: `then` must return a promise: `promise2 = promise1.then(onFulfilled, onRejected)`", function () {
+ specify("is a promise", function () {
+ var promise1 = pending().promise;
+ var promise2 = promise1.then();
+
+ assert(typeof promise2 === "object" || typeof promise2 === "function");
+ assert.notStrictEqual(promise2, null);
+ assert.strictEqual(typeof promise2.then, "function");
+ });
+
+ describe("3.2.6.1: If either `onFulfilled` or `onRejected` returns a value that is not a promise, `promise2` " +
+ "must be fulfilled with that value.", function () {
+ function testValue(expectedValue, stringRepresentation) {
+ describe("The value is " + stringRepresentation, function () {
+ testFulfilled(dummy, function (promise1, done) {
+ var promise2 = promise1.then(function onFulfilled() {
+ return expectedValue;
+ });
+
+ promise2.then(function onPromise2Fulfilled(actualValue) {
+ assert.strictEqual(actualValue, expectedValue);
+ done();
+ });
+ });
+ testRejected(dummy, function (promise1, done) {
+ var promise2 = promise1.then(null, function onRejected() {
+ return expectedValue;
+ });
+
+ promise2.then(function onPromise2Fulfilled(actualValue) {
+ assert.strictEqual(actualValue, expectedValue);
+ done();
+ });
+ });
+ });
+ }
+
+ testValue(undefined, "`undefined`");
+ testValue(null, "`null`");
+ testValue(false, "`false`");
+ testValue(0, "`0`");
+ testValue(new Error(), "an error");
+ testValue(new Date(), "a date");
+ testValue({}, "an object");
+ testValue({ then: 5 }, "an object with a non-function `then` property");
+ });
+
+ describe("3.2.6.2: If either `onFulfilled` or `onRejected` throws an exception, `promise2` " +
+ "must be rejected with the thrown exception as the reason.", function () {
+ function testReason(expectedReason, stringRepresentation) {
+ describe("The reason is " + stringRepresentation, function () {
+ testFulfilled(dummy, function (promise1, done) {
+ var promise2 = promise1.then(function onFulfilled() {
+ throw expectedReason;
+ });
+
+ promise2.then(null, function onPromise2Rejected(actualReason) {
+ assert.strictEqual(actualReason, expectedReason);
+ done();
+ });
+ });
+ testRejected(dummy, function (promise1, done) {
+ var promise2 = promise1.then(null, function onRejected() {
+ throw expectedReason;
+ });
+
+ promise2.then(null, function onPromise2Rejected(actualReason) {
+ assert.strictEqual(actualReason, expectedReason);
+ done();
+ });
+ });
+ });
+ }
+
+ testReason(undefined, "`undefined`");
+ testReason(null, "`null`");
+ testReason(false, "`false`");
+ testReason(0, "`0`");
+ testReason(new Error(), "an error");
+ testReason(new Date(), "a date");
+ testReason({}, "an object");
+ testReason({ then: function () { } }, "a promise-alike");
+ testReason(fulfilled(dummy), "a fulfilled promise");
+ var promise = rejected(dummy); promise.caught(function(){});
+ testReason(promise, "a rejected promise");
+ });
+
+ describe("3.2.6.3: If either `onFulfilled` or `onRejected` returns a promise (call it `returnedPromise`), " +
+ "`promise2` must assume the state of `returnedPromise`", function () {
+ describe("3.2.6.3.1: If `returnedPromise` is pending, `promise2` must remain pending until `returnedPromise` " +
+ "is fulfilled or rejected.", function () {
+ testFulfilled(dummy, function (promise1, done) {
+ var wasFulfilled = false;
+ var wasRejected = false;
+
+ var promise2 = promise1.then(function onFulfilled() {
+ var returnedPromise = pending().promise;
+ return returnedPromise;
+ });
+
+ promise2.then(
+ function onPromise2Fulfilled() {
+ wasFulfilled = true;
+ },
+ function onPromise2Rejected() {
+ wasRejected = true;
+ }
+ );
+
+ setTimeout(function () {
+ assert.strictEqual(wasFulfilled, false);
+ assert.strictEqual(wasRejected, false);
+ done();
+ }, 100);
+ });
+
+ testRejected(dummy, function (promise1, done) {
+ var wasFulfilled = false;
+ var wasRejected = false;
+
+ var promise2 = promise1.then(null, function onRejected() {
+ var returnedPromise = pending().promise;
+ return returnedPromise;
+ });
+
+ promise2.then(
+ function onPromise2Fulfilled() {
+ wasFulfilled = true;
+ },
+ function onPromise2Rejected() {
+ wasRejected = true;
+ }
+ );
+
+ setTimeout(function () {
+ assert.strictEqual(wasFulfilled, false);
+ assert.strictEqual(wasRejected, false);
+ done();
+ }, 100);
+ });
+ });
+
+ describe("3.2.6.3.2: If/when `returnedPromise` is fulfilled, `promise2` must be fulfilled with the same value.",
+ function () {
+ describe("`promise1` is fulfilled, and `returnedPromise` is:", function () {
+ testFulfilled(sentinel, function (returnedPromise, done) {
+ var promise1 = fulfilled(dummy);
+ var promise2 = promise1.then(function onFulfilled() {
+ return returnedPromise;
+ });
+
+ promise2.then(function onPromise2Fulfilled(value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+
+ specify("a pseudo-promise", function (done) {
+ var promise1 = fulfilled(dummy);
+ var promise2 = promise1.then(function onFulfilled() {
+ return {
+ then: function (f) { f(sentinel); }
+ };
+ });
+
+ promise2.then(function onPromise2Fulfilled(value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+ describe("`promise1` is rejected, and `returnedPromise` is:", function () {
+ testFulfilled(sentinel, function (returnedPromise, done) {
+ var promise1 = rejected(dummy);
+ var promise2 = promise1.then(null, function onRejected() {
+ return returnedPromise;
+ });
+
+ promise2.then(function onPromise2Fulfilled(value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+
+ specify("a pseudo-promise", function (done) {
+ var promise1 = rejected(dummy);
+ var promise2 = promise1.then(null, function onRejected() {
+ return {
+ then: function (f) { f(sentinel); }
+ };
+ });
+
+ promise2.then(function onPromise2Fulfilled(value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+ });
+
+ describe("3.2.6.3.3: If/when `returnedPromise` is rejected, `promise2` must be rejected with the same reason.",
+ function () {
+ describe("`promise1` is fulfilled, and `returnedPromise` is:", function () {
+ testRejected(sentinel, function (returnedPromise, done) {
+ var promise1 = fulfilled(dummy);
+ var promise2 = promise1.then(function onFulfilled() {
+ return returnedPromise;
+ });
+
+ promise2.then(null, function onPromise2Rejected(reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+
+ specify("a pseudo-promise", function (done) {
+ var promise1 = fulfilled(dummy);
+ var promise2 = promise1.then(function onFulfilled() {
+ return {
+ then: function (f, r) { r(sentinel); }
+ };
+ });
+
+ promise2.then(null, function onPromise2Rejected(reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+ describe("`promise1` is rejected, and `returnedPromise` is:", function () {
+ testRejected(sentinel, function (returnedPromise, done) {
+ var promise1 = rejected(dummy);
+ var promise2 = promise1.then(null, function onRejected() {
+ return returnedPromise;
+ });
+
+ promise2.then(null, function onPromise2Rejected(reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+
+ specify("a pseudo-promise", function (done) {
+ var promise1 = rejected(dummy);
+ var promise2 = promise1.then(null, function onRejected() {
+ return {
+ then: function (f, r) { r(sentinel); }
+ };
+ });
+
+ promise2.then(null, function onPromise2Rejected(reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+ });
+ });
+
+ describe("3.2.6.4: If `onFulfilled` is not a function and `promise1` is fulfilled, `promise2` must be fulfilled " +
+ "with the same value.", function () {
+
+ function testNonFunction(nonFunction, stringRepresentation) {
+ describe("`onFulfilled` is " + stringRepresentation, function () {
+ testFulfilled(sentinel, function (promise1, done) {
+ var promise2 = promise1.then(nonFunction);
+
+ promise2.then(function onPromise2Fulfilled(value) {
+ assert.strictEqual(value, sentinel);
+ done();
+ });
+ });
+ });
+ }
+
+ testNonFunction(undefined, "`undefined`");
+ testNonFunction(null, "`null`");
+ testNonFunction(false, "`false`");
+ testNonFunction(5, "`5`");
+ testNonFunction({}, "an object");
+ testNonFunction([function () { return other; }], "an array containing a function");
+ });
+
+ describe("3.2.6.5: If `onRejected` is not a function and `promise1` is rejected, `promise2` must be rejected " +
+ "with the same reason.", function () {
+
+ function testNonFunction(nonFunction, stringRepresentation) {
+ describe("`onRejected` is " + stringRepresentation, function () {
+ testRejected(sentinel, function (promise1, done) {
+ var promise2 = promise1.then(null, nonFunction);
+
+ promise2.then(null, function onPromise2Rejected(reason) {
+ assert.strictEqual(reason, sentinel);
+ done();
+ });
+ });
+ });
+ }
+
+ testNonFunction(undefined, "`undefined`");
+ testNonFunction(null, "`null`");
+ testNonFunction(false, "`false`");
+ testNonFunction(5, "`5`");
+ testNonFunction({}, "an object");
+ testNonFunction([function () { return other; }], "an array containing a function");
+ });
+});
diff --git a/test/mocha/any.js b/test/mocha/any.js
new file mode 100644
index 0000000..7509d80
--- /dev/null
+++ b/test/mocha/any.js
@@ -0,0 +1,112 @@
+"use strict";
+/*
+Based on When.js tests
+
+Open Source Initiative OSI - The MIT License
+
+http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2011 Brian Cavalier
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var sentinel = {};
+var other = {};
+var RangeError = Promise.RangeError;
+
+describe("Promise.any-test", function () {
+
+ specify("should reject on empty input array", function() {
+ var a = [];
+ return Promise.any(a)
+ .caught(RangeError, testUtils.returnToken)
+ .then(testUtils.assertToken);
+ });
+
+ specify("should resolve with an input value", function() {
+ var input = [1, 2, 3];
+ return Promise.any(input).then(
+ function(result) {
+ assert(testUtils.contains(input, result));
+ }, assert.fail
+ );
+ });
+
+ specify("should resolve with a promised input value", function() {
+ var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
+ return Promise.any(input).then(
+ function(result) {
+ assert(testUtils.contains([1, 2, 3], result));
+ }, assert.fail
+ );
+ });
+
+ specify("should reject with all rejected input values if all inputs are rejected", function() {
+ var input = [Promise.reject(1), Promise.reject(2), Promise.reject(3)];
+ var promise = Promise.any(input);
+
+ return promise.then(
+ assert.fail,
+ function(result) {
+ //Cannot use deep equality in IE8 because non-enumerable properties are not
+ //supported
+ assert(result[0] === 1);
+ assert(result[1] === 2);
+ assert(result[2] === 3);
+ }
+ );
+ });
+
+ specify("should accept a promise for an array", function() {
+ var expected, input;
+
+ expected = [1, 2, 3];
+ input = Promise.resolve(expected);
+
+ return Promise.any(input).then(
+ function(result) {
+ assert.notDeepEqual(expected.indexOf(result), -1);
+ }, assert.fail
+ );
+ });
+
+ specify("should allow zero handlers", function() {
+ var input = [1, 2, 3];
+ return Promise.any(input).then(
+ function(result) {
+ assert(testUtils.contains(input, result));
+ }, assert.fail
+ );
+ });
+
+ specify("should resolve to empty array when input promise does not resolve to array", function() {
+ return Promise.any(Promise.resolve(1))
+ .caught(TypeError, testUtils.returnToken)
+ .then(testUtils.assertToken);
+ });
+
+ specify("should reject when given immediately rejected promise", function() {
+ var err = new Error();
+ return Promise.any(Promise.reject(err)).then(assert.fail, function(e) {
+ assert.strictEqual(err, e);
+ });
+ });
+});
diff --git a/test/mocha/api_exceptions.js b/test/mocha/api_exceptions.js
new file mode 100644
index 0000000..aacab03
--- /dev/null
+++ b/test/mocha/api_exceptions.js
@@ -0,0 +1,231 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+
+function assertErrorHasLongTraces(e) {
+ assert(e.stack.indexOf("From previous event:") > -1);
+}
+
+function testCollection(name, a1, a2, a3) {
+
+ function getPromise(obj, val) {
+ return obj === void 0
+ ? Promise.resolve(val)[name](a1, a2, a3)
+ : Promise[name](val, a1, a2, a3);
+ }
+
+ function thenable(obj) {
+ var o = {
+ then: function(f) {
+ setTimeout(function(){
+ f(3);
+ }, 1);
+ }
+ }
+ specify("thenable for non-collection value", function() {
+ return getPromise(obj, o)
+ .then(assert.fail)
+ .caught(Promise.TypeError, testUtils.returnToken)
+ .then(testUtils.assertToken)
+ });
+ };
+
+ function immediate(obj) {
+ specify("immediate for non-collection value", function(){
+ return getPromise(obj, 3)
+ .then(assert.fail)
+ .caught(Promise.TypeError, testUtils.returnToken)
+ .then(testUtils.assertToken)
+ });
+ }
+
+ function promise(obj) {
+ var d = Promise.defer();
+ setTimeout(function(){
+ d.resolve(3);
+ }, 1);
+ specify("promise for non-collection value", function() {
+ return getPromise(obj, d.promise)
+ .then(assert.fail)
+ .caught(Promise.TypeError, testUtils.returnToken)
+ .then(testUtils.assertToken)
+ });
+ }
+
+ describe("When passing non-collection argument to Promise."+name + "() it should reject", function() {
+ immediate(Promise);
+ thenable(Promise);
+ promise(Promise);
+ });
+
+ describe("When calling ."+name + "() on a promise that resolves to a non-collection it should reject", function() {
+ immediate();
+ thenable();
+ promise();
+ });
+}
+
+if (Promise.hasLongStackTraces()) {
+
+
+ describe("runtime API misuse should result in rejections", function(){
+ specify("returning promises circularly", function() {
+ var d = Promise.defer();
+ var p = d.promise;
+
+ var c = p.then(function(){
+ return c;
+ });
+ d.fulfill(3);
+ return c.then(assert.fail, function(e){
+ assert(e instanceof Promise.TypeError);
+ });
+ });
+
+ specify("using illegal catchfilter", function() {
+
+ var d = Promise.defer();
+ var p = d.promise;
+ d.fulfill(3);
+ return p.caught(null, function(){
+
+ }).then(assert.fail, function(e){
+ assert(e instanceof Promise.TypeError);
+ });
+ });
+
+ specify("non-function to map", function() {
+
+ return Promise.map([], []).then(assert.fail, function(e){
+ assert(e instanceof Promise.TypeError);
+ });
+ });
+
+
+ specify("non-function to map inside then", function() {
+
+ return Promise.resolve().then(function(){
+ return Promise.map([], []);
+ }).then(assert.fail, function(e){
+ assert(e instanceof Promise.TypeError);
+ assertErrorHasLongTraces(e);
+ });
+ });
+
+
+ specify("non-function to reduce", function() {
+
+ return Promise.reduce([], []).then(assert.fail, function(e){
+ assert(e instanceof Promise.TypeError);
+ });
+ });
+
+
+ specify("non-function to reduce inside then", function() {
+
+ return Promise.resolve().then(function(){
+ return Promise.reduce([], []);
+ }).then(assert.fail, function(e){
+ assert(e instanceof Promise.TypeError);
+ assertErrorHasLongTraces(e);
+ });
+ });
+
+
+ specify("non-integer to some", function() {
+
+ return Promise.some([], "asd").then(assert.fail, function(e){
+ assert(e instanceof Promise.TypeError);
+ });
+ });
+
+
+ specify("non-integer to some inside then", function() {
+
+ return Promise.resolve().then(function(){
+ return Promise.some([], "asd")
+ }).then(assert.fail, function(e){
+ assert(e instanceof Promise.TypeError);
+ assertErrorHasLongTraces(e);
+ });
+ });
+
+ specify("non-array to all", function() {
+
+ Promise.all(3, 3).then(assert.fail, function(e){
+ assert(e instanceof Promise.TypeError);
+ });
+ });
+
+
+ specify("non-array to all inside then", function() {
+
+ return Promise.resolve().then(function(){
+ return Promise.all(3, 3);
+ }).then(assert.fail, function(e) {
+ assert(e instanceof Promise.TypeError);
+ assertErrorHasLongTraces(e);
+ });
+ });
+
+ });
+
+
+ describe("static API misuse should just throw right away", function(){
+
+ specify("non-function to promise constructor", function() {
+ try {
+ new Promise();
+ assert.fail();
+ }
+ catch (e) {
+ assert(e instanceof Promise.TypeError);
+ }
+ });
+
+ specify("non-function to coroutine", function() {
+ try {
+ Promise.coroutine();
+ assert.fail();
+ }
+ catch (e) {
+ assert(e instanceof Promise.TypeError);
+ }
+ });
+
+
+ specify("non-object to promisifyAll", function() {
+ try {
+ Promise.promisifyAll();
+ assert.fail();
+ }
+ catch (e) {
+ assert(e instanceof Promise.TypeError);
+ }
+ });
+
+
+ specify("non-function to promisify", function() {
+ try {
+ Promise.promisify();
+ assert.fail();
+ }
+ catch (e) {
+ assert(e instanceof Promise.TypeError);
+ }
+ });
+
+ });
+
+ testCollection("race");
+ testCollection("all");
+ testCollection("settle");
+ testCollection("any");
+ testCollection("some", 1);
+ testCollection("map", function(){});
+ testCollection("reduce", function(){});
+ testCollection("filter", function(){});
+ testCollection("props", function(){});
+}
diff --git a/test/mocha/async.js b/test/mocha/async.js
new file mode 100644
index 0000000..c487ddd
--- /dev/null
+++ b/test/mocha/async.js
@@ -0,0 +1,245 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+
+describe("Async requirement", function() {
+
+ var arr = [];
+
+ function a() {
+ arr.push(1);
+ }
+
+ function b() {
+ arr.push(2);
+ }
+
+ function c() {
+ arr.push(3);
+ }
+
+
+ function assertArr() {
+ assert.deepEqual(arr, [1,2,3]);
+ arr.length = 0;
+ }
+
+ beforeEach(function() {
+ arr = [];
+ });
+
+ specify("Basic", function() {
+ var p = new Promise(function(resolve) {
+ resolve();
+ });
+ a();
+ p.then(c);
+ b();
+ return p.then(assertArr);
+ });
+
+ specify("Resolve-Before-Then", function() {
+ var resolveP;
+ var p = new Promise(function(resolve) {
+ resolveP = resolve;
+ });
+
+ a();
+ resolveP();
+ p.then(c);
+ b();
+ return p.then(assertArr);
+ });
+
+ specify("Resolve-After-Then", function() {
+ var resolveP;
+ var p = new Promise(function(resolve) {
+ resolveP = resolve;
+ });
+
+ a();
+ p.then(c);
+ resolveP();
+ b();
+ return p.then(assertArr);
+ });
+
+ specify("Then-Inside-Then", function() {
+ var fulfilledP = Promise.resolve();
+ return fulfilledP.then(function() {
+ a();
+ var ret = fulfilledP.then(c).then(assertArr);
+ b();
+ return ret;
+ });
+ });
+
+ if (typeof Error.captureStackTrace === "function") {
+ describe("Should not grow the stack and cause eventually stack overflow.", function(){
+ var lim;
+ beforeEach(function() {
+ lim = Error.stackTraceLimit;
+ Error.stackTraceLimit = 10000;
+ });
+
+ afterEach(function() {
+ Error.stackTraceLimit = lim;
+ });
+
+ function assertStackIsNotGrowing(stack) {
+ assert(stack.split("\n").length > 5);
+ assert(stack.split("\n").length < 15);
+ }
+
+ specify("Already fulfilled.", function() {
+ function test(i){
+ if (i <= 0){
+ return Promise.resolve(new Error().stack);
+ } else {
+ return Promise.resolve(i-1).then(test)
+ }
+ }
+ return test(100).then(function(stack) {
+ assertStackIsNotGrowing(stack);
+ });
+ });
+
+ specify("Already rejected", function() {
+ function test(i){
+ if (i <= 0){
+ return Promise.reject(new Error().stack);
+ } else {
+ return Promise.reject(i-1).then(assert.fail, test)
+ }
+ }
+ return test(100).then(assert.fail, function(stack) {
+ assertStackIsNotGrowing(stack);
+ });
+ });
+
+ specify("Immediately fulfilled", function() {
+ function test(i){
+ var deferred = Promise.defer();
+ if (i <= 0){
+ deferred.fulfill(new Error().stack);
+ return deferred.promise;
+ } else {
+ deferred.fulfill(i-1);
+ return deferred.promise.then(test)
+ }
+ }
+ return test(100).then(function(stack) {
+ assertStackIsNotGrowing(stack);
+ });
+ });
+
+ specify("Immediately rejected", function() {
+ function test(i){
+ var deferred = Promise.defer();
+ if (i <= 0){
+ deferred.reject(new Error().stack);
+ return deferred.promise;
+ } else {
+ deferred.reject(i-1);
+ return deferred.promise.then(assert.fail, test)
+ }
+ }
+ return test(10).then(assert.fail, function(stack) {
+ assertStackIsNotGrowing(stack);
+ });
+ });
+ });
+ }
+
+ if (testUtils.isRecentNode) {
+ describe("Frees memory of old values in promise chains", function () {
+ function getHeapUsed() {
+ global.gc();
+ return process.memoryUsage().heapUsed;
+ }
+
+ var initialHeapUsed;
+
+ before(function () {
+ if (typeof global.gc !== "function") {
+ throw new Error("These tests require the --expose-gc flag");
+ }
+ initialHeapUsed = getHeapUsed();
+ });
+
+ specify(".then", function () {
+ return Promise.resolve()
+ .then(function () {
+ assert.ok(
+ getHeapUsed() < initialHeapUsed * 1.1,
+ "Promise.resolve uses minimal memory"
+ );
+ var rows = [];
+ for (var i = 0; i < 1e6; i++) {
+ rows.push(["Example " + i, i, i * 2]);
+ }
+ return rows;
+ })
+ .then(function (rows) {
+ assert.ok(
+ getHeapUsed() > initialHeapUsed * 12,
+ "large array uses a large amount of memory"
+ );
+ return { len: rows.length };
+ })
+ .then(function (x) {
+ // work around cancellation retaining previous result
+ return x;
+ })
+ .then(function (summaryResult) {
+ assert.ok(
+ getHeapUsed() < initialHeapUsed * 1.1,
+ "memory used by large array is freed"
+ );
+ assert.strictEqual(summaryResult.len, 1e6, "result");
+ });
+ });
+
+ specify(".catch", function () {
+ return Promise.reject(new Error("error 1"))
+ .catch(function () {
+ assert.ok(
+ getHeapUsed() < initialHeapUsed * 1.1,
+ "Promise.reject uses minimal memory"
+ );
+ var rows = [];
+ for (var i = 0; i < 1e6; i++) {
+ rows.push(["Example " + i, i, i * 2]);
+ }
+ var error = new Error("error 2");
+ error.result = rows;
+ throw error;
+ })
+ .catch(function (err) {
+ assert.ok(
+ getHeapUsed() > initialHeapUsed * 12,
+ "large array uses a large amount of memory"
+ );
+ var rows = err.result;
+ var error = new Error("error 3");
+ error.result = { len: rows.length };
+ throw error;
+ })
+ .catch(function (err) {
+ // work around cancellation retaining previous result
+ throw err;
+ })
+ .catch(function (err) {
+ assert.ok(
+ getHeapUsed() < initialHeapUsed * 1.1,
+ "memory used by large array is freed"
+ );
+ var summaryResult = err.result;
+ assert.strictEqual(summaryResult.len, 1e6, "result");
+ });
+ });
+ });
+ }
+});
diff --git a/test/mocha/async_hooks.js b/test/mocha/async_hooks.js
new file mode 100644
index 0000000..15d0eed
--- /dev/null
+++ b/test/mocha/async_hooks.js
@@ -0,0 +1,145 @@
+"use strict";
+
+var assert = require("assert");
+
+var getContextFn = Promise._getContext;
+Promise.config({ asyncHooks: true });
+var supportsAsync = Promise._getContext !== getContextFn;
+Promise.config({ asyncHooks: false });
+if (supportsAsync) {
+ runTests();
+}
+
+function runTests() {
+ var async_hooks = require('async_hooks');
+
+ var tree = new Set();
+ var hook = async_hooks.createHook({
+ init: function(asyncId, type, triggerId) {
+ if (tree.has(triggerId)) {
+ tree.add(asyncId);
+ }
+ }
+ });
+
+ var currentId = async_hooks.executionAsyncId;
+
+ function getAsyncPromise() {
+ return new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ setTimeout(resolve, 1);
+ }, 1);
+ });
+ }
+
+ describe("async_hooks", function() {
+ beforeEach(function() {
+ Promise.config({ asyncHooks: true });
+ })
+ afterEach(function() {
+ tree.clear();
+ hook.disable();
+ Promise.config({ asyncHooks: false });
+ });
+
+ it('should preserve async context when using fromNode', function() {
+ hook.enable()
+ tree.add(currentId());
+
+ return new Promise(function(resolve) {
+ var globalResolve;
+ setImmediate(function() {
+ hook.enable()
+ tree.add(currentId());
+ resolve(
+ new Promise(function(resolve) { globalResolve = resolve; })
+ .then(function() {
+ assert.ok(tree.has(currentId()));
+ })
+ );
+ })
+
+ setTimeout(function() {
+ globalResolve();
+ }, 10);
+ })
+ });
+
+ it('should preserve async context when using .map', function() {
+ hook.enable()
+ tree.add(currentId());
+ var d1 = getAsyncPromise();
+
+ return new Promise(function(resolve, reject) {
+ resolve(Promise.map([d1, null, Promise.resolve(1), Promise.delay(1)], function() {
+ return currentId();
+ }).then(function(asyncIds) {
+ for (var i = 0; i < asyncIds.length; ++i) {
+ assert.ok(tree.has(asyncIds[i]));
+ }
+ }));
+ });
+ });
+
+ it('should preserve async context when using .filter', function() {
+ hook.enable()
+ tree.add(currentId());
+ var d1 = getAsyncPromise();
+
+ return new Promise(function(resolve, reject) {
+ resolve(Promise.filter([d1, null, Promise.resolve(1), Promise.delay(1)], function() {
+ assert.ok(tree.has(currentId()));
+ }));
+ });
+ });
+
+ it('should preserve async context when using .reduce', function() {
+ hook.enable()
+ tree.add(currentId());
+ var d1 = getAsyncPromise();
+
+ return new Promise(function(resolve, reject) {
+ resolve(Promise.reduce([d1, null, Promise.resolve(1), Promise.delay(1)], function() {
+ assert.ok(tree.has(currentId()));
+ }));
+ });
+ });
+
+ it('should preserve async context when using .join', function() {
+ hook.enable()
+ tree.add(currentId());
+ var d1 = getAsyncPromise();
+
+ return new Promise(function(resolve, reject) {
+ resolve(Promise.join(d1, Promise.delay(1), function() {
+ assert.ok(tree.has(currentId()));
+ }));
+ });
+ });
+
+ it('should preserve async context when using .each', function() {
+ hook.enable()
+ tree.add(currentId());
+ var d1 = getAsyncPromise();
+
+ return new Promise(function(resolve, reject) {
+ resolve(Promise.each([d1, null, Promise.resolve(1), Promise.delay(1)], function() {
+ assert.ok(tree.has(currentId()));
+ }));
+ });
+ });
+
+ it('should be able to disable AsyncResource usage', function() {
+ Promise.config({ asyncHooks: false });
+ hook.enable()
+ tree.add(currentId());
+ var d1 = getAsyncPromise();
+
+ return new Promise(function(resolve, reject) {
+ resolve(d1.then(function() {
+ assert.ok(!tree.has(currentId()));
+ }));
+ });
+ });
+ });
+}
diff --git a/test/mocha/bind.js b/test/mocha/bind.js
new file mode 100644
index 0000000..cdb88e4
--- /dev/null
+++ b/test/mocha/bind.js
@@ -0,0 +1,1225 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var defaultThis = function() {return this}();
+
+function timedThenableOf(value) {
+ return {
+ then: function(onFulfilled) {
+ setTimeout(function() {
+ onFulfilled(value);
+ }, 1);
+ }
+ };
+}
+
+function timedPromiseOf(value) {
+ return Promise.delay(1, value);
+}
+
+function immediatePromiseOf(value) {
+ return Promise.resolve(value);
+}
+
+function immediateThenableOf(value) {
+ return {
+ then: function(onFulfilled) {
+ onFulfilled(value);
+ }
+ };
+}
+
+function timedRejectedThenableOf(value) {
+ return {
+ then: function(onFulfilled, onRejected) {
+ setTimeout(function() {
+ onRejected(value);
+ }, 1);
+ }
+ };
+}
+
+function timedRejectedPromiseOf(value) {
+ return Promise.delay(1).then(function() {
+ throw value;
+ });
+}
+
+function immediateRejectedPromiseOf(value) {
+ return Promise.reject(value);
+}
+
+function immediateRejectedThenableOf(value) {
+ return {
+ then: function(onFulfilled, onRejected) {
+ onRejected(value);
+ }
+ };
+}
+
+function toValue(valueOrPromise) {
+ if (valueOrPromise && typeof valueOrPromise.value === "function") {
+ return valueOrPromise.value();
+ }
+ return valueOrPromise
+}
+
+var THIS = {name: "this"};
+
+function CustomError1() {}
+CustomError1.prototype = Object.create(Error.prototype);
+function CustomError2() {}
+CustomError2.prototype = Object.create(Error.prototype);
+
+
+describe("when using .bind", function() {
+ describe("with finally", function() {
+ describe("this should refer to the bound object", function() {
+ specify("in straight-forward handler", function() {
+ return Promise.resolve().bind(THIS).lastly(function(){
+ assert(this === THIS);
+ });
+ });
+
+ specify("after promise returned from finally resolves", function() {
+ var d = Promise.defer();
+ var promise = d.promise;
+ var waited = false;
+
+ setTimeout(function(){
+ waited = true;
+ d.fulfill();
+ }, 1);
+
+ return Promise.resolve().bind(THIS).lastly(function(){
+ return promise;
+ }).lastly(function(){
+ assert(waited);
+ assert(this === THIS);
+ });
+ });
+ })
+
+ });
+
+ describe("with tap", function() {
+ describe("this should refer to the bound object", function() {
+ specify("in straight-forward handler", function() {
+ return Promise.resolve().bind(THIS).tap(function(){
+ assert(this === THIS);
+ });
+ });
+
+ specify("after promise returned from tap resolves", function() {
+ var d = Promise.defer();
+ var promise = d.promise;
+ var waited = false;
+ setTimeout(function(){
+ waited = true;
+ d.fulfill();
+ }, 1);
+
+ return Promise.resolve().bind(THIS).tap(function(){
+ return promise;
+ }).tap(function(){
+ assert(waited);
+ assert(this === THIS);
+ });
+ });
+ })
+
+ });
+
+ describe("with timeout", function() {
+ describe("this should refer to the bound object", function() {
+ specify("in straight-forward handler", function() {
+ return Promise.resolve(3).bind(THIS).timeout(500).then(function(v) {
+ assert(v === 3);
+ assert(this === THIS);
+ });
+ });
+ specify("in rejected handler", function() {
+ return Promise.reject(3).bind(THIS).timeout(500).then(assert.fail, function(v){
+ assert(v === 3);
+ assert(this === THIS);
+ });
+ });
+
+ specify("in rejected handler after timeout", function() {
+ return new Promise(function(){})
+ .bind(THIS).timeout(10).caught(Promise.TimeoutError, function(err){
+ assert(this === THIS);
+ });
+ });
+ })
+
+ });
+
+ describe("With catch filters", function() {
+ describe("this should refer to the bound object", function() {
+ specify("in an immediately trapped catch handler", function() {
+ return Promise.resolve().bind(THIS).then(function(){
+ assert(THIS === this);
+ var a;
+ a.b();
+ }).caught(Error, function(e){
+ assert(THIS === this);
+ });
+ });
+ specify("in a later trapped catch handler", function() {
+ return Promise.resolve().bind(THIS).then(function(){
+ throw new CustomError1();
+ }).caught(CustomError2, assert.fail)
+ .caught(CustomError1, function(e){
+ assert(THIS === this);
+ });
+ });
+ });
+ });
+
+ describe("With .get promises", function(){
+ specify("this should refer to the bound object", function() {
+ return Promise.resolve({key: "value"}).bind(THIS).get("key").then(function(val){
+ assert(val === "value");
+ assert(this === THIS);
+ });
+ });
+ });
+
+ describe("With .call promises", function(){
+ specify("this should refer to the bound object", function() {
+ return Promise.resolve({key: function(){return "value";}}).bind(THIS).call("key").then(function(val){
+ assert(val === "value");
+ assert(this === THIS);
+ });
+ });
+ });
+
+
+ describe("With .done promises", function(){
+
+ describe("this should refer to the bound object", function() {
+ specify("when rejected", function() {
+ return Promise.reject().bind(THIS).done(assert.fail, function(){
+ assert(this === THIS);
+ });
+ });
+ specify("when fulfilled", function() {
+ return Promise.resolve().bind(THIS).done(function(){
+ assert(this === THIS);
+ });
+ });
+ });
+ });
+
+ describe("With .spread promises", function(){
+
+ describe("this should refer to the bound object", function() {
+ specify("when spreading immediate array", function() {
+ return Promise.resolve([1,2,3]).bind(THIS).spread(function(a, b, c){
+ assert(c === 3);
+ assert(this === THIS);
+ });
+ });
+ specify("when spreading eventual array", function() {
+ var d = Promise.defer();
+ var promise = d.promise;
+
+ setTimeout(function(){
+ d.fulfill([1,2,3]);
+ }, 1);
+
+ return promise.bind(THIS).spread(function(a, b, c){
+ assert(c === 3);
+ assert(this === THIS);
+ });
+ });
+
+ specify("when spreading eventual array of eventual values", function() {
+ var d = Promise.defer();
+ var promise = d.promise;
+ setTimeout(function(){
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+ d.fulfill([p1, p2, p3]);
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 3);
+ }, 1);
+ return promise.bind(THIS).all().spread(function(a, b, c){
+ assert(c === 3);
+ assert(this === THIS);
+ });
+
+ });
+ });
+ });
+
+ describe("With nodeify", function() {
+ describe("this should refer to the bound object", function() {
+ specify("when the callback succeeeds", function() {
+ var spy = testUtils.getSpy();
+ Promise.resolve(3).bind(THIS).nodeify(spy(function(err, success){
+ assert(success === 3);
+ assert(this === THIS);
+ }));
+ return spy.promise;
+ });
+ specify("when the callback errs", function() {
+ var spy = testUtils.getSpy();
+ Promise.reject(3).bind(THIS).nodeify(spy(function(err, success){
+ assert(err === 3);
+ assert(this === THIS);
+ }));
+ return spy.promise;
+ });
+ });
+ });
+
+
+ describe("With map", function() {
+ describe("this should refer to the bound object", function() {
+ specify("inside the mapper with immediate values", function() {
+ return Promise.resolve([1,2,3]).bind(THIS).map(function(v, i){
+ if (i === 2) {
+ assert(this === THIS);
+ }
+ });
+ });
+ specify("inside the mapper with eventual values", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.resolve([p1, p2, p3]).bind(THIS).map(function(v, i){
+ if (i === 2) {
+ assert(this === THIS);
+ }
+ });
+ });
+
+ specify("after the mapper with immediate values", function() {
+ return Promise.resolve([1,2,3]).bind(THIS).map(function(){
+ return 1;
+ }).then(function(){
+ assert(this === THIS);
+ });
+ });
+
+ specify("after the mapper with eventual values", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.resolve([p1, p2, p3]).bind(THIS).map(function(){
+ return 1;
+ }).then(function(){
+ assert(this === THIS);
+ });
+
+
+ });
+
+ specify("after the mapper with immediate values when the map returns promises", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ }, 1);
+
+ return Promise.resolve([1,2,3]).bind(THIS).map(function(){
+ return p1;
+ }).then(function(){
+ assert(this === THIS);
+ });
+ });
+ });
+
+ describe("this should not refer to the bound object", function() {
+ specify("in the promises created within the handler", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ }, 1);
+
+ return Promise.resolve([1,2,3]).bind(THIS).map(function(){
+ return p1.then(function(){
+ assert(this !== THIS);
+ return 1;
+ })
+ }).then(function(){
+ assert(this === THIS);
+ });
+
+ });
+ });
+ });
+
+ describe("With reduce", function() {
+ describe("this should refer to the bound object", function() {
+ specify("inside the reducer with immediate values", function() {
+ return Promise.resolve([1,2,3]).bind(THIS).reduce(function(prev, v, i){
+ if (i === 2) {
+ assert(this === THIS);
+ }
+ });
+ });
+ specify("inside the reducer with eventual values", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.resolve([p1, p2, p3]).bind(THIS).reduce(function(prev, v, i){
+ if (i === 2) {
+ assert(this === THIS);
+ }
+ });
+ });
+
+ specify("after the reducer with immediate values", function() {
+ return Promise.resolve([1,2,3]).bind(THIS).reduce(function(){
+ return 1;
+ }).then(function(){
+ assert(this === THIS);
+ });
+ });
+
+ specify("after the reducer with eventual values", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+ return Promise.resolve([p1, p2, p3]).bind(THIS).reduce(function(){
+ return 1;
+ }).then(function(){
+ assert(this === THIS);
+ });
+
+ });
+
+ specify("after the reducer with immediate values when the reducer returns promise", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ }, 1);
+
+ return Promise.resolve([1,2,3]).bind(THIS).reduce(function(){
+ return p1;
+ }).then(function(){
+ assert(this === THIS);
+ });
+
+
+ });
+ });
+
+ describe("this should not refer to the bound object", function() {
+ specify("in the promises created within the handler", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ }, 1);
+
+ return Promise.resolve([1,2,3]).bind(THIS).reduce(function(){
+ return p1.then(function(){
+ assert(this !== THIS);
+ return 1;
+ })
+ }).then(function(){
+ assert(this === THIS);
+ });
+
+
+ });
+ });
+ });
+
+
+ describe("With filter", function() {
+ describe("this should refer to the bound object", function() {
+ specify("inside the filterer with immediate values", function() {
+ return Promise.resolve([1,2,3]).bind(THIS).filter(function(v, i){
+ if (i === 2) {
+ assert(this === THIS);
+ }
+ });
+ });
+ specify("inside the filterer with eventual values", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.resolve([p1, p2, p3]).bind(THIS).filter(function(v, i){
+ if (i === 2) {
+ assert(this === THIS);
+ }
+ });
+ });
+
+ specify("after the filterer with immediate values", function() {
+ return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
+ return 1;
+ }).then(function(){
+ assert(this === THIS);
+ });
+ });
+
+ specify("after the filterer with eventual values", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.resolve([p1, p2, p3]).bind(THIS).filter(function(){
+ return 1;
+ }).then(function(){
+ assert(this === THIS);
+ });
+ });
+
+ specify("after the filterer with immediate values when the filterer returns promises", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ }, 1);
+
+ return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
+ return p1;
+ }).then(function(){
+ assert(this === THIS);
+ });
+ });
+ });
+
+ describe("this should not refer to the bound object", function() {
+ specify("in the promises created within the handler", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ }, 1);
+
+ return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
+ return p1.then(function(){
+ assert(this !== THIS);
+ return 1;
+ })
+ }).then(function(){
+ assert(this === THIS);
+ });
+ });
+ });
+ });
+
+ describe("With all", function() {
+ describe("this should refer to the bound object", function() {
+ specify("after all with immediate values", function() {
+ return Promise.resolve([1,2,3]).bind(THIS).all().then(function(v){
+ assert(v.length === 3);
+ assert(this === THIS);
+ });
+ });
+ specify("after all with eventual values", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.resolve([p1, p2, p3]).bind(THIS).all().then(function(v){
+ assert(v.length === 3);
+ assert(this === THIS);
+ });
+ });
+ });
+
+ describe("this should not refer to the bound object", function() {
+ specify("in the promises created within the handler", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ }, 1);
+
+ return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
+ return Promise.all([p1]).then(function(){
+ assert(this !== THIS);
+ return 1;
+ })
+ }).then(function(){
+ assert(this === THIS);
+ });
+ });
+ });
+ });
+
+ describe("With any", function() {
+ describe("this should refer to the bound object", function() {
+ specify("after any with immediate values", function() {
+ Promise.resolve([1,2,3]).bind(THIS).any().then(function(v){
+ assert(v === 1);
+ assert(this === THIS);
+ });
+ });
+ specify("after any with eventual values", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.resolve([p1, p2, p3]).bind(THIS).any().then(function(v){
+ assert(v === 1);
+ assert(this === THIS);
+ });
+ });
+ });
+
+ describe("this should not refer to the bound object", function() {
+ specify("in the promises created within the handler", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ }, 1);
+
+ return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
+ return Promise.any([p1]).then(function(){
+ assert(this !== THIS);
+ return 1;
+ })
+ }).then(function(){
+ assert(this === THIS);
+ });
+
+ });
+ });
+ });
+
+
+ describe("With race", function() {
+ describe("this should refer to the bound object", function() {
+ specify("after race with immediate values", function() {
+ Promise.resolve([1,2,3]).bind(THIS).race().then(function(v){
+ assert(v === 1);
+ assert(this === THIS);
+ });
+ });
+ specify("after race with eventual values", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.resolve([p1, p2, p3]).bind(THIS).race().then(function(v){
+ assert(v === 1);
+ assert(this === THIS);
+ });
+ });
+ });
+
+ describe("this should not refer to the bound object", function() {
+ specify("in the promises created within the handler", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ }, 1);
+
+ return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
+ return Promise.race([p1]).then(function(){
+ assert(this !== THIS);
+ return 1;
+ })
+ }).then(function(){
+ assert(this === THIS);
+ });
+ });
+ });
+ });
+
+ describe("With delay", function() {
+ describe("this should refer to the bound object", function() {
+ specify("after race with immediate values", function() {
+ Promise.resolve([1,2,3]).bind(THIS).delay(1).then(function(v){
+ assert(v[0] === 1);
+ assert(this === THIS);
+ });
+ });
+ specify("after race with eventual values", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.resolve([p1, p2, p3]).bind(THIS).delay(1).all().then(function(v){
+ assert(v[0] === 1);
+ assert(this === THIS);
+ });
+ });
+ });
+
+ describe("this should not refer to the bound object", function() {
+ specify("in the promises created within the handler", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ }, 1);
+
+ return Promise.resolve([1,2,3]).delay(1).bind(THIS).delay(1).filter(function(){
+ assert(this === THIS);
+ return Promise.delay(1).then(function(){
+ assert(this !== THIS);
+ return 1;
+ })
+ }).then(function(){
+ assert(this === THIS);
+ });
+ });
+ });
+ });
+
+ describe("With settle", function() {
+ describe("this should refer to the bound object", function() {
+ specify("after settle with immediate values", function() {
+ return Promise.resolve([1,2,3]).bind(THIS).settle().then(function(v){
+ assert(v.length === 3);
+ assert(this === THIS);
+ });
+ });
+ specify("after settle with eventual values", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.resolve([p1, p2, p3]).bind(THIS).settle().then(function(v){
+ assert(v.length === 3);
+ assert(this === THIS);
+ });
+ });
+ });
+
+ describe("this should not refer to the bound object", function() {
+ specify("in the promises created within the handler", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ }, 1);
+
+ return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
+ return Promise.settle([p1]).then(function(){
+ assert(this !== THIS);
+ return 1;
+ })
+ }).then(function(){
+ assert(this === THIS);
+ });
+ });
+ });
+ });
+
+ describe("With some", function() {
+ describe("this should refer to the bound object", function() {
+ specify("after some with immediate values", function() {
+ return Promise.resolve([1,2,3]).bind(THIS).some(2).then(function(v){
+ assert.deepEqual(v, [1,2]);
+ assert(this === THIS);
+ });
+ });
+ specify("after some with eventual values", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.resolve([p1, p2, p3]).bind(THIS).some(2).then(function(v){
+ assert.deepEqual(v, [1,2]);
+ assert(this === THIS);
+ });
+ });
+
+ specify("after some with eventual array for eventual values", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ var dArray = Promise.defer();
+ var arrayPromise = dArray.promise;
+
+ setTimeout(function(){
+ dArray.fulfill([p1, p2, p3]);
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+ }, 1);
+
+ return arrayPromise.bind(THIS).some(2).then(function(v){
+ assert.deepEqual(v, [1,2]);
+ assert(this === THIS);
+ });
+ });
+ });
+
+ describe("this should not refer to the bound object", function() {
+ specify("in the promises created within the handler", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ }, 1);
+
+ return Promise.resolve([1,2,3]).bind(THIS).filter(function(){
+ return Promise.some([p1], 1).then(function(){
+ assert(this !== THIS);
+ return 1;
+ })
+ }).then(function(){
+ assert(this === THIS);
+ });
+ });
+ });
+ });
+
+
+
+ describe("With props", function() {
+ describe("this should refer to the bound object", function() {
+ specify("after props with immediate values", function() {
+ return Promise.resolve([1,2,3]).bind(THIS).props().then(function(v){
+ assert(v[2] === 3);
+ assert(this === THIS);
+ });
+ });
+ specify("after props with eventual values", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.resolve([p1, p2, p3]).bind(THIS).props().then(function(v){
+ assert(v[2] === 3);
+ assert(this === THIS);
+ });
+ });
+ });
+
+ describe("this should not refer to the bound object", function() {
+ specify("in the promises created within the handler", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+ setTimeout(function(){
+ d1.fulfill(1);
+ }, 1);
+ return Promise.resolve([1,2,3]).bind(THIS).props(function(){
+ return Promise.settle([p1]).then(function(){
+ assert(this !== THIS);
+ return 1;
+ })
+ }).then(function(){
+ assert(this === THIS);
+ });
+
+
+ });
+ });
+ });
+
+});
+
+describe("When using .bind to gratuitously rebind", function() {
+ var a = {value: 1};
+ var b = {value: 2};
+ var c = {value: 3};
+
+ function makeTest(a, b, c) {
+ return function() {
+ return Promise.bind(a).then(function(){
+ assert(this.value === 1);
+ }).bind(b).then(function(){
+ assert(this.value === 2);
+ }).bind(c).then(function(){
+ assert(this.value === 3);
+ });
+ }
+ }
+
+ specify("should not get confused immediately", makeTest(a, b, c));
+ specify("should not get confused immediate thenable",
+ makeTest(immediateThenableOf(a), immediateThenableOf(b), immediateThenableOf(c)));
+ specify("should not get confused immediate promise",
+ makeTest(immediatePromiseOf(a), immediatePromiseOf(b), immediatePromiseOf(c)));
+ specify("should not get confused timed thenable",
+ makeTest(timedThenableOf(a), timedThenableOf(b), timedThenableOf(c)));
+ specify("should not get confused timed promise",
+ makeTest(timedPromiseOf(a), timedPromiseOf(b), timedPromiseOf(c)));
+});
+
+describe("Promised thisArg", function() {
+
+ var e = {value: 1};
+
+ specify("basic case, this first", function(done) {
+ var thisPromise = Promise.delay(1, 1);
+ var promise = thisPromise.delay(1).thenReturn(2);
+ promise.bind(thisPromise).then(function(val) {
+ assert(+this === 1);
+ assert(+val === 2);
+ done();
+ });
+ });
+
+ specify("bound value is not changed by returned promise", function() {
+ return Promise.resolve().then(function() {
+ return new Promise(function(resolve) {
+ resolve();
+ }).bind(THIS).then(function() {});
+ }).then(function() {
+ assert.strictEqual(this, defaultThis);
+ });
+ });
+
+ specify("basic case, main promise first", function() {
+ var promise = Promise.delay(1, 2);
+ var thisPromise = promise.thenReturn(1);
+ return promise.bind(thisPromise).then(function(val) {
+ assert.strictEqual(+this, 1);
+ assert.strictEqual(+val, 2);
+ });
+ });
+
+ specify("both reject, this rejects first", function(done) {
+ var e1 = new Error();
+ var e2 = new Error();
+ var thisPromise = Promise.delay(1, 0).thenThrow(e1);
+ var promise = Promise.delay(2, 56).thenThrow(e2);
+ promise.bind(thisPromise).then(null, function(reason) {
+ assert(this === defaultThis);
+ assert(reason === e1);
+ done();
+ });
+ });
+
+ specify("both reject, main promise rejects first", function(done) {
+ var e1 = new Error("first");
+ var e2 = new Error("second");
+ var thisPromise = Promise.delay(56, 1).thenThrow(e1);
+ var promise = Promise.delay(2, 0).thenThrow(e2);
+ promise.bind(thisPromise).then(null, function(reason) {
+ assert(this === defaultThis);
+ assert(reason === e2);
+ done();
+ });
+ });
+
+ specify("Immediate value waits for deferred this", function() {
+ var t = Promise.delay(1, THIS);
+ var t2 = {};
+ return Promise.resolve(t2).bind(t).then(function(value) {
+ assert.strictEqual(this, THIS);
+ assert.strictEqual(t2, value);
+ });
+ });
+
+
+ specify("Immediate error waits for deferred this", function() {
+ var t = Promise.delay(1, THIS);
+ var err = new Error();
+ return Promise.reject(err).bind(t).then(assert.fail, function(e) {
+ assert.strictEqual(this, THIS);
+ assert.strictEqual(err, e);
+ });
+ });
+
+ function makeThisArgRejectedTest(reason) {
+ return function() {
+ return Promise.bind(reason()).then(assert.fail, function(e) {
+ assert(this === defaultThis);
+ assert(e.value === 1);
+ })
+ };
+ }
+
+ specify("if thisArg is rejected timed promise, returned promise is rejected",
+ makeThisArgRejectedTest(function() { return timedRejectedPromiseOf(e); }));
+ specify("if thisArg is rejected immediate promise, returned promise is rejected",
+ makeThisArgRejectedTest(function() { return immediateRejectedPromiseOf(e); }));
+ specify("if thisArg is rejected timed thenable, returned promise is rejected",
+ makeThisArgRejectedTest(function() { return timedRejectedThenableOf(e); }));
+ specify("if thisArg is rejected immediate thenable, returned promise is rejected",
+ makeThisArgRejectedTest(function() { return immediateRejectedThenableOf(e); }));
+
+ function makeThisArgRejectedTestMethod(reason) {
+ return function() {
+
+ return Promise.resolve().bind(reason()).then(assert.fail, function(e) {
+ assert(this === defaultThis);
+ assert(e.value === 1);
+ })
+ };
+ }
+
+ specify("if thisArg is rejected timed promise, returned promise is rejected",
+ makeThisArgRejectedTestMethod(function() { return timedRejectedPromiseOf(e); }));
+ specify("if thisArg is rejected immediate promise, returned promise is rejected",
+ makeThisArgRejectedTestMethod(function() { return immediateRejectedPromiseOf(e); }));
+ specify("if thisArg is rejected timed thenable, returned promise is rejected",
+ makeThisArgRejectedTestMethod(function() { return timedRejectedThenableOf(e); }));
+ specify("if thisArg is rejected immediate thenable, returned promise is rejected",
+ makeThisArgRejectedTestMethod(function() { return immediateRejectedThenableOf(e); }));
+});
+
+describe("github issue", function() {
+ specify("gh-426", function() {
+ return Promise.all([Promise.delay(10)]).bind(THIS).spread(function() {
+ assert.equal(this, THIS);
+ });
+ });
+
+ specify("gh-702-1", function() {
+ return Promise.bind(Promise.delay(1, THIS)).then(function() {
+ assert.equal(this, THIS);
+ }).then(function() {
+ assert.equal(this, THIS);
+ });
+ });
+
+ specify("gh-702-2", function() {
+ return Promise.resolve().bind(Promise.delay(1, THIS)).then(function() {
+ assert.equal(this, THIS);
+ }).then(function() {
+ assert.equal(this, THIS);
+ });
+ });
+});
+
+
+describe("promised bind", function() {
+ specify("works after following", function() {
+ return Promise.bind(Promise.delay(1, THIS)).then(function() {
+ assert.equal(this, THIS);
+ return Promise.delay(1);
+ }).then(function() {
+ assert.equal(this, THIS);
+ return Promise.delay(1);
+ }).then(function() {
+ assert.equal(this, THIS);
+ });
+ });
+
+ specify("works with spread", function() {
+ return Promise.bind(Promise.delay(1, THIS), [1,2,3]).spread(function() {
+ assert.equal(this, THIS);
+ assert.deepEqual([1,2,3], [].slice.call(arguments));
+ return Promise.delay(1, [].slice.call(arguments));
+ }).spread(function() {
+ assert.deepEqual([1,2,3], [].slice.call(arguments));
+ assert.equal(this, THIS);
+ return Promise.delay(1, [].slice.call(arguments));
+ }).spread(function() {
+ assert.deepEqual([1,2,3], [].slice.call(arguments));
+ assert.equal(this, THIS);
+ });
+ });
+
+ specify("works with immediate finally", function() {
+ return Promise.bind(Promise.delay(1, THIS), [1,2,3]).lastly(function() {
+ assert.equal(this, THIS);
+ }).then(function() {
+ assert.equal(this, THIS);
+ });
+ });
+
+ specify("works with delayed finally", function() {
+ return Promise.bind(Promise.delay(1, THIS), [1,2,3]).lastly(function() {
+ assert.equal(this, THIS);
+ return Promise.delay(1);
+ }).then(function() {
+ assert.equal(this, THIS);
+ });
+ });
+
+ specify("works with immediate tap", function() {
+ return Promise.bind(Promise.delay(1, THIS), [1,2,3]).tap(function() {
+ assert.equal(this, THIS);
+ }).then(function() {
+ assert.equal(this, THIS);
+ });
+ });
+
+ specify("works with delayed tap", function() {
+ return Promise.bind(Promise.delay(1, THIS), [1,2,3]).tap(function() {
+ assert.equal(this, THIS);
+ return Promise.delay(1);
+ }).then(function() {
+ assert.equal(this, THIS);
+ });
+ });
+});
+
+
diff --git a/test/mocha/bluebird-multiple-instances.js b/test/mocha/bluebird-multiple-instances.js
new file mode 100644
index 0000000..50337bf
--- /dev/null
+++ b/test/mocha/bluebird-multiple-instances.js
@@ -0,0 +1,107 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var isNodeJS = testUtils.isNodeJS;
+var OldPromise = require("./helpers/bluebird0_7_0.js");
+
+if (isNodeJS) {
+ var Promise1 = testUtils.addDeferred(require("../../js/debug/promise.js")());
+ var Promise2 = testUtils.addDeferred(require("../../js/debug/promise.js")());
+
+ var err1 = new Error();
+ var err2 = new Error();
+
+ describe("Separate instances of bluebird", function() {
+
+ specify("Should have identical Error types", function() {
+ assert(Promise1.CancellationError === Promise2.CancellationError);
+ assert(Promise1.RejectionError === Promise2.RejectionError);
+ assert(Promise1.TimeoutError === Promise2.TimeoutError);
+ });
+
+ specify("Should not be identical", function() {
+ assert(Promise1.onPossiblyUnhandledRejection !==
+ Promise2.onPossiblyUnhandledRejection);
+ assert(Promise1 !== Promise2);
+ });
+
+ specify("Should have different unhandled rejection handlers", function() {
+ var spy1 = testUtils.getSpy();
+ var spy2 = testUtils.getSpy();
+
+ Promise1.onPossiblyUnhandledRejection(spy1(function(e, promise) {
+ assert(promise instanceof Promise1);
+ assert(!(promise instanceof Promise2));
+ assert(e === err1);
+ }));
+
+ Promise2.onPossiblyUnhandledRejection(spy2(function(e, promise) {
+ assert(promise instanceof Promise2);
+ assert(!(promise instanceof Promise1));
+ assert(e === err2);
+ }));
+ assert(Promise1.onPossiblyUnhandledRejection !==
+ Promise2.onPossiblyUnhandledRejection);
+
+ var d1 = Promise1.defer();
+ var d2 = Promise2.defer();
+
+ d1.promise.then(function(){
+ throw err1;
+ });
+
+ d2.promise.then(function(){
+ throw err2;
+ });
+
+ setTimeout(function(){
+ d1.fulfill();
+ d2.fulfill();
+ setTimeout(function() {
+ Promise1._unhandledRejectionCheck();
+ Promise2._unhandledRejectionCheck();
+ }, 100);
+ }, 1);
+ return Promise.all([spy1.promise, spy2.promise]);
+ });
+
+ specify("Should use fast cast", function() {
+ var a = Promise1.defer();
+ var b = Promise2.cast(a.promise);
+ assert(a.promise._receiver0 === b);
+ });
+
+ specify("Should use fast cast with very old version", function() {
+ var a = OldPromise.pending();
+ var b = Promise1.cast(a.promise);
+ assert(a.promise._receiver0 === b);
+ });
+
+ specify("Should return 2 from very old promise", function() {
+ return Promise1.resolve().then(
+ function(){ return OldPromise.cast(0).then(function(){return 2});
+ }).then(function(two){
+ assert.equal(two, 2);
+ });
+ });
+
+ specify("Should reject primitive from fast cast", function() {
+ var a = OldPromise.pending();
+ var b = Promise.resolve(a.promise);
+ a.reject(1);
+ return b.then(assert.fail, function(e) {
+ assert.strictEqual(e, 1);
+ });
+ });
+ specify("Should reject object from fast cast", function() {
+ var err = new Error();
+ var a = OldPromise.pending();
+ var b = Promise.resolve(a.promise);
+ a.reject(err);
+ return b.then(assert.fail, function(e) {
+ assert.strictEqual(e, err);
+ });
+ });
+ });
+
+}
diff --git a/test/mocha/call.js b/test/mocha/call.js
new file mode 100644
index 0000000..1186247
--- /dev/null
+++ b/test/mocha/call.js
@@ -0,0 +1,57 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+
+var c = {
+ val: 3,
+ method: function() {
+ return [].slice.call(arguments).concat(this.val);
+ }
+};
+
+describe("call", function() {
+ specify("0 args", function() {
+ return Promise.resolve(c).call("method").then(function(res) {
+ assert.deepEqual([3], res);
+ });
+ });
+ specify("1 args", function() {
+ return Promise.resolve(c).call("method", 1).then(function(res) {
+ assert.deepEqual([1, 3], res);
+ });
+ });
+ specify("2 args", function() {
+ return Promise.resolve(c).call("method", 1, 2).then(function(res) {
+ assert.deepEqual([1, 2, 3], res);
+ });
+ });
+ specify("3 args", function() {
+ return Promise.resolve(c).call("method", 1, 2, 3).then(function(res) {
+ assert.deepEqual([1, 2, 3, 3], res);
+ });
+ });
+ specify("10 args", function() {
+ return Promise.resolve(c).call("method", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10).then(function(res) {
+ assert.deepEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3], res);
+ });
+ });
+ specify("method not found", function() {
+ var promises = [
+ Promise.resolve([]).call("abc").then(assert.fail, testUtils.noop),
+ Promise.resolve([]).call("abc", 1, 2, 3, 4, 5, 6, 7).then(assert.fail, testUtils.noop),
+ Promise.resolve([]).call("abc ").then(assert.fail, testUtils.noop),
+ Promise.resolve(null).call("abc", 1, 2, 3, 4, 5, 6, 7).then(assert.fail, testUtils.noop),
+ Promise.resolve(null).call("abc").then(assert.fail, testUtils.noop),
+ Promise.resolve(null).call("abc ").then(assert.fail, testUtils.noop)
+ ];
+
+ return Promise.all(promises).then(function(errors) {
+ for (var i = 0; i < errors.length; ++i) {
+ var message = errors[i].message || errors[i].toString();
+ assert(message.indexOf("has no method") >= 0);
+ }
+ });
+ });
+});
diff --git a/test/mocha/cancel.js b/test/mocha/cancel.js
new file mode 100644
index 0000000..03d172e
--- /dev/null
+++ b/test/mocha/cancel.js
@@ -0,0 +1,3184 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var awaitLateQueue = testUtils.awaitLateQueue;
+
+describe("Cancellation", function() {
+ specify("requires a function", function() {
+ return new Promise(function(_, __, onCancel) {
+ onCancel();
+ }).then(assert.fail, function(e) {
+ assert(e instanceof Promise.TypeError);
+ });
+ });
+
+ specify("can register multiple on same promise", function() {
+ var cancelled = 0;
+ var p = new Promise(function(_, __, onCancel) {
+ onCancel(function() {cancelled++});
+ onCancel(function() {cancelled++});
+ onCancel(function() {cancelled++});
+ onCancel(function() {cancelled++});
+ });
+
+ p.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(4, cancelled);
+ });
+ });
+
+ specify("follower promises' handlers are not called, registered before", function() {
+ var cancelled = 0;
+ var p = new Promise(function(_, __, onCancel) {
+ onCancel(function() {cancelled++});
+ });
+
+ new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {cancelled++});
+ });
+ new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {cancelled++});
+ });
+ new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {cancelled++});
+ });
+ p.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(1, cancelled);
+ });
+ });
+
+ specify("follower promises' handlers are not called, registered after", function() {
+ var cancelled = 0;
+ var p = new Promise(function(_, __, onCancel) {
+ onCancel(function() {cancelled++});
+ });
+
+ p.cancel();
+ new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {cancelled++});
+ }).suppressUnhandledRejections();
+ new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {cancelled++});
+ }).suppressUnhandledRejections();
+ new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {cancelled++});
+ }).suppressUnhandledRejections();
+ return awaitLateQueue(function() {
+ assert.equal(1, cancelled);
+ });
+ });
+
+ specify("downstream follower promises' handlers are not called, registered before", function() {
+ var cancelled = 0;
+ var p = new Promise(function(_, __, onCancel) {
+ onCancel(function() {cancelled++});
+ });
+
+ new Promise(function(resolve, _, onCancel) {
+ resolve(p.then());
+ onCancel(function() {cancelled++});
+ });
+ new Promise(function(resolve, _, onCancel) {
+ resolve(p.then());
+ onCancel(function() {cancelled++});
+ });
+ new Promise(function(resolve, _, onCancel) {
+ resolve(p.then());
+ onCancel(function() {cancelled++});
+ });
+
+ p.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(1, cancelled);
+ });
+ });
+
+ specify("downstream follower promises' handlers are called, registered after", function() {
+ var cancelled = 0;
+ var p = new Promise(function(_, __, onCancel) {
+ onCancel(function() {cancelled++});
+ });
+
+ p.cancel();
+ new Promise(function(resolve, _, onCancel) {
+ resolve(p.then());
+ onCancel(function() {cancelled++});
+ }).suppressUnhandledRejections();
+ new Promise(function(resolve, _, onCancel) {
+ resolve(p.then());
+ onCancel(function() {cancelled++});
+ }).suppressUnhandledRejections();
+ new Promise(function(resolve, _, onCancel) {
+ resolve(p.then());
+ onCancel(function() {cancelled++});
+ }).suppressUnhandledRejections();
+ return awaitLateQueue(function() {
+ assert.equal(1, cancelled);
+ });
+ });
+
+ specify("immediately rejected promise immediately cancelled with then in-between", function() {
+ var error = new Error();
+ var resolve;
+ var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+ var p = Promise.reject(error).then().lastly(resolve);
+ p.cancel();
+ p.caught(function() {});
+ return result;
+ });
+
+ specify("callback is called asynchronously but fate is sealed synchronously", function() {
+ var called = false;
+ var promiseResolve;
+ var promise = new Promise(function(resolve, reject, onCancel) {
+ promiseResolve = resolve;
+ onCancel(function() {
+ called = true;
+ });
+ });
+ return awaitLateQueue(function() {
+ promise.cancel();
+ promiseResolve();
+ return Promise.resolve().then(function() {
+ assert(called);
+ assert(!promise.isFulfilled());
+ });
+ });
+ });
+
+ if (testUtils.isNodeJS) {
+ specify("throws in process if callback throws", function() {
+ var e = new Error();
+ var promise = new Promise(function(resolve, reject, onCancel) {
+ onCancel(function onCancel() {
+ throw e;
+ });
+ });
+ promise.cancel();
+ return testUtils.awaitGlobalException(function(err) {
+ assert.equal(e, err);
+ });
+ });
+ }
+
+ specify("cancels the promise chain", function() {
+ var called = false;
+ var thens = 0;
+ var resolveChain;
+ var root = new Promise(function(resolve, reject, onCancel) {
+ resolveChain = resolve;
+ onCancel(function() {
+ called = true;
+ });
+ }).then(function() {
+ thens++;
+ }).then(function() {
+ thens++;
+ }).then(function() {
+ thens++;
+ });
+
+ root.cancel();
+ resolveChain();
+ return awaitLateQueue(function() {
+ assert.equal(0, thens);
+ assert(called);
+ });
+ });
+
+ specify("calls finally handlers", function() {
+ var called = false;
+ var thens = 0;
+ var resolveChain;
+ var root = new Promise(function(resolve, reject, onCancel) {
+ resolveChain = resolve;
+ onCancel(function() {
+ called = true;
+ });
+ });
+ var chain = root.lastly(function() {
+ thens++;
+ }).lastly(function() {
+ thens++;
+ }).lastly(function() {
+ thens++;
+ });
+
+ chain.cancel();
+ resolveChain();
+ return awaitLateQueue(function() {
+ assert.equal(3, thens);
+ assert(called);
+ });
+ });
+
+ specify("cancels the followee", function() {
+ var called = false;
+ var finalled = false;
+ var promise = new Promise(function(_, __, onCancel) {
+ onCancel(function() {
+ called = true;
+ });
+ });
+ var promise2 = new Promise(function(resolve, reject, onCancel) {
+ resolve(promise);
+ });
+ var promise3 = new Promise(function(resolve, reject, onCancel) {
+ resolve(promise2);
+ }).lastly(function() {
+ finalled = true;
+ });
+
+ promise3.cancel();
+ return awaitLateQueue(function() {
+ assert(called);
+ assert(finalled);
+ });
+ });
+
+ specify("cancels the followee, calling all callbacks and finally handlers", function() {
+ var called = 0;
+ var finalled = 0;
+
+ var promise = new Promise(function(_, __, onCancel) {
+ onCancel(function() {
+ called++;
+ });
+ }).lastly(function() {
+ finalled++;
+ });
+
+ var promise2 = new Promise(function(resolve, reject, onCancel) {
+ resolve(promise);
+ onCancel(function() {
+ called++;
+ });
+ }).lastly(function() {
+ finalled++;
+ });
+
+ var promise3 = new Promise(function(resolve, reject, onCancel) {
+ resolve(promise2);
+ onCancel(function() {
+ called++;
+ });
+ }).lastly(function() {
+ finalled++;
+ });
+
+ promise3.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(3, called);
+ assert.equal(3, finalled);
+ });
+ });
+
+ specify("cancels the followee, calling all onCancel callbacks", function() {
+ var called = 0;
+
+ var promise = new Promise(function(_, __, onCancel) {
+ onCancel(function() {
+ called++;
+ });
+ })
+
+ var promise2 = new Promise(function(resolve, reject, onCancel) {
+ resolve(promise);
+ onCancel(function() {
+ called++;
+ });
+ });
+
+ var promise3 = new Promise(function(resolve, reject, onCancel) {
+ resolve(promise2);
+ onCancel(function() {
+ called++;
+ });
+ });
+
+ promise3.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(3, called);
+ });
+ });
+
+ specify("can be used for breaking chains early", function() {
+ var called = false;
+ var p = Promise.resolve(1)
+ .then(function(data) {
+ p["break"]();
+ })
+ .then(function() {
+ called = true;
+ });
+ return awaitLateQueue(function() {
+ assert(!called);
+ });
+ });
+
+ specify("multiple cancel calls have no effect", function() {
+ var called = 0;
+ var finalled = 0;
+ var req1 = new Promise(function(resolve, _, onCancel) {
+ resolve();
+ onCancel(function() {
+ called++;
+ });
+ });
+
+ var ret = req1.then(function() {
+ return new Promise(function(_, __, onCancel) {
+ onCancel(function() {
+ called++;
+ });
+ });
+ }).then(function() {
+ return new Promise(function(_, __, onCancel) {
+ onCancel(function() {
+ called++;
+ });
+ });
+ }).lastly(function() {
+ finalled++;
+ });
+
+ req1.then(function() {
+ ret.cancel();
+ ret.cancel();
+ ret.cancel();
+ });
+
+ return awaitLateQueue(function() {
+ assert.equal(1, called);
+ assert.equal(1, finalled);
+ });
+ });
+
+ specify("throwing in finally turns into a rejection", function() {
+ var e = new Error("");
+ var promise = new Promise(function(_, __, onCancel) {})
+ .lastly(function() {
+ throw e;
+ })
+ .caught(function(err) {
+ assert.equal(err, e);
+ });
+ promise.cancel();
+ return promise;
+ });
+
+ specify("returning an immediately rejected promise in finally turns into a rejection", function() {
+ var e = new Error("");
+ var promise = new Promise(function(_, __, onCancel) {})
+ .lastly(function() {
+ return Promise.reject(e);
+ })
+ .caught(function(err) {
+ assert.equal(err, e);
+ });
+ promise.cancel();
+ return promise;
+ });
+ specify("returning an eventually rejected promise in finally turns into a rejection", function() {
+ var e = new Error("");
+ var promise = new Promise(function(_, __, onCancel) {})
+ .lastly(function() {
+ return new Promise(function(resolve, reject, onCancel) {
+ Promise.delay(1).then(function() {
+ reject(e);
+ });
+ });
+ })
+ .caught(function(err) {
+ assert.equal(err, e);
+ });
+ promise.cancel();
+ return promise;
+ });
+
+ specify("finally handler returned promises are awaited for", function() {
+ var awaited = 0;
+ var resolve;
+ var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+ var promise = new Promise(function(_, __, onCancel) {})
+ .lastly(function() {
+ return Promise.delay(1).then(function() {
+ awaited++;
+ });
+ })
+ .lastly(function() {
+ return Promise.delay(1).then(function() {
+ awaited++;
+ });
+ })
+ .lastly(function() {
+ return Promise.delay(1).then(function() {
+ awaited++;
+ });
+ })
+ .lastly(function() {
+ resolve();
+ })
+ promise.cancel();
+ return result.then(function() {
+ assert.equal(3, awaited);
+ });
+ });
+
+ specify("finally handler returned promises are skipped if they are cancelled", function() {
+ var cancelled = 0;
+ var resolve;
+ var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+ var promise = new Promise(function(_, __, onCancel) {})
+ .lastly(function() {
+ var ret = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ ret.cancel();
+ return ret;
+ })
+ .lastly(function() {
+ var ret = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ ret.cancel();
+ return ret;
+ })
+ .lastly(function() {
+ var ret = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ ret.cancel();
+ return ret;
+ })
+ .lastly(function() {
+ resolve();
+ })
+ promise.suppressUnhandledRejections();
+ promise.cancel();
+ return result.then(function() {
+ assert.equal(3, cancelled);
+ });
+ });
+
+ specify("finally handler returned promises are skipped if they are eventually cancelled", function() {
+ var cancelled = 0;
+ var resolve;
+ var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+ var promise = new Promise(function(_, __, onCancel) {})
+ .lastly(function() {
+ var ret = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ Promise.delay(1).then(function() {
+ ret.cancel();
+ });
+ return ret;
+ })
+ .lastly(function() {
+ var ret = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ Promise.delay(1).then(function() {
+ ret.cancel();
+ });
+ return ret;
+ })
+ .lastly(function() {
+ var ret = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ Promise.delay(1).then(function() {
+ ret.cancel();
+ });
+ return ret;
+ })
+ .lastly(function() {
+ resolve();
+ })
+ promise.cancel();
+ return result.then(function() {
+ assert.equal(3, cancelled);
+ });
+ });
+
+ specify("finally handler returned promises are skipped if theiy are eventually cancelled while following", function() {
+ var cancelled = 0;
+ var resolve;
+ var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+ var promise = new Promise(function(_, __, onCancel) {})
+ .lastly(function() {
+ var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ var ret = new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {
+ cancelled++;
+ });
+ });
+ Promise.delay(1).then(function() {
+ ret.cancel();
+ });
+ return ret;
+ })
+ .lastly(function() {
+ var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ var ret = new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {
+ cancelled++;
+ });
+ });
+ Promise.delay(1).then(function() {
+ ret.cancel();
+ });
+ return ret;
+ })
+ .lastly(function() {
+ var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ var ret = new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {
+ cancelled++;
+ });
+ });
+ Promise.delay(1).then(function() {
+ ret.cancel();
+ });
+ return ret;
+ })
+ .lastly(function() {
+ resolve();
+ })
+ promise.cancel();
+ return result.then(function() {
+ assert.equal(6, cancelled);
+ });
+ });
+
+ specify("finally handler returned promises are skipped if theiy are immediately cancelled while following", function() {
+ var cancelled = 0;
+ var resolve;
+ var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+ var promise = new Promise(function(_, __, onCancel) {})
+ .lastly(function() {
+ var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ var ret = new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {
+ cancelled++;
+ });
+ });
+ ret.cancel();
+ return ret;
+ })
+ .lastly(function() {
+ var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ var ret = new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {
+ cancelled++;
+ });
+ });
+ ret.cancel();
+ return ret;
+ })
+ .lastly(function() {
+ var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ var ret = new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {
+ cancelled++;
+ });
+ });
+ ret.cancel();
+ return ret;
+ })
+ .lastly(function() {
+ resolve();
+ });
+ promise.suppressUnhandledRejections();
+ promise.cancel();
+ return result.then(function() {
+ assert.equal(6, cancelled);
+ });
+ });
+
+ specify("finally handler returned promises target are skipped if their follower is eventually cancelled", function() {
+ var cancelled = 0;
+ var resolve;
+ var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+ var promise = new Promise(function(_, __, onCancel) {})
+ .lastly(function() {
+ var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ var ret = new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {
+ cancelled++;
+ });
+ });
+ Promise.delay(1).then(function() {
+ ret.cancel();
+ });
+ return p;
+ })
+ .lastly(function() {
+ var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ var ret = new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {
+ cancelled++;
+ });
+ });
+ Promise.delay(1).then(function() {
+ ret.cancel();
+ });
+ return p;
+ })
+ .lastly(function() {
+ var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ var ret = new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {
+ cancelled++;
+ });
+ });
+ Promise.delay(1).then(function() {
+ ret.cancel();
+ });
+ return p;
+ })
+ .lastly(function() {
+ resolve();
+ })
+ promise.cancel();
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(6, cancelled);
+ });
+ });
+ });
+
+ specify("finally handler returned promises target are skipped if their follower is immediately cancelled", function() {
+ var cancelled = 0;
+ var resolve;
+ var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+ var promise = new Promise(function(_, __, onCancel) {})
+ .lastly(function() {
+ var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ var ret = new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {
+ cancelled++;
+ });
+ });
+ ret.cancel();
+ return p;
+ })
+ .lastly(function() {
+ var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ var ret = new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {
+ cancelled++;
+ });
+ });
+ ret.cancel();
+ return p;
+ })
+ .lastly(function() {
+ var p = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); });
+ var ret = new Promise(function(resolve, _, onCancel) {
+ resolve(p);
+ onCancel(function() {
+ cancelled++;
+ });
+ });
+ ret.cancel();
+ return p;
+ })
+ .lastly(function() {
+ resolve();
+ })
+ promise.cancel();
+ promise.suppressUnhandledRejections();
+ return result.then(function() {
+ assert.equal(6, cancelled);
+ });
+ });
+
+ specify("attaching handler on already cancelled promise", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ p.cancel();
+ return awaitLateQueue(function() {
+ p.lastly(resolve);
+ return result;
+ });
+ });
+
+ specify("if onCancel callback causes synchronous rejection, it is ignored and cancellation wins", function() {
+ var promisifiedXhr = function() {
+ var xhrReject;
+ var xhr = {
+ then: function(resolve, reject) {
+ xhrReject = reject;
+ },
+ abort: function() {
+ xhrReject(new Error(""));
+ }
+ };
+ return new Promise(function(resolve, _, onCancel) {
+ resolve(xhr);
+ onCancel(function() {
+ xhr.abort();
+ });
+ });
+ };
+
+ var req = promisifiedXhr().lastly(function() {
+ resolve();
+ });
+ req.cancel();
+ var resolve;
+ return new Promise(function(_, __, onCancel) {resolve = arguments[0]});
+ });
+
+ specify("isCancelled() synchronously returns true after calling cancel() on pending promise", function() {
+ var promise = new Promise(function () {});
+ promise.cancel();
+ assert(promise.isCancelled());
+ });
+
+ specify("isCancelled() synchronously returns true after calling cancel() on promise created from .then()", function() {
+ var promise = new Promise(function () {});
+ var thenPromise = promise.then();
+ thenPromise.cancel();
+ assert(thenPromise.isCancelled());
+ });
+
+ specify("gh-166", function() {
+ var f1 = false, f2 = false, f3 = false, f4 = false;
+ var a = Promise.resolve();
+ a = a.then(function() {
+ f1 = true;
+ return Promise.delay(1);
+ });
+
+ a = a.then(function() {
+ f2 = true;
+ return Promise.delay(1);
+ });
+
+ a = a.then(function() {
+ f3 = true;
+ return Promise.delay(1);
+ }).then(function() {
+ assert(a.isCancellable());
+ a.cancel();
+ }).delay(100);
+
+
+ a = a.then(function() {
+ f4 = true;
+ });
+
+ var waitingForLongDelay = a;
+
+ a = a.lastly(function() {
+ assert(f1); assert(f2); assert(f3);
+ assert(!f4);
+ assert(waitingForLongDelay.isCancelled());
+ resolve();
+ });
+
+ assert(a.isCancellable());
+ var resolve;
+ var p = new Promise(function(_, __, onCancel) {resolve = arguments[0]});
+ return p;
+ });
+
+ specify("gh-1187", function() {
+ var a = Promise.delay(300).lastly(function() {});
+ a.cancel();
+ assert(a.isCancelled());
+ assert(!a.isCancellable());
+ })
+});
+
+describe("Cancellation with .all", function() {
+ specify("immediately cancelled input", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ p.cancel();
+ Promise.all(p).lastly(resolve);
+ return result;
+ });
+
+ specify("eventually cancelled input", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ Promise.all(p).lastly(resolve);
+ return awaitLateQueue(function() {
+ p.cancel();
+ return result;
+ });
+ });
+
+ specify("immediately cancelled input inside array", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ p.cancel();
+ Promise.all([1,2,p]).lastly(resolve);
+ return result;
+ });
+
+ specify("eventually cancelled input inside array", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ Promise.all([1,2,p]).lastly(resolve);
+ return awaitLateQueue(function() {
+ p.cancel();
+ return result;
+ });
+ });
+
+ specify("immediately cancelled output", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+
+ var all = Promise.all(inputs)
+ .lastly(function() {finalled++; resolve(); });
+
+ all.cancel();
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 3);
+ assert.equal(finalled, 4);
+ });
+ });
+ });
+
+ specify("eventually cancelled output", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+
+ var all = Promise.all(inputs)
+ .lastly(function() {finalled++; resolve(); });
+
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ Promise.delay(1).then(function() {
+ all.cancel();
+ });
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 3);
+ assert.equal(finalled, 4);
+ });
+ });
+ });
+
+ specify("immediately cancelled output while waiting on promise-for-input", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() {
+ finalled++;
+ });
+
+ var all = Promise.all(input)
+ .lastly(function() {finalled++; resolve(); });
+
+ all.cancel();
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 1);
+ assert.equal(finalled, 2);
+ });
+ });
+ });
+
+ specify("eventually cancelled output while waiting on promise-for-input", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() {
+ finalled++;
+ });
+
+ var all = Promise.all(input).lastly(function() {finalled++; resolve(); });
+
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ Promise.delay(1).then(function() {
+ all.cancel();
+ });
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 1);
+ assert.equal(finalled, 2);
+ });
+ });
+ });
+});
+
+describe("Cancellation with .props", function() {
+ specify("immediately cancelled input", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ p.cancel();
+ Promise.props(p).lastly(resolve).suppressUnhandledRejections();
+ return result;
+ });
+
+ specify("eventually cancelled input", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ Promise.props(p).lastly(resolve);
+ return awaitLateQueue(function() {
+ p.cancel();
+ return result;
+ });
+ });
+
+ specify("immediately cancelled input inside array", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ p.cancel();
+ Promise.props([1,2,p]).lastly(resolve);
+ return result;
+ });
+
+ specify("eventually cancelled input inside array", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ Promise.props([1,2,p]).lastly(resolve);
+ return awaitLateQueue(function() {
+ p.cancel();
+ return result;
+ });
+ });
+
+ specify("immediately cancelled output", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+
+ var all = Promise.props(inputs).lastly(function() {finalled++; resolve(); });
+
+ all.cancel();
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 3);
+ assert.equal(finalled, 4);
+ });
+ });
+ });
+
+ specify("eventually cancelled output", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+
+ var all = Promise.props(inputs).lastly(function() {finalled++; resolve(); });
+
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ Promise.delay(1).then(function() {
+ all.cancel();
+ });
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 3);
+ assert.equal(finalled, 4);
+ });
+ });
+ });
+
+ specify("immediately cancelled output while waiting on promise-for-input", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() {
+ finalled++;
+ });
+
+ var all = Promise.props(input).lastly(function() {finalled++; resolve(); });
+
+ all.cancel();
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 1);
+ assert.equal(finalled, 2);
+ });
+ });
+ });
+
+ specify("eventually cancelled output while waiting on promise-for-input", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() {
+ finalled++;
+ });
+
+ var all = Promise.props(input)
+ .lastly(function() {finalled++; resolve(); });
+
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ Promise.delay(1).then(function() {
+ all.cancel();
+ });
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 1);
+ assert.equal(finalled, 2);
+ });
+ });
+ });
+});
+
+describe("Cancellation with .some", function() {
+ specify("immediately cancelled input", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ p.cancel();
+ Promise.some(p, 1).lastly(resolve);
+ return result;
+ });
+
+ specify("eventually cancelled input", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ Promise.some(p, 1).lastly(resolve);
+ return awaitLateQueue(function() {
+ p.cancel();
+ return result;
+ });
+ });
+
+ specify("immediately cancelled output", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+
+ var all = Promise.some(inputs, 1)
+ .lastly(function() {finalled++; resolve(); });
+
+ all.cancel();
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 3);
+ assert.equal(finalled, 4);
+ });
+ });
+ });
+
+ specify("eventually cancelled output", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+
+ var all = Promise.some(inputs, 1)
+ .lastly(function() {finalled++; resolve(); });
+
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ Promise.delay(1).then(function() {
+ all.cancel();
+ });
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 3);
+ assert.equal(finalled, 4);
+ });
+ });
+ });
+
+
+ specify("immediately cancelled output while waiting on promise-for-input", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() {
+ finalled++;
+ });
+
+ var all = Promise.some(input, 1)
+ .lastly(function() {finalled++; resolve(); });
+
+ all.cancel();
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 1);
+ assert.equal(finalled, 2);
+ });
+ });
+ });
+
+ specify("eventually cancelled output while waiting on promise-for-input", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() {
+ finalled++;
+ });
+
+ var all = Promise.some(input, 1)
+ .lastly(function() {finalled++; resolve(); });
+
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ Promise.delay(1).then(function() {
+ all.cancel();
+ });
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 1);
+ assert.equal(finalled, 2);
+ });
+ });
+ });
+
+ specify("some promises are cancelled immediately", function() {
+ var resolve;
+ var p1 = new Promise(function(_, __, onCancel) {});
+ var p2 = new Promise(function(_, __, onCancel) {});
+ var p3 = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+
+ p1.cancel();
+ p2.cancel();
+ resolve(1);
+ return Promise.some([p1, p2, p3], 1).then(function(result) {
+ assert.deepEqual([1], result);
+ });
+ });
+
+ specify("some promises are cancelled eventually", function() {
+ var resolve;
+ var p1 = new Promise(function(_, __, onCancel) {});
+ var p2 = new Promise(function(_, __, onCancel) {});
+ var p3 = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+
+ Promise.delay(1).then(function() {
+ p1.cancel();
+ p2.cancel();
+ return Promise.delay(1).then(function() {
+ resolve(1);
+ });
+ });
+ return Promise.some([p1, p2, p3], 1).then(function(result) {
+ assert.deepEqual([1], result);
+ });
+ });
+
+ specify("promise for some promises that are cancelled immediately", function() {
+ var resolve;
+ var p1 = new Promise(function(_, __, onCancel) {});
+ var p2 = new Promise(function(_, __, onCancel) {});
+ var p3 = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+
+ p1.cancel();
+ p2.cancel();
+ resolve(1);
+ var promise = Promise.delay(1, [p1, p2, p3]);
+ return Promise.some(promise, 1).then(function(result) {
+ assert.deepEqual([1], result);
+ });
+ });
+
+ specify("promise for some promises that are cancelled eventually", function() {
+ var resolve;
+ var p1 = new Promise(function(_, __, onCancel) {});
+ var p2 = new Promise(function(_, __, onCancel) {});
+ var p3 = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+
+ Promise.delay(1).then(function() {
+ p1.cancel();
+ p2.cancel();
+ return Promise.delay(1).then(function() {
+ resolve(1);
+ });
+ });
+ var promise = Promise.delay(1, [p1, p2, p3]);
+ return Promise.some(promise, 1).then(function(result) {
+ assert.deepEqual([1], result);
+ });
+ });
+
+
+ specify("all promises cancel, not enough for fulfillment - immediately", function() {
+ var resolve;
+ var p1 = new Promise(function(_, __, onCancel) {});
+ var p2 = new Promise(function(_, __, onCancel) {});
+ var p3 = new Promise(function(_, __, onCancel) {});
+ var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+
+ p1.cancel();
+ p2.cancel();
+ p3.cancel();
+ Promise.some([p1, p2, p3], 1).then(assert.fail, function(e)Â {
+ assert(e instanceof Promise.CancellationError);
+ resolve();
+ });
+ return result;
+ });
+
+ specify("all promises cancel, not enough for fulfillment - eventually", function() {
+ var resolve;
+ var p1 = new Promise(function(_, __, onCancel) {});
+ var p2 = new Promise(function(_, __, onCancel) {});
+ var p3 = new Promise(function(_, __, onCancel) {});
+ var result = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+
+ Promise.delay(1).then(function() {
+ p1.cancel();
+ p2.cancel();
+ return Promise.delay(1).then(function() {
+ p3.cancel();
+ });
+ });
+ Promise.some([p1, p2, p3], 1).then(assert.fail, assert.fail).lastly(resolve);
+ return result;
+ });
+
+ specify("some promises cancel, some reject, not enough for fulfillment - immediately", function() {
+ var error = new Error();
+ var reject;
+ var p1 = new Promise(function(_, __, onCancel) {});
+ var p2 = new Promise(function(_, __, onCancel) {});
+ var p3 = new Promise(function(_, __, onCancel) {reject = arguments[1];});
+
+ p1.cancel();
+ p2.cancel();
+ reject(error);
+ return Promise.some([p1, p2, p3], 1).then(assert.fail, function(result) {
+ assert(result instanceof Promise.AggregateError);
+ assert.equal(1, result.length);
+ assert.equal(error, result[0]);
+ });
+ });
+
+ specify("some promises cancel, some reject, not enough for fulfillment - eventually", function() {
+ var error = new Error();
+ var reject;
+ var p1 = new Promise(function(_, __, onCancel) {});
+ var p2 = new Promise(function(_, __, onCancel) {});
+ var p3 = new Promise(function(_, __, onCancel) {reject = arguments[1];});
+
+ Promise.delay(1).then(function() {
+ p1.cancel();
+ p2.cancel();
+ return Promise.delay(1).then(function() {
+ reject(error);
+ });
+ });
+ return Promise.some([p1, p2, p3], 1).then(assert.fail, function(result) {
+ assert(result instanceof Promise.AggregateError);
+ assert.equal(1, result.length);
+ assert.equal(error, result[0]);
+ });
+ });
+});
+
+describe("Cancellation with .reduce", function() {
+ specify("initialValue immediately cancelled immediate input", function() {
+ var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+ initialValue.cancel();
+ Promise.reduce(inputs, function(){
+ finalled++;
+ }, initialValue).lastly(function() {
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ assert.equal(2, finalled);
+ assert.equal(1, cancelled);
+ });
+ });
+
+ specify("initialValue eventually cancelled immediate input", function() {
+ var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+ Promise.reduce(inputs, function(){
+ finalled++;
+ }, initialValue).lastly(function() {
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ initialValue.cancel();
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(2, finalled);
+ assert.equal(1, cancelled);
+ });
+ });
+ });
+ });
+
+ specify("initialValue eventually cancelled eventual input", function() {
+ var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = new Promise(function(_, __, onCancel) {});
+ Promise.reduce(inputs, function(){
+ finalled++;
+ }, initialValue).lastly(function() {
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ initialValue.cancel();
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(2, finalled);
+ assert.equal(1, cancelled);
+ });
+ });
+ });
+ });
+
+ specify("initialValue immediately cancelled eventual input", function() {
+ var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = new Promise(function(_, __, onCancel) {});
+ initialValue.cancel();
+ Promise.reduce(inputs, function(){
+ finalled++;
+ }, initialValue).lastly(function() {
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ assert.equal(2, finalled);
+ assert.equal(1, cancelled);
+ });
+ });
+
+ specify("returned promise cancels immediately", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [1, 2,
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+ Promise.reduce(inputs, function(a, b) {
+ finalled++;
+ var ret = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++});
+ ret.cancel();
+ return ret;
+ }).lastly(function() {
+ finalled++;
+ });
+
+ return awaitLateQueue(function() {
+ assert.equal(3, finalled);
+ assert.equal(1, cancelled);
+ });
+ });
+
+ specify("returned promise cancels eventually", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [1, 2,
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+ Promise.reduce(inputs, function(a, b) {
+ finalled++;
+ var ret = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++});
+ awaitLateQueue(function() {
+ ret.cancel();
+ });
+ return ret;
+ }).lastly(function() {
+ finalled++;
+ });
+
+ return awaitLateQueue(function() {
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(3, finalled);
+ assert.equal(1, cancelled);
+ });
+ });
+ });
+ });
+
+ specify("input immediately cancelled while waiting initialValue", function() {
+ var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = new Promise(function(_, __, onCancel) {});
+ inputs.cancel();
+ Promise.reduce(inputs, function(){
+ finalled++;
+ }, initialValue).lastly(function() {
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ assert.equal(1, finalled);
+ assert.equal(0, cancelled);
+ });
+ });
+
+ specify("input eventually cancelled while waiting initialValue", function() {
+ var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = new Promise(function(_, __, onCancel) {});
+ Promise.reduce(inputs, function(){
+ finalled++;
+ }, initialValue).lastly(function() {
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ inputs.cancel();
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(1, finalled);
+ assert.equal(0, cancelled);
+ });
+ });
+ });
+ });
+
+ specify("output immediately cancelled while waiting inputs", function() {
+ var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++});
+
+ var all = Promise.reduce(inputs, function(){
+ finalled++;
+ }, initialValue).lastly(function() {
+ finalled++;
+ });
+ all.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(3, finalled);
+ assert.equal(2, cancelled);
+ });
+ });
+
+ specify("output immediately cancelled while waiting initialValue", function() {
+ var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+ var all = Promise.reduce(inputs, function(){
+ finalled++;
+ }, initialValue).lastly(function() {
+ finalled++;
+ });
+ all.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(5, finalled);
+ assert.equal(4, cancelled);
+ });
+ });
+
+ specify("output immediately cancelled while waiting firstValue", function() {
+ var initialValue = 1;
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+ var all = Promise.reduce(inputs, function(){
+ finalled++;
+ }, initialValue).lastly(function() {
+ finalled++;
+ });
+ all.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(4, finalled);
+ assert.equal(3, cancelled);
+ });
+ });
+
+ specify("output immediately cancelled while waiting firstValue and secondValue", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+ var all = Promise.reduce(inputs, function(){
+ finalled++;
+ }).lastly(function() {
+ finalled++;
+ });
+ all.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(4, finalled);
+ assert.equal(3, cancelled);
+ });
+ });
+
+ specify("output immediately cancelled while waiting for a result", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [1, 2,
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+ var all = Promise.reduce(inputs, function(a, b) {
+ finalled++;
+ return new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++});
+ }).lastly(function() {
+ finalled++;
+ });
+ all.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(4, finalled);
+ assert.equal(2, cancelled);
+ });
+ });
+
+ specify("output eventually cancelled while waiting inputs", function() {
+ var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++});
+
+ var all = Promise.reduce(inputs, function(){
+ finalled++;
+ }, initialValue).lastly(function() {
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ all.cancel();
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(3, finalled);
+ assert.equal(2, cancelled);
+ });
+ });
+ });
+ });
+
+ specify("output eventually cancelled while waiting initialValue", function() {
+ var initialValue = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+ var all = Promise.reduce(inputs, function(){
+ finalled++;
+ }, initialValue).lastly(function() {
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ all.cancel();
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(5, finalled);
+ assert.equal(4, cancelled);
+ });
+ });
+ });
+ });
+
+ specify("output eventually cancelled while waiting firstValue", function() {
+ var initialValue = 1;
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+ var all = Promise.reduce(inputs, function(){
+ finalled++;
+ }, initialValue).lastly(function() {
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ all.cancel();
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(4, finalled);
+ assert.equal(3, cancelled);
+ });
+ });
+ });
+ });
+
+ specify("output eventually cancelled while waiting firstValue and secondValue", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+ var all = Promise.reduce(inputs, function(){
+ finalled++;
+ }).lastly(function() {
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ all.cancel();
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(4, finalled);
+ assert.equal(3, cancelled);
+ });
+ });
+ });
+ });
+
+ specify("output eventually cancelled while waiting for a result", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [1, 2,
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+ var all = Promise.reduce(inputs, function(a, b) {
+ finalled++;
+ return new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++});
+ }).lastly(function() {
+ finalled++;
+ });
+
+ return awaitLateQueue(function() {
+ all.cancel();
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(4, finalled);
+ assert.equal(2, cancelled);
+ });
+ });
+ });
+ });
+});
+
+describe("Cancellation with .map", function() {
+ specify("immediately cancelled input", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ p.cancel();
+ Promise.map(p, function(){}).lastly(resolve);
+ return result;
+ });
+
+ specify("eventually cancelled input", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ Promise.map(p, function(){}).lastly(resolve);
+ return awaitLateQueue(function() {
+ p.cancel();
+ return result;
+ });
+ });
+
+ specify("immediately cancelled input inside array", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ p.cancel();
+ Promise.map([1,2,p], function(){}).lastly(resolve);
+ return result;
+ });
+
+ specify("eventually cancelled input inside array", function() {
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ var p = new Promise(function(_, __, onCancel) {});
+ Promise.map([1,2,p], function(){}).lastly(resolve);
+ return awaitLateQueue(function() {
+ p.cancel();
+ return result;
+ });
+ });
+
+ specify("immediately cancelled output", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+
+ var all = Promise.map(inputs, function(){})
+ .lastly(function() {finalled++; resolve(); });
+
+ all.cancel();
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 3);
+ assert.equal(finalled, 4);
+ });
+ });
+ });
+
+ specify("eventually cancelled output", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+
+ var all = Promise.map(inputs, function(){})
+ .lastly(function() {finalled++; resolve(); });
+
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ Promise.delay(1).then(function() {
+ all.cancel();
+ });
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 3);
+ assert.equal(finalled, 4);
+ });
+ });
+ });
+
+ specify("immediately cancelled output while waiting on promise-for-input", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() {
+ finalled++;
+ });
+
+ var all = Promise.map(input, function(){})
+ .lastly(function() {finalled++; resolve(); });
+
+ all.cancel();
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 1);
+ assert.equal(finalled, 2);
+ });
+ });
+ });
+
+ specify("eventually cancelled output while waiting on promise-for-input", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var input = new Promise(function(_, __, onCancel) { onCancel(function(){ cancelled++; }); }).lastly(function() {
+ finalled++;
+ });
+
+ var all = Promise.map(input, function(){})
+ .lastly(function() {finalled++; resolve(); });
+
+ var resolve;
+ var result = new Promise(function()Â {resolve = arguments[0]});
+ Promise.delay(1).then(function() {
+ all.cancel();
+ });
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 1);
+ assert.equal(finalled, 2);
+ });
+ });
+ });
+
+ specify("result cancelled immediately while there are in-flight returned promises", function() {
+ var cancelled = 0;
+ var finalled = 0;
+
+ var all = Promise.map([1, 2, 3], function(value, index) {
+ return new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ }).lastly(function() {
+ finalled++;
+ });
+ all.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(4, finalled);
+ assert.equal(3, cancelled);
+ });
+ });
+
+ specify("result cancelled eventually while there are in-flight returned promises", function() {
+ var cancelled = 0;
+ var finalled = 0;
+
+ var all = Promise.map([1, 2, 3], function(value, index) {
+ return new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ }).lastly(function() {
+ finalled++;
+ });
+
+ return awaitLateQueue(function() {
+ all.cancel();
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(4, finalled);
+ assert.equal(3, cancelled);
+ });
+ });
+ });
+ });
+
+ specify("returned promise cancelled immediately while there are in-flight returned promises", function() {
+ var cancelled = 0;
+ var finalled = 0;
+
+ Promise.map([1, 2, 3], function(value, index) {
+ var ret = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++});
+ if (index === 2) {
+ ret.cancel();
+ }
+ return ret;
+ }).lastly(function() {
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ assert.equal(2, finalled);
+ assert.equal(1, cancelled);
+ });
+ });
+
+ specify("returned promise cancelled eventually while there are in-flight returned promises", function() {
+ var cancelled = 0;
+ var finalled = 0;
+
+ Promise.map([1, 2, 3], function(value, index) {
+ var ret = new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++});
+ if (index === 2) {
+ awaitLateQueue(function() {
+ ret.cancel();
+ });
+ }
+ return ret;
+ }).lastly(function() {
+ finalled++;
+ });
+
+ return awaitLateQueue(function() {
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(2, finalled);
+ assert.equal(1, cancelled);
+ });
+ });
+ });
+ });
+});
+describe("Cancellation with .bind", function() {
+ specify("immediately cancelled promise passed as ctx", function() {
+ var finalled = 0;
+ var cancelled = 0;
+ var ctx = new Promise(function(_, __, onCancel) {});
+ ctx.cancel();
+ Promise.bind(ctx).lastly(function() {
+ finalled++;
+ }).suppressUnhandledRejections();
+ return awaitLateQueue(function() {
+ assert.equal(1, finalled);
+ assert.equal(0, cancelled);
+ });
+ });
+
+ specify("eventually cancelled promise passed as ctx", function() {
+ var finalled = 0;
+ var cancelled = 0;
+ var ctx = new Promise(function(_, __, onCancel) {});
+ Promise.bind(ctx).lastly(function() {
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ ctx.cancel();
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(1, finalled);
+ assert.equal(0, cancelled);
+ });
+ });
+ });
+ });
+
+ specify("main promise is immediately cancelled while waiting on binding", function() {
+ var finalled = 0;
+ var cancelled = 0;
+ var resolve;
+ var ctx = new Promise(function(_, __, onCancel) {resolve = arguments[0];});
+ var main = new Promise(function(_, __, onCancel) {});
+ main.cancel();
+ main.bind(ctx).lastly(function() {
+ finalled++;
+ }).suppressUnhandledRejections();
+ return awaitLateQueue(function() {
+ resolve();
+ return ctx;
+ }).then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(1, finalled);
+ assert.equal(0, cancelled);
+ });
+ });
+ });
+
+ specify("main promise is eventually cancelled while waiting on binding", function() {
+ var finalled = 0;
+ var cancelled = 0;
+ var ctx = new Promise(function(_, __, onCancel) {});
+ var main = new Promise(function(_, __, onCancel) {});
+
+ main.bind(ctx).lastly(function() {
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ main.cancel();
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(1, finalled);
+ assert.equal(0, cancelled);
+ });
+ });
+ });
+ });
+
+ specify("main promise is immediately cancelled with immediate binding", function() {
+ var finalled = 0;
+ var cancelled = 0;
+ var ctx = {};
+ var main = new Promise(function(_, __, onCancel) {});
+ main.bind(ctx).lastly(function() {
+ assert.equal(this, ctx);
+ finalled++;
+ });
+ main.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(1, finalled);
+ assert.equal(0, cancelled);
+ });
+ });
+
+ specify("main promise is eventually cancelled with immediate binding", function() {
+ var finalled = 0;
+ var cancelled = 0;
+ var ctx = {};
+ var main = new Promise(function(_, __, onCancel) {});
+ main.bind(ctx).lastly(function() {
+ assert.equal(this, ctx);
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ main.cancel();
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(1, finalled);
+ assert.equal(0, cancelled);
+ });
+ });
+ });
+ });
+
+ specify("result is immediately cancelled while waiting for binding", function() {
+ var finalled = 0;
+ var cancelled = 0;
+ var ctx = new Promise(function(_, __, onCancel) {
+ onCancel(function() {
+ cancelled++;
+ });
+ }).lastly(function() {
+ finalled++;
+ });
+ var result = Promise.bind(ctx).lastly(function() {
+ finalled++;
+ });
+ result.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(2, finalled);
+ assert.equal(1, cancelled);
+ });
+ });
+
+ specify("result is eventually cancelled while waiting for binding", function() {
+ var finalled = 0;
+ var cancelled = 0;
+ var ctx = new Promise(function(_, __, onCancel) {
+ onCancel(function() {
+ cancelled++;
+ });
+ }).lastly(function() {
+ finalled++;
+ });
+
+ var result = Promise.bind(ctx).lastly(function() {
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ result.cancel();
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(2, finalled);
+ assert.equal(1, cancelled);
+ });
+ });
+ });
+ });
+
+ specify("result is immediately cancelled while waiting for main promise", function() {
+ var finalled = 0;
+ var cancelled = 0;
+ var ctx = {};
+ var main = new Promise(function(_, __, onCancel) {
+ onCancel(function() {
+ cancelled++;
+ });
+ }).lastly(function() {
+ finalled++;
+ });
+
+ var result = main.bind(ctx).lastly(function() {
+ assert.equal(this, ctx);
+ finalled++;
+ });
+ result.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(2, finalled);
+ assert.equal(1, cancelled);
+ });
+ });
+
+ specify("result is eventually cancelled while waiting for main promise", function() {
+ var finalled = 0;
+ var cancelled = 0;
+ var ctx = {};
+ var main = new Promise(function(_, __, onCancel) {
+ onCancel(function() {
+ cancelled++;
+ });
+ }).lastly(function() {
+ finalled++;
+ });
+
+ var result = main.bind(ctx).lastly(function() {
+ assert.equal(this, ctx);
+ finalled++;
+ });
+ return awaitLateQueue(function() {
+ result.cancel();
+ return Promise.resolve().then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(2, finalled);
+ assert.equal(1, cancelled);
+ });
+ });
+ });
+ });
+});
+
+describe("Cancellation with .join", function() {
+ specify("immediately cancelled input inside array", function() {
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+ var p = new Promise(function(_, __, onCancel) {});
+ p.cancel();
+ Promise.join(1,2,p, assert.fail).then(reject, function(e)Â {
+ assert(e instanceof Promise.CancellationError);
+ resolve();
+ });
+ return result;
+ });
+
+ specify("eventually cancelled input inside array", function() {
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+ var p = new Promise(function(_, __, onCancel) {});
+ Promise.join(1,2,p, assert.fail).then(reject, reject).lastly(resolve);
+ return awaitLateQueue(function() {
+ p.cancel();
+ return result;
+ });
+ });
+
+ specify("immediately cancelled output", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+
+ var all = Promise.join(inputs[0], inputs[1], inputs[2], assert.fail)
+ .then(reject, reject)
+ .lastly(function() {finalled++; resolve(); });
+
+ all.cancel();
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 3);
+ assert.equal(finalled, 4);
+ });
+ });
+ });
+
+ specify("eventually cancelled output", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++}),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ ];
+
+ var all = Promise.join(inputs[0], inputs[1], inputs[2], assert.fail)
+ .then(reject, reject)
+ .lastly(function() {finalled++; resolve(); });
+
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+ Promise.delay(1).then(function() {
+ all.cancel();
+ });
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 3);
+ assert.equal(finalled, 4);
+ });
+ });
+ });
+});
+
+describe("Cancellation with .reflect", function() {
+ specify("immediately cancelled", function() {
+ var promise = new Promise(function(_, __, onCancel) {});
+ promise.cancel();
+ return promise.reflect().then(function(value) {
+ assert(!value.isFulfilled());
+ assert(!value.isRejected());
+ assert(!value.isPending());
+ assert(value.isCancelled());
+ });
+ });
+
+ specify("eventually cancelled", function() {
+ var promise = new Promise(function(_, __, onCancel) {});
+
+ var ret = promise.reflect().then(function(value) {
+ assert(!value.isFulfilled());
+ assert(!value.isRejected());
+ assert(!value.isPending());
+ assert(value.isCancelled());
+ });
+
+ Promise.delay(1).then(function() {
+ promise.cancel();
+ });
+ return ret;
+ });
+});
+
+describe("Cancellation with .using", function() {
+ specify("immediately cancelled input", function() {
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+ var p = new Promise(function(_, __, onCancel) {});
+ p.cancel();
+
+ var disposerCalled = false;
+ var disposable = new Promise(function(_, __, onCancel) {
+ setTimeout(arguments[0], 1);
+ }).disposer(function() {
+ disposerCalled = true;
+ });
+
+ Promise.using(1, disposable, p, assert.fail).then(reject, function(e)Â {
+ assert(e instanceof Promise.CancellationError);
+ assert(disposerCalled);
+ resolve();
+ });
+
+ return result;
+ });
+
+ specify("eventually cancelled input", function() {
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+ var p = new Promise(function(_, __, onCancel) {});
+
+ var disposerCalled = false;
+ var disposable = new Promise(function(_, __, onCancel) {
+ setTimeout(arguments[0], 1);
+ }).disposer(function() {
+ disposerCalled = true;
+ });
+
+ Promise.using(1, disposable, p, assert.fail).then(reject, reject).lastly(function() {
+ assert(disposerCalled);
+ resolve();
+ });
+
+ return awaitLateQueue(function() {
+ p.cancel();
+ return result;
+ });
+ });
+
+ specify("eventually cancelled input with 1 fulfilled disposer", function() {
+ var resolve, reject;
+ var fulfillResource;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+ var p = new Promise(function(_, __, onCancel) {});
+
+ var disposerCalled = false;
+ var disposable = new Promise(function(_, __, onCancel) {fulfillResource = arguments[0];}).disposer(function() {
+ disposerCalled = true;
+ });
+
+ Promise.using(1,disposable,p, assert.fail).then(reject, reject).lastly(function() {
+ assert(disposerCalled);
+ resolve();
+ });
+
+ return awaitLateQueue(function() {
+ fulfillResource({});
+ p.cancel();
+ return result;
+ });
+ });
+
+ specify("immediately cancelled output", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var disposerCalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ .disposer(function() {
+ disposerCalled++;
+ }),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ .disposer(function() {
+ disposerCalled++;
+ }),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ .disposer(function() {
+ disposerCalled++;
+ })
+ ];
+
+ var all = Promise.using(inputs[0], inputs[1], inputs[2], assert.fail)
+ .then(reject, reject)
+ .lastly(function() {finalled++; resolve(); });
+
+ all.cancel();
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 3);
+ assert.equal(finalled, 4);
+ assert.equal(disposerCalled, 0);
+ });
+ });
+ });
+
+ specify("eventually cancelled output", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var disposerCalled = 0;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ .disposer(function() {
+ disposerCalled++;
+ }),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ .disposer(function() {
+ disposerCalled++;
+ }),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ .disposer(function() {
+ disposerCalled++;
+ })
+ ];
+
+ var all = Promise.using(inputs[0], inputs[1], inputs[2], assert.fail)
+ .then(reject, reject)
+ .lastly(function() {finalled++; resolve(); });
+
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+ Promise.delay(1).then(function() {
+ all.cancel();
+ });
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 3);
+ assert.equal(finalled, 4);
+ assert.equal(disposerCalled, 0);
+ });
+ });
+ });
+
+ specify("eventually cancelled output with 1 disposer fulfilled", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var disposerCalled = 0;
+ var fulfillResource;
+ var inputs = [
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ .disposer(function() {
+ disposerCalled++;
+ }),
+ new Promise(function(_, __, onCancel) {fulfillResource = arguments[0];})
+ .disposer(function() {
+ disposerCalled++;
+ }),
+ new Promise(function(_, __, onCancel) { onCancel(function() { cancelled++; }); })
+ .lastly(function() {finalled++})
+ .disposer(function() {
+ disposerCalled++;
+ })
+ ];
+
+ var all = Promise.using(inputs[0], inputs[1], inputs[2], assert.fail)
+ .then(reject, reject)
+ .lastly(function() {finalled++; resolve(); });
+
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+ Promise.delay(1).then(function() {
+ fulfillResource({})
+ all.cancel();
+ });
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 2);
+ assert.equal(finalled, 3);
+ assert.equal(disposerCalled, 1);
+ });
+ });
+ });
+
+ specify("result immediately cancelled when inside handler", function() {
+ var disposerCalled = 0;
+ var cancelled = 0;
+ var finalled = 0;
+ var resource1 = Promise.resolve().disposer(function() {
+ disposerCalled++;
+ });
+
+ var resource2 = Promise.resolve().disposer(function() {
+ disposerCalled++;
+ });
+
+ var all = Promise.using(resource1, resource2, function(res1, res2) {
+ var ret = new Promise(function(_, __, onCancel) {});
+ all.cancel();
+ return ret;
+ }).then(reject, reject)
+ .lastly(function() {finalled++; resolve(); });
+
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 0);
+ assert.equal(finalled, 1);
+ assert.equal(disposerCalled, 2);
+ });
+ });
+ });
+
+ specify("result eventually cancelled when inside handler", function() {
+ var disposerCalled = 0;
+ var cancelled = 0;
+ var finalled = 0;
+ var resource1 = Promise.resolve().disposer(function() {
+ disposerCalled++;
+ });
+
+ var resource2 = Promise.resolve().disposer(function() {
+ disposerCalled++;
+ });
+
+ var all = Promise.using(resource1, resource2, function(res1, res2) {
+ var ret = new Promise(function(_, __, onCancel) {});
+ Promise.delay(1).then(function() {
+ all.cancel();
+ });
+ return ret;
+ }).then(reject, reject)
+ .lastly(function() {finalled++; resolve(); });
+
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 0);
+ assert.equal(finalled, 1);
+ assert.equal(disposerCalled, 2);
+ });
+ });
+ });
+
+ specify("promise returned from handler immediately cancelled", function() {
+ var disposerCalled = 0;
+ var cancelled = 0;
+ var finalled = 0;
+ var resource1 = Promise.resolve().disposer(function() {
+ disposerCalled++;
+ });
+
+ var resource2 = Promise.resolve().disposer(function() {
+ disposerCalled++;
+ });
+
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+
+ var all = Promise.using(resource1, resource2, function(res1, res2) {
+ var ret = new Promise(function(_, __, onCancel) {});
+ ret.cancel();
+ return ret;
+ }).then(reject, function(e)Â {
+ if(!(e instanceof Promise.CancellationError)) reject(new Error());
+ }).lastly(function() {finalled++; resolve(); });
+
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 0);
+ assert.equal(finalled, 1);
+ assert.equal(disposerCalled, 2);
+ });
+ });
+ });
+
+ specify("promise returned from handler eventually cancelled", function() {
+ var disposerCalled = 0;
+ var cancelled = 0;
+ var finalled = 0;
+ var resource1 = Promise.resolve().disposer(function() {
+ disposerCalled++;
+ });
+
+ var resource2 = Promise.resolve().disposer(function() {
+ disposerCalled++;
+ });
+
+ var all = Promise.using(resource1, resource2, function(res1, res2) {
+ var ret = new Promise(function(_, __, onCancel) {});
+ Promise.delay(1).then(function() {
+ ret.cancel();
+ });
+ return ret;
+ }).then(reject, reject)
+ .lastly(function() {finalled++; resolve(); });
+
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(cancelled, 0);
+ assert.equal(finalled, 1);
+ assert.equal(disposerCalled, 2);
+ });
+ });
+ });
+});
+
+describe("Multi-branch cancellation", function() {
+ specify("3 branches, 1 cancels", function() {
+ var successCalls = 0;
+ var rootGotCancelled = false;
+ var resolveRoot;
+ var root = new Promise(function(resolve, __, onCancel) {
+ onCancel(function() {
+ rootGotCancelled = true;
+ });
+ resolveRoot = resolve;
+ });
+
+ var a = root.then(function() {
+ successCalls++;
+ });
+ var b = root.then(function() {
+ successCalls++;
+ });
+ var c = root.then(function() {
+ successCalls++;
+ });
+
+ return awaitLateQueue(function() {
+ b.cancel();
+ }).then(function() {
+ return awaitLateQueue(resolveRoot);
+ }).then(function() {
+ return awaitLateQueue(function() {
+ assert(!rootGotCancelled);
+ assert.equal(2, successCalls);
+ });
+ });
+ });
+
+ specify("3 branches, 3 cancels", function() {
+ var successCalls = 0;
+ var rootGotCancelled = false;
+ var resolveRoot;
+ var root = new Promise(function(resolve, __, onCancel) {
+ onCancel(function() {
+ rootGotCancelled = true;
+ });
+ resolveRoot = resolve;
+ });
+
+ var a = root.then(function() {
+ successCalls++;
+ });
+ var b = root.then(function() {
+ successCalls++;
+ });
+ var c = root.then(function() {
+ successCalls++;
+ });
+
+ return awaitLateQueue(function() {
+ a.cancel();
+ b.cancel();
+ c.cancel();
+ }).then(function() {
+ return awaitLateQueue(resolveRoot);
+ }).then(function() {
+ return awaitLateQueue(function() {
+ assert(rootGotCancelled);
+ assert.equal(0, successCalls);
+ });
+ });
+ });
+
+ specify("3 branches, root cancels", function() {
+ var successCalls = 0;
+ var rootGotCancelled = false;
+ var resolveRoot;
+ var root = new Promise(function(resolve, __, onCancel) {
+ onCancel(function() {
+ rootGotCancelled = true;
+ });
+ resolveRoot = resolve;
+ });
+
+ var a = root.then(function() {
+ successCalls++;
+ });
+ var b = root.then(function() {
+ successCalls++;
+ });
+ var c = root.then(function() {
+ successCalls++;
+ });
+
+ return awaitLateQueue(function() {
+ root.cancel();
+ }).then(function() {
+ return awaitLateQueue(resolveRoot);
+ }).then(function() {
+ return awaitLateQueue(function() {
+ assert(rootGotCancelled);
+ assert.equal(0, successCalls);
+ });
+ });
+ });
+
+ specify("3 branches, each have 3 branches, all children of b cancel", function() {
+ var successCalls = 0;
+ var rootGotCancelled = false;
+ var resolveRoot;
+ var root = new Promise(function(resolve, __, onCancel) {
+ onCancel(function() {
+ rootGotCancelled = true;
+ });
+ resolveRoot = resolve;
+ });
+
+ var a = root.then(function() {
+ successCalls++;
+ });
+
+ var a1 = a.then(function() {
+ successCalls++;
+ });
+
+ var a2 = a.then(function() {
+ successCalls++;
+ });
+
+ var a3 = a.then(function() {
+ successCalls++;
+ });
+
+ var b = root.then(function() {
+ successCalls++;
+ });
+
+ var b1 = b.then(function() {
+ successCalls++;
+ });
+
+ var b2 = b.then(function() {
+ successCalls++;
+ });
+
+ var b3 = b.then(function() {
+ successCalls++;
+ });
+
+ var c = root.then(function() {
+ successCalls++;
+ });
+
+ var c1 = c.then(function() {
+ successCalls++;
+ });
+
+ var c2 = c.then(function() {
+ successCalls++;
+ });
+
+ var c3 = c.then(function() {
+ successCalls++;
+ });
+
+ return awaitLateQueue(function() {
+ b1.cancel();
+ b2.cancel();
+ b3.cancel();
+ }).then(function() {
+ return awaitLateQueue(resolveRoot);
+ }).then(function() {
+ return awaitLateQueue(function() {
+ assert(!rootGotCancelled);
+ assert.equal(8, successCalls);
+ assert(b.isCancelled());
+ assert(b1.isCancelled());
+ assert(b2.isCancelled());
+ assert(b3.isCancelled());
+ });
+ });
+ });
+
+ specify("3 branches, each have 3 branches, all grand children cancel", function() {
+ var successCalls = 0;
+ var rootGotCancelled = false;
+ var resolveRoot;
+ var root = new Promise(function(resolve, __, onCancel) {
+ onCancel(function() {
+ rootGotCancelled = true;
+ });
+ resolveRoot = resolve;
+ });
+
+ var a = root.then(function() {
+ successCalls++;
+ });
+
+ var a1 = a.then(function() {
+ successCalls++;
+ });
+
+ var a2 = a.then(function() {
+ successCalls++;
+ });
+
+ var a3 = a.then(function() {
+ successCalls++;
+ });
+
+ var b = root.then(function() {
+ successCalls++;
+ });
+
+ var b1 = b.then(function() {
+ successCalls++;
+ });
+
+ var b2 = b.then(function() {
+ successCalls++;
+ });
+
+ var b3 = b.then(function() {
+ successCalls++;
+ });
+
+ var c = root.then(function() {
+ successCalls++;
+ });
+
+ var c1 = c.then(function() {
+ successCalls++;
+ });
+
+ var c2 = c.then(function() {
+ successCalls++;
+ });
+
+ var c3 = c.then(function() {
+ successCalls++;
+ });
+
+ return awaitLateQueue(function() {
+ a1.cancel();
+ a2.cancel();
+ a3.cancel();
+ b1.cancel();
+ b2.cancel();
+ b3.cancel();
+ c1.cancel();
+ c2.cancel();
+ c3.cancel();
+ }).then(function() {
+ return awaitLateQueue(resolveRoot);
+ }).then(function() {
+ return awaitLateQueue(function() {
+ assert(rootGotCancelled);
+ assert.equal(0, successCalls);
+ assert(a.isCancelled());
+ assert(a1.isCancelled());
+ assert(a2.isCancelled());
+ assert(a3.isCancelled());
+ assert(b.isCancelled());
+ assert(b1.isCancelled());
+ assert(b2.isCancelled());
+ assert(b3.isCancelled());
+ assert(c.isCancelled());
+ assert(c1.isCancelled());
+ assert(c2.isCancelled());
+ assert(c3.isCancelled());
+ });
+ });
+ });
+});
+
+
+
+if (testUtils.isNodeJS) {
+ describe("issues", function() {
+ specify("cancels the promise chain within a domain GH963", function() {
+ var called = 0;
+ var thens = 0;
+ var resolveChain;
+ var Domain = require("domain");
+ var domain = Domain.create();
+
+ domain.enter();
+
+ var root = new Promise(function(resolve, reject, onCancel) {
+ resolveChain = resolve;
+ onCancel(function() {
+ called++;
+ });
+ }).then(function() {
+ thens++;
+ }).then(function() {
+ thens++;
+ }).then(function() {
+ thens++;
+ }).lastly(function() {
+ called++;
+ });
+
+ root.cancel();
+ resolveChain();
+ return awaitLateQueue(function() {
+ assert.equal(0, thens);
+ assert.equal(2, called);
+ domain.exit();
+ });
+ });
+ });
+
+ describe("GH926", function() {
+ var clear, set;
+ var clears = 0;
+ before(function() {
+ clears = 0;
+ set = setTimeout;
+ clear = clearTimeout;
+ setTimeout = function() {
+ return set.apply(this, arguments);
+ };
+ clearTimeout = function() {
+ clears++;
+ return clear.apply(this, arguments);
+ };
+ });
+
+ after(function() {
+ clears = 0;
+ setTimeout = set;
+ clearTimeout = clear;
+ });
+
+ specify("GH926", function() {
+ var calls = 0;
+ var p = new Promise(function(resolve, reject, onCancel) {
+ onCancel(function() { calls++; });
+ })
+ .timeout(10000000)
+ .lastly(function() {
+ calls++;
+ });
+
+ p.cancel();
+
+ return awaitLateQueue(function() {
+ assert.equal(2, calls);
+ assert.equal(1, clears);
+ });
+ });
+ });
+
+ describe("GH1000", function() {
+ var clear, set;
+ var clears = 0, sets = 0;
+ beforeEach(function() {
+ clears = 0;
+ sets = 0;
+ set = setTimeout;
+ clear = clearTimeout;
+ setTimeout = function() {
+ sets++;
+ return set.apply(this, arguments);
+ };
+ clearTimeout = function() {
+ clears++;
+ return clear.apply(this, arguments);
+ };
+ });
+
+ afterEach(function() {
+ clears = 0;
+ sets = 0;
+ setTimeout = set;
+ clearTimeout = clear;
+ });
+
+ specify("delay", function() {
+ var calls = 0,
+ never = 0;
+ var p = Promise
+ .delay(10000000)
+ .then(function () {
+ never++;
+ });
+
+ p.lastly(function() {
+ if (p.isCancelled()) {
+ calls++;
+ }
+ });
+
+ p.cancel();
+
+ return awaitLateQueue(function() {
+ assert.equal(0, never);
+ assert.equal(1, calls);
+ assert.equal(1, clears);
+ });
+ });
+
+ specify("delay with value", function() {
+ var calls = 0,
+ never = 0;
+
+ var p = Promise
+ .delay(10000000, true)
+ .then(function () {
+ never++;
+ });
+
+ p.lastly(function() {
+ if (p.isCancelled()) {
+ calls++;
+ }
+ });
+
+
+
+ return Promise.delay(10)
+ .then(function () {
+ p.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(0, never);
+ assert.equal(1, calls);
+ assert.equal(1, clears);
+ });
+ });
+ });
+
+ specify("cancel delay cancels inner promise", function() {
+ var calls = 0,
+ never = 0;
+ var pInner = Promise.delay(1000)
+ .then(function () {
+ never++;
+ });
+
+ pInner.lastly(function() {
+ if (pInner.isCancelled()) {
+ calls++;
+ }
+ });
+
+ var pOuter = Promise
+ .delay(10000000, pInner)
+ .then(function () {
+ never++;
+ });
+
+ pOuter.lastly(function() {
+ if (pOuter.isCancelled()) {
+ calls++;
+ }
+ });
+
+
+
+ pOuter.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(0, never);
+ assert.equal(2, calls);
+ });
+ });
+
+ specify("cancel inner promise cancels delay", function() {
+ var calls = 0,
+ never = 0;
+ var pInner = Promise.delay(1000)
+ .then(function () {
+ never++;
+ });
+
+ pInner.lastly(function() {
+ if (pInner.isCancelled()) {
+ calls++;
+ }
+ });
+
+ var pOuter = Promise
+ .delay(10000000, pInner)
+ .then(function () {
+ never++;
+ });
+
+ pOuter.lastly(function() {
+ if (pOuter.isCancelled()) {
+ calls++;
+ }
+ });
+
+
+
+ pInner.cancel();
+ return awaitLateQueue(function() {
+ assert.equal(0, never);
+ assert.equal(2, calls);
+ });
+ });
+ });
+}
diff --git a/test/mocha/catch_filter.js b/test/mocha/catch_filter.js
new file mode 100644
index 0000000..0199c9d
--- /dev/null
+++ b/test/mocha/catch_filter.js
@@ -0,0 +1,431 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+var CustomError = function(){};
+
+CustomError.prototype = new Error();
+
+var predicateFilter = function(e) {
+ return (/invalid/).test(e.message);
+}
+
+function BadError(msg) {
+ this.message = msg;
+ return this;
+}
+
+function predicatesUndefined(e) {
+ return e === void 0;
+}
+
+function predicatesPrimitiveString(e) {
+ return /^asd$/.test(e);
+}
+
+var token = {};
+var returnToken = function() {
+ return token;
+};
+
+var assertToken = function(val) {
+ assert.strictEqual(token, val);
+};
+
+describe("A promise handler that throws a TypeError must be caught", function() {
+
+ specify("in a middle.caught filter", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+ return a.promise.then(function(){
+ a.b.c.d()
+ }).then(assert.fail).caught(SyntaxError, function(e){
+ assert.fail();
+ }).caught(Promise.TypeError, returnToken)
+ .then(assertToken);
+ });
+
+
+ specify("in a generic.caught filter that comes first", function() {
+ var a = Promise.defer();
+
+ a.fulfill(3);
+ return a.promise.then(function(){
+ a.b.c.d()
+ }).then(assert.fail, returnToken).caught(SyntaxError, function(e){
+ assert.fail();
+ }).caught(Promise.TypeError, function(e){
+ assert.fail();
+ }).then(assertToken);
+
+ });
+
+ specify("in an explicitly generic.caught filter that comes first", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+
+ return a.promise.then(function(){
+ a.b.c.d()
+ })
+ .then(assert.fail)
+ .caught(Error, returnToken)
+ .caught(SyntaxError, assert.fail)
+ .caught(Promise.TypeError, assert.fail)
+ .then(assertToken);
+ });
+
+ specify("in a specific handler after thrown in generic", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+
+ return a.promise.then(function(){
+ a.b.c.d()
+ }).then(assert.fail, function(e){
+ throw e
+ }).caught(SyntaxError, assert.fail)
+ .then(assert.fail)
+ .caught(Promise.TypeError, returnToken)
+ .then(assertToken);
+
+
+ });
+
+
+ specify("in a multi-filter handler", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+
+ return a.promise.then(function(){
+ a.b.c.d()
+ })
+ .then(assert.fail)
+ .caught(SyntaxError, TypeError, returnToken)
+ .then(assertToken);
+ });
+
+
+ specify("in a specific handler after non-matching multi.caught handler", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+
+ return a.promise.then(function(){
+ a.b.c.d()
+ })
+ .then(assert.fail)
+ .caught(SyntaxError, CustomError, assert.fail)
+ .caught(Promise.TypeError, returnToken)
+ .then(assertToken)
+ });
+
+});
+
+
+describe("A promise handler that throws a custom error", function() {
+
+ specify("Is filtered if inheritance was done even remotely properly", function() {
+ var a = Promise.defer();
+ var b = new CustomError();
+ a.fulfill(3);
+ return a.promise.then(function(){
+ throw b;
+ })
+ .then(assert.fail)
+ .caught(SyntaxError, assert.fail)
+ .caught(Promise.TypeError, assert.fail)
+ .caught(CustomError, function(e){
+ assert.equal(e, b);
+ return token;
+ })
+ .then(assertToken);
+
+
+ });
+
+ specify("Is filtered along with built-in errors", function() {
+ var a = Promise.defer();
+ var b = new CustomError();
+ a.fulfill(3);
+ return a.promise.then(function(){
+ throw b;
+ })
+ .then(assert.fail)
+ .caught(Promise.TypeError, SyntaxError, CustomError, returnToken)
+ .caught(assert.fail)
+ .then(assertToken)
+ });
+
+ specify("Throws after matched type handler throws", function() {
+ var err = new Promise.TypeError();
+ var err2 = new Error();
+ return Promise.reject(err).caught(Promise.TypeError, function() {
+ throw err2;
+ }).then(assert.fail, function(e) {
+ assert.strictEqual(err2, e);
+ });
+ });
+});
+
+describe("A promise handler that throws a CustomError must be caught", function() {
+ specify("in a middle.caught filter", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+
+ return a.promise.then(function(){
+ throw new CustomError()
+ })
+ .caught(SyntaxError, assert.fail)
+ .caught(CustomError, returnToken)
+ .then(assertToken);
+ });
+
+
+ specify("in a generic.caught filter that comes first", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+
+ return a.promise.then(function(){
+ throw new CustomError()
+ }).then(assert.fail, returnToken)
+ .caught(SyntaxError, assert.fail)
+ .caught(CustomError, assert.fail)
+ .then(assertToken)
+ });
+
+ specify("in an explicitly generic.caught filter that comes first", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+
+ return a.promise.then(function(){
+ throw new CustomError()
+ })
+ .caught(Error, returnToken)
+ .caught(SyntaxError, assert.fail)
+ .caught(CustomError, assert.fail)
+ .then(assertToken);
+ });
+
+ specify("in a specific handler after thrown in generic", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+
+ return a.promise.then(function(){
+ throw new CustomError()
+ }).then(assert.fail, function(e){
+ throw e
+ })
+ .caught(SyntaxError, assert.fail)
+ .caught(CustomError, returnToken)
+ .then(assertToken);
+
+ });
+
+
+ specify("in a multi-filter handler", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+
+ return a.promise.then(function(){
+ throw new CustomError()
+ })
+ .caught(SyntaxError, CustomError, returnToken)
+ .then(assertToken)
+
+ });
+
+
+ specify("in a specific handler after non-matching multi.caught handler", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+
+ return a.promise.then(function(){
+ throw new CustomError()
+ })
+ .caught(SyntaxError, TypeError, assert.fail)
+ .caught(CustomError, returnToken)
+ .then(assertToken);
+ });
+
+});
+
+describe("A promise handler that is caught in a filter", function() {
+
+ specify("is continued normally after returning a promise in filter", function() {
+ var a = Promise.defer();
+ var c = Promise.defer();
+ var b = new CustomError();
+ a.fulfill(3);
+ setTimeout(function(){
+ c.fulfill(3);
+ }, 1);
+ return a.promise.then(function(){
+ throw b;
+ }).caught(SyntaxError, function(e){
+ assert.fail();
+ }).caught(Promise.TypeError, function(e){
+ assert.fail();
+ }).caught(CustomError, function(e){
+ assert.equal(e, b);
+ return c.promise.thenReturn(token);
+ }).then(assertToken, assert.fail, assert.fail);
+ });
+
+ specify("is continued normally after returning a promise in original handler", function() {
+ var a = Promise.defer();
+ var c = Promise.defer();
+ a.fulfill(3);
+ setTimeout(function(){
+ c.fulfill(3);
+ }, 1);
+ return a.promise.then(function(){
+ return c.promise;
+ }).caught(SyntaxError, function(e){
+ assert.fail();
+ }).caught(Promise.TypeError, function(e){
+ assert.fail();
+ }).caught(CustomError, function(e){
+ assert.fail();
+ });
+
+ });
+
+ specify("should throw type error for not passing function", function() {
+ try {
+ var a = Promise.reject(new Error("asd"));
+ a.caught(Promise.TypeError, "string");
+ throw new Error("fail");
+ } catch (e) {
+ if (e instanceof Promise.TypeError) {
+ return true;
+ } else {
+ throw new Error("fail");
+ }
+ }
+ });
+});
+
+describe("A promise handler with a predicate filter", function() {
+
+ specify("will catch a thrown thing matching the filter", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+ return a.promise.then(function(){
+ throw new Error("horrible invalid error string");
+ }).caught(predicateFilter, returnToken)
+ .then(assertToken);
+
+ });
+ specify("will NOT catch a thrown thing not matching the filter", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+ return a.promise.then(function(){
+ throw new Error("horrible valid error string");
+ }).caught(predicateFilter, function(e){
+ assert.fail();
+ }).then(assert.fail, function(){})
+ });
+
+ specify("will assert.fail when a predicate is a bad error class", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+ return a.promise.then(function(){
+ throw new Error("horrible custom error");
+ }).caught(BadError, function(e){
+ assert.fail();
+ }).then(assert.fail, returnToken)
+ .then(assertToken);
+
+ });
+
+ specify("will catch a thrown undefiend", function(){
+ var a = Promise.defer();
+ a.fulfill(3);
+ return a.promise.then(function(){
+ throw void 0;
+ }).caught(function(e) { return false }, function(e){
+ assert.fail();
+ }).caught(predicatesUndefined, returnToken)
+ .then(assertToken);
+
+ });
+
+ specify("will catch a thrown string", function(){
+ var a = Promise.defer();
+ a.fulfill(3);
+ return a.promise.then(function(){
+ throw "asd";
+ }).caught(function(e) { return false }, function(e){
+ assert.fail();
+ }).caught(predicatesPrimitiveString, returnToken)
+ .then(assertToken);
+
+ });
+
+ specify("will assert.fail when a predicate throws", function() {
+ var a = Promise.defer();
+ a.fulfill(3);
+ return a.promise.then(function(){
+ throw new CustomError("error happens");
+ }).then(assert.fail, function(e) { return e.f.g; }, function(e){
+ assert.fail();
+ }).caught(TypeError, returnToken)
+ .then(assertToken);
+ });
+});
+
+describe("object property predicates", function() {
+ specify("matches 1 property loosely", function() {
+ var e = new Error();
+ e.code = "3";
+ return Promise.resolve()
+ .then(function() {
+ throw e;
+ })
+ .caught({code: 3}, function(err) {
+ assert.equal(e, err);
+ });
+ });
+
+ specify("matches 2 properties loosely", function() {
+ var e = new Error();
+ e.code = "3";
+ e.code2 = "3";
+ return Promise.resolve()
+ .then(function() {
+ throw e;
+ })
+ .caught({code: 3, code2: 3}, function(err) {
+ assert.equal(e, err);
+ });
+ });
+
+ specify("doesn't match inequal properties", function() {
+ var e = new Error();
+ e.code = "3";
+ e.code2 = "4";
+ return Promise.resolve()
+ .then(function() {
+ throw e;
+ })
+ .caught({code: 3, code2: 3}, function(err) {
+ assert.fail();
+ })
+ .caught(function(v) {return v === e}, function() {});
+ });
+
+ specify("doesn't match primitives even if the property matches", function() {
+ var e = "string";
+ var length = e.length;
+ return Promise.resolve()
+ .then(function() {
+ throw e;
+ })
+ .caught({length: length}, function(err) {
+ assert.fail();
+ })
+ .caught(function(v) {return typeof v === "string"}, function(err) {
+ assert.equal(e, err);
+ });
+ });
+});
+
diff --git a/test/mocha/collections_thenables.js b/test/mocha/collections_thenables.js
new file mode 100644
index 0000000..7a79747
--- /dev/null
+++ b/test/mocha/collections_thenables.js
@@ -0,0 +1,355 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+
+function Thenable(value, defer, reject) {
+ this.value = value;
+ this.defer = !!defer;
+ this.reject = !!reject;
+}
+
+Thenable.prototype.then = function Then$then(onFulfilled, onRejected) {
+ var fn = this.reject ? onRejected : onFulfilled;
+ var value = this.value;
+
+ if (this.defer) {
+ setTimeout(function(){
+ fn(value);
+ }, 1)
+ }
+ else {
+ fn(value);
+ }
+
+};
+
+function testFulfillSync(name, cb, a1, a2, a3) {
+ var thenables = [new Thenable(1), new Thenable(2), new Thenable(3)];
+
+ specify("Promise." + name + " thenables that fulfill synchronously", function(){
+ return cb(Promise[name](thenables, a1, a2, a3));
+ });
+
+}
+
+function testFulfillAsync(name, cb, a1, a2, a3) {
+ var thenables = [new Thenable(1, true), new Thenable(2, true), new Thenable(3, true)];
+
+ specify("Promise." + name + " thenables that fulfill asynchronously", function(){
+ return cb(Promise[name](thenables, a1, a2, a3));
+ });
+}
+
+function testRejectSync(name, cb, a1, a2, a3) {
+ var thenables = [new Thenable(1, false, true), new Thenable(2, false, true), new Thenable(3, false, true)];
+
+ specify("Promise." + name + " thenables that reject synchronously", function(){
+ return cb(Promise[name](thenables, a1, a2, a3));
+ });
+
+}
+
+function testRejectAsync(name, cb, a1, a2, a3) {
+ var thenables = [new Thenable(1, true, true), new Thenable(2, true, true), new Thenable(3, true, true)];
+
+ specify("Promise." + name + " thenables that reject asynchronously", function(){
+ return cb(Promise[name](thenables, a1, a2, a3));
+ });
+}
+
+
+describe("Using collection methods with thenables", function() {
+ var name = "race";
+ testFulfillSync(name, function(promise) {
+ return promise.then(function(v){
+ assert(v === 1);
+ });
+ });
+ testFulfillAsync(name, function(promise) {
+ return promise.then(function(v){
+ assert(v === 1);
+ });
+ });
+ testRejectSync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert(v === 1);
+ });
+ });
+ testRejectAsync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert(v === 1);
+ });
+ });
+});
+
+describe("Using collection methods with thenables", function() {
+ var name = "all";
+ testFulfillSync(name, function(promise) {
+ return promise.then(function(v){
+ assert.deepEqual(v, [1,2,3]);
+ });
+ });
+ testFulfillAsync(name, function(promise) {
+ return promise.then(function(v){
+ assert.deepEqual(v, [1,2,3]);
+ });
+ });
+ testRejectSync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert(v === 1);
+ });
+ });
+ testRejectAsync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert(v === 1);
+ });
+ });
+});
+
+describe("Using collection methods with thenables", function() {
+ var name = "settle";
+ testFulfillSync(name, function(promise) {
+ return promise.then(function(v) {
+ assert(v[0].value() === 1)
+ assert(v[1].value() === 2)
+ assert(v[2].value() === 3)
+ });
+ });
+ testFulfillAsync(name, function(promise) {
+ return promise.then(function(v){
+ assert(v[0].value() === 1)
+ assert(v[1].value() === 2)
+ assert(v[2].value() === 3)
+ });
+ });
+ testRejectSync(name, function(promise) {
+ return promise.then(function(v){
+ assert(v[0].error() === 1)
+ assert(v[1].error() === 2)
+ assert(v[2].error() === 3)
+ });
+ });
+ testRejectAsync(name, function(promise) {
+ return promise.then(function(v){
+ assert(v[0].error() === 1)
+ assert(v[1].error() === 2)
+ assert(v[2].error() === 3)
+ });
+ });
+});
+
+describe("Using collection methods with thenables", function() {
+ var name = "any";
+ testFulfillSync(name, function(promise) {
+ return promise.then(function(v){
+ assert(v === 1);
+ });
+ });
+ testFulfillAsync(name, function(promise) {
+ return promise.then(function(v){
+ assert(v === 1);
+ });
+ });
+ testRejectSync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert(v[0] === 1);
+ assert(v[1] === 2);
+ assert(v[2] === 3);
+ });
+ });
+ testRejectAsync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert(v[0] === 1);
+ assert(v[1] === 2);
+ assert(v[2] === 3);
+ });
+ });
+});
+
+describe("Using collection methods with thenables", function() {
+ var name = "some";
+ testFulfillSync(name, function(promise) {
+ return promise.then(function(v){
+ assert(v[0] === 1);
+ });
+ }, 1);
+ testFulfillAsync(name, function(promise) {
+ return promise.then(function(v){
+ assert(v[0] === 1);
+ });
+ }, 1);
+ testRejectSync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert(v[0] === 1);
+ assert(v[1] === 2);
+ assert(v[2] === 3);
+ });
+ }, 1);
+ testRejectAsync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert(v[0] === 1);
+ assert(v[1] === 2);
+ assert(v[2] === 3);
+ });
+ }, 1);
+});
+
+describe("Using collection methods with thenables", function() {
+ var name = "join";
+ testFulfillSync(name, function(promise) {
+ return promise.then(function(v){
+ assert(v[0][0].value === 1);
+ assert(v[0][1].value === 2);
+ assert(v[0][2].value === 3);
+ assert(v[1] === 1);
+ assert(v[2] === 2);
+ assert(v[3] === 3);
+ });
+ }, 1, 2, 3);
+ testFulfillAsync(name, function(promise) {
+ return promise.then(function(v){
+ assert(v[0][0].value === 1);
+ assert(v[0][1].value === 2);
+ assert(v[0][2].value === 3);
+ assert(v[1] === 1);
+ assert(v[2] === 2);
+ assert(v[3] === 3);
+ });
+ }, 1, 2, 3);
+ testRejectSync(name, function(promise) {
+ return promise.then(function(v){
+ assert(v[0][0].value === 1);
+ assert(v[0][1].value === 2);
+ assert(v[0][2].value === 3);
+ assert(v[1] === 1);
+ assert(v[2] === 2);
+ assert(v[3] === 3);
+ });
+ }, 1, 2, 3);
+ testRejectAsync(name, function(promise) {
+ return promise.then(function(v){
+ assert(v[0][0].value === 1);
+ assert(v[0][1].value === 2);
+ assert(v[0][2].value === 3);
+ assert(v[1] === 1);
+ assert(v[2] === 2);
+ assert(v[3] === 3);
+ });
+ }, 1, 2, 3);
+});
+
+function mapper(v) {
+ return {
+ then: function(f) {
+ f(v*2);
+ }
+ };
+}
+function reducer(a, b) {
+ return a + b;
+}
+function filterer(v) {
+ return {
+ then: function(f) {
+ f(v > 0);
+ }
+ };
+}
+
+describe("Using collection methods with thenables", function() {
+ var name = "map";
+ testFulfillSync(name, function(promise) {
+ return promise.then(function(v){
+ assert.deepEqual(v, [2,4,6]);
+ });
+ }, mapper);
+ testFulfillAsync(name, function(promise) {
+ return promise.then(function(v){
+ assert.deepEqual(v, [2,4,6]);
+ });
+ }, mapper);
+ testRejectSync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert.deepEqual(v, 1);
+ });
+ }, mapper);
+ testRejectAsync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert.deepEqual(v, 1);
+ });
+ }, mapper);
+});
+
+describe("Using collection methods with thenables", function() {
+ var name = "reduce";
+ testFulfillSync(name, function(promise) {
+ return promise.then(function(v){
+ assert.deepEqual(v, 6);
+ });
+ }, reducer);
+ testFulfillAsync(name, function(promise) {
+ return promise.then(function(v){
+ assert.deepEqual(v, 6);
+ });
+ }, reducer);
+ testRejectSync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert.deepEqual(v, 1);
+ });
+ }, reducer);
+ testRejectAsync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert.deepEqual(v, 1);
+ });
+ }, reducer);
+});
+
+describe("Using collection methods with thenables", function() {
+ var name = "filter";
+ testFulfillSync(name, function(promise) {
+ return promise.then(function(v){
+ assert.deepEqual(v, [1,2,3]);
+ });
+ }, filterer);
+ testFulfillAsync(name, function(promise) {
+ return promise.then(function(v){
+ assert.deepEqual(v, [1,2,3]);
+ });
+ }, filterer);
+ testRejectSync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert.deepEqual(v, 1);
+ });
+ }, filterer);
+ testRejectAsync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert.deepEqual(v, 1);
+ });
+ }, filterer);
+});
+
+describe("Using collection methods with thenables", function() {
+ var name = "props";
+ testFulfillSync(name, function(promise) {
+ return promise.then(function(v){
+ assert.deepEqual(v, {0: 1, 1: 2, 2: 3});
+ });
+ }, filterer);
+ testFulfillAsync(name, function(promise) {
+ return promise.then(function(v){
+ assert.deepEqual(v, {0: 1, 1: 2, 2: 3});
+ });
+ }, filterer);
+ testRejectSync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert.deepEqual(v, 1);
+ });
+ }, filterer);
+ testRejectAsync(name, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert.deepEqual(v, 1);
+ });
+ }, filterer);
+});
diff --git a/test/mocha/constructor.js b/test/mocha/constructor.js
new file mode 100644
index 0000000..c9153fe
--- /dev/null
+++ b/test/mocha/constructor.js
@@ -0,0 +1,201 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+
+function fulfills(value, test) {
+ specify("immediately-fulfilled", function() {
+ return test(new Promise(function(resolve){
+ resolve(value);
+ }));
+ });
+
+ specify("eventually-fulfilled", function() {
+ return test(new Promise(function(resolve){
+ setTimeout(function(){
+ resolve(value);
+ }, 1);
+ }));
+ });
+};
+
+function rejects(reason, test) {
+ specify("immediately-rejected", function() {
+ return test(new Promise(function(resolve, reject){
+ reject(reason);
+ }));
+ });
+
+ specify("eventually-rejected", function() {
+ return test(new Promise(function(resolve, reject){
+ setTimeout(function(){
+ reject(reason);
+ }, 1);
+ }));
+ });
+};
+
+function testFulfilled(value, test) {
+ describe("immediate value", function(){
+ fulfills(value, test);
+ });
+
+ describe("already fulfilled promise for value", function(){
+ fulfills(Promise.resolve(value), test);
+ });
+
+ describe("immediately fulfilled promise for value", function(){
+ var a = Promise.defer();
+ fulfills(a.promise, test);
+ a.resolve(value);
+ });
+
+ describe("eventually fulfilled promise for value", function(){
+ var a = Promise.defer();
+ fulfills(a.promise, test);
+ setTimeout(function(){
+ a.resolve(value);
+ }, 1)
+
+ });
+
+ describe("synchronous thenable for value", function () {
+ fulfills({
+ then: function (f) {
+ f(value);
+ }
+ }, test);
+ });
+
+ describe("asynchronous thenable for value", function () {
+ fulfills({
+ then: function (f) {
+ setTimeout(function () {
+ f(value);
+ }, 1);
+ }
+ }, test);
+ });
+}
+
+function testRejected(reason, test) {
+ describe("immediate reason", function(){
+ rejects(reason, test);
+ });
+}
+
+
+describe("Promise constructor", function() {
+ it("should throw type error when called as function", function() {
+ try {
+ Promise(function(){});
+ }
+ catch (e) {
+ return;
+ }
+ assert.fail();
+ });
+
+ it("should throw type error when passed non-function", function() {
+ try {
+ new Promise({});
+ }
+ catch (e) {
+ return;
+ }
+ assert.fail();
+ });
+
+
+ var defaultThis = (function(){
+ return this;
+ })();
+
+ it("calls the resolver as a function", function(){
+ new Promise(function() {
+ assert(this === defaultThis);
+ });
+ });
+
+ it("passes arguments even if parameters are not defined", function(){
+ new Promise(function() {
+ assert(arguments.length === 2 ||Â arguments.length === 3);
+ });
+ });
+
+
+ it("should reject with any thrown error", function() {
+ var e = new Error();
+ return new Promise(function(){
+ throw e;
+ }).then(assert.fail, function(err) {
+ assert(err === e)
+ });
+ });
+
+ it("should call the resolver function synchronously", function() {
+ var e = new Error();
+ var a = 0;
+ new Promise(function(){
+ a = 1;
+ });
+ assert(a === 1);
+ });
+
+
+ describe("resolves the promise with the given object value", function() {
+ var value = {};
+ testFulfilled(value, function(promise) {
+ return promise.then(function(v){
+ assert(v === value);
+ });
+ });
+ });
+
+ describe("resolves the promise with the given primitive value", function() {
+ var value = 3;
+ testFulfilled(value, function(promise) {
+ return promise.then(function(v){
+ assert(v === value);
+ });
+ });
+ });
+
+ describe("resolves the promise with the given undefined value", function() {
+ var value = void 0;
+ testFulfilled(value, function(promise) {
+ return promise.then(function(v){
+ assert(v === value);
+ });
+ });
+ });
+
+ describe("rejects the promise with the given object reason", function() {
+ var reason = {};
+ testRejected(reason, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert(v === reason);
+ });
+ });
+ });
+
+ describe("rejects the promise with the given primitive reason", function() {
+ var reason = 3;
+ testRejected(reason, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert(v === reason);
+ });
+ });
+ });
+
+ describe("rejects the promise with the given undefined reason", function() {
+ var reason = void 0;
+ testRejected(reason, function(promise) {
+ return promise.then(assert.fail, function(v){
+ assert(v === reason);
+ });
+ });
+ });
+
+});
diff --git a/test/mocha/cycles.js b/test/mocha/cycles.js
new file mode 100644
index 0000000..ad26407
--- /dev/null
+++ b/test/mocha/cycles.js
@@ -0,0 +1,68 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+var helpers = require("./helpers/testThreeCases.js");
+var TypeError = Promise.TypeError;
+
+
+describe("Cyclical promises should throw TypeError when", function(){
+ describe("returning from fulfill", function() {
+ helpers.testFulfilled(3, function(promise) {
+ var self = promise.then(function() {
+ return self;
+ });
+
+ return self.then(assert.fail).caught(TypeError, testUtils.noop);
+ });
+ });
+
+ describe("returning from reject", function() {
+ helpers.testRejected(3, function(promise) {
+ var self = promise.then(assert.fail, function() {
+ return self;
+ });
+
+ return self.then(assert.fail).caught(TypeError, testUtils.noop);
+ });
+ });
+
+ describe("fulfill with itself when using a ", function() {
+ specify("deferred", function() {
+ var d = Promise.defer();
+ d.fulfill(d.promise);
+ return d.promise.then(assert.fail).caught(TypeError, testUtils.noop);
+ });
+
+ specify("constructor", function() {
+ var resolve;
+ var p = new Promise(function(r) {
+ resolve = r;
+ });
+ resolve(p);
+ return p.then(assert.fail).caught(TypeError, testUtils.noop);
+ });
+ });
+
+ describe("reject with itself when using a ", function() {
+ specify("deferred", function() {
+ var d = Promise.defer();
+ d.reject(d.promise);
+ return d.promise.then(assert.fail).caught(function(v) {
+ assert.equal(d.promise, v);
+ });
+ });
+
+ specify("constructor", function() {
+ var reject;
+ var p = new Promise(function(f, r) {
+ reject = r;
+ });
+ reject(p);
+ return p.then(assert.fail).caught(function(v) {
+ assert.equal(p, v);
+ });
+ });
+ });
+});
diff --git a/test/mocha/direct_resolving.js b/test/mocha/direct_resolving.js
new file mode 100644
index 0000000..11c752f
--- /dev/null
+++ b/test/mocha/direct_resolving.js
@@ -0,0 +1,257 @@
+"use strict";
+
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+var helpers = require("./helpers/testThreeCases.js");
+var TypeError = Promise.TypeError;
+
+function passthru(fn) {
+ return function() {
+ fn();
+ };
+}
+
+function wrap(fn, val) {
+ var args = [].slice.call(arguments, 1);
+ return function() {
+ return fn.apply(this, args);
+ }
+}
+
+function returnValue(value) {
+ helpers.testFulfilled(void 0, function(promise) {
+ return promise.thenReturn(value).then(function(v){
+ assert(v === value);
+ });
+ });
+}
+
+function throwValue(value) {
+ helpers.testFulfilled(void 0, function(promise) {
+ return promise.thenThrow(value).then(assert.fail, function(v) {
+ assert(v === value);
+ });
+ });
+}
+
+function returnThenable(thenable, expected) {
+ helpers.testFulfilled(void 0, function(promise) {
+ return promise.thenReturn(thenable).then(function(v){
+ assert(v === expected);
+ });
+ });
+}
+
+function returnThenableReject(thenable, expected) {
+ helpers.testFulfilled(void 0, function(promise) {
+ return promise.thenReturn(thenable).then(assert.fail, function(v){
+ assert(v === expected);
+ });
+ });
+}
+
+describe("thenReturn", function () {
+
+ describe("primitives", function() {
+ describe("null", wrap(returnValue, null));
+ describe("undefined", wrap(returnValue, void 0));
+ describe("string", wrap(returnValue, "asd"));
+ describe("number", wrap(returnValue, 3));
+ describe("boolean", wrap(returnValue, true));
+ });
+
+ describe("objects", function() {
+ describe("plain", wrap(returnValue, {}));
+ describe("function", wrap(returnValue, function(){}));
+ describe("built-in function", wrap(returnValue, Array));
+ describe("built-in object", wrap(returnValue, Math));
+ });
+
+ describe("thenables", function() {
+ describe("which fulfill", function() {
+ describe("immediately", wrap(returnThenable, {
+ then: function(f) {
+ f(10);
+ }
+ }, 10));
+ describe("eventually", wrap(returnThenable, {
+ then: function(f) {
+ setTimeout(function() {
+ f(10);
+ }, 1);
+ }
+ }, 10));
+ });
+ describe("which reject", function(){
+ describe("immediately", wrap(returnThenableReject, {
+ then: function(f, r) {
+ r(10);
+ }
+ }, 10));
+ describe("eventually", wrap(returnThenableReject, {
+ then: function(f, r) {
+ setTimeout(function() {
+ r(10);
+ }, 1);
+ }
+ }, 10));
+ });
+ });
+
+ describe("promises", function() {
+ describe("which fulfill", function() {
+ var d1 = Promise.defer();
+ var d2 = Promise.defer();
+ describe("already", wrap(returnThenable, Promise.resolve(10), 10));
+ describe("immediately", wrap(returnThenable, d1.promise, 10));
+ describe("eventually", wrap(returnThenable, d2.promise, 10));
+ d1.fulfill(10);
+ setTimeout(function(){
+ d2.fulfill(10);
+ }, 1);
+ });
+ describe("which reject", function() {
+ var d1 = Promise.defer();
+ var d2 = Promise.defer();
+ var alreadyRejected = Promise.reject(10);
+ alreadyRejected.then(assert.fail, function(){});
+ describe("already", wrap(returnThenableReject, alreadyRejected, 10));
+ describe("immediately", wrap(returnThenableReject, d1.promise, 10));
+ describe("eventually", wrap(returnThenableReject, d2.promise, 10));
+ d1.reject(10);
+ setTimeout(function(){
+ d2.reject(10);
+ }, 1);
+
+ d1.promise.caught(function(){});
+ d2.promise.caught(function(){});
+ });
+
+ });
+
+ describe("doesn't swallow errors", function() {
+ var e = {};
+ helpers.testRejected(e, function(promise){
+ return promise.thenReturn(3).then(assert.fail, function(err) {
+ assert(err = e);
+ });
+ });
+ });
+});
+
+describe("thenThrow", function () {
+
+ describe("primitives", function() {
+ describe("null", wrap(throwValue, null));
+ describe("undefined", wrap(throwValue, void 0));
+ describe("string", wrap(throwValue, "asd"));
+ describe("number", wrap(throwValue, 3));
+ describe("boolean", wrap(throwValue, true));
+ });
+
+ describe("objects", function() {
+ describe("plain", wrap(throwValue, {}));
+ describe("function", wrap(throwValue, function(){}));
+ describe("built-in function", wrap(throwValue, Array));
+ describe("built-in object", wrap(throwValue, Math));
+ });
+
+ describe("doesn't swallow errors", function() {
+ var e = {};
+ helpers.testRejected(e, function(promise){
+ return promise.thenThrow(3).then(assert.fail, function(err) {
+ assert(err = e);
+ });
+ });
+ });
+});
+
+describe("catchReturn", function () {
+
+ specify("catches and returns", function() {
+ return Promise.reject(3).catchReturn(1).then(function(val) {
+ assert.strictEqual(1, val);
+ });
+ });
+
+ specify("doesn't catch succesful promise", function() {
+ return Promise.resolve(3).catchReturn(1).then(function(val) {
+ assert.strictEqual(3, val);
+ });
+ });
+
+ specify("supports 1 error type", function() {
+ var e = new Error();
+ e.prop = 3;
+ var predicate = function(e) {return e.prop === 3};
+ return Promise.reject(e)
+ .catchReturn(TypeError, 1)
+ .catchReturn(predicate, 2)
+ .then(function(val) {
+ assert.strictEqual(2, val);
+ });
+ });
+});
+
+describe("catchThrow", function () {
+
+ specify("catches and throws", function() {
+ return Promise.reject(3).catchThrow(1).then(assert.fail, function(val) {
+ assert.strictEqual(1, val);
+ });
+ });
+
+ specify("doesn't catch succesful promise", function() {
+ return Promise.resolve(3).catchThrow(1).then(function(val) {
+ assert.strictEqual(3, val);
+ });
+ });
+
+ specify("supports 1 error type", function() {
+ var e = new Error();
+ e.prop = 3;
+ var predicate = function(e) {return e.prop === 3};
+ return Promise.reject(e)
+ .catchThrow(TypeError, 1)
+ .catchThrow(predicate, 2)
+ .then(assert.fail, function(val) {
+ assert.strictEqual(2, val);
+ });
+ });
+});
+
+
+describe("gh-627", function() {
+ it("can return undefined", function() {
+ return Promise.bind(42)
+ .thenReturn(undefined)
+ .then(function (value) {
+ assert.strictEqual(value, undefined);
+ });
+ });
+ it("can throw undefined", function() {
+ return Promise.bind(42)
+ .thenThrow(undefined)
+ .then(assert.fail, function (reason) {
+ assert.strictEqual(reason, undefined);
+ });
+ });
+
+ it("can catch return undefined", function() {
+ return Promise.bind(42).thenThrow(new Error())
+ .catchReturn()
+ .then(function (value) {
+ assert.strictEqual(value, undefined);
+ });
+ });
+ it("can catch throw undefined", function() {
+ return Promise.bind(42).thenThrow(new Error())
+ .catchThrow()
+ .then(assert.fail, function (reason) {
+ assert.strictEqual(reason, undefined);
+ });
+ });
+});
diff --git a/test/mocha/domain.js b/test/mocha/domain.js
new file mode 100644
index 0000000..f911ed2
--- /dev/null
+++ b/test/mocha/domain.js
@@ -0,0 +1,503 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+if (testUtils.isRecentNode) {
+ describe("domain", function() {
+ afterEach(function() {
+ Promise.onPossiblyUnhandledRejection(null);
+ });
+
+ specify("gh-148", function() {
+ var called = false;
+ var e = new Error("the error");
+ Promise.resolve(23).then(function(){called = true});
+ return testUtils.awaitDomainException(function(E) {
+ assert.equal(e, E);
+ assert(called);
+ }, function() {
+ Promise.onPossiblyUnhandledRejection(function(error) {
+ throw error;
+ });
+ var P = new Promise(function(_, reject){reject(e);});
+ });
+ });
+
+ specify("gh-521-promisified", function() {
+ return new Promise(function(resolve, reject) {
+ var domain = require('domain').create();
+ var data = {};
+
+ function callsBack(cb) {
+ setTimeout(function() {
+ cb(null, 1);
+ }, 1);
+ }
+
+ var promisified = Promise.promisify(callsBack);
+ domain.on('error', reject);
+ domain.run(function() {
+ process.domain.data = data;
+ resolve(promisified().then(function() {
+ assert.strictEqual(process.domain.data, data);
+ assert.strictEqual(process.domain, domain);
+ }));
+ });
+ });
+ });
+
+ specify("gh-521-constructed", function() {
+ return new Promise(function(resolve, reject) {
+ var domain = require('domain').create();
+ var data = {asd: 3};
+ domain.on('error', reject);
+ domain.run(function() {
+ var promise = new Promise(function(resolve) {
+ setTimeout(resolve, 1);
+ });
+
+ process.domain.data = data;
+ resolve(promise.then(function() {
+ assert.strictEqual(process.domain.data, data);
+ assert.strictEqual(process.domain, domain);
+ }));
+ });
+ });
+ });
+ });
+
+
+ describe("domain preservation" , function() {
+ var Domain = require("domain");
+
+ function createGroupDone(limit, next) {
+
+ return function done(err) {
+ if (err) {
+ return next(err);
+ }
+ if (--limit <= 0) {
+ next();
+ }
+ };
+ }
+
+ before(function () {
+ var current;
+ while((current = process.domain)) {
+ current.exit();
+ }
+ });
+
+ afterEach(function () {
+ var current;
+ while((current = process.domain)) {
+ current.exit();
+ }
+ });
+
+ it("should preserve empty domain and this function", function(done) {
+
+ var deferred = new Promise.defer();
+ var p = deferred.promise;
+
+ p.then(function shouldBeEmpty() {
+ assert.equal(false, !!process.domain);
+ }).bind({
+ ref: 'foo'
+ }).then(function shouldKeepThisAndEmptyDomain() {
+ assert.equal(false, !!process.domain);
+ assert.equal('foo', this.ref);
+ done();
+ }).caught(done);
+
+ deferred.resolve("ok");
+
+ });
+
+ it("should preserve empty domain, nodeify", function(done) {
+ done = createGroupDone(3, done);
+
+ var deferred = new Promise.defer();
+ var p = deferred.promise;
+
+ p.then(function shouldBeEmpty() {
+ assert.equal(false, !!process.domain);
+ done();
+ }).bind({
+ ref: 'foo'
+ }).then(function shouldKeepThisAndEmptyDomain() {
+ assert.equal(false, !!process.domain);
+ assert.equal('foo', this.ref);
+ done();
+ }).nodeify(function shouldKeepThisAndEmptyDomain() {
+ try {
+ assert.equal(false, !!process.domain);
+ assert.equal('foo', this.ref);
+ done();
+ } catch (err) {
+ done(err);
+ }
+ }).caught(done);
+
+ deferred.resolve("ok");
+
+ });
+
+ it("should preserve corresponding state of domain", function(done) {
+
+ done = createGroupDone(6, done);
+
+ var deferred = new Promise.defer();
+ var p = deferred.promise;
+
+ p.then(function shouldBeEmpty() {
+ assert.equal(false, !!process.domain);
+ done();
+ }).bind({
+ ref: 'foo'
+ }).then(function shouldKeepThisAndEmptyDomain() {
+ assert.equal(false, !!process.domain);
+ assert.equal('foo', this.ref);
+ done();
+ }).nodeify(function shouldKeepThisAndEmptyDomain() {
+ try {
+ assert.equal(false, !!process.domain);
+ assert.equal('foo', this.ref);
+ done();
+ } catch (err) { done(err); }
+ }).caught(done);
+
+ var domain = Domain.create();
+ domain.run(function () {
+ p.then(function shouldNoBeEmpty() {
+ assert.equal(domain, process.domain);
+ done();
+ }).bind({
+ ref: 'bar'
+ }).then(function shouldKeepThisAndDomain() {
+ assert.equal(domain, process.domain);
+ assert.equal('bar', this.ref);
+ done();
+ }).caught(done).nodeify(function shouldKeepThisAndDomain() {
+ try {
+ assert.equal(domain, process.domain);
+ assert.equal('bar', this.ref);
+ done();
+ } catch (err) { done(err); }
+ });
+ });
+
+ deferred.resolve("ok");
+
+ });
+
+ it('should preserve corresponding state of domain, complex', function(done) {
+
+ done = createGroupDone(9, done);
+
+ var deferred = new Promise.defer();
+ var p = deferred.promise;
+ p.then(function shouldBeEmpty() {
+ assert.equal(false, !!process.domain);
+ done();
+ }).bind({
+ ref: 'foo'
+ }).then(function shouldKeepThisAndEmptyDomain() {
+ assert.equal(false, !!process.domain);
+ assert.equal('foo', this.ref);
+ done();
+ }).caught(done).nodeify(function shouldKeepThisAndEmptyDomain() {
+ try {
+ assert.equal(false, !!process.domain);
+ assert.equal('foo', this.ref);
+ done();
+ }
+ catch (err) { done(err); }
+ }, done);
+
+ var domain1 = Domain.create();
+ domain1.run(function () {
+ p.then(function shouldNoBeEmpty() {
+ assert.equal(domain1, process.domain);
+ done();
+ }).bind({
+ ref: 'bar'
+ }).then(function shouldKeepThisAndDomain() {
+ assert.equal(domain1, process.domain);
+ assert.equal('bar', this.ref);
+ done();
+ }).caught(done).nodeify(function shouldKeepThisAndDomain() {
+ try {
+ assert.equal(domain1, process.domain);
+ assert.equal('bar', this.ref);
+ done();
+ }
+ catch (err) { done(err); }
+ }, done);
+ });
+
+ var domain2 = Domain.create();
+ domain2.run(function () {
+ p.then(function shouldNoBeEmpty() {
+ assert.equal(domain2, process.domain);
+ done();
+ }).bind({
+ ref: 'qaz'
+ }).then(function shouldKeepThisAndDomain() {
+ assert.equal(domain2, process.domain);
+ assert.equal('qaz', this.ref);
+ done();
+ }).caught(done).nodeify(function shouldKeepThisAndDomain() {
+ try {
+ assert.equal(domain2, process.domain);
+ assert.equal('qaz', this.ref);
+ done();
+ }
+ catch (err) { done(err); }
+ });
+ });
+
+ deferred.resolve("ok");
+
+ });
+
+ it('should preserve corresponding state of domain in reject', function(done) {
+
+ done = createGroupDone(4, done);
+
+ var deferred = new Promise.defer();
+ var p = deferred.promise;
+
+ p.bind({
+ ref: 'foo'
+ }).caught(function shouldKeepThisAndEmptyDomain() {
+ assert.equal(false, !!process.domain);
+ assert.equal('foo', this.ref);
+ done();
+ }).caught(done).nodeify(function shouldKeepThisAndEmptyDomain() {
+ try {
+ assert.equal(false, !!process.domain);
+ assert.equal('foo', this.ref);
+ done();
+ }
+ catch (err) { done(err); }
+ });
+
+ var domain = Domain.create();
+ domain.run(function () {
+ p.bind({
+ ref: 'bar'
+ }).caught(function shouldNoBeEmpty() {
+ assert.equal(true, !!process.domain);
+ assert.equal('bar', this.ref);
+ done();
+ }).caught(done).nodeify(function shouldKeepThisAndDomain(err) {
+ try {
+ assert.equal(true, !!process.domain);
+ assert.equal('bar', this.ref);
+ done();
+ }
+ catch (err) { done(err); }
+ }).caught(done);
+ });
+
+ deferred.reject('bad');
+
+ });
+
+ it('should preserve corresponding state of domain in reject, complex', function(done) {
+
+ done = createGroupDone(6, done);
+
+ var deferred = new Promise.defer();
+ var p = deferred.promise;
+ p.bind({
+ ref: 'foo'
+ }).caught(function shouldBeEmpty() {
+ assert.equal(false, !!process.domain);
+ assert.equal('foo', this.ref);
+ done();
+ }).caught(done).nodeify(function shouldKeepThisAndEmptyDomain() {
+ try {
+ assert.equal(false, !!process.domain);
+ assert.equal('foo', this.ref);
+ done();
+ }
+ catch (err) { done(err); }
+ });
+
+ var domain1 = Domain.create();
+ domain1.run(function () {
+ p.bind({
+ ref: 'bar'
+ }).caught(function shouldNoBeEmpty() {
+ assert.equal(domain1, process.domain);
+ assert.equal('bar', this.ref);
+ done();
+ }).caught(done).nodeify(function shouldKeepThisAndDomain() {
+ try {
+ assert.equal(domain1, process.domain);
+ assert.equal('bar', this.ref);
+ done();
+ }
+ catch (err) { done(err); }
+ });
+ });
+
+ var domain2 = Domain.create();
+ domain2.run(function () {
+ p.bind({
+ ref: 'qaz'
+ }).caught(function shouldNoBeEmpty() {
+ assert.equal(domain2, process.domain);
+ assert.equal('qaz', this.ref);
+ done();
+ }).caught(done).nodeify(function shouldKeepThisAndDomain() {
+ try {
+ assert.equal(domain2, process.domain);
+ assert.equal('qaz', this.ref);
+ done();
+ }
+ catch (err) { done(err); }
+ });
+ });
+
+ deferred.reject('bad');
+
+ });
+
+ it('should preserve domain when using .join', function() {
+ var domain = Domain.create();
+ var d1 = new Promise(function(resolve, reject) {
+ Domain.create().run(function() {
+ setTimeout(resolve, 1);
+ });
+ });
+ var d2 = new Promise(function(resolve, reject) {
+ Domain.create().run(function() {
+ setTimeout(resolve, 1);
+ });
+ });
+
+ return new Promise(function(resolve, reject) {
+ domain.on("error", reject);
+ domain.run(function() {
+ resolve(Promise.join(d1, d2, function() {
+ assert.strictEqual(domain, process.domain);
+ }));
+ });
+ });
+ });
+
+ it('should preserve domain when using .using', function() {
+ var domain = Domain.create();
+ var d1 = new Promise(function(resolve, reject) {
+ Domain.create().run(function() {
+ setTimeout(resolve, 1);
+ });
+ });
+ var d2 = new Promise(function(resolve, reject) {
+ Domain.create().run(function() {
+ setTimeout(resolve, 1);
+ });
+ });
+
+ return new Promise(function(resolve, reject) {
+ domain.on("error", reject);
+ domain.run(function() {
+ resolve(Promise.using(d1, d2, function() {
+ assert.strictEqual(domain, process.domain);
+ }));
+ });
+ });
+ });
+
+ it('should preserve domain when using .map', function() {
+ var domain = Domain.create();
+ var d1 = new Promise(function(resolve, reject) {
+ Domain.create().run(function() {
+ setTimeout(resolve, 1);
+ });
+ });
+
+ return new Promise(function(resolve, reject) {
+ domain.on("error", reject);
+ domain.run(function() {
+ resolve(Promise.map([d1, null, Promise.resolve(1), Promise.delay(1)], function() {
+ return process.domain;
+ }).then(function(domains) {
+ assert.deepEqual([domain, domain, domain, domain], domains);
+ assert.equal(process.domain, domain);
+ }));
+ });
+ });
+ });
+
+ it('should preserve domain when using .filter', function() {
+ var domain = Domain.create();
+ var d1 = new Promise(function(resolve, reject) {
+ Domain.create().run(function() {
+ setTimeout(resolve, 1);
+ });
+ });
+
+ return new Promise(function(resolve, reject) {
+ domain.on("error", reject);
+ domain.run(function() {
+ resolve(Promise.filter([d1, null, Promise.resolve(1), Promise.delay(1)], function() {
+ assert.equal(process.domain, domain);
+ }));
+ });
+ });
+ });
+
+ it('should preserve domain when using .reduce', function() {
+ var domain = Domain.create();
+ var d1 = new Promise(function(resolve, reject) {
+ Domain.create().run(function() {
+ setTimeout(resolve, 1);
+ });
+ });
+
+ return new Promise(function(resolve, reject) {
+ domain.on("error", reject);
+ domain.run(function() {
+ resolve(Promise.reduce([d1, null, Promise.resolve(1), Promise.delay(1)], function() {
+ assert.equal(process.domain, domain);
+ }));
+ });
+ });
+ });
+
+ it('should preserve domain when using .each', function() {
+ var domain = Domain.create();
+ var d1 = new Promise(function(resolve, reject) {
+ Domain.create().run(function() {
+ setTimeout(resolve, 1);
+ });
+ });
+
+ return new Promise(function(resolve, reject) {
+ domain.on("error", reject);
+ domain.run(function() {
+ resolve(Promise.each([d1, null, Promise.resolve(1), Promise.delay(1)], function() {
+ assert.equal(process.domain, domain);
+ }));
+ });
+ });
+ });
+
+ it("should not crash with already rejected promise", function() {
+ return new Promise(function(resolve) {
+ Domain.create().run(function() {
+ Promise.resolve(1).timeout(200).then(function() {
+ resolve();
+ })
+ });
+ });
+ })
+ });
+
+}
diff --git a/test/mocha/done.js b/test/mocha/done.js
new file mode 100644
index 0000000..5cf484e
--- /dev/null
+++ b/test/mocha/done.js
@@ -0,0 +1,131 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var isNodeJS = testUtils.isNodeJS;
+
+/*!
+ *
+Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+*/
+describe("done", function () {
+ var errCount = 0;
+ var safeError = new Error("safe_error");
+
+ describe("when the promise is fulfilled", function () {
+ describe("and the callback does not throw", function () {
+ it("should call the callback and return nothing", function () {
+ var called = false;
+ var promise = Promise.resolve();
+
+ var returnValue = promise.done(function () {
+ called = true;
+ });
+
+ return promise.lastly(function () {
+ assert.equal(called,true);
+ assert.equal(returnValue,undefined);
+ });
+ });
+ });
+
+ if (isNodeJS) {
+ describe("and the callback throws", function () {
+ it("should rethrow that error in the next turn and return nothing", function() {
+ var turn = 0;
+ process.nextTick(function () {
+ ++turn;
+ });
+
+ var returnValue = Promise.resolve().done(
+ function () {
+ throw safeError;
+ }
+ );
+
+ return testUtils.awaitProcessExit(function(e) {
+ assert.equal(turn,1);
+ assert.equal(returnValue,undefined);
+ });
+ });
+ });
+ }
+ });
+
+
+ describe("when the promise is rejected", function () {
+ describe("and the errback handles it", function () {
+ it("should call the errback and return nothing", function () {
+ var called = false;
+
+ var promise = Promise.reject("unsafe_error");
+
+ var returnValue = promise.done(
+ function () { },
+ function () {
+ called = true;
+ }
+ );
+
+ return promise.caught(function(){}).lastly(function () {
+ assert.equal(called,true);
+ assert.equal(returnValue,undefined);
+ });
+ });
+ });
+
+ if (isNodeJS) {
+ describe("and the errback throws", function () {
+ it("should rethrow that error in the next turn and return nothing", function() {
+ var turn = 0;
+ process.nextTick(function () {
+ ++turn;
+ });
+
+ var returnValue = Promise.reject("unsafe_error").done(
+ null,
+ function () {
+ throw safeError;
+ }
+ );
+ return testUtils.awaitProcessExit(function(e) {
+ assert.equal(turn,1);
+ assert.equal(returnValue,undefined);
+ });
+ });
+ });
+
+
+ describe("and there is no errback", function () {
+ it("should throw the original error in the next turn", function() {
+ var turn = 0;
+ process.nextTick(function () {
+ ++turn;
+ });
+
+ var returnValue = Promise.reject(safeError).done();
+ return testUtils.awaitProcessExit(function(e) {
+ assert.equal(turn,1);
+ assert.equal(returnValue,undefined);
+ });
+ });
+ });
+ }
+ });
+});
diff --git a/test/mocha/each.js b/test/mocha/each.js
new file mode 100644
index 0000000..ef86ec9
--- /dev/null
+++ b/test/mocha/each.js
@@ -0,0 +1,184 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+
+function promised(val) {
+ return new Promise(function(f) {
+ setTimeout(function() {
+ f(val);
+ }, 1);
+ });
+}
+
+function thenabled(val, arr) {
+ return {
+ then: function(f){
+ setTimeout(function() {
+ if (arr) arr.push(val);
+ f(val);
+ }, 1);
+ }
+ };
+}
+
+describe("Promise.each", function() {
+
+ it("should return the array's values mapped", function() {
+ var a = [promised(1), promised(2), promised(3)];
+ var b = [];
+ return Promise.resolve(a).mapSeries(function(val) {
+ b.push(3-val);
+ return val + 2;
+ }).then(function(ret) {
+ assert.deepEqual(ret, [3,4,5]);
+ assert.deepEqual(b, [2, 1, 0]);
+ });
+ });
+
+
+ it("takes value, index and length", function() {
+ var a = [promised(1), promised(2), promised(3)];
+ var b = [];
+ return Promise.resolve(a).each(function(value, index, length) {
+ b.push(value, index, length);
+ }).then(function(ret) {
+ assert.deepEqual(b, [1, 0, 3, 2, 1, 3, 3, 2, 3]);
+ });
+ });
+
+ it("waits for returned promise before proceeding next", function() {
+ var a = [promised(1), promised(2), promised(3)];
+ var b = [];
+ return Promise.resolve(a).each(function(value) {
+ b.push(value);
+ return Promise.delay(1).then(function(){
+ b.push(value*2);
+ });
+ }).then(function(ret) {
+ assert.deepEqual(b, [1,2,2,4,3,6]);
+ });
+ });
+
+ it("waits for returned thenable before proceeding next", function() {
+ var b = [1, 2, 3];
+ var a = [thenabled(1), thenabled(2), thenabled(3)];
+ return Promise.resolve(a).each(function(val) {
+ b.push(val * 50);
+ return thenabled(val * 500, b);
+ }).then(function(ret) {
+ assert.deepEqual(b, [1, 2, 3, 50, 500, 100, 1000, 150, 1500]);
+ });
+ });
+
+ it("doesnt iterate with an empty array", function() {
+ return Promise.each([], function(val) {
+ throw new Error();
+ }).then(function(ret) {
+ assert.deepEqual(ret, []);
+ });
+ });
+
+ it("iterates with an array of single item", function() {
+ var b = [];
+ return Promise.each([promised(1)], function(val) {
+ b.push(val);
+ return thenabled(val*2, b);
+ }).then(function(ret) {
+ assert.deepEqual(b, [1,2]);
+ });
+ });
+});
+
+describe("Promise.prototype.each", function() {
+
+ it("should return the array's values", function() {
+ var a = [promised(1), promised(2), promised(3)];
+ var b = [];
+ return Promise.resolve(a).each(function(val) {
+ b.push(3-val);
+ return val;
+ }).then(function(ret) {
+ assert.deepEqual(ret, [1,2,3]);
+ assert.deepEqual(b, [2, 1, 0]);
+ });
+ });
+
+
+ it("takes value, index and length", function() {
+ var a = [promised(1), promised(2), promised(3)];
+ var b = [];
+ return Promise.resolve(a).each(function(value, index, length) {
+ b.push(value, index, length);
+ }).then(function(ret) {
+ assert.deepEqual(b, [1, 0, 3, 2, 1, 3, 3, 2, 3]);
+ });
+ });
+
+ it("waits for returned promise before proceeding next", function() {
+ var a = [promised(1), promised(2), promised(3)];
+ var b = [];
+ return Promise.resolve(a).each(function(value) {
+ b.push(value);
+ return Promise.delay(1).then(function(){
+ b.push(value*2);
+ });
+ }).then(function(ret) {
+ assert.deepEqual(b, [1,2,2,4,3,6]);
+ });
+ });
+
+ it("waits for returned thenable before proceeding next", function() {
+ var b = [1, 2, 3];
+ var a = [thenabled(1), thenabled(2), thenabled(3)];
+ return Promise.resolve(a).each(function(val) {
+ b.push(val * 50);
+ return thenabled(val * 500, b);
+ }).then(function(ret) {
+ assert.deepEqual(b, [1, 2, 3, 50, 500, 100, 1000, 150, 1500]);
+ });
+ });
+
+ it("doesnt iterate with an empty array", function() {
+ return Promise.resolve([]).each(function(val) {
+ throw new Error();
+ }).then(function(ret) {
+ assert.deepEqual(ret, []);
+ });
+ });
+
+ it("iterates with an array of single item", function() {
+ var b = [];
+ return Promise.resolve([promised(1)]).each(function(val) {
+ b.push(val);
+ return thenabled(val*2, b);
+ }).then(function(ret) {
+ assert.deepEqual(b, [1,2]);
+ });
+ });
+});
+
+describe("mapSeries and each", function() {
+ it("is mixed", function() {
+ return Promise.mapSeries([1, 2, 3], function(value) {
+ return value * 2;
+ }).then(function(result) {
+ assert.deepEqual(result, [2, 4, 6]);
+ }).then(function() {
+ return Promise.each([1, 2, 3], function(value) {
+ return value * 2;
+ }).then(function(result) {
+ assert.deepEqual(result, [1, 2, 3]);
+ });
+ }).thenReturn([1, 2, 3]).mapSeries(function(value) {
+ return value * 2;
+ }).then(function(result) {
+ assert.deepEqual(result, [2, 4, 6]);
+ }).thenReturn([1, 2, 3]).each(function(value) {
+ return value * 2;
+ }).then(function(result) {
+ assert.deepEqual(result, [1, 2, 3]);
+ });
+ })
+});
diff --git a/test/mocha/error.js b/test/mocha/error.js
new file mode 100644
index 0000000..bd2e101
--- /dev/null
+++ b/test/mocha/error.js
@@ -0,0 +1,184 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+describe("Promise.prototype.error", function(){
+ describe("catches stuff originating from explicit rejections", function() {
+ specify("using callback", function() {
+ var e = new Promise.TypeError("sup");
+ function callsback(a, b, c, fn) {
+ fn(e);
+ }
+ callsback = Promise.promisify(callsback);
+
+ return callsback(1, 2, 3).error(function(err) {
+ assert(err === e);
+ });
+ });
+ });
+
+ describe("does not catch stuff originating from thrown errors", function() {
+ specify("using constructor", function() {
+ var e = new Error("sup");
+ return new Promise(function(resolve, reject) {
+ throw e;
+ }).error(function(err) {
+ assert.fail();
+ }).then(assert.fail, function(err){
+ assert(err === e);
+ });
+ });
+ specify("using thenable", function() {
+ var e = new Error("sup");
+ var thenable = {
+ then: function(resolve, reject){
+ reject(e);
+ }
+ };
+ return Promise.cast(thenable).error(function(err) {
+ console.error(err);
+ assert.fail();
+ }).then(assert.fail, function(err) {
+ assert(err === e);
+ });
+ });
+ specify("using callback", function() {
+ var e = new Error("sup");
+ function callsback(a, b, c, fn) {
+ throw e;
+ }
+ callsback = Promise.promisify(callsback);
+
+ return callsback(1, 2, 3).error(function(err) {
+ assert.fail();
+ }).then(assert.fail, function(err){
+ assert(err === e);
+ });
+ });
+ });
+})
+
+if (testUtils.ecmaScript5) {
+ describe("Weird errors", function() {
+ specify("unwritable stack", function() {
+ var e = new Error();
+ var stack = e.stack;
+ Object.defineProperty(e, "stack", {
+ configurable: true,
+ get: function() {return stack;},
+ set: function() {throw new Error("cannot set");}
+ });
+ return new Promise(function(_, reject) {
+ setTimeout(function() {
+ reject(e);
+ }, 1);
+ }).caught(function(err) {
+ assert.equal(e, err);
+ });
+ });
+ });
+}
+
+describe("Error constructors", function() {
+ describe("OperationalError", function() {
+ it("should work without new", function() {
+ var a = Promise.OperationalError("msg");
+ assert.strictEqual(a.message, "msg");
+ assert(a instanceof Error);
+ });
+
+ it("should work with new", function() {
+ var a = new Promise.OperationalError("msg");
+ assert.strictEqual(a.message, "msg");
+ assert(a instanceof Error);
+ });
+
+ it("should retain custom properties", function() {
+ var message;
+ var name;
+ function f(cb) {
+ var err = new Error("custom message");
+ message = err.message;
+ name = err.name;
+ err.code = "ENOENT";
+ err.path = "C:\\";
+ cb(err);
+ }
+ return Promise.promisify(f)().error(function(e) {
+ assert.strictEqual(e.message, message);
+ assert.strictEqual(e.name, name);
+ assert(e instanceof Promise.OperationalError);
+ assert.strictEqual(e.code, "ENOENT");
+ assert.strictEqual(e.path, "C:\\");
+ });
+ });
+ });
+
+ describe("CancellationError", function() {
+ it("should work without new", function() {
+ var a = Promise.CancellationError("msg");
+ assert.strictEqual(a.message, "msg");
+ assert(a instanceof Error);
+ });
+
+ it("should work with new", function() {
+ var a = new Promise.CancellationError("msg");
+ assert.strictEqual(a.message, "msg");
+ assert(a instanceof Error);
+ });
+ });
+
+ describe("TimeoutError", function() {
+ it("should work without new", function() {
+ var a = Promise.TimeoutError("msg");
+ assert.strictEqual(a.message, "msg");
+ assert(a instanceof Error);
+ });
+
+ it("should work with new", function() {
+ var a = new Promise.TimeoutError("msg");
+ assert.strictEqual(a.message, "msg");
+ assert(a instanceof Error);
+ });
+ });
+
+ describe("AggregateError", function() {
+ it("should work without new", function() {
+ var a = Promise.AggregateError("msg");
+ assert.strictEqual(a.message, "msg");
+ assert(a instanceof Error);
+ });
+
+ it("should work with new", function() {
+ var a = new Promise.AggregateError("msg");
+ assert.strictEqual(a.message, "msg");
+ assert(a instanceof Error);
+ });
+
+ if (testUtils.isNodeJS) {
+ it("should stringify without circular errors", function() {
+ var a = Promise.AggregateError();
+ a.push(new Error("1"));
+ a.push(new Error("2"));
+ a.push(new Error("3"));
+ a = a.toString();
+ assert(a.indexOf("Error: 1") >= 0);
+ assert(a.indexOf("Error: 2") >= 0);
+ assert(a.indexOf("Error: 3") >= 0);
+ });
+
+ it("should stringify with circular errors", function() {
+ var a = Promise.AggregateError();
+ a.push(new Error("1"));
+ a.push(a);
+ a.push(new Error("3"));
+ a = a.toString();
+ assert(a.indexOf("Error: 1") >= 0);
+ assert(a.indexOf("[Circular AggregateError]") >= 0);
+ assert(a.indexOf("Error: 3") >= 0);
+ });
+ }
+ });
+
+
+});
diff --git a/test/mocha/filter.js b/test/mocha/filter.js
new file mode 100644
index 0000000..724ae11
--- /dev/null
+++ b/test/mocha/filter.js
@@ -0,0 +1,120 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+
+
+describe("Promise filter", function() {
+
+ function ThrownError() {}
+
+
+ var arr = [1,2,3];
+
+ function assertArr(arr) {
+ assert(arr.length === 2);
+ assert(arr[0] === 1);
+ assert(arr[1] === 3);
+ }
+
+ function assertErr(e) {
+ assert(e instanceof ThrownError);
+ }
+
+ function assertFail() {
+ assert.fail();
+ }
+
+ describe("should accept eventual booleans", function() {
+ specify("immediately fulfilled", function() {
+ return Promise.filter(arr, function(v) {
+ return new Promise(function(r){
+ r(v !== 2);
+ });
+ }).then(assertArr);
+ });
+
+ specify("already fulfilled", function() {
+ return Promise.filter(arr, function(v) {
+ return Promise.resolve(v !== 2);
+ }).then(assertArr);
+ });
+
+ specify("eventually fulfilled", function() {
+ return Promise.filter(arr, function(v) {
+ return new Promise(function(r){
+ setTimeout(function(){
+ r(v !== 2);
+ }, 1);
+ });
+ }).then(assertArr);
+ });
+
+ specify("immediately rejected", function() {
+ return Promise.filter(arr, function(v) {
+ return new Promise(function(v, r){
+ r(new ThrownError());
+ });
+ }).then(assertFail, assertErr);
+ });
+ specify("already rejected", function() {
+ return Promise.filter(arr, function(v) {
+ return Promise.reject(new ThrownError());
+ }).then(assertFail, assertErr);
+ });
+ specify("eventually rejected", function() {
+ return Promise.filter(arr, function(v) {
+ return new Promise(function(v, r){
+ setTimeout(function(){
+ r(new ThrownError());
+ }, 1);
+ });
+ }).then(assertFail, assertErr);
+ });
+
+
+ specify("immediately fulfilled thenable", function() {
+ return Promise.filter(arr, function(v) {
+ return {
+ then: function(f, r) {
+ f(v !== 2);
+ }
+ };
+ }).then(assertArr);
+ });
+ specify("eventually fulfilled thenable", function() {
+ return Promise.filter(arr, function(v) {
+ return {
+ then: function(f, r) {
+ setTimeout(function(){
+ f(v !== 2);
+ }, 1);
+ }
+ };
+ }).then(assertArr);
+ });
+
+ specify("immediately rejected thenable", function() {
+ return Promise.filter(arr, function(v) {
+ return {
+ then: function(f, r) {
+ r(new ThrownError());
+ }
+ };
+ }).then(assertFail, assertErr);
+ });
+ specify("eventually rejected thenable", function() {
+ return Promise.filter(arr, function(v) {
+ return {
+ then: function(f, r) {
+ setTimeout(function(){
+ r(new ThrownError());
+ }, 1);
+ }
+ };
+ }).then(assertFail, assertErr);
+ });
+
+ });
+});
diff --git a/test/mocha/finally.js b/test/mocha/finally.js
new file mode 100644
index 0000000..1a34082
--- /dev/null
+++ b/test/mocha/finally.js
@@ -0,0 +1,274 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+/*!
+ *
+Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+*/
+
+describe("finally", function () {
+
+ var exception1 = new Error("boo!");
+ var exception2 = new Promise.TypeError("evil!");
+
+ describe("when nothing is passed", function() {
+ it("should do nothing", function() {
+ return Promise.resolve("foo")
+ .lastly()
+ .lastly()
+ .lastly()
+ .lastly()
+ .then(function(val){
+ assert(val === "foo");
+ })
+ });
+ });
+
+ describe("when the promise is fulfilled", function () {
+
+ it("should call the callback", function() {
+ var called = false;
+
+ return Promise.resolve("foo")
+ .lastly(function () {
+ called = true;
+ })
+ .then(function () {
+ assert.equal(called,true);
+ });
+ });
+
+ it("should fulfill with the original value", function() {
+ return Promise.resolve("foo")
+ .lastly(function () {
+ return "bar";
+ })
+ .then(function (result) {
+ assert.equal(result,"foo");
+ });
+ });
+
+ describe("when the callback returns a promise", function () {
+
+ describe("that is fulfilled", function () {
+ it("should fulfill with the original reason after that promise resolves", function() {
+ var promise = Promise.delay(1);
+
+ return Promise.resolve("foo")
+ .lastly(function () {
+ return promise;
+ })
+ .then(function (result) {
+ assert.equal(promise.isPending(),false);
+ assert.equal(result,"foo");
+ });
+ });
+ });
+
+ describe("that is rejected", function () {
+ it("should reject with this new rejection reason", function() {
+ return Promise.resolve("foo")
+ .lastly(function () {
+ return Promise.reject(exception1);
+ })
+ .then(function () {
+ assert.equal(false,true);
+ },
+ function (exception) {
+ assert.equal(exception,exception1);
+ });
+ });
+ });
+
+ });
+
+ describe("when the callback throws an exception", function () {
+ it("should reject with this new exception", function() {
+ return Promise.resolve("foo")
+ .lastly(function () {
+ throw exception1;
+ })
+ .then(function () {
+ assert.equal(false,true);
+ },
+ function (exception) {
+ assert.equal(exception,exception1);
+ });
+ });
+ });
+
+ });
+
+ describe("when the promise is rejected", function () {
+
+ it("should call the callback", function() {
+ var called = false;
+
+ return Promise.reject(exception1)
+ .lastly(function () {
+ called = true;
+ })
+ .then(function () {
+ assert.fail();
+ }, function () {
+ assert.equal(called,true);
+ });
+ });
+
+ it("should reject with the original reason", function() {
+ return Promise.reject(exception1)
+ .lastly(function () {
+ return "bar";
+ })
+ .then(function () {
+ assert.equal(false,true);
+ },
+ function (exception) {
+ assert.equal(exception,exception1);
+ });
+ });
+
+ describe("when the callback returns a promise", function () {
+
+ describe("that is fulfilled", function () {
+ it("should reject with the original reason after that promise resolves", function() {
+ var promise = Promise.delay(1);
+
+ return Promise.reject(exception1)
+ .lastly(function () {
+ return promise;
+ })
+ .then(function () {
+ assert.equal(false,true);
+ },
+ function (exception) {
+ assert.equal(exception,exception1);
+ assert.equal(promise.isPending(),false);
+ });
+ });
+ });
+
+ describe("that is rejected", function () {
+ it("should reject with the new reason", function() {
+ return Promise.reject(exception1)
+ .lastly(function () {
+ return Promise.reject(exception2);
+ })
+ .then(function () {
+ assert.equal(false,true);
+ },
+ function (exception) {
+ assert.equal(exception,exception2);
+ });
+ });
+ });
+
+ });
+
+ describe("when the callback throws an exception", function () {
+ it("should reject with this new exception", function() {
+ return Promise.reject(exception1)
+ .lastly(function () {
+ throw exception2;
+ })
+ .then(function () {
+ assert.equal(false,true);
+ },
+ function (exception) {
+ assert.equal(exception,exception2);
+ });
+ });
+ });
+
+ });
+
+ describe("when the callback returns a thenable", function () {
+
+ describe("that will fulfill", function () {
+ it("should reject with the original reason after that", function() {
+ var promise = {
+ then: function(fn) {
+ setTimeout(function(){
+ fn(15);
+ }, 1);
+ }
+ };
+
+ return Promise.reject(exception1)
+ .lastly(function () {
+ return promise;
+ })
+ .then(function () {
+ assert.equal(false,true);
+ },
+ function (exception) {
+ assert.equal(exception,exception1);
+ });
+ });
+ });
+
+ describe("that is rejected", function () {
+ it("should reject with the new reason", function() {
+ var promise = {
+ then: function(f, fn) {
+ setTimeout(function(){
+ fn(exception2);
+ }, 1);
+ }
+ };
+
+ return Promise.reject(exception1)
+ .lastly(function () {
+ return promise;
+ })
+ .then(function () {
+ assert.equal(false,true);
+ },
+ function (exception) {
+ assert.equal(exception,exception2);
+ });
+ });
+ it("should reject with the new primitive reason", function() {
+ var primitive = 3;
+ var promise = {
+ then: function(f, fn) {
+ setTimeout(function(){
+ fn(primitive);
+ }, 1);
+ }
+ };
+
+ return Promise.reject(exception1)
+ .lastly(function () {
+ return promise;
+ })
+ .then(function () {
+ assert.equal(false,true);
+ },
+ function (exception) {
+ assert.strictEqual(exception, primitive);
+ });
+ });
+ });
+
+
+
+ });
+});
diff --git a/test/mocha/following.js b/test/mocha/following.js
new file mode 100644
index 0000000..52ee19a
--- /dev/null
+++ b/test/mocha/following.js
@@ -0,0 +1,185 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+
+describe("Using deferreds", function() {
+ describe("a promise A that is following a promise B", function() {
+ specify("Must not react to fulfill/reject/ that don't come from promise B", function() {
+ var deferred = Promise.defer();
+ var promiseA = deferred.promise;
+ var promiseB = Promise.defer().promise;
+ var called = 0;
+ function incrementCalled() {
+ called++;
+ }
+
+ promiseA.then(
+ incrementCalled,
+ incrementCalled
+ );
+ deferred.fulfill(promiseB);
+
+ deferred.fulfill(1);
+ deferred.reject(1);
+ return Promise.delay(1).then(function() {
+ assert.equal(0, called);
+ assert.equal(promiseA.isPending(), true);
+ assert.equal(promiseB.isPending(), true);
+ });
+ });
+
+ specify("Must not start following another promise C", function() {
+ var deferred = Promise.defer();
+ var promiseA = deferred.promise;
+ var promiseB = Promise.defer().promise;
+ var deferredC = Promise.defer();
+ var promiseC = deferredC.promise;
+ var called = 0;
+ function incrementCalled() {
+ called++;
+ }
+
+
+ promiseA.then(
+ incrementCalled,
+ incrementCalled
+ );
+ deferred.fulfill(promiseB);
+ deferred.fulfill(promiseC);
+
+ deferredC.fulfill(1);
+ deferredC.reject(1);
+
+ return promiseC.then(function() {
+ assert.equal(called, 0);
+ assert.equal(promiseA.isPending(), true);
+ assert.equal(promiseB.isPending(), true);
+ assert.equal(promiseC.isPending(), false);
+ });
+ });
+
+ specify("Must react to fulfill/reject that come from promise B", function() {
+ var deferred = Promise.defer();
+ var promiseA = deferred.promise;
+ var deferredFollowee = Promise.defer();
+ var promiseB = deferredFollowee.promise;
+ var called = 0;
+ function incrementCalled() {
+ called++;
+ }
+ var c = 0;
+
+ var ret = promiseA.then(function(v){
+ c++;
+ assert.equal(c, 1);
+ assert.equal(called, 0);
+ }, incrementCalled);
+
+ deferred.fulfill(promiseB);
+
+
+ deferredFollowee.fulfill(1);
+ deferredFollowee.reject(1);
+ return ret;
+ });
+ });
+});
+
+describe("Using static immediate methods", function() {
+ describe("a promise A that is following a promise B", function() {
+ specify("Should be instantly fulfilled with Bs fulfillment value if B was fulfilled", function() {
+ var val = {};
+ var B = Promise.resolve(val);
+ var A = Promise.resolve(B);
+ assert.equal(A.value(), val);
+ assert.equal(A.value(), B.value());
+ });
+
+ specify("Should be instantly fulfilled with Bs parent fulfillment value if B was fulfilled with a parent", function() {
+ var val = {};
+ var parent = Promise.resolve(val);
+ var B = Promise.resolve(parent);
+ var A = Promise.resolve(B);
+ assert.equal(A.value(), val);
+ assert.equal(A.value(), B.value());
+ assert.equal(A.value(), parent.value());
+ });
+ });
+
+ describe("Rejecting a promise A with promise B", function(){
+ specify("Should reject promise A with B as reason ", function() {
+ var val = {};
+ var B = Promise.resolve(val);
+ var A = Promise.reject(B);
+ assert.equal(A.reason(), B);
+ A.then(assert.fail, function(){});
+ });
+ });
+});
+
+describe("Using constructor", function() {
+ describe("a promise A that is following a promise B", function() {
+ specify("Must not react to fulfill/reject that don't come from promise B", function() {
+ var resolveA;
+ var rejectA;
+ var promiseA = new Promise(function() {
+ resolveA = arguments[0];
+ rejectA = arguments[1];
+ });
+ var promiseB = new Promise(function(){});
+ var called = 0;
+ function incrementCalled() {
+ called++;
+ }
+
+ promiseA.then(
+ incrementCalled,
+ incrementCalled
+ );
+
+ resolveA(promiseB);
+ resolveA(1);
+ rejectA(1);
+ return Promise.delay(1).then(function() {
+ assert.equal(0, called);
+ assert.equal(promiseA.isPending(), true);
+ assert.equal(promiseB.isPending(), true);
+ });
+ });
+
+ specify("Must not start following another promise C", function() {
+ var resolveA;
+ var promiseA = new Promise(function(){
+ resolveA = arguments[0];
+ });
+ var promiseB = new Promise(function(){});
+ var resolveC, rejectC;
+ var promiseC = new Promise(function(){
+ resolveC = arguments[0];
+ rejectC = arguments[1];
+ });
+ var called = 0;
+ function incrementCalled() {
+ called++;
+ }
+
+ promiseA.then(
+ incrementCalled,
+ incrementCalled,
+ incrementCalled
+ );
+ resolveA(promiseB);
+ resolveA(promiseC);
+ resolveC(1);
+ rejectC(1);
+ return promiseC.then(function() {
+ assert.equal(called, 0);
+ assert.equal(promiseA.isPending(), true);
+ assert.equal(promiseB.isPending(), true);
+ assert.equal(promiseC.isPending(), false);
+ });
+ });
+ });
+});
diff --git a/test/mocha/generator.js b/test/mocha/generator.js
new file mode 100644
index 0000000..eac2d7c
--- /dev/null
+++ b/test/mocha/generator.js
@@ -0,0 +1,754 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var assertLongTrace = require("./helpers/assert_long_trace.js");
+var awaitLateQueue = testUtils.awaitLateQueue;
+
+function get(arg) {
+ return {
+ then: function(ful, rej) {
+ ful(arg)
+ }
+ }
+}
+
+function fail(arg) {
+ return {
+ then: function(ful, rej) {
+ rej(arg)
+ }
+ };
+}
+
+Promise.coroutine.addYieldHandler(function(yieldedValue) {
+ if (Array.isArray(yieldedValue)) return Promise.all(yieldedValue);
+});
+
+var error = new Error("asd");
+
+describe("yielding", function() {
+
+ specify("non-promise should throw", function() {
+ return Promise.coroutine(function*(){
+
+ var a = yield {};
+ assert.fail();
+ return 4;
+
+ })().then(assert.fail).caught(function(e){
+ assert(e instanceof TypeError);
+ });
+ });
+
+ specify("an array should implicitly Promise.all them", function() {
+ var a = Promise.defer();
+ var ap = a.promise;
+ var b = Promise.defer();
+ var bp = b.promise;
+ var c = Promise.defer();
+ var cp = c.promise;
+
+
+ setTimeout(function(){
+ a.fulfill(1);
+ b.fulfill(2);
+ c.fulfill(3);
+ }, 1);
+ return Promise.coroutine(function*(){
+ return yield [ap, bp, cp];
+ })().then(function(r) {
+ //.spread will also implicitly use .all() so that cannot be used here
+ var a = r[0]; var b = r[1]; var c = r[2];
+ assert(a === 1);
+ assert(b === 2);
+ assert(c === 3);
+ });
+ });
+
+ specify("non-promise should throw but be catchable", function() {
+
+ return Promise.coroutine(function*(){
+ try {
+ var a = yield {};
+ assert.fail();
+ }
+ catch (e){
+ assert(e instanceof TypeError);
+ return 4;
+ }
+
+ })().then(function(val){
+ assert.equal(val, 4);
+ });
+ });
+
+ specify("yielding a function should not call the function", function() {
+ let functionWasCalled = false;
+ return Promise.coroutine(function*(){
+ try {
+ yield (function() {functionWasCalled = true;});
+ }
+ catch(e){
+ assert(e instanceof TypeError);
+ assert.equal(functionWasCalled, false);
+ return 4;
+ }
+ })().then(function(val){
+ assert.equal(val, 4);
+ });
+ });
+});
+
+describe("thenables", function(){
+
+ specify("when they fulfill, the yielded value should be that fulfilled value", function(){
+
+ return Promise.coroutine(function*(){
+
+ var a = yield get(3);
+ assert.equal(a, 3);
+ return 4;
+
+ })().then(function(arg){
+ assert.equal(arg, 4);
+ });
+
+ });
+
+
+ specify("when they reject, and the generator doesn't have try.caught, it should immediately reject the promise", function(){
+
+ return Promise.coroutine(function*(){
+ var a = yield fail(error);
+ assert.fail();
+
+ })().then(assert.fail).then(assert.fail, function(e){
+ assert.equal(e, error);
+ });
+
+ });
+
+ specify("when they reject, and the generator has try.caught, it should continue working normally", function(){
+
+ return Promise.coroutine(function*(){
+ try {
+ var a = yield fail(error);
+ }
+ catch (e) {
+ return e;
+ }
+ assert.fail();
+
+ })().then(function(v){
+ assert.equal(v, error);
+ });
+
+ });
+
+ specify("when they fulfill but then throw, it should become rejection", function(){
+
+ return Promise.coroutine(function*(){
+ var a = yield get(3);
+ assert.equal(a, 3);
+ throw error;
+ })().then(assert.fail, function(e){
+ assert.equal(e, error);
+ });
+ });
+});
+
+describe("yield loop", function(){
+
+ specify("should work", function(){
+ return Promise.coroutine(function* () {
+ var a = [1,2,3,4,5];
+
+ for (var i = 0, len = a.length; i < len; ++i) {
+ a[i] = yield get(a[i] * 2);
+ }
+
+ return a;
+ })().then(function(arr){
+ assert.deepEqual([2,4,6,8,10], arr);
+ });
+ });
+
+ specify("inside yield should work", function(){
+ return Promise.coroutine(function *() {
+ var a = [1,2,3,4,5];
+
+ return yield Promise.all(a.map(function(v){
+ return Promise.coroutine(function *() {
+ return yield get(v*2);
+ })();
+ }));
+ })().then(function(arr){
+ assert.deepEqual([2,4,6,8,10], arr);
+ });
+ });
+
+ specify("with simple map should work", function(){
+ return Promise.coroutine(function *() {
+ var a = [1,2,3,4,5];
+
+ return yield Promise.map(a, function(v){
+ return Promise.cast(get(v*2));
+ });
+ })().then(function(arr){
+ assert.deepEqual([2,4,6,8,10], arr);
+ });
+ });
+
+});
+
+
+describe("Promise.coroutine", function() {
+ describe("thenables", function() {
+ specify("when they fulfill, the yielded value should be that fulfilled value", function(){
+
+ return Promise.coroutine(function*(){
+
+ var a = yield get(3);
+ assert.equal(a, 3);
+ return 4;
+
+ })().then(function(arg){
+ assert.equal(arg, 4);
+ });
+
+ });
+
+
+ specify("when they reject, and the generator doesn't have try.caught, it should immediately reject the promise", function(){
+
+ return Promise.coroutine(function*(){
+ var a = yield fail(error);
+ assert.fail();
+
+ })().then(assert.fail).then(assert.fail, function(e){
+ assert.equal(e, error);
+ });
+
+ });
+
+ specify("when they reject, and the generator has try.caught, it should continue working normally", function(){
+
+ return Promise.coroutine(function*(){
+ try {
+ var a = yield fail(error);
+ }
+ catch (e) {
+ return e;
+ }
+ assert.fail();
+
+ })().then(function(v){
+ assert.equal(v, error);
+ });
+
+ });
+
+ specify("when they fulfill but then throw, it should become rejection", function(){
+
+ return Promise.coroutine(function*(){
+ var a = yield get(3);
+ assert.equal(a, 3);
+ throw error;
+ })().then(assert.fail).then(assert.fail, function(e){
+ assert.equal(e, error);
+ });
+ });
+
+ specify("when they are already fulfilled, the yielded value should be returned asynchronously", function(){
+ var value;
+
+ var promise = Promise.coroutine(function*(){
+ yield Promise.resolve();
+ value = 2;
+ })();
+
+ value = 1;
+
+ return promise.then(function(){
+ assert.equal(value, 2);
+ });
+ });
+
+ specify("when they are already rejected, the yielded reason should be thrown asynchronously", function(){
+ var value;
+
+ var promise = Promise.coroutine(function*(){
+ try {
+ yield Promise.reject();
+ }
+ catch (e) {
+ value = 2;
+ }
+ })();
+
+ value = 1;
+
+ return promise.then(function(){
+ assert.equal(value, 2);
+ });
+ });
+ });
+
+ describe("yield loop", function(){
+
+ specify("should work", function(){
+ return Promise.coroutine(function* () {
+ var a = [1,2,3,4,5];
+
+ for (var i = 0, len = a.length; i < len; ++i) {
+ a[i] = yield get(a[i] * 2);
+ }
+
+ return a;
+ })().then(function(arr){
+ assert.deepEqual([2,4,6,8,10], arr);
+ });
+ });
+
+ specify("inside yield should work", function(){
+ return Promise.coroutine(function *() {
+ var a = [1,2,3,4,5];
+
+ return yield Promise.all(a.map(function(v){
+ return Promise.coroutine(function *() {
+ return yield get(v*2);
+ })();
+ }));
+ })().then(function(arr){
+ assert.deepEqual([2,4,6,8,10], arr);
+ });
+ });
+
+ specify("with simple map should work", function(){
+ return Promise.coroutine(function *() {
+ var a = [1,2,3,4,5];
+
+ return yield Promise.map(a, function(v){
+ return Promise.cast(get(v*2));
+ });
+ })().then(function(arr){
+ assert.deepEqual([2,4,6,8,10], arr);
+ });
+ });
+
+ });
+
+ describe("when using coroutine as a method", function(){
+
+ function MyClass() {
+ this.goblins = 3;
+ }
+
+ MyClass.prototype.spawnGoblins = Promise.coroutine(function* () {
+ this.goblins = yield get(this.goblins+1);
+ });
+
+
+ specify("generator function's receiver should be the instance too", function() {
+ var a = new MyClass();
+ var b = new MyClass();
+
+ return Promise.join(a.spawnGoblins().then(function(){
+ return a.spawnGoblins()
+ }), b.spawnGoblins()).then(function(){
+ assert.equal(a.goblins, 5);
+ assert.equal(b.goblins, 4);
+ });
+
+ });
+ });
+});
+
+describe("Spawn", function() {
+ it("should work", function() {
+ return Promise.spawn(function*() {
+ return yield Promise.resolve(1);
+ }).then(function(value) {
+ assert.strictEqual(value, 1);
+ });
+ });
+ it("should return rejected promise when passed non function", function() {
+ return Promise.spawn({}).then(assert.fail, function(err) {
+ assert(err instanceof Promise.TypeError);
+ });
+ });
+});
+
+describe("custom yield handlers", function() {
+ specify("should work with timers", function() {
+ var n = 0;
+ Promise.coroutine.addYieldHandler(function(v) {
+ if (typeof v === "number") {
+ n = 1;
+ return Promise.resolve(n);
+ }
+ });
+
+
+ return Promise.coroutine(function*() {
+ return yield 50;
+ })().then(function(value) {
+ assert.equal(value, 1);
+ assert.equal(n, 1);
+ });
+ });
+
+ var _ = (function()Â {
+ var promise = null;
+ Promise.coroutine.addYieldHandler(function(v) {
+ if (v === void 0 && promise != null)Â {
+ return promise;
+ }
+ promise = null;
+ });
+ return function() {
+ var cb;
+ promise = Promise.fromNode(function(callback) {
+ cb = callback;
+ });
+ return cb;
+ };
+ })();
+
+ specify("Should work with callbacks", function(){
+ var callbackApiFunction = function(a, b, c, cb) {
+ setTimeout(function(){
+ cb(null, [a, b, c]);
+ }, 1);
+ };
+
+ return Promise.coroutine(function*() {
+ return yield callbackApiFunction(1, 2, 3, _());
+ })().then(function(result) {
+ assert(result.length === 3);
+ assert(result[0] === 1);
+ assert(result[1] === 2);
+ assert(result[2] === 3);
+ });
+ });
+
+ specify("should work with thunks", function(){
+ Promise.coroutine.addYieldHandler(function(v) {
+ if (typeof v === "function") {
+ var cb;
+ var promise = Promise.fromNode(function(callback) {
+ cb = callback;
+ });
+ try { v(cb); } catch (e) { cb(e); }
+ return promise;
+ }
+ });
+
+ var thunk = function(a) {
+ return function(callback) {
+ setTimeout(function(){
+ callback(null, a*a);
+ }, 1);
+ };
+ };
+
+ return Promise.coroutine(function*() {
+ return yield thunk(4);
+ })().then(function(result) {
+ assert(result === 16);
+ });
+ });
+
+ specify("individual yield handler", function() {
+ var dummy = {};
+ var yieldHandler = function(value) {
+ if (value === dummy) return Promise.resolve(3);
+ };
+ var coro = Promise.coroutine(function* () {
+ return yield dummy;
+ }, {yieldHandler: yieldHandler});
+
+ return coro().then(function(result) {
+ assert(result === 3);
+ });
+ });
+
+ specify("yield handler that throws", function() {
+ var dummy = {};
+ var unreached = false;
+ var err = new Error();
+ var yieldHandler = function(value) {
+ if (value === dummy) throw err;
+ };
+
+ var coro = Promise.coroutine(function* () {
+ yield dummy;
+ unreached = true;
+ }, {yieldHandler: yieldHandler});
+
+ return coro().then(assert.fail, function(e) {
+ assert.strictEqual(e, err);
+ assert(!unreached);
+ });
+ });
+
+ specify("yield handler is not a function", function() {
+ try {
+ Promise.coroutine.addYieldHandler({});
+ } catch (e) {
+ assert(e instanceof Promise.TypeError);
+ return;
+ }
+ assert.fail();
+ });
+});
+
+if (Promise.hasLongStackTraces()) {
+ describe("Long stack traces with coroutines as context", function() {
+ it("1 level", function() {
+ return Promise.coroutine(function* () {
+ yield Promise.delay(10);
+ throw new Error();
+ })().then(assert.fail, function(e) {
+ assertLongTrace(e, 1+1, [2]);
+ });
+ });
+ it("4 levels", function() {
+ var secondLevel = Promise.coroutine(function* () {
+ yield thirdLevel();
+ });
+ var thirdLevel = Promise.coroutine(function* () {
+ yield fourthLevel();
+ });
+ var fourthLevel = Promise.coroutine(function* () {
+ throw new Error();
+ });
+
+ return Promise.coroutine(function* () {
+ yield secondLevel();
+ })().then(assert.fail, function(e) {
+ assertLongTrace(e, 4+1, [2, 2, 2, 2]);
+ });
+ });
+ });
+}
+
+describe("Cancellation with generators", function() {
+ specify("input immediately cancelled", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var unreached = 0;
+
+ var p = new Promise(function(_, __, onCancel) {});
+ p.cancel();
+
+ var asyncFunction = Promise.coroutine(function* () {
+ try {
+ yield p;
+ unreached++;
+ } catch(e) {
+ if (e === Promise.coroutine.returnSentinel) throw e;
+ unreached++;
+ } finally {
+ yield Promise.resolve();
+ finalled++;
+ }
+ unreached++;
+ });
+
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+
+ asyncFunction()
+ .then(reject, function(e)Â {
+ if(!(e instanceof Promise.CancellationError)) reject(new Error());
+ })
+ .lastly(function() {
+ finalled++;
+ resolve();
+ });
+
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(2, finalled);
+ assert.equal(0, cancelled);
+ assert.equal(0, unreached);
+ });
+ });
+ });
+
+ specify("input eventually cancelled", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var unreached = 0;
+
+ var p = new Promise(function(_, __, onCancel) {});
+ var asyncFunction = Promise.coroutine(function* () {
+ try {
+ yield p;
+ unreached++;
+ } catch(e) {
+ if (e === Promise.coroutine.returnSentinel) throw e;
+ unreached++;
+ } finally {
+ yield Promise.resolve();
+ finalled++;
+ }
+ unreached++;
+ });
+
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+
+ asyncFunction()
+ .then(reject, reject)
+ .lastly(function() {
+ finalled++;
+ resolve();
+ });
+
+ Promise.delay(1).then(function()Â {
+ p.cancel();
+ });
+
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(2, finalled);
+ assert.equal(0, cancelled);
+ assert.equal(0, unreached);
+ });
+ });
+ });
+
+ specify("output immediately cancelled", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var unreached = 0;
+
+ var p = new Promise(function(_, __, onCancel) {
+ onCancel(function() {
+ cancelled++;
+ });
+ }).lastly(function() {
+ finalled++;
+ });
+
+ var asyncFunction = Promise.coroutine(function* () {
+ try {
+ yield p;
+ unreached++;
+ } catch(e) {
+ if (e === Promise.coroutine.returnSentinel) throw e;
+ unreached++;
+ } finally {
+ yield Promise.resolve()
+ finalled++;
+ }
+ unreached++;
+ });
+
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+
+ var output = asyncFunction()
+ .then(reject, reject)
+ .lastly(function() {
+ finalled++;
+ resolve();
+ });
+
+ output.cancel();
+
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(3, finalled);
+ assert.equal(1, cancelled);
+ assert.equal(0, unreached);
+ });
+ });
+ });
+
+ specify("output eventually cancelled", function() {
+ var cancelled = 0;
+ var finalled = 0;
+ var unreached = 0;
+
+ var p = new Promise(function(_, __, onCancel) {
+ onCancel(function() {
+ cancelled++;
+ });
+ }).lastly(function() {
+ finalled++;
+ });
+
+ var asyncFunction = Promise.coroutine(function* () {
+ try {
+ yield p;
+ unreached++;
+ } catch(e) {
+ if (e === Promise.coroutine.returnSentinel) throw e;
+ unreached++;
+ } finally {
+ yield Promise.resolve()
+ finalled++;
+ }
+ unreached++;
+ });
+
+ var resolve, reject;
+ var result = new Promise(function()Â {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+
+ var output = asyncFunction()
+ .then(reject, reject)
+ .lastly(function() {
+ finalled++;
+ resolve();
+ });
+
+ Promise.delay(1).then(function() {
+ output.cancel();
+ });
+
+ return result.then(function() {
+ return awaitLateQueue(function() {
+ assert.equal(3, finalled);
+ assert.equal(1, cancelled);
+ assert.equal(0, unreached);
+ });
+ });
+ });
+
+
+ specify("finally block runs before finally handler", function(done) {
+ var finallyBlockCalled = false;
+ var asyncFn = Promise.coroutine(function* () {
+ try {
+ yield Promise.delay(100);
+ } finally {
+ yield Promise.delay(100);
+ finallyBlockCalled = true;
+ }
+ });
+ var p = asyncFn();
+ Promise.resolve().then(function() {
+ p.cancel();
+ });
+ p.finally(function() {
+ assert.ok(finallyBlockCalled, "finally block should have been called before finally handler");
+ done();
+ }).catch(done);
+ });
+});
diff --git a/test/mocha/get.js b/test/mocha/get.js
new file mode 100644
index 0000000..513f63f
--- /dev/null
+++ b/test/mocha/get.js
@@ -0,0 +1,83 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var join = Promise.join;
+
+describe("indexed getter", function() {
+ var p = Promise.resolve([0, 1, 2, 3, 4, 5, 7, 5,10]);
+ specify("gets positive index", function() {
+ var first = p.get(0);
+ var fourth = p.get(3);
+ var last = p.get(8);
+
+ return join(first, fourth, last, function(a, b, c) {
+ assert(a === 0);
+ assert(b === 3);
+ assert(c === 10);
+ });
+ });
+
+ specify("gets negative index", function() {
+ var last = p.get(-1);
+ var first = p.get(-20);
+
+ return join(last, first, function(a, b) {
+ assert.equal(a, 10);
+ assert.equal(b, 0);
+ });
+ });
+});
+
+describe("identifier getter", function() {
+ var p = Promise.resolve(new RegExp("", ""));
+ specify("gets property", function() {
+ var ci = p.get("ignoreCase");
+ var g = p.get("global");
+ var lastIndex = p.get("lastIndex");
+ var multiline = p.get("multiline");
+
+ return join(ci, g, lastIndex, multiline, function(ci, g, lastIndex, multiline) {
+ assert(ci === false);
+ assert(g === false);
+ assert(lastIndex === 0);
+ assert(multiline === false);
+ });
+ });
+
+ specify("gets same property", function() {
+ var o = {o: 1};
+ var o2 = {o: 2};
+ o = Promise.resolve(o).get("o");
+ o2 = Promise.resolve(o2).get("o");
+ return join(o, o2, function(one, two) {
+ assert.strictEqual(1, one);
+ assert.strictEqual(2, two);
+ });
+ });
+});
+
+describe("non identifier getters", function() {
+ var p = Promise.resolve({"-": "val"});
+ specify("get property", function() {
+ return p.get("-").then(function(val) {
+ assert(val === "val");
+ });
+ });
+
+ specify.skip("overflow cache", function() {
+ var a = new Array(1024);
+ var o = {};
+ for (var i = 0; i < a.length; ++i) {
+ a[i] = "get" + i;
+ o["get" + i] = i*2;
+ }
+ var b = Promise.map(a, function(item, index) {
+ return Promise.resolve(o).get(a[index]);
+ }).filter(function(value, index) {
+ return value === index * 2;
+ }).then(function(values) {
+ assert.strictEqual(values.length, a.length);
+ });
+ return b;
+ });
+});
diff --git a/test/mocha/getNewLibraryCopy.js b/test/mocha/getNewLibraryCopy.js
new file mode 100644
index 0000000..16eb10d
--- /dev/null
+++ b/test/mocha/getNewLibraryCopy.js
@@ -0,0 +1,30 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+describe("Promise.getNewLibraryCopy", function() {
+ it("should return an independent copy of Bluebird library", function() {
+ var Promise2 = Promise.getNewLibraryCopy();
+ Promise2.x = 123;
+
+ assert.equal(typeof Promise2.prototype.then, "function");
+ assert.notEqual(Promise2, Promise);
+
+ assert.equal(Promise2.x, 123);
+ assert.notEqual(Promise.x, 123);
+ });
+ it("should return copy of Bluebird library with its own getNewLibraryCopy method", function() {
+ var Promise2 = Promise.getNewLibraryCopy();
+ var Promise3 = Promise2.getNewLibraryCopy();
+ Promise3.x = 123;
+
+ assert.equal(typeof Promise3.prototype.then, "function");
+ assert.notEqual(Promise3, Promise);
+ assert.notEqual(Promise3, Promise2);
+
+ assert.equal(Promise3.x, 123);
+ assert.notEqual(Promise.x, 123);
+ assert.notEqual(Promise2.x, 123);
+ });
+});
diff --git a/test/mocha/github-2xx-76.js b/test/mocha/github-2xx-76.js
new file mode 100644
index 0000000..4dbd66a
--- /dev/null
+++ b/test/mocha/github-2xx-76.js
@@ -0,0 +1,21 @@
+"use strict";
+
+
+Promise.longStackTraces();
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var isNodeJS = testUtils.isNodeJS;
+
+
+if (isNodeJS) {
+ describe("github276 - stack trace cleaner", function(){
+ specify("message with newline and a$_b should not be removed", function(){
+ return Promise.resolve(1).then(function() {
+ throw new Error("Blah\n a$_b");
+ }).then(assert.fail, function(e) {
+ var msg = e.stack.split('\n')[1]
+ assert(msg.indexOf('a$_b') >= 0, 'message should contain a$_b');
+ });
+ });
+ });
+}
diff --git a/test/mocha/github-3.6.4.js b/test/mocha/github-3.6.4.js
new file mode 100644
index 0000000..2d0c1e3
--- /dev/null
+++ b/test/mocha/github-3.6.4.js
@@ -0,0 +1,40 @@
+"use strict";
+
+var assert = require("assert");
+var Promise = adapter;
+
+function defer() {
+ var resolve, reject;
+ var promise = new Promise(function() {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+ return {
+ resolve: resolve,
+ reject: reject,
+ promise: promise
+ };
+}
+
+
+describe("github-364", function() {
+ specify("resolve between thens", function(done) {
+ var calls = 0;
+ var def = defer();
+
+ def.promise.then(function() {
+ calls++
+ });
+ def.resolve();
+ def.promise.then(function() {
+ calls++
+ }).then(function() {
+ calls++
+ }).then(function() {
+ Promise.delay(11).then(function() {
+ assert.equal(calls, 3);
+ done();
+ });
+ });
+ });
+});
diff --git a/test/mocha/github-3.7.3.js b/test/mocha/github-3.7.3.js
new file mode 100644
index 0000000..99b2b4b
--- /dev/null
+++ b/test/mocha/github-3.7.3.js
@@ -0,0 +1,14 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var Promise = adapter;
+
+describe("github-373", function() {
+ specify("unhandled unsuccessful Promise.join should result in correct error being reported", function() {
+ var err = new Error("test");
+ var rejected = Promise.delay(1).thenThrow(err);
+ Promise.join(rejected, Promise.resolve(1), function(){});
+ return testUtils.onUnhandledSucceed(err);
+ });
+});
diff --git a/test/mocha/github-4.1.7.js b/test/mocha/github-4.1.7.js
new file mode 100644
index 0000000..8f152a3
--- /dev/null
+++ b/test/mocha/github-4.1.7.js
@@ -0,0 +1,46 @@
+var Promise = adapter;
+var assert = require("assert");
+
+describe("Github #417", function() {
+
+ specify("minimal repro", function() {
+ var promise = new Promise(function(resolve) {
+ resolve(Promise.resolve().then(function() {
+ return new Promise(function(resolve) {
+ setTimeout(resolve, 1);
+ });
+ }));
+ });
+
+ return promise.then(function() {
+ assert(promise.isResolved());
+ });
+ });
+
+ specify("original repro", function() {
+ var called = 0;
+ var bar = Promise.method(function() {
+ return Promise.bind(this)
+ .then(Promise.method(function() {
+ called++;
+ }));
+ });
+
+ var foo = Promise.method(function() {
+ return Promise.bind(this)
+ .then(Promise.method(function() {
+ return bar();
+ }))
+ .bind(this)
+ .lastly(Promise.method(function() {
+ called++;
+ }));
+ });
+
+ return foo().then(function() {
+ called++;
+ assert.equal(3, called);
+ });
+ });
+});
+
diff --git a/test/mocha/github36.js b/test/mocha/github36.js
new file mode 100644
index 0000000..dd2094c
--- /dev/null
+++ b/test/mocha/github36.js
@@ -0,0 +1,59 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+
+describe("github36", function(){
+ specify("should work", function() {
+ return new Promise(function(resolve, reject) {
+ var called = 0;
+ var donecalled = false;
+ var _d = Promise.defer();
+
+ _d.resolve()
+
+ var f1 = function() {
+ return _d.promise.then(function() {
+ return true;
+ })
+ }
+
+ var f2 = function() {
+ var d1 = Promise.defer()
+
+ setTimeout(function() {
+ d1.resolve()
+ }, 1)
+
+ return d1.promise.then(function() {
+ return _d.promise.then(function() {
+ })
+ });
+ }
+
+ var f3 = function() {
+ called++;
+ if (called > 15) {
+ return resolve();
+ }
+ var promise = f1().then(function() {
+ f2()
+ .then(function() {
+ f3()
+ })
+ })
+
+ promise.lastly(function() {
+ setTimeout(function() {
+ f3()
+ }, 1)
+ })
+
+ }
+
+ f3();
+ });
+ });
+});
+
diff --git a/test/mocha/helpers/assert_long_trace.js b/test/mocha/helpers/assert_long_trace.js
new file mode 100644
index 0000000..e1fb571
--- /dev/null
+++ b/test/mocha/helpers/assert_long_trace.js
@@ -0,0 +1,86 @@
+var assert = require("assert");
+
+function assertLongTrace(error, expectedJumpCount, expectedFramesForJumpsMap) {
+ var envFramePattern = /(?:\(node.js:|\(module.js:|\(timers.js:|\bcheckTimers\b|\bdrainQueue\b|\btimerLoop\b|\b_onImmediate\b|\b_immediateCallback\b)/;
+ var stack = error.stack.split("\n");
+ var frameLinePattern = /(^\s+at|@|\s+\(No stack trace\))/;
+ var previousEventPattern = /^From previous event/;
+ var firstLine;
+ for (var i = 0; i < stack.length; ++i) {
+ if (previousEventPattern.test(stack[i])) {
+ throw new Error("From previous event before any frames");
+ }
+ if (frameLinePattern.test(stack[i])) {
+ firstLine = i - 1;
+ break;
+ }
+ }
+ var prev = stack[firstLine - 1];
+ var jumpCount = 1;
+ var jumpIndex = 0;
+ var currentJumpFramesCount = 0;
+ var envFramesCount = 0;
+ for (var i = firstLine; i < stack.length; ++i) {
+ var line = stack[i];
+ if (previousEventPattern.test(line)) {
+ var jumpContainsOnlyEnvFrames =
+ currentJumpFramesCount === 0 && envFramesCount > 0;
+ if (!jumpContainsOnlyEnvFrames) {
+ if (previousEventPattern.test(prev)) {
+ throw new Error("2 consecutive From previous events");
+ }
+ if (jumpIndex < expectedFramesForJumpsMap.length) {
+ var expectedFrames = expectedFramesForJumpsMap[jumpIndex];
+ var expectedMessage = typeof expectedFrames === "number"
+ ? (expectedFrames + "")
+ : (expectedFrames[0] + "-" + expectedFrames[1]);
+ var message = "Expected " + (jumpIndex+1) + "th jump to contain " +
+ expectedMessage + " frames " +
+ "but it contains " + currentJumpFramesCount + " frames";
+ if (typeof expectedFrames === "number") {
+ assert(expectedFrames === currentJumpFramesCount, message);
+ } else {
+ assert(expectedFrames[0] <= currentJumpFramesCount &&
+ currentJumpFramesCount <= expectedFrames[1],
+ message);
+ }
+ }
+ jumpCount++;
+ jumpIndex++;
+ }
+ currentJumpFramesCount = 0;
+ envFramesCount = 0;
+ } else if (frameLinePattern.test(line)) {
+ if (envFramePattern.test(line)) {
+ envFramesCount++;
+ } else {
+ currentJumpFramesCount++;
+ }
+ }
+ prev = line;
+ }
+ assert.strictEqual(
+ previousEventPattern.test(stack[stack.length - 1]), false,
+ "The last line cannot be 'From previous event:'");
+ if (typeof expectedJumpCount === "number") {
+ assert.strictEqual(expectedJumpCount, jumpCount, "Expected " +
+ expectedJumpCount + " jumps but saw " + jumpCount + " jumps");
+ } else {
+ assert(expectedJumpCount[0] <= jumpCount &&
+ jumpCount <= expectedJumpCount[1],
+ "Expected " +
+ expectedJumpCount[0] + "-" + expectedJumpCount[1] +
+ " jumps but saw " + jumpCount + " jumps"
+ );
+ }
+
+ if (jumpCount > (expectedFramesForJumpsMap.length + 1)) {
+ throw new Error("All jumps except the last one require an "+
+ "expected frame count. " +
+ "Got expected frame counts for only " +
+ expectedFramesForJumpsMap.length + " while " + (jumpCount-1) +
+ " was expected");
+ }
+
+}
+module.exports = assertLongTrace;
diff --git a/test/mocha/helpers/bluebird0_7_0.js b/test/mocha/helpers/bluebird0_7_0.js
new file mode 100644
index 0000000..f8abe3d
--- /dev/null
+++ b/test/mocha/helpers/bluebird0_7_0.js
@@ -0,0 +1,2272 @@
+/* jshint -W014, -W116, -W106 */
+/* global process, unreachable */
+/**
+ * @preserve Copyright (c) 2013 Petka Antonov
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+(function( global, Function, Array, Error, Object ) {"use strict";
+
+var ASSERT = (function(){/* jshint -W014, -W116 */
+ var AssertionError = (function() {
+ function AssertionError( a ) {
+ this.constructor$( a );
+ this.message = a;
+ this.name = "AssertionError";
+ }
+ AssertionError.prototype = new Error();
+ AssertionError.prototype.constructor = AssertionError;
+ AssertionError.prototype.constructor$ = Error;
+ return AssertionError;
+ })();
+
+ return function assert( boolExpr, message ) {
+ if( boolExpr === true ) return;
+
+ var ret = new AssertionError( message );
+ if( Error.captureStackTrace ) {
+ Error.captureStackTrace( ret, assert );
+ }
+ if( console && console.error ) {
+ console.error( ret.stack + "" );
+ }
+ throw ret;
+
+ };
+})();
+
+var errorObj = {e: {}};
+var rescape = /[\r\n\u2028\u2029']/g;
+
+var replacer = function( ch ) {
+ return "\\u" + (("0000") +
+ (ch.charCodeAt(0).toString(16))).slice(-4);
+};
+
+function safeToEmbedString( str ) {
+ return str.replace( rescape, replacer );
+}
+
+function tryCatch1( fn, receiver, arg ) {
+ ASSERT(((typeof fn) === "function"),
+ "typeof fn === \u0022function\u0022");
+ try {
+ return fn.call( receiver, arg );
+ }
+ catch( e ) {
+ errorObj.e = e;
+ return errorObj;
+ }
+}
+
+function tryCatch2( fn, receiver, arg, arg2 ) {
+ ASSERT(((typeof fn) === "function"),
+ "typeof fn === \u0022function\u0022");
+ try {
+ return fn.call( receiver, arg, arg2 );
+ }
+ catch( e ) {
+ errorObj.e = e;
+ return errorObj;
+ }
+}
+
+function tryCatchApply( fn, args ) {
+ ASSERT(((typeof fn) === "function"),
+ "typeof fn === \u0022function\u0022");
+ try {
+ return fn.apply( void 0, args );
+ }
+ catch( e ) {
+ errorObj.e = e;
+ return errorObj;
+ }
+}
+
+var create = Object.create || function( proto ) {
+ function F(){}
+ F.prototype = proto;
+ return new F();
+};
+
+function makeNodePromisified( callback, receiver ) {
+
+ function getCall(count) {
+ var args = new Array(count);
+ for( var i = 0, len = args.length; i < len; ++i ) {
+ args[i] = "a" + (i+1);
+ }
+ var comma = count > 0 ? "," : "";
+ return ( receiver === void 0
+ ? "callback("+args.join(",")+ comma +" fn);"
+ : "callback.call(receiver, "+args.join(",") + comma + " fn);" ) +
+ "break;";
+ }
+
+ return new Function("Promise", "callback", "receiver",
+ "return function promisified( a1, a2, a3, a4, a5 ) {\"use strict\";" +
+ "var len = arguments.length;" +
+ "var resolver = Promise.pending( promisified );" +
+ "" +
+ "var fn = function fn( err, value ) {" +
+ "if( err ) {" +
+ "resolver.reject( err );" +
+ "}" +
+ "else {" +
+ "resolver.fulfill( value );" +
+ "}" +
+ "};" +
+ "switch( len ) {" +
+ "case 5:" + getCall(5) +
+ "case 4:" + getCall(4) +
+ "case 3:" + getCall(3) +
+ "case 2:" + getCall(2) +
+ "case 1:" + getCall(1) +
+ "case 0:" + getCall(0) +
+ "default: callback.apply(receiver, arguments); break;" +
+ "}" +
+ "return resolver.promise;" +
+ "" +
+ "};"
+ )(Promise, callback, receiver);
+}
+
+
+var inherits = function( Child, Parent ) {
+ var hasProp = {}.hasOwnProperty;
+
+ function T() {
+ this.constructor = Child;
+ this.constructor$ = Parent;
+ for (var propertyName in Parent.prototype) {
+ if (hasProp.call( Parent.prototype, propertyName) &&
+ propertyName.charAt(propertyName.length-1) !== "$"
+ ) {
+ this[ propertyName + "$"] = Parent.prototype[propertyName];
+ }
+ }
+ }
+ T.prototype = Parent.prototype;
+ Child.prototype = new T();
+ return Child.prototype;
+};
+
+function subError( constructorName, nameProperty, defaultMessage ) {
+ defaultMessage = safeToEmbedString("" + defaultMessage );
+ nameProperty = safeToEmbedString("" + nameProperty );
+
+ return new Function("create", "'use strict';\n" +
+ constructorName + ".prototype = create(Error.prototype);" +
+ constructorName + ".prototype.constructor = "+constructorName+";" +
+ "function "+constructorName+"(msg){" +
+ "if( Error.captureStackTrace ) {" +
+ "Error.captureStackTrace(this, this.constructor);" +
+ "}" +
+ "Error.call(this, msg);" +
+ "this.message = typeof msg === 'string'" +
+ "? msg" +
+ ": '"+defaultMessage+"';" +
+ "this.name = '"+nameProperty+"';" +
+ "} return "+constructorName+";")(create);
+}
+
+if( typeof global.TypeError === "undefined" ) {
+ global.TypeError = subError( "TypeError", "TypeError" );
+}
+var CancellationError = subError( "CancellationError",
+ "Cancel", "cancellation error" );
+var TimeoutError = subError( "TimeoutError", "Timeout", "timeout error" );
+
+
+var CapturedTrace = (function() {
+
+var rignore = new RegExp(
+ "\\b(?:Promise(?:Array)?\\$_\\w+|tryCatch(?:1|2|Apply)|setTimeout" +
+ "|makeNodePromisified|processImmediate|nextTick" +
+ "|Async\\$\\w+)\\b"
+);
+
+var rtraceline = null;
+var formatStack = null;
+
+function CapturedTrace( ignoreUntil ) {
+ ASSERT(((typeof ignoreUntil) === "function"),
+ "typeof ignoreUntil === \u0022function\u0022");
+ ASSERT(((typeof ignoreUntil.name) === "string"),
+ "typeof ignoreUntil.name === \u0022string\u0022");
+ ASSERT((ignoreUntil.name.length > 0),
+ "ignoreUntil.name.length > 0");
+ this.captureStackTrace( ignoreUntil );
+}
+var method = inherits( CapturedTrace, Error );
+
+method.captureStackTrace =
+function CapturedTrace$captureStackTrace( ignoreUntil ) {
+ captureStackTrace( this, ignoreUntil );
+};
+
+CapturedTrace.possiblyUnhandledRejection =
+function CapturedTrace$PossiblyUnhandledRejection( reason ) {
+ if( typeof console === "object" ) {
+ var stack = reason.stack;
+ var message = "Possibly unhandled " + formatStack( stack, reason );
+ if( typeof console.error === "function" ) {
+ console.error( message );
+ }
+ else if( typeof console.log === "function" ) {
+ console.log( message );
+ }
+ }
+};
+
+CapturedTrace.combine = function CapturedTrace$Combine( current, prev ) {
+ var curLast = current.length - 1;
+ for( var i = prev.length - 1; i >= 0; --i ) {
+ var line = prev[i];
+ if( current[ curLast ] === line ) {
+ current.pop();
+ curLast--;
+ }
+ else {
+ break;
+ }
+ }
+ var lines = current.concat( prev );
+
+ var ret = [];
+
+
+ for( var i = 0, len = lines.length; i < len; ++i ) {
+ if( rignore.test( lines[i] ) ||
+ ( i > 0 && !rtraceline.test( lines[i] ) )
+ ) {
+ continue;
+ }
+ ret.push( lines[i] );
+ }
+ return ret;
+};
+
+CapturedTrace.isSupported = function CapturedTrace$IsSupported() {
+ return typeof captureStackTrace === "function";
+};
+
+var captureStackTrace = (function stackDetection() {
+ if( typeof Error.stackTraceLimit === "number" &&
+ typeof Error.captureStackTrace === "function" ) {
+ rtraceline = /^\s*at\s*/;
+ formatStack = function( stack, error ) {
+ return ( typeof stack === "string" )
+ ? stack
+ : error.name + ". " + error.message;
+ };
+ return Error.captureStackTrace;
+ }
+ var err = new Error();
+
+ if( typeof err.stack === "string" &&
+ typeof "".startsWith === "function" &&
+ ( err.stack.startsWith("stackDetection@")) &&
+ stackDetection.name === "stackDetection" ) {
+
+ Object.defineProperty( Error, "stackTraceLimit", {
+ writable: true,
+ enumerable: false,
+ configurable: false,
+ value: 25
+ });
+ rtraceline = /@/;
+ var rline = /[@\n]/;
+
+ formatStack = function( stack, error ) {
+ return ( typeof stack === "string" )
+ ? ( error.name + ". " + error.message + "\n" + stack )
+ : ( error.name + ". " + error.message );
+ };
+
+ return function captureStackTrace(o, fn) {
+ var name = fn.name;
+ var stack = new Error().stack;
+ var split = stack.split( rline );
+ var i, len = split.length;
+ for (i = 0; i < len; i += 2) {
+ if (split[i] === name) {
+ break;
+ }
+ }
+ ASSERT(((i + 2) < split.length),
+ "i + 2 < split.length");
+ split = split.slice(i + 2);
+ len = split.length - 2;
+ var ret = "";
+ for (i = 0; i < len; i += 2) {
+ ret += split[i];
+ ret += "@";
+ ret += split[i + 1];
+ ret += "\n";
+ }
+ o.stack = ret;
+ };
+ }
+ else {
+ return null;
+ }
+})();
+
+return CapturedTrace;})();
+
+function GetterCache(){}
+function FunctionCache(){}
+
+
+var getterCache = new GetterCache(),
+ functionCache = new FunctionCache(),
+ rjsident = /^[a-zA-Z$_][a-zA-Z0-9$_]*$/,
+ rkeyword = new RegExp(
+ "^(?:__proto__|undefined|NaN|Infinity|this|false|true|null|eval|" +
+ "arguments|break|case|catch|continue|debugger|default|delete|do|" +
+ "else|finally|for|function|if|in|instanceof|new|return|switch|th" +
+ "row|try|typeof|var|void|while|with|class|enum|export|extends|im" +
+ "port|super|implements|interface|let|package|private|protected|pu" +
+ "blic|static|yield)$"
+ ),
+ hasProp = {}.hasOwnProperty;
+
+
+
+function isJsIdentifier( val ) {
+ return rjsident.test(val) &&
+ !rkeyword.test(val);
+}
+
+function formatPropertyRead( val ) {
+ if( isJsIdentifier(val) ) {
+ return "." + val;
+ }
+ else {
+ return "['"+safeToEmbedString(val)+"']";
+ }
+}
+
+function getGetter( propertyName ) {
+ if( hasProp.call( getterCache, propertyName ) ) {
+ return getterCache[propertyName];
+ }
+ var fn = new Function("obj", "return obj"+
+ formatPropertyRead(""+propertyName)
+ +";");
+ getterCache[propertyName] = fn;
+ return fn;
+}
+
+function getFunction( propertyName ) {
+ if( hasProp.call( functionCache, propertyName ) ) {
+ return functionCache[propertyName];
+ }
+ var fn = new Function("obj", "return obj"+
+ formatPropertyRead(""+propertyName)
+ +"();");
+ functionCache[propertyName] = fn;
+ return fn;
+}
+var Async = (function() {
+
+var deferFn = typeof process !== "undefined" ?
+ ( typeof global.setImmediate !== "undefined"
+ ? function( fn ){
+ global.setImmediate( fn );
+ }
+ : function( fn ) {
+ process.nextTick( fn );
+ }
+
+ ) :
+ ( typeof setTimeout !== "undefined"
+ ? function( fn ) {
+ setTimeout( fn, 4 );
+ }
+ : function( fn ) {
+ fn();
+ }
+) ;
+
+function Async() {
+ this._isTickUsed = false;
+ this._length = 0;
+ this._backupBuffer = [];
+ var functionBuffer = this._functionBuffer =
+ new Array( 1000 * 3 );
+ var self = this;
+ this.consumeFunctionBuffer = function Async$consumeFunctionBuffer() {
+ self._consumeFunctionBuffer();
+ };
+
+ for( var i = 0, len = functionBuffer.length; i < len; ++i ) {
+ functionBuffer[i] = void 0;
+ }
+}
+var method = Async.prototype;
+
+method.haveItemsQueued = function Async$haveItemsQueued() {
+ return this._length > 0;
+};
+
+method.invokeLater = function Async$invokeLater( fn, receiver, arg ) {
+ ASSERT(((typeof fn) === "function"),
+ "typeof fn === \u0022function\u0022");
+ ASSERT((arguments.length === 3),
+ "arguments.length === 3");
+ this._backupBuffer.push( fn, receiver, arg );
+ if( !this._isTickUsed ) {
+ deferFn( this.consumeFunctionBuffer );
+ this._isTickUsed = true;
+ }
+};
+
+method.invoke = function Async$invoke( fn, receiver, arg ) {
+ ASSERT(((typeof fn) === "function"),
+ "typeof fn === \u0022function\u0022");
+ ASSERT((arguments.length === 3),
+ "arguments.length === 3");
+ var functionBuffer = this._functionBuffer,
+ len = functionBuffer.length,
+ length = this._length;
+
+ if( length === len ) {
+ functionBuffer.push( fn, receiver, arg );
+ }
+ else {
+ ASSERT((length < len),
+ "length < len");
+ functionBuffer[ length + 0 ] = fn;
+ functionBuffer[ length + 1 ] = receiver;
+ functionBuffer[ length + 2 ] = arg;
+ }
+ this._length = length + 3;
+
+ if( !this._isTickUsed ) {
+ deferFn( this.consumeFunctionBuffer );
+ this._isTickUsed = true;
+ }
+};
+
+method._consumeFunctionBuffer = function Async$_consumeFunctionBuffer() {
+ var functionBuffer = this._functionBuffer;
+ ASSERT(this._isTickUsed,
+ "this._isTickUsed");
+ for( var i = 0; i < this._length; i += 3 ) {
+ functionBuffer[ i + 0 ].call(
+ functionBuffer[ i + 1 ],
+ functionBuffer[ i + 2 ] );
+
+ functionBuffer[ i + 0 ] =
+ functionBuffer[ i + 1 ] =
+ functionBuffer[ i + 2 ] =
+ void 0;
+ }
+ this._reset();
+ if( this._backupBuffer.length ) {
+ var buffer = this._backupBuffer;
+ for( var i = 0; i < buffer.length; i+= 3 ) {
+ buffer[ i + 0 ].call(
+ buffer[ i + 1 ] ,
+ buffer[ i + 2 ] );
+ }
+ buffer.length = 0;
+ }
+};
+
+method._reset = function Async$_reset() {
+ this._isTickUsed = false;
+ this._length = 0;
+};
+
+
+return Async;})();
+
+var async = new Async();
+var Thenable = (function() {
+
+function Thenable() {
+ this.errorObj = errorObj;
+ this.__id__ = 0;
+ this.treshold = 1000;
+ this.thenableCache = new Array( this.treshold );
+ this.promiseCache = new Array( this.treshold );
+ this._compactQueued = false;
+}
+var method = Thenable.prototype;
+
+method.couldBe = function Thenable$couldBe( ret ) {
+ if( ret === null ||
+ typeof ret === "undefined" ||
+ typeof ret === "string" ||
+ typeof ret === "boolean" ||
+ typeof ret === "number" ) {
+ return false;
+ }
+ var id = ret.__id_$thenable__;
+ if( typeof id === "number" &&
+ this.thenableCache[id] !== void 0 ) {
+ return true;
+ }
+ return ("then" in ret);
+};
+
+method.is = function Thenable$is( ret, ref ) {
+ var id = ret.__id_$thenable__;
+ if( typeof id === "number" &&
+ this.thenableCache[id] !== void 0 ) {
+ ref.ref = this.thenableCache[id];
+ ref.promise = this.promiseCache[id];
+ return true;
+ }
+ return this._thenableSlowCase( ret, ref );
+};
+
+method.addCache = function Thenable$_addCache( thenable, promise ) {
+ var id = this.__id__;
+ this.__id__ = id + 1;
+ var descriptor = this._descriptor( id );
+ Object.defineProperty( thenable, "__id_$thenable__", descriptor );
+ this.thenableCache[id] = thenable;
+ this.promiseCache[id] = promise;
+ ASSERT((this.thenableCache[thenable.__id_$thenable__] === thenable),
+ "this.thenableCache[ thenable.__id_$thenable__ ] === thenable");
+ if( this.thenableCache.length > this.treshold &&
+ !this._compactQueued) {
+ this._compactQueued = true;
+ async.invokeLater( this._compactCache, this, void 0 );
+ }
+};
+
+method.deleteCache = function Thenable$deleteCache( thenable ) {
+ var id = thenable.__id_$thenable__;
+ ASSERT(((typeof id) === "number"),
+ "typeof id === \u0022number\u0022");
+ ASSERT(((id | 0) === id),
+ "(id | 0) === id");
+ if( id === -1 ) {
+ return;
+ }
+ ASSERT((id > -1),
+ "id > -1");
+ ASSERT((id < this.__id__),
+ "id < this.__id__");
+ ASSERT((this.thenableCache[id] === thenable),
+ "this.thenableCache[id] === thenable");
+ this.thenableCache[id] = void 0;
+ this.promiseCache[id] = void 0;
+ thenable.__id_$thenable__ = -1;};
+
+var descriptor = {
+ value: 0,
+ enumerable: false,
+ writable: true,
+ configurable: true
+};
+method._descriptor = function Thenable$_descriptor( id ) {
+ descriptor.value = id;
+ return descriptor;
+};
+
+method._compactCache = function Thenable$_compactCache() {
+ var arr = this.thenableCache;
+ var promiseArr = this.promiseCache;
+ var skips = 0;
+ var j = 0;
+ for( var i = 0, len = arr.length; i < len; ++i ) {
+ var item = arr[ i ];
+ if( item === void 0 ) {
+ skips++;
+ }
+ else {
+ promiseArr[ j ] = promiseArr[ i ];
+ item.__id_$thenable__ = j;
+ arr[ j++ ] = item;
+ }
+ }
+ var newId = arr.length - skips;
+ if( newId === this.__id__ ) {
+ this.treshold *= 2;
+ }
+ else for( var i = newId, len = arr.length; i < len; ++i ) {
+ promiseArr[ j ] = arr[i] = void 0;
+ }
+
+ this.__id__ = newId;
+ this._compactQueued = false;
+};
+
+method._thenableSlowCase = function Thenable$_thenableSlowCase( ret, ref ) {
+ try {
+ var then = ret.then;
+ if( typeof then === "function" ) {
+ ref.ref = then;
+ return true;
+ }
+ return false;
+ }
+ catch(e) {
+ this.errorObj.e = e;
+ ref.ref = this.errorObj;
+ return true;
+ }
+};
+
+
+
+
+return Thenable;})();
+var CatchFilter = (function() {
+
+function CatchFilter( instances, callback ) {
+ this._instances = instances;
+ this._callback = callback;
+}
+var method = CatchFilter.prototype;
+
+method.doFilter = function( e ) {
+ if( e === null || typeof e !== "object" ) {
+ throw e;
+ }
+ var cb = this._callback;
+ for( var i = 0, len = this._instances.length; i < len; ++i ) {
+ var item = this._instances[i];
+ if( e instanceof item ) {
+ var ret = tryCatch1( cb, void 0, e );
+ if( ret === errorObj ) {
+ throw ret.e;
+ }
+ return ret;
+ }
+ }
+ throw e;
+};
+
+return CatchFilter;})();
+var Promise = (function() {
+
+function isObject( value ) {
+ if( value === null ) {
+ return false;
+ }
+ return ( typeof value === "object" ||
+ typeof value === "function" );
+}
+
+function isPromise( obj ) {
+ if( typeof obj !== "object" ) return false;
+ return obj instanceof Promise;
+}
+
+var Err = Error;
+function isError( obj ) {
+ if( typeof obj !== "object" ) return false;
+ return obj instanceof Err;
+}
+
+var Arr = Array;
+var isArray = Arr.isArray || function( obj ) {
+ return obj instanceof Arr;
+};
+
+
+var APPLY = {};
+var thenable = new Thenable( errorObj );
+
+function Promise( resolver ) {
+ if( typeof resolver === "function" )
+ this._resolveResolver( resolver );
+
+
+ this._bitField = 67108864;
+ this._fulfill0 = void 0;
+ this._reject0 = void 0;
+ this._progress0 = void 0;
+ this._promise0 = void 0;
+ this._receiver0 = void 0;
+ this._resolvedValue = void 0;
+ this._cancellationParent = void 0;
+ if( longStackTraces ) this._traceParent = this._peekContext();
+}
+
+var method = Promise.prototype;
+
+var longStackTraces = true;
+Promise.longStackTraces = function() {
+ if( async.haveItemsQueued() &&
+ longStackTraces === false
+ ) {
+ throw new Error("Cannot enable long stack traces " +
+ "after promises have been created");
+ }
+ longStackTraces = true;
+};
+
+method._setTrace = function _setTrace( fn ) {
+ ASSERT((this._trace == null),
+ "this._trace == null");
+ if( longStackTraces ) {
+ this._trace = new CapturedTrace(
+ typeof fn === "function"
+ ? fn
+ : _setTrace
+ );
+ }
+ return this;
+};
+
+method.toString = function Promise$toString() {
+ return "[object Promise]";
+};
+
+
+method.caught = method["catch"] = function Promise$catch( fn ) {
+ var len = arguments.length;
+ if( len > 1 ) {
+ var catchInstances = new Array( len - 1 ),
+ j = 0, i;
+ for( i = 0; i < len - 1; ++i ) {
+ var item = arguments[i];
+ if( typeof item === "function" &&
+ ( item.prototype instanceof Error ||
+ item === Error ) ) {
+ catchInstances[j++] = item;
+ }
+ }
+ catchInstances.length = j;
+ fn = arguments[i];
+ var catchFilter = new CatchFilter( catchInstances, fn );
+ return this._then( void 0, catchFilter.doFilter, void 0,
+ catchFilter, void 0, this.caught );
+ }
+ return this._then( void 0, fn, void 0, void 0, void 0, this.caught );
+};
+
+method.progressed = function Promise$progressed( fn ) {
+ return this._then( void 0, void 0, fn, void 0, void 0, this.progressed );
+};
+
+
+function thrower( r ) {
+ throw r;
+}
+function slowFinally( ret, reasonOrValue ) {
+ if( this.isFulfilled() ) {
+ return ret._then(function() {
+ return reasonOrValue;
+ }, thrower, void 0, this, void 0, slowFinally );
+ }
+ else {
+ return ret._then(function() {
+ throw reasonOrValue;
+ }, thrower, void 0, this, void 0, slowFinally );
+ }
+}
+method.lastly = method["finally"] = function Promise$finally( fn ) {
+ var r = function( reasonOrValue ) {
+ var ret = fn( reasonOrValue );
+ if( isPromise( ret ) ) {
+ return slowFinally.call( this, ret, reasonOrValue );
+ }
+ if( this.isRejected() ) throw reasonOrValue;
+ return reasonOrValue;
+ };
+ return this._then( r, r, void 0, this, void 0, this.anyway );
+};
+
+method.inspect = function Promise$inspect() {
+ return new PromiseInspection( this );
+};
+
+method.cancel = function Promise$cancel() {
+ if( !this.isCancellable() ) return this;
+ var cancelTarget = this;
+ while( cancelTarget._cancellationParent !== void 0 ) {
+ cancelTarget = cancelTarget._cancellationParent;
+ }
+ if( cancelTarget === this ) {
+ var err = new CancellationError();
+ this._attachExtraTrace( err );
+ async.invoke( this._reject, this, err );
+ }
+ else {
+ async.invoke( cancelTarget.cancel, cancelTarget, void 0 );
+ }
+ return this;
+};
+
+method.uncancellable = function Promise$uncancellable() {
+ var ret = new Promise();
+ ret._setTrace();
+ ret._unsetCancellable();
+ ret._assumeStateOf( this, true );
+ return ret;
+};
+
+method.fork = function Promise$fork( didFulfill, didReject, didProgress ) {
+ var ret = this._then( didFulfill, didReject, didProgress,
+ void 0, void 0, this.fork );
+ ret._cancellationParent = void 0;
+ return ret;
+};
+
+method.call = function Promise$call( propertyName ) {
+ var len = arguments.length;
+
+ if( len < 2 ) {
+ return this._callFast( propertyName );
+ }
+ else {
+ var args = new Array(len-1);
+ for( var i = 1; i < len; ++i ) {
+ args[ i - 1 ] = arguments[ i ];
+ }
+ return this._callSlow( propertyName, args );
+ }
+
+};
+
+method.get = function Promise$get( propertyName ) {
+ return this._then(
+ getGetter( propertyName ),
+ void 0,
+ void 0,
+ void 0,
+ void 0,
+ this.get
+ );
+};
+
+method.then = function Promise$then( didFulfill, didReject, didProgress ) {
+ return this._then( didFulfill, didReject, didProgress,
+ void 0, void 0, this.then );
+};
+
+method.spread = function Promise$spread( didFulfill, didReject ) {
+ return this._then( didFulfill, didReject, void 0,
+ APPLY, void 0, this.spread );
+};
+method.isFulfilled = function Promise$isFulfilled() {
+ return ( this._bitField & 268435456 ) > 0;
+};
+
+method.isRejected = function Promise$isRejected() {
+ return ( this._bitField & 134217728 ) > 0;
+};
+
+method.isPending = function Promise$isPending() {
+ return !this.isResolved();
+};
+
+method.isResolved = function Promise$isResolved() {
+ return ( this._bitField & 402653184 ) > 0;
+};
+
+method.isCancellable = function Promise$isCancellable() {
+ return !this.isResolved() &&
+ this._cancellable();
+};
+
+method.toJSON = function Promise$toJSON() {
+ var inspection = this.inspect();
+ var ret = {
+ isFulfilled: false,
+ isRejected: false
+ };
+ if( inspection.isFulfilled() ) {
+ ret.fulfillmentValue = inspection.value();
+ ret.isFulfilled = true;
+ }
+ else if( inspection.isRejected() ) {
+ ret.rejectionReason = inspection.error();
+ ret.isRejected = true;
+ }
+ return ret;
+};
+
+method.map = function Promise$map( fn ) {
+ return Promise.map( this, fn );
+};
+
+method.all = function Promise$all() {
+ return Promise.all( this );
+};
+
+method.any = function Promise$any() {
+ return Promise.any( this );
+};
+
+method.settle = function Promise$settle() {
+ return Promise.settle( this );
+};
+
+method.some = function Promise$some( count ) {
+ return Promise.some( this, count );
+};
+
+method.reduce = function Promise$reduce( fn, initialValue ) {
+ return Promise.reduce( this, fn, initialValue );
+};
+
+Promise.is = isPromise;
+
+Promise.settle = function Promise$Settle( promises ) {
+ var ret = Promise._all( promises, SettledPromiseArray );
+ return ret.promise();
+};
+
+Promise.all = function Promise$All( promises ) {
+ var ret = Promise._all( promises, PromiseArray );
+ return ret.promise();
+};
+
+Promise.join = function Promise$Join() {
+ var ret = new Array( arguments.length );
+ for( var i = 0, len = ret.length; i < len; ++i ) {
+ ret[i] = arguments[i];
+ }
+ return Promise._all( ret, PromiseArray ).promise();
+};
+
+Promise.any = function Promise$Any( promises ) {
+ var ret = Promise._all( promises, AnyPromiseArray );
+ return ret.promise();
+};
+
+Promise.some = function Promise$Some( promises, howMany ) {
+ var ret = Promise._all( promises, SomePromiseArray );
+ if( ( howMany | 0 ) !== howMany ) {
+ throw new TypeError("howMany must be an integer");
+ }
+ var len = ret.length();
+ howMany = Math.max(0, Math.min( howMany, len ) );
+ ret._howMany = howMany;
+ return ret.promise();
+};
+
+
+function mapper( fulfilleds ) {
+ var fn = this;
+ var shouldDefer = false;
+ for( var i = 0, len = fulfilleds.length; i < len; ++i ) {
+ var fulfill = fn(fulfilleds[i]);
+ if( !shouldDefer && isPromise( fulfill ) ) {
+ if( fulfill.isFulfilled() ) {
+ fulfilleds[i] = fulfill._resolvedValue;
+ continue;
+ }
+ else {
+ shouldDefer = true;
+ }
+ }
+ fulfilleds[i] = fulfill;
+ }
+ return shouldDefer ? Promise.all( fulfilleds ) : fulfilleds;
+}
+Promise.map = function Promise$Map( promises, fn ) {
+ if( typeof fn !== "function" )
+ throw new TypeError( "fn is not a function" );
+ return Promise.all( promises )._then(
+ mapper,
+ void 0,
+ void 0,
+ fn,
+ void 0,
+ Promise.all
+ );
+};
+
+function reducer( fulfilleds, initialValue ) {
+ var fn = this;
+ var len = fulfilleds.length;
+ var accum;
+ var startIndex = 0;
+
+ if( initialValue !== void 0 ) {
+ accum = initialValue;
+ startIndex = 0;
+ }
+ else {
+ accum = len > 0 ? fulfilleds[0] : void 0;
+ startIndex = 1;
+ }
+ for( var i = startIndex; i < len; ++i ) {
+ accum = fn( accum, fulfilleds[i], i, len );
+ }
+ return accum;
+}
+
+function slowReduce( promises, fn, initialValue ) {
+ return Promise._all( promises, PromiseArray, slowReduce )
+ .promise()
+ .then( function( fulfilleds ) {
+ return reducer.call( fn, fulfilleds, initialValue );
+ });
+}
+
+
+Promise.reduce = function Promise$Reduce( promises, fn, initialValue ) {
+ if( typeof fn !== "function" )
+ throw new TypeError( "fn is not a function");
+ if( initialValue !== void 0 ) {
+ return slowReduce( promises, fn, initialValue );
+ }
+ return Promise
+ .all( promises )
+ ._then( reducer, void 0, void 0, fn, void 0, Promise.all );
+};
+
+Promise.fulfilled = function Promise$Fulfilled( value ) {
+ var ret = new Promise();
+ ret._setTrace();
+ if( ret._tryAssumeStateOf( value, false ) ) {
+ return ret;
+ }
+ ret._cleanValues();
+ ret._setFulfilled();
+ ret._resolvedValue = value;
+ return ret;
+};
+
+Promise.rejected = function Promise$Rejected( reason ) {
+ var ret = new Promise();
+ ret._setTrace();
+ ret._cleanValues();
+ ret._setRejected();
+ ret._resolvedValue = reason;
+ return ret;
+};
+
+Promise.pending = function Promise$Pending( caller ) {
+ var promise = new Promise();
+ promise._setTrace( caller );
+ return new PromiseResolver( promise );
+};
+
+
+Promise.cast = function Promise$Cast( obj ) {
+ var ret = cast( obj );
+ if( !( ret instanceof Promise ) ) {
+ return Promise.fulfilled( ret );
+ }
+ return ret;
+};
+
+Promise.onPossiblyUnhandledRejection =
+function Promise$OnPossiblyUnhandledRejection( fn ) {
+ if( typeof fn === "function" ) {
+ CapturedTrace.possiblyUnhandledRejection = fn;
+ }
+ else {
+ CapturedTrace.possiblyUnhandledRejection = void 0;
+ }
+};
+
+Promise.promisify = function Promise$Promisify( callback, receiver) {
+ return makeNodePromisified( callback, receiver );
+};
+
+method._then =
+function Promise$_then(
+ didFulfill,
+ didReject,
+ didProgress,
+ receiver,
+ internalData,
+ caller
+) {
+ var haveInternalData = internalData !== void 0;
+ var ret = haveInternalData ? internalData : new Promise();
+
+ if( longStackTraces && !haveInternalData ) {
+ ret._traceParent = this;
+ ret._setTrace( typeof caller === "function" ? caller : this._then );
+ }
+
+ var callbackIndex =
+ this._addCallbacks( didFulfill, didReject, didProgress, ret, receiver );
+
+ if( this.isResolved() ) {
+ async.invoke( this._resolveLast, this, callbackIndex );
+ }
+ else if( !haveInternalData && this.isCancellable() ) {
+ ret._cancellationParent = this;
+ }
+
+ if( this._isDelegated() ) {
+ this._unsetDelegated();
+ ASSERT((! this.isResolved()),
+ "!this.isResolved()");
+ var x = this._resolvedValue;
+ if( !this._tryThenable( x ) ) {
+ async.invoke( this._fulfill, this, x );
+ }
+ }
+ return ret;
+};
+
+method._length = function Promise$_length() {
+ ASSERT(isPromise(this),
+ "isPromise( this )");
+ ASSERT((arguments.length === 0),
+ "arguments.length === 0");
+ return this._bitField & 16777215;
+};
+
+method._isFollowingOrFulfilledOrRejected =
+function Promise$_isFollowingOrFulfilledOrRejected() {
+ return ( this._bitField & 939524096 ) > 0;
+};
+
+method._setLength = function Promise$_setLength( len ) {
+ this._bitField = ( this._bitField & -16777216 ) |
+ ( len & 16777215 ) ;
+};
+
+method._cancellable = function Promise$_cancellable() {
+ return ( this._bitField & 67108864 ) > 0;
+};
+
+method._setFulfilled = function Promise$_setFulfilled() {
+ this._bitField = this._bitField | 268435456;
+};
+
+method._setRejected = function Promise$_setRejected() {
+ this._bitField = this._bitField | 134217728;
+};
+
+method._setFollowing = function Promise$_setFollowing() {
+ this._bitField = this._bitField | 536870912;
+};
+
+method._setDelegated = function Promise$_setDelegated() {
+ this._bitField = this._bitField | -1073741824;
+
+};
+
+method._isDelegated = function Promise$_isDelegated() {
+ return ( this._bitField & -1073741824 ) === -1073741824;
+};
+
+method._unsetDelegated = function Promise$_unsetDelegated() {
+ this._bitField = this._bitField & ( ~-1073741824 );
+};
+
+method._setCancellable = function Promise$_setCancellable() {
+ this._bitField = this._bitField | 67108864;
+};
+
+method._unsetCancellable = function Promise$_unsetCancellable() {
+ this._bitField = this._bitField & ( ~67108864 );
+};
+
+method._receiverAt = function Promise$_receiverAt( index ) {
+ ASSERT(((typeof index) === "number"),
+ "typeof index === \u0022number\u0022");
+ ASSERT((index >= 0),
+ "index >= 0");
+ ASSERT((index < this._length()),
+ "index < this._length()");
+ ASSERT(((index % 5) === 0),
+ "index % CALLBACK_SIZE === 0");
+ if( index === 0 ) return this._receiver0;
+ return this[ index + 4 - 5 ];
+};
+
+method._promiseAt = function Promise$_promiseAt( index ) {
+ ASSERT(((typeof index) === "number"),
+ "typeof index === \u0022number\u0022");
+ ASSERT((index >= 0),
+ "index >= 0");
+ ASSERT((index < this._length()),
+ "index < this._length()");
+ ASSERT(((index % 5) === 0),
+ "index % CALLBACK_SIZE === 0");
+ if( index === 0 ) return this._promise0;
+ return this[ index + 3 - 5 ];
+};
+
+method._fulfillAt = function Promise$_fulfillAt( index ) {
+ ASSERT(((typeof index) === "number"),
+ "typeof index === \u0022number\u0022");
+ ASSERT((index >= 0),
+ "index >= 0");
+ ASSERT((index < this._length()),
+ "index < this._length()");
+ ASSERT(((index % 5) === 0),
+ "index % CALLBACK_SIZE === 0");
+ if( index === 0 ) return this._fulfill0;
+ return this[ index + 0 - 5 ];
+};
+
+method._rejectAt = function Promise$_rejectAt( index ) {
+ ASSERT(((typeof index) === "number"),
+ "typeof index === \u0022number\u0022");
+ ASSERT((index >= 0),
+ "index >= 0");
+ ASSERT((index < this._length()),
+ "index < this._length()");
+ ASSERT(((index % 5) === 0),
+ "index % CALLBACK_SIZE === 0");
+ if( index === 0 ) return this._reject0;
+ return this[ index + 1 - 5 ];
+};
+
+method._progressAt = function Promise$_progressAt( index ) {
+ ASSERT(((typeof index) === "number"),
+ "typeof index === \u0022number\u0022");
+ ASSERT((index >= 0),
+ "index >= 0");
+ ASSERT((index < this._length()),
+ "index < this._length()");
+ ASSERT(((index % 5) === 0),
+ "index % CALLBACK_SIZE === 0");
+ if( index === 0 ) return this._progress0;
+ return this[ index + 2 - 5 ];
+};
+
+var fulfiller = new Function("p",
+ "'use strict';return function Promise$_fulfiller(a){ p.fulfill( a ); }" );
+var rejecter = new Function("p",
+ "'use strict';return function Promise$_rejecter(a){ p.reject( a ); }" );
+
+method._resolveResolver = function Promise$_resolveResolver( resolver ) {
+ ASSERT(((typeof resolver) === "function"),
+ "typeof resolver === \u0022function\u0022");
+ this._setTrace( this._resolveResolver );
+ var p = new PromiseResolver( this );
+ this._pushContext();
+ var r = tryCatch2( resolver, this, fulfiller( p ), rejecter( p ) );
+ this._popContext();
+ if( r === errorObj ) {
+ p.reject( r.e );
+ }
+};
+
+method._addCallbacks = function Promise$_addCallbacks(
+ fulfill,
+ reject,
+ progress,
+ promise,
+ receiver
+) {
+ fulfill = typeof fulfill === "function" ? fulfill : void 0;
+ reject = typeof reject === "function" ? reject : void 0;
+ progress = typeof progress === "function" ? progress : void 0;
+ var index = this._length();
+
+ if( index === 0 ) {
+ this._fulfill0 = fulfill;
+ this._reject0 = reject;
+ this._progress0 = progress;
+ this._promise0 = promise;
+ this._receiver0 = receiver;
+ this._setLength( index + 5 );
+ return index;
+ }
+
+ this[ index - 5 + 0 ] = fulfill;
+ this[ index - 5 + 1 ] = reject;
+ this[ index - 5 + 2 ] = progress;
+ this[ index - 5 + 3 ] = promise;
+ this[ index - 5 + 4 ] = receiver;
+
+ this._setLength( index + 5 );
+ return index;
+};
+
+method._callFast = function Promise$_callFast( propertyName ) {
+ return this._then(
+ getFunction( propertyName ),
+ void 0,
+ void 0,
+ void 0,
+ void 0,
+ this.call
+ );
+};
+
+method._callSlow = function Promise$_callSlow( propertyName, args ) {
+ ASSERT(isArray(args),
+ "isArray( args )");
+ ASSERT((args.length > 0),
+ "args.length > 0");
+ return this._then( function( obj ) {
+ return obj[propertyName].apply( obj, args );
+ },
+ void 0,
+ void 0,
+ void 0,
+ void 0,
+ this.call
+ );
+};
+
+method._resolveLast = function Promise$_resolveLast( index ) {
+ ASSERT(((typeof index) === "number"),
+ "typeof index === \u0022number\u0022");
+ ASSERT((index >= 0),
+ "index >= 0");
+ ASSERT((index < this._length()),
+ "index < this._length()");
+ var promise = this._promiseAt( index );
+ var receiver = this._receiverAt( index );
+ var fn;
+
+ if( this.isFulfilled() ) {
+ fn = this._fulfillAt( index );
+ }
+ else if( this.isRejected() ) {
+ fn = this._rejectAt( index );
+ }
+ else unreachable();
+
+ var obj = this._resolvedValue;
+ var ret = obj;
+ if( fn !== void 0 ) {
+ this._resolvePromise( fn, receiver, obj, promise );
+ }
+ else if( this.isFulfilled() ) {
+ promise._fulfill( ret );
+ }
+ else {
+ promise._reject( ret );
+ }
+};
+
+method._spreadSlowCase =
+function Promise$_spreadSlowCase( targetFn, promise, values ) {
+ promise._assumeStateOf(
+ Promise.all( values )._then( targetFn, void 0, void 0, APPLY, void 0,
+ this._spreadSlowCase ),
+ false
+ );
+};
+
+
+function cast( obj ) {
+ if( isObject( obj ) ) {
+ if( obj instanceof Promise ) {
+ return obj;
+ }
+ var ref = { ref: null, promise: null };
+ if( thenable.is( obj, ref ) ) {
+ if( ref.promise != null ) {
+ return ref.promise;
+ }
+ var resolver = Promise.pending();
+ var result = ref.ref;
+ if( result === errorObj ) {
+ resolver.reject( result.e );
+ return resolver.promise;
+ }
+ thenable.addCache( obj, resolver.promise );
+ var called = false;
+ var ret = tryCatch2( result, obj, function t( a ) {
+ if( called ) return;
+ called = true;
+ async.invoke( thenable.deleteCache, thenable, obj );
+ var b = cast( a );
+ if( b === a ) {
+ resolver.fulfill( a );
+ }
+ else {
+ b._then(
+ resolver.fulfill,
+ resolver.reject,
+ void 0,
+ resolver,
+ void 0,
+ t
+ );
+ }
+ }, function t( a ) {
+ if( called ) return;
+ called = true;
+ async.invoke( thenable.deleteCache, thenable, obj );
+ resolver.reject( a );
+ });
+ if( ret === errorObj && !called ) {
+ resolver.reject( ret.e );
+ async.invoke( thenable.deleteCache, thenable, obj );
+ }
+ return resolver.promise;
+ }
+ }
+ return obj;
+}
+
+method._resolveThenable = function Promise$_resolveThenable( x, ref ) {
+ if( ref.promise != null ) {
+ this._assumeStateOf( ref.promise, true );
+ return;
+ }
+ if( ref.ref === errorObj ) {
+ this._attachExtraTrace( ref.ref.e );
+ async.invoke( this._reject, this, ref.ref.e );
+ }
+ else {
+ thenable.addCache( x, this );
+ var then = ref.ref;
+ var localX = x;
+ var localP = this;
+ var key = {};
+ var called = false;
+ var t = function t( v ) {
+ if( called && this !== key ) return;
+ called = true;
+ var b = cast( v );
+
+ if( b !== v ||
+ ( b instanceof Promise && b.isPending() ) ) {
+ b._then( t, r, void 0, key, void 0, t);
+ return;
+ }
+
+ var fn = localP._fulfill;
+ if( b instanceof Promise ) {
+ var fn = b.isFulfilled()
+ ? localP._fulfill : localP._reject;
+ ASSERT(b.isResolved(),
+ "b.isResolved()");
+ v = v._resolvedValue;
+ b = cast( v );
+ ASSERT(((b instanceof Promise) || (b === v)),
+ "b instanceof Promise || b === v");
+ if( b !== v ||
+ ( b instanceof Promise && b !== v ) ) {
+ b._then( t, r, void 0, key, void 0, t);
+ return;
+ }
+ }
+ async.invoke( fn, localP, v );
+ async.invoke( thenable.deleteCache,
+ thenable, localX );
+ };
+
+ var r = function r( v ) {
+ if( called && this !== key ) return;
+ called = true;
+
+ var b = cast( v );
+
+ if( b !== v ||
+ ( b instanceof Promise && b.isPending() ) ) {
+ b._then( t, r, void 0, key, void 0, t);
+ return;
+ }
+
+ var fn = localP._reject;
+ if( b instanceof Promise ) {
+ var fn = b.isFulfilled()
+ ? localP._fulfill : localP._reject;
+ ASSERT(b.isResolved(),
+ "b.isResolved()");
+ v = v._resolvedValue;
+ b = cast( v );
+ if( b !== v ||
+ ( b instanceof Promise && b.isPending() ) ) {
+ b._then( t, r, void 0, key, void 0, t);
+ return;
+ }
+ }
+
+ async.invoke( fn, localP, v );
+ async.invoke( thenable.deleteCache,
+ thenable, localX );
+
+ };
+ var threw = tryCatch2( then, x, t, r);
+ if( threw === errorObj &&
+ !called ) {
+ this._attachExtraTrace( threw.e );
+ async.invoke( this._reject, this, threw.e );
+ async.invoke( thenable.deleteCache, thenable, x );
+ }
+ }
+};
+
+method._tryThenable = function Promise$_tryThenable( x ) {
+ var ref;
+ if( !thenable.is( x, ref = {ref: null, promise: null} ) ) {
+ return false;
+ }
+ this._resolveThenable( x, ref );
+ return true;
+};
+
+var ignore = CatchFilter.prototype.doFilter;
+method._resolvePromise = function Promise$_resolvePromise(
+ onFulfilledOrRejected, receiver, value, promise
+) {
+ if( isError( value ) ) {
+ value.__handled = true;
+ }
+
+ if( !isPromise( promise ) ) {
+ return onFulfilledOrRejected.call( receiver, value, promise );
+ }
+
+ var x;
+ if( receiver === APPLY ) {
+ if( isArray( value ) ) {
+ for( var i = 0, len = value.length; i < len; ++i ) {
+ if( isPromise( value[i] ) ) {
+ this._spreadSlowCase(
+ onFulfilledOrRejected,
+ promise,
+ value
+ );
+ return;
+ }
+ }
+ promise._pushContext();
+ x = tryCatchApply( onFulfilledOrRejected, value );
+ }
+ else {
+ this._spreadSlowCase( onFulfilledOrRejected, promise, value );
+ return;
+ }
+ }
+ else {
+ promise._pushContext();
+ x = tryCatch1( onFulfilledOrRejected, receiver, value );
+ }
+
+ promise._popContext();
+
+ if( x === errorObj ) {
+ if( onFulfilledOrRejected !== ignore ) {
+ promise._attachExtraTrace( x.e );
+ }
+ async.invoke( promise._reject, promise, x.e );
+ }
+ else if( x === promise ) {
+ async.invoke(
+ promise._reject,
+ promise,
+ new TypeError( "Circular thenable chain" )
+ );
+ }
+ else {
+ if( promise._tryAssumeStateOf( x, true ) ) {
+ return;
+ }
+ else if( thenable.couldBe( x ) ) {
+
+ if( promise._length() === 0 ) {
+ promise._resolvedValue = x;
+ promise._setDelegated();
+ return;
+ }
+ else if( promise._tryThenable( x ) ) {
+ return;
+ }
+ }
+ async.invoke( promise._fulfill, promise, x );
+ }
+};
+
+method._assumeStateOf =
+function Promise$_assumeStateOf( promise, mustAsync ) {
+ ASSERT(isPromise(promise),
+ "isPromise( promise )");
+ ASSERT(((typeof mustAsync) === "boolean"),
+ "typeof mustAsync === \u0022boolean\u0022");
+ ASSERT((this._isFollowingOrFulfilledOrRejected() === false),
+ "this._isFollowingOrFulfilledOrRejected() === false");
+ this._setFollowing();
+ if( promise.isPending() ) {
+ if( promise._cancellable() ) {
+ this._cancellationParent = promise;
+ }
+ promise._then(
+ this._resolveFulfill,
+ this._resolveReject,
+ this._resolveProgress,
+ this,
+ void 0,
+ this._tryAssumeStateOf
+ );
+ }
+ else if( promise.isFulfilled() ) {
+ if( mustAsync )
+ async.invoke( this._resolveFulfill, this, promise._resolvedValue );
+ else
+ this._resolveFulfill( promise._resolvedValue );
+ }
+ else {
+ if( mustAsync )
+ async.invoke( this._resolveReject, this, promise._resolvedValue );
+ else
+ this._resolveReject( promise._resolvedValue );
+ }
+
+ if( longStackTraces &&
+ promise._traceParent == null ) {
+ promise._traceParent = this;
+ }
+};
+
+method._tryAssumeStateOf =
+function Promise$_tryAssumeStateOf( value, mustAsync ) {
+ if( !isPromise( value ) ||
+ this._isFollowingOrFulfilledOrRejected() ) return false;
+
+ this._assumeStateOf( value, mustAsync );
+ return true;
+};
+
+
+
+method._attachExtraTrace = function Promise$_attachExtraTrace( error ) {
+ if( longStackTraces &&
+ isError( error ) ) {
+ var promise = this;
+ var stack = error.stack.split("\n");
+ var headerLineCount = 1;
+
+ while( promise != null &&
+ promise._trace != null ) {
+ stack = CapturedTrace.combine(
+ stack,
+ promise._trace.stack.split( "\n" )
+ );
+ promise = promise._traceParent;
+ }
+
+ var max = Error.stackTraceLimit + headerLineCount;
+ var len = stack.length;
+ if( len > max ) {
+ stack.length = max;
+ }
+ if( stack.length <= headerLineCount ) {
+ error.stack = "(No stack trace)";
+ }
+ else {
+ error.stack = stack.join("\n");
+ }
+ }
+};
+
+method._notifyUnhandledRejection =
+function Promise$_notifyUnhandledRejection( reason ) {
+ if( !reason.__handled ) {
+ reason.__handled = true;
+ CapturedTrace.possiblyUnhandledRejection( reason );
+ }
+};
+
+method._unhandledRejection = function Promise$_unhandledRejection( reason ) {
+ if( !reason.__handled ) {
+ async.invokeLater( this._notifyUnhandledRejection, this, reason );
+ }
+};
+
+method._cleanValues = function Promise$_cleanValues() {
+ this._cancellationParent = void 0;
+};
+
+method._fulfill = function Promise$_fulfill( value ) {
+ if( this._isFollowingOrFulfilledOrRejected() ) return;
+ this._resolveFulfill( value );
+
+};
+
+method._reject = function Promise$_reject( reason ) {
+ if( this._isFollowingOrFulfilledOrRejected() ) return;
+ this._resolveReject( reason );
+};
+
+method._progress = function Promise$_progress( progressValue ) {
+ if( this._isFollowingOrFulfilledOrRejected() ) return;
+ this._resolveProgress( progressValue );
+
+};
+
+method._resolveFulfill = function Promise$_resolveFulfill( value ) {
+ ASSERT(this.isPending(),
+ "this.isPending()");
+ this._cleanValues();
+ this._setFulfilled();
+ this._resolvedValue = value;
+ var len = this._length();
+ for( var i = 0; i < len; i+= 5 ) {
+ var fn = this._fulfillAt( i );
+ var promise = this._promiseAt( i );
+ var receiver = this._receiverAt( i );
+ if( fn !== void 0 ) {
+ this._resolvePromise(
+ fn,
+ receiver,
+ value,
+ promise
+ );
+
+ }
+ else {
+ async.invoke( promise._fulfill, promise, value );
+ }
+ }
+};
+
+method._resolveReject = function Promise$_resolveReject( reason ) {
+ ASSERT(this.isPending(),
+ "this.isPending()");
+ this._cleanValues();
+ this._setRejected();
+ this._resolvedValue = reason;
+ var len = this._length();
+ var rejectionWasHandled = false;
+ for( var i = 0; i < len; i+= 5 ) {
+ var fn = this._rejectAt( i );
+ var promise = this._promiseAt( i );
+ if( fn !== void 0 ) {
+ rejectionWasHandled = true;
+ this._resolvePromise(
+ fn,
+ this._receiverAt( i ),
+ reason,
+ promise
+ );
+ }
+ else {
+ if( !rejectionWasHandled )
+ rejectionWasHandled = promise._length() > 0;
+ async.invoke( promise._reject, promise, reason );
+ }
+ }
+ if( !rejectionWasHandled &&
+ isError( reason ) &&
+ CapturedTrace.possiblyUnhandledRejection !== void 0
+
+ ) {
+ if( reason.__handled !== true ) {
+ reason.__handled = false;
+ async.invoke(
+ this._unhandledRejection,
+ this,
+ reason
+ );
+ }
+ }
+
+};
+
+method._resolveProgress = function Promise$_resolveProgress( progressValue ) {
+ ASSERT(this.isPending(),
+ "this.isPending()");
+ var len = this._length();
+ for( var i = 0; i < len; i += 5 ) {
+ var fn = this._progressAt( i );
+ var promise = this._promiseAt( i );
+ if( !isPromise( promise ) ) {
+ fn.call( this._receiverAt( i ), progressValue, promise );
+ continue;
+ }
+ var ret = progressValue;
+ if( fn !== void 0 ) {
+ this._pushContext();
+ ret = tryCatch1( fn, this._receiverAt( i ), progressValue );
+ this._popContext();
+ if( ret === errorObj ) {
+ if( ret.e != null &&
+ ret.e.name === "StopProgressPropagation" ) {
+ ret.e.__handled = true;
+ }
+ else {
+ promise._attachExtraTrace( ret.e );
+ async.invoke( promise._progress, promise, ret.e );
+ }
+ }
+ else if( isPromise( ret ) ) {
+ ret._then( promise._progress, null, null, promise, void 0,
+ this._progress );
+ }
+ else {
+ async.invoke( promise._progress, promise, ret );
+ }
+ }
+ else {
+ async.invoke( promise._progress, promise, ret );
+ }
+ }
+};
+
+var contextStack = [];
+method._peekContext = function Promise$_peekContext() {
+ var lastIndex = contextStack.length - 1;
+ if( lastIndex >= 0 ) {
+ return contextStack[ lastIndex ];
+ }
+ return void 0;
+
+};
+
+method._pushContext = function Promise$_pushContext() {
+ if( !longStackTraces ) return;
+ contextStack.push( this );
+};
+
+method._popContext = function Promise$_popContext() {
+ if( !longStackTraces ) return;
+ contextStack.pop();
+};
+
+
+Promise._all =
+function Promise$_All( promises, PromiseArray, caller ) {
+ ASSERT(((typeof PromiseArray) === "function"),
+ "typeof PromiseArray === \u0022function\u0022");
+ if( isPromise( promises ) ||
+ isArray( promises ) ) {
+
+ return new PromiseArray( promises,
+ typeof caller === "function"
+ ? caller
+ : Promise$_All
+ );
+ }
+ throw new TypeError("expecting an array or a promise");
+};
+
+
+if( !CapturedTrace.isSupported() ) {
+ Promise.longStackTraces = void 0;
+ CapturedTrace.possiblyUnhandledRejection = void 0;
+ Promise.onPossiblyUnhandledRejection = void 0;
+ longStackTraces = false;
+}
+
+return Promise;})();
+
+
+
+var PromiseArray = (function() {
+
+function nullToUndefined( val ) {
+ return val === null
+ ? void 0
+ : val;
+}
+
+var hasOwn = {}.hasOwnProperty;
+var empty = [];
+
+function isPromise( obj ) {
+ if( typeof obj !== "object" ) return false;
+ return obj instanceof Promise;
+}
+
+var Arr = Array;
+var isArray = Arr.isArray || function( obj ) {
+ return obj instanceof Arr;
+};
+
+function PromiseArray( values, caller ) {
+ this._values = values;
+ this._resolver = Promise.pending( caller );
+ this._length = 0;
+ this._totalResolved = 0;
+ this._init( void 0, empty );
+}
+var method = PromiseArray.prototype;
+
+method.length = function PromiseArray$length() {
+ return this._length;
+};
+
+method.promise = function PromiseArray$promise() {
+ return this._resolver.promise;
+};
+
+
+method._init = function PromiseArray$_init( _, fulfillValueIfEmpty ) {
+ var values = this._values;
+ if( isPromise( values ) ) {
+ if( values.isPending() ) {
+ values._then(
+ this._init,
+ this._reject,
+ void 0,
+ this,
+ fulfillValueIfEmpty,
+ this.constructor
+ );
+ return;
+ }
+ else if( values.isRejected() ) {
+ this._reject( values._resolvedValue );
+ return;
+ }
+ else {
+ values = values._resolvedValue;
+ if( !isArray( values ) ) {
+ this._fulfill( nullToUndefined( fulfillValueIfEmpty ) );
+ return;
+ }
+ this._values = values;
+ }
+
+ }
+ if( !values.length ) {
+ this._fulfill( nullToUndefined( fulfillValueIfEmpty ) );
+ return;
+ }
+ var len = values.length;
+ var newLen = len;
+ var newValues = new Array( len );
+ for( var i = 0; i < len; ++i ) {
+ var promise = values[i];
+
+ if( promise === void 0 && !hasOwn.call( values, i ) ) {
+ newLen--;
+ continue;
+ }
+
+ promise = Promise.cast( promise );
+
+ promise._then(
+ this._promiseFulfilled,
+ this._promiseRejected,
+ this._promiseProgressed,
+
+ this, Integer.get( i ), this.constructor
+
+
+
+ );
+ newValues[i] = promise;
+ }
+ this._values = newValues;
+ this._length = newLen;
+};
+
+method._isResolved = function PromiseArray$_isResolved() {
+ return this._values === null;
+};
+
+method._fulfill = function PromiseArray$_fulfill( value ) {
+ ASSERT((! this._isResolved()),
+ "!this._isResolved()");
+ this._values = null;
+ this._resolver.fulfill( value );
+};
+
+method._reject = function PromiseArray$_reject( reason ) {
+ ASSERT((! this._isResolved()),
+ "!this._isResolved()");
+ this._values = null;
+ this._resolver.reject( reason );
+};
+
+method._promiseProgressed =
+function PromiseArray$_promiseProgressed( progressValue, index ) {
+ if( this._isResolved() ) return;
+ ASSERT(isArray(this._values),
+ "isArray( this._values )");
+
+ this._resolver.progress({
+ index: index.valueOf(),
+ value: progressValue
+ });
+};
+
+method._promiseFulfilled =
+function PromiseArray$_promiseFulfilled( value, index ) {
+ if( this._isResolved() ) return;
+ ASSERT(isArray(this._values),
+ "isArray( this._values )");
+ ASSERT((index instanceof Integer),
+ "index instanceof Integer");
+ this._values[ index.valueOf() ] = value;
+ var totalResolved = ++this._totalResolved;
+ if( totalResolved >= this._length ) {
+ this._fulfill( this._values );
+ }
+};
+
+method._promiseRejected =
+function PromiseArray$_promiseRejected( reason ) {
+ if( this._isResolved() ) return;
+ ASSERT(isArray(this._values),
+ "isArray( this._values )");
+ this._totalResolved++;
+ this._reject( reason );
+};
+
+function Integer( value ) {
+ this._value = value;
+}
+
+Integer.prototype.valueOf = function Integer$valueOf() {
+ return this._value;
+};
+Integer.get = function Integer$get( i ) {
+ if( i < 256 ) {
+ return ints[i];
+ }
+ return new Integer(i);
+};
+
+var ints = [];
+for( var i = 0; i < 256; ++i ) {
+ ints.push( new Integer(i) );
+}
+
+
+
+
+
+return PromiseArray;})();
+var SettledPromiseArray = (function() {
+function SettledPromiseArray( values, caller ) {
+ this.constructor$( values, caller );
+}
+var method = inherits( SettledPromiseArray, PromiseArray );
+
+
+
+method._promiseResolved =
+function SettledPromiseArray$_promiseResolved( index, inspection ) {
+ ASSERT(((typeof index) === "number"),
+ "typeof index === \u0022number\u0022");
+ this._values[ index ] = inspection;
+ var totalResolved = ++this._totalResolved;
+ if( totalResolved >= this._length ) {
+ this._fulfill( this._values );
+ }
+};
+
+var throwawayPromise = new Promise()._setTrace();
+method._promiseFulfilled =
+function SettledPromiseArray$_promiseFulfilled( value, index ) {
+ if( this._isResolved() ) return;
+ var ret = new PromiseInspection( throwawayPromise );
+ ret._bitField = 268435456;
+ ret._resolvedValue = value;
+ this._promiseResolved( index.valueOf(), ret );
+};
+method._promiseRejected =
+function SettledPromiseArray$_promiseRejected( reason, index ) {
+ if( this._isResolved() ) return;
+ var ret = new PromiseInspection( throwawayPromise );
+ ret._bitField = 134217728;
+ ret._resolvedValue = reason;
+ this._promiseResolved( index.valueOf(), ret );
+};
+
+return SettledPromiseArray;})();
+var AnyPromiseArray = (function() {
+function AnyPromiseArray( values, caller ) {
+ this.constructor$( values, caller );
+}
+var method = inherits( AnyPromiseArray, PromiseArray );
+
+method._init = function AnyPromiseArray$_init() {
+ this._init$( void 0, null );
+};
+
+method._promiseFulfilled =
+function AnyPromiseArray$_promiseFulfilled( value ) {
+ if( this._isResolved() ) return;
+ ++this._totalResolved;
+ this._fulfill( value );
+
+};
+method._promiseRejected =
+function AnyPromiseArray$_promiseRejected( reason, index ) {
+ if( this._isResolved() ) return;
+ var totalResolved = ++this._totalResolved;
+ this._values[ index.valueOf() ] = reason;
+ if( totalResolved >= this._length ) {
+ this._reject( this._values );
+ }
+
+};
+
+return AnyPromiseArray;})();
+var SomePromiseArray = (function() {
+function SomePromiseArray( values, caller ) {
+ this.constructor$( values, caller );
+}
+var method = inherits( SomePromiseArray, PromiseArray );
+
+
+
+method._init = function SomePromiseArray$_init() {
+ this._init$( void 0, [] );
+ this._howMany = 0;
+ this._rejected = 0;
+ this._rejectionValues = new Array( this.length() );
+ this._resolutionValues = new Array( this.length() );
+ if( this._isResolved() ) return;
+
+ if( this._howMany > this._canPossiblyFulfill() ) {
+ this._reject( [] );
+ }
+};
+
+method._canPossiblyFulfill =
+function SomePromiseArray$_canPossiblyFulfill() {
+ return this._totalResolved - this._rejected +
+ ( this.length() - this._totalResolved );
+};
+
+method._promiseFulfilled =
+function SomePromiseArray$_promiseFulfilled( value ) {
+ if( this._isResolved() ) return;
+
+ var totalResolved = this._totalResolved;
+ this._resolutionValues[ totalResolved ] = value;
+ this._totalResolved = totalResolved + 1;
+ if( totalResolved + 1 === this._howMany ) {
+ this._resolutionValues.length = this._howMany;
+ this._fulfill( this._resolutionValues );
+ this._resolutionValues =
+ this._rejectionValues = null;
+ }
+
+};
+method._promiseRejected =
+function SomePromiseArray$_promiseRejected( reason ) {
+ if( this._isResolved() ) return;
+
+ this._rejectionValues[ this._rejected ] = reason;
+ this._rejected++;
+ this._totalResolved++;
+
+ if( this._howMany > this._canPossiblyFulfill() ) {
+ this._rejectionValues.length = this._rejected;
+ this._reject( this._rejectionValues );
+ this._resolutionValues =
+ this._rejectionValues = null;
+ }
+};
+
+return SomePromiseArray;})();
+var PromiseInspection = (function() {
+
+
+function PromiseInspection( promise ) {
+ this._bitField = promise._bitField;
+ this._resolvedValue = promise.isResolved()
+ ? promise._resolvedValue
+ : void 0;
+}
+var method = PromiseInspection.prototype;
+
+method.isFulfilled = function PromiseInspection$isFulfilled() {
+ return ( this._bitField & 268435456 ) > 0;
+};
+
+method.isRejected = function PromiseInspection$isRejected() {
+ return ( this._bitField & 134217728 ) > 0;
+};
+
+method.isPending = function PromiseInspection$isPending() {
+ return ( this._bitField & 402653184 ) === 0;
+};
+
+method.value = function PromiseInspection$value() {
+ if( !this.isFulfilled() ) {
+ throw new TypeError(
+ "cannot get fulfillment value of a non-fulfilled promise");
+ }
+ return this._resolvedValue;
+};
+
+method.error = function PromiseInspection$error() {
+ if( !this.isRejected() ) {
+ throw new TypeError(
+ "cannot get rejection reason of a non-rejected promise");
+ }
+ return this._resolvedValue;
+};
+
+
+
+
+return PromiseInspection;})();
+
+var PromiseResolver = (function() {
+
+function PromiseResolver( promise ) {
+ this.promise = promise;
+}
+var method = PromiseResolver.prototype;
+
+method.toString = function PromiseResolver$toString() {
+ return "[object PromiseResolver]";
+};
+
+method.fulfill = function PromiseResolver$fulfill( value ) {
+ if( this.promise._tryAssumeStateOf( value, false ) ) {
+ return;
+ }
+ async.invoke( this.promise._fulfill, this.promise, value );
+};
+
+method.reject = function PromiseResolver$reject( reason ) {
+ this.promise._attachExtraTrace( reason );
+ async.invoke( this.promise._reject, this.promise, reason );
+};
+
+method.progress = function PromiseResolver$progress( value ) {
+ async.invoke( this.promise._progress, this.promise, value );
+};
+
+method.cancel = function PromiseResolver$cancel() {
+ async.invoke( this.promise.cancel, this.promise, void 0 );
+};
+
+method.timeout = function PromiseResolver$timeout() {
+ this.reject( new TimeoutError( "timeout" ) );
+};
+
+method.isResolved = function PromiseResolver$isResolved() {
+ return this.promise.isResolved();
+};
+
+method.toJSON = function PromiseResolver$toJSON() {
+ return this.promise.toJSON();
+};
+
+
+return PromiseResolver;})();
+if( typeof module !== "undefined" && module.exports ) {
+ module.exports = Promise;
+}
+else if( typeof define === "function" && define.amd ) {
+ define( "Promise", [], function(){return Promise;});
+}
+else {
+ global.Promise = Promise;
+}
+
+
+return Promise;})(
+ new Function("return this")(),
+ Function,
+ Array,
+ Error,
+ Object
+);
diff --git a/test/mocha/helpers/error.js b/test/mocha/helpers/error.js
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/test/mocha/helpers/error.js
@@ -0,0 +1 @@
+
diff --git a/test/mocha/helpers/reasons.js b/test/mocha/helpers/reasons.js
new file mode 100644
index 0000000..9c24281
--- /dev/null
+++ b/test/mocha/helpers/reasons.js
@@ -0,0 +1,56 @@
+"use strict";
+
+// This module exports some valid rejection reason factories, keyed by human-readable versions of their names.
+
+var adapter = global.adapter;
+var fulfilled = adapter.fulfilled;
+var rejected = adapter.rejected;
+
+var dummy = { dummy: "dummy" };
+
+exports["`undefined`"] = function () {
+ return undefined;
+};
+
+exports["`null`"] = function () {
+ return null;
+};
+
+exports["`false`"] = function () {
+ return false;
+};
+
+exports["`0`"] = function () {
+ return 0;
+};
+
+exports["an error"] = function () {
+ return new Error();
+};
+
+exports["an error without a stack"] = function () {
+ var error = new Error();
+ delete error.stack;
+
+ return error;
+};
+
+exports["a date"] = function () {
+ return new Date();
+};
+
+exports["an object"] = function () {
+ return {};
+};
+
+exports["an always-pending thenable"] = function () {
+ return { then: function () { } };
+};
+
+exports["a fulfilled promise"] = function () {
+ return fulfilled(dummy);
+};
+
+exports["a rejected promise"] = function () {
+ return rejected(dummy);
+};
diff --git a/test/mocha/helpers/testThreeCases.js b/test/mocha/helpers/testThreeCases.js
new file mode 100644
index 0000000..6455157
--- /dev/null
+++ b/test/mocha/helpers/testThreeCases.js
@@ -0,0 +1,64 @@
+"use strict";
+
+var adapter = global.adapter;
+var fulfilled = adapter.fulfilled;
+var rejected = adapter.rejected;
+var pending = adapter.pending;
+
+function success(done) {
+ return function() {
+ done();
+ };
+}
+
+function fail(done) {
+ return function(err) {
+ done(err);
+ };
+}
+
+function handlePromise(val, done) {
+ if (val && typeof val.then === "function") {
+ val.then(success(done), fail(done));
+ }
+}
+
+exports.testFulfilled = function (value, test) {
+ specify("already-fulfilled", function (done) {
+ handlePromise(test(fulfilled(value), done), done);
+ });
+
+ specify("immediately-fulfilled", function (done) {
+ var tuple = pending();
+ handlePromise(test(tuple.promise, done), done);
+ tuple.fulfill(value);
+ });
+
+ specify("eventually-fulfilled", function (done) {
+ var tuple = pending();
+ handlePromise(test(tuple.promise, done), done);
+ setTimeout(function () {
+ tuple.fulfill(value);
+ }, 1);
+ });
+};
+
+exports.testRejected = function (reason, test) {
+ specify("already-rejected", function (done) {
+ handlePromise(test(rejected(reason), done), done);
+ });
+
+ specify("immediately-rejected", function (done) {
+ var tuple = pending();
+ handlePromise(test(tuple.promise, done), done);
+ tuple.reject(reason);
+ });
+
+ specify("eventually-rejected", function (done) {
+ var tuple = pending();
+ handlePromise(test(tuple.promise, done), done);
+ setTimeout(function () {
+ tuple.reject(reason);
+ }, 1);
+ });
+};
diff --git a/test/mocha/helpers/thenables.js b/test/mocha/helpers/thenables.js
new file mode 100644
index 0000000..4028797
--- /dev/null
+++ b/test/mocha/helpers/thenables.js
@@ -0,0 +1,146 @@
+"use strict";
+
+var adapter = global.adapter;
+var fulfilled = adapter.fulfilled;
+var rejected = adapter.rejected;
+var pending = adapter.pending;
+
+var other = { other: "other" }; // a value we don't want to be strict equal to
+
+exports.fulfilled = {
+ "a synchronously-fulfilled custom thenable": function (value) {
+ return {
+ then: function (onFulfilled) {
+ onFulfilled(value);
+ }
+ };
+ },
+
+ "an asynchronously-fulfilled custom thenable": function (value) {
+ return {
+ then: function (onFulfilled) {
+ setTimeout(function () {
+ onFulfilled(value);
+ }, 1);
+ }
+ };
+ },
+
+ "a synchronously-fulfilled one-time thenable": function (value) {
+ var numberOfTimesThenRetrieved = 0;
+ var ret = Object.create(null, {
+ then: {
+ get: function () {
+
+ if (numberOfTimesThenRetrieved === 0) {
+ ++numberOfTimesThenRetrieved;
+ return function (onFulfilled) {
+ onFulfilled(value);
+ };
+ }
+ return null;
+ }
+ }
+ });
+ return ret;
+ },
+
+ "a thenable that tries to fulfill twice": function (value) {
+ return {
+ then: function (onFulfilled) {
+ onFulfilled(value);
+ onFulfilled(other);
+ }
+ };
+ },
+
+ "a thenable that fulfills but then throws": function (value) {
+ return {
+ then: function (onFulfilled) {
+ onFulfilled(value);
+ throw other;
+ }
+ };
+ },
+
+ "an already-fulfilled promise": function (value) {
+ return fulfilled(value);
+ },
+
+ "an eventually-fulfilled promise": function (value) {
+ var tuple = pending();
+ setTimeout(function () {
+ tuple.fulfill(value);
+ }, 1);
+ return tuple.promise;
+ }
+};
+
+exports.rejected = {
+ "a synchronously-rejected custom thenable": function (reason) {
+ return {
+ then: function (onFulfilled, onRejected) {
+ onRejected(reason);
+ }
+ };
+ },
+
+ "an asynchronously-rejected custom thenable": function (reason) {
+ return {
+ then: function (onFulfilled, onRejected) {
+ setTimeout(function () {
+ onRejected(reason);
+ }, 1);
+ }
+ };
+ },
+
+ "a synchronously-rejected one-time thenable": function (reason) {
+ var numberOfTimesThenRetrieved = 0;
+ return Object.create(null, {
+ then: {
+ get: function () {
+ if (numberOfTimesThenRetrieved === 0) {
+ ++numberOfTimesThenRetrieved;
+ return function (onFulfilled, onRejected) {
+ onRejected(reason);
+ };
+ }
+ return null;
+ }
+ }
+ });
+ },
+
+ "a thenable that immediately throws in `then`": function (reason) {
+ return {
+ then: function () {
+ throw reason;
+ }
+ };
+ },
+
+ "an object with a throwing `then` accessor": function (reason) {
+ return Object.create(null, {
+ then: {
+ get: function () {
+ throw reason;
+ }
+ }
+ });
+ },
+
+ "an already-rejected promise": function (reason) {
+ var ret = rejected(reason);
+ ret.caught(function(){});
+ return ret;
+ },
+
+ "an eventually-rejected promise": function (reason) {
+ var tuple = pending();
+ setTimeout(function () {
+ tuple.reject(reason);
+ }, 1);
+ return tuple.promise;
+ }
+};
diff --git a/test/mocha/helpers/util.js b/test/mocha/helpers/util.js
new file mode 100644
index 0000000..22ece67
--- /dev/null
+++ b/test/mocha/helpers/util.js
@@ -0,0 +1,280 @@
+var assert = require("assert");
+var token = {};
+module.exports = {
+ awaitGlobalException: function(fn) {
+ function replaceListeners(by) {
+ var single = typeof by === "function";
+ if (process.title === "browser") {
+ var original = window.onerror;
+ window.onerror = single ? function(message, file, line, column, e) {
+ return by(e);
+ } : by[0];
+ return [original];
+ } else {
+ var original = process.listeners("uncaughtException");
+ process.removeAllListeners("uncaughtException");
+ if (single) by = [by];
+ by.forEach(function(listener) {
+ process.on("uncaughtException", listener);
+ });
+ return original;
+ }
+ }
+ return new Promise(function(resolve, reject) {
+ var listeners = replaceListeners(function(e) {
+ var err;
+ var ret;
+ try {
+ ret = fn(e);
+ } catch (e) {
+ err = e;
+ }
+ if (!err && ret === false) return;
+ replaceListeners(listeners);
+ Promise.delay(1).then(function() {
+ if (err) reject(err);
+ resolve();
+ });
+ });
+ });
+ },
+
+ awaitLateQueue: function(fn) {
+ return new Promise(function(res, rej) {
+ Promise._async.invokeLater(function() {
+ try {
+ var result = fn();
+ res(result);
+ } catch(e) {
+ rej(e);
+ }
+ }, null, null);
+ });
+ },
+
+ awaitProcessExit: function(fn) {
+ if (typeof process !== "undefined" && typeof process.execPath === "string") {
+ var exit;
+ return new Promise(function(resolve, reject) {
+ exit = process.exit;
+ process.exit = function(code) {
+ try {
+ assert(code != 0);
+ fn();
+ resolve();
+ } catch (e) {
+ reject(e);
+ }
+ };
+ }).lastly(function() {
+ process.exit = exit;
+ });
+ } else {
+ return Promise.delay(1);
+ }
+ },
+
+ addDeferred: function(Promise) {
+ Promise.defer = Promise.pending = function() {
+ var ret = {};
+ ret.promise = new Promise(function(resolve, reject) {
+ ret.resolve = ret.fulfill = resolve;
+ ret.reject = reject;
+ });
+ return ret;
+ };
+ return Promise;
+ },
+
+ returnToken: function() {
+ return token;
+ },
+
+ assertToken: function(val) {
+ assert.strictEqual(token, val);
+ },
+
+ getSpy: function() {
+ var resolve, reject;
+ var promise = new Promise(function() {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+ var ret = function(fn) {
+ ret.callback = fn;
+ return ret.node;
+ };
+ ret.promise = promise;
+ ret.node = function() {
+ try {
+ ret.callback.apply(this, arguments);
+ resolve();
+ } catch (e) {
+ reject(e);
+ }
+ };
+ return ret;
+ },
+
+ awaitDomainException: function(onError, fn) {
+ var domain;
+ var resolve, reject;
+ var promise = new Promise(function() {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+ domain = require('domain').create();
+ domain.on("error", function(e) {
+ try {
+ onError(e);
+ resolve();
+ } catch (err) {
+ reject(err);
+ }
+ });
+ domain.run(fn);
+ return promise;
+ },
+
+ onUnhandledFail: function(testFunction) {
+ Promise._unhandledRejectionClear();
+ return new Promise(function(resolve, reject) {
+ var err = new Error("Reporting handled rejection as unhandled from: " +
+ testFunction);
+ Promise.onPossiblyUnhandledRejection(function() {
+ reject(err);
+ });
+ Promise.delay(150).then(function() {
+ Promise._unhandledRejectionCheck();
+ resolve();
+ });
+ }).lastly(function() {
+ Promise.onPossiblyUnhandledRejection(null);
+ });
+ },
+
+ onUnhandledSucceed: function(testAgainst, count) {
+ return new Promise(function(resolveTest, reject) {
+ var total = typeof count === "number" ? count : 1;
+ var cur = 0;
+
+ function resolve(e) {
+ cur++;
+ if (cur >= total) {
+ resolveTest(e);
+ }
+ }
+
+ Promise.onPossiblyUnhandledRejection(function(e){
+ if (testAgainst !== undefined) {
+ try {
+ if (typeof testAgainst === "function") {
+ assert(testAgainst(e));
+ }
+ else {
+ assert.equal(testAgainst, e);
+ }
+ resolve(e);
+ }
+ catch (e) {
+ reject(e);
+ }
+ } else {
+ resolve(e);
+ }
+ });
+ Promise.delay(50).then(function() {
+ Promise._unhandledRejectionCheck();
+ return Promise.delay(1);
+ }).then(function() {
+ var message = "Expected onPossiblyUnhandledRejection to be called " +
+ total + " times but it was only called " + cur + " times";
+ reject(new Error(message));
+ });
+ }).lastly(function() {
+ Promise.onPossiblyUnhandledRejection(null);
+ });
+
+ },
+
+ //Used in expressions like: onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ //If strict mode is supported NFEs work, if it is not, NFEs don't work but arguments.callee does
+ isStrictModeSupported: (function() {
+ try {
+ new Function("'use strict'; with({});");
+ return false;
+ }
+ catch (e) {
+ return true;
+ }
+ })(),
+
+ noop: function(v) {
+ return v;
+ },
+
+ isSubset: function(subset, superset) {
+ var i, subsetLen;
+
+ subsetLen = subset.length;
+
+ if (subsetLen > superset.length) {
+ return false;
+ }
+
+ for(i = 0; i -1;
+ },
+
+ fakeResolved: function(val) {
+ return {
+ then: function(callback) {
+ return fakeResolved(callback ? callback(val) : val);
+ }
+ };
+ },
+
+ fakeRejected: function(reason) {
+ return {
+ then: function(callback, errback) {
+ return errback ? fakeResolved(errback(reason)) : fakeRejected(reason);
+ }
+ };
+ },
+
+ assertFulfilled: function(p, v) {
+ assert.strictEqual(p.value(), v);
+ },
+
+ assertRejected: function(p, v) {
+ assert.strictEqual(p.error(), v);
+ },
+
+ ecmaScript6Collections: (typeof Set === "function" &&
+ typeof Symbol !== "undefined" &&
+ Symbol.iterator &&
+ typeof ((new Set())[Symbol.iterator]().next) === "function"),
+
+ ecmaScript5: (function() {"use strict"
+ return this === undefined;
+ })(),
+ isNodeJS: typeof process !== "undefined" && typeof process.execPath === "string"
+};
+
+if (module.exports.isNodeJS) {
+ var version = process.versions.node.split(".").map(Number);
+ module.exports.isRecentNode = version[0] > 0;
+ module.exports.isOldNode = !module.exports.isRecentNode;
+} else {
+ module.exports.isOldNode = false;
+ module.exports.isRecentNode = false;
+}
diff --git a/test/mocha/is.js b/test/mocha/is.js
new file mode 100644
index 0000000..3987402
--- /dev/null
+++ b/test/mocha/is.js
@@ -0,0 +1,15 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+describe("Promise.is", function() {
+ it("should return true for trusted promise", function() {
+ assert.strictEqual(Promise.is(new Promise(function(){})), true);
+ });
+ it("should return false for untrusted promise", function() {
+ assert.strictEqual(Promise.is({
+ then: function() {}
+ }), false);
+ });
+});
diff --git a/test/mocha/join.js b/test/mocha/join.js
new file mode 100644
index 0000000..08ec62f
--- /dev/null
+++ b/test/mocha/join.js
@@ -0,0 +1,119 @@
+"use strict";
+/*
+Based on When.js tests
+
+Open Source Initiative OSI - The MIT License
+
+http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2011 Brian Cavalier
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+describe("Promise.join-test", function () {
+
+
+
+ specify("should resolve empty input", function() {
+ return Promise.join().then(
+ function(result) {
+ assert.deepEqual(result, []);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should join values", function() {
+ return Promise.join(1, 2, 3).then(
+ function(results) {
+ assert.deepEqual(results, [1, 2, 3]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should join promises array", function() {
+ return Promise.join(Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)).then(
+ function(results) {
+ assert.deepEqual(results, [1, 2, 3]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should join mixed array", function() {
+ return Promise.join(Promise.resolve(1), 2, Promise.resolve(3), 4).then(
+ function(results) {
+ assert.deepEqual(results, [1, 2, 3, 4]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should reject if any input promise rejects", function() {
+ return Promise.join(Promise.resolve(1), Promise.reject(2), Promise.resolve(3)).then(
+ assert.fail,
+ function(err) {
+ assert.deepEqual(err, 2);
+ }
+ );
+ });
+
+ specify("should call last argument as a spread function", function() {
+ return Promise.join(Promise.resolve(1), Promise.resolve(2), Promise.resolve(3), function(a, b, c) {
+ assert(a === 1);
+ assert(b === 2);
+ assert(c === 3);
+ });
+ });
+
+
+ specify("gh-227", function() {
+ function a() {
+ return Promise.join(Promise.resolve(1), function () {
+ throw new Error();
+ });
+ }
+
+ return a().then(assert.fail, function(e) {});
+ });
+
+ specify("should not pass the callback as argument, <5 arguments", function() {
+ return Promise.join(1, 2, 3, function() {
+ assert.strictEqual(arguments.length, 3);
+ });
+ });
+
+ specify("should not pass the callback as argument >5 arguments", function() {
+ return Promise.join(1, 2, 3, 4, 5, 6, 7, function() {
+ assert.strictEqual(arguments.length, 7);
+ });
+ });
+
+ specify("should ensure asynchronity", function() {
+ var sync = false;
+ Promise.join(Promise.resolve(1), Promise.resolve(2), function() {
+ sync = true;
+ });
+ assert.strictEqual(false, sync);
+ })
+});
diff --git a/test/mocha/late_buffer_safety.js b/test/mocha/late_buffer_safety.js
new file mode 100644
index 0000000..d197745
--- /dev/null
+++ b/test/mocha/late_buffer_safety.js
@@ -0,0 +1,37 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var isNodeJS = testUtils.isNodeJS;
+
+function async(cb){
+ return Promise.resolve().nodeify(cb);
+}
+
+if (isNodeJS) {
+ describe("Late buffer", function() {
+ specify("shouldn't stop at first error but continue consumption until everything is consumed", function(){
+ var length = 10;
+ var l = length;
+ var a = 0;
+ while (l--){
+ async(function(){
+ throw (a++);
+ });
+ }
+ var errs = [];
+ return testUtils.awaitGlobalException(function(e) {
+ errs.push(e);
+ if (errs.length === length) {
+ var a = [];
+ for (var i = 0, len = length; i < len; ++i) {
+ a[i] = i;
+ }
+ assert.deepEqual(a, errs);
+ } else {
+ return false;
+ }
+ });
+ });
+ });
+}
diff --git a/test/mocha/long_stack_traces.js b/test/mocha/long_stack_traces.js
new file mode 100644
index 0000000..346ce9a
--- /dev/null
+++ b/test/mocha/long_stack_traces.js
@@ -0,0 +1,537 @@
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var assertLongTrace = require("./helpers/assert_long_trace.js");
+var nodeVersion = typeof process !== "undefined" &&
+ typeof process.version === "string"
+ ? process.version.replace(/[^0-9.]/g, "").split(".").map(Number)
+ : [-1, -1, -1];
+
+// Node's V8 captureStackTrace is completely broken - it returns different
+// results on different runs and sometimes causes this test to fail
+if (!Promise.hasLongStackTraces() ||Â testUtils.isOldNode) return;
+
+describe(".then as context", function() {
+ it("1 level", function() {
+ return Promise.resolve().then(function() {
+ throw new Error();
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [1]);
+ });
+ });
+ it("4 levels", function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ throw new Error();
+ });
+ });
+ });
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [1, 1, 1, 1]);
+ });
+ });
+ it("1 level using promise reject with no stack", function() {
+ return Promise.resolve().then(function() {
+ var e;
+ try {throw new Error()} catch (err){e = err;}
+ e.stack;
+ delete e.stack;
+ return Promise.reject(e);
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [1, 1]);
+ });
+ });
+ it("4 levels using promise reject", function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ var e;
+ try {throw new Error()} catch (err){e = err;}
+ return Promise.reject(e);
+ });
+ });
+ });
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [1, 1, 1, 1]);
+ });
+ });
+ it("Circular 1 level", function() {
+ var i = 0;
+ return (function circle() {
+ if (i++ > 5) throw new Error()
+ return Promise.resolve().then(circle);
+ })().then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [1]);
+ });
+ });
+ it("Circular 4 levels", function() {
+ var i = 0;
+ return (function circle() {
+ if (i++ > 5) throw new Error()
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(circle);
+ });
+ });
+ });
+ })().then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [1, 1, 1, 1]);
+ });
+ });
+
+ it("followers unaffected", function() {
+ return Promise.resolve().then(function() {
+ return new Promise(function(res) {
+ res(Promise.delay(13).then(function() {
+ return new Promise(function(res) {
+ res(Promise.delay(13).then(function() {
+ throw new Error();
+ }));
+ });
+ }));
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 5 + 1, [1, 1, 1, 1, 1]);
+ throw new Error();
+ });
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 2 + 1, [1, 1]);
+ });
+ });
+
+ it("3 distinct episodes of circularity with unique frames in between", function() {
+ var i = 0;
+ var j = 0;
+ var k = 0;
+
+ function circle1() {
+ if (i++ > 5) return u1_1();
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(circle1);
+ });
+ });
+ });
+ }
+
+ function circle2() {
+ if (j++ > 5) return u2_1();
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(circle2);
+ });
+ });
+ });
+ }
+
+ function circle3() {
+ if (k++ > 5) return u3_1();
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(circle3);
+ });
+ });
+ });
+ }
+
+ function u1_1() {
+ return Promise.resolve().then(u1_2);
+ }
+
+ function u1_2() {
+ return Promise.resolve().then(circle2);
+ }
+
+ function u2_1() {
+ return Promise.resolve().then(u2_2);
+ }
+
+ function u2_2() {
+ return Promise.resolve().then(circle3);
+ }
+
+ function u3_1() {
+ return Promise.resolve().then(u3_2);
+ }
+
+ function u3_2() {
+ return Promise.resolve().then(function() {
+ throw new Error("The error");
+ });
+ }
+
+ return circle1().then(assert.fail, function(e) {
+ assertLongTrace(e,
+ 1 + 1 + 1 + 1,
+ [
+ 1, 1, 2, 1, 1,
+ 1, 1, 2, 1, 1,
+ 1, 1, 2, 1, 1
+ ]);
+ });
+ });
+});
+
+describe(".spread as context", function() {
+ it("1 level", function() {
+ return Promise.resolve([]).spread(function() {
+ throw new Error();
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [1]);
+ });
+ });
+ it("4 levels", function() {
+ return Promise.resolve([]).spread(function() {
+ return Promise.resolve([]).spread(function() {
+ return Promise.resolve([]).spread(function() {
+ return Promise.resolve([]).spread(function() {
+ throw new Error();
+ });
+ });
+ });
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [1, 1, 1, 1]);
+ });
+ });
+});
+
+describe("constructor as context", function() {
+ it("0 level", function() {
+ return new Promise(function() {
+ throw new Error();
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1, []);
+ });
+ });
+ it("1 level", function() {
+ return new Promise(function(res) {
+ res(new Promise(function() {
+ throw new Error();
+ }))
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [2]);
+ });
+ });
+ it("0 level, no stack property", function() {
+ return new Promise(function(_ ,rej) {
+ var e = new Error();
+ e.stack;
+ delete e.stack;
+ rej(e);
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1, [1]);
+ });
+ });
+ it("1 level, no stack property", function() {
+ return new Promise(function(res) {
+ res(new Promise(function(_, rej) {
+ var e = new Error();
+ e.stack;
+ delete e.stack;
+ rej(e);
+ }))
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [1, 1]);
+ });
+ });
+
+ it("4 levels", function() {
+ return new Promise(function(res) {
+ res(new Promise(function(res) {
+ res(new Promise(function(res) {
+ res(new Promise(function(res) {
+ res(new Promise(function(res) {
+ throw new Error();
+ }));
+ }));
+ }));
+ }));
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [2, 1, 1, 1]);
+ });
+ });
+});
+
+describe(".join as context", function() {
+ it("0 level", function() {
+ var err;
+ try {throw new Error(); } catch(e) {err = e;};
+ return Promise.join(1, 2, Promise.reject(err), function() {
+ throw new Error();
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 0 + 1, []);
+ });
+ });
+ it("1 level", function() {
+ return Promise.join(1, 2, 3, function() {
+ throw new Error();
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [1]);
+ });
+ });
+ it("4 levels", function() {
+ return Promise.join(1, 2, 3, function() {
+ return Promise.join(1, 2, 3, function() {
+ return Promise.join(1, 2, 3, function() {
+ return Promise.join(1, 2, 3, function() {
+ throw new Error();
+ });
+ });
+ });
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [1, 1, 1, 1]);
+ });
+ });
+});
+
+describe(".map as context", function() {
+ it("1 level", function() {
+ return Promise.map([1,2,3], function() {
+ throw new Error();
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [1]);
+ });
+ });
+ it("4 levels", function() {
+ return Promise.map([1,2,3], function() {
+ return Promise.map([1,2,3], function() {
+ return Promise.map([1,2,3], function() {
+ return Promise.map([1,2,3], function() {
+ throw new Error();
+ });
+ });
+ });
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [1, 1, 1, 1]);
+ });
+ });
+});
+
+describe(".reduce as context", function() {
+ it("1 level", function() {
+ return Promise.reduce([1,2,3], function() {
+ throw new Error();
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [1]);
+ });
+ });
+ it("4 levels", function() {
+ return Promise.reduce([1,2,3], function() {
+ return Promise.reduce([1,2,3], function() {
+ return Promise.reduce([1,2,3], function() {
+ return Promise.reduce([1,2,3], function() {
+ throw new Error();
+ });
+ });
+ });
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [1, 1, 1, 1]);
+ });
+ });
+});
+
+describe(".method as context", function() {
+ it("1 level", function() {
+ return Promise.method(function() {
+ throw new Error();
+ })().then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [1]);
+ });
+ });
+ it("4 levels", function() {
+ var second = Promise.method(function() {
+ return third();
+ });
+ var third = Promise.method(function() {
+ return fourth();
+ });
+ var fourth = Promise.method(function() {
+ throw new Error();
+ });
+
+ return Promise.method(function() {
+ return second();
+ })().then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [[1,2], 1, 1, 1]);
+ });
+ });
+});
+
+describe(".try as context", function() {
+ it("1 level", function() {
+ return Promise.attempt(function() {
+ throw new Error();
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [1]);
+ });
+ });
+
+ it("4 levels", function() {
+ return Promise.attempt(function() {
+ return Promise.attempt(function() {
+ return Promise.attempt(function() {
+ return Promise.attempt(function() {
+ throw new Error();
+ });
+ });
+ });
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [1, 1, 1, 1]);
+ });
+ });
+});
+
+describe(".using as context", function() {
+ it("0 level", function() {
+ var err;
+ try {throw new Error(); } catch(e) {err = e};
+ return Promise.using(1, 2, Promise.reject(err), function() {
+ throw new Error();
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 0 + 1, []);
+ });
+ });
+ it("1 level", function() {
+ return Promise.using(1, 2, 3, function() {
+ throw new Error();
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [1]);
+ });
+ });
+ it("4 levels", function() {
+ return Promise.using(1, 2, 3, function() {
+ return Promise.using(1, 2, 3, function() {
+ return Promise.using(1, 2, 3, function() {
+ return Promise.using(1, 2, 3, function() {
+ throw new Error();
+ });
+ });
+ });
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [1, 1, 1, 1]);
+ });
+ });
+});
+
+describe("Long stack traces from thenable rejections", function() {
+ var es5 = (function(){"use strict"; return this})() === undefined;
+ // Todo, for 100% coverage thenables should be tested with every
+ // feature, not just then
+ var syncRej = function() {
+ return {
+ then: function(_, rej) {
+ rej(new Error());
+ }
+ };
+ };
+ var asyncRej = function() {
+ return {
+ then: function(_, rej) {
+ setTimeout(function() {
+ rej(new Error());
+ }, 1);
+ }
+ };
+ };
+ var throwRej = function() {
+ return {
+ then: function(_, rej) {
+ throw(new Error());
+ }
+ };
+ };
+ var thenGetRej = function() {
+ var ret = {};
+ Object.defineProperty(ret, "then", {
+ get: function() {
+ throw new Error()
+ }
+ });
+ return ret;
+ };
+ it("1 level sync reject", function() {
+ return Promise.resolve().then(function() {
+ return syncRej();
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1+1, [1]);
+ });
+ });
+ it("4 levels sync reject", function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return syncRej();
+ });
+ });
+ });
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [1, 1, 1, 1]);
+ });
+ });
+ it("1 level async reject", function() {
+ return Promise.resolve().then(function() {
+ return asyncRej();
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [1]);
+ });
+ });
+ it("4 levels async reject", function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return asyncRej();
+ });
+ });
+ });
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [1, 1, 1, 1]);
+ });
+ });
+ it("1 level throw", function() {
+ return Promise.resolve().then(function() {
+ return throwRej();
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [1]);
+ });
+ });
+ it("4 levels throw", function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return throwRej();
+ });
+ });
+ });
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [1, 1, 1, 1]);
+ });
+ });
+ it("1 level getter throw", function() {
+ return Promise.resolve().then(function() {
+ return thenGetRej();
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 1 + 1, [1]);
+ });
+ });
+ it("4 levels getter throw", function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return Promise.resolve().then(function() {
+ return thenGetRej();
+ });
+ });
+ });
+ }).then(assert.fail, function(e) {
+ assertLongTrace(e, 4 + 1, [1, 1, 1, 1]);
+ });
+ });
+});
diff --git a/test/mocha/map.js b/test/mocha/map.js
new file mode 100644
index 0000000..2387290
--- /dev/null
+++ b/test/mocha/map.js
@@ -0,0 +1,295 @@
+"use strict";
+/*
+Based on When.js tests
+
+Open Source Initiative OSI - The MIT License
+
+http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2011 Brian Cavalier
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+describe("Promise.map-test", function () {
+
+ function mapper(val) {
+ return val * 2;
+ }
+
+ function deferredMapper(val) {
+ return Promise.delay(1, mapper(val));
+ }
+
+ specify("should map input values array", function() {
+ var input = [1, 2, 3];
+ return Promise.map(input, mapper).then(
+ function(results) {
+ assert.deepEqual(results, [2,4,6]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should map input promises array", function() {
+ var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
+ return Promise.map(input, mapper).then(
+ function(results) {
+ assert.deepEqual(results, [2,4,6]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should map mixed input array", function() {
+ var input = [1, Promise.resolve(2), 3];
+ return Promise.map(input, mapper).then(
+ function(results) {
+ assert.deepEqual(results, [2,4,6]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should map input when mapper returns a promise", function() {
+ var input = [1,2,3];
+ return Promise.map(input, deferredMapper).then(
+ function(results) {
+ assert.deepEqual(results, [2,4,6]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should accept a promise for an array", function() {
+ return Promise.map(Promise.resolve([1, Promise.resolve(2), 3]), mapper).then(
+ function(result) {
+ assert.deepEqual(result, [2,4,6]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should throw a TypeError when input promise does not resolve to an array", function() {
+ return Promise.map(Promise.resolve(123), mapper).caught(TypeError, function(e){
+ });
+ });
+
+ specify("should map input promises when mapper returns a promise", function() {
+ var input = [Promise.resolve(1),Promise.resolve(2),Promise.resolve(3)];
+ return Promise.map(input, mapper).then(
+ function(results) {
+ assert.deepEqual(results, [2,4,6]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should reject when input contains rejection", function() {
+ var input = [Promise.resolve(1), Promise.reject(2), Promise.resolve(3)];
+ return Promise.map(input, mapper).then(
+ assert.fail,
+ function(result) {
+ assert(result === 2);
+ }
+ );
+ });
+
+ specify("should call mapper asynchronously on values array", function() {
+ var calls = 0;
+ function mapper(val) {
+ calls++;
+ }
+
+ var input = [1, 2, 3];
+ var p = Promise.map(input, mapper);
+ assert(calls === 0);
+ return p.then(function() {
+ assert(calls === 3);
+ });
+ });
+
+ specify("should call mapper asynchronously on promises array", function() {
+ var calls = 0;
+ function mapper(val) {
+ calls++;
+ }
+
+ var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
+ var p = Promise.map(input, mapper);
+ assert(calls === 0);
+ return p.then(function() {
+ assert(calls === 3);
+ });
+ });
+
+ specify("should call mapper asynchronously on mixed array", function() {
+ var calls = 0;
+ function mapper(val) {
+ calls++;
+ }
+
+ var input = [1, Promise.resolve(2), 3];
+ var p = Promise.map(input, mapper);
+ assert(calls === 0);
+ return p.then(function() {
+ assert(calls === 3);
+ });
+ });
+});
+
+describe("Promise.map-test with concurrency", function () {
+
+ var concurrency = {concurrency: 2};
+
+ function mapper(val) {
+ return val * 2;
+ }
+
+ function deferredMapper(val) {
+ return Promise.delay(1, mapper(val));
+ }
+
+ specify("should map input values array with concurrency", function() {
+ var input = [1, 2, 3];
+ return Promise.map(input, mapper, concurrency).then(
+ function(results) {
+ assert.deepEqual(results, [2,4,6]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should map input promises array with concurrency", function() {
+ var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
+ return Promise.map(input, mapper, concurrency).then(
+ function(results) {
+ assert.deepEqual(results, [2,4,6]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should map mixed input array with concurrency", function() {
+ var input = [1, Promise.resolve(2), 3];
+ return Promise.map(input, mapper, concurrency).then(
+ function(results) {
+ assert.deepEqual(results, [2,4,6]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should map input when mapper returns a promise with concurrency", function() {
+ var input = [1,2,3];
+ return Promise.map(input, deferredMapper, concurrency).then(
+ function(results) {
+ assert.deepEqual(results, [2,4,6]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should accept a promise for an array with concurrency", function() {
+ return Promise.map(Promise.resolve([1, Promise.resolve(2), 3]), mapper, concurrency).then(
+ function(result) {
+ assert.deepEqual(result, [2,4,6]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should resolve to empty array when input promise does not resolve to an array with concurrency", function() {
+ return Promise.map(Promise.resolve(123), mapper, concurrency).caught(TypeError, function(e){
+ });
+ });
+
+ specify("should map input promises when mapper returns a promise with concurrency", function() {
+ var input = [Promise.resolve(1),Promise.resolve(2),Promise.resolve(3)];
+ return Promise.map(input, mapper, concurrency).then(
+ function(results) {
+ assert.deepEqual(results, [2,4,6]);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should reject when input contains rejection with concurrency", function() {
+ var input = [Promise.resolve(1), Promise.reject(2), Promise.resolve(3)];
+ return Promise.map(input, mapper, concurrency).then(
+ assert.fail,
+ function(result) {
+ assert(result === 2);
+ }
+ );
+ });
+
+ specify("should not have more than {concurrency} promises in flight", function() {
+ var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ var b = [];
+ var now = Date.now();
+
+ var immediates = [];
+ function immediate(index) {
+ var resolve;
+ var ret = new Promise(function(){resolve = arguments[0]});
+ immediates.push([ret, resolve, index]);
+ return ret;
+ }
+
+ var lates = [];
+ function late(index) {
+ var resolve;
+ var ret = new Promise(function(){resolve = arguments[0]});
+ lates.push([ret, resolve, index]);
+ return ret;
+ }
+
+
+ function promiseByIndex(index) {
+ return index < 5 ? immediate(index) : late(index);
+ }
+
+ function resolve(item) {
+ item[1](item[2]);
+ }
+
+ var ret1 = Promise.map(array, function(value, index) {
+ return promiseByIndex(index).then(function() {
+ b.push(value);
+ });
+ }, {concurrency: 5});
+
+ var ret2 = Promise.delay(100).then(function() {
+ assert.strictEqual(0, b.length);
+ immediates.forEach(resolve);
+ return immediates.map(function(item){return item[0]});
+ }).delay(100).then(function() {
+ assert.deepEqual(b, [0, 1, 2, 3, 4]);
+ lates.forEach(resolve);
+ }).delay(100).then(function() {
+ assert.deepEqual(b, [0, 1, 2, 3, 4, 10, 9, 8, 7, 6 ]);
+ lates.forEach(resolve);
+ }).thenReturn(ret1).then(function() {
+ assert.deepEqual(b, [0, 1, 2, 3, 4, 10, 9, 8, 7, 6, 5]);
+ });
+ return Promise.all([ret1, ret2]);
+ });
+});
diff --git a/test/mocha/method.js b/test/mocha/method.js
new file mode 100644
index 0000000..0fa7fe6
--- /dev/null
+++ b/test/mocha/method.js
@@ -0,0 +1,134 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+
+var obj = {};
+var error = new Error();
+var thrower = Promise.method(function() {
+ throw error;
+});;
+
+var identity = Promise.method(function(val) {
+ return val;
+});
+
+var array = Promise.method(function() {
+ return [].slice.call(arguments);
+});
+
+var receiver = Promise.method(function() {
+ return this;
+});
+
+
+
+describe("Promise.method", function(){
+ specify("should reject when the function throws", function() {
+ var async = false;
+ var ret = thrower().then(assert.fail, function(e) {
+ assert(async);
+ assert(e === error);
+ });
+ async = true;
+ return ret;
+ });
+ specify("should throw when the function is not a function", function() {
+ try {
+ Promise.method(null);
+ }
+ catch (e) {
+ assert(e instanceof TypeError);
+ return;
+ }
+ assert.fail();
+ });
+ specify("should call the function with the given receiver", function(){
+ var async = false;
+ var ret = receiver.call(obj).then(function(val) {
+ assert(async);
+ assert(val === obj);
+ }, assert.fail);
+ async = true;
+ return ret;
+ });
+ specify("should call the function with the given value", function(){
+ var async = false;
+ var ret = identity(obj).then(function(val) {
+ assert(async);
+ assert(val === obj);
+ }, assert.fail);
+ async = true;
+ return ret;
+ });
+ specify("should apply the function if given value is array", function(){
+ var async = false;
+ var ret = array(1, 2, 3).then(function(val) {
+ assert(async);
+ assert.deepEqual(val, [1,2,3]);
+ }, assert.fail);
+ async = true;
+ return ret;
+ });
+
+ specify("should unwrap returned promise", function(){
+ var d = Promise.defer();
+
+ var ret = Promise.method(function(){
+ return d.promise;
+ })().then(function(v){
+ assert(v === 3);
+ })
+
+ setTimeout(function(){
+ d.fulfill(3);
+ }, 1);
+ return ret;
+ });
+ specify("should unwrap returned thenable", function(){
+
+ return Promise.method(function(){
+ return {
+ then: function(f, v) {
+ f(3);
+ }
+ }
+ })().then(function(v){
+ assert(v === 3);
+ });
+ });
+
+ specify("should unwrap a following promise", function() {
+ var resolveF;
+ var f = new Promise(function() {
+ resolveF = arguments[0];
+ });
+ var v = new Promise(function(f) {
+ setTimeout(function() {
+ f(3);
+ }, 1);
+ });
+ resolveF(v);
+ return Promise.method(function(){
+ return f;
+ })().then(function(v){
+ assert(v === 3);
+ });
+ });
+
+ specify("zero arguments length should remain zero", function() {
+ return Promise.method(function(){
+ assert(arguments.length === 0);
+ })();
+ });
+ specify("should retain binding from returned promise", function() {
+ var THIS = {};
+ return Promise.method(function() {
+ return Promise.bind(THIS, 1);
+ })().then(function(value) {
+ assert.strictEqual(THIS, this);
+ assert.strictEqual(1, value);
+ });
+ });
+});
diff --git a/test/mocha/monitoring.js b/test/mocha/monitoring.js
new file mode 100644
index 0000000..0653ed8
--- /dev/null
+++ b/test/mocha/monitoring.js
@@ -0,0 +1,183 @@
+var assert = require("assert");
+var util = require("../../js/debug/util");
+
+describe("monitoring: promise lifecycle events subscriptions", function() {
+ var numCreated = 0;
+ var numChained = 0;
+ var numFulfilled = 0;
+ var numRejected = 0;
+ var on = null;
+ var off = null;
+
+ before(function(){
+ Promise.config({monitoring: true});
+ });
+
+ after(function(){
+ Promise.config({monitoring: false});
+ });
+
+ beforeEach(function(){
+ numCreated = 0;
+ numChained = 0;
+ numFulfilled = 0;
+ numRejected = 0;
+ });
+
+ function nodeOn(eventName, eventHandler) {
+ process.on.call(process, eventName, eventHandler);
+ }
+ function nodeOff(eventName, eventHandler) {
+ process.removeListener.call(process, eventName, eventHandler);
+ }
+ function browserSimpleOn(eventName, eventHandler) {
+ eventName = "on" + eventName.toLowerCase();
+ self[eventName] = eventHandler;
+ }
+ function browserSimpleOff(eventName, eventHandler) {
+ eventName = "on" + eventName.toLowerCase();
+ assert(self[eventName] === eventHandler);
+ delete self[eventName];
+ }
+ function browserDomOn (eventName, eventHandler) {
+ self.addEventListener.call(self, eventName.toLowerCase(),
+ eventHandler);
+ }
+ function browserDomOff (eventName, eventHandler) {
+ self.removeEventListener.call(self, eventName.toLowerCase(),
+ eventHandler);
+ }
+
+ function testCreated(onCreated) {
+ on("promiseCreated", onCreated);
+ var promise = new Promise(function(resolve){resolve()});
+ assert(numCreated === 1);
+ promise = promise.then(function(){});
+ assert(numCreated === 2);
+ off("promiseCreated", onCreated);
+ promise.then(function(){});
+ assert(numCreated === 2);
+ }
+
+ function testChained(onChained) {
+ on("promiseChained", onChained);
+ var promise = new Promise(function(resolve){resolve()});
+ assert(numChained === 0);
+ promise = promise.then(function(){});
+ assert(numChained === 1);
+ off("promiseChained", onChained);
+ promise.then(function(){});
+ assert(numChained === 1);
+ }
+
+ function testRejected(onRejected) {
+ on("promiseRejected", onRejected);
+ assert(numRejected === 0);
+ var promise = new Promise(function(resolve,reject){
+ reject();
+ });
+ assert(numRejected === 1);
+ promise = promise.caught(function(resolve,reject){
+ assert(numRejected === 1);
+ reject();
+ });
+ off("promiseRejected", onRejected);
+ return promise.caught(function(){
+ assert(numRejected === 1);
+ });
+
+ }
+
+ function testFulfilled(onFulfilled) {
+ on("promiseFulfilled", onFulfilled);
+ assert(numFulfilled === 0);
+ var promise = new Promise(function(resolve){resolve()});
+ assert(numFulfilled === 1);
+ promise = promise.then(function(){});
+ assert(numFulfilled === 1);
+ off("promiseFulfilled", onFulfilled);
+ promise.then(function(){});
+ assert(numFulfilled === 1);
+ }
+
+ describe("simple events API", function() {
+
+ before(function() {
+ if (util.isNode) {
+ on = nodeOn;
+ off = nodeOff;
+ } else if (typeof self !== "undefined") {
+ on = browserSimpleOn;
+ off = browserSimpleOff;
+ } else {
+ assert(1===0);
+ }
+ });
+
+ it("promiseCreated", function () {
+ return testCreated(function (promise) {
+ assert(Promise.is(promise));
+ numCreated++
+ });
+ });
+ it("promiseChained", function () {
+ return testChained(function (promise, child) {
+ assert(Promise.is(promise));
+ assert(Promise.is(child));
+ numChained++;
+ });
+ });
+ it("promiseRejected", function () {
+ return testRejected(function (promise) {
+ assert(Promise.is(promise));
+ numRejected++
+ });
+ });
+ it("promiseFulfilled", function () {
+ return testFulfilled(function (promise) {
+ assert(Promise.is(promise));
+ numFulfilled++
+ });
+ });
+ });
+
+ if (!util.isNode) {
+ describe("events API", function() {
+
+ before(function() {
+ on = browserDomOn;
+ off = browserDomOff;
+ });
+
+ it("promiseCreated", function () {
+ return testCreated(function (event) {
+ assert(event.type === "promisecreated");
+ assert(Promise.is(event.detail.promise));
+ numCreated++;
+ });
+ });
+ it("promiseChained", function () {
+ return testChained(function (event) {
+ assert(event.type === "promisechained");
+ assert(Promise.is(event.detail.promise));
+ assert(Promise.is(event.detail.child));
+ numChained++;
+ });
+ });
+ it("promiseRejected", function () {
+ return testRejected(function (event) {
+ numRejected++;
+ assert(event.type === "promiserejected");
+ assert(Promise.is(event.detail.promise));
+ });
+ });
+ it("promiseFulfilled", function () {
+ return testFulfilled(function (event) {
+ numFulfilled++;
+ assert(event.type === "promisefulfilled");
+ assert(Promise.is(event.detail.promise));
+ });
+ });
+ });
+ }
+});
diff --git a/test/mocha/multiple-copies.js b/test/mocha/multiple-copies.js
new file mode 100644
index 0000000..77ab84b
--- /dev/null
+++ b/test/mocha/multiple-copies.js
@@ -0,0 +1,18 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+if (testUtils.isNodeJS) {
+ describe("multiple copies", function() {
+ specify("are being loaded", function() {
+ var a = require("../../js/debug/bluebird.js");
+ Object.keys(require.cache).forEach(function(key) {
+ if (/debug/.test(key))
+ delete require.cache[key];
+ });
+ var b = require("../../js/debug/bluebird.js");
+ assert.notEqual(a, b);
+ });
+ });
+}
diff --git a/test/mocha/no_conflict.js b/test/mocha/no_conflict.js
new file mode 100644
index 0000000..9c82c10
--- /dev/null
+++ b/test/mocha/no_conflict.js
@@ -0,0 +1,21 @@
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+describe("Promise.noConflict", function() {
+ specify("should work", function() {
+ var glob = typeof window !== "undefined" ? window : global;
+ var bluebird = Promise.noConflict();
+ assert(bluebird !== Promise);
+ glob.Promise = null;
+ assert.strictEqual(bluebird, bluebird.noConflict());
+ try {
+ delete glob.Promise;
+ assert.strictEqual(bluebird, bluebird.noConflict());
+ } catch (e) {
+ assert.strictEqual(bluebird, bluebird.noConflict());
+ }
+ glob.Promise = bluebird;
+ assert.strictEqual(bluebird, Promise.noConflict());
+ glob.Promise = bluebird;
+ })
+});
diff --git a/test/mocha/nodeify.js b/test/mocha/nodeify.js
new file mode 100644
index 0000000..7883209
--- /dev/null
+++ b/test/mocha/nodeify.js
@@ -0,0 +1,200 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var awaitGlobalException = testUtils.awaitGlobalException;
+var sinon = require("sinon");
+var isNodeJS = testUtils.isNodeJS;
+/*
+Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+*/
+
+describe("nodeify", function () {
+
+ it("calls back with a resolution", function () {
+ var spy = sinon.spy();
+ Promise.resolve(10).nodeify(spy);
+ setTimeout(function(){
+ sinon.assert.calledOnce(spy);
+ sinon.assert.calledWith(spy, null, 10);
+ }, 1);
+ });
+
+ it("calls back with an undefined resolution", function() {
+ var spy = sinon.spy();
+ Promise.resolve().nodeify(spy);
+ setTimeout(function(){
+ sinon.assert.calledOnce(spy);
+ sinon.assert.calledWithExactly(spy, null);
+ }, 1);
+ });
+
+ it("calls back with an error", function () {
+ var spy = sinon.spy();
+ Promise.reject(10).nodeify(spy);
+ setTimeout(function(){
+ sinon.assert.calledOnce(spy);
+ sinon.assert.calledWith(spy, 10);
+ }, 1);
+ });
+
+ it("forwards a promise", function () {
+ return Promise.resolve(10).nodeify().then(function (ten) {
+ assert(10 === ten);
+ });
+ });
+
+ it("returns undefined when a callback is passed", function () {
+ return 'undefined' === typeof Promise.resolve(10).nodeify(function () {});
+ });
+
+});
+var getSpy = testUtils.getSpy;
+if (isNodeJS) {
+ describe("nodeify", function () {
+ var h = [];
+ var e = new Error();
+ function thrower() {
+ throw e;
+ }
+
+ it("throws normally in the node process if the function throws", function() {
+ var promise = Promise.resolve(10);
+ var turns = 0;
+ process.nextTick(function(){
+ turns++;
+ });
+ promise.nodeify(thrower);
+ return awaitGlobalException(function(err) {
+ assert(err === e);
+ assert(turns === 1);
+ });
+ });
+
+ it("always returns promise for now", function() {
+ return Promise.resolve(3).nodeify().then(function() {
+ var a = 0;
+ Promise.resolve(3).nodeify(function(){
+ a++;
+ }).then(function() {
+ assert(1 == 1);
+ });
+ });
+ });
+
+ it("should spread arguments with spread option", function() {
+ var spy = getSpy();
+ Promise.resolve([1,2,3]).nodeify(spy(function(err, a, b, c) {
+ assert(err === null);
+ assert(a === 1);
+ assert(b === 2);
+ assert(c === 3);
+ }), {spread: true});
+ return spy.promise;
+ });
+
+ describe("promise rejected with falsy values", function() {
+ specify("no reason", function() {
+ var spy = getSpy();
+ Promise.reject().nodeify(spy(function(err) {
+ assert.strictEqual(arguments.length, 1);
+ assert.strictEqual(err.cause, undefined);
+ }));
+ return spy.promise;
+ });
+ specify("null reason", function() {
+ var spy = getSpy();
+ Promise.reject(null).nodeify(spy(function(err) {
+ assert.strictEqual(arguments.length, 1);
+ assert.strictEqual(err.cause, null);
+ }));
+ return spy.promise;
+ });
+ specify("nodefying a follewer promise", function() {
+ var spy = getSpy();
+ new Promise(function(resolve, reject) {
+ resolve(new Promise(function(_, reject) {
+ setTimeout(function() {
+ reject();
+ }, 1);
+ }))
+ }).nodeify(spy(function(err) {
+ assert.strictEqual(arguments.length, 1);
+ assert.strictEqual(err.cause, undefined);
+ }));
+ return spy.promise;
+ });
+ specify("nodefier promise becomes follower", function() {
+ var spy = getSpy();
+ Promise.resolve(1).then(function() {
+ return new Promise(function(_, reject) {
+ setTimeout(function() {
+ reject();
+ }, 1);
+ });
+ }).nodeify(spy(function(err) {
+ assert.strictEqual(arguments.length, 1);
+ assert.strictEqual(err.cause, undefined);
+ }));
+ return spy.promise;
+ });
+ });
+ it("should wrap arguments with spread option", function() {
+ var spy = getSpy();
+ Promise.resolve([1,2,3]).nodeify(spy(function(err, a, b, c) {
+ assert(err === null);
+ assert(a === 1);
+ assert(b === 2);
+ assert(c === 3);
+ }), {spread: true});
+ return spy.promise;
+ });
+
+ it("should work then result is not an array", function() {
+ var spy = getSpy();
+ Promise.resolve(3).nodeify(spy(function(err, a) {
+ assert(err === null);
+ assert(a === 3);
+ }), {spread: true});
+ return spy.promise;
+ });
+
+ it("should work if the callback throws when spread", function() {
+ var err = new Error();
+ Promise.resolve([1,2,3]).nodeify(function(_, a) {
+ throw err;
+ }, {spread: true});
+
+ return awaitGlobalException(function(e) {
+ assert.strictEqual(err, e);
+ });
+ });
+
+ it("should work if the callback throws when rejected", function() {
+ var err = new Error();
+ Promise.reject(new Error()).nodeify(function(_, a) {
+ throw err;
+ });
+
+ return awaitGlobalException(function(e) {
+ assert.strictEqual(err, e);
+ });
+ });
+ });
+}
diff --git a/test/mocha/promise_array.js b/test/mocha/promise_array.js
new file mode 100644
index 0000000..39ba5fb
--- /dev/null
+++ b/test/mocha/promise_array.js
@@ -0,0 +1,178 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+/*!
+ *
+Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+*/
+describe("all", function () {
+ it("fulfills when passed an empty array", function () {
+ return Promise.all([]);
+ });
+
+ if (testUtils.ecmaScript6Collections) {
+ it("supports iterables", function () {
+ return Promise.all(new Set([1, 2, 3])).then(function(v) {
+ assert.deepEqual([1,2,3].sort(), v.sort());
+ });
+ });
+ }
+
+ it("rejects after any constituent promise is rejected", function () {
+ var toResolve = Promise.defer(); // never resolve
+ var toReject = Promise.defer();
+ var promises = [toResolve.promise, toReject.promise];
+ var promise = Promise.all(promises);
+
+ toReject.reject(new Error("Rejected"));
+
+ promise.then(assert.fail, function(e){
+ //Unhandled rejection
+ });
+
+ return Promise.delay(1)
+ .then(function () {
+ assert.equal(promise.isRejected(), true);
+ })
+ .timeout(1000);
+
+
+ });
+
+ it("resolves foreign thenables", function () {
+ var normal = Promise.resolve(1);
+ var foreign = { then: function (f) { f(2); } };
+
+ return Promise.all([normal, foreign])
+ .then(function (result) {
+ assert.deepEqual(result,[1, 2]);
+ });
+ });
+
+
+ it("fulfills when passed an sparse array", function () {
+ var toResolve = Promise.defer();
+ var promises = [];
+ promises[0] = Promise.resolve(0);
+ promises[2] = toResolve.promise;
+ var promise = Promise.all(promises);
+
+ toResolve.resolve(2);
+
+ return promise.then(function (result) {
+ assert.deepEqual(result, [0, void 0, 2]);
+ });
+ });
+});
+
+/*
+Based on When.js tests
+
+Open Source Initiative OSI - The MIT License
+
+http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2011 Brian Cavalier
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
+describe("Promise.all-test", function () {
+
+ specify("should resolve empty input", function() {
+ return Promise.all([]).then(
+ function(result) {
+ assert.deepEqual(result, []);
+ }, assert.fail
+ );
+ });
+
+ specify("should resolve values array", function() {
+ var input = [1, 2, 3];
+ return Promise.all(input).then(
+ function(results) {
+ assert.deepEqual(results, input);
+ }, assert.fail
+ );
+ });
+
+ specify("should resolve promises array", function() {
+ var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
+ return Promise.all(input).then(
+ function(results) {
+ assert.deepEqual(results, [1, 2, 3]);
+ }, assert.fail
+ );
+ });
+
+ specify("should not resolve sparse array input", function() {
+ var input = [, 1, , 1, 1 ];
+ return Promise.all(input).then(
+ function(results) {
+ assert.deepEqual(results, [void 0, 1, void 0, 1, 1]);
+ }, assert.fail
+ );
+ });
+
+ specify("should reject if any input promise rejects", function() {
+ var input = [Promise.resolve(1), Promise.reject(2), Promise.resolve(3)];
+ return Promise.all(input).then(
+ assert.fail,
+ function(err) {
+ assert.deepEqual(err, 2);
+ }
+ );
+ });
+
+ specify("should accept a promise for an array", function() {
+ var expected, input;
+
+ expected = [1, 2, 3];
+ input = Promise.resolve(expected);
+
+ return Promise.all(input).then(
+ function(results) {
+ assert.deepEqual(results, expected);
+ }, assert.fail
+ );
+ });
+
+ specify("should reject when input promise does not resolve to array", function() {
+ return Promise.all(Promise.resolve(1)).caught(TypeError, function(e){
+ });
+ });
+
+});
diff --git a/test/mocha/promisify.js b/test/mocha/promisify.js
new file mode 100644
index 0000000..87b1b76
--- /dev/null
+++ b/test/mocha/promisify.js
@@ -0,0 +1,1116 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+var OperationalError = Promise.OperationalError;
+
+var erroneusNode = function(a, b, c, cb) {
+ setTimeout(function(){
+ cb(sentinelError);
+ }, 1);
+};
+
+var sentinel = {};
+var sentinelError = new OperationalError();
+
+var successNode = function(a, b, c, cb) {
+ setTimeout(function(){
+ cb(null, a);
+ }, 1);
+};
+
+var successNodeMultipleValues = function(a, b, c, cb) {
+ setTimeout(function(){
+ cb(null, a, b, c);
+ }, 1);
+};
+
+var syncErroneusNode = function(a, b, c, cb) {
+ cb(sentinelError);
+};
+
+var syncSuccessNode = function(a, b, c, cb) {
+ cb(null, a);
+};
+
+var syncSuccessNodeMultipleValues = function(a, b, c, cb) {
+ cb(null, a, b, c);
+};
+
+var errToThrow;
+var thrower = Promise.promisify(function(a, b, c, cb) {
+ errToThrow = new OperationalError();
+ throw errToThrow;
+});
+
+var tprimitive = "Where is your stack now?";
+var throwsStrings = Promise.promisify(function(cb){
+ throw tprimitive;
+});
+
+var errbacksStrings = Promise.promisify(function(cb){
+ cb(tprimitive);
+});
+
+var errbacksStringsAsync = Promise.promisify(function(cb){
+ setTimeout(function(){
+ cb(tprimitive);
+ }, 1);
+});
+var THIS = {};
+
+var error = Promise.promisify(erroneusNode);
+var syncError = Promise.promisify(syncErroneusNode);
+var success = Promise.promisify(successNode);
+var syncSuccess = Promise.promisify(syncSuccessNode);
+var successMultiArgsSingleValue = Promise.promisify(successNode, {multiArgs: true});
+var successMultiOptDisabledNoReceiver = Promise.promisify(successNodeMultipleValues);
+var syncSuccessMultiOptDisabledNoReceiver = Promise.promisify(syncSuccessNodeMultipleValues);
+var successMultiOptEnabledNoReceiver = Promise.promisify(successNodeMultipleValues, {multiArgs: true});
+var syncSuccessMultiOptEnabledNoReceiver = Promise.promisify(syncSuccessNodeMultipleValues, {multiArgs: true});
+var successMultiOptEnabledWithReceiver = Promise.promisify(successNodeMultipleValues, {multiArgs: true, context: THIS});
+var syncSccessMultiOptEnabledWithReceiver = Promise.promisify(syncSuccessNodeMultipleValues, {multiArgs: true, context: THIS});
+var successMultiOptDisabledWithReceiver = Promise.promisify(successNodeMultipleValues, {context: THIS});
+var syncSccessMultiOptDisabledWithReceiver = Promise.promisify(syncSuccessNodeMultipleValues, {context: THIS});
+var successMulti = successMultiOptDisabledNoReceiver;
+var syncSuccessMulti = syncSuccessMultiOptDisabledNoReceiver;
+describe("when calling promisified function it should ", function(){
+ specify("return a promise that is pending", function() {
+ var a = error(1,2,3);
+ var b = success(1,2,3);
+ var c = successMulti(1,2,3);
+
+ var calls = 0;
+ assert.equal(a.isPending(), true);
+ assert.equal(b.isPending(), true);
+ assert.equal(c.isPending(), true);
+ return a.caught(testUtils.noop);
+ });
+
+ specify("should use this if no receiver was given", function(){
+ var o = {};
+ var fn = Promise.promisify(function(cb){
+
+ cb(null, this === o);
+ });
+
+ o.fn = fn;
+
+ return o.fn().then(function(val){
+ assert(val);
+ });
+ });
+
+ specify("do nothing when called more than 1 times", function() {
+ var err = new Error();
+ var stack = err.stack;
+
+ var fn = Promise.promisify(function(cb) {
+ cb(null);
+ cb(err);
+ });
+
+ return fn().then(function() {
+ return Promise.delay(1).then(function() {
+ assert.strictEqual(stack, err.stack);
+ })
+ });
+ });
+
+ specify("undefined as receiver", function() {
+ return Promise.promisify(function(cb) {
+ assert.strictEqual(this, (function(){return this;})());
+ cb(null, 1);
+ }, {context: undefined})().then(function(result) {
+ assert.strictEqual(1, result);
+ });
+ });
+
+ specify("double promisification returns same function back", function() {
+ var c = function(){};
+ var a = Promise.promisify(function(){});
+ var b = Promise.promisify(a);
+ assert.notEqual(c, a);
+ assert.strictEqual(a, b);
+ });
+
+ specify("call future attached handlers later", function() {
+ var a = error(1,2,3).then(0, testUtils.noop);
+ var b = success(1,2,3);
+ var c = successMulti(1,2,3);
+ var d = syncError(1,2,3).then(0, testUtils.noop);
+ var e = syncSuccess(1,2,3).then(0, testUtils.noop);
+ var f = syncSuccessMulti(1,2,3).then(0, testUtils.noop);
+ var calls = 0;
+ return Promise.all([a, b, c, d, e, f]);
+ });
+
+ specify("Reject with the synchronously caught reason", function(){
+ thrower(1, 2, 3).then(assert.fail).then(assert.fail, function(e){
+ assert(e === errToThrow);
+ });
+ });
+
+ specify("reject with the proper reason", function() {
+ var a = error(1,2,3);
+ var b = syncError(1,2,3);
+
+ return Promise.all([
+ a.then(assert.fail, function(e){
+ assert.equal(sentinelError, e);
+ }),
+ b.then(assert.fail, function(e){
+ assert.equal(sentinelError, e);
+ })
+ ]);
+ });
+
+ describe("multi-args behaviors", function() {
+ specify("successMultiArgsSingleValue", function() {
+ var a = successMultiArgsSingleValue(1, 2, 3);
+ return a.then(function(value) {
+ assert.deepEqual([1], value);
+ })
+ });
+ specify("successMultiOptDisabledNoReceiver", function() {
+ var a = successMultiOptDisabledNoReceiver(1, 2, 3);
+ return a.then(function(value) {
+ assert.strictEqual(value, 1);
+ })
+ });
+ specify("syncSuccessMultiOptDisabledNoReceiver", function() {
+ var a = syncSuccessMultiOptDisabledNoReceiver(1, 2, 3);
+ return a.then(function(value) {
+ assert.strictEqual(value, 1);
+ })
+ });
+ specify("successMultiOptEnabledNoReceiver", function() {
+ var a = successMultiOptEnabledNoReceiver(1, 2, 3);
+ return a.then(function(value) {
+ assert.deepEqual([1,2,3], value);
+ })
+ });
+ specify("syncSuccessMultiOptEnabledNoReceiver", function() {
+ var a = syncSuccessMultiOptEnabledNoReceiver(1, 2, 3);
+ return a.then(function(value) {
+ assert.deepEqual([1,2,3], value);
+ })
+ });
+ specify("successMultiOptEnabledWithReceiver", function() {
+ var a = successMultiOptEnabledWithReceiver(1, 2, 3);
+ return a.then(function(value) {
+ assert.deepEqual([1,2,3], value);
+ })
+ });
+ specify("syncSccessMultiOptEnabledWithReceiver", function() {
+ var a = syncSccessMultiOptEnabledWithReceiver(1, 2, 3);
+ return a.then(function(value) {
+ assert.deepEqual([1,2,3], value);
+ })
+ });
+ specify("successMultiOptDisabledWithReceiver", function() {
+ var a = successMultiOptDisabledWithReceiver(1, 2, 3);
+ return a.then(function(value) {
+ assert.strictEqual(value, 1);
+ })
+ });
+ specify("syncSccessMultiOptDisabledWithReceiver", function() {
+ var a = syncSccessMultiOptDisabledWithReceiver(1, 2, 3);
+ return a.then(function(value) {
+ assert.strictEqual(value, 1);
+ })
+ });
+ });
+});
+
+describe("with more than 5 arguments", function(){
+
+ var o = {
+ value: 15,
+
+ f: function(a,b,c,d,e,f,g, cb) {
+ cb(null, [a,b,c,d,e,f,g, this.value])
+ }
+
+ }
+
+
+ var prom = Promise.promisify(o.f, {context: o});
+
+ specify("receiver should still work", function() {
+ return prom(1,2,3,4,5,6,7).then(function(val){
+ assert.deepEqual(
+ val,
+ [1,2,3,4,5,6,7, 15]
+ );
+ });
+
+ });
+
+});
+
+describe("promisify on objects", function(){
+
+ var o = {
+ value: 15,
+
+ f: function(a,b,c,d,e,f,g, cb) {
+ cb(null, [a,b,c,d,e,f,g, this.value])
+ }
+
+ };
+
+ var objf = function(){};
+
+ objf.value = 15;
+ objf.f = function(a,b,c,d,e,f,g, cb) {
+ cb(null, [a,b,c,d,e,f,g, this.value])
+ };
+
+ function Test(data) {
+ this.data = data;
+ }
+
+ Test.prototype.get = function(a, b, c, cb) {
+ cb(null, a, b, c, this.data);
+ };
+
+ Test.prototype.getMany = function(a, b, c, d, e, f, g, cb) {
+ cb(null, a, b, c, d, e, f, g, this.data);
+ };
+
+ Promise.promisifyAll(o);
+ Promise.promisifyAll(objf);
+ Promise.promisifyAll(Test.prototype);
+
+ specify("should not repromisify", function() {
+ var f = o.f;
+ var fAsync = o.fAsync;
+ var getOwnPropertyNames = Object.getOwnPropertyNames(o);
+ var ret = Promise.promisifyAll(o);
+ assert.equal(f, o.f);
+ assert.equal(fAsync, o.fAsync);
+ assert.deepEqual(getOwnPropertyNames, Object.getOwnPropertyNames(o));
+ assert.equal(ret, o);
+ });
+
+ specify("should not repromisify function object", function() {
+ var f = objf.f;
+ var fAsync = objf.fAsync;
+ var getOwnPropertyNames = Object.getOwnPropertyNames(objf);
+ var ret = Promise.promisifyAll(objf);
+ assert.equal(f, objf.f);
+ assert.equal(fAsync, objf.fAsync);
+ assert.deepEqual(getOwnPropertyNames, Object.getOwnPropertyNames(objf));
+ assert.equal(ret, objf);
+ });
+
+ specify("should work on function objects too", function() {
+ objf.fAsync(1, 2, 3, 4, 5, 6, 7).then(function(result){
+ assert.deepEqual(result, [1, 2, 3, 4, 5, 6, 7, 15]);
+ });
+ });
+
+ specify("should work on prototypes and not mix-up the instances", function() {
+ var a = new Test(15);
+ var b = new Test(30);
+ var c = new Test(45);
+ return Promise.all([
+ a.getAsync(1, 2, 3).then(function(result){
+ assert.strictEqual(result, 1);
+ }),
+
+ b.getAsync(4, 5, 6).then(function(result){
+ assert.strictEqual(result, 4);
+ }),
+
+ c.getAsync(7, 8, 9).then(function(result){
+ assert.strictEqual(result, 7);
+ })
+ ]);
+ });
+
+ specify("should work on prototypes and not mix-up the instances with more than 5 arguments", function() {
+ var a = new Test(15);
+ var b = new Test(30);
+ var c = new Test(45);
+
+ return Promise.all([
+ a.getManyAsync(1, 2, 3, 4, 5, 6, 7).then(function(result){
+ assert.strictEqual(result, 1);
+ }),
+
+ b.getManyAsync(4, 5, 6, 7, 8, 9, 10).then(function(result){
+ assert.strictEqual(result, 4);
+ }),
+
+ c.getManyAsync(7, 8, 9, 10, 11, 12, 13).then(function(result){
+ assert.strictEqual(result, 7);
+ })
+ ]);
+ });
+
+ specify("Fails to promisify Async suffixed methods", function() {
+ var o = {
+ x: function(cb){
+ cb(null, 13);
+ },
+ xAsync: function(cb) {
+ cb(null, 13);
+ },
+
+ xAsyncAsync: function(cb) {
+ cb(null, 13)
+ }
+ };
+ try {
+ Promise.promisifyAll(o);
+ }
+ catch (e) {
+ assert(e instanceof Promise.TypeError);
+ }
+ });
+
+ specify("Calls overridden methods", function() {
+ function Model() {
+ this.save = function() {};
+ }
+ Model.prototype.save = function() {
+ throw new Error("");
+ };
+
+ Promise.promisifyAll(Model.prototype);
+ var model = new Model();
+ model.saveAsync();
+ });
+
+ specify("gh-232", function() {
+ function f() {
+ var args = [].slice.call(arguments, 0, -1);
+ assert.deepEqual(args, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+ var cb = [].slice.call(arguments, -1)[0];
+ cb(null, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+ }
+ var fAsync = Promise.promisify(f);
+ return fAsync(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).then(function(result) {
+ assert.strictEqual(result, 1);
+ });
+ });
+
+ specify("Should lookup method dynamically if 'this' is given", function() {
+ var obj = {
+ fn: function(cb) {
+ cb(null, 1);
+ }
+ };
+ Promise.promisifyAll(obj);
+ obj.fn = function(cb) {
+ cb(null, 2);
+ };
+ return obj.fnAsync().then(function(val) {
+ assert.strictEqual(2, val);
+ });
+ });
+
+ specify("gh335", function() {
+ function HasArgs() { }
+ HasArgs.prototype.args = function(cb) {
+ return cb(null, "ok");
+ };
+
+ Promise.promisifyAll(HasArgs.prototype);
+ var a = new HasArgs();
+ return a.argsAsync().then(function(res) {
+ assert.equal(res, "ok");
+ });
+ });
+ specify("Should not promisify Object.prototype methods", function() {
+ var o = {};
+ var keys = Object.keys(o);
+ Promise.promisifyAll(o);
+ assert.deepEqual(keys.sort(), Object.keys(o).sort());
+ });
+
+ specify("Should not promisify Object.prototype methods", function() {
+ var o = {method: function(){}};
+ Promise.promisifyAll(o);
+ assert.deepEqual(["method", "methodAsync"].sort(), Object.keys(o).sort());
+ });
+
+ if (testUtils.ecmaScript5) {
+ specify("Should promisify non-enumerable methods", function() {
+ var o = {};
+ Object.defineProperty(o, "method", {
+ value: function() {},
+ enumerable: false
+ });
+ Promise.promisifyAll(o);
+ assert.deepEqual(["method", "methodAsync"].sort(),
+ Object.getOwnPropertyNames(o).sort());
+ });
+ }
+});
+
+describe("Promisify with custom suffix", function() {
+ it("should define methods with the custom suffix", function() {
+ function Test() {
+
+ }
+
+ Test.prototype.method = function method() {};
+
+ Promise.promisifyAll(Test.prototype, {suffix: "$P"});
+ assert(typeof Test.prototype.method$P == "function");
+ });
+
+ it("should throw on invalid suffix", function() {
+ try {
+ Promise.promisifyAll({}, {suffix: ""});
+ }
+ catch (e) {
+ return;
+ }
+ assert.fail();
+ });
+})
+
+describe("Module promisification", function() {
+ it("should promisify module with direct property classes", function() {
+ function RedisClient() {}
+ RedisClient.prototype.query = function() {};
+ function Multi() {}
+ Multi.prototype.exec = function() {};
+ Multi.staticMethod = function() {}
+
+ var redis = {
+ RedisClient: RedisClient,
+ Multi: Multi,
+ moduleMethod: function() {}
+ };
+ redis.Multi.staticMethod.tooDeep = function() {};
+
+ Promise.promisifyAll(redis);
+
+ assert(typeof redis.moduleMethodAsync === "function");
+ assert(typeof redis.Multi.staticMethodAsync === "function");
+ assert(typeof redis.Multi.prototype.execAsync === "function");
+ assert(typeof redis.RedisClient.prototype.queryAsync === "function");
+ assert(typeof redis.Multi.staticMethod.tooDeepAsync === "undefined");
+ })
+
+ it("should promisify module with inherited property classes", function() {
+ function Mongoose() {}
+ var Model = Mongoose.prototype.Model = function() {};
+ Model.prototype.find = function() {};
+ var Document = Mongoose.prototype.Document = function() {};
+ Document.prototype.create = function() {};
+ Document.staticMethod = function() {};
+ var mongoose = new Mongoose();
+
+ Promise.promisifyAll(mongoose);
+
+ assert(typeof mongoose.Model.prototype.findAsync === "function");
+ assert(typeof mongoose.Document.prototype.createAsync === "function");
+ assert(typeof mongoose.Document.staticMethodAsync === "function")
+ })
+
+ it("should promisify classes that have static methods", function() {
+ function MongoClient() {this.connect = 3;}
+ MongoClient.connect = function() {};
+ var module = {};
+ module.MongoClient = MongoClient;
+ Promise.promisifyAll(module);
+
+ assert(typeof MongoClient.connectAsync === "function");
+ });
+})
+
+describe("Promisify from prototype to object", function() {
+ var getterCalled = 0;
+
+ function makeClass() {
+ var Test = (function() {
+
+ function Test() {
+
+ }
+ var method = Test.prototype;
+
+ method.test = function() {
+
+ };
+
+ method["---invalid---"] = function(){};
+
+ if (testUtils.ecmaScript5) {
+ Object.defineProperty(method, "thrower", {
+ enumerable: true,
+ configurable: true,
+ get: function() {
+ throw new Error("getter called");
+ },
+ set: function() {
+ throw new Error("setter called");
+ }
+ });
+ Object.defineProperty(method, "counter", {
+ enumerable: true,
+ configurable: true,
+ get: function() {
+ getterCalled++;
+ },
+ set: function() {
+ throw new Error("setter called");
+ }
+ });
+ }
+
+ return Test;})();
+
+ return Test;
+ }
+
+ specify("Shouldn't touch the prototype when promisifying instance", function() {
+ var Test = makeClass();
+
+ var origKeys = Object.getOwnPropertyNames(Test.prototype).sort();
+ var a = new Test();
+ Promise.promisifyAll(a);
+
+ assert(typeof a.testAsync === "function");
+ assert(a.hasOwnProperty("testAsync"));
+ assert.deepEqual(Object.getOwnPropertyNames(Test.prototype).sort(), origKeys);
+ assert(getterCalled === 0);
+ });
+
+ specify("Shouldn't touch the method", function() {
+ var Test = makeClass();
+
+ var origKeys = Object.getOwnPropertyNames(Test.prototype.test).sort();
+ var a = new Test();
+ Promise.promisifyAll(a);
+
+
+ assert(typeof a.testAsync === "function");
+ assert.deepEqual(Object.getOwnPropertyNames(Test.prototype.test).sort(), origKeys);
+ assert(Promise.promisify(a.test) !== a.testAsync);
+ assert(getterCalled === 0);
+ });
+
+ specify("Should promisify own method even if a promisified method of same name already exists somewhere in proto chain", function(){
+ var Test = makeClass();
+ var instance = new Test();
+ Promise.promisifyAll(instance);
+ var origKeys = Object.getOwnPropertyNames(Test.prototype).sort();
+ var origInstanceKeys = Object.getOwnPropertyNames(instance).sort();
+ instance.test = function() {};
+ Promise.promisifyAll(instance);
+ assert.deepEqual(origKeys, Object.getOwnPropertyNames(Test.prototype).sort());
+ assert.notDeepEqual(origInstanceKeys, Object.getOwnPropertyNames(instance).sort());
+ assert(getterCalled === 0);
+ });
+
+ specify("Shouldn promisify the method closest to the object if method of same name already exists somewhere in proto chain", function(){
+ //IF the implementation is for-in, this pretty much tests spec compliance
+ var Test = makeClass();
+ var origKeys = Object.getOwnPropertyNames(Test.prototype).sort();
+ var instance = new Test();
+ instance.test = function() {};
+ Promise.promisifyAll(instance);
+
+ assert.deepEqual(Object.getOwnPropertyNames(Test.prototype).sort(), origKeys);
+ assert(instance.test === instance.test);
+ assert(getterCalled === 0);
+ });
+
+});
+
+
+function assertLongStackTraces(e) {
+ assert(e.stack.indexOf("From previous event:") > -1);
+}
+if (Promise.hasLongStackTraces()) {
+ describe("Primitive errors wrapping", function() {
+ specify("when the node function throws it", function(){
+ return throwsStrings().then(assert.fail, function(e){
+ assert(e instanceof Error);
+ assert(e.message == tprimitive);
+ });
+ });
+
+ specify("when the node function throws it inside then", function(){
+ return Promise.resolve().then(function() {
+ throwsStrings().then(assert.fail, function(e) {
+ assert(e instanceof Error);
+ assert(e.message == tprimitive);
+ assertLongStackTraces(e);
+ });
+ });
+ });
+
+
+ specify("when the node function errbacks it synchronously", function(){
+ return errbacksStrings().then(assert.fail, function(e){
+ assert(e instanceof Error);
+ assert(e.message == tprimitive);
+ });
+ });
+
+ specify("when the node function errbacks it synchronously inside then", function(){
+ return Promise.resolve().then(function(){
+ errbacksStrings().then(assert.fail, function(e){
+ assert(e instanceof Error);
+ assert(e.message == tprimitive);
+ assertLongStackTraces(e);
+ });
+ });
+ });
+
+ specify("when the node function errbacks it asynchronously", function(){
+ return errbacksStringsAsync().then(assert.fail, function(e){
+ assert(e instanceof Error);
+ assert(e.message == tprimitive);
+ assertLongStackTraces(e);
+ });
+ });
+
+ specify("when the node function errbacks it asynchronously inside then", function(){
+ return Promise.resolve().then(function(){
+ errbacksStringsAsync().then(assert.fail, function(e){
+ assert(e instanceof Error);
+ assert(e.message == tprimitive);
+ assertLongStackTraces(e);
+ });
+ });
+ });
+ });
+}
+
+describe("Custom promisifier", function() {
+ var dummy = {};
+ var err = new Error();
+ var chrome = {
+ getTab: function(tabId, callback) {
+ setTimeout(function() {
+ callback(dummy);
+ }, 1);
+ },
+ getTabErroneous: function(tabId, callback, errback) {
+ setTimeout(function() {
+ errback(err);
+ }, 1);
+ }
+ };
+
+ Promise.promisifyAll(chrome, {
+ promisifier: function(originalMethod) {
+ return function() {
+ var self = this;
+ var args = [].slice.call(arguments);
+ return new Promise(function(f, r) {
+ args.push(f, r);
+ originalMethod.apply(self, args);
+ });
+ };
+ }
+ });
+
+ specify("getTab", function() {
+ return chrome.getTabAsync(1).then(function(result) {
+ assert.equal(dummy, result);
+ });
+ });
+
+ specify("getTabErroneous", function() {
+ return chrome.getTabErroneousAsync(2).then(assert.fail, function(e) {
+ assert.equal(e, err);
+ });
+ });
+
+ specify("Copies custom props promisifyFirst", function() {
+ var request = function(cb){
+ cb(null, 1);
+ };
+ request.zero = 0;
+ request.get = function(cb) {
+ cb(null, 2 + this.zero);
+ };
+ request.post = function(cb) {
+ cb(null, 3);
+ };
+
+ request = Promise.promisifyAll(Promise.promisify(request));
+ return Promise.all([
+ request(),
+ request.getAsync(),
+ request.postAsync()
+ ]).then(function(a) {
+ assert.deepEqual([1,2,3], a);
+ });
+ });
+
+ specify("Copies custom props promisifyAll first", function() {
+ var request = function(cb){
+ cb(null, 1);
+ };
+ request.zero = 0;
+ request.get = function(cb) {
+ cb(null, 2 + this.zero);
+ };
+ request.post = function(cb) {
+ cb(null, 3);
+ };
+
+ request = Promise.promisify(Promise.promisifyAll(request));
+ return Promise.all([
+ request(),
+ request.getAsync(),
+ request.postAsync()
+ ]).then(function(a) {
+ assert.deepEqual([1,2,3], a);
+ });
+ });
+
+ specify("Copies custom props no this", function() {
+ var request = function(cb){
+ cb(null, 1);
+ };
+ request.zero = 0;
+ request.get = function(cb) {
+ cb(null, 2);
+ };
+ request.post = function(cb) {
+ cb(null, 3);
+ };
+
+ request = Promise.promisify(Promise.promisifyAll(request));
+ var getAsync = request.getAsync;
+ var postAsync = request.postAsync;
+ return Promise.all([
+ request(),
+ getAsync(),
+ postAsync()
+ ]).then(function(a) {
+ assert.deepEqual([1,2,3], a);
+ });
+ });
+
+ specify("custom promisifier enhancing default promisification", function() {
+ var obj = {
+ a: function(cb) {
+ setTimeout(function() {
+ cb(null, 1);
+ }, 1);
+ },
+
+ b: function(val, cb) {
+ setTimeout(function() {
+ cb(null, val);
+ }, 1);
+ }
+ };
+ obj = Promise.promisifyAll(obj, {
+ promisifier: function(originalFunction, defaultPromisifier) {
+ var promisified = defaultPromisifier(originalFunction);
+
+ return function() {
+ var args = [].slice.call(arguments);
+ var self = this;
+ return Promise.all(args).then(function(awaitedArgs) {
+ return promisified.apply(self, awaitedArgs);
+ });
+ };
+ }
+ });
+
+ return obj.bAsync(obj.aAsync()).then(function(val) {
+ assert.strictEqual(val, 1);
+ });
+
+ });
+
+ specify("multiArgs option enabled single value", function() {
+ var o = {
+ get: function(cb) {
+ cb(null, 1)
+ }
+ };
+ Promise.promisifyAll(o, {multiArgs: true});
+ return o.getAsync().then(function(value) {
+ assert.deepEqual([1], value);
+ });
+ });
+ specify("multiArgs option enabled multi value", function() {
+ var o = {
+ get: function(cb) {
+ cb(null, 1, 2, 3)
+ }
+ };
+ Promise.promisifyAll(o, {multiArgs: true});
+ return o.getAsync().then(function(value) {
+ assert.deepEqual([1,2,3], value);
+ });
+ });
+ specify("multiArgs option disabled single value", function() {
+ var o = {
+ get: function(cb) {
+ cb(null, 1)
+ }
+ };
+ Promise.promisifyAll(o);
+ return o.getAsync().then(function(value) {
+ assert.strictEqual(value, 1);
+ });
+ });
+ specify("multiArgs option disabled multi value", function() {
+ var o = {
+ get: function(cb) {
+ cb(null, 1)
+ }
+ };
+ Promise.promisifyAll(o);
+ return o.getAsync().then(function(value) {
+ assert.strictEqual(value, 1);
+ });
+ });
+});
+
+describe("OperationalError wrapping", function() {
+
+ var CustomError = function(){
+ }
+ CustomError.prototype = new Error();
+ CustomError.prototype.constructor = CustomError;
+
+ function isUntypedError(obj) {
+ return obj instanceof Error &&
+ Object.getPrototypeOf(obj) === Error.prototype;
+ }
+
+
+ if (!isUntypedError(new Error())) {
+ console.log("error must be untyped");
+ }
+
+ if (isUntypedError(new CustomError())) {
+ console.log("customerror must be typed");
+ }
+
+ function stringback(cb) {
+ cb("Primitive as error");
+ }
+
+ function errback(cb) {
+ cb(new Error("error as error"));
+ }
+
+ function typeback(cb) {
+ cb(new CustomError());
+ }
+
+ function stringthrow(cb) {
+ throw("Primitive as error");
+ }
+
+ function errthrow(cb) {
+ throw(new Error("error as error"));
+ }
+
+ function typethrow(cb) {
+ throw(new CustomError());
+ }
+
+ stringback = Promise.promisify(stringback);
+ errback = Promise.promisify(errback);
+ typeback = Promise.promisify(typeback);
+ stringthrow = Promise.promisify(stringthrow);
+ errthrow = Promise.promisify(errthrow);
+ typethrow = Promise.promisify(typethrow);
+
+ specify("should wrap stringback", function() {
+ return stringback().error(function(e) {
+ assert(e instanceof OperationalError);
+ });
+ });
+
+ specify("should wrap errback", function() {
+ return errback().error(function(e) {
+ assert(e instanceof OperationalError);
+ });
+ });
+
+ specify("should not wrap typeback", function() {
+ return typeback().caught(CustomError, function(e){
+ });
+ });
+
+ specify("should not wrap stringthrow", function() {
+ return stringthrow().error(assert.fail).then(assert.fail, function(e){
+ assert(e instanceof Error);
+ });
+ });
+
+ specify("should not wrap errthrow", function() {
+ return errthrow().error(assert.fail).then(assert.fail, function(e) {
+ assert(e instanceof Error);
+ });
+ });
+
+ specify("should not wrap typethrow", function() {
+ return typethrow().error(assert.fail)
+ .caught(CustomError, function(e){
+ });
+ });
+});
+describe("nodeback with multiple arguments", function() {
+ specify("spreaded with immediate values", function() {
+ var promise = Promise.promisify(function(cb) {
+ cb(null, 1, 2, 3);
+ }, {multiArgs: true})();
+
+ return promise.spread(function(a, b, c) {
+ assert.equal(a, 1);
+ assert.equal(b, 2);
+ assert.equal(c, 3);
+ });
+ });
+
+ specify("spreaded with thenable values should be unwrapped", function() {
+ var a = {then: function(a){a(1)}};
+ var b = a;
+ var c = a;
+ var promise = Promise.promisify(function(cb) {
+ cb(null, a, b, c);
+ }, {multiArgs: true})();
+
+ return promise.spread(function(a_, b_, c_) {
+ assert.equal(a_, 1);
+ assert.equal(b_, 1);
+ assert.equal(c_, 1);
+ });
+ });
+
+ specify("spreaded with promise values should be unwrapped", function() {
+ var a = Promise.resolve(1);
+ var b = Promise.resolve(2);
+ var c = Promise.resolve(3);
+ var promise = Promise.promisify(function(cb) {
+ cb(null, a, b, c);
+ }, {multiArgs: true})();
+
+ return promise.spread(function(a_, b_, c_) {
+ assert.strictEqual(a_, 1);
+ assert.strictEqual(b_, 2);
+ assert.strictEqual(c_, 3);
+ });
+ });
+});
+describe("filter", function() {
+ specify("gets an argument whether default filter was passed", function() {
+ Promise.promisifyAll({
+ abc: function() {}
+ }, {
+ filter: function(_, __, ___, passesDefaultFilter) {
+ assert.strictEqual(passesDefaultFilter, true);
+ }
+ })
+ });
+
+ specify("doesn't fail when allowing non-identifier methods", function() {
+ var a = Promise.promisifyAll({
+ " a s d ": function(cb) {
+ cb(null, 1);
+ }
+ }, {
+ filter: function() {
+ return true;
+ }
+ });
+
+ a[" a s d Async"]().then(function(val) {
+ assert.strictEqual(1, val);
+ });
+ });
+});
+
+var global = new Function("return this")();
+var canEvaluate = (function() {
+ if (typeof window !== "undefined" && window !== null &&
+ typeof window.document !== "undefined" &&
+ typeof navigator !== "undefined" && navigator !== null &&
+ typeof navigator.appName === "string" &&
+ window === global) {
+ return false;
+ }
+ return true;
+})();
+var canTestArity = (function(a, b, c) {}).length === 3 && canEvaluate;
+
+if (canTestArity) {
+ describe("arity", function() {
+ specify("should be original - 1", function() {
+ var fn = function(a, b, c, callback) {};
+ assert.equal(Promise.promisify(fn).length, 3);
+
+ var o = {
+ fn: function(a, b, c, callback) {
+
+ }
+ };
+ assert.equal(Promise.promisifyAll(o).fnAsync.length, 3);
+ })
+ })
+}
+
+describe("github 680", function() {
+ before(function() {
+ Function.prototype.method = function() {};
+ });
+
+ after(function() {
+ delete Function.prototype.method;
+ });
+
+ specify("should not try to promisify methods from Function.prototype, native or otherwise", function() {
+ var a = function() {};
+ a.fn = function() {};
+ Promise.promisifyAll(a);
+ assert.strictEqual(undefined, a.methodAsync);
+ assert.strictEqual(undefined, a.applyAsync);
+ assert(typeof a.fnAsync === "function");
+ });
+});
+
+describe("github 1063", function() {
+ specify("should not cause error when called with no arguments", function() {
+ return Promise.promisify(function(cb) {
+ cb();
+ }, { multiArgs: true})().then(function(values) {
+ assert(Array.isArray(values));
+ assert.strictEqual(values.length, 0);
+ });
+ })
+});
+
+describe("github 1023", function() {
+ specify("promisify triggers custom schedulers", function() {
+ var triggered = false;
+ var defaultScheduler = Promise.setScheduler(function(fn) {
+ triggered = true;
+ setTimeout(fn, 0);
+ });
+ var fnAsync = Promise.promisify(function(cb) {
+ setTimeout(function() {
+ cb(null, true);
+ }, 0);
+ });
+
+ return fnAsync().then(function(result) {
+ assert(result);
+ assert(triggered);
+ }).lastly(function() {
+ Promise.setScheduler(defaultScheduler);
+ });
+ });
+})
diff --git a/test/mocha/props.js b/test/mocha/props.js
new file mode 100644
index 0000000..585ef58
--- /dev/null
+++ b/test/mocha/props.js
@@ -0,0 +1,220 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+describe("Promise.props", function () {
+
+ specify("should reject undefined", function() {
+ return Promise.props().caught(TypeError, function(){
+ })
+ });
+
+ specify("should reject primitive", function() {
+ return Promise.props("str").caught(TypeError, function(){
+ })
+ });
+
+ specify("should resolve to new object", function() {
+ var o = {};
+ return Promise.props(o).then(function(v){
+ assert(v !== o);
+ assert.deepEqual(o, v);
+ });
+ });
+
+ specify("should resolve value properties", function() {
+ var o = {
+ one: 1,
+ two: 2,
+ three: 3
+ };
+ return Promise.props(o).then(function(v){
+ assert.deepEqual({
+ one: 1,
+ two: 2,
+ three: 3
+ }, v);
+ });
+ });
+
+ specify("should resolve immediate properties", function() {
+ var o = {
+ one: Promise.resolve(1),
+ two: Promise.resolve(2),
+ three: Promise.resolve(3)
+ };
+ return Promise.props(o).then(function(v){
+ assert.deepEqual({
+ one: 1,
+ two: 2,
+ three: 3
+ }, v);
+ });
+ });
+
+ specify("should resolve eventual properties", function() {
+ var d1 = Promise.defer(),
+ d2 = Promise.defer(),
+ d3 = Promise.defer();
+ var o = {
+ one: d1.promise,
+ two: d2.promise,
+ three: d3.promise
+ };
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.props(o).then(function(v){
+ assert.deepEqual({
+ one: 1,
+ two: 2,
+ three: 3
+ }, v);
+ });
+
+
+ });
+
+ specify("should reject if any input promise rejects", function() {
+ var o = {
+ one: Promise.resolve(1),
+ two: Promise.reject(2),
+ three: Promise.resolve(3)
+ };
+ return Promise.props(o).then(assert.fail, function(v){
+ assert(v === 2);
+ });
+ });
+
+ specify("should accept a promise for an object", function() {
+ var o = {
+ one: Promise.resolve(1),
+ two: Promise.resolve(2),
+ three: Promise.resolve(3)
+ };
+ var d1 = Promise.defer();
+ setTimeout(function(){
+ d1.fulfill(o);
+ }, 1);
+ return Promise.props(d1.promise).then(function(v){
+ assert.deepEqual({
+ one: 1,
+ two: 2,
+ three: 3
+ }, v);
+ });
+
+ });
+
+ specify("should reject a promise for a primitive", function() {
+ var d1 = Promise.defer();
+ setTimeout(function(){
+ d1.fulfill("text");
+ }, 1);
+ return Promise.props(d1.promise).caught(TypeError, function(){
+ });
+
+ });
+
+ specify("should accept thenables in properties", function() {
+ var t1 = {then: function(cb){cb(1);}};
+ var t2 = {then: function(cb){cb(2);}};
+ var t3 = {then: function(cb){cb(3);}};
+ var o = {
+ one: t1,
+ two: t2,
+ three: t3
+ };
+ return Promise.props(o).then(function(v){
+ assert.deepEqual({
+ one: 1,
+ two: 2,
+ three: 3
+ }, v);
+ });
+ });
+
+ specify("should accept a thenable for thenables in properties", function() {
+ var o = {
+ then: function (f) {
+ f({
+ one: {
+ then: function (cb) {
+ cb(1);
+ }
+ },
+ two: {
+ then: function (cb) {
+ cb(2);
+ }
+ },
+ three: {
+ then: function (cb) {
+ cb(3);
+ }
+ }
+ });
+ }
+ };
+ return Promise.props(o).then(function(v){
+ assert.deepEqual({
+ one: 1,
+ two: 2,
+ three: 3
+ }, v);
+ });
+ });
+
+ specify("treats arrays for their properties", function() {
+ var o = [1,2,3];
+
+ return Promise.props(o).then(function(v){
+ assert.deepEqual({
+ 0: 1,
+ 1: 2,
+ 2: 3
+ }, v);
+ });
+ });
+
+
+ if (typeof Map !== "undefined") {
+ specify("works with es6 maps", function() {
+ return Promise.props(new Map([
+ ["a", Promise.resolve(1)],
+ ["b", Promise.resolve(2)],
+ ["c", Promise.resolve(3)]
+ ])).then(function(result) {
+ assert.strictEqual(result.get("a"), 1);
+ assert.strictEqual(result.get("b"), 2);
+ assert.strictEqual(result.get("c"), 3);
+ });
+ });
+
+ specify("doesn't await promise keys in es6 maps", function() {
+ var a = new Promise(function() {});
+ var b = new Promise(function() {});
+ var c = new Promise(function() {});
+
+ return Promise.props(new Map([
+ [a, Promise.resolve(1)],
+ [b, Promise.resolve(2)],
+ [c, Promise.resolve(3)]
+ ])).then(function(result) {
+ assert.strictEqual(result.get(a), 1);
+ assert.strictEqual(result.get(b), 2);
+ assert.strictEqual(result.get(c), 3);
+ });
+ });
+
+ specify("empty map should resolve to empty map", function() {
+ return Promise.props(new Map()).then(function(result) {
+ assert(result instanceof Map);
+ });
+ });
+ }
+
+});
diff --git a/test/mocha/race.js b/test/mocha/race.js
new file mode 100644
index 0000000..77eeeac
--- /dev/null
+++ b/test/mocha/race.js
@@ -0,0 +1,121 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+
+
+
+describe("Promise.race", function(){
+ it("remains forever pending when passed an empty array", function() {
+ var p = Promise.race([]);
+ return Promise.delay(1).then(function() {
+ assert(p.isPending());
+ });
+ });
+
+ it("remains forever pending when passed an empty sparse array", function() {
+ var p = Promise.race([,,,,,]);
+ return Promise.delay(1).then(function() {
+ assert(p.isPending());
+ });
+ });
+
+ it("fulfills when passed an immediate value", function() {
+ return Promise.race([1,2,3]).then(function(v){
+ assert.deepEqual(v, 1);
+ });
+ });
+
+ it("fulfills when passed an immediately fulfilled value", function() {
+ var d1 = Promise.defer();
+ d1.fulfill(1);
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ d2.fulfill(2);
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ d3.fulfill(3);
+ var p3 = d3.promise;
+
+ return Promise.race([p1, p2, p3]).then(function(v){
+ assert.deepEqual(v, 1);
+ });
+ });
+
+ it("fulfills when passed an eventually fulfilled value", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ setTimeout(function(){
+ d1.fulfill(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.race([p1, p2, p3]).then(function(v){
+ assert.deepEqual(v, 1);
+ });
+ });
+
+ it("rejects when passed an immediate value", function() {
+ return Promise.race([Promise.reject(1), 2, 3]).then(assert.fail, function(v){
+ assert.deepEqual(v, 1);
+ });
+ });
+
+ it("rejects when passed an immediately rejected value", function() {
+ var d1 = Promise.defer();
+ d1.reject(1);
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ d2.fulfill(2);
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ d3.fulfill(3);
+ var p3 = d3.promise;
+
+ return Promise.race([, p1, , p2, , , p3]).then(assert.fail, function(v){
+ assert.deepEqual(v, 1);
+ });
+ });
+
+ it("rejects when passed an eventually rejected value", function() {
+ var d1 = Promise.defer();
+ var p1 = d1.promise;
+
+ var d2 = Promise.defer();
+ var p2 = d2.promise;
+
+ var d3 = Promise.defer();
+ var p3 = d3.promise;
+
+ setTimeout(function(){
+ d1.reject(1);
+ d2.fulfill(2);
+ d3.fulfill(3);
+ }, 1);
+
+ return Promise.race([p1, p2, p3]).then(assert.fail, function(v){
+ assert.deepEqual(v, 1);
+ })
+ });
+
+ it("propagates bound value", function() {
+ var o = {};
+ return Promise.resolve([1]).bind(o).race().then(function(v){
+ assert(v === 1);
+ assert(this === o);
+ });
+ });
+});
diff --git a/test/mocha/reduce.js b/test/mocha/reduce.js
new file mode 100644
index 0000000..b29a07d
--- /dev/null
+++ b/test/mocha/reduce.js
@@ -0,0 +1,705 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+
+function promised(val) {
+ return new Promise(function(f) {
+ setTimeout(function() {
+ f(val);
+ }, 1);
+ });
+}
+function promising(val) {
+ return function() {
+ return promised(val);
+ }
+}
+function promisingThen(val) {
+ return function() {
+ return promised(val).then(function(resolved) {
+ return resolved;
+ });
+ }
+}
+
+function thenabled(val) {
+ return {
+ then: function(f){
+ setTimeout(function() {
+ f(val);
+ }, 1);
+ }
+ };
+}
+function thenabling(val) {
+ return function() { return thenabled(val); }
+}
+
+function evaluate(val) {
+ if (typeof val === 'function') {
+ val = val();
+ }
+ if (Array.isArray(val)) {
+ val = val.map(function(member) {
+ return evaluate(member);
+ });
+ }
+ return val;
+}
+
+
+var ACCUM_CRITERIA = [
+ { value: 0, desc: "that is resolved" },
+ { value: promising(0), desc: "as a Promise" },
+ { value: promisingThen(0), desc: "as a deferred Promise" },
+ { value: thenabling(0), desc: "as a thenable" },
+];
+
+var VALUES_CRITERIA = [
+ { value: [], total: 0, desc: "and no values" },
+ { value: [ 1 ], total: 1, desc: "and a single resolved value" },
+ { value: [ 1, 2, 3 ], total: 6, desc: "and multiple resolved values" },
+ { value: [ promising(1) ], total: 1, desc: "and a single Promise" },
+ { value: [
+ promising(1),
+ promising(2),
+ promising(3)
+ ], total: 6, desc: "and multiple Promises" },
+ { value: [
+ promisingThen(1)
+ ], total: 1, desc: "and a single deferred Promise" },
+ { value: [
+ promisingThen(1),
+ promisingThen(2),
+ promisingThen(3)
+ ], total: 6, desc: "and multiple deferred Promises" },
+ { value: [
+ thenabling(1)
+ ], total: 1, desc: "and a single thenable" },
+ { value: [
+ thenabling(1),
+ thenabling(2),
+ thenabling(3)
+ ], total: 6, desc: "and multiple thenables" },
+ { value: [
+ thenabling(1),
+ promisingThen(2),
+ promising(3),
+ 4
+ ], total: 10, desc: "and a blend of values" },
+];
+
+var ERROR = new Error("BOOM");
+
+
+describe("Promise.prototype.reduce", function() {
+ it("works with no values", function() {
+ return Promise.resolve([]).reduce(function(total, value) {
+ return total + value + 5;
+ }).then(function(total) {
+ assert.strictEqual(total, undefined);
+ });
+ });
+
+ it("works with a single value", function() {
+ return Promise.resolve([ 1 ]).reduce(function(total, value) {
+ return total + value + 5;
+ }).then(function(total) {
+ assert.strictEqual(total, 1);
+ });
+ });
+
+ it("works when the iterator returns a value", function() {
+ return Promise.resolve([ 1, 2, 3 ]).reduce(function(total, value) {
+ return total + value + 5;
+ }).then(function(total) {
+ assert.strictEqual(total, (1 + 2+5 + 3+5));
+ });
+ });
+
+ it("works when the iterator returns a Promise", function() {
+ return Promise.resolve([ 1, 2, 3 ]).reduce(function(total, value) {
+ return promised(5).then(function(bonus) {
+ return total + value + bonus;
+ });
+ }).then(function(total) {
+ assert.strictEqual(total, (1 + 2+5 + 3+5));
+ });
+ });
+
+ it("works when the iterator returns a thenable", function() {
+ return Promise.resolve([ 1, 2, 3 ]).reduce(function(total, value) {
+ return thenabled(total + value + 5);
+ }).then(function(total) {
+ assert.strictEqual(total, (1 + 2+5 + 3+5));
+ });
+ });
+});
+
+
+describe("Promise.reduce", function() {
+
+ it("should allow returning values", function() {
+ var a = [promised(1), promised(2), promised(3)];
+
+ return Promise.reduce(a, function(total, a) {
+ return total + a + 5;
+ }, 0).then(function(total){
+ assert.equal(total, 1+5 + 2+5 + 3+5);
+ });
+ });
+
+ it("should allow returning promises", function() {
+ var a = [promised(1), promised(2), promised(3)];
+
+ return Promise.reduce(a, function(total, a) {
+ return promised(5).then(function(b) {
+ return total + a + b;
+ });
+ }, 0).then(function(total){
+ assert.equal(total, 1+5 + 2+5 + 3+5);
+ });
+ });
+
+ it("should allow returning thenables", function() {
+ var b = [1,2,3];
+ var a = [];
+
+ return Promise.reduce(b, function(total, cur) {
+ a.push(cur);
+ return thenabled(3);
+ }, 0).then(function(total) {
+ assert.equal(total, 3);
+ assert.deepEqual(a, b);
+ });
+ });
+
+ it("propagates error", function() {
+ var a = [promised(1), promised(2), promised(3)];
+ var e = new Error("asd");
+ return Promise.reduce(a, function(total, a) {
+ if (a > 2) {
+ throw e;
+ }
+ return total + a + 5;
+ }, 0).then(assert.fail, function(err) {
+ assert.equal(err, e);
+ });
+ });
+
+ describe("with no initial accumulator or values", function() {
+ it("works when the iterator returns a value", function() {
+ return Promise.reduce([], function(total, value) {
+ return total + value + 5;
+ }).then(function(total){
+ assert.strictEqual(total, undefined);
+ });
+ });
+
+ it("works when the iterator returns a Promise", function() {
+ return Promise.reduce([], function(total, value) {
+ return promised(5).then(function(bonus) {
+ return total + value + bonus;
+ });
+ }).then(function(total){
+ assert.strictEqual(total, undefined);
+ });
+ });
+
+ it("works when the iterator returns a thenable", function() {
+ return Promise.reduce([], function(total, value) {
+ return thenabled(total + value + 5);
+ }).then(function(total){
+ assert.strictEqual(total, undefined);
+ });
+ });
+ });
+
+ describe("with an initial accumulator value", function() {
+ ACCUM_CRITERIA.forEach(function(criteria) {
+ var initial = criteria.value;
+
+ describe(criteria.desc, function() {
+ VALUES_CRITERIA.forEach(function(criteria) {
+ var values = criteria.value;
+ var valueTotal = criteria.total;
+
+ describe(criteria.desc, function() {
+ it("works when the iterator returns a value", function() {
+ return Promise.reduce(evaluate(values), function(total, value) {
+ return total + value + 5;
+ }, evaluate(initial)).then(function(total){
+ assert.strictEqual(total, valueTotal + (values.length * 5));
+ });
+ });
+
+ it("works when the iterator returns a Promise", function() {
+ return Promise.reduce(evaluate(values), function(total, value) {
+ return promised(5).then(function(bonus) {
+ return total + value + bonus;
+ });
+ }, evaluate(initial)).then(function(total){
+ assert.strictEqual(total, valueTotal + (values.length * 5));
+ });
+ });
+
+ it("works when the iterator returns a thenable", function() {
+ return Promise.reduce(evaluate(values), function(total, value) {
+ return thenabled(total + value + 5);
+ }, evaluate(initial)).then(function(total){
+ assert.strictEqual(total, valueTotal + (values.length * 5));
+ });
+ });
+ });
+ });
+ });
+ });
+
+ it("propagates an initial Error", function() {
+ var initial = Promise.reject(ERROR);
+ var values = [
+ thenabling(1),
+ promisingThen(2)(),
+ promised(3),
+ 4
+ ];
+
+ return Promise.reduce(values, function(total, value) {
+ return value;
+ }, initial).then(assert.fail, function(err) {
+ assert.equal(err, ERROR);
+ });
+ });
+
+ it("propagates a value's Error", function() {
+ var initial = 0;
+ var values = [
+ thenabling(1),
+ promisingThen(2)(),
+ Promise.reject(ERROR),
+ promised(3),
+ 4
+ ];
+
+ return Promise.reduce(values, function(total, value) {
+ return value;
+ }, initial).then(assert.fail, function(err) {
+ assert.equal(err, ERROR);
+ });
+ });
+
+ it("propagates an Error from the iterator", function() {
+ var initial = 0;
+ var values = [
+ thenabling(1),
+ promisingThen(2)(),
+ promised(3),
+ 4
+ ];
+
+ return Promise.reduce(values, function(total, value) {
+ if (value === 2) {
+ throw ERROR;
+ }
+ return value;
+ }, initial).then(assert.fail, function(err) {
+ assert.equal(err, ERROR);
+ });
+ });
+ });
+
+ describe("with a 0th value acting as an accumulator", function() {
+ it("acts this way when an accumulator value is provided yet `undefined`", function() {
+ return Promise.reduce([ 1, 2, 3 ], function(total, value) {
+ return ((total === void 0) ? 0 : total) + value + 5;
+ }, undefined).then(function(total){
+ assert.strictEqual(total, (1 + 2+5 + 3+5));
+ });
+ });
+
+ it("survives an `undefined` 0th value", function() {
+ return Promise.reduce([ undefined, 1, 2, 3 ], function(total, value) {
+ return ((total === void 0) ? 0 : total) + value + 5;
+ }).then(function(total){
+ assert.strictEqual(total, (1+5 + 2+5 + 3+5));
+ });
+ });
+
+ ACCUM_CRITERIA.forEach(function(criteria) {
+ var zeroth = criteria.value;
+
+ describe(criteria.desc, function() {
+ VALUES_CRITERIA.forEach(function(criteria) {
+ var values = criteria.value;
+ var zerothAndValues = [ zeroth ].concat(values);
+ var valueTotal = criteria.total;
+
+ describe(criteria.desc, function() {
+ it("works when the iterator returns a value", function() {
+ return Promise.reduce(evaluate(zerothAndValues), function(total, value) {
+ return total + value + 5;
+ }).then(function(total){
+ assert.strictEqual(total, valueTotal + (values.length * 5));
+ });
+ });
+
+ it("works when the iterator returns a Promise", function() {
+ return Promise.reduce(evaluate(zerothAndValues), function(total, value) {
+ return promised(5).then(function(bonus) {
+ return total + value + bonus;
+ });
+ }).then(function(total){
+ assert.strictEqual(total, valueTotal + (values.length * 5));
+ });
+ });
+
+ it("works when the iterator returns a thenable", function() {
+ return Promise.reduce(evaluate(zerothAndValues), function(total, value) {
+ return thenabled(total + value + 5);
+ }).then(function(total){
+ assert.strictEqual(total, valueTotal + (values.length * 5));
+ });
+ });
+ });
+ });
+ });
+ });
+
+ it("propagates an initial Error", function() {
+ var values = [
+ Promise.reject(ERROR),
+ thenabling(1),
+ promisingThen(2)(),
+ promised(3),
+ 4
+ ];
+
+ return Promise.reduce(values, function(total, value) {
+ return value;
+ }).then(assert.fail, function(err) {
+ assert.equal(err, ERROR);
+ });
+ });
+
+ it("propagates a value's Error", function() {
+ var values = [
+ 0,
+ thenabling(1),
+ promisingThen(2)(),
+ Promise.reject(ERROR),
+ promised(3),
+ 4
+ ];
+
+ return Promise.reduce(values, function(total, value) {
+ return value;
+ }).then(assert.fail, function(err) {
+ assert.equal(err, ERROR);
+ });
+ });
+
+ it("propagates an Error from the iterator", function() {
+ var values = [
+ 0,
+ thenabling(1),
+ promisingThen(2)(),
+ promised(3),
+ 4
+ ];
+
+ return Promise.reduce(values, function(total, value) {
+ if (value === 2) {
+ throw ERROR;
+ }
+ return value;
+ }).then(assert.fail, function(err) {
+ assert.equal(err, ERROR);
+ });
+ });
+ });
+});
+
+/*
+Based on When.js tests
+
+Open Source Initiative OSI - The MIT License
+
+http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2011 Brian Cavalier
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var sentinel = {};
+var other = {};
+describe("Promise.reduce-test", function () {
+
+ function plus(sum, val) {
+ return sum + val;
+ }
+
+ function later(val) {
+ return Promise.delay(1, val);
+ }
+
+
+ specify("should reduce values without initial value", function() {
+ return Promise.reduce([1,2,3], plus).then(
+ function(result) {
+ assert.deepEqual(result, 6);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should reduce values with initial value", function() {
+ return Promise.reduce([1,2,3], plus, 1).then(
+ function(result) {
+ assert.deepEqual(result, 7);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should reduce values with initial promise", function() {
+ return Promise.reduce([1,2,3], plus, Promise.resolve(1)).then(
+ function(result) {
+ assert.deepEqual(result, 7);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should reduce promised values without initial value", function() {
+ var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
+ return Promise.reduce(input, plus).then(
+ function(result) {
+ assert.deepEqual(result, 6);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should reduce promised values with initial value", function() {
+ var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
+ return Promise.reduce(input, plus, 1).then(
+ function(result) {
+ assert.deepEqual(result, 7);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should reduce promised values with initial promise", function() {
+ var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
+ return Promise.reduce(input, plus, Promise.resolve(1)).then(
+ function(result) {
+ assert.deepEqual(result, 7);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should reduce empty input with initial value", function() {
+ var input = [];
+ return Promise.reduce(input, plus, 1).then(
+ function(result) {
+ assert.deepEqual(result, 1);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should reduce empty input with eventual promise", function() {
+ return Promise.reduce([], plus, Promise.delay(1, 1)).then(
+ function(result) {
+ assert.deepEqual(result, 1);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should reduce empty input with initial promise", function() {
+ return Promise.reduce([], plus, Promise.resolve(1)).then(
+ function(result) {
+ assert.deepEqual(result, 1);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should reject Promise input contains rejection", function() {
+ var input = [Promise.resolve(1), Promise.reject(2), Promise.resolve(3)];
+ return Promise.reduce(input, plus, Promise.resolve(1)).then(
+ assert.fail,
+ function(result) {
+ assert.deepEqual(result, 2);
+ }
+ );
+ });
+
+ specify("should reduce to undefined with empty array", function() {
+ return Promise.reduce([], plus).then(function(r){
+ assert(r === void 0);
+ });
+ });
+
+ specify("should reduce to initial value with empty array", function() {
+ return Promise.reduce([], plus, sentinel).then(function(r){
+ assert(r === sentinel);
+ });
+ });
+
+ specify("should reduce in input order", function() {
+ return Promise.reduce([later(1), later(2), later(3)], plus, '').then(
+ function(result) {
+ assert.deepEqual(result, '123');
+ },
+ assert.fail
+ );
+ });
+
+ specify("should accept a promise for an array", function() {
+ return Promise.reduce(Promise.resolve([1, 2, 3]), plus, '').then(
+ function(result) {
+ assert.deepEqual(result, '123');
+ },
+ assert.fail
+ );
+ });
+
+ specify("should resolve to initialValue Promise input promise does not resolve to an array", function() {
+ return Promise.reduce(Promise.resolve(123), plus, 1).caught(TypeError, function(e){
+ });
+ });
+
+ specify("should provide correct basis value", function() {
+ function insertIntoArray(arr, val, i) {
+ arr[i] = val;
+ return arr;
+ }
+
+ return Promise.reduce([later(1), later(2), later(3)], insertIntoArray, []).then(
+ function(result) {
+ assert.deepEqual(result, [1,2,3]);
+ },
+ assert.fail
+ );
+ });
+
+ describe("checks", function() {
+ function later(val, ms) {
+ return Promise.delay(ms, val);
+ }
+
+ function plus(sum, val) {
+ return sum + val;
+ }
+
+ function plusDelayed(sum, val) {
+ return Promise.delay(0).then(function() {
+ return sum + val;
+ });
+ }
+
+ function check(delay1, delay2, delay3) {
+ return Promise.reduce([
+ later(1, delay1),
+ later(2, delay2),
+ later(3, delay3)
+ ], plus, '').then(function(result) {
+ assert.deepEqual(result, '123');
+ })
+ }
+
+ function checkDelayed(delay1, delay2, delay3) {
+ return Promise.reduce([
+ later(1, delay1),
+ later(2, delay2),
+ later(3, delay3)
+ ], plusDelayed, '').then(function(result) {
+ assert.deepEqual(result, '123');
+ })
+ }
+
+ specify("16, 16, 16", function() {
+ return check(16, 16, 16);
+ });
+
+ specify("16, 16, 4", function() {
+ return check(16, 16, 4);
+ });
+ specify("4, 16, 16", function() {
+ return check(4, 16, 16);
+ });
+ specify("16, 4, 16", function() {
+ return check(16, 4, 16);
+ });
+ specify("16, 16, 4", function() {
+ return check(16, 16, 4);
+ });
+ specify("4, 4, 16", function() {
+ return check(4, 4, 16);
+ });
+ specify("16, 4, 4", function() {
+ return check(16, 4, 4);
+ });
+ specify("4, 16, 4", function() {
+ return check(4, 16, 4);
+ });
+ specify("4, 4, 4", function() {
+ return check(4, 4, 4);
+ });
+
+
+ specify("16, 16, 16", function() {
+ return checkDelayed(16, 16, 16);
+ });
+
+ specify("16, 16, 4", function() {
+ return checkDelayed(16, 16, 4);
+ });
+ specify("4, 16, 16", function() {
+ return checkDelayed(4, 16, 16);
+ });
+ specify("16, 4, 16", function() {
+ return checkDelayed(16, 4, 16);
+ });
+ specify("16, 16, 4", function() {
+ return checkDelayed(16, 16, 4);
+ });
+ specify("4, 4, 16", function() {
+ return checkDelayed(4, 4, 16);
+ });
+ specify("16, 4, 4", function() {
+ return checkDelayed(16, 4, 4);
+ });
+ specify("4, 16, 4", function() {
+ return checkDelayed(4, 16, 4);
+ });
+ specify("4, 4, 4", function() {
+ return checkDelayed(4, 4, 4);
+ });
+
+ })
+});
diff --git a/test/mocha/reflect.js b/test/mocha/reflect.js
new file mode 100644
index 0000000..7adc222
--- /dev/null
+++ b/test/mocha/reflect.js
@@ -0,0 +1,23 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var testFulfilled = require("./helpers/testThreeCases").testFulfilled;
+var testRejected = require("./helpers/testThreeCases").testRejected;
+
+describe(".reflect()", function() {
+ testFulfilled(1, function(promise) {
+ return promise.reflect().then(function(inspection) {
+ assert(inspection instanceof Promise.PromiseInspection);
+ assert(inspection.isFulfilled());
+ assert(inspection.value() === 1);
+ });
+ });
+ testRejected(2, function(promise) {
+ return promise.reflect().then(function(inspection) {
+ assert(inspection instanceof Promise.PromiseInspection);
+ assert(inspection.isRejected());
+ assert(inspection.reason() === 2);
+ });
+ });
+});
diff --git a/test/mocha/regress.js b/test/mocha/regress.js
new file mode 100644
index 0000000..28de3ca
--- /dev/null
+++ b/test/mocha/regress.js
@@ -0,0 +1,169 @@
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+describe("regressions", function() {
+ specify("should be able to call .then more than once inside that promise's handler", function() {
+ var called = 0;
+ var resolve;
+ var promise = new Promise(function() {
+ resolve = arguments[0];
+ });
+ return new Promise(function(resolve) {
+ promise.then(function() {
+ called++;
+ promise.then(function(){
+ called++;
+ });
+ promise.then(function(){
+ called++;
+ assert.equal(4, called);
+ resolve();
+ });
+ });
+
+ promise.then(function() {
+ called++;
+ });
+
+ setTimeout(resolve, 1);
+ });
+
+ });
+
+ specify("should be able to nest arbitrary amount of then handlers on already resolved promises", function() {
+ var called = 0;
+ var resolve;
+ var promise = Promise.resolve();
+ return new Promise(function(resolve) {
+ promise.then(function() {
+ called++;
+ promise.then(function(){
+ called++;
+ promise.then(function(){
+ called++;
+ });
+ promise.then(function(){
+ called++;
+ });
+ });
+ promise.then(function(){
+ promise.then(function(){
+ called++;
+ });
+ promise.then(function(){
+ called++;
+ assert.equal(8, called);
+ resolve();
+ });
+ called++;
+ });
+ });
+
+ promise.then(function() {
+ called++;
+ });
+ });
+ });
+
+ specify("github-682", function() {
+ var o = {
+ then: function(f) {
+ setTimeout(function() {
+ delete o.then;
+ f(o);
+ }, 1);
+ }
+ };
+
+ return Promise.resolve(o).then(function(value) {
+ assert.equal(o, value);
+ });
+ });
+
+ specify("gh-1006", function() {
+ return Promise.resolve().then(function() {
+ new Promise(function() {}).tap(function() {}).cancel();
+ });
+ });
+
+ if (testUtils.isNodeJS) {
+ describe("github-689", function() {
+ var originalProperty = Object.getOwnPropertyDescriptor(process, "domain");
+ var bindCalls = 0;
+
+ beforeEach(function() {
+ bindCalls = 0;
+ });
+
+ before(function() {
+ Object.defineProperty(process, "domain", {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ value: {
+ emit: function() {},
+ bind: function(fn) {
+ bindCalls++;
+ // Ensure non-strict mode.
+ return new Function("fn", "return function() {return fn.apply(this, arguments);}")(fn);
+ },
+ enter: function() {},
+ exit: function() {}
+ }
+ });
+ });
+
+ after(function() {
+ Object.defineProperty(process, "domain", originalProperty);
+ });
+
+ specify(".return", function() {
+ return Promise.resolve().thenReturn(true).then(function(val) {
+ assert.strictEqual(val, true);
+ assert.strictEqual(bindCalls, 4);
+ });
+ });
+
+ specify(".throw", function() {
+ return Promise.resolve().thenThrow(true).then(assert.fail, function(err) {
+ assert.strictEqual(err, true);
+ assert.strictEqual(bindCalls, 5);
+ });
+ });
+
+ specify(".finally", function() {
+ return Promise.resolve(true).lastly(function() {
+ return Promise.delay(1);
+ }).then(function(val) {
+ assert.strictEqual(val, true);
+ assert.strictEqual(bindCalls, 6);
+ });
+ });
+ });
+
+ describe("long promise chain stack overflow", function() {
+ specify("mapSeries", function() {
+ var array = new Array(5000);
+ for (var i = 0; i < array.length; ++i) {
+ array[i] = null;
+ }
+
+ var theError = new Error();
+
+ var queryAsync = Promise.promisify(function(cb) {
+ process.nextTick(function() {
+ cb(theError);
+ }, 1);
+ });
+
+ return Promise.mapSeries(array, function() {
+ return queryAsync();
+ }).caught(function(e) {
+ assert.strictEqual(e.cause, theError);
+ });
+ });
+ });
+ }
+
+
+});
diff --git a/test/mocha/rejections.js b/test/mocha/rejections.js
new file mode 100644
index 0000000..a33ec3d
--- /dev/null
+++ b/test/mocha/rejections.js
@@ -0,0 +1,220 @@
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+describe("Using as a rejection reason", function() {
+ var nullObject = (function() {
+ var es5 = (function(){"use strict";
+ return this;
+ })() === undefined;
+ if (es5) {
+ return function() {
+ return Object.create(null);
+ };
+ } else {
+ return function() {
+ return {};
+ };
+ }
+ })();
+ describe("Object.create(null)", function() {
+ specify("directly", function() {
+ var o = nullObject();
+ return Promise.reject(o).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ specify("through constructor by throw", function() {
+ var o = nullObject();
+ return new Promise(function() {
+ throw o;
+ }).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+
+ specify("through constructor immediately", function() {
+ var o = nullObject();
+ return new Promise(function() {
+ arguments[1](o);
+ }).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ specify("through constructor eventually", function() {
+ var o = nullObject();
+ return new Promise(function(_, r) {
+ setTimeout(function() {
+ r(o);
+ }, 1);
+ }).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ specify("through defer immediately", function() {
+ var o = nullObject();
+ var d = Promise.defer();
+ var ret = d.promise.then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ d.reject(o);
+ return ret;
+ });
+
+ specify("through defer eventually", function() {
+ var o = nullObject();
+ var d = Promise.defer();
+ var ret = d.promise.then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ setTimeout(function() {
+ d.reject(o);
+ }, 1);
+ return ret;
+ });
+
+ specify("through thenThrow immediately", function() {
+ var o = nullObject();
+ return Promise.resolve().thenThrow(o).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ specify("through handler throw", function() {
+ var o = nullObject();
+ return Promise.resolve().then(function() {
+ throw o;
+ }).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ specify("through handler-returned-promise immediately", function() {
+ var o = nullObject();
+ return Promise.resolve().then(function() {
+ return Promise.reject(o);
+ }).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ specify("through handler-returned-promise eventually", function() {
+ var o = nullObject();
+ return Promise.resolve().then(function() {
+ return new Promise(function(_, r) {
+ setTimeout(function() {
+ r(o);
+ }, 1);
+ });
+ }).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ specify("through handler-returned-thenable throw", function() {
+ var o = nullObject();
+ return Promise.resolve().then(function() {
+ return {
+ then: function(_, r) {
+ throw o;
+ }
+ };
+ }).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ specify("through handler-returned-thenable immediately", function() {
+ var o = nullObject();
+ return Promise.resolve().then(function() {
+ return {
+ then: function(_, r) {
+ r(o);
+ }
+ };
+ }).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ specify("through handler-returned-thenable eventually", function() {
+ var o = nullObject();
+ return Promise.resolve().then(function() {
+ return {
+ then: function(_, r) {
+ setTimeout(function() {
+ r(o);
+ }, 1);
+ }
+ };
+ }).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ var BluebirdThenable = require("../../js/debug/promise.js")();
+ specify("through handler-returned-bluebird-thenable immediately", function() {
+ var o = nullObject();
+ return Promise.resolve().then(function() {
+ return BluebirdThenable.reject(o);
+ }).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ specify("through handler-returned-bluebird-thenable eventually", function() {
+ var o = nullObject();
+ return Promise.resolve().then(function() {
+ return new BluebirdThenable(function(_, r) {
+ setTimeout(function() {
+ r(o);
+ }, 1);
+ });
+ }).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ specify("through promisified callback immediately", function() {
+ var o = nullObject();
+ return Promise.promisify(function(cb) {
+ cb(o);
+ })().then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ specify("through immediate PromiseArray promise", function() {
+ var o = nullObject();
+ return Promise.all([Promise.reject(o)]).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ specify("through eventual PromiseArray promise", function() {
+ var o = nullObject();
+ return Promise.all([new Promise(function(_, r) {
+ setTimeout(function() {
+ r(o);
+ }, 1);
+ })]).then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ specify("through promisified callback eventually", function() {
+ var o = nullObject();
+ return Promise.promisify(function(cb) {
+ setTimeout(function() {
+ cb(o);
+ }, 1);
+ })().then(assert.fail, function(e) {
+ assert.strictEqual(e, o);
+ });
+ });
+
+ });
+});
diff --git a/test/mocha/resolution.js b/test/mocha/resolution.js
new file mode 100644
index 0000000..1b70668
--- /dev/null
+++ b/test/mocha/resolution.js
@@ -0,0 +1,434 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+
+var getValues = function() {
+ var d = Promise.defer();
+ var f = Promise.resolve(3);
+ var r = Promise.reject(3);
+
+ setTimeout(function(){
+ d.resolve(3);
+ }, 1);
+
+ return {
+ value: 3,
+ thenableFulfill: {then: function(fn){setTimeout(function(){fn(3)}, 1);}},
+ thenableReject: {then: function(_, fn){setTimeout(function(){fn(3)}, 1);}},
+ promiseFulfilled: f,
+ promiseRejected: r,
+ promiseEventual: d.promise
+ };
+};
+
+function expect(count, callback) {
+ var cur = 0;
+ return new Promise(function() {
+
+ });
+}
+
+function expect(count, done) {
+ var total = 0;
+ return function() {
+ total++;
+ if (total >= count) {
+ }
+ }
+}
+
+describe("Promise.resolve", function() {
+ specify("follows thenables and promises", function() {
+ var values = getValues();
+ var async = false;
+ var ret = Promise.all([
+ Promise.resolve(values.value).then(testUtils.noop),
+ Promise.resolve(values.thenableFulfill).then(testUtils.noop),
+ Promise.resolve(values.thenableReject).then(assert.fail, testUtils.noop),
+ Promise.resolve(values.promiseFulfilled).then(testUtils.noop),
+ Promise.resolve(values.promiseRejected).then(assert.fail, testUtils.noop),
+ Promise.resolve(values.promiseEventual).then(testUtils.noop)
+ ]).then(function(v) {
+ assert.deepEqual(v, [3, 3, 3, 3, 3, 3]);
+ assert(async);
+ });
+ async = true;
+ return ret;
+ });
+});
+
+describe("Cast thenable", function() {
+ var b = {
+ then: function(f, fn){
+ fn(b);
+ }
+ };
+
+ specify("rejects with itself", function() {
+ var promise = Promise.cast(b);
+
+ return promise.then(assert.fail, function(v){
+ assert(v === b);
+ });
+ });
+});
+
+describe("Implicitly cast thenable", function() {
+ var b = {
+ then: function(f, fn){
+ fn(b);
+ }
+ };
+
+ specify("rejects with itself", function() {
+ return Promise.resolve().then(function(){
+ return b;
+ }).then(assert.fail, function(v){
+ assert(v === b);
+ });
+ });
+});
+
+
+
+
+/*!
+ *
+Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+*/
+describe("propagation", function () {
+
+ it("propagate through then with no callback", function () {
+ return Promise.resolve(10)
+ .then()
+ .then(function (ten) {
+ assert.equal(ten,10);
+ });
+ });
+
+ it("propagate through then with modifying callback", function () {
+ return Promise.resolve(10)
+ .then(function (ten) {
+ return ten + 10;
+ })
+ .then(function (twen) {
+ assert.equal(twen,20);
+ });
+ });
+
+ it("errback recovers from exception", function () {
+ var error = new Error("Bah!");
+ return Promise.reject(error)
+ .then(null, function (_error) {
+ assert.equal(_error,error);
+ return 10;
+ })
+ .then(function (value) {
+ assert.equal(value,10);
+ });
+ });
+
+ it("rejection propagates through then with no errback", function () {
+ var error = new Error("Foolish mortals!");
+ return Promise.reject(error)
+ .then()
+ .then(null, function (_error) {
+ assert.equal(_error,error);
+ });
+ });
+
+ it("rejection intercepted and rethrown", function () {
+ var error = new Error("Foolish mortals!");
+ var nextError = new Error("Silly humans!");
+ return Promise.reject(error)
+ .caught(function () {
+ throw nextError;
+ })
+ .then(null, function (_error) {
+ assert.equal(_error,nextError);
+ });
+ });
+
+ it("resolution is forwarded through deferred promise", function () {
+ var a = Promise.defer();
+ var b = Promise.defer();
+ a.resolve(b.promise);
+ b.resolve(10);
+ return a.promise.then(function (eh) {
+ assert.equal(eh, 10);
+ });
+ });
+});
+
+/*
+Based on When.js tests
+
+Open Source Initiative OSI - The MIT License
+
+http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2011 Brian Cavalier
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var sentinel = {};
+var other = {};
+describe("Promise.defer-test", function () {
+
+
+ specify("should fulfill with an immediate value", function() {
+ var d = Promise.defer();
+ d.resolve(sentinel);
+ return d.promise.then(
+ function(val) {
+ assert.equal(val, sentinel);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should return a promise for the resolution value", function() {
+ var d = Promise.defer();
+
+ d.resolve(sentinel);
+ return d.promise.then(
+ function(returnedPromiseVal) {
+ assert.deepEqual(returnedPromiseVal, sentinel);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should return a promise for a promised resolution value", function() {
+ var d = Promise.defer();
+
+ d.resolve(Promise.resolve(sentinel))
+ return d.promise.then(
+ function(returnedPromiseVal) {
+ assert.deepEqual(returnedPromiseVal, sentinel);
+ },
+ assert.fail
+ );
+ });
+
+ specify("should return a promise for a promised rejection value", function() {
+ var d = Promise.defer();
+
+ // Both the returned promise, and the deferred's own promise should
+ // be rejected with the same value
+ d.resolve(Promise.reject(sentinel))
+ return d.promise.then(
+ assert.fail,
+ function(returnedPromiseVal) {
+ assert.deepEqual(returnedPromiseVal, sentinel);
+ }
+ );
+ });
+
+ specify("should invoke newly added callback when already resolved", function() {
+ var d = Promise.defer();
+
+ d.resolve(sentinel);
+
+ return d.promise.then(
+ function(val) {
+ assert.equal(val, sentinel);
+ },
+ assert.fail
+ );
+ });
+
+
+
+ specify("should reject with an immediate value", function() {
+ var d = Promise.defer();
+ d.reject(sentinel);
+ return d.promise.then(
+ assert.fail,
+ function(val) {
+ assert.equal(val, sentinel);
+ }
+ );
+ });
+
+ specify("should reject with fulfilled promised", function() {
+ var d, expected;
+
+ d = Promise.defer();
+ expected = testUtils.fakeResolved(sentinel);
+
+ var ret = d.promise.then(
+ assert.fail,
+ function(val) {
+ assert.equal(val, expected);
+ }
+ );
+
+ d.reject(expected);
+ return ret;
+ });
+
+ specify("should reject with rejected promise", function() {
+ var d, expected;
+
+ d = Promise.defer();
+ expected = testUtils.fakeRejected(sentinel);
+
+ var ret = d.promise.then(
+ assert.fail,
+ function(val) {
+ assert.equal(val, expected);
+ }
+ );
+
+ d.reject(expected);
+ return ret;
+ });
+
+
+ specify("should return a promise for the rejection value", function() {
+ var d = Promise.defer();
+
+ // Both the returned promise, and the deferred's own promise should
+ // be rejected with the same value
+ d.reject(sentinel);
+ return d.promise.then(
+ assert.fail,
+ function(returnedPromiseVal) {
+ assert.deepEqual(returnedPromiseVal, sentinel);
+ }
+ );
+ });
+
+ specify("should invoke newly added errback when already rejected", function() {
+ var d = Promise.defer();
+
+ d.reject(sentinel);
+
+ return d.promise.then(
+ assert.fail,
+ function (val) {
+ assert.deepEqual(val, sentinel);
+ }
+ );
+ });
+});
+
+describe("Promise.fromNode", function() {
+ specify("rejects thrown errors from resolver", function() {
+ var err = new Error();
+ return Promise.fromNode(function(callback) {
+ throw err;
+ }).then(assert.fail, function(e) {
+ assert.strictEqual(err, e);
+ });
+ });
+ specify("rejects rejections as operational errors", function() {
+ var err = new Error();
+ return Promise.fromNode(function(callback) {
+ callback(err);
+ }).caught(Promise.OperationalError, function(e) {
+ assert.strictEqual(err, e.cause);
+ });
+ });
+ specify("resolves normally", function() {
+ var result = {};
+ return Promise.fromNode(function(callback) {
+ callback(null, result);
+ }).then(function(res) {
+ assert.strictEqual(result, res);
+ });
+ });
+ specify("resolves with bound thunk", function() {
+ var nodeFn = function(param, cb) {
+ setTimeout(function() {
+ cb(null, param);
+ }, 1);
+ };
+
+ return Promise.fromNode(nodeFn.bind(null, 1)).then(function(res) {
+ assert.strictEqual(1, res);
+ });
+ });
+ specify("multiArgs option enabled single value", function() {
+ var nodeFn = function(cb) {
+ cb(null, 1);
+ };
+ return Promise.fromNode(function(callback) {
+ nodeFn(callback);
+ }, {multiArgs: true}).then(function(value) {
+ assert.deepEqual([1], value);
+ });
+
+ });
+ specify("multiArgs option enabled multi value", function() {
+ var nodeFn = function(cb) {
+ cb(null, 1, 2, 3);
+ };
+ return Promise.fromNode(function(callback) {
+ nodeFn(callback);
+ }, {multiArgs: true}).then(function(value) {
+ assert.deepEqual([1,2,3], value);
+ });
+
+ });
+ specify("multiArgs option disabled single value", function() {
+ var nodeFn = function(cb) {
+ cb(null, 1);
+ };
+ return Promise.fromNode(function(callback) {
+ nodeFn(callback);
+ }).then(function(value) {
+ assert.strictEqual(1, value);
+ });
+
+ });
+ specify("multiArgs option disabled multi value", function() {
+ var nodeFn = function(cb) {
+ cb(null, 1, 2, 3);
+ };
+ return Promise.fromNode(function(callback) {
+ nodeFn(callback);
+ }).then(function(value) {
+ assert.strictEqual(1, value);
+ });
+
+ });
+});
diff --git a/test/mocha/schedule.js b/test/mocha/schedule.js
new file mode 100644
index 0000000..7af8b12
--- /dev/null
+++ b/test/mocha/schedule.js
@@ -0,0 +1,48 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var schedule = require("../../js/debug/schedule");
+var isNodeJS = testUtils.isNodeJS;
+
+describe("schedule", function () {
+ if (isNodeJS) {
+ describe("for Node.js", function () {
+ it("should preserve the active domain", function() {
+ var domain = require("domain");
+ var activeDomain = domain.create();
+ return new Promise(function(resolve) {
+ activeDomain.run(function () {
+ schedule(function () {
+ assert(domain.active);
+ assert.equal(domain.active, activeDomain);
+ resolve();
+ });
+ });
+ });
+
+ });
+ });
+
+ describe("Promise.setScheduler", function() {
+ it("should work with synchronous scheduler", function() {
+ var prev = Promise.setScheduler(function(task) {
+ task();
+ });
+ var success = false;
+ Promise.resolve().then(function() {
+ success = true;
+ });
+ assert(success);
+ Promise.setScheduler(prev);
+ });
+ it("should throw for non function", function() {
+ try {
+ Promise.setScheduler({});
+ } catch (e) {
+ return Promise.resolve();
+ }
+ assert.fail();
+ });
+ });
+ }
+});
diff --git a/test/mocha/settle.js b/test/mocha/settle.js
new file mode 100644
index 0000000..6ffb0f4
--- /dev/null
+++ b/test/mocha/settle.js
@@ -0,0 +1,172 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+/*!
+ *
+Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+*/
+
+describe("allSettled", function () {
+ it("works on an empty array", function () {
+ return Promise.settle([])
+ .then(function (snapshots) {
+ assert.deepEqual(snapshots, []);
+ });
+ });
+
+ it("deals with a mix of non-promises and promises", function () {
+ return Promise.settle([1, Promise.resolve(2), Promise.reject(3)])
+ .then(function (snapshots) {
+ assert.equal(snapshots[0].value(), 1);
+ assert.equal(snapshots[1].value(), 2);
+ assert.equal(snapshots[2].error(), 3);
+ });
+ });
+
+ it("is settled after every constituent promise is settled", function () {
+ var toFulfill = Promise.defer();
+ var toReject = Promise.defer();
+ var promises = [toFulfill.promise, toReject.promise];
+ var fulfilled;
+ var rejected;
+
+ Promise.attempt(function () {
+ toReject.reject();
+ rejected = true;
+ })
+ .delay(1)
+ .then(function () {
+ toFulfill.resolve();
+ fulfilled = true;
+ });
+
+ return Promise.settle(promises)
+ .then(function () {
+ assert.equal(fulfilled, true);
+ assert.equal(rejected, true);
+ });
+ });
+
+ it("does not modify the input array", function () {
+ var input = [1, Promise.resolve(2), Promise.reject(3)];
+
+ return Promise.settle(input)
+ .then(function (snapshots) {
+ assert.notEqual(snapshots, input);
+ assert.equal(snapshots[0].value(), 1);
+ assert.equal(snapshots[1].value(), 2);
+ assert.equal(snapshots[2].error(), 3);
+ });
+ });
+
+});
+
+/*
+Based on When.js tests
+
+Open Source Initiative OSI - The MIT License
+
+http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2011 Brian Cavalier
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var sentinel = {};
+var other = {};
+describe("Promise.settle-test", function () {
+
+
+ Promise.promise = function(rs){
+ var a = Promise.defer();
+ rs(a);
+ return a.promise;
+ };
+
+
+ specify("should settle empty array", function() {
+ return Promise.settle([]).then(function(settled) {
+ assert.deepEqual(settled.length, 0);
+ });
+ });
+
+ specify("should reject if promise for input array rejects", function() {
+ return Promise.settle(Promise.reject(sentinel)).then(
+ assert.fail,
+ function(reason) {
+ assert.equal(reason, sentinel);
+ }
+ );
+ });
+
+ specify("should settle values", function() {
+ var array = [0, 1, sentinel];
+ return Promise.settle(array).then(function(settled) {
+ testUtils.assertFulfilled(settled[0], 0);
+ testUtils.assertFulfilled(settled[1], 1);
+ testUtils.assertFulfilled(settled[2], sentinel);
+ });
+ });
+
+ specify("should settle promises", function() {
+ var array = [0, Promise.resolve(sentinel), Promise.reject(sentinel)];
+ return Promise.settle(array).then(function(settled) {
+ testUtils.assertFulfilled(settled[0], 0);
+ testUtils.assertFulfilled(settled[1], sentinel);
+ testUtils.assertRejected(settled[2], sentinel);
+ });
+ });
+
+ specify("returned promise should fulfill once all inputs settle", function() {
+ var array, p1, p2, resolve, reject;
+
+ p1 = Promise.promise(function(r) { resolve = function(a){r.fulfill(a);}; });
+ p2 = Promise.promise(function(r) { reject = function(a){r.reject(a);}; });
+
+ array = [0, p1, p2];
+
+ setTimeout(function() { resolve(sentinel); }, 0);
+ setTimeout(function() { reject(sentinel); }, 0);
+
+ return Promise.settle(array).then(function(settled) {
+ testUtils.assertFulfilled(settled[0], 0);
+ testUtils.assertFulfilled(settled[1], sentinel);
+ testUtils.assertRejected(settled[2], sentinel);
+ });
+ });
+});
diff --git a/test/mocha/some.js b/test/mocha/some.js
new file mode 100644
index 0000000..ee91c0c
--- /dev/null
+++ b/test/mocha/some.js
@@ -0,0 +1,174 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+describe("Promise.some", function(){
+ it("should reject on negative number", function(){
+ return Promise.some([1,2,3], -1)
+ .then(assert.fail)
+ .caught(Promise.TypeError, function(){
+ });
+ });
+
+ it("should reject on NaN", function(){
+ return Promise.some([1,2,3], -0/0)
+ .then(assert.fail)
+ .caught(Promise.TypeError, function(){
+ });
+ });
+
+ it("should reject on non-array", function(){
+ return Promise.some({}, 2)
+ .then(assert.fail)
+ .caught(Promise.TypeError, function(){
+ });
+ });
+
+ it("should reject with rangeerror when impossible to fulfill", function(){
+ return Promise.some([1,2,3], 4)
+ .then(assert.fail)
+ .caught(Promise.RangeError, function(e){
+ });
+ });
+
+ it("should fulfill with empty array with 0", function(){
+ return Promise.some([1,2,3], 0).then(function(result){
+ assert.deepEqual(result, []);
+ });
+ });
+});
+
+/*
+Based on When.js tests
+
+Open Source Initiative OSI - The MIT License
+
+http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2011 Brian Cavalier
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
+
+var RangeError = Promise.RangeError;
+
+describe("Promise.some-test", function () {
+
+ specify("should reject empty input", function() {
+ return Promise.some([], 1).caught(RangeError, function() {
+ });
+ });
+
+ specify("should resolve values array", function() {
+ var input = [1, 2, 3];
+ return Promise.some(input, 2).then(
+ function(results) {
+ assert(testUtils.isSubset(results, input));
+ },
+ assert.fail
+ )
+ });
+
+ specify("should resolve promises array", function() {
+ var input = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
+ return Promise.some(input, 2).then(
+ function(results) {
+ assert(testUtils.isSubset(results, [1, 2, 3]));
+ },
+ assert.fail
+ )
+ });
+
+ specify("should not resolve sparse array input", function() {
+ var input = [, 1, , 2, 3 ];
+ return Promise.some(input, 2).then(
+ function(results) {
+ assert.deepEqual(results, [void 0, 1]);
+ },
+ function() {
+ console.error(arguments);
+ assert.fail();
+ }
+ )
+ });
+
+ specify("should reject with all rejected input values if resolving howMany becomes impossible", function() {
+ var input = [Promise.resolve(1), Promise.reject(2), Promise.reject(3)];
+ return Promise.some(input, 2).then(
+ assert.fail,
+ function(err) {
+ //Cannot use deep equality in IE8 because non-enumerable properties are not
+ //supported
+ assert(err[0] === 2);
+ assert(err[1] === 3);
+ }
+ )
+ });
+
+ specify("should reject with aggregateError", function() {
+ var input = [Promise.resolve(1), Promise.reject(2), Promise.reject(3)];
+ var AggregateError = Promise.AggregateError;
+ return Promise.some(input, 2)
+ .then(assert.fail)
+ .caught(AggregateError, function(e) {
+ assert(e[0] === 2);
+ assert(e[1] === 3);
+ assert(e.length === 2);
+ });
+ });
+
+ specify("aggregate error should be caught in .error", function() {
+ var input = [Promise.resolve(1), Promise.reject(2), Promise.reject(3)];
+ var AggregateError = Promise.AggregateError;
+ return Promise.some(input, 2)
+ .then(assert.fail)
+ .error(function(e) {
+ assert(e[0] === 2);
+ assert(e[1] === 3);
+ assert(e.length === 2);
+ });
+ });
+
+ specify("should accept a promise for an array", function() {
+ var expected, input;
+
+ expected = [1, 2, 3];
+ input = Promise.resolve(expected);
+
+ return Promise.some(input, 2).then(
+ function(results) {
+ assert.deepEqual(results.length, 2);
+ },
+ assert.fail
+ )
+ });
+
+ specify("should reject when input promise does not resolve to array", function() {
+ return Promise.some(Promise.resolve(1), 1).caught(TypeError, function(e){
+ });
+ });
+
+ specify("should reject when given immediately rejected promise", function() {
+ var err = new Error();
+ return Promise.some(Promise.reject(err), 1).then(assert.fail, function(e) {
+ assert.strictEqual(err, e);
+ });
+ });
+});
diff --git a/test/mocha/spread.js b/test/mocha/spread.js
new file mode 100644
index 0000000..8c23efd
--- /dev/null
+++ b/test/mocha/spread.js
@@ -0,0 +1,304 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+/*!
+ *
+Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+*/
+
+describe("spread", function () {
+ it("spreads values across arguments", function () {
+ return Promise.resolve([1, 2, 3]).spread(function (a, b) {
+ assert.equal(b,2);
+ });
+ });
+
+ it("spreads promises for arrays across arguments", function () {
+ return Promise.resolve([Promise.resolve(10)])
+ .all()
+ .spread(function (value) {
+ assert.equal(value,10);
+ });
+ });
+
+ it("spreads arrays of promises across arguments", function () {
+ var deferredA = Promise.defer();
+ var deferredB = Promise.defer();
+
+ var promise = Promise.resolve([deferredA.promise, deferredB.promise]).all().spread(
+ function (a, b) {
+ assert.equal(a,10);
+ assert.equal(b,20);
+ });
+
+ Promise.delay(1).then(function () {
+ deferredA.resolve(10);
+ });
+ Promise.delay(1).then(function () {
+ deferredB.resolve(20);
+ });
+
+ return promise;
+ });
+
+ it("spreads arrays of thenables across arguments", function () {
+ var p1 = {
+ then: function(v) {
+ v(10);
+ }
+ };
+ var p2 = {
+ then: function(v) {
+ v(20);
+ }
+ };
+
+ var promise = Promise.resolve([p1, p2]).all().spread(function (a, b) {
+ assert.equal(a,10);
+ assert.equal(b,20);
+ });
+ return promise;
+ });
+
+ it("should wait for promises in the returned array even when not calling .all", function() {
+ var d1 = Promise.defer();
+ var d2 = Promise.defer();
+ var d3 = Promise.defer();
+ setTimeout(function(){
+ d1.resolve(1);
+ d2.resolve(2);
+ d3.resolve(3);
+ }, 1);
+ return Promise.resolve().then(function(){
+ return [d1.promise, d2.promise, d3.promise];
+ }).all().spread(function(a, b, c){
+ assert(a === 1);
+ assert(b === 2);
+ assert(c === 3);
+ });
+ });
+
+ it("should wait for thenables in the returned array even when not calling .all", function() {
+ var t1 = {
+ then: function(fn) {
+ setTimeout(function(){
+ fn(1);
+ }, 1);
+ }
+ };
+ var t2 = {
+ then: function(fn) {
+ setTimeout(function(){
+ fn(2);
+ }, 1);
+ }
+ };
+ var t3 = {
+ then: function(fn) {
+ setTimeout(function(){
+ fn(3);
+ }, 1);
+ }
+ };
+ return Promise.resolve().then(function(){
+ return [t1, t2, t3];
+ }).all().spread(function(a, b, c){
+ assert(a === 1);
+ assert(b === 2);
+ assert(c === 3);
+ });
+ });
+
+ it("should wait for promises in an array that a returned promise resolves to even when not calling .all", function() {
+ var d1 = Promise.defer();
+ var d2 = Promise.defer();
+ var d3 = Promise.defer();
+ var defer = Promise.defer();
+
+ setTimeout(function(){
+ defer.resolve([d1.promise, d2.promise, d3.promise]);
+ setTimeout(function(){
+ d1.resolve(1);
+ d2.resolve(2);
+ d3.resolve(3);
+ }, 1);
+ }, 1);
+
+ return Promise.resolve().then(function(){
+ return defer.promise;
+ }).all().spread(function(a, b, c){
+ assert(a === 1);
+ assert(b === 2);
+ assert(c === 3);
+ });
+ });
+
+ it("should wait for thenables in an array that a returned thenable resolves to even when not calling .all", function() {
+ var t1 = {
+ then: function(fn) {
+ setTimeout(function(){
+ fn(1);
+ }, 1);
+ }
+ };
+ var t2 = {
+ then: function(fn) {
+ setTimeout(function(){
+ fn(2);
+ }, 1);
+ }
+ };
+ var t3 = {
+ then: function(fn) {
+ setTimeout(function(){
+ fn(3);
+ }, 1);
+ }
+ };
+
+ var thenable = {
+ then: function(fn) {
+ setTimeout(function(){
+ fn([t1, t2, t3])
+ }, 1);
+ }
+ };
+
+ return Promise.resolve().then(function(){
+ return thenable;
+ }).all().spread(function(a, b, c){
+ assert(a === 1);
+ assert(b === 2);
+ assert(c === 3);
+ });
+ });
+
+ it("should reject with error when non array is the ultimate value to be spread", function(){
+ return Promise.resolve().then(function(){
+ return 3
+ }).spread(function(a, b, c){
+ assert.fail();
+ }).then(assert.fail, function(e){
+ })
+ });
+
+ specify("gh-235", function() {
+ var P = Promise;
+ return P.resolve(1).then(function(x) {
+ return [x, P.resolve(2)]
+ }).spread(function(x, y) {
+ return P.all([P.resolve(3), P.resolve(4)]);
+ }).then(function(a) {
+ assert.deepEqual([3, 4], a);
+ });
+ })
+
+ specify("error when passed non-function", function() {
+ return Promise.resolve(3)
+ .spread()
+ .then(assert.fail)
+ .caught(Promise.TypeError, function() {});
+ });
+
+ specify("error when resolution is non-spredable", function() {
+ return Promise.resolve(3)
+ .spread(function(){})
+ .then(assert.fail)
+ .caught(Promise.TypeError, function() {});
+ });
+});
+
+/*
+Based on When.js tests
+
+Open Source Initiative OSI - The MIT License
+
+http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2011 Brian Cavalier
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
+
+describe("Promise.spread-test", function () {
+ var slice = [].slice;
+
+ specify("should return a promise", function() {
+ assert(typeof (Promise.defer().promise.spread(function(){}).then) === "function");
+ });
+
+ specify("should apply onFulfilled with array as argument list", function() {
+ var expected = [1, 2, 3];
+ return Promise.resolve(expected).spread(function() {
+ assert.deepEqual(slice.call(arguments), expected);
+ });
+ });
+
+ specify("should resolve array contents", function() {
+ var expected = [Promise.resolve(1), 2, Promise.resolve(3)];
+ return Promise.resolve(expected).all().spread(function() {
+ assert.deepEqual(slice.call(arguments), [1, 2, 3]);
+ });
+ });
+
+ specify("should reject if any item in array rejects", function() {
+ var expected = [Promise.resolve(1), 2, Promise.reject(3)];
+ return Promise.resolve(expected).all()
+ .spread(assert.fail)
+ .then(assert.fail, function() {});
+ });
+
+ specify("should apply onFulfilled with array as argument list", function() {
+ var expected = [1, 2, 3];
+ return Promise.resolve(Promise.resolve(expected)).spread(function() {
+ assert.deepEqual(slice.call(arguments), expected);
+ });
+ });
+
+ specify("should resolve array contents", function() {
+ var expected = [Promise.resolve(1), 2, Promise.resolve(3)];
+ return Promise.resolve(Promise.resolve(expected)).all().spread(function() {
+ assert.deepEqual(slice.call(arguments), [1, 2, 3]);
+ });
+ });
+
+ specify("should reject if input is a rejected promise", function() {
+ var expected = Promise.reject([1, 2, 3]);
+ return Promise.resolve(expected)
+ .spread(assert.fail)
+ .then(assert.fail, function() {});
+ });
+});
diff --git a/test/mocha/synchronous_inspection.js b/test/mocha/synchronous_inspection.js
new file mode 100644
index 0000000..d02705b
--- /dev/null
+++ b/test/mocha/synchronous_inspection.js
@@ -0,0 +1,144 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+describe("Promise.prototype.toJSON", function() {
+ it("should match pending state", function() {
+ var a = new Promise(function(){}).toJSON();
+ assert.strictEqual(a.isFulfilled, false);
+ assert.strictEqual(a.isRejected, false);
+ assert.strictEqual(a.rejectionReason, undefined);
+ assert.strictEqual(a.fulfillmentValue, undefined);
+ });
+ it("should match rejected state", function() {
+ var a = Promise.reject(3).toJSON();
+ assert.strictEqual(a.isFulfilled, false);
+ assert.strictEqual(a.isRejected, true);
+ assert.strictEqual(a.rejectionReason, 3);
+ assert.strictEqual(a.fulfillmentValue, undefined);
+ });
+ it("should match fulfilled state", function() {
+ var a = Promise.resolve(3).toJSON();
+ assert.strictEqual(a.isFulfilled, true);
+ assert.strictEqual(a.isRejected, false);
+ assert.strictEqual(a.rejectionReason, undefined);
+ assert.strictEqual(a.fulfillmentValue, 3);
+ });
+});
+/*!
+ *
+Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+*/
+// In browsers that support strict mode, it'll be `undefined`; otherwise, the global.
+var calledAsFunctionThis = (function () { return this; }());
+describe("inspect", function () {
+
+ it("for a fulfilled promise", function () {
+ var ret = Promise.resolve(10);
+ assert.equal(ret.value(), 10);
+ assert.equal(ret.isFulfilled(), true);
+ });
+
+ it("for a rejected promise", function () {
+ var e = new Error("In your face.");
+ var ret = Promise.reject(e);
+ assert.equal(ret.reason(), e);
+ assert.equal(ret.isRejected(), true);
+ return ret.then(assert.fail, function(){});
+ });
+
+ it("for a pending, unresolved promise", function () {
+ var pending = Promise.defer().promise;
+ assert.equal(pending.isPending(), true);
+ });
+
+ it("for a promise resolved to a rejected promise", function () {
+ var deferred = Promise.defer();
+ var error = new Error("Rejected!");
+ var reject = Promise.reject(error);
+ deferred.resolve(reject);
+
+ assert.equal(deferred.promise.isRejected(), true);
+ assert.equal(deferred.promise.reason(), error);
+ return deferred.promise.then(assert.fail, function(){});
+ });
+
+ it("for a promise resolved to a fulfilled promise", function () {
+ var deferred = Promise.defer();
+ var fulfilled = Promise.resolve(10);
+ deferred.resolve(fulfilled);
+
+ assert.equal(deferred.promise.isFulfilled(), true);
+ assert.equal(deferred.promise.value(), 10);
+ });
+
+ it("for a promise resolved to a pending promise", function () {
+ var a = Promise.defer();
+ var b = Promise.defer();
+ a.resolve(b.promise);
+
+ assert.equal(a.promise.isPending(), true);
+ });
+
+ describe(".value()", function() {
+ specify("of unfulfilled inspection should throw", function() {
+ Promise.reject(1).reflect().then(function(inspection) {
+ try {
+ inspection.value();
+ } catch (e) {
+ return Promise.resolve();
+ }
+ assert.fail();
+ });
+ });
+ specify("of unfulfilled promise should throw", function() {
+ var r = Promise.reject(1);
+ r.reason();
+ try {
+ r.value();
+ } catch (e) {
+ return Promise.resolve();
+ }
+ assert.fail();
+ });
+ });
+
+ describe(".reason()", function() {
+ specify("of unrejected inspection should throw", function() {
+ Promise.resolve(1).reflect().then(function(inspection) {
+ try {
+ inspection.reason();
+ } catch (e) {
+ return Promise.resolve();
+ }
+ assert.fail();
+ });
+ });
+
+ specify("of unrejected promise should throw", function() {
+ try {
+ Promise.resolve(1).reason();
+ } catch (e) {
+ return Promise.resolve();
+ }
+ assert.fail();
+ });
+ });
+});
diff --git a/test/mocha/tap.js b/test/mocha/tap.js
new file mode 100644
index 0000000..d4f70a7
--- /dev/null
+++ b/test/mocha/tap.js
@@ -0,0 +1,66 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+
+describe("tap", function () {
+ specify("passes through value", function() {
+ return Promise.resolve("test").tap(function() {
+ return 3;
+ }).then(function(value){
+ assert.equal(value, "test");
+ });
+ });
+
+ specify("passes through value after returned promise is fulfilled", function() {
+ var async = false;
+ return Promise.resolve("test").tap(function() {
+ return new Promise(function(r) {
+ setTimeout(function(){
+ async = true;
+ r(3);
+ }, 1);
+ });
+ }).then(function(value){
+ assert(async);
+ assert.equal(value, "test");
+ });
+ });
+
+ specify("is not called on rejected promise", function() {
+ var called = false;
+ return Promise.reject("test").tap(function() {
+ called = true;
+ }).then(assert.fail, function(value){
+ assert(!called);
+ });
+ });
+
+ specify("passes immediate rejection", function() {
+ var err = new Error();
+ return Promise.resolve("test").tap(function() {
+ throw err;
+ }).tap(assert.fail).then(assert.fail, function(e){
+ assert(err === e);
+ });
+ });
+
+ specify("passes eventual rejection", function() {
+ var err = new Error();
+ return Promise.resolve("test").tap(function() {
+ return new Promise(function(_, rej) {
+ setTimeout(function(){
+ rej(err);
+ }, 1)
+ });
+ }).tap(assert.fail).then(assert.fail, function(e) {
+ assert(err === e);
+ });
+ });
+
+ specify("passes value", function() {
+ return Promise.resolve(123).tap(function(a) {
+ assert(a === 123);
+ });
+ });
+});
diff --git a/test/mocha/tapCatch.js b/test/mocha/tapCatch.js
new file mode 100644
index 0000000..e141ea4
--- /dev/null
+++ b/test/mocha/tapCatch.js
@@ -0,0 +1,130 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+function rejection() {
+ var error = new Error("test");
+ var rejection = Promise.reject(error);
+ rejection.err = error;
+ return rejection;
+}
+
+describe("tapCatch", function () {
+
+ specify("passes through rejection reason", function() {
+ return rejection().tapCatch(function() {
+ return 3;
+ }).caught(function(value) {
+ assert.equal(value.message, "test");
+ });
+ });
+
+ specify("passes through reason after returned promise is fulfilled", function() {
+ var async = false;
+ return rejection().tapCatch(function() {
+ return new Promise(function(r) {
+ setTimeout(function(){
+ async = true;
+ r(3);
+ }, 1);
+ });
+ }).caught(function(value) {
+ assert(async);
+ assert.equal(value.message, "test");
+ });
+ });
+
+ specify("is not called on fulfilled promise", function() {
+ var called = false;
+ return Promise.resolve("test").tapCatch(function() {
+ called = true;
+ }).then(function(value){
+ assert(!called);
+ }, assert.fail);
+ });
+
+ specify("passes immediate rejection", function() {
+ var err = new Error();
+ return rejection().tapCatch(function() {
+ throw err;
+ }).tap(assert.fail).then(assert.fail, function(e) {
+ assert(err === e);
+ });
+ });
+
+ specify("passes eventual rejection", function() {
+ var err = new Error();
+ return rejection().tapCatch(function() {
+ return new Promise(function(_, rej) {
+ setTimeout(function(){
+ rej(err);
+ }, 1)
+ });
+ }).tap(assert.fail).then(assert.fail, function(e) {
+ assert(err === e);
+ });
+ });
+
+ specify("passes reason", function() {
+ return rejection().tapCatch(function(a) {
+ assert(a === rejection);
+ }).then(assert.fail, function() {});
+ });
+
+ specify("Works with predicates", function() {
+ var called = false;
+ return Promise.reject(new TypeError).tapCatch(TypeError, function(a) {
+ called = true;
+ assert(err instanceof TypeError)
+ }).then(assert.fail, function(err) {
+ assert(called === true);
+ assert(err instanceof TypeError);
+ });
+ });
+ specify("Does not get called on predicates that don't match", function() {
+ var called = false;
+ return Promise.reject(new TypeError).tapCatch(ReferenceError, function(a) {
+ called = true;
+ }).then(assert.fail, function(err) {
+ assert(called === false);
+ assert(err instanceof TypeError);
+ });
+ });
+
+ specify("Supports multiple predicates", function() {
+ var calledA = false;
+ var calledB = false;
+ var calledC = false;
+
+ var promiseA = Promise.reject(new ReferenceError).tapCatch(
+ ReferenceError,
+ TypeError,
+ function (e) {
+ assert(e instanceof ReferenceError);
+ calledA = true;
+ }
+ ).catch(function () {});
+
+ var promiseB = Promise.reject(new TypeError).tapCatch(
+ ReferenceError,
+ TypeError,
+ function (e) {
+ assert(e instanceof TypeError);
+ calledB = true;
+ }
+ ).catch(function () {});
+
+ var promiseC = Promise.reject(new SyntaxError).tapCatch(
+ ReferenceError,
+ TypeError,
+ function (e) {
+ calledC = true;
+ }
+ ).catch(function () {});
+
+ return Promise.join(promiseA, promiseB, promiseC, function () {
+ assert(calledA === true);
+ assert(calledB === true);
+ assert(calledC === false);
+ });
+ })
+});
diff --git a/test/mocha/timers.js b/test/mocha/timers.js
new file mode 100644
index 0000000..17bbc87
--- /dev/null
+++ b/test/mocha/timers.js
@@ -0,0 +1,192 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var Q = Promise;
+Promise.config({cancellation: true})
+var globalObject = typeof window !== "undefined" ? window : new Function("return this;")();
+/*
+Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+*/
+
+describe("timeout", function () {
+ it("should do nothing if the promise fulfills quickly", function() {
+ Promise.delay(1).timeout(200).then(function(){
+ });
+ });
+
+ it("should do nothing if the promise rejects quickly", function() {
+ var goodError = new Error("haha!");
+ return Promise.delay(1)
+ .then(function () {
+ throw goodError;
+ })
+ .timeout(200)
+ .then(undefined, function (error) {
+ assert(error === goodError);
+ });
+ });
+
+ it("should reject with a timeout error if the promise is too slow", function() {
+ return Promise.delay(1)
+ .timeout(10)
+ .caught(Promise.TimeoutError, function(){
+ })
+ });
+
+ it("should reject with a custom timeout error if the promise is too slow and msg was provided", function() {
+ return Promise.delay(1)
+ .timeout(10, "custom")
+ .caught(Promise.TimeoutError, function(e){
+ assert(/custom/i.test(e.message));
+ });
+ });
+
+ it("should cancel the parent promise once the timeout expires", function() {
+ var didNotExecute = true;
+ var wasRejectedWithTimeout = false;
+ var p = Promise.delay(22).then(function() {
+ didNotExecute = false;
+ })
+ p.timeout(11).thenReturn(10).caught(Promise.TimeoutError, function(e) {
+ wasRejectedWithTimeout = true;
+ })
+ return Promise.delay(33).then(function() {
+ assert(didNotExecute, "parent promise was not cancelled");
+ assert(wasRejectedWithTimeout, "promise was not rejected with timeout");
+ })
+ });
+
+ it("should not cancel the parent promise if there are multiple consumers", function() {
+ var derivedNotCancelled = false;
+ var p = Promise.delay(22);
+ var derived = p.then(function() {
+ derivedNotCancelled = true;
+ })
+ p.timeout(11).thenReturn(10)
+ return Promise.delay(33).then(function() {
+ assert(derivedNotCancelled, "derived promise was cancelled")
+ })
+ })
+
+ var globalsAreReflectedInGlobalObject = (function(window) {
+ var fn = function(id){return clearTimeout(id);};
+ var old = window.clearTimeout;
+ window.clearTimeout = fn;
+ var ret = clearTimeout === fn;
+ window.clearTimeout = old;
+ return ret;
+ })(globalObject);
+
+ if (globalsAreReflectedInGlobalObject) {
+ describe("timer handle clearouts", function() {
+ var fakeSetTimeout, fakeClearTimeout;
+ var expectedHandleType;
+
+ before(function() {
+ fakeSetTimeout = globalObject.setTimeout;
+ fakeClearTimeout = globalObject.clearTimeout;
+ globalObject.setTimeout = globalObject.oldSetTimeout;
+ globalObject.clearTimeout = globalObject.oldClearTimeout;
+ expectedHandleType = typeof (globalObject.setTimeout(function(){}, 1));
+ });
+
+ after(function() {
+ globalObject.setTimeout = fakeSetTimeout;
+ globalObject.clearTimeout = fakeClearTimeout;
+ });
+
+ it("should clear timeouts with proper handle type when fulfilled", function() {
+ var old = globalObject.clearTimeout;
+ var handleType = "empty";
+ globalObject.clearTimeout = function(handle) {
+ handleType = typeof handle;
+ globalObject.clearTimeout = old;
+ };
+
+ return Promise.delay(1).timeout(10000).then(function() {
+ assert.strictEqual(expectedHandleType, handleType);
+ });
+ });
+
+ it("should clear timeouts with proper handle type when rejected", function() {
+ var old = globalObject.clearTimeout;
+ var handleType = "empty";
+ globalObject.clearTimeout = function(handle) {
+ handleType = typeof handle;
+ globalObject.clearTimeout = old;
+ };
+
+ return new Promise(function(_, reject) {
+ setTimeout(reject, 10);
+ }).timeout(10000).then(null, function() {
+ assert.strictEqual(expectedHandleType, handleType);
+ });
+ });
+ })
+
+ }
+});
+
+describe("delay", function () {
+ it("should not delay rejection", function() {
+ var promise = Promise.reject(5).delay(1);
+
+ promise.then(assert.fail, function(){});
+
+ return Promise.delay(1).then(function () {
+ assert(!promise.isPending());
+ });
+ });
+
+ it("should delay after resolution", function () {
+ var promise1 = Promise.delay(1, "what");
+ var promise2 = promise1.delay(1);
+
+ return promise2.then(function (value) {
+ assert(value === "what");
+ });
+ });
+
+ it("should resolve follower promise's value", function() {
+ var resolveF;
+ var f = new Promise(function() {
+ resolveF = arguments[0];
+ });
+ var v = new Promise(function(f) {
+ setTimeout(function() {
+ f(3);
+ }, 1);
+ });
+ resolveF(v);
+ return Promise.delay(1, f).then(function(value) {
+ assert.equal(value, 3);
+ });
+ });
+
+ it("should reject with a custom error if an error was provided as a parameter", function() {
+ var err = Error("Testing Errors")
+ return Promise.delay(1)
+ .timeout(10, err)
+ .caught(function(e){
+ assert(e === err);
+ });
+ });
+
+});
diff --git a/test/mocha/try.js b/test/mocha/try.js
new file mode 100644
index 0000000..93087db
--- /dev/null
+++ b/test/mocha/try.js
@@ -0,0 +1,73 @@
+"use strict";
+
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+var obj = {};
+var error = new Error();
+var thrower = function() {
+ throw error;
+};
+
+var identity = function(val) {
+ return val;
+};
+
+var array = function() {
+ return [].slice.call(arguments);
+};
+
+var receiver = function() {
+ return this;
+};
+
+var tryy = Promise["try"];
+
+describe("Promise.attempt", function(){
+ specify("should reject when the function throws", function() {
+ var async = false;
+ var ret = tryy(thrower).then(assert.fail, function(e) {
+ assert(async);
+ assert(e === error);
+ });
+ async = true;
+ return ret;
+ });
+
+ specify("should reject when the function is not a function", function() {
+ var async = false;
+ var ret = tryy(null).then(assert.fail, function(e) {
+ assert(async);
+ assert(e instanceof Promise.TypeError);
+ });
+ async = true;
+ return ret;
+ });
+
+ specify("should unwrap returned promise", function(){
+ var d = Promise.defer();
+
+ var ret = tryy(function(){
+ return d.promise;
+ }).then(function(v){
+ assert(v === 3);
+ })
+
+ setTimeout(function(){
+ d.fulfill(3);
+ }, 1);
+ return ret;
+ });
+ specify("should unwrap returned thenable", function(){
+ return tryy(function(){
+ return {
+ then: function(f, v) {
+ f(3);
+ }
+ }
+ }).then(function(v){
+ assert(v === 3);
+ });
+
+ });
+});
diff --git a/test/mocha/unhandled_rejections.js b/test/mocha/unhandled_rejections.js
new file mode 100644
index 0000000..0745b5b
--- /dev/null
+++ b/test/mocha/unhandled_rejections.js
@@ -0,0 +1,875 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+var noop = testUtils.noop;
+var isStrictModeSupported = testUtils.isStrictModeSupported;
+var onUnhandledFail = testUtils.onUnhandledFail;
+var onUnhandledSucceed = testUtils.onUnhandledSucceed;
+
+function yesE() {
+ return new Error();
+}
+
+function notE() {
+ var rets = [{}, []];
+ return rets[Math.random()*rets.length|0];
+}
+
+function cleanUp() {
+ Promise.onPossiblyUnhandledRejection(null);
+ Promise.onUnhandledRejectionHandled(null);
+}
+
+function setupCleanUps() {
+ beforeEach(cleanUp);
+ afterEach(cleanUp);
+}
+
+describe("Will report rejections that are not handled in time", function() {
+ setupCleanUps();
+
+ specify("Immediately rejected not handled at all", function testFunction() {
+ var promise = Promise.defer();
+ promise.reject(yesE());
+ return onUnhandledSucceed();
+ });
+
+ specify("Eventually rejected not handled at all", function testFunction() {
+ var promise = Promise.defer();
+ setTimeout(function(){
+ promise.reject(yesE());
+ }, 1);
+ return onUnhandledSucceed();
+ });
+
+ specify("Immediately rejected handled too late", function testFunction() {
+ var promise = Promise.defer();
+ promise.reject(yesE());
+ setTimeout(function() {
+ promise.promise.then(assert.fail, function(){});
+ }, 150);
+ return onUnhandledSucceed();
+ });
+ specify("Eventually rejected handled too late", function testFunction() {
+ var promise = Promise.defer();
+ setTimeout(function(){
+ promise.reject(yesE());
+ setTimeout(function() {
+ promise.promise.then(assert.fail, function(){});
+ }, 150);
+ }, 1);
+
+ return onUnhandledSucceed();
+ });
+});
+
+describe("Will report rejections that are code errors", function() {
+ setupCleanUps();
+
+ specify("Immediately fulfilled handled with erroneous code", function testFunction() {
+ var deferred = Promise.defer();
+ var promise = deferred.promise;
+ deferred.fulfill(null);
+ promise.then(function(itsNull){
+ itsNull.will.fail.four.sure();
+ });
+ return onUnhandledSucceed();
+ });
+ specify("Eventually fulfilled handled with erroneous code", function testFunction() {
+ var deferred = Promise.defer();
+ var promise = deferred.promise;
+ setTimeout(function(){
+ deferred.fulfill(null);
+ }, 1);
+ promise.then(function(itsNull){
+ itsNull.will.fail.four.sure();
+ });
+ return onUnhandledSucceed();
+ });
+
+ specify("Already fulfilled handled with erroneous code but then recovered and failDeferred again", function testFunction() {
+ var err = yesE();
+ var promise = Promise.resolve(null);
+ promise.then(function(itsNull){
+ itsNull.will.fail.four.sure();
+ }).then(assert.fail, function(e){
+ assert.ok(e instanceof Promise.TypeError);
+ }).then(function(){
+ //then assert.failing again
+ //this error should be reported
+ throw err;
+ });
+ return onUnhandledSucceed(err);
+ });
+
+ specify("Immediately fulfilled handled with erroneous code but then recovered and failDeferred again", function testFunction() {
+ var err = yesE();
+ var deferred = Promise.defer();
+ var promise = deferred.promise;
+ deferred.fulfill(null);
+ promise.then(function(itsNull){
+ itsNull.will.fail.four.sure();
+ }).then(assert.fail, function(e){
+ assert.ok(e instanceof Promise.TypeError)
+ //Handling the type error here
+ }).then(function(){
+ //then assert.failing again
+ //this error should be reported
+ throw err;
+ });
+ return onUnhandledSucceed(err);
+ });
+
+ specify("Eventually fulfilled handled with erroneous code but then recovered and failDeferred again", function testFunction() {
+ var err = yesE();
+ var deferred = Promise.defer();
+ var promise = deferred.promise;
+
+ promise.then(function(itsNull){
+ itsNull.will.fail.four.sure();
+ }).then(assert.fail, function(e){
+ assert.ok(e instanceof Promise.TypeError)
+ //Handling the type error here
+ }).then(function(){
+ //then assert.failing again
+ //this error should be reported
+ throw err;
+ });
+
+ setTimeout(function(){
+ deferred.fulfill(null);
+ }, 1);
+ return onUnhandledSucceed(err);
+ });
+
+ specify("Already fulfilled handled with erroneous code but then recovered in a parallel handler and failDeferred again", function testFunction() {
+ var err = yesE();
+ var promise = Promise.resolve(null);
+ promise.then(function(itsNull){
+ itsNull.will.fail.four.sure();
+ }).then(assert.fail, function(e){
+ assert.ok(e instanceof Promise.TypeError)
+ });
+
+ promise.then(function(){
+ //then assert.failing again
+ //this error should be reported
+ throw err;
+ });
+ return onUnhandledSucceed(err);
+ });
+});
+
+describe("Will report rejections that are not instanceof Error", function() {
+ setupCleanUps();
+
+ specify("Immediately rejected with non instanceof Error", function testFunction() {
+ var failDeferred = Promise.defer();
+ failDeferred.reject(notE());
+ return onUnhandledSucceed();
+ });
+
+ specify("Eventually rejected with non instanceof Error", function testFunction() {
+ var failDeferred = Promise.defer();
+ setTimeout(function(){
+ failDeferred.reject(notE());
+ }, 1);
+ return onUnhandledSucceed();
+ });
+});
+
+describe("Will handle hostile rejection reasons like frozen objects", function() {
+ setupCleanUps();
+
+ specify("Immediately rejected with non instanceof Error", function testFunction() {
+ var failDeferred = Promise.defer();
+ failDeferred.reject(Object.freeze({}));
+ return onUnhandledSucceed(function(e) {
+ return true;
+ });
+ });
+
+
+ specify("Eventually rejected with non instanceof Error", function testFunction() {
+ var failDeferred = Promise.defer();
+ var obj = {};
+ setTimeout(function(){
+ failDeferred.reject(Object.freeze(obj));
+ }, 1);
+ return onUnhandledSucceed(function(e) {
+ return e === obj;
+ });
+ });
+});
+
+
+describe("Will not report rejections that are handled in time", function() {
+ setupCleanUps();
+
+ specify("Already rejected handled", function testFunction() {
+ var failDeferred = Promise.reject(yesE()).caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+ specify("Immediately rejected handled", function testFunction() {
+ var failDeferred = Promise.defer();
+ failDeferred.promise.caught(noop);
+ failDeferred.reject(yesE());
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+
+ specify("Eventually rejected handled", function testFunction() {
+ var failDeferred = Promise.defer();
+ setTimeout(function() {
+ failDeferred.reject(yesE());
+ }, 1);
+ failDeferred.promise.caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+ specify("Already rejected handled in a deep sequence", function testFunction() {
+ var failDeferred = Promise.reject(yesE());
+
+ failDeferred
+ .then(function(){})
+ .then(function(){}, null, function(){})
+ .then()
+ .then(function(){})
+ .caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+ specify("Immediately rejected handled in a deep sequence", function testFunction() {
+ var failDeferred = Promise.defer();
+
+ failDeferred.promise.then(function(){})
+ .then(function(){}, null, function(){})
+ .then()
+ .then(function(){})
+ .caught(noop);
+
+
+ failDeferred.reject(yesE());
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+
+ specify("Eventually handled in a deep sequence", function testFunction() {
+ var failDeferred = Promise.defer();
+ setTimeout(function() {
+ failDeferred.reject(yesE());
+ }, 1);
+ failDeferred.promise.then(function(){})
+ .then(function(){}, null, function(){})
+ .then()
+ .then(function(){})
+ .caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+
+ specify("Already rejected handled in a middle parallel deep sequence", function testFunction() {
+ var failDeferred = Promise.reject(yesE());
+
+ failDeferred
+ .then(function(){})
+ .then(function(){}, null, function(){})
+ .then()
+ .then(function(){});
+
+
+ failDeferred
+ .then(function(){})
+ .then(function(){}, null, function(){})
+ .then(assert.fail, function(){
+ });
+
+ failDeferred
+ .then(function(){})
+ .then(function(){}, null, function(){})
+ .then()
+ .then(function(){});
+
+ return onUnhandledSucceed(undefined, 2);
+ });
+
+
+ specify("Immediately rejected handled in a middle parallel deep sequence", function testFunction() {
+ var failDeferred = Promise.defer();
+
+ failDeferred.promise
+ .then(function(){})
+ .then(function(){}, null, function(){})
+ .then()
+ .then(function(){});
+
+ failDeferred.promise
+ .then(function(){})
+ .then(function(){}, null, function(){})
+ .then(assert.fail, function(){
+ });
+
+ failDeferred.promise
+ .then(function(){})
+ .then(function(){}, null, function(){})
+ .then()
+ .then(function(){});
+
+ failDeferred.reject(yesE());
+ return onUnhandledSucceed(undefined, 2);
+ });
+
+
+ specify("Eventually handled in a middle parallel deep sequence", function testFunction() {
+ var failDeferred = Promise.defer();
+
+ failDeferred.promise
+ .then(function(){})
+ .then(function(){}, null, function(){})
+ .then()
+ .then(function(){});
+
+ failDeferred.promise
+ .then(function(){})
+ .then(function(){}, null, function(){})
+ .then(assert.fail, function(){
+ });
+
+ failDeferred.promise
+ .then(function(){})
+ .then(function(){}, null, function(){})
+ .then()
+ .then(function(){});
+
+
+ setTimeout(function(){
+ failDeferred.reject(yesE());
+ }, 1);
+ return onUnhandledSucceed(undefined, 2);
+ });
+});
+
+describe("immediate assert.failures without .then", function testFunction() {
+ setupCleanUps();
+ var err = new Error('');
+ specify("Promise.reject", function testFunction() {
+ Promise.reject(err);
+ return onUnhandledSucceed(function(e) {
+ return e === err;
+ });
+ });
+
+ specify("new Promise throw", function testFunction() {
+ new Promise(function() {
+ throw err;
+ });
+ return onUnhandledSucceed(function(e) {
+ return e === err;
+ });
+ });
+
+ specify("new Promise reject", function testFunction() {
+ new Promise(function(_, r) {
+ r(err);
+ });
+ return onUnhandledSucceed(function(e) {
+ return e === err;
+ });
+ });
+
+ specify("Promise.method", function testFunction() {
+ Promise.method(function() {
+ throw err;
+ })();
+ return onUnhandledSucceed(function(e) {
+ return e === err;
+ });
+ });
+
+ specify("Promise.all", function testFunction() {
+ Promise.all([Promise.reject(err)]);
+ return onUnhandledSucceed(function(e) {
+ return e === err;
+ });
+ });
+});
+
+
+describe("immediate assert.failures with .then", function testFunction() {
+ setupCleanUps();
+ var err = new Error('');
+ specify("Promise.reject", function testFunction() {
+ Promise.reject(err).caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+ specify("new Promise throw", function testFunction() {
+ new Promise(function() {
+ throw err;
+ }).caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+ specify("new Promise reject", function testFunction() {
+ new Promise(function(_, r) {
+ r(err);
+ }).caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+ specify("Promise.method", function testFunction() {
+ Promise.method(function() {
+ throw err;
+ })().caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+ specify("Promise.all", function testFunction() {
+ Promise.all([Promise.reject("err")])
+ .caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+ specify("Promise.all many", function testFunction() {
+ Promise.all([Promise.reject("err"), Promise.reject("err2")])
+ .caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+ specify("Promise.all many, latter async", function testFunction() {
+ Promise.all([Promise.reject("err"), Promise.delay(1).thenThrow(new Error())])
+ .caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+ specify("Promise.all many pending", function testFunction() {
+ var a = new Promise(function(v, w){
+ setTimeout(function(){w("err");}, 1);
+ });
+ var b = new Promise(function(v, w){
+ setTimeout(function(){w("err2");}, 1);
+ });
+
+ Promise.all([a, b])
+ .caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+ specify("Already rejected promise for a collection", function testFunction(){
+ Promise.settle(Promise.reject(err))
+ .caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+});
+
+describe("gh-118", function() {
+ setupCleanUps();
+ specify("eventually rejected promise", function testFunction() {
+ Promise.resolve().then(function() {
+ return new Promise(function(_, reject) {
+ setTimeout(function() {
+ reject(13);
+ }, 1);
+ });
+ }).caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+ specify("already rejected promise", function testFunction() {
+ Promise.resolve().then(function() {
+ return Promise.reject(13);
+ }).caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+
+ specify("immediately rejected promise", function testFunction() {
+ Promise.resolve().then(function() {
+ return new Promise(function(_, reject) {
+ reject(13);
+ });
+ }).caught(noop);
+ return onUnhandledFail(isStrictModeSupported ? testFunction : arguments.callee);
+ });
+});
+
+describe("Promise.onUnhandledRejectionHandled", function() {
+ specify("should be called when unhandled promise is later handled", function() {
+ var unhandledPromises = [];
+ var spy1 = testUtils.getSpy();
+ var spy2 = testUtils.getSpy();
+
+ Promise.onPossiblyUnhandledRejection(spy1(function(reason, promise) {
+ unhandledPromises.push({
+ reason: reason,
+ promise: promise
+ });
+ }));
+
+ Promise.onUnhandledRejectionHandled(spy2(function(promise) {
+ assert.equal(unhandledPromises.length, 1);
+ assert(unhandledPromises[0].promise === promise);
+ assert(promise === a);
+ assert(unhandledPromises[0].reason === reason);
+ }));
+
+ var reason = new Error("error");
+ var a = new Promise(function(){
+ throw reason;
+ });
+
+ setTimeout(function() {
+ Promise._unhandledRejectionCheck();
+ a.then(assert.fail, function(){});
+ }, 1);
+
+ return Promise.all([spy1.promise, spy2.promise]);
+ });
+});
+
+describe("global events", function() {
+ var attachGlobalHandler, detachGlobalHandlers;
+ if (typeof process !== "undefined" &&
+ typeof process.version === "string" &&
+ typeof window === "undefined") {
+ attachGlobalHandler = function(name, fn) {
+ process.on(name, fn);
+ };
+ detachGlobalHandlers = function() {
+ process.removeAllListeners("unhandledRejection");
+ process.removeAllListeners("rejectionHandled");
+ };
+ } else {
+ attachGlobalHandler = function(name, fn) {
+ window[("on" + name).toLowerCase()] = fn;
+ };
+ detachGlobalHandlers = function() {
+ window.onunhandledrejection = null;
+ window.onrejectionhandled = null;
+ };
+ }
+ setupCleanUps();
+ beforeEach(detachGlobalHandlers);
+ afterEach(detachGlobalHandlers);
+ specify("are fired", function() {
+ return new Promise(function(resolve, reject) {
+ var err = new Error();
+ var receivedPromise;
+ attachGlobalHandler("unhandledRejection", function(reason, promise) {
+ assert.strictEqual(reason, err);
+ receivedPromise = promise;
+ });
+ attachGlobalHandler("rejectionHandled", function(promise) {
+ assert.strictEqual(receivedPromise, promise);
+ resolve();
+ });
+
+ var promise = new Promise(function() {throw err;});
+ setTimeout(function() {
+ Promise._unhandledRejectionCheck();
+ promise.then(assert.fail, function(){});
+ }, 1);
+ });
+ });
+
+ specify("are fired with local events", function() {
+ return new Promise(function(resolve, reject) {
+ var expectedOrder = [1, 2, 3, 4];
+ var order = [];
+ var err = new Error();
+ var receivedPromises = [];
+
+ Promise.onPossiblyUnhandledRejection(function(reason, promise) {
+ assert.strictEqual(reason, err);
+ receivedPromises.push(promise);
+ order.push(1);
+ });
+
+ Promise.onUnhandledRejectionHandled(function(promise) {
+ assert.strictEqual(receivedPromises[0], promise);
+ order.push(3);
+ });
+
+ attachGlobalHandler("unhandledRejection", function(reason, promise) {
+ assert.strictEqual(reason, err);
+ receivedPromises.push(promise);
+ order.push(2);
+ });
+
+ attachGlobalHandler("rejectionHandled", function(promise) {
+ assert.strictEqual(receivedPromises[1], promise);
+ order.push(4);
+ assert.deepEqual(expectedOrder, order);
+ assert.strictEqual(receivedPromises.length, 2);
+ resolve();
+ });
+
+ var promise = new Promise(function() {throw err;});
+ setTimeout(function() {
+ Promise._unhandledRejectionCheck();
+ promise.then(assert.fail, function(){});
+ }, 1);
+ });
+
+ });
+});
+var windowDomEventSupported = true;
+try {
+ var event = document.createEvent("CustomEvent");
+ event.initCustomEvent("testingtheevent", false, true, {});
+ self.dispatchEvent(event);
+} catch (e) {
+ windowDomEventSupported = false;
+}
+if (windowDomEventSupported) {
+ describe("dom events", function() {
+ var events = [];
+
+ beforeEach(detachEvents);
+ afterEach(detachEvents);
+ function detachEvents() {
+ events.forEach(function(e) {
+ self.removeEventListener(e.type, e.fn, false);
+ });
+ events = [];
+ }
+
+ function attachEvent(type, fn) {
+ events.push({type: type, fn: fn});
+ self.addEventListener(type, fn, false);
+ }
+
+ specify("are fired", function() {
+ return new Promise(function(resolve, reject) {
+ var order = [];
+ var err = new Error();
+ var promise = Promise.reject(err);
+ attachEvent("unhandledrejection", function(e) {
+ e.preventDefault();
+ assert.strictEqual(e.detail.promise, promise);
+ assert.strictEqual(e.detail.reason, err);
+ assert.strictEqual(e.promise, promise);
+ assert.strictEqual(e.reason, err);
+ order.push(1);
+ });
+ attachEvent("unhandledrejection", function(e) {
+ assert.strictEqual(e.detail.promise, promise);
+ assert.strictEqual(e.detail.reason, err);
+ assert.strictEqual(e.promise, promise);
+ assert.strictEqual(e.reason, err);
+ assert.strictEqual(e.defaultPrevented, true);
+ order.push(2);
+ });
+ attachEvent("rejectionhandled", function(e) {
+ e.preventDefault();
+ assert.strictEqual(e.detail.promise, promise);
+ assert.strictEqual(e.detail.reason, undefined);
+ assert.strictEqual(e.promise, promise);
+ assert.strictEqual(e.reason, undefined);
+ order.push(3);
+ });
+ attachEvent("rejectionhandled", function(e) {
+ assert.strictEqual(e.detail.promise, promise);
+ assert.strictEqual(e.detail.reason, undefined);
+ assert.strictEqual(e.promise, promise);
+ assert.strictEqual(e.reason, undefined);
+ assert.strictEqual(e.defaultPrevented, true);
+ order.push(4);
+ resolve();
+ });
+
+ setTimeout(function() {
+ Promise._unhandledRejectionCheck();
+ promise.then(assert.fail, function(r) {
+ order.push(5);
+ assert.strictEqual(r, err);
+ assert.deepEqual(order, [1,2,3,4,5]);
+ });
+ }, 1);
+ });
+
+ })
+ });
+
+ if (typeof Worker !== "undefined") {
+ describe("dom events in a worker", function() {
+ var worker;
+ beforeEach(function () {
+ worker = new Worker("./worker.js");
+ });
+
+ afterEach(function () {
+ worker.terminate();
+ });
+
+ specify("are fired", function() {
+ var order = [];
+ return new Promise(function(resolve, reject) {
+ worker.onmessage = function (message) {
+ try {
+ switch(message.data) {
+ case "unhandledrejection":
+ order.push(1);
+ break;
+ case "rejectionhandled":
+ order.push(2);
+ resolve();
+ break;
+ default:
+ throw new Error("unexpected message: " + message);
+ }
+ }
+ catch (e) {
+ reject(e);
+ }
+ };
+
+ worker.postMessage("reject");
+ }).then(function () {
+ assert.deepEqual(order, [1, 2]);
+ });
+ });
+ });
+ }
+
+}
+
+describe("Unhandled rejection when joining chains with common rejected parent", function testFunction() {
+ specify("GH 645", function() {
+ var aError = new Error('Something went wrong');
+ var a = Promise.try(function(){
+ throw aError;
+ });
+
+ var b = Promise.try(function(){
+ throw new Error('Something went wrong here as well');
+ });
+
+ var c = Promise
+ .join(a, b)
+ .spread(function( a, b ){
+ return a+b;
+ });
+
+ var test1 = Promise
+ .join(a, c)
+ .spread(function( a, product ){
+ // ...
+ })
+ .caught(Error, function(e) {
+ assert.strictEqual(aError, e);
+ });
+
+ var test2 = onUnhandledFail(testFunction);
+
+ return Promise.all([test1, test2]);
+ });
+});
+
+var asyncAwaitSupported = (function() {
+ try {
+ new Function("async function abc() {}");
+ return true;
+ } catch (e) {
+ return false;
+ }
+})();
+
+if (asyncAwaitSupported) {
+ describe("No unhandled rejection from async await", function () {
+ setupCleanUps();
+ specify("gh-1404", function testFunction() {
+ var ret = onUnhandledFail(testFunction);
+ Promise.using(Promise.resolve(),
+ (new Function("Bluebird", "return async function() { await Bluebird.reject(new Error('foo')); }"))(Promise))
+ .caught(function() {});
+ return ret;
+ });
+ });
+}
+
+describe("issues", function () {
+ setupCleanUps();
+
+ specify("GH-1501-1", function testFunction() {
+ var ret = onUnhandledFail(testFunction);
+ Promise.reduce([Promise.resolve("foo"), Promise.reject(new Error("reason"), Promise.resolve("bar"))],
+ function() {},
+ {}).caught(function() {});
+ return ret;
+ });
+
+ specify("GH-1501-2", function testFunction() {
+ var ret = onUnhandledFail(testFunction);
+ Promise.reduce([Promise.delay(1), Promise.reject(new Error("reason"))],
+ function() {},
+ {}).caught(function() {});
+ return ret;
+ });
+
+ specify("GH-1501-3", function testFunction() {
+ var ret = onUnhandledFail(testFunction);
+ Promise.reduce([Promise.reject(new Error("reason"))],
+ function() {},
+ Promise.reject(new Error("reason2"))).caught(function() {});
+ return ret;
+ });
+
+ specify("GH-1487-1", function testFunction() {
+ var ret = onUnhandledFail(testFunction);
+ var p = Promise.reject( new Error('foo') );
+ Promise.map( p, function() {} ).caught( function() {} );
+ return ret;
+ });
+
+ specify("GH-1487-2", function testFunction() {
+ var ret = onUnhandledFail(testFunction);
+ var arr = [ Promise.reject( new Error('foo') ) ];
+ Promise.map( arr, function() {} ).caught( function() {} );
+ return ret;
+ });
+
+ specify("GH-1487-3", function testFunction() {
+ var ret = onUnhandledFail(testFunction);
+ var p = Promise.reject( new Error('foo') );
+ p.map( function() {} ).caught( function() {} );
+ return ret;
+ });
+
+ specify("GH-1487-4", function testFunction() {
+ var ret = onUnhandledFail(testFunction);
+ var arr = [ Promise.reject( new Error('foo') ) ];
+ var p = Promise.resolve( arr );
+ p.map( function() {} ).caught( function() {} );
+ return ret;
+ });
+
+ specify("GH-1487-5", function testFunction() {
+ var ret = onUnhandledFail(testFunction);
+ var p = Promise.reject( new Error('foo') );
+ Promise.filter( p, function() {} ).caught( function() {} );
+ return ret;
+ });
+
+ specify("GH-1487-6", function testFunction() {
+ var ret = onUnhandledFail(testFunction);
+ var arr = [ Promise.reject( new Error('foo') ) ];
+ Promise.filter( arr, function() {} ).caught( function() {} );
+ return ret;
+ });
+
+ specify("GH-1487-7", function testFunction() {
+ var ret = onUnhandledFail(testFunction);
+ var p = Promise.reject( new Error('foo') );
+ p.filter( function() {} ).caught( function() {} );
+ return ret;
+ });
+
+ specify("GH-1487-8", function testFunction() {
+ var ret = onUnhandledFail(testFunction);
+ var arr = [ Promise.reject( new Error('foo') ) ];
+ var p = Promise.resolve( arr );
+ p.filter( function() {} ).caught( function() {} );
+ return ret;
+ });
+})
diff --git a/test/mocha/using.js b/test/mocha/using.js
new file mode 100644
index 0000000..85b428d
--- /dev/null
+++ b/test/mocha/using.js
@@ -0,0 +1,450 @@
+"use strict";
+var assert = require("assert");
+var testUtils = require("./helpers/util.js");
+
+var Promise2 = require("../../js/debug/promise.js")();
+
+var using = Promise.using;
+var error = new Error("");
+var id = 0;
+function Resource() {
+ this.isClosed = false;
+ this.closesCalled = 0;
+ this.commited = false;
+ this.rollbacked = false;
+ this.id = id++;
+
+}
+
+Resource.prototype.commit = function () {
+ if (this.commited || this.rollbacked) {
+ throw new Error("was already commited or rolled back")
+ }
+ this.commited = true;
+};
+
+Resource.prototype.commitAsync = function () {
+ return Promise.delay(1).bind(this).then(this.commit)
+}
+
+Resource.prototype.rollback = function () {
+ if (this.commited || this.rollbacked) {
+ throw new Error("was already commited or rolled back")
+ }
+ this.rollbacked = true;
+};
+
+Resource.prototype.rollbackAsync = function () {
+ return Promise.delay(1).bind(this).then(this.rollback)
+}
+
+Resource.prototype.closeAsync = function() {
+ return Promise.delay(1).bind(this).then(this.close);
+};
+
+Resource.prototype.close = function() {
+ this.closesCalled++;
+ if (this.isClosed) {
+ return;
+ }
+ this.isClosed = true;
+};
+
+Resource.prototype.closeError = function() {
+ throw error;
+};
+
+Resource.prototype.query = function(value) {
+ return Promise.delay(1, value);
+};
+
+function _connect() {
+ return new Promise(function(resolve) {
+ setTimeout(function(){
+ resolve(new Resource())
+ }, 1);
+ });
+}
+
+function _connect2() {
+ return new Promise2(function(resolve) {
+ setTimeout(function(){
+ resolve(new Resource())
+ }, 1);
+ });
+}
+
+function connectCloseAsync(arr, value) {
+ return _connect().disposer(function(resource){
+ return resource.closeAsync().then(function() {
+ arr.push(value);
+ });
+ });
+}
+
+function promiseForConnectCloseAsync(arr, value) {
+ return Promise.delay(1).then(function() {
+ return connectCloseAsync(arr, value);
+ });
+}
+
+function connect() {
+ return _connect().disposer(Resource.prototype.close);
+}
+
+function connect2() {
+ return _connect2().disposer(Resource.prototype.close);
+}
+
+
+function connectError() {
+ return new Promise(function(resolve, reject) {
+ setTimeout(function(){
+ reject(error);
+ }, 1);
+ });
+}
+
+function transactionDisposer(tx, outcome) {
+ outcome.isFulfilled() ? tx.commit() : tx.rollback();
+}
+
+function transactionDisposerAsync(tx, outcome) {
+ return outcome.isFulfilled() ? tx.commitAsync() : tx.rollbackAsync();
+}
+
+function transaction() {
+ return _connect().disposer(transactionDisposer);
+}
+
+function transactionWithImmediatePromiseAfterConnect() {
+ return _connect().then(function (connection) {
+ return Promise.resolve(connection).then(function(c) { return c; })
+ }).disposer(transactionDisposer);
+}
+
+function transactionWithEventualPromiseAfterConnect() {
+ return _connect().then(function (connection) {
+ return Promise.delay(1).thenReturn(connection);
+ }).disposer(transactionDisposer);
+}
+
+function transactionAsync() {
+ return _connect().disposer(transactionDisposerAsync);
+}
+
+describe("Promise.using", function() {
+ specify("simple happy case", function() {
+ var res;
+
+ return using(connect(), function(connection){
+ res = connection;
+ }).then(function() {
+ assert(res.isClosed);
+ assert.equal(res.closesCalled, 1);
+ });
+ });
+
+ specify("simple async happy case", function() {
+ var res;
+ var async = false;
+
+ return using(connect(), function(connection) {
+ res = connection;
+ return Promise.delay(1).then(function() {
+ async = true;
+ });
+ }).then(function() {
+ assert(async);
+ assert(res.isClosed);
+ assert.equal(res.closesCalled, 1);
+ });
+ });
+
+ specify("simple unhappy case", function() {
+ var a = connect();
+ var promise = a._promise;
+ var b = connectError();
+ return using(b, a, function(a, b) {
+ assert(false);
+ }).then(assert.fail, function() {
+ assert(promise.value().isClosed);
+ assert.equal(promise.value().closesCalled, 1);
+ })
+ });
+
+ specify("calls async disposers sequentially", function() {
+ var a = [];
+ var _res3 = connectCloseAsync(a, 3);
+ var _res2 = connectCloseAsync(a, 2);
+ var _res1 = connectCloseAsync(a, 1);
+ return using(_res1, _res2, _res3, function(res1, res2, res3) {
+ _res1 = res1;
+ _res2 = res2;
+ _res3 = res3;
+ }).then(function() {
+ assert.deepEqual(a, [1,2,3]);
+ assert(_res1.isClosed);
+ assert(_res2.isClosed);
+ assert(_res3.isClosed);
+ assert(_res1.closesCalled == 1);
+ assert(_res2.closesCalled == 1);
+ assert(_res3.closesCalled == 1);
+ });
+ });
+
+ specify("calls async disposers sequentially when assert.failing", function() {
+ var a = [];
+ var _res3 = connectCloseAsync(a, 3);
+ var _res2 = connectCloseAsync(a, 2);
+ var _res1 = connectCloseAsync(a, 1);
+ var p1 = _res1.promise();
+ var p2 = _res2.promise();
+ var p3 = _res3.promise();
+ var e = new Error();
+ var promise = Promise.delay(1).thenThrow(e);
+ return using(_res1, _res2, _res3, promise, function() {
+
+ }).then(assert.fail, function(err) {
+ assert.deepEqual(a, [1,2,3]);
+ assert(p1.value().isClosed);
+ assert(p2.value().isClosed);
+ assert(p3.value().isClosed);
+ assert(p1.value().closesCalled == 1);
+ assert(p2.value().closesCalled == 1);
+ assert(p3.value().closesCalled == 1);
+ assert(e === err)
+ });
+ });
+
+ specify("calls promised async disposers sequentially", function() {
+ var a = [];
+ var _res3 = promiseForConnectCloseAsync(a, 3);
+ var _res2 = promiseForConnectCloseAsync(a, 2);
+ var _res1 = promiseForConnectCloseAsync(a, 1);
+ return using(_res1, _res2, _res3, function(res1, res2, res3) {
+ assert(res1 instanceof Resource)
+ assert(res2 instanceof Resource)
+ assert(res3 instanceof Resource)
+ _res1 = res1;
+ _res2 = res2;
+ _res3 = res3;
+ }).then(function() {
+ assert.deepEqual(a, [1,2,3]);
+ assert(_res1.isClosed);
+ assert(_res2.isClosed);
+ assert(_res3.isClosed);
+ assert(_res1.closesCalled == 1);
+ assert(_res2.closesCalled == 1);
+ assert(_res3.closesCalled == 1);
+ });
+ });
+
+ specify("calls promised async disposers sequentially when assert.failing", function() {
+ var a = [];
+ var _res3 = promiseForConnectCloseAsync(a, 3);
+ var _res2 = promiseForConnectCloseAsync(a, 2);
+ var _res1 = promiseForConnectCloseAsync(a, 1);
+ var e = new Error();
+ var promise = Promise.delay(1).thenThrow(e);
+ return using(_res1, _res2, _res3, promise, function() {
+
+ }).then(assert.fail, function(err) {
+ assert.deepEqual(a, [1,2,3]);
+ assert(_res1.value().promise().value().isClosed);
+ assert(_res2.value().promise().value().isClosed);
+ assert(_res3.value().promise().value().isClosed);
+ assert(_res1.value().promise().value().closesCalled == 1);
+ assert(_res2.value().promise().value().closesCalled == 1);
+ assert(_res3.value().promise().value().closesCalled == 1);
+ assert(e === err)
+ });
+ });
+
+ specify("mixed promise, promise-for-disposer and disposer", function() {
+ var a = [];
+ var _res3 = promiseForConnectCloseAsync(a, 3);
+ var _res2 = connectCloseAsync(a, 2);
+ var _res1 = Promise.delay(1, 10);
+ return using(_res1, _res2, _res3, function(res1, res2, res3) {
+ assert(res1 === 10);
+ assert(res2 instanceof Resource);
+ assert(res3 instanceof Resource);
+ _res1 = res1;
+ _res2 = res2;
+ _res3 = res3;
+ }).then(function() {
+ assert.deepEqual(a, [2,3]);
+ assert(_res2.isClosed);
+ assert(_res3.isClosed);
+ assert(_res2.closesCalled == 1);
+ assert(_res3.closesCalled == 1);
+ });
+ });
+
+ specify("successful transaction", function() {
+ var _tx;
+ return using(transaction(), function(tx) {
+ _tx = tx;
+ return tx.query(1).then(function() {
+ return tx.query(2);
+ })
+ }).then(function(){
+ assert(_tx.commited);
+ assert(!_tx.rollbacked);
+ });
+ });
+
+ specify("successful async transaction", function() {
+ var _tx;
+ return using(transactionAsync(), function(tx) {
+ _tx = tx;
+ return tx.query(1).then(function() {
+ return tx.query(3);
+ })
+ }).then(function(){
+ assert(_tx.commited);
+ assert(!_tx.rollbacked);
+ })
+ })
+
+ specify("successful transaction with an immediate promise before disposer creation", function() {
+ var _tx;
+ return using(transactionWithImmediatePromiseAfterConnect(), function(tx) {
+ _tx = tx;
+ return tx.query(1);
+ }).then(function(){
+ assert(_tx.commited);
+ assert(!_tx.rollbacked);
+ });
+ });
+
+ specify("successful transaction with an eventual promise before disposer creation", function() {
+ var _tx;
+ return using(transactionWithEventualPromiseAfterConnect(), function(tx) {
+ _tx = tx;
+ return tx.query(1);
+ }).then(function(){
+ assert(_tx.commited);
+ assert(!_tx.rollbacked);
+ });
+ });
+
+ specify("assert.fail transaction", function() {
+ var _tx;
+ return using(transaction(), function(tx) {
+ _tx = tx;
+ return tx.query(1).then(function() {
+ throw new Error();
+ })
+ }).then(assert.fail, function(){
+ assert(!_tx.commited);
+ assert(_tx.rollbacked);
+ });
+ });
+
+ specify("assert.fail async transaction", function() {
+ var _tx;
+ return using(transactionAsync(), function(tx) {
+ _tx = tx;
+ return tx.query(1).then(function() {
+ throw new Error();
+ })
+ }).then(assert.fail, function(){
+ assert(!_tx.commited);
+ assert(_tx.rollbacked);
+ });
+ });
+
+ specify("with using coming from another Promise instance", function() {
+ var res;
+ return Promise2.using(connect(), function(connection){
+ res = connection;
+ }).then(function() {
+ assert(res.isClosed);
+ assert.equal(res.closesCalled, 1);
+ });
+ });
+
+ specify("with using coming from another Promise instance other way around", function() {
+ var res;
+ return Promise.using(connect2(), function(connection){
+ res = connection;
+ }).then(function() {
+ assert(res.isClosed);
+ assert.equal(res.closesCalled, 1);
+ });
+ });
+
+ if (testUtils.isNodeJS) {
+ specify("disposer throwing should throw in node process", function() {
+ var err = new Error();
+ var disposer = Promise.resolve().disposer(function() {
+ throw err;
+ });
+ using(disposer, function(){});
+ return testUtils.awaitGlobalException(function(e) {
+ assert.strictEqual(e, err);
+ });
+ });
+ }
+
+ specify("Return rejected promise with less than 2 arguments", function() {
+ return Promise.using(1).caught(Promise.TypeError, function(e) {});
+ });
+
+ specify("Throw if disposer is not passed a function", function() {
+ try {
+ Promise.resolve().disposer({});
+ } catch (e) {
+ return Promise.resolve();
+ }
+ assert.fail();
+ });
+
+ specify("Mixed rejected disposers are not called", function() {
+ var err = new Error("rejected should not be called");
+ var a = Promise.delay(1).thenThrow(err).disposer(function() {
+ done(err);
+ });
+ var b = Promise.delay(1).thenReturn(connect());
+ return Promise.using(a, b, function() {
+ done(new Error("should not be here"));
+ }).then(assert.fail, function(e) {
+ assert.strictEqual(b.value()._promise.value().isClosed, true);
+ assert.strictEqual(b.value()._promise.value().closesCalled, 1);
+ });
+ });
+
+ specify("Return rejected promise when last argument is not function", function() {
+ return Promise.using({}, {}, {}, {}).caught(Promise.TypeError, function(e) {});
+ });
+
+
+ specify("Supports an array of 2 items", function() {
+ return Promise.using([Promise.resolve(1), Promise.resolve(2)], function(results) {
+ assert.deepEqual(results, [1,2]);
+ });
+ });
+
+ specify("Supports an empty array", function() {
+ return Promise.using([], function(results) {
+ assert.deepEqual(results, []);
+ });
+ })
+
+ specify("null disposer is called", function() {
+ var calls = 0;
+ var getNull = function() {
+ return Promise.resolve(null).disposer(function() {
+ calls++;
+ });
+ };
+
+ return Promise.using(getNull(), function() {
+ calls++;
+ }).then(function() {
+ assert.equal(2, calls);
+ })
+ })
+})
diff --git a/tests b/tests
new file mode 100755
index 0000000..083e2bc
--- /dev/null
+++ b/tests
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+node tools/test.js "$@"
diff --git a/tools/README.md b/tools/README.md
new file mode 100644
index 0000000..e210e89
--- /dev/null
+++ b/tools/README.md
@@ -0,0 +1,19 @@
+## Tools
+
+Tools that can be run from command line:
+
+### test.js
+
+For running tests. See [Testing](../#testing).
+
+### build.js
+
+For building the library. See [Custom builds](../#custom-builds).
+
+### jshintrc_generator.js
+
+Generates a .jshintrc file in the project root.
+
+Example:
+
+ node tools/jshintrc_generator
diff --git a/tools/ast_passes.js b/tools/ast_passes.js
new file mode 100644
index 0000000..833b7a8
--- /dev/null
+++ b/tools/ast_passes.js
@@ -0,0 +1,746 @@
+//All kinds of conversion passes over the source code
+var jsp = require("acorn");
+var walk = require("acorn-walk");
+var rnonIdentMember = /[.\-_$a-zA-Z0-9]/g;
+var global = new Function("return this")();
+
+function equals( a, b ) {
+ if( a.type === b.type ) {
+ if( a.type === "MemberExpression" ) {
+ return equals( a.object, b.object ) &&
+ equals( a.property, b.property );
+ }
+ else if( a.type === "Identifier" ) {
+ return a.name === b.name;
+ }
+ else if( a.type === "ThisExpression" ) {
+ return true;
+ }
+ else {
+ console.log("equals", a, b);
+ unhandled();
+ }
+ }
+ return false;
+}
+
+function getReceiver( expr ) {
+ if( expr.type === "MemberExpression" ) {
+ return expr.object;
+ }
+ return null;
+}
+
+function nodeToString( expr ) {
+ if( expr == null || typeof expr !== "object" ) {
+ if( expr === void 0 ) {
+ return "void 0";
+ }
+ else if( typeof expr === "string" ) {
+ return '"' + safeToEmbedString(expr) + '"';
+ }
+ return ("" + expr);
+ }
+ if( expr.type === "Identifier" ) {
+ return expr.name;
+ }
+ else if( expr.type === "MemberExpression" ) {
+ if( expr.computed )
+ return nodeToString( expr.object ) + "[" + nodeToString( expr.property ) + "]";
+ else
+ return nodeToString( expr.object ) + "." + nodeToString( expr.property );
+ }
+ else if( expr.type === "UnaryExpression" ) {
+ if( expr.operator === "~" ||
+ expr.operator === "-" ||
+ expr.operator === "+" ) {
+ return expr.operator + nodeToString( expr.argument );
+ }
+ return "(" + expr.operator + " " + nodeToString( expr.argument ) + ")";
+ }
+ else if( expr.type === "Literal" ) {
+ return expr.raw;
+ }
+ else if( expr.type === "BinaryExpression" || expr.type === "LogicalExpression" ) {
+ return "("+nodeToString(expr.left) + " " +
+ expr.operator + " " +
+ nodeToString(expr.right) + ")";
+ }
+ else if( expr.type === "ThisExpression" ) {
+ return "this";
+ }
+ else if( expr.type === "ObjectExpression") {
+ var props = expr.properties;
+ var ret = [];
+ for( var i = 0, len = props.length; i < len; ++i ) {
+ var prop = props[i];
+ ret.push( nodeToString(prop.key) + ": " + nodeToString(prop.value));
+ }
+ return "({"+ret.join(",\n")+"})";
+ }
+ else if( expr.type === "NewExpression" ) {
+ return "new " + nodeToString(expr.callee) + "(" + nodeToString(expr.arguments) +")";
+ }
+ //assuming it is arguments
+ else if( Array.isArray( expr ) ) {
+ var tmp = [];
+ for( var i = 0, len = expr.length; i < len; ++i ) {
+ tmp.push( nodeToString(expr[i]) );
+ }
+ return tmp.join(", ");
+ }
+ else if( expr.type === "FunctionExpression" ) {
+ var params = [];
+ for( var i = 0, len = expr.params.length; i < len; ++i ) {
+ params.push( nodeToString(expr.params[i]) );
+ }
+ }
+ else if( expr.type === "BlockStatement" ) {
+ var tmp = [];
+ for( var i = 0, len = expr.body.length; i < len; ++i ) {
+ tmp.push( nodeToString(expr.body[i]) );
+ }
+ return tmp.join(";\n");
+ }
+ else if( expr.type === "CallExpression" ) {
+ var args = [];
+ for( var i = 0, len = expr.arguments.length; i < len; ++i ) {
+ args.push( nodeToString(expr.arguments[i]) );
+ }
+ return nodeToString( expr.callee ) + "("+args.join(",")+")";
+ }
+ else {
+ console.log( "nodeToString", expr );
+ unhandled()
+ }
+}
+
+function DynamicCall( receiver, fnDereference, arg, start, end ) {
+ this.receiver = receiver;
+ this.fnDereference = fnDereference;
+ this.arg = arg;
+ this.start = start;
+ this.end = end;
+}
+
+DynamicCall.prototype.toString = function() {
+ return nodeToString(this.fnDereference) + ".call(" +
+ nodeToString(this.receiver) + ", " +
+ nodeToString(this.arg) +
+ ")";
+};
+
+function DirectCall( receiver, fnName, arg, start, end ) {
+ this.receiver = receiver;
+ this.fnName = fnName;
+ this.arg = arg;
+ this.start = start;
+ this.end = end;
+}
+DirectCall.prototype.toString = function() {
+ return nodeToString(this.receiver) + "." + nodeToString(this.fnName) +
+ "(" + nodeToString(this.arg) + ")"
+};
+
+
+function ConstantReplacement( value, start, end ) {
+ this.value = value;
+ this.start = start;
+ this.end = end;
+}
+
+ConstantReplacement.prototype.toString = function() {
+ return nodeToString(this.value);
+};
+
+function Empty(start, end) {
+ this.start = start;
+ this.end = end;
+}
+Empty.prototype.toString = function() {
+ return "";
+};
+
+function Assertion( expr, exprStr, start, end ) {
+ this.expr = expr;
+ this.exprStr = exprStr;
+ this.start = start;
+ this.end = end;
+}
+Assertion.prototype.toString = function() {
+ return 'ASSERT('+nodeToString(this.expr)+',\n '+this.exprStr+')';
+};
+
+function BitFieldRead(mask, start, end, fieldExpr) {
+ if (mask === 0) throw new Error("mask cannot be zero");
+ this.mask = mask;
+ this.start = start;
+ this.end = end;
+ this.fieldExpr = fieldExpr;
+}
+
+BitFieldRead.prototype.getShiftCount = function() {
+ var b = 1;
+ var shiftCount = 0;
+ while ((this.mask & b) === 0) {
+ b <<= 1;
+ shiftCount++;
+ }
+ return shiftCount;
+};
+
+BitFieldRead.prototype.toString = function() {
+ var fieldExpr = this.fieldExpr ? nodeToString(this.fieldExpr) : "bitField";
+ var mask = this.mask;
+ var shiftCount = this.getShiftCount();
+ return shiftCount === 0
+ ? "(" + fieldExpr + " & " + mask + ")"
+ : "((" + fieldExpr + " & " + mask + ") >>> " + shiftCount + ")";
+};
+
+function BitFieldCheck(value, inverted, start, end, fieldExpr) {
+ this.value = value;
+ this.inverted = inverted;
+ this.start = start;
+ this.end = end;
+ this.fieldExpr = fieldExpr;
+}
+
+BitFieldCheck.prototype.toString = function() {
+ var fieldExpr = this.fieldExpr ? nodeToString(this.fieldExpr) : "bitField";
+ var equality = this.inverted ? "===" : "!==";
+ return "((" + fieldExpr + " & " + this.value + ") " + equality + " 0)";
+};
+
+function InlineSlice(varExpr, collectionExpression, startExpression, endExpression, start, end, isBrowser,
+ pad) {
+ this.varExpr = varExpr;
+ this.collectionExpression = collectionExpression;
+ this.startExpression = startExpression;
+ this.endExpression = endExpression;
+ this.start = start;
+ this.end = end;
+ this.isBrowser = isBrowser;
+ this.pad = typeof pad === "number" ? pad : 0;
+}
+
+InlineSlice.prototype.hasSimpleStartExpression =
+function InlineSlice$hasSimpleStartExpression() {
+ return this.startExpression.type === "Identifier" ||
+ this.startExpression.type === "Literal";
+};
+
+InlineSlice.prototype.hasSimpleEndExpression =
+function InlineSlice$hasSimpleEndExpression() {
+ return this.endExpression.type === "Identifier" ||
+ this.endExpression.type === "Literal";
+};
+
+InlineSlice.prototype.hasSimpleCollection = function InlineSlice$hasSimpleCollection() {
+ return this.collectionExpression.type === "Identifier";
+};
+
+InlineSlice.prototype.toString = function InlineSlice$toString() {
+ var pad = this.pad;
+
+ var init = this.hasSimpleCollection()
+ ? ""
+ : "var $_collection = " + nodeToString(this.collectionExpression) + ";";
+
+ var collectionExpression = this.hasSimpleCollection()
+ ? nodeToString(this.collectionExpression)
+ : "$_collection";
+
+ init += "var $_len = " + collectionExpression + ".length";
+
+ if (pad !== 0) {
+ init += " + " + Math.abs(pad) + ";";
+ } else {
+ init += ";";
+ }
+
+ var varExpr = nodeToString(this.varExpr);
+
+ //No offset arguments at all
+ if( this.startExpression === firstElement ) {
+ if (this.isBrowser) {
+ if (pad > 0) {
+ return "var " + varExpr + " = (new Array("+pad+")).concat([].slice.call("+collectionExpression+"));";
+ } else if (pad < 0) {
+ return "var " + varExpr + " = ([].slice.call("+collectionExpression+")).concat(new Array("+Math.abs(pad)+"));";
+ } else {
+ return "var " + varExpr + " = [].slice.call("+collectionExpression+");";
+ }
+ } else {
+ var startVal = pad > 0 ? String(pad) : "0";
+ var collectionExpressionVal = pad > 0 ? " - " + pad : "";
+ var lenVal = pad < 0 ? "- " + Math.abs(pad) : "";
+
+ return init + "var " + varExpr + " = new Array($_len); " +
+ "for(var $_i = " + startVal + "; $_i < $_len " + lenVal + "; ++$_i) {" +
+ varExpr + "[$_i] = " + collectionExpression + "[$_i " + collectionExpressionVal + "];" +
+ "}";
+ }
+
+ }
+ else {
+ if( !this.hasSimpleStartExpression() ) {
+ init += "var $_start = " + nodeToString(this.startExpression) + ";";
+ }
+ var startExpression = this.hasSimpleStartExpression()
+ ? nodeToString(this.startExpression)
+ : "$_start";
+
+ //Start offset argument given
+ if( this.endExpression === lastElement ) {
+ if (this.isBrowser) {
+ return "var " + varExpr + " = [].slice.call("+collectionExpression+", "+startExpression+");";
+ } else {
+ return init + "var " + varExpr + " = new Array(Math.max($_len - " +
+ startExpression + ", 0)); " +
+ "for(var $_i = " + startExpression + "; $_i < $_len; ++$_i) {" +
+ varExpr + "[$_i - "+startExpression+"] = " + collectionExpression + "[$_i];" +
+ "}";
+ }
+ }
+ //Start and end offset argument given
+ else {
+
+ if( !this.hasSimpleEndExpression() ) {
+ init += "var $_end = " + nodeToString(this.endExpression) + ";";
+ }
+ var endExpression = this.hasSimpleEndExpression()
+ ? nodeToString(this.endExpression)
+ : "$_end";
+
+ if (this.isBrowser) {
+ return "var " + varExpr + " = [].slice.call("+collectionExpression+", "+startExpression+", "+endExpression+");";
+ } else {
+ return init + "var " + varExpr + " = new Array(Math.max(" + endExpression + " - " +
+ startExpression + ", 0)); " +
+ "for(var $_i = " + startExpression + "; $_i < " + endExpression + "; ++$_i) {" +
+ varExpr + "[$_i - "+startExpression+"] = " + collectionExpression + "[$_i];" +
+ "}";
+ }
+
+ }
+
+ }
+};
+
+var opts = {
+ ecmaVersion: 5,
+ strictSemicolons: false,
+ allowTrailingCommas: true,
+ forbidReserved: false,
+ locations: false,
+ onComment: null,
+ ranges: false,
+ program: null,
+ sourceFile: null
+};
+
+var rlineterm = /[\r\n\u2028\u2029]/;
+var rhorizontalws = /[ \t]/;
+
+var convertSrc = function( src, results ) {
+ if( results.length ) {
+ results.sort(function(a, b){
+ var ret = a.start - b.start;
+ if( ret === 0 ) {
+ ret = a.end - b.end;
+ }
+ return ret;
+ });
+ for( var i = 1; i < results.length; ++i ) {
+ var item = results[i];
+ if( item.start === results[i-1].start &&
+ item.end === results[i-1].end ) {
+ results.splice(i++, 1);
+ }
+ }
+ var ret = "";
+ var start = 0;
+ for( var i = 0, len = results.length; i < len; ++i ) {
+ var item = results[i];
+ ret += src.substring( start, item.start );
+ ret += item.toString();
+ start = item.end;
+ }
+ ret += src.substring( start );
+ return ret;
+ }
+ return src;
+};
+
+var rescape = /[\r\n\u2028\u2029"]/g;
+
+var replacer = function( ch ) {
+ return "\\u" + (("0000") +
+ (ch.charCodeAt(0).toString(16))).slice(-4);
+};
+
+function safeToEmbedString( str ) {
+ return str.replace( rescape, replacer );
+}
+
+function parse( src, opts, fileName) {
+ if( !fileName ) {
+ fileName = opts;
+ opts = void 0;
+ }
+ try {
+ return jsp.parse(src, opts);
+ }
+ catch(e) {
+ e.message = e.message + " " + fileName;
+ e.scriptSrc = src;
+ throw e;
+ }
+}
+
+var inlinedFunctions = Object.create(null);
+
+var lastElement = jsp.parse("___input.length").body[0].expression;
+var firstElement = jsp.parse("0").body[0].expression;
+
+inlinedFunctions.INLINE_SLICE = function( node, isBrowser ) {
+ var statement = node;
+ node = node.expression;
+ var args = node.arguments;
+
+ if( !(2 <= args.length && args.length <= 4 ) ) {
+ throw new Error("INLINE_SLICE must have exactly 2, 3 or 4 arguments");
+ }
+
+ var varExpression = args[0];
+ var collectionExpression = args[1];
+ var startExpression = args.length < 3
+ ? firstElement
+ : args[2];
+ var endExpression = args.length < 4
+ ? lastElement
+ : args[3];
+ return new InlineSlice(varExpression, collectionExpression,
+ startExpression, endExpression, statement.start, statement.end, isBrowser);
+};
+
+inlinedFunctions.INLINE_SLICE_LEFT_PADDED = function( node, isBrowser ) {
+ var statement = node;
+ node = node.expression;
+ var args = node.arguments;
+
+ if(args.length !== 3) {
+ throw new Error("INLINE_SLICE_LEFT_PADDED must have exactly 3 arguments");
+ }
+
+ var padCount = Number(nodeToString(args[0]));
+ var varExpression = args[1];
+ var collectionExpression = args[2];
+ var startExpression = firstElement;
+ var endExpression = lastElement;
+ return new InlineSlice(varExpression, collectionExpression,
+ startExpression, endExpression, statement.start, statement.end, isBrowser, padCount);
+};
+
+inlinedFunctions.BIT_FIELD_READ = function(node) {
+ var statement = node;
+ var args = node.expression.arguments;
+ if (args.length !== 1 && args.length !== 2) {
+ throw new Error("BIT_FIELD must have 1 or 2 arguments");
+ }
+ var arg = args[0];
+ if (arg.type !== "Identifier") {
+ throw new Error("BIT_FIELD argument must be an identifier");
+ }
+ var name = arg.name;
+ var constant = constants[name];
+ if (constant === undefined) {
+ throw new Error(name + " is not a constant");
+ }
+ var value = constant.value;
+ return new BitFieldRead(value, statement.start, statement.end, args[1]);
+};
+inlinedFunctions.BIT_FIELD_CHECK = function(node) {
+ var statement = node;
+ var args = node.expression.arguments;
+ if (args.length !== 1 && args.length !== 2) {
+ throw new Error("BIT_FIELD must have 1 or 2 arguments");
+ }
+ var arg = args[0];
+ if (arg.type !== "Identifier") {
+ throw new Error("BIT_FIELD argument must be an identifier");
+ }
+ var name = arg.name;
+ var constant = constants[name];
+ if (constant === undefined) {
+ throw new Error(name + " is not a constant");
+ }
+ var value = constant.value;
+ var inverted = false;
+ if (name.slice(-4) === "_NEG") {
+ inverted = true;
+ }
+ return new BitFieldCheck(value, inverted, statement.start, statement.end, args[1]);
+};
+inlinedFunctions.USE = function(node) {
+ return new Empty(node.start, node.end);
+};
+
+var constants = {};
+var ignore = [];
+Error.stackTraceLimit = 10000;
+var astPasses = module.exports = {
+
+ inlineExpansion: function( src, fileName, isBrowser ) {
+ var ast = parse(src, fileName);
+ var results = [];
+ var expr = [];
+ function doInline(node) {
+ if( node.expression.type !== 'CallExpression' ) {
+ return;
+ }
+
+ var name = node.expression.callee.name;
+
+ if(typeof inlinedFunctions[ name ] === "function" &&
+ expr.indexOf(node.expression) === -1) {
+ expr.push(node.expression);
+ try {
+ results.push( inlinedFunctions[ name ]( node, isBrowser ) );
+ }
+ catch(e) {
+ e.fileName = fileName;
+ throw e;
+ }
+
+ }
+ }
+ walk.simple(ast, {
+ ExpressionStatement: doInline,
+ CallExpression: function(node) {
+ node.expression = node;
+ doInline(node);
+ }
+ });
+ var ret = convertSrc( src, results );
+ return ret;
+ },
+
+ //Parse constants in from constants.js
+ readConstants: function( src, fileName ) {
+ var ast = parse(src, fileName);
+ walk.simple(ast, {
+ ExpressionStatement: function( node ) {
+ if( node.expression.type !== 'CallExpression' ) {
+ return;
+ }
+
+ var start = node.start;
+ var end = node.end;
+ node = node.expression;
+ var callee = node.callee;
+ if( callee.name === "CONSTANT" &&
+ callee.type === "Identifier" ) {
+
+ if( node.arguments.length !== 2 ) {
+ throw new Error( "Exactly 2 arguments must be passed to CONSTANT\n" +
+ src.substring(start, end)
+ );
+ }
+
+ if( node.arguments[0].type !== "Identifier" ) {
+ throw new Error( "Can only define identifier as a constant\n" +
+ src.substring(start, end)
+ );
+ }
+
+ var args = node.arguments;
+
+ var name = args[0];
+ var nameStr = name.name;
+ var expr = args[1];
+
+ var e = eval;
+ constants[nameStr] = {
+ identifier: name,
+ value: e(nodeToString(expr))
+ };
+ walk.simple( expr, {
+ Identifier: function( node ) {
+ ignore.push(node);
+ }
+ });
+ global[nameStr] = constants[nameStr].value;
+ }
+ }
+ });
+ },
+
+ //Expand constants in normal source files
+ expandConstants: function( src, fileName ) {
+ var results = [];
+ var identifiers = [];
+ var ast = parse(src, fileName);
+ walk.simple(ast, {
+ Identifier: function( node ) {
+ identifiers.push( node );
+ }
+ });
+
+ for( var i = 0, len = identifiers.length; i < len; ++i ) {
+ var id = identifiers[i];
+ if( ignore.indexOf(id) > -1 ) {
+ continue;
+ }
+ var constant = constants[id.name];
+ if( constant === void 0 ) {
+ continue;
+ }
+ if( constant.identifier === id ) {
+ continue;
+ }
+
+ results.push( new ConstantReplacement( constant.value, id.start, id.end ) );
+
+ }
+ return convertSrc( src, results );
+ },
+
+ removeComments: function( src, fileName ) {
+ var results = [];
+ var rnoremove = /^[*\s\/]*(?:@preserve|jshint|global)/;
+ opts.onComment = function( block, text, start, end ) {
+ if( rnoremove.test(text) ) {
+ return;
+ }
+ var e = end + 1;
+ var s = start - 1;
+ while(rhorizontalws.test(src.charAt(s--)));
+ while(rlineterm.test(src.charAt(e++)));
+ results.push( new Empty( s + 2, e - 1 ) );
+ };
+ var ast = parse(src, opts, fileName);
+ return convertSrc( src, results );
+ },
+
+ expandAsserts: function( src, fileName ) {
+ var ast = parse( src, fileName );
+ var results = [];
+ walk.simple(ast, {
+ CallExpression: function( node ) {
+
+ var start = node.start;
+ var end = node.end;
+ var callee = node.callee;
+
+ if( callee.type === "Identifier" &&
+ callee.name === "ASSERT" ) {
+ if( node.arguments.length !== 1 ) {
+ results.push({
+ start: start,
+ end: end,
+ toString: function() {
+ return src.substring(start, end);
+ }
+ });
+ return;
+ }
+
+ var expr = node.arguments[0];
+ var str = src.substring(expr.start, expr.end);
+ str = '"' + safeToEmbedString(str) + '"'
+ var assertion = new Assertion( expr, str, start, end );
+
+ results.push( assertion );
+ }
+ }
+ });
+ return convertSrc( src, results );
+ },
+
+ removeAsserts: function( src, fileName ) {
+ var ast = parse( src, fileName );
+ var results = [];
+ walk.simple(ast, {
+ ExpressionStatement: function( node ) {
+ if( node.expression.type !== 'CallExpression' ) {
+ return;
+ }
+ var start = node.start;
+ var end = node.end;
+ node = node.expression;
+ var callee = node.callee;
+
+ if( callee.type === "Identifier" &&
+ callee.name === "ASSERT" ) {
+ var e = end + 1;
+ var s = start - 1;
+
+ while(rhorizontalws.test(src.charAt(s--)));
+ while(rlineterm.test(src.charAt(e++)));
+ results.push( new Empty( s + 2, e - 1) );
+ }
+ },
+ VariableDeclaration: function(node) {
+ var start = node.start;
+ var end = node.end;
+ if (node.kind === 'var' && node.declarations.length === 1) {
+ var decl = node.declarations[0];
+ if (decl.id.type === "Identifier" &&
+ decl.id.name === "ASSERT") {
+ var e = end + 1;
+ var s = start - 1;
+ while(rhorizontalws.test(src.charAt(s--)));
+ while(rlineterm.test(src.charAt(e++)));
+ results.push( new Empty( s + 2, e - 1) );
+ }
+ }
+ }
+ });
+ return convertSrc( src, results );
+ },
+
+ asyncConvert: function( src, objName, fnProp, fileName ) {
+ var ast = parse( src, fileName );
+
+ var results = [];
+ walk.simple(ast, {
+ CallExpression: function( node ) {
+ var start = node.start;
+ var end = node.end;
+ if( node.callee.type === "MemberExpression" &&
+ node.callee.object.name === objName &&
+ node.callee.property.name === fnProp &&
+ node.arguments.length === 3
+ ) {
+
+ var args = node.arguments;
+ var fnDereference = args[0];
+ var dynamicReceiver = args[1];
+ var arg = args[2];
+
+ var receiver = getReceiver(fnDereference);
+
+ if( receiver == null || !equals(receiver, dynamicReceiver) ) {
+ //Have to use fnDereference.call(dynamicReceiver, arg);
+ results.push(
+ new DynamicCall( dynamicReceiver, fnDereference, arg, start, end )
+ );
+ }
+ else {
+ var fnName = fnDereference.property;
+ results.push(
+ new DirectCall( receiver, fnName, arg, start, end )
+ );
+ //Can use receiver.fnName( arg );
+
+ }
+
+
+ }
+ }
+ });
+ return convertSrc( src, results );
+ }
+};
diff --git a/tools/browser_test_generator.js b/tools/browser_test_generator.js
new file mode 100644
index 0000000..f114c1a
--- /dev/null
+++ b/tools/browser_test_generator.js
@@ -0,0 +1,58 @@
+var Promise = require("bluebird");
+var path = require("path");
+var readFile = Promise.promisify(require("fs").readFile);
+var writeFile = Promise.promisify(require("fs").writeFile);
+var stringToStream = require("./utils.js").stringToStream;
+var baseDir = path.join(__dirname, "..", "test", "browser");
+
+// Sinon pulls the entire NPM as dependencies (including crypto code) for
+// some reason so
+// the browserify bundle ends up taking megabyte and having high risk of
+// IE-incompatible code
+function dependsOnSinon(test) {
+ return /3\.2\.5\.|done|nodeify|2\.2\.6/.test(test.name);
+};
+
+module.exports = function(tests, options) {
+ var testRequires = tests.filter(function(test) {
+ return !dependsOnSinon(test) && test.name.indexOf("generator") === -1;
+ }).map(function(test) {
+ var code = "require('../mocha/" + test.name + "');";
+ if (test.name.indexOf("2.3.3") >= 0) {
+ code = "if (haveGetters) " + code;
+ }
+ return code;
+ }).join("\n");
+
+ var promiseExport = options.cover
+ ? readFile(path.join(baseDir, "promise_instrumented.js"), "utf8")
+ : readFile(path.join(baseDir, "promise_debug.js"), "utf8");
+
+ var main = readFile(path.join(baseDir, "main.js"), "utf8")
+
+ return Promise.join(promiseExport, main, function(promiseExport, main) {
+ var browserify = require("browserify");
+ var contents = promiseExport + "\n" + main + "\n" + testRequires;
+ var complete = browserify({
+ basedir: baseDir,
+ entries: stringToStream(contents)
+ });
+ var worker = browserify({
+ basedir: baseDir,
+ entries: stringToStream(promiseExport),
+ });
+ return Promise.join(
+ Promise.promisify(complete.bundle, complete)().then(function(src) {
+ return writeFile(path.join(baseDir, "bundle.js"), src);
+ }), Promise.promisify(worker.bundle, worker)().then(function (src) {
+ return writeFile(path.join(baseDir, "worker_bundle.js"), src);
+ }));
+ }).then(function() {
+ if (options.executeBrowserTests) {
+ return require("./browser_test_runner.js")(options);
+ }
+ }).catch(function(e) {
+ console.error(e.stack || e.message);
+ process.exit(2);
+ });
+};
diff --git a/tools/browser_test_runner.js b/tools/browser_test_runner.js
new file mode 100644
index 0000000..af751cc
--- /dev/null
+++ b/tools/browser_test_runner.js
@@ -0,0 +1,97 @@
+var path = require("path");
+var build = require("./build.js");
+var Promise = require("bluebird");
+var cp = Promise.promisifyAll(require("child_process"));
+var fs = Promise.promisifyAll(require("fs"));
+var baseDir = path.join(__dirname, "..", "test", "browser");
+var browsers = [
+ ["Windows XP", "internet explorer", "7"],
+ ["Windows XP", "internet explorer", "8"],
+ ["Windows 7", "internet explorer", "9"],
+ ["Windows 7", "internet explorer", "10"],
+ ["Windows 8.1", "internet explorer", "11"],
+ ["Windows 7", "firefox", "3.5"],
+ ["Windows 7", "firefox", "4"],
+ ["Windows 7", "firefox", "25"],
+ ["Windows 7", "firefox", "33"],
+ ["Windows 7", "chrome", "beta"],
+ ["Windows 7", "safari", "5"],
+ ["OS X 10.9", "iphone", "8.1"],
+ ["OS X 10.8", "safari", "6"],
+ ["OS X 10.9", "safari", "7"]
+];
+
+var saucelabsOptions = {
+ urls: ["http://127.0.0.1:9999/index.html"],
+ tunnelTimeout: 30,
+ build: process.env.TRAVIS_JOB_ID,
+ maxPollRetries: 3,
+ throttled: 3,
+ browsers: browsers,
+ testname: "mocha tests",
+ tags: ["master"]
+};
+
+module.exports = function(options) {
+ var Promise = require("bluebird");
+ var ret = Promise.resolve();
+ function createServer() {
+ var http = require("http");
+ var serve = require("serve-static")(baseDir, {'index': ['index.html']});
+ var bodyParser = require("body-parser").urlencoded({
+ limit: "100mb",
+ extended: false
+ });
+ var server = http.createServer(function(req, res) {
+ serve(req, res, function() {
+ if (options.cover &&
+ req.url.indexOf("coverdata") >= 0 &&
+ req.method.toLowerCase() === "post") {
+ bodyParser(req, res, function() {
+ try {
+ var json = JSON.parse(req.body.json);
+ } catch (e) {
+ res.writeHead(404, {'Content-Type': 'text/plain'});
+ res.end('404\n');
+ return;
+ }
+ var browser = (req.body.browser + "").replace(/[^a-zA-Z0-9]/g, "");
+ var fileName = path.join(build.dirs.coverage, "coverage-" + browser + ".json");
+ fs.writeFileAsync(fileName, JSON.stringify(json), "utf8").then(function() {
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.end('Success\n');
+ });
+ });
+ } else {
+ res.writeHead(404, {'Content-Type': 'text/plain'});
+ res.end('404\n');
+ }
+ });
+ });
+ return Promise.promisify(server.listen, server)(options.port)
+ }
+
+ if (options.saucelabs) {
+ var saucelabsRunner = require("./saucelabs_runner.js");
+ ret = createServer().then(function() {
+ return saucelabsRunner(saucelabsOptions);
+ }).then(function() {
+ process.exit(0);
+ });
+ } else if (options.nw) {
+ ret = cp.execAsync((options.nwPath || "nw") + " .", {
+ maxBuffer: 2 * 1024 * 1024,
+ cwd: path.join(process.cwd(), "test/browser")
+ });
+ } else {
+ var open = require("open");
+ ret = createServer().then(function() {
+ var url = "http://localhost:" + options.port;
+ console.log("Test can be run at " + url);
+ if (options.openBrowser && !options.cover) {
+ return Promise.promisify(open)(url);
+ }
+ });
+ }
+ return ret;
+};
diff --git a/tools/build.js b/tools/build.js
new file mode 100644
index 0000000..721f513
--- /dev/null
+++ b/tools/build.js
@@ -0,0 +1,340 @@
+var Promise = require("bluebird");
+var path = require("path");
+var jobRunner = require("./job-runner/job-runner.js");
+Promise.longStackTraces();
+var utils = require("./utils.js");
+var glob = Promise.promisify(require("glob"));
+var fs = Promise.promisifyAll(require("fs"));
+var mkdirp = Promise.promisify(require("mkdirp"));
+var rimraf = Promise.promisify(require("rimraf"));
+
+jobRunner.init(path.join(__dirname, ".."), function() {
+ var fs = Promise.promisifyAll(require("fs"));
+ var utils = require("./tools/utils.js");
+ var path = require("path");
+ var astPasses = require("./tools/ast_passes.js");
+ var Mocha = require("mocha");
+ astPasses.readConstants(
+ fs.readFileSync("./src/constants.js", "utf8"),
+ "constants.js"
+ );
+ function applyOptionalRequires(code, depsRequireCode) {
+ return code.replace( /};([^}]*)$/, depsRequireCode + "\n};$1");
+ }
+});
+
+var optionalModuleRequireMap = {
+ "race.js": true,
+ "call_get.js": true,
+ "generators.js": true,
+ "map.js": true,
+ "nodeify.js": true,
+ "promisify.js": true,
+ "props.js": true,
+ "reduce.js": true,
+ "settle.js": true,
+ "some.js": true,
+ "using.js": true,
+ "timers.js": true,
+ "filter.js": ["map.js"],
+ "any.js": ["some.js"],
+ "each.js": ["reduce.js"]
+};
+
+var lastLineCode = " \n\
+ util.toFastProperties(Promise); \n\
+ util.toFastProperties(Promise.prototype); \n\
+ function fillTypes(value) { \n\
+ var p = new Promise(INTERNAL); \n\
+ p._fulfillmentHandler0 = value; \n\
+ p._rejectionHandler0 = value; \n\
+ p._promise0 = value; \n\
+ p._receiver0 = value; \n\
+ } \n\
+ // Complete slack tracking, opt out of field-type tracking and \n\
+ // stabilize map \n\
+ fillTypes({a: 1}); \n\
+ fillTypes({b: 2}); \n\
+ fillTypes({c: 3}); \n\
+ fillTypes(1); \n\
+ fillTypes(function(){}); \n\
+ fillTypes(undefined); \n\
+ fillTypes(false); \n\
+ fillTypes(new Promise(INTERNAL)); \n\
+ debug.setBounds(Async.firstLineError, util.lastLineError); \n\
+ return Promise; \n\
+";
+
+function getOptionalRequireCode(srcs) {
+ return srcs.sort(function(a, b) {
+ var aHasDeps = Array.isArray(optionalModuleRequireMap[a.sourceFileName]);
+ var bHasDeps = Array.isArray(optionalModuleRequireMap[b.sourceFileName]);
+ return aHasDeps - bHasDeps;
+ }).reduce(function(ret, cur, i) {
+ if(optionalModuleRequireMap[cur.sourceFileName]) {
+ ret += "require('./"+cur.sourceFileName+"')("+ cur.deps.join(", ") +");\n";
+ }
+ return ret;
+ }, "") + lastLineCode;
+}
+
+function getBrowserBuildHeader(sources, npmPackage) {
+ var header = "/**\n * bluebird build version " + npmPackage.version + "\n";
+ var enabledFeatures = ["core"];
+ var disabledFeatures = [];
+ featureLoop: for (var key in optionalModuleRequireMap) {
+ for (var i = 0, len = sources.length; i < len; ++i) {
+ var source = sources[i];
+ if(source.sourceFileName === key) {
+ enabledFeatures.push(key.replace( ".js", ""));
+ continue featureLoop;
+ }
+ }
+ disabledFeatures.push(key.replace(".js", ""));
+ }
+
+ header += (" * Features enabled: " + enabledFeatures.join(", ") + "\n");
+
+ if (disabledFeatures.length) {
+ header += " * Features disabled: " + disabledFeatures.join(", ") + "\n";
+ }
+ header += "*/\n";
+ return header;
+}
+
+function getSourcePaths(features) {
+ return glob("./src/*.js").map(function(v) {
+ return path.basename(v);
+ }).then(function(results) {
+ if (features) features = features.toLowerCase().split(/\s+/g);
+ return results.filter(function(fileName) {
+ if (features && optionalModuleRequireMap[fileName] !== undefined) {
+ for (var i = 0; i < features.length; ++i) {
+ if (fileName.indexOf(features[i]) >= 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return fileName !== "constants.js";
+ });
+ });
+}
+
+function ensureDirectory(dir, isUsed, clean) {
+ return (clean ? rimraf(dir) : Promise.resolve()).then(function() {
+ if (!isUsed) return dir;
+ return mkdirp(dir).thenReturn(dir);
+ });
+}
+
+function buildRelease(sources, depsRequireCode, dir) {
+ return dir.then(function(dir) {
+ return Promise.map(sources, function(source) {
+ return jobRunner.run(function() {
+ var code = source.source;
+ var sourceFileName = source.sourceFileName;
+ code = astPasses.removeAsserts(code, sourceFileName);
+ code = astPasses.inlineExpansion(code, sourceFileName);
+ code = astPasses.expandConstants(code, sourceFileName);
+ code = code.replace( /__DEBUG__/g, "false" );
+ code = code.replace( /__BROWSER__/g, "false" );
+ if (sourceFileName === "promise.js") {
+ code = applyOptionalRequires(code, depsRequireCode);
+ }
+ return fs.writeFileAsync(path.join(root, sourceFileName), code);
+ }, {
+ context: {
+ depsRequireCode: depsRequireCode,
+ source: source,
+ root: dir
+ }
+ });
+ });
+ });
+}
+
+function buildDebug(sources, depsRequireCode, dir) {
+ return dir.then(function(dir) {
+ return Promise.map(sources, function(source) {
+ return jobRunner.run(function() {
+ var code = source.source;
+ var sourceFileName = source.sourceFileName;
+ code = astPasses.expandAsserts(code, sourceFileName);
+ code = astPasses.inlineExpansion(code, sourceFileName);
+ code = astPasses.expandConstants(code, sourceFileName);
+ code = code.replace( /__DEBUG__/g, "true" );
+ code = code.replace( /__BROWSER__/g, "false" );
+ if (sourceFileName === "promise.js") {
+ code = applyOptionalRequires(code, depsRequireCode);
+ }
+ return fs.writeFileAsync(path.join(root, sourceFileName), code);
+ }, {
+ context: {
+ depsRequireCode: depsRequireCode,
+ source: source,
+ root: dir
+ }
+ });
+ });
+ });
+}
+
+function buildBrowser(sources, dir, tmpDir, depsRequireCode, minify, npmPackage, license) {
+ return Promise.join(dir, tmpDir, npmPackage, license, function(dir, tmpDir, npmPackage, license) {
+ return Promise.map(sources, function(source) {
+ return jobRunner.run(function() {
+ var code = source.source;
+ var sourceFileName = source.sourceFileName;
+ code = astPasses.removeAsserts(code, sourceFileName);
+ code = astPasses.inlineExpansion(code, sourceFileName, true);
+ code = astPasses.expandConstants(code, sourceFileName);
+ code = code.replace( /__BROWSER__/g, "true" );
+ if (sourceFileName === "promise.js") {
+ code = applyOptionalRequires(code, depsRequireCode);
+ }
+ return fs.writeFileAsync(path.join(root, sourceFileName), code);
+ }, {
+ context: {
+ depsRequireCode: depsRequireCode,
+ source: source,
+ root: tmpDir
+ }
+ });
+ }).then(function() {
+ var header = getBrowserBuildHeader(sources, npmPackage);
+ return jobRunner.run(function() {
+ var UglifyJS = require("uglify-js");
+ var browserify = require("browserify");
+ var dest = path.join(root, "bluebird.js");
+ var minDest = path.join(root, "bluebird.min.js");
+ var b = browserify({
+ entries: entries,
+ detectGlobals: false,
+ standalone: "Promise"
+ }).exclude('async_hooks').exclude("timers");
+ return Promise.promisify(b.bundle, b)().then(function(src) {
+ var alias = "\
+ ;if (typeof window !== 'undefined' && window !== null) { \
+ window.P = window.Promise; \
+ } else if (typeof self !== 'undefined' && self !== null) { \
+ self.P = self.Promise; \
+ }";
+ src = src + alias;
+ src = src.replace(/\brequire\b/g, "_dereq_");
+ var minWrite, write;
+ if (minify) {
+ var minSrc = src.replace( /__DEBUG__/g, "false" );
+ minSrc = UglifyJS.minify(minSrc, {
+ comments: false,
+ compress: true,
+ fromString: true
+ }).code;
+ minSrc = license + header + minSrc;
+ minWrite = fs.writeFileAsync(minDest, minSrc);
+ }
+ src = src.replace( /__DEBUG__/g, "true" );
+ src = license + header + src;
+ write = fs.writeFileAsync(dest, src);
+
+ return Promise.all([write, minWrite]);
+ })
+ }, {
+ context: {
+ header: header,
+ root: dir,
+ entries: path.join(tmpDir, "bluebird.js"),
+ license: license,
+ minify: minify
+ }
+ })
+ });
+ });
+}
+
+var root = process.cwd();
+// Since rm -rf is called, better be sure...
+if (path.basename(root).toLowerCase().indexOf("bluebird") !== 0) {
+ throw new Error("cwd must be set to bluebird project root. cwd is currently\n\n" +
+ " " + process.cwd() + "\n");
+}
+var dirs = {
+ release: path.join(root, "js", "release"),
+ debug: path.join(root, "js", "debug"),
+ browser: path.join(root, "js", "browser"),
+ browserTmp: path.join(root, "js", "tmp"),
+ instrumented: path.join(root, "js", "instrumented"),
+ coverage: path.join(root, "coverage")
+};
+
+function build(options) {
+ options = Object(options);
+ var clean = (typeof options.clean !== "boolean" ? true : options.clean);
+ var npmPackage = fs.readFileAsync("./package.json").then(JSON.parse);
+ var version = npmPackage.get("version");
+ var sourceFileNames = getSourcePaths(options.features);
+ var license = utils.getLicense();
+ var releaseDir = ensureDirectory(dirs.release, options.release, clean);
+ var debugDir = ensureDirectory(dirs.debug, options.debug, clean);
+ var browserDir = ensureDirectory(dirs.browser, options.browser, clean);
+ var browserTmpDir = ensureDirectory(dirs.browserTmp, options.browser, clean);
+ return Promise.join(license, version, function(license, version) {
+ return sourceFileNames.map(function(sourceFileName) {
+ return jobRunner.run(function() {
+ var sourcePath = path.join("./src", sourceFileName);
+ var source = fs.readFileAsync(sourcePath, "utf8");
+ return source.then(function(source) {
+ utils.checkAscii(sourceFileName, source);
+ var deps = null;
+ if (optionalModuleRequireMap[sourceFileName] !== undefined) {
+ deps = utils.parseDeps(source);
+ }
+ source = astPasses.removeComments(source, sourceFileName);
+ source = source.replace(/__VERSION__/g, version);
+ return {
+ sourceFileName: sourceFileName,
+ source: source,
+ deps: deps
+ };
+ });
+ }, {
+ context: {
+ sourceFileName: sourceFileName,
+ optionalModuleRequireMap: optionalModuleRequireMap,
+ license: license,
+ version: version
+ }
+ });
+ });
+ }).then(function(results) {
+ var depsRequireCode = getOptionalRequireCode(results);
+ var release, debug, browser;
+ if (options.release)
+ release = buildRelease(results, depsRequireCode, releaseDir);
+ if (options.debug)
+ debug = buildDebug(results, depsRequireCode, debugDir);
+ if (options.browser)
+ browser = buildBrowser(results, browserDir, browserTmpDir, depsRequireCode, options.minify, npmPackage, license);
+
+ return Promise.all([release, debug, browser]);
+ });
+}
+
+module.exports = build;
+module.exports.ensureDirectory = ensureDirectory;
+module.exports.dirs = dirs;
+
+
+if (require.main === module) {
+ var argv = require("optimist").argv;
+ var browser = (typeof argv.browser !== "boolean" ? false : argv.browser) || !!argv.features;
+ var clean = (typeof argv.clean !== "boolean" ? true : argv.clean);
+ module.exports({
+ minify: browser && (typeof argv.minify !== "boolean" ? true : argv.minify),
+ browser: browser,
+ debug: !!argv.debug,
+ release: !!argv.release,
+ features: argv.features || null,
+ clean: clean
+ });
+}
diff --git a/tools/job-runner/job-runner.js b/tools/job-runner/job-runner.js
new file mode 100644
index 0000000..c8f30b2
--- /dev/null
+++ b/tools/job-runner/job-runner.js
@@ -0,0 +1,557 @@
+var Promise = require("bluebird");
+var spawn = require("child_process").spawn;
+var assert = require("assert");
+var argv = require("optimist").argv;
+var ARGS = [].concat(
+ process.execArgv,
+ __filename,
+ process.argv.slice(2)
+);
+function sanitizeCpCount(input) {
+ return Math.min(Math.max(1, (+input | 0)), 16);
+}
+
+if (typeof argv["child-processes"] === "number") {
+ var CHILD_PROCESSES = sanitizeCpCount(argv["child-processes"]);
+} else if (process.env.CHILD_PROCESSES) {
+ var CHILD_PROCESSES = sanitizeCpCount(process.env.CHILD_PROCESSES);
+} else {
+ var CHILD_PROCESSES = 4;
+}
+
+
+var debugging = false;
+
+function debug() {
+ if (debugging) {
+ var msg = [].slice.call(arguments).join(" ");
+ console.log(msg);
+ }
+}
+
+function ResultWithOutput(result, stdout, stderr) {
+ this.result = result;
+ this.stdout = stdout;
+ this.stderr = stderr;
+}
+
+var jobRunner = (function() {
+ var taskId = 0;
+ var workerCount;
+ var workers = [];
+ var tasks = [];
+ var killed = true;
+
+ function leastTotalRunningTime(a, b) {
+ return a.totalRunningTime() - b.totalRunningTime();
+ }
+
+ function each(fn) {
+ if (typeof fn === "string") {
+ var args = [].slice.call(arguments, 1);
+ return workers.forEach(function(worker) {
+ worker[fn].apply(worker, args);
+ });
+ }
+ return workers.forEach(fn);
+ }
+
+ function retLogger(result) {
+ if (result instanceof ResultWithOutput) {
+ process.stdout.write(result.stdout);
+ process.stderr.write(result.stderr);
+ }
+ return result;
+ }
+
+ function throwLogger(result) {
+ if (result && (result.stderr || result.stdout)) {
+ process.stdout.write(result.stdout);
+ process.stderr.write(result.stderr);
+ }
+ throw result;
+ }
+
+ function reinit() {
+ each("init");
+ }
+
+ function checkShutDown(secondCheck) {
+ if (tasks.length > 0) return;
+ var anyWorkerHasTasks = workers.some(function(w) {
+ return w.hasTasks();
+ });
+ if (anyWorkerHasTasks) return;
+ if (secondCheck) return ret.exit();
+ setTimeout(function() {
+ checkShutDown(true);
+ }, 10);
+ }
+
+ function schedule(task, queued) {
+ var worker = workers.filter(function(worker) {
+ return task.isolated ?
+ !worker.hasTasks() : !worker._runningIsolatedTask;
+ }).sort(leastTotalRunningTime)[0];
+
+ if (!worker) {
+ if (!queued) tasks.push(task);
+ return false;
+ } else {
+ assert(task.isolated ? !worker.hasTasks() : true);
+ debug("found free worker", worker._id, "for task", task.id);
+ worker.performTask(task);
+ return true;
+ }
+ }
+
+ var ret = {
+ init: function(requirePath, initTaskFn) {
+ if (workers.length) return;
+ if (typeof requirePath !== "string") throw new TypeError();
+ var count = CHILD_PROCESSES;
+ workerCount = count;
+ var id = 0;
+ for (var i = 0; i < count; ++i) {
+ workers.push(new Worker(id++, requirePath, initTaskFn));
+ }
+ process.on("exit", ret.exit);
+ },
+
+ exit: function() {
+ if (killed) return;
+ killed = true;
+ each("kill");
+ },
+
+ run: function(task, opts) {
+ if (!workerCount) throw new Error("task runner has not been initialized");
+ if (typeof task !== "function") throw new TypeError("fn not function");
+ if (killed) {
+ killed = false;
+ reinit();
+ }
+ opts = opts || {};
+ var context = opts.context || {};
+ var log = opts.log === false ? false : true;
+ var estimate = typeof opts.estimate === "number" ? opts.estimate : null;
+ var progress = typeof opts.progress === "function" ? opts.progress : null;
+ var isolated = !!opts.isolated;
+ var resolve, reject;
+ var promise = new Promise(function() {
+ resolve = arguments[0];
+ reject = arguments[1];
+ });
+ task = {
+ isolated: isolated,
+ task: {
+ code: task + "",
+ context: context
+ },
+ resolve: resolve,
+ reject: reject,
+ estimate: estimate,
+ id: taskId++,
+ log: log,
+ progress: progress
+ };
+ schedule(task, false);
+ if (log) promise = promise.then(retLogger, throwLogger);
+ return promise;
+ },
+
+ setVerbose: function(v) {
+ debugging = !!v;
+ },
+
+ _workerIdleNotification: function() {
+ var _t = tasks;
+ if (_t.length === 0) {
+ return checkShutDown();
+ }
+ while(_t.length > 0) {
+ var task = _t.shift();
+ if (!schedule(task, true)) {
+ _t.unshift(task);
+ return;
+ }
+ }
+ }
+ };
+ return ret;
+})();
+
+function Worker(id, requirePath, initTaskFn) {
+ this._initTaskFn = initTaskFn;
+ this._requirePath = requirePath;
+ this._id = id;
+ this._runningTaskCount = 0;
+ this._runningIsolatedTask = false;
+ this._performingIsolatedTask = false;
+ this._queuedIsolatedTask = null;
+ this._bufferingStdio = false;
+ this._runningTime = 0;
+ this._c = null;
+ this._stdout = "";
+ this._stderr = "";
+ this._tasks = {};
+ this._onStdOut = bind(this._onStdOut, this);
+ this._onStdErr = bind(this._onStdErr, this);
+ this._onError = bind(this._onError, this);
+ this._onMessage = bind(this._onMessage, this);
+}
+
+Worker.prototype.totalRunningTime = function() {
+ var ret = this._runningTime;
+ var ids = Object.keys(this._tasks);
+ var now = Date.now();
+ for (var i = 0; i < ids.length; ++i) {
+ var task = this._tasks[ids[i]];
+ ret += task.estimate === null ? (now - task.started + 1): task.estimate;
+ }
+ return ret;
+};
+
+Worker.prototype._onStdOut = function(data) {
+ data = data.toString();
+ if (this._bufferingStdio) {
+ this._stdout += data;
+ } else {
+ process.stdout.write(data);
+ }
+};
+
+Worker.prototype._onStdErr = function(data) {
+ data = data.toString();
+
+ if (this._bufferingStdio) {
+ this._stderr += data;
+ } else {
+ process.stderr.write(data);
+ }
+};
+
+Worker.prototype._onError = function(e) {
+ process.stderr.write(e && e.stack && e.stack + "" || (e + ""));
+};
+
+Worker.prototype._onMessage = function(payload) {
+ var self = this;
+ setImmediate(function() {
+ self[payload.type].call(self, payload);
+ });
+};
+
+Worker.prototype.removeListeners = function() {
+ var c = this._c;
+ c.stdout.removeListener("data", this._onStdOut);
+ c.stderr.removeListener("data", this._onStdErr);
+ c.removeListener("message", this._onMessage);
+ c.removeListener("error", this._onError);
+};
+
+Worker.prototype.debug = function(msg, task) {
+ debug("worker", this._id, msg, (task.isolated ?
+ "isolated" : ""), "task", task.id);
+};
+
+Worker.prototype.hasTasks = function() {
+ return this.runningTaskCount() > 0 ||
+ this._queuedIsolatedTask ||
+ this._runningIsolatedTask;
+};
+
+Worker.prototype.runningTaskCount = function() {
+ return this._runningTaskCount;
+};
+
+Worker.prototype.performTask = function(task) {
+ if (task !== this._queuedIsolatedTask) {
+ assert(!this._runningIsolatedTask);
+ if (task.isolated) {
+ this._runningIsolatedTask = true;
+ if (this.runningTaskCount() > 0) {
+ this.debug("queued", task);
+ this._queuedIsolatedTask = task;
+ return;
+ } else {
+ assert(this._queuedIsolatedTask === null);
+ this._performingIsolatedTask = true;
+ }
+ }
+ } else {
+ assert(this.runningTaskCount() === 0);
+ assert(this._runningIsolatedTask);
+ this._queuedIsolatedTask = null;
+ this._performingIsolatedTask = true;
+ }
+ this._runningTaskCount++;
+ assert(this._performingIsolatedTask ? this._runningTaskCount === 1 : true);
+ this._tasks[task.id] = task;
+ task.started = Date.now();
+ this.debug("starts to perform", task);
+ this._c.send({
+ type: "newTask",
+ id: task.id,
+ task: task.task,
+ isolated: task.isolated,
+ log: task.log,
+ progress: !!task.progress
+ });
+
+};
+
+function getFunctionSource(fn) {
+ return (fn + "")
+ .replace(/^\s*function\s*\(\s*\)\s*{/, "")
+ .replace(/}\s*$/, "");
+}
+
+Worker.prototype.init = function() {
+ assert(Array.isArray(ARGS));
+ assert(!this._c);
+ var env = {};
+ Object.getOwnPropertyNames(process.env).forEach(function(key) {
+ env[key] = process.env[key];
+ });
+ env.requirePath = this._requirePath;
+ if (typeof this._initTaskFn === "function") {
+ env.initialCode = getFunctionSource(this._initTaskFn);
+ }
+
+ var c = spawn(process.execPath, ARGS, {
+ env: env,
+ stdio: ["pipe", "pipe", "pipe", "ipc"],
+ cwd: this._requirePath
+ });
+ assert(typeof c.send === "function");
+ this._c = c;
+ c.stdout.on("data", this._onStdOut);
+ c.stderr.on("data", this._onStdErr);
+ c.on("error", this._onError);
+ c.on("message", this._onMessage);
+};
+
+Worker.prototype.taskComplete = function(payload) {
+ var task = this._tasks[payload.id];
+ this.debug("completed", task);
+ delete this._tasks[payload.id];
+ this._runningTaskCount--;
+ var resolve, result;
+ if (payload.isError) {
+ resolve = task.reject;
+ var err = payload.error;
+ if (err.__isErrorInstance__) {
+ result = new Error();
+ Object.keys(err).forEach(function(key) {
+ if (key === "__isErrorInstance__") return;;
+ result[key] = err[key];
+ });
+ result.name = err.name;
+ result.stack = err.stack;
+ result.message = err.message;
+ } else {
+ result = err;
+ }
+ } else {
+ resolve = task.resolve;
+ result = payload.result;
+ }
+ if (this._runningIsolatedTask) {
+ if (this._queuedIsolatedTask) {
+ if (this.runningTaskCount() === 0) {
+ this.performTask(this._queuedIsolatedTask);
+ }
+ } else {
+ if (payload.error) {
+ result.stdout = this._stdout;
+ result.stderr = this._stderr;
+ } else {
+ result = new ResultWithOutput(result, this._stdout, this._stderr);
+ }
+ this._stderr = this._stdout = "";
+ this._performingIsolatedTask = false;
+ this._runningIsolatedTask = false;
+ this._bufferingStdio = false;
+ this.kill();
+ this.init();
+ }
+ }
+ resolve(result);
+ if (!this._runningIsolatedTask) {
+ jobRunner._workerIdleNotification();
+ }
+};
+
+Worker.prototype.kill = function() {
+ if (this._c) {
+ this.removeListeners();
+ this._c.kill("SIGKILL");
+ this._c = null;
+ }
+};
+
+Worker.prototype.progress = function(payload) {
+ var id = payload.id;
+ var task = this._tasks[id];
+ if (task && typeof task.progress === "function") {
+ task.progress.call(undefined, payload.value);
+ }
+};
+
+Worker.prototype.outputFlushed = function() {
+ this._bufferingStdio = true;
+ this._c.send({type: "outputFlushedAck"});
+};
+
+
+
+function bind(fn, ctx) {return function() {return fn.apply(ctx, arguments); };}
+
+
+function getTaskFunction(context, code) {
+ with (context) {
+ return eval( "(" + code + ")");
+ }
+}
+
+if (require.main === module) {
+ var __requirePath = process.env.requirePath;
+ var __oldreq = require;
+ var __path = require("path");
+ require = function(p) {
+ if (p.charAt(0) === ".") {
+ p = __path.join(__requirePath, p);
+ }
+ return __oldreq(p);
+ };
+ if (process.env.initialCode) {
+ eval(process.env.initialCode);
+ }
+ (function() {
+ function waitForOutput() {
+ return new Promise(function(resolve, reject) {
+ var flushCount = 0;
+ function onFlushed() {
+ flushCount++;
+ if (flushCount === 2) {
+ resolve();
+ }
+ }
+ function checkStream(stream) {
+ if (stream.bufferSize === 0) {
+ onFlushed();
+ } else {
+ stream.write("", "utf-8", onFlushed);
+ }
+ }
+ checkStream(process.stdout);
+ checkStream(process.stderr);
+ });
+ }
+
+ function waitForPreviousOutput(id) {
+ return waitForOutput().then(function() {
+ var ret = waitForFlushAck(id);
+ process.send({type: "outputFlushed", id: id});
+ return ret;
+ });
+ }
+
+ var ackWaitResolve = null;
+ function waitForFlushAck(id) {
+ assert(ackWaitResolve === null);
+ return new Promise(function(resolve) {
+ ackWaitResolve = resolve;
+ });
+ }
+ var noop = function() {};
+ var noopWrite = function(_, a, b) {
+ if (typeof a === "function") return a();
+ if (typeof b === "function") return b();
+ };
+
+ function toSerializableError(err) {
+ if (err instanceof Error) {
+ var ret = Object.create(null);
+ Object.keys(err).forEach(function(key){
+ ret[key] = err[key];
+ });
+ ret.name = err.name;
+ ret.stack = err.stack;
+ ret.message = err.message;
+ ret.__isErrorInstance__ = true;
+ return ret;
+ } else {
+ return err;
+ }
+ }
+
+ var actions = {
+ newTask: function(payload) {
+ var task = payload.task;
+ var code = task.code;
+ var context = task.context;
+ var id = payload.id;
+ var promise = payload.isolated
+ ? waitForPreviousOutput(id) : Promise.resolve();
+
+
+ return promise
+ .then(function() {
+ if (payload.log === false && payload.isolated) {
+ process.stdout.write = noopWrite;
+ }
+ var fn = getTaskFunction(context, code);
+ if (typeof fn !== "function")
+ throw new Error("fn must be function");
+ return fn(payload.progress ? function(value) {
+ process.send({type: "progress", id: id, value: value});
+ } : noop);
+ })
+ .finally(function() {
+ if (payload.isolated) {
+ return waitForOutput();
+ }
+ })
+ .then(function(result) {
+ process.send({
+ type: "taskComplete",
+ id: payload.id,
+ result: result
+ });
+ })
+ .catch(function(error) {
+ process.send({
+ type: "taskComplete",
+ id: payload.id,
+ error: toSerializableError(error),
+ isError: true
+ });
+ });
+ },
+
+ outputFlushedAck: function() {
+ var resolve = ackWaitResolve;
+ ackWaitResolve = null;
+ resolve();
+ },
+
+ addGlobals: function(payload) {
+ new Function(payload.code)();
+ }
+ };
+
+ process.on("message", function(payload) {
+ setImmediate(function() {
+ actions[payload.type].call(actions, payload);
+ });
+ });
+ })();
+} else {
+ module.exports = jobRunner;
+ Object.defineProperty(module.exports, "CHILD_PROCESSES", {
+ value: CHILD_PROCESSES,
+ writable: false
+ });
+}
diff --git a/tools/jshint.js b/tools/jshint.js
new file mode 100644
index 0000000..e5af351
--- /dev/null
+++ b/tools/jshint.js
@@ -0,0 +1,20 @@
+var utils = require("./utils.js");
+var path = require("path");
+
+module.exports = function() {
+ var wd = path.join(__dirname, "..");
+ return utils.run("node_modules/jshint/bin/jshint", [
+ "--verbose",
+ "--reporter",
+ "node_modules/jshint-stylish/stylish.js",
+ "src/"
+ ], wd);
+};
+function log(value) {
+ process.stdout.write(value.stdout);
+ process.stderr.write(value.stderr);
+}
+if (require.main === module) {
+ module.exports().then(log, log);
+}
+
diff --git a/tools/jshintrc_generator.js b/tools/jshintrc_generator.js
new file mode 100644
index 0000000..a24a7c3
--- /dev/null
+++ b/tools/jshintrc_generator.js
@@ -0,0 +1,53 @@
+var assert = require("assert");
+assert.equal(require.main, module);
+// Since many globals are dynamic, this file is needed to generate jshintrc dynamically
+var Promise = require("bluebird");
+var path = require("path");
+Promise.longStackTraces();
+var fs = Promise.promisifyAll(require("fs"));
+
+var constantsFile = path.join(__dirname, "..", "src", "constants.js");
+var globals = fs.readFileAsync(constantsFile, "utf8").then(function(contents) {
+ var rconstantname = /CONSTANT\(\s*([^,]+)/g;
+ var m;
+ var globals = {
+ Symbol: false,
+ Map: false,
+ JSON: false,
+ Error: true,
+ args: true,
+ chrome: true,
+ INLINE_SLICE: false,
+ INLINE_SLICE_LEFT_PADDED: false,
+ BIT_FIELD_CHECK: false,
+ BIT_FIELD_READ: false,
+ USE: false,
+ global: true,
+ setImmediate: true,
+ Promise: true,
+ WebKitMutationObserver: true,
+ TypeError: true,
+ RangeError: true,
+ __DEBUG__: false,
+ __BROWSER__: false,
+ process: true,
+ self: true,
+ "console": false,
+ "require": false,
+ "module": false,
+ "define": false
+ };
+ while((m = rconstantname.exec(contents))) {
+ globals[m[1]] = false;
+ }
+ return globals;
+});
+
+var jshintrcFile = path.join(__dirname, "..", ".jshintrc");
+var jshintrc = fs.readFileAsync(jshintrcFile, "utf8").then(JSON.parse);
+
+Promise.join(jshintrc, globals, function(jshintrc, globals) {
+ jshintrc.globals = globals;
+ var json = JSON.stringify(jshintrc, null, " ");
+ return fs.writeFileAsync(jshintrcFile, json);
+});
diff --git a/tools/mocha_runner.js b/tools/mocha_runner.js
new file mode 100644
index 0000000..96ce3ca
--- /dev/null
+++ b/tools/mocha_runner.js
@@ -0,0 +1,143 @@
+module.exports = function mochaRun(progress) {
+ var currentTime = 0;
+ var timers = {};
+ var currentId = 0;
+
+ function checkTimers() {
+ var keys = Object.keys(timers);
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ var timer = timers[key];
+ if (!timer) continue;
+ if (currentTime >= (timer.started + timer.time)) {
+ if (timer.interval) {
+ timer.started = currentTime;
+ } else {
+ delete timers[key];
+ }
+ var fn = timer.fn;
+ if (timer.domain) timer.domain.enter();
+ fn();
+ if (timer.domain) timer.domain.exit();
+ }
+ }
+ }
+
+ function setInterval(fn, time) {
+ var id = currentId++;
+ time = (+time || 0) | 0;
+ if (time < 0) time = 0;
+ timers[id] = {
+ fn: fn,
+ time: time,
+ started: currentTime,
+ interval: true,
+ domain: process.domain
+ };
+ return id;
+ }
+
+ function setTimeout(fn, time) {
+ var id = currentId++;
+ time = (+time || 0) | 0;
+ if (time < 11) time = 11;
+ timers[id] = {
+ fn: fn,
+ time: time,
+ started: currentTime,
+ interval: false,
+ domain: process.domain
+ };
+ return id;
+ }
+
+ function clearTimeout(id) {
+ delete timers[id];
+ }
+
+ var clearInterval = clearTimeout;
+ if (fakeTimers) {
+ (function timerLoop() {
+ currentTime += 10;
+ try {
+ checkTimers();
+ } finally {
+ setImmediate(timerLoop);
+ }
+ })();
+
+ global.oldSetTimeout = global.setTimeout;
+ global.oldClearTimeout = global.clearTimeout;
+ global.setTimeout = setTimeout;
+ global.clearTimeout = clearTimeout;
+ global.setInterval = setInterval;
+ global.clearInterval = clearInterval;
+ }
+ var failures = [];
+ delete Error.__BluebirdErrorTypes__;
+ global.adapter = cover
+ ? require("./js/instrumented/bluebird.js")
+ : require("./js/debug/bluebird.js");
+ global.Promise = adapter;
+ Promise = adapter;
+ adapter.defer = adapter.pending = function() {
+ var ret = {};
+ ret.promise = new Promise(function(resolve, reject) {
+ ret.resolve = ret.fulfill = resolve;
+ ret.reject = reject;
+ });
+ return ret;
+ };
+ Promise.config({cancellation: true});
+ Promise.config({longStackTraces: false});
+ Promise.config({longStackTraces: true});
+ return Promise.each(testGroup, function(test, index, length) {
+ var mocha = new Mocha({
+ reporter: "spec",
+ timeout: "300s",
+ enableTimeouts: true,
+ slow: Infinity,
+ bail: true
+ });
+ mocha.addFile(test.path);
+ return new Promise(function(resolve, reject) {
+ mocha.run(function(failures) {
+ if (failures === 0) {
+ test.failed = false;
+ progress(test);
+ }
+ resolve();
+ }).on("fail", function(_, err) {
+ test.failed = true;
+ progress(test);
+ failures.push({
+ name: test.name,
+ error: err
+ });
+ });
+ });
+ }).then(function() {
+ function failAdvice(failedTestFileName) {
+ return "For additional details you can run it individually " +
+ " with the command `node tools/test --run=" + failedTestFileName + "`";
+ }
+ if (failures.length > 0) {
+ var error;
+ if (singleTest) {
+ error = failures[0].error;
+ }
+ else {
+ var message = "\u001b[31mSome tests failed: \u001b[m\n"
+ failures.forEach(function(failResult) {
+ message += " " + failResult.name + " " + failAdvice(failResult.name) + "\n";
+ });
+ error = new Error(message);
+ error.noStackPrint = true;
+ }
+ throw error;
+ }
+ if (cover) {
+ return __coverage__;
+ }
+ });
+};
diff --git a/tools/saucelabs_runner.js b/tools/saucelabs_runner.js
new file mode 100644
index 0000000..c6da6b8
--- /dev/null
+++ b/tools/saucelabs_runner.js
@@ -0,0 +1,198 @@
+/*
+
+Ripped from grunt-saucelabs node module and made independent on grunt
+
+MIT license:
+
+Copyright (c) 2012 Parashuram
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+module.exports = function(options) {
+ var Promise = require("bluebird");
+ var SauceTunnel = require('grunt-saucelabs/node_modules/sauce-tunnel/index.js');
+ var TestRunner = require('grunt-saucelabs/src/TestRunner.js');
+
+ function reportProgress(notification) {
+ switch (notification.type) {
+ case 'tunnelOpen':
+ console.log('=> Starting Tunnel to Sauce Labs');
+ break;
+ case 'tunnelOpened':
+ console.log('Connected to Saucelabs');
+ break;
+ case 'tunnelClose':
+ console.log('=> Stopping Tunnel to Sauce Labs');
+ break;
+ case 'tunnelEvent':
+ console.log(notification.text);
+ break;
+ case 'jobStarted':
+ console.log('\n', notification.startedJobs, '/', notification.numberOfJobs, 'tests started');
+ break;
+ case 'jobCompleted':
+ console.log('\nTested %s', notification.url);
+ console.log('Platform: %s', notification.platform);
+
+ if (notification.tunnelId && unsupportedPort(notification.url)) {
+ console.log('Warning: This url might use a port that is not proxied by Sauce Connect.');
+ }
+
+ console.log('Passed: %s', notification.passed);
+ console.log('Url %s', notification.jobUrl);
+ break;
+ case 'testCompleted':
+ console.log('All tests completed with status %s', notification.passed);
+ break;
+ case 'retrying':
+ console.log('Timed out, retrying');
+ break;
+ default:
+ console.error('Unexpected notification type');
+ }
+ }
+
+ function createTunnel(arg) {
+ var tunnel;
+
+ reportProgress({
+ type: 'tunnelOpen'
+ });
+
+ tunnel = new SauceTunnel(arg.username, arg.key, arg.identifier, true, ['-P', '0'].concat(arg.tunnelArgs));
+
+ ['write', 'writeln', 'error', 'ok', 'debug'].forEach(function (method) {
+ tunnel.on('log:' + method, function (text) {
+ reportProgress({
+ type: 'tunnelEvent',
+ verbose: false,
+ method: method,
+ text: text
+ });
+ });
+ tunnel.on('verbose:' + method, function (text) {
+ reportProgress({
+ type: 'tunnelEvent',
+ verbose: true,
+ method: method,
+ text: text
+ });
+ });
+ });
+
+ return tunnel;
+ }
+
+ function runTask(arg, framework) {
+ var tunnel;
+
+ return Promise
+ .try(function () {
+ var deferred;
+
+ if (arg.tunneled) {
+ deferred = Promise.defer();
+
+ tunnel = createTunnel(arg);
+ tunnel.start(function (succeeded) {
+ if (!succeeded) {
+ deferred.reject('Could not create tunnel to Sauce Labs');
+ } else {
+ reportProgress({
+ type: 'tunnelOpened'
+ });
+
+ deferred.resolve();
+ }
+ });
+ return deferred.promise;
+ }
+ })
+ .then(function () {
+ var testRunner = new TestRunner(arg, framework, reportProgress);
+ return testRunner.runTests();
+ })
+ .finally(function () {
+ var deferred;
+
+ if (tunnel) {
+ deferred = Promise.defer();
+
+ reportProgress({
+ type: 'tunnelClose'
+ });
+
+ tunnel.stop(function () {
+ deferred.resolve();
+ });
+
+ return deferred.promise;
+ }
+ });
+ }
+
+ function unsupportedPort(url) {
+ // Not all ports are proxied by Sauce Connect. List of supported ports is
+ // available at https://saucelabs.com/docs/connect#localhost
+ var portRegExp = /:(\d+)\//;
+ var matches = portRegExp.exec(url);
+ var port = matches ? parseInt(matches[1], 10) : null;
+ var supportedPorts = [
+ 80, 443, 888, 2000, 2001, 2020, 2109, 2222, 2310, 3000, 3001, 3030,
+ 3210, 3333, 4000, 4001, 4040, 4321, 4502, 4503, 4567, 5000, 5001, 5050, 5555, 5432, 6000,
+ 6001, 6060, 6666, 6543, 7000, 7070, 7774, 7777, 8000, 8001, 8003, 8031, 8080, 8081, 8765,
+ 8888, 9000, 9001, 9080, 9090, 9876, 9877, 9999, 49221, 55001
+ ];
+
+ if (port) {
+ return supportedPorts.indexOf(port) === -1;
+ }
+
+ return false;
+ }
+
+ var defaults = {
+ username: process.env.SAUCE_USERNAME,
+ key: process.env.SAUCE_ACCESS_KEY,
+ tunneled: true,
+ identifier: Math.floor((new Date()).getTime() / 1000 - 1230768000).toString(),
+ pollInterval: 1000 * 2,
+ maxPollRetries: 0,
+ testname: '',
+ browsers: [{}],
+ tunnelArgs: [],
+ sauceConfig: {},
+ maxRetries: 0
+ };
+
+ var opts = {};
+ options = options || {};
+ Object.keys(defaults).forEach(function(key) {
+ opts[key] = defaults[key];
+ });
+ Object.keys(options).forEach(function(key) {
+ opts[key] = options[key];
+ });
+
+ return runTask(opts, "mocha");
+};
diff --git a/tools/test.js b/tools/test.js
new file mode 100644
index 0000000..27f5355
--- /dev/null
+++ b/tools/test.js
@@ -0,0 +1,315 @@
+process.env.BLUEBIRD_WARNINGS = 0;
+var assert = require("assert");
+assert.equal(require.main, module);
+var Promise = require("bluebird");
+var build = require("./build.js");
+var utils = require("./utils.js");
+var tableLogger = utils.tableLogger;
+var argv = require("optimist").argv;
+var glob = Promise.promisify(require("glob"));
+var path = require("path");
+var mkdirp = Promise.promisify(require("mkdirp"));
+var rimraf = Promise.promisify(require("rimraf"));
+var jobRunner = require("./job-runner/job-runner.js");
+var mochaRunner = require("./mocha_runner.js");
+var fs = Promise.promisifyAll(require("fs"));
+var testUtils = require("../test/mocha/helpers/util");
+jobRunner.setVerbose(0);
+// Random slowness after tests complete
+function getTests(options) {
+ var g;
+
+ if (options.testName === "all" || options.testName.indexOf("no") === 0) {
+ g = "./test/mocha/*.js";
+ } else if (options.testName === "aplus") {
+ g = "./test/mocha/[0-9].[0-9].[0-9].js";
+ } else if (options.testName.indexOf("*") >= 0) {
+ g = "./test/mocha/" + options.testName;
+ } else {
+ var testName = options.testName.replace(/^(\d)(\d)(\d)$/, "$1.$2.$3");
+ g = "./test/mocha/" + testName + ".js";
+ }
+
+ var excludes = [];
+ if (options.testName.indexOf("no") === 0) {
+ excludes = options.testName.slice(2).split(",");
+ }
+
+ return glob(g).then(function(matches) {
+ return matches.filter(function(match) {
+ if (match.indexOf("generator") >= 0) {
+ return options.generators;
+ }
+
+ for (var i = 0; i < excludes.length; ++i) {
+ if (match.indexOf(excludes[i]) >= 0) {
+ return false;
+ }
+ }
+
+ return true;
+ })
+ }).tap(function(m) {
+ if (m.length === 0) {
+ throw new Error("No test file matches: '" + options.testName + "'");
+ }
+ }).map(function(filePath, i) {
+ var name = path.basename(filePath);
+ return {
+ name: name,
+ path: filePath,
+ index: i,
+ nameMatcher: "\\b" +
+ name.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&") +
+ "\\b"
+ };
+ });
+}
+
+function getColorForCoverage(coveragePct) {
+ var colorThresholds = {
+ 95: "brightgreen",
+ 85: "green",
+ 80: "yellowgreen",
+ 70: "yellow",
+ 60: "red"
+ };
+ var values = Object.keys(colorThresholds).map(Number).sort(function(a, b) {
+ return b - a;
+ });
+ for (var i = 0; i < values.length; ++i) {
+ if (coveragePct >= values[i]) return colorThresholds[values[i].toString()];
+ }
+ return colorThresholds[values[values.length - 1].toString()];
+}
+
+function getCoverage() {
+ return utils.run("npm", ["run", "istanbul", "--", "report", "text-summary"]).then(function(result) {
+ var stdout = result.stdout;
+ var pctPattern = /(\d+\.\d+)%/g;
+ var matches = stdout.match(pctPattern);
+ var sum = matches.map(function(pct) {
+ return parseFloat(pct.replace(/[^0-9.]/g, ""))
+ }).reduce(function(a, b) {
+ return a + b;
+ }, 0);
+ var average = Math.round(sum / matches.length);
+ return average;
+ });
+}
+
+function generateCoverageBadge(coverage) {
+ var text = "coverage-" + coverage + "%";
+ var color = getColorForCoverage(coverage);
+ var imgSrc = "http://img.shields.io/badge/" + text + "-" + color + ".svg?style=flat";
+ var link = "http://petkaantonov.github.io/bluebird/coverage/debug/index.html";
+ var markdown = "[!["+text+"]("+imgSrc+")]("+link+")";
+ return markdown;
+}
+
+function writeCoverageFile(coverage, groupNumber) {
+ var dir = build.dirs.coverage;
+ var fileName = path.join(dir, "coverage-group" + groupNumber + ".json");
+ var json = JSON.stringify(coverage);
+ return fs.writeFileAsync(fileName, json, "utf8").thenReturn(fileName);
+}
+
+function needsFreshProcess(testName) {
+ return /regress|using|domain|multiple-copies|unhandled_rejections|nodeify|getNewLibraryCopy|long_stack_traces/.test(testName) ||
+ testUtils.isOldNode && /api_exceptions|promisify/.test(testName);
+}
+
+function runTestGroup(testGroup, options, progress) {
+ return jobRunner.run(mochaRunner, {
+ isolated: !options.singleTest,
+ log: options.singleTest,
+ progress: progress,
+ context: {
+ testGroup: testGroup,
+ singleTest: options.singleTest,
+ fakeTimers: options.fakeTimers,
+ cover: options.cover
+ }
+ });
+}
+
+function combineTests(tests) {
+ var arrays = new Array(jobRunner.CHILD_PROCESSES);
+ for (var i = 0; i < arrays.length; ++i) {
+ arrays[i] = [];
+ }
+
+ var initialLength = arrays.length;
+ for (var i = 0; i < tests.length; ++i) {
+ var test = tests[i];
+ if (needsFreshProcess(test.name)) {
+ arrays.push([test]);
+ } else {
+ arrays[i % initialLength].push(tests[i]);
+ }
+
+ }
+ return arrays;
+}
+
+var testName = "all";
+if ("run" in argv) {
+ testName = (argv.run + "");
+ if (testName.indexOf("*") === -1) {
+ testName = testName.toLowerCase()
+ .replace( /\.js$/, "" )
+ .replace( /[^,a-zA-Z0-9_\-.]/g, "" );
+ }
+}
+
+var options = {
+ generators: (function() {
+ try {
+ new Function("(function*(){})");
+ return true;
+ } catch (e) {
+ return false;
+ }
+ })() || !!argv.nw,
+ cover: !!argv["cover"],
+ testName: testName,
+ singleTest: false,
+ saucelabs: !!argv.saucelabs,
+ testBrowser: !!argv.saucelabs || !!argv.browser || !!argv.nw,
+ executeBrowserTests: !!argv.saucelabs || !!argv.nw || (typeof argv["execute-browser-tests"] === "boolean" ?
+ argv["execute-browser-tests"] : !!argv.browser),
+ openBrowser: typeof argv["open-browser"] === "boolean" ? argv["open-browser"] : true,
+ port: argv.port || 9999,
+ fakeTimers: typeof argv["fake-timers"] === "boolean"
+ ? argv["fake-timers"] : true,
+ jsHint: typeof argv["js-hint"] === "boolean" ? argv["js-hint"] : true,
+ nw: !!argv.nw,
+ nwPath: argv["nw-path"]
+};
+
+
+
+if (options.cover && typeof argv["cover"] === "string") {
+ options.coverFormat = argv["cover"];
+} else {
+ options.coverFormat = "html";
+}
+
+var jsHint = options.jsHint ? require("./jshint.js")() : Promise.resolve();
+var tests = getTests(options);
+var buildOpts = {
+ debug: true
+};
+if (options.testBrowser) {
+ buildOpts.browser = true;
+ buildOpts.minify = true;
+}
+var buildResult = build(buildOpts);
+if (options.cover) {
+ var exclusions = ["assert.js", "captured_trace.js"];
+ var coverageInstrumentedRoot = build.ensureDirectory(build.dirs.instrumented,options.cover, true);
+ var coverageReportsRoot = mkdirp(build.dirs.coverage, true).then(function() {
+ return fs.readdirAsync(build.dirs.coverage);
+ }).map(function(fileName) {
+ var filePath = path.join(build.dirs.coverage, fileName);
+ if (path.extname(fileName).indexOf("json") === -1) {
+ return rimraf(filePath);
+ }
+ });
+ buildResult = Promise.join(coverageInstrumentedRoot, buildResult, coverageReportsRoot, function() {
+ return utils.run("npm", ["-v"]).then(function(result) {
+ var version = result.stdout.split(".").map(Number);
+ if (version[0] < 2) {
+ throw new Error("Npm version 2.x.x required, current version is " + result.stdout);
+ }
+ });
+ }).tap(function() {
+ var copyExclusions = Promise.map(exclusions, function(exclusion) {
+ var fromPath = path.join(build.dirs.debug, exclusion);
+ var toPath = path.join(build.dirs.instrumented, exclusion);
+ return fs.readFileAsync(fromPath, "utf8").then(function(contents) {
+ return fs.writeFileAsync(toPath, contents, "utf8");
+ });
+ });
+ var args = [
+ "run",
+ "istanbul",
+ "--",
+ "instrument",
+ "--output",
+ build.dirs.instrumented,
+ "--no-compact",
+ "--preserve-comments",
+ "--embed-source"
+ ];
+ exclusions.forEach(function(x) {
+ args.push("-x", x);
+ });
+ args.push(build.dirs.debug);
+ var istanbul = utils.run("npm", args, null, true);
+ return Promise.all([istanbul, copyExclusions]);
+ });
+}
+
+var testResults = Promise.join(tests, buildResult, function(tests) {
+ var singleTest = tests.length === 1;
+ options.singleTest = singleTest;
+ process.stdout.write("\u001b[m");
+ if (options.testBrowser) {
+ return require("./browser_test_generator.js")(tests, options);
+ } else if (singleTest) {
+ return runTestGroup(tests, options);
+ } else {
+ utils.cursorTo(0, 0);
+ utils.clearScreenDown();
+ tableLogger.addTests(tests);
+ return Promise.map(combineTests(tests), function(testGroup, index) {
+ return runTestGroup(testGroup, options, function(test) {
+ if (test.failed) {
+ tableLogger.testFail(test);
+ } else {
+ tableLogger.testSuccess(test);
+ }
+ }).then(function(maybeCoverage) {
+ if (options.cover) {
+ return writeCoverageFile(maybeCoverage.result, index + 1);
+ }
+ })
+ }).then(function() {
+ var p = Promise.resolve();
+ if (options.cover) {
+ var coverage = getCoverage();
+ if (process.execPath.indexOf("iojs") >= 0 && testName === "all") {
+ p = p.return(coverage).then(generateCoverageBadge).then(console.log);
+ }
+ p = p.then(function() {
+ return utils.run("npm", ["run", "istanbul", "--", "report", options.coverFormat], null, true);
+ }).return(coverage).then(function(coverage) {
+ console.log("Total coverage " + coverage + "%");
+ });
+ }
+ console.log("All tests passed");
+ return p;
+ });
+ }
+});
+
+Promise.all([testResults, jsHint]).spread(function(_, jsHintResponse) {
+ if (jsHintResponse) {
+ console.log("JSHint:");
+ console.log(jsHintResponse.stdout);
+ console.log(jsHintResponse.stderr);
+ }
+}).catch(function(e) {
+ if (e && e.stdout || e.stderr) {
+ console.log(e.stdout);
+ console.error(e.stderr);
+ }
+
+ if (!e || !e.stack) {
+ console.error(e + "");
+ } else {
+ console.error(e.noStackPrint ? e.message : e.stack);
+ }
+ process.exit(2);
+});
diff --git a/tools/utils.js b/tools/utils.js
new file mode 100755
index 0000000..d082a4f
--- /dev/null
+++ b/tools/utils.js
@@ -0,0 +1,216 @@
+var Promise = require("bluebird");
+var assert = require("assert");
+var path = require("path");
+var spawn = require("cross-spawn");
+Promise.longStackTraces();
+var fs = Promise.promisifyAll(require("fs"));
+var notAscii = /[^\u000D\u0019-\u007E]/;
+var Table = require('cli-table');
+
+function noStackError(message) {
+ var e = new Error(message);
+ e.noStackPrint = true;
+ return e;
+}
+
+function checkAscii(fileName, contents) {
+ if (notAscii.test(contents)) {
+ contents.split("\n").forEach(function(line, i) {
+ if (notAscii.test(line)) {
+ var lineNo = i + 1;
+ var col = line.indexOf(RegExp.lastMatch) + 1;
+ var code = "U+" + (("0000" + line.charCodeAt(col-1)
+ .toString(16)).slice(-4));
+ code = RegExp.lastMatch + " (" + code.toUpperCase() + ")";
+ var fullPath = path.join(process.cwd(), "src", fileName);
+ throw noStackError("The file " + fullPath + "\ncontains an illegal character: " +
+ code + " on line " + lineNo + " at column " + col);
+ }
+ });
+ }
+}
+
+var license;
+function getLicense() {
+ if (license) return license;
+ var licenseFile = path.join(__dirname, "..", "LICENSE");
+ return license = fs.readFileAsync(licenseFile, "utf8").then(function(text) {
+ return "/* @preserve\n" + text.split("\n").map(function(line) {
+ return " * " + line;
+ }).join("\n") + "\n */\n";
+ });
+}
+
+function cursorTo(x, y) {
+ if (process.stdout.cursorTo)
+ process.stdout.cursorTo(x, y);
+}
+
+function clearScreenDown() {
+ if (process.stdout.clearScreenDown)
+ process.stdout.clearScreenDown();
+}
+
+function run(cmd, args, dir, log) {
+ return new Promise(function(resolve, reject) {
+ function makeResult(errorMessage) {
+ var ret = errorMessage ? new Error(errorMessage) : {};
+ ret.stdout = out.trim();
+ ret.stderr = err.trim();
+ return ret;
+ }
+
+ var out = "";
+ var err = "";
+ var c = spawn(cmd, args, {stdin: ["ignore", "ignore", "ignore"], cwd: dir || process.cwd()});
+
+ c.stdout.on("data", function(data) {
+ if (log) process.stdout.write(data.toString());
+ out += data;
+ });
+ c.stderr.on("data", function(data) {
+ if (log) process.stderr.write(data.toString());
+ err += data;
+ });
+
+ c.on("error", function(err) {
+ reject(makeResult(err.message));
+ });
+ c.on("close", function(code) {
+ if (code == 0) resolve(makeResult())
+ else reject(makeResult(path.basename(cmd) + " exited with code: " + code + "\n" + err.trim()));
+ })
+ });
+}
+
+function parseDeps(src) {
+ var rdeps = /function\s*\(\s*([^)]+)\)/;
+ var match = rdeps.exec(src);
+ assert.equal(match.length, 2);
+ var deps = match[1].split(/\s*,\s*/g).map(function(val) {
+ return val.trim();
+ });
+ return deps;
+}
+
+var tableLogger = (function() {
+ var metaKeyCodeReAnywhere = /(?:\x1b)([a-zA-Z0-9])/;
+ var functionKeyCodeReAnywhere = new RegExp('(?:\x1b+)(O|N|\\[|\\[\\[)(?:' + [
+ '(\\d+)(?:;(\\d+))?([~^$])',
+ '(?:M([@ #!a`])(.)(.))', // mouse
+ '(?:1;)?(\\d+)?([a-zA-Z])'
+ ].join('|') + ')');
+
+ function stripVTControlCharacters(str) {
+ str = str.replace(new RegExp(functionKeyCodeReAnywhere.source, 'g'), '');
+ return str.replace(new RegExp(metaKeyCodeReAnywhere.source, 'g'), '');
+ }
+
+ var ROWS = 35;
+ var log = new Array(ROWS);
+ for (var i = 0; i < ROWS; ++i) log[i] = [];
+ var tableOpts = {
+ chars: {
+ 'mid': '',
+ 'left-mid': '',
+ 'mid-mid': '',
+ 'right-mid': ''
+ },
+ style: {
+ 'padding-left': 0,
+ 'padding-right': 0,
+ compact: true
+ }
+ };
+ var table;
+ var split;
+
+ function showTable() {
+ assert(!table);
+ table = new Table(tableOpts);
+ table.push.apply(table, log);
+ table = table.toString();
+ split = table.split("\n").map(function(line) {
+ return stripVTControlCharacters(line);
+ });
+ cursorTo(0, 0);
+ process.stdout.write(table);
+ cursorTo(0, split.length + 1);
+ }
+
+ function addTests(tests) {
+ var cols = 0;
+ tests.forEach(function(test) {
+ var index = test.index;
+ var row = index % ROWS;
+ var column = (index / ROWS) | 0;
+ cols = Math.max(column, cols);
+ log[row][column] = "\u001b[m" + test.name + " \u001b[31m\u00D7 ";
+ });
+ cols = cols + 1;
+ for (var i = 0; i < log.length; ++i) {
+ var row = log[i];
+ for (var j = 0; j < cols; ++j) {
+ if (!row[j]) {
+ row[j] = " ";
+ }
+ }
+ }
+ showTable();
+ }
+
+ function getPosition(test) {
+ for (var y = 0; y < split.length; ++y) {
+ var s = split[y];
+ var x = s.search(new RegExp(test.nameMatcher));
+ if (x >= 0) {
+ return {
+ x: x + test.name.length,
+ y: y
+ };
+ }
+ }
+ assert(false);
+ }
+
+ function update(test, message) {
+ var pos = getPosition(test);
+ cursorTo(pos.x + 1, pos.y);
+ process.stdout.write(message);
+ cursorTo(0, split.length + 2);
+ }
+
+ function testFail(test) {
+ update(test, "\u001b[31m\u00D7 FAILURE\u001b[39m");
+ }
+
+
+ function testSuccess(test) {
+ update(test, "\u001b[32m\u221A\u001b[39m")
+ }
+
+ return {
+ addTests: addTests,
+ testFail: testFail,
+ testSuccess: testSuccess
+ }
+})();
+
+function stringToStream(str) {
+ var Readable = require('stream').Readable;
+ var readable = new Readable()
+ readable.push(str + "");
+ readable.push(null);
+ return readable;
+}
+
+module.exports = {
+ checkAscii: checkAscii,
+ getLicense: getLicense,
+ run: run,
+ parseDeps: parseDeps,
+ tableLogger: tableLogger,
+ stringToStream: stringToStream,
+ cursorTo: cursorTo,
+ clearScreenDown: clearScreenDown
+};