├── README.md ├── TODO.md ├── meta └── specification.md ├── modules ├── discussions.md └── specification.md ├── packages ├── TODO.md ├── notes.md └── specification.md ├── promises ├── TODO.md └── specification.md ├── template └── TODO.md └── tests ├── specification.md ├── test.js └── tutorial.md /README.md: -------------------------------------------------------------------------------- 1 | UncommonJS 2 | ========== 3 | 4 | Decentralized JavaScript conventions and API design. An 5 | opinionated fork of CommonJS. Fork me to show support, fix, 6 | and extend. 7 | 8 | 9 | Contributing 10 | ============ 11 | 12 | Specifications are developed in separate directories and 13 | eponymous branches. These are merged one-way into a master 14 | branch to provide the whole picture. I encourage 15 | participants to create their own master branch that only 16 | merges the specifications that they are tracking and 17 | approve. 18 | 19 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | 2 | To Do 3 | ===== 4 | 5 | 1. tests (the unit test specification) 6 | 1. promises 7 | 1. QQ 8 | 1. add promises to modules-async 9 | 1. bytes, buffers, blobs 10 | 1. packages 11 | 1. registry/catalog 12 | 1. fs 13 | 1. io 14 | 1. http 15 | 1. JSGI 16 | 17 | 18 | Done 19 | ==== 20 | 21 | 1. modules 22 | 1. define 23 | 1. async 24 | 1. specifications (the meta-specification specification) 25 | 1. promises 26 | 1. thenables 27 | 1. promiseSendables 28 | 1. Q 29 | 1. packages 30 | 1. ``package.json`` 31 | 1. mappings 32 | 1. dependencies 33 | 34 | -------------------------------------------------------------------------------- /meta/specification.md: -------------------------------------------------------------------------------- 1 | 2 | 1. Specifications must be in Markdown format. 3 | 1. Specifications must be physically wrapped at 72 columns. 4 | 1. Specifications must use visually equivalent spaces instead of tabs. 5 | 1. Specifications must align tab stops every 4 spaces. 6 | 1. Specifications must use an outline of numbered sentences so that 7 | they can be easily referenced. 8 | 1. Within Markdown format, all numbered points must be number 1 9 | such that they are easy to edit, diff, and patch. 10 | 1. The markdown ``1.`` must start on and be followed by spaces 11 | until the next tab stop. 12 | 1. Non-normative text (*statements that cannot be tested*) must be 13 | marked down with emphasis or in non-numbered paragraphs following 14 | the specification. 15 | 1. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 16 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in 17 | this document are to be interpreted as described in RFC 2119. 18 | 19 | In Vim, use ``:set tw=72`` or ``textwidth=72`` to configure automatic 20 | physical wrapping at the 72nd column. Select a region and type `gq` to 21 | reflow a paragraph or bullet point. Use ``ts=4`` and ``sw=4`` 22 | (``tabstop=4`` and ``shiftwidth=4``) to align text at tab stops. 23 | 24 | -------------------------------------------------------------------------------- /modules/discussions.md: -------------------------------------------------------------------------------- 1 | 2 | * [http://groups.google.com/group/commonjs/browse_thread/thread/6ad5c2c3b005cb3b/5a0f17e43d347673](RFC require.paths behaviour) 3 | * [http://groups.google.com/group/commonjs/browse_thread/thread/c3682135d72b1f8](Module meta data Proposal) (resurrection of this discussion) 4 | * [http://groups.google.com/group/commonjs/browse_thread/thread/6e6eeb9b3d2990f1](require.main === undefined Options) 5 | * [http://groups.google.com/group/commonjs/browse_thread/thread/0f9e8fc585211f6a](Modules 1.1 Comments) 6 | * [http://groups.google.com/group/commonjs/browse_thread/thread/2f6c87b65e30fb71](Modules/1.1.1 Show of Hands) 7 | 8 | -------------------------------------------------------------------------------- /modules/specification.md: -------------------------------------------------------------------------------- 1 | This specification establishes a convention for creating a style of 2 | reusable JavaScript modules and systems that will load and link those 3 | modules. 4 | 5 | * Modules are singletons. 6 | * Modules have an implicitly isolated lexical scope. 7 | * Loading may be eager in asynchronous loaders. 8 | * Execution must be lazy. 9 | * With some care, modules may have cyclic dependencies. 10 | * Systems of modules may be isolated but still share the same context. 11 | * Modules may be used both in browsers and servers. 12 | 13 | 14 | Guarantees Made by Module Writers 15 | ================================= 16 | 17 | No requirement in this section implies that a module system must enforce 18 | that requirement. These are merely requirements that a module must 19 | satisfy in order to function in any module system. 20 | 21 | 1. A module must be encoded in UTF-8. 22 | 1. A module must only assume that its scope contains ``require``, 23 | ``exports``, and ``module`` in addition to the globals provided by 24 | JavaScript. 25 | 26 | 1. *Depending on any other values limits portability to engines 27 | that do not provide those values and will cause reference errors 28 | on those systems.* 29 | 30 | 1. A module must consider that every object in its scope may be 31 | immutable. 32 | 33 | 1. *Depending on the mutability of an object precludes the use of 34 | the module in a system that shares its global scope among 35 | mutually suspcious programs, like between mashups in a secured 36 | JavaScript context.* 37 | 38 | 1. *It may be acceptable to upgrade an object in scope (shim) if it 39 | does not provide the needed, specified behavior, but this will 40 | limit portability to secure contexts with legacy 41 | implementations.* 42 | 43 | 1. A module must only depend on the specified behavior of values in 44 | scope. 45 | 46 | 1. *Depending on any extension to the specified behavior limits 47 | portability.* 48 | 49 | 1. All calls to ``require`` must be given a single string literal as 50 | the argument, or the value of ``require.main`` if it is defined. 51 | 52 | 1. *If the argument to ``require`` is not a string, the dependency 53 | cannot be discovered and asynchronously loaded before executing 54 | the module. This would limit portability to systems that read 55 | the text of a module to discover dependencies and load them 56 | asynchronously before execution, which is necessary for 57 | portability to browsers and other systems that only support 58 | asynchronous loading.* 59 | 60 | 1. *``require.main`` is guaranteed to already have been loaded, so 61 | it is a reasonable exception to the rule that all dependencies 62 | must be discoverable without executing the module.* 63 | 64 | 1. All calls to ``require`` must be by the name ``require``. 65 | 66 | 1. *If ``require`` takes on a different name, the dependency will 67 | not be discoverable without executing the module.* 68 | 69 | 1. All calls to ``require`` must be given a module identifier as the 70 | argument. 71 | 72 | 1. *Some module loaders may accept values that are not module 73 | identifiers. Depending on this behavior inside a module limits 74 | the portability of the module, particularly to systems that use 75 | packages to isolate the module identifier name space for a set 76 | of modules.* 77 | 78 | 79 | Guarantees Made by Module Interpreters 80 | ====================================== 81 | 82 | 1. The top scope of a module must not be shared with any other module. 83 | 84 | 1. *All ``var`` declarations in a module will only be accessible 85 | within that module and will not collide with declarations in 86 | other modules* 87 | 88 | 1. A ``require`` function must exist in a function's lexical scope. 89 | 90 | 1. The ``require`` function must accept a module identifier as its 91 | first and only argument. 92 | 93 | 1. ``require`` must return the same value as ``module.exports`` in 94 | the identified module, or must throw an exception while trying. 95 | 96 | 1. ``require`` may have a ``main`` property. 97 | 98 | 1. The ``require.main`` property may be read-only and 99 | non-configurable. 100 | 101 | 1. The ``require.main`` property must be the same value as 102 | ``module`` in the lexical scope of the first module that 103 | began executing in the current system of modules. 104 | 105 | 1. The ``require.main`` property must be the same value in 106 | every module. 107 | 108 | 1. ``require`` must have a ``resolve`` function. 109 | 110 | 1. ``resolve`` accepts a module identifier as its first and only 111 | argument 112 | 113 | 1. ``resolve`` must return the resolved module identifier 114 | corresponding to the given module identifier relative to 115 | this module's resolved module identifier. 116 | 117 | 1. ``require`` must have an ``async`` function. 118 | 119 | 1. ``async`` must accept an identifier or an array of 120 | identifiers as its first argument. 121 | 122 | 1. A single identifier is equivalent to an array with a 123 | single identifier. 124 | 125 | 1. ``async`` accepts an optional ``callback`` function as its 126 | second argument. 127 | 128 | 1. ``async`` accepts an optional ``errback`` function as its 129 | third argument. 130 | 131 | 1. ``async`` must call ``require`` for each module identifier 132 | in order 133 | 134 | 1. ``async`` may wait for each module's transitively required 135 | modules to asynchronously load before calling ``require``. 136 | 137 | 1. If any ``require`` call throws an exception, or if any 138 | module cannot be loaded before it is required, ``errback`` 139 | must be called with the ``Error`` as its argument. 140 | 141 | 1. If every ``require`` call returns, ``async`` must call 142 | ``callback`` with the respective exports for each module 143 | identifier as its arguments. 144 | 145 | 1. An ``exports`` object must exist in a function's lexical scope. 146 | 147 | 1. the ``exports`` object must initially be the same value as 148 | ``module.exports``. 149 | 150 | 1. A ``module`` object must exist in a function's lexical scope. 151 | 152 | 1. The ``module`` object must have an ``id`` property. 153 | 154 | 1. The ``module.id`` property must be a module identifier such 155 | that ``require(module.id)`` must return the 156 | ``module.exports`` value when called in this or any module 157 | in the same system of modules. 158 | 159 | 1. The ``module.id`` property may be read-only and 160 | non-configurable. 161 | 162 | 1. The ``module`` object must have an ``exports`` property. 163 | 164 | 1. The ``module.exports`` property must initially be an empty 165 | object. 166 | 167 | 1. The ``module.exports`` property must be writable and 168 | configurable. 169 | 170 | 1. The ``module`` object may have a ``location`` absolute URL. 171 | 172 | 1. The ``module`` object may have a ``directory`` absolute URL. 173 | 174 | 1. The ``directory`` must be the directory containing the 175 | ``location``. 176 | 177 | 178 | Module Identifiers 179 | ================== 180 | 181 | 1. A module identifier is a string of "terms" delimited by forward 182 | slashes. 183 | 184 | 1. A term is either: 185 | 186 | 1. any combination of lower-case letters, numbers, and hyphens, 187 | 188 | 1. a single dot, "``.``", or 189 | 190 | 1. a double dot, "``..``". 191 | 192 | 1. Module identifiers should not have file-name extensions like 193 | ``.js``. 194 | 195 | 1. Module identifiers may be "relative" or "resolved". A module 196 | identifier is "relative" if the first term is ``.`` or ``..``. 197 | 198 | 1. Top-level identifiers are resolved relative to ``""``. 199 | 200 | 1. The ``require`` function in each module resolves relative 201 | identifiers from the corresponding ``module.id``. 202 | 203 | 1. To resolve any path of module identifiers, 204 | 205 | 1. An array of terms must be initialized to an empty array. 206 | 207 | 1. For each module identifier in the path of identifiers, 208 | 209 | 1. Pop off the last term in the array, provided one exists. 210 | 211 | 1. For each term in a module identifier in order, 212 | 213 | 1. Take no action if the term is ``"."``. 214 | 215 | 1. Pop a term off the end of the array if the term is 216 | ``".."``. 217 | 218 | 1. Push the term on the end of the array otherwise. 219 | 220 | 1. The array of terms must be joined with forward slashes, ``"/"`` 221 | to construct the resulting "resolved" identifier. 222 | 223 | 224 | Unspecified 225 | =========== 226 | 227 | This specification leaves the following important points of 228 | interoperability unspecified: 229 | 230 | 1. Whether modules are stored with a database, file system, or factory 231 | functions, or are interchangeable with link libraries. 232 | 233 | 1. Whether ``require`` accepts values that are not module identifiers 234 | as specified here. 235 | 236 | -------------------------------------------------------------------------------- /packages/TODO.md: -------------------------------------------------------------------------------- 1 | 2 | To Do 3 | ===== 4 | 5 | * Registry and catalog specifications 6 | 7 | -------------------------------------------------------------------------------- /packages/notes.md: -------------------------------------------------------------------------------- 1 | 2 | This specification attempts to assimilate most of the points on 3 | CommmonJS/Packages/1.1. 4 | 5 | Node's module loader gives priority to its own built-ins over the 6 | contents and configuration of packages. This specification clarifies 7 | that the package's name space should come first so that packages do not 8 | unexpectedly break when engines change their built-ins and packages are 9 | not subject to name-space collisions with engines unless they really 10 | need to load those modules. 11 | 12 | This specification encourages engines like Node to add a system for 13 | capability based package linkage, particularly for its own API's, so 14 | that packages are encouraged to explicitly request API's from the 15 | engines they support. The intent is to make it possible for a package 16 | registry to automatically construct a reliable compatibility table which 17 | can be enforced by engines. 18 | 19 | Some things that are not used in practice have been removed. 20 | 21 | Some requirements have been relaxed, with the intent that individual 22 | package managers will enforce stronger requirements based on their own 23 | design. Particularly, some package managers will require "name" and 24 | "version" to be provided, while others will only need "location". 25 | 26 | Consistent hashing has been defined. 27 | 28 | Support for "mappings" has been specified and a translation function for 29 | NPM's "dependencies" has been provided and mandated. It's my hope that 30 | both properties will be supported, even by NPM eventually, since they 31 | both have merits. 32 | 33 | This specification hopes to encourage NPM to eventually relax the name 34 | "node_modules" to "packages", and provides a migration path. 35 | 36 | Requirements necessary for browser based loaders, particularly 37 | deterministic package and module locations, have been addressed. 38 | 39 | -------------------------------------------------------------------------------- /packages/specification.md: -------------------------------------------------------------------------------- 1 | 2 | 1. A package is a directory. 3 | 1. A package must contain a ``package.json`` file. 4 | 1. ``package.json`` must be valid JSON. 5 | 1. Particular package registries or catalogs may impose a more strict 6 | requirements. 7 | 1. Any properties of ``package.json`` not specified here must occur 8 | under an "overlays" mapping. 9 | 10 | 11 | Informative Configuration 12 | ========================= 13 | 14 | 1. In ``package.json``, the following apply to the properties of the 15 | root object. 16 | 1. "name" may be the name of the package. 17 | 1. The name must be a combination of one or more 18 | hyphen-delimited lower-case words and numbers. 19 | 1. "title" may be the title for the package for human readers, if 20 | the package has one. 21 | 1. "version" may be a string representing a semantic version 22 | . 23 | 1. "description" may be a one-line description of the package. 24 | 1. "keywords" is an array of short words that may be used to search 25 | and find this package in a registry. 26 | 1. "author" may be a single person. 27 | 1. "maintainers" may be an array of people. 28 | 1. "contributors" may be an array of people. 29 | 1. "licenses" may be an array of licenses. **This property is not 30 | legally binding and does not necessarily mean your package is 31 | licensed under the terms you define in this property** 32 | 1. The existence of multiple lincenses implies that a user may 33 | chose to be bound by any one of them, not that every user 34 | must be bound by all. 35 | 1. "bugs" may be a string or an object. 36 | 1. A "bugs" string must be the URL for a site where bugs can be 37 | reported. 38 | 1. A "bugs" object may have an "email" property with an email 39 | address to which bugs can be reported. 40 | 1. A "bugs" object may have a "url" property with the URL of a 41 | site where bugs can be reported. 42 | 1. "repositories" may be an array of repositories. 43 | 1. A person can be represented as either a string or an object. 44 | *Note: this implies that the string must only contain information 45 | representable with an equivalent object.* 46 | 1. A person string must begin with a name. 47 | 1. A person string may then contain one email address in angle 48 | brackets (``<>``) delimited by a space. 49 | 1. A person string then may contain one web address in parentheses 50 | (``()``) delimited by a space. 51 | 1. A person object must contain a "name" property. 52 | 1. A person object may contain an "email" property with an email 53 | address. 54 | 1. A person object may contain a "web" property with a URL. 55 | 1. A license may be represented either as a URL or an object. 56 | 1. A license object may have a "type" property, having a value from 57 | any of the parenthesized open source license abbreviations from 58 | . 59 | 1. A license object may have a "url" property 60 | 1. A repository must be represented as an object with "type" and 61 | "url" properties. 62 | 1. A repository "type" may be "git", "svn", "hg", or others 63 | based on their command name. 64 | 1. A repository may specify a "url" property. 65 | 1. A repository may spefify a "path" property if the root of 66 | the project is not the the root of the package. 67 | 68 | Normative Configuration 69 | ======================= 70 | 71 | 1. In ``package.json``, the following apply to the properties of the 72 | root object. 73 | 1. "main" may be provided and must be a URL relative to 74 | ``package.json`` to a JavaScript file, by its full name 75 | including its extension. 76 | 1. "overlays" may be provided and must be an object that maps overlay 77 | names to objects conforming to the same specification as the 78 | ``package.json``'s root object. 79 | 1. "directories" may be provided and must be an object that maps 80 | conventional directory names to configured directory names within 81 | the package. 82 | 1. "lib" may be configured 83 | 1. "packages" may be configured, and is a URL relative to the 84 | directory containing ``package.json`` where named package 85 | dependencies may be found. 86 | 1. "mappings" may be provided and must be an object that maps module 87 | identifiers to dependencies. 88 | 1. "registry" may be provided and must be a URL for a package registry. 89 | *What exists at that URL is beyond the scope of this specification* 90 | 1. "hash" may be provided and must be the consitent hash of the 91 | package. 92 | 1. "seed" may be provided and must be a string that is used to seed the 93 | consistent hash of the package. 94 | 1. "manifest" may be provided and must be a recursive directory listing 95 | of a package's contents in lexicographic order with paths 96 | represented as URL's relative to ``package.json`` 97 | 1. A package dependency may be represented by a string. 98 | 1. If the string contains "@", it is equivalent to an object with 99 | "name", "version", and "registry" properties. 100 | 1. ``@version`` where the name is implied by the mapping 101 | identifier and the registry is implied by the default 102 | registry location. 103 | 1. ``name@version`` where the registry is implied by the 104 | default registry location. 105 | 1. ``name@version@registry`` 106 | 1. Otherwise, the dependency is equivalent to an object with a 107 | "location" property with the dependency as its value. 108 | 1. A package dependency may be represented by an object. 109 | 1. A dependency may have a "location" property. *In the absence of 110 | a location property, a module loader and a package manager may 111 | agree on a location computed based on the "packages" directory 112 | and the "name", "version", and "hash" of the package.* 113 | 1. A dependency may have a "name" property. 114 | 1. With a "name", a dependency may have a "version" property. 115 | 1. With a "name", a dependency may have a "registry" property. 116 | 1. A dependency may have a "capability" property. The capability 117 | must be a URL for a specification of that capability, or an 118 | array of URLs for specifications for that capability. 119 | 1. Each specification must be sufficient alone to satisfy the 120 | dependency of the package. *An array is supported in the 121 | case where there are multiple URLs of specifications that 122 | are effectively equivalent for the purpose of the package, 123 | when supporting package systems do not agree on which URL is 124 | the canonical version.* 125 | 1. A dependency may have a "zip" property that is a URL for a ZIP 126 | file containing the package. 127 | 1. A dependency may have a "tar" property that is a URL for a TAR 128 | or gzipped TAR file containing the package. 129 | 1. The "zip" and "tar" URLs are for the use of package managers and 130 | must not be used in place of "location" or "name" dependencies 131 | by module loaders, unless the module loader also serves as a 132 | package manager. For compatibility with all module loaders, a 133 | "location" for a package must be provided or computable from 134 | some subset of "name", "version", "hash" and the 135 | ``directories.packages`` configuration. 136 | 1. A dependency may have an "overlays" property. 137 | 1. For each applicable overlay name, the mapped object must be 138 | copied over the dependency. 139 | 1. A dependency may have a "hash" property, corresponding to the 140 | consistent hash of the dependency package. 141 | 1. Overlay names are strings that identify a specific implementation of 142 | this specification or a class of implementations such that 143 | configuration specific to those implementations may be copied over 144 | the object containing the overlays. 145 | 146 | 147 | Linkage 148 | ======= 149 | 150 | Each package has its own resolved module identifier name-space. In the 151 | parlance of the Modules specification, this means that each package is 152 | an independent system of modules, linked by explicit configuration to 153 | other packages. 154 | 155 | 1. There must not be cyclic dependencies among packages. 156 | 1. If a package has a "name" and the first term of a module identifier 157 | is that name, that module identifier is equivalent to the rest of 158 | the identifier, even if the rest of the identifier is ``""`` (empty 159 | string). 160 | 1. The resolved module identifier ``""`` (empty string) corresponds to 161 | the main module if it exists. 162 | 1. For each mapping, from longest to shortest, if the first terms of 163 | the mapping match the first terms of a module identifier 164 | 1. If there are no subsequent terms in the module identifier, the 165 | dependency must link the ``""`` (empty string) module from the 166 | dependency package. 167 | 1. If there are subsequent terms in the module identifier, the 168 | dependency must link the module from that package with the 169 | remaining terms. 170 | 1. If no mapping corresponds to a module identifier, the package must 171 | link to a module in its own library. 172 | 1. The library of a package when used in a browser consists of the 173 | contents of the ``lib`` directory of the package, where each module 174 | identifier corresponds to the lower-case and extensionless URL 175 | relative to the library directory for all files that have a ``.js`` 176 | extension. 177 | 1. ``lib`` may be configured to an alternate location with the 178 | ``directories`` property of ``package.json``. 179 | 1. The library of a package when used in any engine other than the 180 | browser may incorporate the contents of the ``overlays/OVERLAY/lib`` 181 | directory before the ``lib`` directory, if it exists. 182 | 1. ``lib`` and ``overlays`` may be configured to alternate 183 | locations with the ``directories`` property of ``package.json``. 184 | 1. In each of these directories, any engine other than the browser 185 | may incoroporate modules with alternate extensions and loaders, 186 | searching for each extension before moving to the next 187 | directory. 188 | 1. If a package requires a capability, a package management system can 189 | loosely link packages that provide a capability and packages that 190 | consume that capability, such that different packages can provide 191 | the same capability on different engines. In particular, it would 192 | make sense for Node's own API's to be loaded as an explicitly 193 | requested capability. 194 | 195 | A variety of arrangements are possible between package managers and 196 | package-aware module loaders. 197 | 198 | 1. The module loader may itself be the package manager, in which case 199 | it is repsonsible for downloading, executing, and linking the 200 | packages. In such cases, the "name", "version", "registry", or 201 | "location" properties of dependencies might be used. 202 | 1. The module loader and package manager may agree on locations where 203 | packages may be stored and found. The module loader may elect to 204 | use the "location" of a package and compute missing locations based 205 | on "packages", "name", "version", and "hash", or some combination 206 | thereof. The package manager would agree to drop packages in those 207 | locations. Alternately, the package manager and module loader might 208 | agree on a database or file system location scheme based on some of 209 | those variables. 210 | 211 | 212 | NPM 213 | === 214 | 215 | To maintain a semblance of compatibility with NPM 1. 216 | 217 | 1. "dependencies" may be provided and must be an object that maps a 218 | module identifier and a package with the same name to a version of 219 | that package from the NPM registry. These (name/id, version) pairs 220 | may be translated to mappings with the ``"http://npmjs.org"`` 221 | registry implied. 222 | 1. "directories" may configure the location of "packages". If a 223 | "dependencies" property exists, the default "packages" location is 224 | "node_modules" 225 | 226 | 227 | Hash 228 | ==== 229 | 230 | 1. The consistent hash for a package is a SHA256 hash in lower-case 231 | hexadecimal format. 232 | 1. To compute the package's consistent hash 233 | 1. Digest the "seed" property of ``package.json`` if it exists. 234 | 1. Digest the "main" property of ``package.json`` if it exists. 235 | 1. For each mapping in lexicographic order based on the module 236 | identifier, digest the module identifier, location, name, 237 | version, registry, and hash, in that order, for each property 238 | that is defined. 239 | 1. For each file in a recursive traversal of the package contents 240 | in lexicographic order, except ``package.json``, digest the 241 | binary content. 242 | 243 | 244 | Secure Loading 245 | ============== 246 | 247 | For a package to be loaded with a secure loader: 248 | 249 | 1. A package must provide a "hash". 250 | 1. A package must provide a "manifest". 251 | 252 | A secure loader: 253 | 254 | 1. Must verify the consistent hash of the package before executing any 255 | code that links against it. 256 | 1. Must only acknowledge the existence of paths in the manifest. 257 | 1. Must refuse to load a package that lacks a "hash" or "manifest". 258 | 259 | -------------------------------------------------------------------------------- /promises/TODO.md: -------------------------------------------------------------------------------- 1 | 2 | To Do 3 | ===== 4 | 5 | * Extended Promise Manager API 6 | * Progress Observers and Notifiers 7 | * Address inelegance of sending messages to functions, or 8 | automatically marking functions as unserializable 9 | * Address awkwardness of the ``def`` name. 10 | 11 | Done 12 | ==== 13 | 14 | * Sendable Promises 15 | * Promise Manager API 16 | 17 | -------------------------------------------------------------------------------- /promises/specification.md: -------------------------------------------------------------------------------- 1 | An asynchronous promise loosely represents the eventual result of a 2 | function. A promise is initially "unresolved" and may eventually become 3 | "resolved". A resolution can either be "fulfilled" with a value or 4 | "rejected" with a reason, corresponding by analogy to synchronously 5 | returned values and thrown exceptions respectively. Once a promise has 6 | become resolved, it cannot be resolved again, so a promise's fulfillment 7 | value or rejection reason are guaranteed to be the same across multiple 8 | observations. Although the identity of the value or reason cannot 9 | change, their properties may be mutable. 10 | 11 | 12 | Thenable Promises 13 | ================= 14 | 15 | Thenable promises are sufficent for modeling fulfillment and rejection 16 | of promises within a single program. If promises are needed to model 17 | remote objects between cooperating event loops (like workers, processes, 18 | and network clients, servers, and peers), they can either be extended 19 | with the message passing "sendable promises" system, or assimilated by a 20 | sendable promise manager system. If a thenable promise is provided by a 21 | system that is suspicious for safety or security reasons, it is 22 | **necessary** to use a promise manager to provide the normative 23 | guarantees of this specification and the stricter guarantees provided by 24 | the promise manager specification. 25 | 26 | 1. A promise is an object (*that may be a function*) with a ``then`` 27 | function. 28 | 29 | 1. When using promises, the existence of a ``then`` function must 30 | be sufficient to distinguish a promise from any other value. 31 | 32 | 1. ``then`` accepts a ``fulfilled`` callback and a ``rejected`` 33 | callback. 34 | 35 | 1. ``then`` may be called any number of times to register multiple 36 | observers. 37 | 38 | 1. The callbacks registered by any ``then`` call must not be called 39 | after callbacks registered in a later ``then`` call. 40 | 41 | 1. Callbacks should not be called before ``then`` returns. 42 | 43 | 1. ``fulfilled`` and ``rejected`` are both optional arguments. 44 | 45 | 1. If truthy, ``fulfilled`` must accept a value as its only 46 | argument. 47 | 48 | 1. The ``fulfilled`` callback must be called after the promise 49 | is fulfilled. 50 | 51 | 1. The ``fulfilled`` callback must not be called more than 52 | once. 53 | 54 | 1. The ``fulfilled`` callback must not be called if the 55 | ``rejected`` callback has been called. 56 | 57 | 1. If truthy, ``rejected`` must accept a rejection reason as its 58 | only argument. 59 | 60 | 1. The ``rejected`` callback must be called after the promise 61 | has been rejected. 62 | 63 | 1. The ``rejected`` callback must not be called more than once. 64 | 65 | 1. The ``rejected`` callback must not be called if the 66 | ``fulfilled`` callback has been called. 67 | 68 | 1. ``then`` must return a thenable promise. 69 | 70 | 1. The returned promise must be fulfilled with the value 71 | returned by either callback. 72 | 73 | 1. The returned promise must be rejected if either callback 74 | throws an exception. 75 | 76 | 1. The reason for the rejection must be the error thrown by 77 | the callback. 78 | 79 | Thenable Promise Manager 80 | ======================== 81 | 82 | 1. A promise manager is an object with the following functions for 83 | creating and manipulating promises. 84 | 85 | 1. ``defer(annotation)`` 86 | 87 | 1. Annotation is an optional string describing the operation 88 | being deferred, which may be used by debuggers to assist in 89 | the visualization of when promises are made and resolved. 90 | 91 | 1. Returns an object with ``promise``, ``resolve``, and 92 | ``reject`` properties. 93 | 94 | 1. ``promise`` is a promise (the "deferred promise"). 95 | 96 | 1. ``resolve`` is a function that accepts a promise or a value. 97 | 98 | 1. Ignores all resolutions after the first resolution. 99 | 100 | 1. If the value is a promise, resolves the deferred promise 101 | with the resolved promise, such that any observations of 102 | the resolved promise are forwarded to the deferred 103 | promise. 104 | 105 | 1. If the value is not a promise, fulfills the deferred 106 | promise with the resolved value. 107 | 108 | 1. ``reject`` is a function that accepts a rejection reason 109 | 110 | 1. The rejection reason may be any value. 111 | 112 | 1. Resolves the deferred promise with a rejection promise 113 | for the given reason. 114 | 115 | 1. All functions of the deferred object may be called without 116 | being bound to the deferred object. 117 | 118 | 1. ``when(value, fulfilled, rejected)`` 119 | 120 | 1. ``value`` may be a promise or any other value. 121 | 122 | 1. Non-promise values are equivalent to fulfilled promises 123 | with that value. 124 | 125 | 1. Arranges for ``fulfilled`` to be called 126 | 127 | 1. if ``fulfilled`` is truthy 128 | 129 | 1. with the fulfilled value as its argument 130 | 131 | 1. not before ``when`` returns 132 | 133 | 1. if and when the value is fulfilled 134 | 135 | 1. at most once 136 | 137 | 1. if ``rejected`` has not been called 138 | 139 | 1. Arranges for ``rejected`` to be called 140 | 141 | 1. if ``rejected`` is truthy 142 | 143 | 1. with the reason for rejection as its argument 144 | 145 | 1. not before ``when`` returns 146 | 147 | 1. if and when the promised value is rejected 148 | 149 | 1. at most once 150 | 151 | 1. if ``fulfilled`` has not been called 152 | 153 | 1. Must return a promise 154 | 155 | 1. that must be resolved if and when ``fulfilled`` or 156 | ``rejected`` return a value or promise, with that value 157 | or promise as the resolution. 158 | 159 | 1. that must be rejected if and when ``fulfilled`` or 160 | ``rejected`` throw an error, with the error as the reason 161 | for rejection. 162 | 163 | 1. ``resolve(value)`` 164 | 165 | 1. ``value`` may be a promise or any other value. 166 | 167 | 1. If ``value`` is a promise, returns that promise. 168 | 169 | 1. If ``value`` is not a promise, returns a promise that 170 | has already been fulfilled with the value. 171 | 172 | 1. ``reject(reason)`` 173 | 174 | 1. Returns a promise that has been rejected for the given 175 | reason. 176 | 177 | 1. The reason may be any value. 178 | 179 | 1. ``isPromise(value)`` 180 | 181 | 1. Returns a boolean value of whether the given value is a 182 | promise. 183 | 184 | 1. ``isResolved(value)`` 185 | 186 | 1. Returns a boolean value of whether the given value is a 187 | resolved promise. 188 | 189 | 1. ``value`` may be any value. 190 | 191 | 1. ``isFulfilled(value)`` 192 | 193 | 1. Returns a boolean value of whether the given value is a 194 | resolved and fulfilled promise. 195 | 196 | 1. ``value`` may be any value. 197 | 198 | 1. ``isRejected(value)`` 199 | 200 | 1. Returns a boolean value of whether the given value is a 201 | resolved and rejected promise. 202 | 203 | 1. ``value`` may be any value. 204 | 205 | 206 | Sendable Promises 207 | ================= 208 | 209 | If a promise is a remote object or a proxy for a remote object, in 210 | addition to being able to observe fulfillment and rejection, it is 211 | useful to be able to pipeline "messages" to such promises so that the 212 | remote promise can rapidly dispatch responses to those messages. It is 213 | also useful to have promises for remote objects and proxies for remote 214 | objects that are not serializable, such as stateful functions or 215 | functions that provide access to capabilities. 216 | 217 | 1. A promise is an object (*that may be a function*) with a 218 | ``promiseSend`` function. 219 | 220 | 1. When using promises, the existence of a ``promiseSend`` function 221 | must be sufficient to distinguish a promise from any other 222 | value. 223 | 224 | 1. The ``promiseSend`` function must accept an "operator name" as 225 | its first argument. 226 | 227 | 1. Operator names are an extensible set of strings. The following 228 | operators are reserved: 229 | 230 | 1. ``"when"``, in which case the third argument must be a 231 | rejection callback. 232 | 233 | 1. A rejection callback must accept a rejection reason (any 234 | value) as its argument. 235 | 236 | 1. ``"get"`` in which case the third argument is a property 237 | name (string). 238 | 239 | 1. ``"put"`` in which case the third argument is a property 240 | name (string) and the fourth is a value for the new 241 | property. 242 | 243 | 1. ``"del"`` in which case the third argument is a property 244 | name. 245 | 246 | 1. ``"post"`` in which case the third argument is a property 247 | name and all subsequent arguments are variadic. 248 | 249 | 1. The ``promiseSend`` function must accept a resolver function as 250 | its second argument. 251 | 252 | 1. The resolver function may eventually be called with a value 253 | or a promise as its argument. 254 | 255 | 1. The ``promiseSend`` function may receive variadic arguments. 256 | 257 | 1. The ``promiseSend`` function must return ``undefined``. 258 | 259 | 1. A sendable promise may also be a thenable promise. 260 | 261 | Any thenable promise can be trivially wrapped with a sendable promise 262 | that implements all of the object-oriented operators by waiting for the 263 | promise to be fulfilled and then performing the corresponding operation. 264 | So, it is only necessary for a promise to implement promiseSend if it is 265 | on the boundary between two processes. 266 | 267 | 268 | Sendable Promise Manager 269 | ======================== 270 | 271 | A sendable promise manager extends the thenable promise manager system 272 | with functions that can forward messages to a promise, returning 273 | promises for the fulfilled object's response. It also provides tools 274 | for sending and handling messages, and for annotating an object that is 275 | not serializable. 276 | 277 | 1. A sendable promise manager creates and manipulates both sendable 278 | promises and thenable promises. 279 | 280 | 1. A sendable promise manager must support the thenable promise manager 281 | specification. 282 | 283 | 1. A sendable promise manager must support the following additional 284 | functions. 285 | 286 | 1. ``get``, ``put``, ``del``, ``post``, ``invoke`` are functions. 287 | 288 | 1. These functions must accept an object as their first 289 | argument. 290 | 291 | 1. The object may be a promise or any other value. 292 | 293 | 1. If object is not a promise, it is treated as a fulfilled 294 | promise with its value. 295 | 296 | 1. These functions must return a promise. 297 | 298 | 1. The promise must be rejected with the same reason if the 299 | object promise is rejected. 300 | 301 | 1. The promise must be rejected if the object promise is 302 | fulfilled with a type that does not support properties. 303 | 304 | 1. ``get(object, name)`` 305 | 306 | 1. Must fulfill the returned promise with the named property of 307 | of the object if and when it is fulfilled. 308 | 309 | 1. ``put(object, name, value)`` 310 | 311 | 1. Must fulfill the returned promise with ``undefined`` if and 312 | when the object is fulfilled and the named property has been 313 | assigned the value. 314 | 315 | 1. Must reject the returned promise if the object is fulfilled 316 | and setting the named property to value throws an exception. 317 | 318 | 1. ``del(object, name)`` 319 | 320 | 1. Must fulfill the returned promise with ``undefined`` if and 321 | when the object is fulfilled and an attempt has been made to 322 | delete the named property . 323 | 324 | 1. Must reject the returned promise with an exception if the 325 | object is fulfilled and attempting to delete the named 326 | property throws an exception. 327 | 328 | 1. ``post(object, name, args)`` 329 | 330 | 1. Must apply the ``args`` to the named function of object if 331 | and when it is fullfilled. 332 | 333 | 1. Must resolve the returned promise with the return value 334 | of that application. 335 | 336 | 1. Must reject the returned promise if that application 337 | throws an error, using the error as the reason for 338 | rejection. 339 | 340 | 1. ``invoke(object, name, ...args)`` 341 | 342 | 1. Must apply the spread, variadic ``args`` to the named 343 | function of object if and when it is fullfilled. 344 | 345 | 1. Must resolve the returned promise with the return value 346 | of that application. 347 | 348 | 1. Must reject the returned promise if that application 349 | throws an error, using the error as the reason for 350 | rejection. 351 | 352 | 1. ``keys(object)`` 353 | 354 | 1. Must fulfill the returned promise with an array of the owned 355 | property names of the object if and when it is fulfilled. 356 | 357 | 1. ``makePromise(handlers, fallback)`` 358 | 359 | 1. Returns a sendable promise 360 | 361 | 1. Accepts a ``handlers`` object that maps message names to 362 | handler functions that return values or promises, 363 | particularly: 364 | 365 | 1. ``when(rejected)`` 366 | 367 | 1. ``get(name)`` 368 | 369 | 1. ``put(name)`` 370 | 371 | 1. ``del(name)`` 372 | 373 | 1. ``post(name, args)`` 374 | 375 | 1. Accepts a ``fallback(operator, resolve, ...args)`` function 376 | 377 | 1. ``promiseSend(operator, resolve, ...args)`` calls the 378 | handler with the given operator name from handlers if one 379 | is truthy. 380 | 381 | 1. Calls the handler with the spreaded, variadic arguments 382 | only. 383 | 384 | 1. Calls resolve with the value returned by the handler. 385 | 386 | 1. ``promiseSend(operator, resolve, ...args)`` call the 387 | fallback function if property of the handlers mapping for 388 | the given operator name is falsy. 389 | 390 | 1. Calls fallback with the operator and the spreaded, 391 | variadic arguments only. 392 | 393 | 1. Calls resolve with the value returned by the fallback 394 | function. 395 | 396 | 1. ``send(operator, ...args)`` 397 | 398 | 1. Constructs a deferred. 399 | 400 | 1. Calls ``promiseSend(operator, resolve, ...args)`` 401 | 402 | 1. Where ``resolve`` is the resolve function of the 403 | deferred. 404 | 405 | 1. Not before ``send`` returns. 406 | 407 | 1. Returns the deferred promise. 408 | 409 | 1. ``resolve(value)`` 410 | 411 | 1. Must support ``ref`` as defined by the thenable promise 412 | manager specification. 413 | 414 | 1. Must handle the following messages (per ``makePromise``): 415 | 416 | 1. ``when(errback)``, ignores the errback and returns 417 | ``value``. 418 | 419 | 1. ``get(name)``, returns the named property of ``value``. 420 | 421 | 1. ``put(name, value)``, sets the named property to the 422 | given value of the ref value and returns ``undefined``. 423 | 424 | 1. ``del(name)``, deletes the named property of the 425 | resolved value and returns ``undefined``. 426 | 427 | 1. ``post(name, args)``, calls the named function of the 428 | value and returns the returned value. 429 | 430 | 1. Must reject all other messages with the reason ``"Promise 431 | does not handle OPERATOR"`` where ``OPERATOR`` is the 432 | message operator. 433 | 434 | 1. ``reject(reason)`` 435 | 436 | 1. Must support ``reject`` as defined by the thenable promise 437 | manager specification. 438 | 439 | 1. Handles the ``"when"`` operator: 440 | 441 | 1. If the ``rejected`` callback is truthy, calls the 442 | ``rejected`` callback with ``reason`` and returns the 443 | value that the callback returns. 444 | 445 | 1. Otherwise, returns a rejected promise with ``reason``. 446 | 447 | 1. Handles all other messages by forwarding its own reason for 448 | rejection. 449 | 450 | 1. ``defer(annotation)`` 451 | 452 | 1. Must support ``defer`` as defined by the thenable promise 453 | manager specification. 454 | 455 | 1. Must eventually forward all messages received to the ``ref`` 456 | promise representing the resolution of its ``promise``. 457 | 458 | -------------------------------------------------------------------------------- /template/TODO.md: -------------------------------------------------------------------------------- 1 | 2 | To Do 3 | ===== 4 | 5 | git checkout start-here 6 | git checkout -b $SPEC 7 | cp -r template $SPEC 8 | cd $SPEC 9 | 10 | 11 | 1. README.md (synopsis, index, philosophy, prior art, references, 12 | discussions, implementations) 13 | 1. specification.md (adhering to ../specification/specification.md) 14 | 1. tests/all.js (adhering to ../tests/specification.md) 15 | 1. notes.md (for implementors) 16 | 1. tutorial.md (for users) 17 | 18 | 19 | Done 20 | ==== 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/specification.md: -------------------------------------------------------------------------------- 1 | 2 | Assertions 3 | ========== 4 | 5 | An assertion object must conform to the following specification. 6 | *Note: the ``"assert"`` module identifier is conventionally reserved 7 | for an assertion module.* 8 | 9 | 1. The assertion object must have an ``AssertionError`` 10 | constructor function. 11 | 12 | 1. All instances of ``AssertionError`` must be instances of 13 | ``Error``. 14 | 15 | 1. An ``AssertionError`` must be instantiated with an options 16 | object describing the failure. 17 | 18 | 1. Assertion options must have a ``message`` string. 19 | 20 | 1. Assertion options must have an ``actual`` value. 21 | 22 | 1. Assertion options must have an ``expected`` value, or 23 | string describing the expected value or range of values. 24 | 25 | 1. Assertion options may have an ``operator`` string, that 26 | may represent any JavaScript operator or function name 27 | used to test whether the actual value met the expectation. 28 | 29 | 1. In every assertion function, given its criterion for passing or 30 | failing, 31 | 32 | 1. If ``this`` has a ``pass`` function property and the criterion 33 | is met, the assertion function must call ``pass`` with the 34 | given message. 35 | 36 | 1. If ``this`` has a ``fail`` function property and the criterion 37 | is not met, the assertion function must call ``fail`` with an 38 | ``AssertionError`` object with the corresponding ``message``, 39 | ``actual`` value, ``expected`` value description, and 40 | ``operator`` if applicable. 41 | 42 | 1. If ``this`` does not have a ``fail`` function property and the 43 | criteron is not met, the assertion function must throw an 44 | ``AssertionError`` object with the corresponding ``message``, 45 | ``actual`` value, ``expected`` value description, and 46 | ``operator`` if applicable. 47 | 48 | 1. The assertion object must have a function 49 | ``ok(guard, message_opt)``. 50 | 51 | 1. The criterion of ``ok`` is ``!!guard``. 52 | 53 | 1. The corresponding ``operator`` for an ``AssertionError`` is 54 | ``"=="``. 55 | 56 | 1. The corresponding ``expected`` value for an ``AssertionError`` 57 | is ``true``. 58 | 59 | 1. The assertion object must have a function 60 | ``equal(actual, expected, message_opt)`` 61 | 62 | 1. The criterion of ``equal`` is that ``actual == expected``. 63 | 64 | 1. The corresponding ``operator`` for an ``AssertionError`` is 65 | ``"=="``. 66 | 67 | 1. The assertion object must have a function 68 | ``notEqual(actual, unexpected, message_opt)`` 69 | 70 | 1. The criterion of ``notEqual`` is that ``actual != expected`` 71 | 72 | 1. The corresponding ``operator`` for an ``AssertionError`` is 73 | ``"!="``. 74 | 75 | 1. The assertion object must have a function 76 | ``deepEqual(actual, expected, message_opt)`` 77 | 78 | 1. The corresponding ``operator`` for an ``AssertionError`` is 79 | ``"deepEqual"``. 80 | 81 | 1. The criterion for ``deepEqual`` is that: 82 | 83 | 1. If ``expected == actual``, they are deeply equal. 84 | 85 | 1. If the expected value is a ``Date``, they are deeply equal 86 | only if the actual value is a ``Date`` representing the 87 | same time. 88 | 89 | 1. If the ``typeof`` ``actual`` or ``expected`` is not 90 | ``"object"``, they are deeply equal if ``expected == 91 | actual``. 92 | 93 | 1. Otherwise, for all objects, including arrays, they are 94 | deeply equal only if they have the same number of owned, 95 | enumerable properties, the same property names (although 96 | not necessarily in the same order), and the respective 97 | values for each key are deeply equal. 98 | 99 | 1. The assertion object must have a function 100 | ``notDeepEqual(actual, unexpected, message_opt)`` 101 | 102 | 1. The corresponding ``operator`` for an ``AssertionError`` is 103 | ``"notDeepEqual"``. 104 | 105 | 1. The criterion for ``notDeepEqual`` is that the actual and 106 | expected values do not meet the criterion of ``deepEqual``. 107 | 108 | 1. The assertion object must have a function 109 | ``strictEqual(actual, expected, message_opt)`` 110 | 111 | 1. The criterion of ``strictEqual`` is that ``expected === 112 | actual``. 113 | 114 | 1. The corresponding ``operator`` for an ``AssertionError`` is 115 | ``"==="``. 116 | 117 | 1. The assertion object must have a function 118 | ``notStrictEqual(actual, expected, message_opt)`` 119 | 120 | 1. The criterion of ``notStrictEqual`` is that ``expected !== 121 | actual``. 122 | 123 | 1. The corresponding ``operator`` for an ``AssertionError`` is 124 | ``"!=="``. 125 | 126 | 1. The assertion object must have a function 127 | ``error(callback, Error_opt, message_opt)`` 128 | *Note: in the CommonJS specification, this was named ``throws``, 129 | however this name was not ergonomic before ECMAScript 5. 130 | Assertion objects may provide ``throws`` for backward 131 | compatibility with that specification provided that they note that 132 | it is deprecated.* 133 | 134 | 1. The criterion for ``error`` is 135 | 136 | 1. If ``Error`` is provided, calling ``callback`` must throw 137 | an error that is an instance of the given ``Error``. 138 | 139 | 1. If ``Error`` is not provided, calling ``callback`` must 140 | throw an exception. 141 | 142 | 1. The corresponding ``operator`` for an ``AssertionError`` is 143 | ``"throws"``. 144 | 145 | 1. The corresponding ``actual`` value for an ``AssertionError`` 146 | is ``undefined`` if callback does not throw an error. 147 | 148 | 1. The corresponding ``actual`` value for an ``AssertionError`` 149 | is the exception thrown if the callback does throw an error. 150 | 151 | 1. The corresponding ``expected`` value for an ``AssertionError`` 152 | is the global ``Error`` if the ``Error`` argument is undefined. 153 | 154 | 1. The corresponding ``expected`` value for an ``AssertionError`` 155 | is the given ``Error`` argument if defined. 156 | 157 | 1. Custom assertion objects may provide additional assertion 158 | functions. *Note: to use an alternate assertion object for logging, 159 | a test object must specify an ``Assert`` constructor property* 160 | 161 | 162 | Tests 163 | ===== 164 | 165 | 1. A test function, 166 | 167 | 1. A test function may accept an assertion object as its first 168 | argument, indicating that the test may log results rather than 169 | fail at the first exception. 170 | 171 | 1. A test function of ``length`` 2 must accept a completion 172 | function, indicating that the test will end when the 173 | test calls the completion function. 174 | 175 | 1. If a test function does not have ``length`` 2, it may return a 176 | promise to complete. 177 | 178 | 1. The completion value may be undefined. 179 | 180 | 1. A test object, 181 | 182 | 1. May have an ``Assert`` constructor function for providing 183 | custom assertions in the context of contained tests. 184 | 185 | 1. All owned, enumerable properties of a test object that have 186 | names that start with ``"test"`` must be test objects or 187 | functions. 188 | 189 | 190 | Runner 191 | ====== 192 | 193 | *Note: by convention, the ``"test"`` module in a package is reserved 194 | for a test runner.* 195 | 196 | 1. A test runner object must have a function 197 | ``run(tests, log_opt)`` 198 | 199 | 1. If ``log`` is undefined, ``run`` must construct a 200 | default logger object. 201 | 202 | 1. If ``tests`` has an ``Assert`` constructor function property, 203 | ``run`` must use this function to create assertion objects. 204 | 205 | 1. For each enumerable property of ``tests`` that starts with 206 | ``"test"``, ``run`` must execute the corresponding test, 207 | proceeding to the next test only when the previous ends. 208 | 209 | 1. If the corresponding value is a function, 210 | 211 | 1. **Prepare:** The test runner must create a logger 212 | object by calling 213 | ``log.section()``. 214 | 215 | 1. The test runner must create an assertion object and 216 | connect its ``pass`` and ``fail`` function properties 217 | to the ``pass`` and ``fail`` properties of the 218 | constructed logger. 219 | 220 | 1. **Run:** The test runner must call the test 221 | function with an assertion object and a completion 222 | function. 223 | 224 | 1. **End:** If a test function does not return a 225 | promise, the test ends upon returning. 226 | 227 | 1. If the test function returns a promise, the test ends 228 | when the promise is resolved. 229 | *Note: resolution is either fulfillment or rejection.* 230 | 231 | 1. If the ``length`` of the test function is ``2``, the 232 | test must eventually call the completion function and 233 | must not return a promise. 234 | 235 | 1. The test ends when the test calls the completion 236 | function. 237 | 238 | 1. **Fail:** If the test function throws an 239 | exception, or if the promise returned by the test 240 | function is rejected, 241 | 242 | 1. If that exception (or reason for rejection) is an 243 | instance of the ``AssertionError`` in the 244 | assertion object, the runner must call the 245 | ``fail`` function of ``log`` with the exception. 246 | 247 | 1. Otherwise, the runner must call the ``error`` 248 | function of the ``log`` with the given exception 249 | or reason for rejection. 250 | 251 | 1. **Pass:** If a test ends without throwing an 252 | exception or rejecting the returned promise, the 253 | runner must call the ``pass`` function of the ``log`` 254 | object with the key of the test function property. 255 | 256 | 1. If the corresponding value is an object, 257 | 258 | 1. ``run`` must create a new logger by calling 259 | ``log.section()``. 260 | 261 | 1. ``run`` must call itself with the test and logger 262 | object. 263 | 264 | 1. ``run`` must return a promise. 265 | 266 | 1. The promise must be fulfilled when the last contained test 267 | ends. 268 | 269 | 1. The last contained test of a test object with no 270 | properties is already ended. 271 | 272 | Log 273 | === 274 | 275 | 1. A logger object must have a ``pass`` function property. 276 | 277 | 1. A logger object must have a ``fail`` function property. 278 | 279 | 1. A logger object must have an ``error`` function property. 280 | 281 | 1. A logger object must have a ``section`` function property that, 282 | when called, returns a new logger object. 283 | 284 | -------------------------------------------------------------------------------- /tests/test.js: -------------------------------------------------------------------------------- 1 | 2 | // From Node.js test/mjsunit/test-assert.js 3 | // Felix Geisendörfer (felixge), backported from NodeJS 4 | // Karl Guertin (greyrest), backported from NodeJS 5 | // Kris Kowal (kriskowal), conversion to CommonJS 6 | 7 | // strangely meta, no? 8 | 9 | var assert = require('assert'); 10 | 11 | function makeBlock(f) { 12 | var args = Array.prototype.slice.call(arguments,1); 13 | return function(){ 14 | return f.apply(this, args); 15 | } 16 | } 17 | 18 | exports['test AssertionError instanceof Error'] = function () { 19 | assert.ok(new assert.AssertionError({}) instanceof Error); 20 | }; 21 | 22 | exports['test ok false'] = function () { 23 | assert.error(makeBlock(assert.ok, false), assert.AssertionError); 24 | }; 25 | 26 | exports['test ok(true)'] = makeBlock(assert.ok, true); 27 | exports['test ok("test")'] = makeBlock(assert.ok, "test"); 28 | exports['test equal true false'] = function () { 29 | assert.error(makeBlock(assert.equal, true, false), assert.AssertionError, 'equal'); 30 | }; 31 | 32 | exports['test equal null null'] = makeBlock(assert.equal, null, null); 33 | exports['test equal undefined undefined'] = makeBlock(assert.equal, undefined, undefined); 34 | exports['test equal null undefined'] = makeBlock(assert.equal, null, undefined); 35 | exports['test equal 2 "2"'] = makeBlock(assert.equal, 2, "2"); 36 | exports['test equal "2" 2'] = makeBlock(assert.equal, "2", 2); 37 | exports['test equal true true'] = makeBlock(assert.equal, true, true); 38 | exports['test notEqual true false'] = makeBlock(assert.notEqual, true, false); 39 | exports['test notEqual true true'] = function () { 40 | assert.error(makeBlock(assert.notEqual, true, true), assert.AssertionError, 'notEqual'); 41 | }; 42 | exports['test strictEqual 2 "2"'] = function () { 43 | assert.error(makeBlock(assert.strictEqual, 2, "2"), assert.AssertionError, 'strictEqual'); 44 | }; 45 | exports['test strictEqual null undefined'] = function () { 46 | assert.error(makeBlock(assert.strictEqual, null, undefined), assert.AssertionError, 'strictEqual'); 47 | }; 48 | exports['test notStrictEqual 2 "2"'] = makeBlock(assert.notStrictEqual, 2, "2"); 49 | 50 | //deepEquals 51 | 52 | exports['test deepEqual date'] = makeBlock(assert.deepEqual, new Date(2000,3,14), new Date(2000,3,14)); 53 | exports['test deepEqual date negative'] = function () { 54 | assert.error(makeBlock(assert.deepEqual, new Date(), new Date(2000,3,14)), assert.AssertionError, 'deepEqual date'); 55 | }; 56 | 57 | exports['test deepEqual 4 "4"'] = makeBlock(assert.deepEqual, 4, "4"); 58 | exports['test deepEqual "4" 4'] = makeBlock(assert.deepEqual, "4", 4); 59 | exports['test deepEqual true 1'] = makeBlock(assert.deepEqual, true, 1); 60 | exports['test deepEqual 4 "5"'] = function () { 61 | assert.error(makeBlock(assert.deepEqual, 4, "5")); 62 | }; 63 | 64 | // having the same number of owned properties && the same set of keys 65 | exports['test deepEqual {a:4} {a:4}'] = makeBlock(assert.deepEqual, {a:4}, {a:4}); 66 | exports['test deepEqual {a:4,b:"2"} {a:4,b:"2"}'] = makeBlock(assert.deepEqual, {a:4,b:"2"}, {a:4,b:"2"}); 67 | exports['test deepEqual [4] ["4"]'] = makeBlock(assert.deepEqual, [4], ["4"]); 68 | exports['test deepEqual {a:4} {a:4,b:true}'] = function () { 69 | assert.error(makeBlock(assert.deepEqual, {a:4}, {a:4,b:true}), assert.AssertionError); 70 | }; 71 | 72 | exports['test deepEqual ["a"], {0:"a"}'] = makeBlock(assert.deepEqual, ["a"], {0:"a"}); 73 | //(although not necessarily the same order), 74 | exports['test deepEqual {a:4,b:"1"} {b:"1",a:4}'] = makeBlock(assert.deepEqual, {a:4,b:"1"}, {b:"1",a:4}); 75 | 76 | exports['test deepEqual arrays with non-numeric properties'] = function () { 77 | var a1 = [1,2,3]; 78 | var a2 = [1,2,3]; 79 | a1.a = "test"; 80 | a1.b = true; 81 | a2.b = true; 82 | a2.a = "test" 83 | assert.error(makeBlock(assert.deepEqual, Object.keys(a1), Object.keys(a2)), assert.AssertionError); 84 | makeBlock(assert.deepEqual, a1, a2); 85 | }; 86 | 87 | exports['test deepEqual identical prototype'] = function () { 88 | // having an identical prototype property 89 | var nbRoot = { 90 | toString: function(){return this.first+' '+this.last;} 91 | } 92 | var nameBuilder = function(first,last){ 93 | this.first = first; 94 | this.last = last; 95 | return this; 96 | } 97 | nameBuilder.prototype = nbRoot; 98 | var nameBuilder2 = function(first,last){ 99 | this.first = first; 100 | this.last = last; 101 | return this; 102 | } 103 | nameBuilder2.prototype = nbRoot; 104 | var nb1 = new nameBuilder('Ryan', 'Dahl'); 105 | var nb2 = new nameBuilder2('Ryan', 'Dahl'); 106 | 107 | assert.deepEqual(nb1, nb2); 108 | 109 | nameBuilder2.prototype = Object; 110 | nb2 = new nameBuilder2('Ryan', 'Dahl'); 111 | assert.error(makeBlock(assert.deepEqual, nb1, nb2), assert.AssertionError); 112 | 113 | }; 114 | 115 | exports['test deepEqual "a" {}'] = function () { 116 | assert.error(makeBlock(assert.deepEqual, 'a', {}), assert.AssertionError); 117 | }; 118 | 119 | exports['test deepEqual "" ""'] = function () { 120 | assert.deepEqual("", ""); 121 | }; 122 | 123 | exports['test deepEqual "" [""]'] = function () { 124 | assert.error(makeBlock(assert.deepEqual, '', ['']), assert.AssertionError); 125 | }; 126 | 127 | exports['test deepEqual [""] [""]'] = function () { 128 | assert.deepEqual([""], [""]); 129 | }; 130 | 131 | exports['test throw AssertionError'] = function () { 132 | 133 | //Testing the throwing 134 | function thrower(errorConstructor){ 135 | throw new errorConstructor('test'); 136 | } 137 | var aethrow = makeBlock(thrower, assert.AssertionError); 138 | var aethrow = makeBlock(thrower, assert.AssertionError); 139 | //the basic calls work 140 | assert.error(makeBlock(thrower, assert.AssertionError), assert.AssertionError, 'message'); 141 | assert.error(makeBlock(thrower, assert.AssertionError), assert.AssertionError); 142 | assert.error(makeBlock(thrower, assert.AssertionError)); 143 | //if not passing an error, catch all. 144 | assert.error(makeBlock(thrower, TypeError)); 145 | //when passing a type, only catch errors of the appropriate type 146 | var threw = false; 147 | try { 148 | assert.error(makeBlock(thrower, TypeError), assert.AssertionError); 149 | } catch (e) { 150 | threw = true; 151 | assert.ok(e instanceof TypeError, 'type'); 152 | } 153 | assert.ok(threw, 'assert.throws with an explicit error is eating extra errors', assert.AssertionError); 154 | threw = false; 155 | 156 | }; 157 | 158 | if (module == require.main) 159 | require("test").run(exports); 160 | 161 | -------------------------------------------------------------------------------- /tests/tutorial.md: -------------------------------------------------------------------------------- 1 | 2 | ### A typical test, failing at the first failed assertion 3 | 4 | Having arranged for "assert" and "test" to be modules in my package 5 | for assertions and running tests, I create a test function and run it 6 | if the module is executed as the main module. 7 | 8 | var assert = require("assert"); 9 | 10 | function test() { 11 | assert.ok(false, "it worked"); // it doesn't 12 | // but we don't get here 13 | assert.equal(1, 1, "they're equal"); 14 | // and they would have been, but they 15 | // didn't get a chance. 16 | } 17 | 18 | if (require.main === module) 19 | require("test").run(test); 20 | 21 | Depending on the default logger, this might look like: 22 | 23 | FAIL it worked 24 | 0 passes, 1 failure, 0 errors 25 | 26 | 27 | ### A test that logs passes and fails instead of stopping at the first failed assertion 28 | 29 | exports['test foo'] = function (assert) { 30 | assert.ok(false, "it worked"); // it doesn't 31 | // and we log it, moving on... 32 | assert.equal(1, 1, "they're equal"); 33 | // and they are, and we log that too 34 | }; 35 | 36 | if (require.main === module) 37 | require("test").run(exports); 38 | 39 | Again, depending on the logger, it might look like: 40 | 41 | FAIL it worked 42 | PASS they're equal 43 | 0 passes, 2 failures, 0 errors 44 | 45 | 46 | ### Composing test suites with modules. 47 | 48 | I can now create a test suite as a module by requiring the foo test as 49 | part of mine. 50 | 51 | exports['test foo'] = require("./foo"); 52 | exports['test bar'] = require("./bar"); 53 | 54 | if (require.main === module) 55 | require("test").run(exports); 56 | 57 | Since the ``"foo"`` test module also checks whether it's the main 58 | module before executing, we will only invoke the test runner once. 59 | 60 | 61 | ### An asynchronous test using a completion function 62 | 63 | Relying on the accuracy of ``setTimeout``, we'll test the accuracy of 64 | ``inABit(callback)``. We'll accept the assertion module as a first 65 | argument and call the ``done`` function to end the test. 66 | 67 | var tolerance = 100; // miliseconds 68 | exports['test in a bit'] = function (assert, done) { 69 | var called = false; 70 | inABit(function () { 71 | called = true; 72 | }); 73 | setTimeout(function () { 74 | assert.ok(called, "called one second later"); 75 | done(); 76 | }, 1000 + tolerance); 77 | }; 78 | 79 | 80 | ### An asynchronous test using a promise 81 | 82 | var Q = require("q"); 83 | 84 | var inABit = function () { 85 | var deferred = Q.defer(); 86 | setTimeout(deferred.resolve, 1000); 87 | return deferred.promise; 88 | } 89 | 90 | var tolerance = 100; // miliseconds 91 | 92 | exports['test a promise'] = function () { 93 | var start = +new Date(); 94 | return Q.when(inABit(), function () { 95 | var stop = +new Date(); 96 | var duration = stop - start; 97 | assert.ok(duration < 1000 + tolerance, 'within tolerance'); 98 | }); 99 | }; 100 | 101 | 102 | ### A set of tests with custom assertion functions 103 | 104 | var assert = require("assert"); 105 | 106 | exports.Assert = function () { 107 | var self = Object.create(assert); 108 | self.inRange = function (actual, min, max, message) { 109 | var ok = actual >= min && actual <= max; 110 | if (ok) { 111 | self.pass(message); 112 | } else { 113 | self.fail({ 114 | message: message, 115 | actual: actual, 116 | expected: "between " + min + " and " + max, 117 | operator: "inRange" 118 | }); 119 | } 120 | }; 121 | return self; 122 | }; 123 | 124 | var tolerance = 100; 125 | exports['test a promise'] = function () { 126 | var inABit = require("in-a-bit").inABit; 127 | var start = +new Date(); 128 | return Q.when(inABit(), function () { 129 | var stop = +new Date(); 130 | var duration = stop - start; 131 | var min = 1000 - tolerance; 132 | var max = 1000 + tolerance; 133 | assert.inRange(duration, min, max, 'within tolerance'); 134 | }); 135 | }; 136 | 137 | 138 | ### Running a test with a custom logger 139 | 140 | var Logger = function () { 141 | var self = { 142 | passes: 0, 143 | failures: 0, 144 | errors: 0, 145 | pass: function (message) { 146 | console.log('PASS', message); 147 | root.passes++; 148 | }, 149 | fail: function (assertion) { 150 | console.error(assertion); 151 | root.failures++; 152 | }, 153 | error: function (exception) { 154 | console.error(exception); 155 | root.errors++; 156 | }, 157 | section: function () { 158 | return self; 159 | }, 160 | report: function () { 161 | console.log( 162 | self.passes, 'passes', 163 | self.failures, 'failures', 164 | self.errors, 'errors' 165 | ); 166 | } 167 | }; 168 | return self; 169 | }; 170 | 171 | if (require.main === module) { 172 | var log = Logger(); 173 | var allSpecs = require("./all"); 174 | require("test").run(allSpecs, Logger()) 175 | log.report(); 176 | } 177 | 178 | 179 | ### Running a module as a test. 180 | 181 | exports['test foo'] = require.bind(null, './foo-spec'); 182 | 183 | if (require.main === module) 184 | require("test").run(exports); 185 | 186 | --------------------------------------------------------------------------------