├── .dependabot └── config.yml ├── .eslintrc.json ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example ├── .gitignore ├── package-lock.json ├── package.json ├── src │ ├── function.ts │ ├── import-1.ts │ ├── import-2.ts │ ├── import.ts │ └── types.ts ├── test │ ├── function.ts │ ├── helper.ts │ ├── import.ts │ ├── index.ts │ └── types.ts └── tsconfig.json ├── images ├── beater-wip.svg └── beater.svg ├── package-lock.json ├── package.json ├── src ├── index.ts ├── run-with-options.ts ├── run.ts └── type │ ├── run-options.ts │ ├── run-with-options.ts │ └── run.ts ├── test ├── concurrent.ts ├── concurrent │ ├── f.ts │ └── g.ts ├── example.ts ├── helper │ ├── index.ts │ ├── named-test.ts │ ├── sandboxed-test.ts │ └── slow-test.ts ├── index.ts ├── many-test.ts ├── run-with-options.ts ├── run.ts └── test.ts └── tsconfig.json /.dependabot/config.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | update_configs: 3 | - package_manager: "javascript" 4 | directory: "/" 5 | update_schedule: "daily" 6 | automerged_updates: 7 | - match: 8 | dependency_name: '@babel/*' 9 | update_type: 'semver:minor' 10 | - match: 11 | dependency_name: '@types/*' 12 | update_type: 'semver:minor' 13 | - match: 14 | dependency_name: '@typescript-eslint/*' 15 | update_type: 'semver:minor' 16 | - match: 17 | dependency_name: 'eslint*' 18 | update_type: 'semver:minor' 19 | - match: 20 | dependency_name: 'prettier' 21 | update_type: 'all' 22 | - match: 23 | dependency_name: 'sinon' 24 | update_type: 'semver:minor' 25 | ignored_updates: 26 | - match: 27 | dependency_name: "@types/node" 28 | version_requirement: ">10" 29 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:@typescript-eslint/eslint-recommended", 5 | "plugin:@typescript-eslint/recommended", 6 | "prettier", 7 | "prettier/@typescript-eslint" 8 | ], 9 | "ignorePatterns": ["node_modules"], 10 | "parser": "@typescript-eslint/parser", 11 | "plugins": ["@typescript-eslint"], 12 | "root": true, 13 | "rules": { 14 | "@typescript-eslint/no-unused-vars": [ 15 | "error", 16 | { "argsIgnorePattern": "^_" } 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /*.log 2 | /*.tgz 3 | /.nyc_output/ 4 | /.tmp/ 5 | /coverage/ 6 | /lib/ 7 | /node_modules/ 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | - "12" 5 | - "10" 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [9.0.1](https://github.com/bouzuya/beater/compare/v9.0.0...v9.0.1) (2020-04-11) 2 | 3 | * build(deps): update dependencies 4 | * build(deps-dev): update devDependencies 5 | 6 | 7 | 8 | # [9.0.0](https://github.com/bouzuya/beater/compare/v8.0.2...v9.0.0) (2020-02-01) 9 | 10 | * Bump eslint-config-prettier from 6.9.0 to 6.10.0 ([7ee3e2d](https://github.com/bouzuya/beater/commit/7ee3e2d)) 11 | * Bump sinon from 8.0.4 to 8.1.0 ([1affec4](https://github.com/bouzuya/beater/commit/1affec4)) 12 | * Bump typescript from 3.7.4 to 3.7.5 ([a8fe209](https://github.com/bouzuya/beater/commit/a8fe209)) 13 | * refactor!: drop support for node.js v8 ([02564a4](https://github.com/bouzuya/beater/commit/02564a4)) 14 | * build: add eslint and prettier ([80d3b07](https://github.com/bouzuya/beater/commit/80d3b07)) 15 | * build: bump sinon from 8.0.1 to 8.1.1 ([5bb1f3c](https://github.com/bouzuya/beater/commit/5bb1f3c)) 16 | * build: stop searching for other eslint configs ([1d9d266](https://github.com/bouzuya/beater/commit/1d9d266)) 17 | * build(deps-dev): bump @typescript-eslint 2.17.0 to 2.18.0 ([482617c](https://github.com/bouzuya/beater/commit/482617c)) 18 | * build(deps-dev): bump babel from 7.8.3 to 7.8.4 ([f476a4c](https://github.com/bouzuya/beater/commit/f476a4c)) 19 | * build(deps-dev): update devDependencies ([7d6464b](https://github.com/bouzuya/beater/commit/7d6464b)) 20 | * build(deps): update dependencies ([945b342](https://github.com/bouzuya/beater/commit/945b342)) 21 | * refactor: apply eslint ([573cbae](https://github.com/bouzuya/beater/commit/573cbae)) 22 | * style: apply prettier ([a57d6bf](https://github.com/bouzuya/beater/commit/a57d6bf)) 23 | 24 | 25 | ### BREAKING CHANGE 26 | 27 | * drop support for node.js v8 28 | 29 | 30 | ## [8.0.2](https://github.com/bouzuya/beater/compare/v8.0.0...v8.0.2) (2020-01-16) 31 | 32 | 33 | 34 | # [8.0.0](https://github.com/bouzuya/beater/compare/v7.0.0...v8.0.0) (2019-12-08) 35 | 36 | 37 | 38 | # [7.0.0](https://github.com/bouzuya/beater/compare/v6.0.2...v7.0.0) (2019-11-28) 39 | 40 | 41 | 42 | ## [6.0.2](https://github.com/bouzuya/beater/compare/v6.0.1...v6.0.2) (2019-11-16) 43 | 44 | 45 | 46 | ## [6.0.1](https://github.com/bouzuya/beater/compare/v6.0.0...v6.0.1) (2019-05-02) 47 | 48 | 49 | 50 | # [6.0.0](https://github.com/bouzuya/beater/compare/5.0.1...v6.0.0) (2019-05-02) 51 | 52 | 53 | 54 | ## [5.0.1](https://github.com/bouzuya/beater/compare/5.0.0...5.0.1) (2017-08-04) 55 | 56 | 57 | 58 | # [5.0.0](https://github.com/bouzuya/beater/compare/0.4.0...5.0.0) (2017-07-15) 59 | 60 | 61 | 62 | # [0.4.0](https://github.com/bouzuya/beater/compare/0.3.0...0.4.0) (2016-06-16) 63 | 64 | 65 | 66 | # [0.3.0](https://github.com/bouzuya/beater/compare/0.2.0...0.3.0) (2016-06-08) 67 | 68 | 69 | 70 | # [0.2.0](https://github.com/bouzuya/beater/compare/0.1.3...0.2.0) (2016-06-06) 71 | 72 | 73 | 74 | ## [0.1.3](https://github.com/bouzuya/beater/compare/0.1.2...0.1.3) (2016-06-06) 75 | 76 | 77 | 78 | ## [0.1.2](https://github.com/bouzuya/beater/compare/0.1.1...0.1.2) (2016-06-05) 79 | 80 | 81 | 82 | ## [0.1.1](https://github.com/bouzuya/beater/compare/0.1.0...0.1.1) (2016-06-05) 83 | 84 | 85 | 86 | # 0.1.0 (2016-06-05) 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 bouzuya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![beater logo][beater-logo] 2 | 3 | beater: **b**ouzuya's **ea**sy **t**est runn**er**. beater is inspired by [eater][yosuke-furukawa/eater]. 4 | 5 | [yosuke-furukawa/eater]: https://github.com/yosuke-furukawa/eater 6 | [beater-logo]: https://cloud.githubusercontent.com/assets/1221346/15892977/e69386f0-2db7-11e6-9163-bcb2f2471581.png 7 | 8 | ## Features 9 | 10 | - Only 2 functions: 11 | - `run()` 12 | - `runWithOptions()` 13 | - You can use your favorite `assert()`. 14 | - You can use your favorite reporter. 15 | - You can use TypeScript (3.x `*.d.ts` is included). 16 | 17 | ## Usage 18 | 19 | ### 1. Install 20 | 21 | ``` 22 | $ npm install --save-dev beater 23 | ``` 24 | 25 | ### 2. Write test 26 | 27 | ```js 28 | // test/index.js 29 | const assert = require('assert'); 30 | const { run } = require('beater'); 31 | 32 | const test1 = function simple_test() { 33 | assert(1 === 1); 34 | }; 35 | 36 | const test2 = function promise_test() { 37 | return new Promise((resolve) => { 38 | assert(1 === 1); 39 | resolve(); 40 | }); 41 | }; 42 | 43 | const test3 = async function async_fn_test() { 44 | await new Promise((resolve) => setTimeout(resolve, 0)); 45 | assert(1 === 1); 46 | }; 47 | 48 | run([test1, test2, test3]).catch(() => process.exit(1)); 49 | ``` 50 | 51 | ### 3. Run 52 | 53 | ``` 54 | $ node test/index.js 55 | TAP version 13 56 | 1..3 57 | ok 1 - simple_test 58 | ok 2 - promise_test 59 | ok 3 - async_fn_test 60 | ``` 61 | 62 | ## Related Packages 63 | 64 | - [bouzuya/beater-helpers][] ... beater helper functions. 65 | - [bouzuya/beater-reporter][] ... beater reporter interface. 66 | - [bouzuya/beater-tap-reporter][] ... beater TAP reporter (default reporter) . 67 | - You can pipe to [any TAP reporter](https://github.com/sindresorhus/awesome-tap#reporters). 68 | - e.g. `$ node test/index.js | tap-dot` 69 | - [bouzuya/create-beater-index][] ... beater index generator. 70 | - [bouzuya/beater-double][] ... beater test double functions (alpha) . 71 | - [bouzuya/beater-snapshot][] ... beater snapshot testing functions (alpha) . 72 | - [bouzuya/beater-cli][] ... DEPRECATED. A command-line interface for beater. 73 | - [bouzuya/beater-cli-reporter][] ... DEPRECATED. beater-cli default reporter. 74 | 75 | [bouzuya/beater-cli-reporter]: https://github.com/bouzuya/beater-cli-reporter 76 | [bouzuya/beater-cli]: https://github.com/bouzuya/beater-cli 77 | [bouzuya/beater-double]: https://github.com/bouzuya/beater-double 78 | [bouzuya/beater-helpers]: https://github.com/bouzuya/beater-helpers 79 | [bouzuya/beater-reporter]: https://github.com/bouzuya/beater-reporter 80 | [bouzuya/beater-snapshot]: https://github.com/bouzuya/beater-snapshot 81 | [bouzuya/beater-tap-reporter]: https://github.com/bouzuya/beater-tap-reporter 82 | [bouzuya/create-beater-index]: https://github.com/bouzuya/create-beater-index 83 | 84 | ## Badges 85 | 86 | [![npm version][npm-badge-url]][npm-url] 87 | [![Travis CI][travis-ci-badge-url]][travis-ci-url] 88 | 89 | [npm-badge-url]: https://img.shields.io/npm/v/beater 90 | [npm-url]: https://www.npmjs.com/package/beater 91 | [travis-ci-badge-url]: https://img.shields.io/travis/bouzuya/beater 92 | [travis-ci-url]: https://travis-ci.org/bouzuya/beater 93 | 94 | ## License 95 | 96 | [MIT](LICENSE) 97 | 98 | ## Author 99 | 100 | [bouzuya][user] <[m@bouzuya.net][email]> ([https://bouzuya.net/][url]) 101 | 102 | [user]: https://github.com/bouzuya 103 | [email]: mailto:m@bouzuya.net 104 | [url]: https://bouzuya.net/ 105 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | /.tmp/ 2 | /node_modules/ 3 | -------------------------------------------------------------------------------- /example/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@sinonjs/commons": { 8 | "version": "1.6.0", 9 | "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.6.0.tgz", 10 | "integrity": "sha512-w4/WHG7C4WWFyE5geCieFJF6MZkbW4VAriol5KlmQXpAQdxvV0p26sqNZOW6Qyw6Y0l9K4g+cHvvczR2sEEpqg==", 11 | "dev": true, 12 | "requires": { 13 | "type-detect": "4.0.8" 14 | } 15 | }, 16 | "@sinonjs/formatio": { 17 | "version": "3.2.2", 18 | "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", 19 | "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", 20 | "dev": true, 21 | "requires": { 22 | "@sinonjs/commons": "^1", 23 | "@sinonjs/samsam": "^3.1.0" 24 | } 25 | }, 26 | "@sinonjs/samsam": { 27 | "version": "3.3.3", 28 | "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", 29 | "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", 30 | "dev": true, 31 | "requires": { 32 | "@sinonjs/commons": "^1.3.0", 33 | "array-from": "^2.1.1", 34 | "lodash": "^4.17.15" 35 | } 36 | }, 37 | "@sinonjs/text-encoding": { 38 | "version": "0.7.1", 39 | "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", 40 | "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", 41 | "dev": true 42 | }, 43 | "ansi-styles": { 44 | "version": "3.2.1", 45 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 46 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 47 | "dev": true, 48 | "requires": { 49 | "color-convert": "^1.9.0" 50 | } 51 | }, 52 | "array-from": { 53 | "version": "2.1.1", 54 | "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", 55 | "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", 56 | "dev": true 57 | }, 58 | "balanced-match": { 59 | "version": "1.0.0", 60 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 61 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 62 | "dev": true 63 | }, 64 | "beater": { 65 | "version": "7.0.0", 66 | "resolved": "https://registry.npmjs.org/beater/-/beater-7.0.0.tgz", 67 | "integrity": "sha512-54ySGs1gnU5rbjbkT+Vf/4Jyad3raf94KDYkQj8kwsTIIXhiFKpb1ApdZzF7O7ou/T1P3UnZ1DY3C+eLjeLjhg==", 68 | "dev": true, 69 | "requires": { 70 | "beater-reporter": "^1.0.0", 71 | "beater-tap-reporter": "^2.0.1" 72 | } 73 | }, 74 | "beater-helpers": { 75 | "version": "1.0.1", 76 | "resolved": "https://registry.npmjs.org/beater-helpers/-/beater-helpers-1.0.1.tgz", 77 | "integrity": "sha512-PXcg76oRvqla3qWmtULTfH2fglak5IhKk+kw9SM93ot7/SleLdn+hqZEUrYKCi2EpyXXdDAmxPC9QrmqQHWBJA==", 78 | "dev": true 79 | }, 80 | "beater-reporter": { 81 | "version": "1.0.0", 82 | "resolved": "https://registry.npmjs.org/beater-reporter/-/beater-reporter-1.0.0.tgz", 83 | "integrity": "sha512-bL6B39f/7MEGbbuHWSG9aLELgBJ/SOSDP+o5zw92rf9rks+a/oFMHJdBstI4bJZAgmhLJaYdY2ALC7GdoRzNnQ==", 84 | "dev": true 85 | }, 86 | "beater-tap-reporter": { 87 | "version": "2.0.1", 88 | "resolved": "https://registry.npmjs.org/beater-tap-reporter/-/beater-tap-reporter-2.0.1.tgz", 89 | "integrity": "sha512-ELWjMtkce4gpxAi/EjxyPnUqaaXbNyz2kI3IJQD2AqFfTsL2wI0m5t8Dtn3Fm4cocOREC5oZTz9c74D5mTJ+FA==", 90 | "dev": true, 91 | "requires": { 92 | "beater-reporter": "^1.0.0" 93 | } 94 | }, 95 | "brace-expansion": { 96 | "version": "1.1.11", 97 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 98 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 99 | "dev": true, 100 | "requires": { 101 | "balanced-match": "^1.0.0", 102 | "concat-map": "0.0.1" 103 | } 104 | }, 105 | "chalk": { 106 | "version": "2.4.2", 107 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 108 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 109 | "dev": true, 110 | "requires": { 111 | "ansi-styles": "^3.2.1", 112 | "escape-string-regexp": "^1.0.5", 113 | "supports-color": "^5.3.0" 114 | } 115 | }, 116 | "color-convert": { 117 | "version": "1.9.3", 118 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 119 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 120 | "dev": true, 121 | "requires": { 122 | "color-name": "1.1.3" 123 | } 124 | }, 125 | "color-name": { 126 | "version": "1.1.3", 127 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 128 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 129 | "dev": true 130 | }, 131 | "concat-map": { 132 | "version": "0.0.1", 133 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 134 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 135 | "dev": true 136 | }, 137 | "cross-spawn": { 138 | "version": "6.0.5", 139 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 140 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 141 | "dev": true, 142 | "requires": { 143 | "nice-try": "^1.0.4", 144 | "path-key": "^2.0.1", 145 | "semver": "^5.5.0", 146 | "shebang-command": "^1.2.0", 147 | "which": "^1.2.9" 148 | } 149 | }, 150 | "define-properties": { 151 | "version": "1.1.3", 152 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 153 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 154 | "dev": true, 155 | "requires": { 156 | "object-keys": "^1.0.12" 157 | } 158 | }, 159 | "diff": { 160 | "version": "3.5.0", 161 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 162 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 163 | "dev": true 164 | }, 165 | "error-ex": { 166 | "version": "1.3.2", 167 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 168 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 169 | "dev": true, 170 | "requires": { 171 | "is-arrayish": "^0.2.1" 172 | } 173 | }, 174 | "es-abstract": { 175 | "version": "1.16.2", 176 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.2.tgz", 177 | "integrity": "sha512-jYo/J8XU2emLXl3OLwfwtuFfuF2w6DYPs+xy9ZfVyPkDcrauu6LYrw/q2TyCtrbc/KUdCiC5e9UajRhgNkVopA==", 178 | "dev": true, 179 | "requires": { 180 | "es-to-primitive": "^1.2.1", 181 | "function-bind": "^1.1.1", 182 | "has": "^1.0.3", 183 | "has-symbols": "^1.0.1", 184 | "is-callable": "^1.1.4", 185 | "is-regex": "^1.0.4", 186 | "object-inspect": "^1.7.0", 187 | "object-keys": "^1.1.1", 188 | "string.prototype.trimleft": "^2.1.0", 189 | "string.prototype.trimright": "^2.1.0" 190 | } 191 | }, 192 | "es-to-primitive": { 193 | "version": "1.2.1", 194 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 195 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 196 | "dev": true, 197 | "requires": { 198 | "is-callable": "^1.1.4", 199 | "is-date-object": "^1.0.1", 200 | "is-symbol": "^1.0.2" 201 | } 202 | }, 203 | "escape-string-regexp": { 204 | "version": "1.0.5", 205 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 206 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 207 | "dev": true 208 | }, 209 | "exec-sh": { 210 | "version": "0.2.2", 211 | "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz", 212 | "integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==", 213 | "dev": true, 214 | "requires": { 215 | "merge": "^1.2.0" 216 | } 217 | }, 218 | "function-bind": { 219 | "version": "1.1.1", 220 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 221 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 222 | "dev": true 223 | }, 224 | "graceful-fs": { 225 | "version": "4.2.3", 226 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", 227 | "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", 228 | "dev": true 229 | }, 230 | "has": { 231 | "version": "1.0.3", 232 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 233 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 234 | "dev": true, 235 | "requires": { 236 | "function-bind": "^1.1.1" 237 | } 238 | }, 239 | "has-flag": { 240 | "version": "3.0.0", 241 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 242 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 243 | "dev": true 244 | }, 245 | "has-symbols": { 246 | "version": "1.0.1", 247 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", 248 | "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", 249 | "dev": true 250 | }, 251 | "hosted-git-info": { 252 | "version": "2.8.5", 253 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", 254 | "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", 255 | "dev": true 256 | }, 257 | "is-arrayish": { 258 | "version": "0.2.1", 259 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 260 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", 261 | "dev": true 262 | }, 263 | "is-callable": { 264 | "version": "1.1.4", 265 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", 266 | "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", 267 | "dev": true 268 | }, 269 | "is-date-object": { 270 | "version": "1.0.1", 271 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", 272 | "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", 273 | "dev": true 274 | }, 275 | "is-regex": { 276 | "version": "1.0.4", 277 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", 278 | "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", 279 | "dev": true, 280 | "requires": { 281 | "has": "^1.0.1" 282 | } 283 | }, 284 | "is-symbol": { 285 | "version": "1.0.3", 286 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", 287 | "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", 288 | "dev": true, 289 | "requires": { 290 | "has-symbols": "^1.0.1" 291 | } 292 | }, 293 | "isarray": { 294 | "version": "0.0.1", 295 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 296 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 297 | "dev": true 298 | }, 299 | "isexe": { 300 | "version": "2.0.0", 301 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 302 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 303 | "dev": true 304 | }, 305 | "json-parse-better-errors": { 306 | "version": "1.0.2", 307 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", 308 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", 309 | "dev": true 310 | }, 311 | "just-extend": { 312 | "version": "4.0.2", 313 | "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz", 314 | "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==", 315 | "dev": true 316 | }, 317 | "load-json-file": { 318 | "version": "4.0.0", 319 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", 320 | "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", 321 | "dev": true, 322 | "requires": { 323 | "graceful-fs": "^4.1.2", 324 | "parse-json": "^4.0.0", 325 | "pify": "^3.0.0", 326 | "strip-bom": "^3.0.0" 327 | } 328 | }, 329 | "lodash": { 330 | "version": "4.17.15", 331 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 332 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", 333 | "dev": true 334 | }, 335 | "lolex": { 336 | "version": "4.2.0", 337 | "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz", 338 | "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", 339 | "dev": true 340 | }, 341 | "memorystream": { 342 | "version": "0.3.1", 343 | "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", 344 | "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", 345 | "dev": true 346 | }, 347 | "merge": { 348 | "version": "1.2.1", 349 | "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", 350 | "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", 351 | "dev": true 352 | }, 353 | "minimatch": { 354 | "version": "3.0.4", 355 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 356 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 357 | "dev": true, 358 | "requires": { 359 | "brace-expansion": "^1.1.7" 360 | } 361 | }, 362 | "minimist": { 363 | "version": "1.2.5", 364 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 365 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 366 | "dev": true 367 | }, 368 | "nice-try": { 369 | "version": "1.0.5", 370 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 371 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", 372 | "dev": true 373 | }, 374 | "nise": { 375 | "version": "1.5.2", 376 | "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.2.tgz", 377 | "integrity": "sha512-/6RhOUlicRCbE9s+94qCUsyE+pKlVJ5AhIv+jEE7ESKwnbXqulKZ1FYU+XAtHHWE9TinYvAxDUJAb912PwPoWA==", 378 | "dev": true, 379 | "requires": { 380 | "@sinonjs/formatio": "^3.2.1", 381 | "@sinonjs/text-encoding": "^0.7.1", 382 | "just-extend": "^4.0.2", 383 | "lolex": "^4.1.0", 384 | "path-to-regexp": "^1.7.0" 385 | } 386 | }, 387 | "normalize-package-data": { 388 | "version": "2.5.0", 389 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", 390 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", 391 | "dev": true, 392 | "requires": { 393 | "hosted-git-info": "^2.1.4", 394 | "resolve": "^1.10.0", 395 | "semver": "2 || 3 || 4 || 5", 396 | "validate-npm-package-license": "^3.0.1" 397 | } 398 | }, 399 | "npm-run-all": { 400 | "version": "4.1.5", 401 | "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", 402 | "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", 403 | "dev": true, 404 | "requires": { 405 | "ansi-styles": "^3.2.1", 406 | "chalk": "^2.4.1", 407 | "cross-spawn": "^6.0.5", 408 | "memorystream": "^0.3.1", 409 | "minimatch": "^3.0.4", 410 | "pidtree": "^0.3.0", 411 | "read-pkg": "^3.0.0", 412 | "shell-quote": "^1.6.1", 413 | "string.prototype.padend": "^3.0.0" 414 | } 415 | }, 416 | "object-inspect": { 417 | "version": "1.7.0", 418 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", 419 | "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", 420 | "dev": true 421 | }, 422 | "object-keys": { 423 | "version": "1.1.1", 424 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 425 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 426 | "dev": true 427 | }, 428 | "parse-json": { 429 | "version": "4.0.0", 430 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", 431 | "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", 432 | "dev": true, 433 | "requires": { 434 | "error-ex": "^1.3.1", 435 | "json-parse-better-errors": "^1.0.1" 436 | } 437 | }, 438 | "path-key": { 439 | "version": "2.0.1", 440 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 441 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 442 | "dev": true 443 | }, 444 | "path-parse": { 445 | "version": "1.0.6", 446 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 447 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 448 | "dev": true 449 | }, 450 | "path-to-regexp": { 451 | "version": "1.8.0", 452 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", 453 | "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", 454 | "dev": true, 455 | "requires": { 456 | "isarray": "0.0.1" 457 | } 458 | }, 459 | "path-type": { 460 | "version": "3.0.0", 461 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", 462 | "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", 463 | "dev": true, 464 | "requires": { 465 | "pify": "^3.0.0" 466 | } 467 | }, 468 | "pidtree": { 469 | "version": "0.3.0", 470 | "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz", 471 | "integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==", 472 | "dev": true 473 | }, 474 | "pify": { 475 | "version": "3.0.0", 476 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 477 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", 478 | "dev": true 479 | }, 480 | "read-pkg": { 481 | "version": "3.0.0", 482 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", 483 | "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", 484 | "dev": true, 485 | "requires": { 486 | "load-json-file": "^4.0.0", 487 | "normalize-package-data": "^2.3.2", 488 | "path-type": "^3.0.0" 489 | } 490 | }, 491 | "resolve": { 492 | "version": "1.13.1", 493 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz", 494 | "integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==", 495 | "dev": true, 496 | "requires": { 497 | "path-parse": "^1.0.6" 498 | } 499 | }, 500 | "semver": { 501 | "version": "5.7.1", 502 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 503 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 504 | "dev": true 505 | }, 506 | "shebang-command": { 507 | "version": "1.2.0", 508 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 509 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 510 | "dev": true, 511 | "requires": { 512 | "shebang-regex": "^1.0.0" 513 | } 514 | }, 515 | "shebang-regex": { 516 | "version": "1.0.0", 517 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 518 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 519 | "dev": true 520 | }, 521 | "shell-quote": { 522 | "version": "1.7.2", 523 | "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", 524 | "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", 525 | "dev": true 526 | }, 527 | "sinon": { 528 | "version": "7.5.0", 529 | "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", 530 | "integrity": "sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q==", 531 | "dev": true, 532 | "requires": { 533 | "@sinonjs/commons": "^1.4.0", 534 | "@sinonjs/formatio": "^3.2.1", 535 | "@sinonjs/samsam": "^3.3.3", 536 | "diff": "^3.5.0", 537 | "lolex": "^4.2.0", 538 | "nise": "^1.5.2", 539 | "supports-color": "^5.5.0" 540 | } 541 | }, 542 | "spdx-correct": { 543 | "version": "3.1.0", 544 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", 545 | "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", 546 | "dev": true, 547 | "requires": { 548 | "spdx-expression-parse": "^3.0.0", 549 | "spdx-license-ids": "^3.0.0" 550 | } 551 | }, 552 | "spdx-exceptions": { 553 | "version": "2.2.0", 554 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", 555 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", 556 | "dev": true 557 | }, 558 | "spdx-expression-parse": { 559 | "version": "3.0.0", 560 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 561 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 562 | "dev": true, 563 | "requires": { 564 | "spdx-exceptions": "^2.1.0", 565 | "spdx-license-ids": "^3.0.0" 566 | } 567 | }, 568 | "spdx-license-ids": { 569 | "version": "3.0.5", 570 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", 571 | "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", 572 | "dev": true 573 | }, 574 | "string.prototype.padend": { 575 | "version": "3.0.0", 576 | "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz", 577 | "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=", 578 | "dev": true, 579 | "requires": { 580 | "define-properties": "^1.1.2", 581 | "es-abstract": "^1.4.3", 582 | "function-bind": "^1.0.2" 583 | } 584 | }, 585 | "string.prototype.trimleft": { 586 | "version": "2.1.0", 587 | "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", 588 | "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", 589 | "dev": true, 590 | "requires": { 591 | "define-properties": "^1.1.3", 592 | "function-bind": "^1.1.1" 593 | } 594 | }, 595 | "string.prototype.trimright": { 596 | "version": "2.1.0", 597 | "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", 598 | "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", 599 | "dev": true, 600 | "requires": { 601 | "define-properties": "^1.1.3", 602 | "function-bind": "^1.1.1" 603 | } 604 | }, 605 | "strip-bom": { 606 | "version": "3.0.0", 607 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 608 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", 609 | "dev": true 610 | }, 611 | "supports-color": { 612 | "version": "5.5.0", 613 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 614 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 615 | "dev": true, 616 | "requires": { 617 | "has-flag": "^3.0.0" 618 | } 619 | }, 620 | "type-detect": { 621 | "version": "4.0.8", 622 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 623 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 624 | "dev": true 625 | }, 626 | "typescript": { 627 | "version": "3.7.2", 628 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.2.tgz", 629 | "integrity": "sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==", 630 | "dev": true 631 | }, 632 | "validate-npm-package-license": { 633 | "version": "3.0.4", 634 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 635 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 636 | "dev": true, 637 | "requires": { 638 | "spdx-correct": "^3.0.0", 639 | "spdx-expression-parse": "^3.0.0" 640 | } 641 | }, 642 | "watch": { 643 | "version": "1.0.2", 644 | "resolved": "https://registry.npmjs.org/watch/-/watch-1.0.2.tgz", 645 | "integrity": "sha1-NApxe952Vyb6CqB9ch4BR6VR3ww=", 646 | "dev": true, 647 | "requires": { 648 | "exec-sh": "^0.2.0", 649 | "minimist": "^1.2.0" 650 | } 651 | }, 652 | "which": { 653 | "version": "1.3.1", 654 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 655 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 656 | "dev": true, 657 | "requires": { 658 | "isexe": "^2.0.0" 659 | } 660 | } 661 | } 662 | } 663 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "prepare": "tsc", 8 | "test": "node ./.tmp/es2015/test/index.js" 9 | }, 10 | "keywords": [], 11 | "author": "bouzuya (https://bouzuya.net/)", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "beater": "^7.0.0", 15 | "beater-helpers": "^1.0.1", 16 | "npm-run-all": "^4.1.5", 17 | "sinon": "^7.5.0", 18 | "typescript": "^3.7.2", 19 | "watch": "^1.0.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /example/src/function.ts: -------------------------------------------------------------------------------- 1 | const f1 = (): number => 1; 2 | 3 | const f2 = (a: number): number => a; 4 | 5 | const f3 = (a: number, b: number): number => a + b; 6 | 7 | export { 8 | f1, 9 | f2, 10 | f3 11 | }; 12 | -------------------------------------------------------------------------------- /example/src/import-1.ts: -------------------------------------------------------------------------------- 1 | const f1 = (): number => 1; 2 | 3 | export { f1 }; 4 | -------------------------------------------------------------------------------- /example/src/import-2.ts: -------------------------------------------------------------------------------- 1 | class C1 { 2 | m1(): void { 3 | throw new Error('broken'); 4 | } 5 | } 6 | 7 | export { C1 }; 8 | -------------------------------------------------------------------------------- /example/src/import.ts: -------------------------------------------------------------------------------- 1 | import { f1 } from './import-1'; 2 | import { C1 } from './import-2'; 3 | 4 | const import1 = (): number => f1(); 5 | 6 | const import2 = (): void => new C1().m1(); 7 | 8 | export { import1, import2 }; 9 | -------------------------------------------------------------------------------- /example/src/types.ts: -------------------------------------------------------------------------------- 1 | const undefined1 = undefined; 2 | 3 | const null1 = null; 4 | 5 | const boolean1 = true; 6 | 7 | const number1 = 123; 8 | 9 | const string1 = 'abc'; 10 | 11 | const symbol1 = Symbol(1); 12 | 13 | const object1 = { number2: 1, string2: 'a' }; 14 | 15 | export { 16 | undefined1, 17 | null1, 18 | boolean1, 19 | number1, 20 | string1, 21 | symbol1, 22 | object1 23 | }; 24 | -------------------------------------------------------------------------------- /example/test/function.ts: -------------------------------------------------------------------------------- 1 | import { Test, assert, group, test } from './helper'; 2 | import { 3 | f1, 4 | f2, 5 | f3 6 | } from '../src/function'; 7 | 8 | const typesTests: Test[] = group('function/', [ 9 | test('f1', async () => { 10 | assert.deepStrictEqual(f1(), 1); 11 | }), 12 | 13 | test('f2', async () => { 14 | assert.deepStrictEqual(f2(1), 1); 15 | }), 16 | 17 | test('f3', async () => { 18 | assert.deepStrictEqual(f3(1, 2), 3); 19 | }) 20 | 21 | ]); 22 | 23 | export { typesTests as tests }; 24 | -------------------------------------------------------------------------------- /example/test/helper.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { Test, run, test as testOriginal } from 'beater'; 3 | import { SinonSandbox, createSandbox } from 'sinon'; 4 | 5 | type TestContext = { sandbox: SinonSandbox; }; 6 | const group = (name: string, tests: Test[]): Test[] => 7 | tests.map(({ fn, meta }) => test(name + meta.get('name'), fn)); 8 | 9 | const test = ( 10 | name: string, 11 | fn: (context: TestContext) => Promise 12 | ): Test => testOriginal(name, async () => { 13 | const sandbox = createSandbox(); 14 | try { 15 | await fn({ sandbox }); 16 | } finally { 17 | sandbox.restore(); 18 | } 19 | }); 20 | 21 | export { Test, TestContext, assert, group, run, test }; 22 | -------------------------------------------------------------------------------- /example/test/import.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Test, 3 | TestContext as TestContextOriginal, 4 | assert, 5 | group, 6 | test as testOriginal 7 | } from './helper'; 8 | import * as import1Module from '../src/import-1'; 9 | import * as import2Module from '../src/import-2'; 10 | import { import1, import2 } from '../src/import'; 11 | import { SinonStub, SinonStubbedInstance } from 'sinon'; 12 | 13 | type TestContext = { 14 | c1: SinonStubbedInstance; 15 | f1: SinonStub<[], number>; 16 | } & TestContextOriginal; 17 | 18 | const test = ( 19 | name: string, 20 | fn: (context: TestContext) => Promise 21 | ): Test => testOriginal(name, async (context) => { 22 | const { sandbox } = context; 23 | const f1 = sandbox.stub(import1Module, 'f1'); 24 | const c1 = sandbox.createStubInstance(import2Module.C1); 25 | sandbox.stub(import2Module, 'C1').returns(c1); 26 | await fn({ ...context, c1, f1 }); 27 | }); 28 | 29 | const tests: Test[] = group('import/', [ 30 | test('import1', async ({ f1 }) => { 31 | const value = 2; 32 | f1.returns(value); 33 | assert.deepStrictEqual(import1(), value); 34 | assert(f1.callCount === 1); 35 | }), 36 | 37 | test('import2', async ({ c1 }) => { 38 | import2(); 39 | assert(c1.m1.callCount === 1); 40 | }) 41 | ]); 42 | 43 | export { tests as tests }; 44 | -------------------------------------------------------------------------------- /example/test/index.ts: -------------------------------------------------------------------------------- 1 | import { Test, group, run } from './helper'; 2 | import { tests as functionTests } from './function'; 3 | import { tests as importTests } from './import'; 4 | import { tests as typesTests } from './types'; 5 | 6 | const tests: Test[] = group('/', [ 7 | ...functionTests, 8 | ...importTests, 9 | ...typesTests 10 | ]); 11 | 12 | run(tests).catch(() => process.exit(1)); 13 | -------------------------------------------------------------------------------- /example/test/types.ts: -------------------------------------------------------------------------------- 1 | import { Test, assert, group, test } from './helper'; 2 | import { 3 | undefined1, 4 | null1, 5 | boolean1, 6 | number1, 7 | string1, 8 | symbol1, 9 | object1 10 | } from '../src/types'; 11 | 12 | const tests: Test[] = group('types/', [ 13 | test('undefined', async () => { 14 | assert.deepStrictEqual(undefined1, undefined); 15 | }), 16 | 17 | test('null', async () => { 18 | assert.deepStrictEqual(null1, null); 19 | }), 20 | 21 | test('boolean', async () => { 22 | assert.deepStrictEqual(boolean1, true); 23 | }), 24 | 25 | test('number', async () => { 26 | assert.deepStrictEqual(number1, 123); 27 | }), 28 | 29 | test('string', async () => { 30 | assert.deepStrictEqual(string1, 'abc'); 31 | }), 32 | 33 | test('symbol', async () => { 34 | assert.deepStrictEqual(symbol1, symbol1); 35 | }), 36 | 37 | test('object', async () => { 38 | assert.deepStrictEqual(object1, { number2: 1, string2: 'a' }); 39 | }) 40 | ]); 41 | 42 | export { tests }; 43 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "inlineSourceMap": true, 8 | "module": "commonjs", 9 | "moduleResolution": "node", 10 | "noFallthroughCasesInSwitch": true, 11 | "noImplicitReturns": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "outDir": "./.tmp/es2015", 15 | "strict": true, 16 | "target": "es2015" 17 | }, 18 | "files": [ 19 | "./test/index.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /images/beater-wip.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 29 | 31 | 51 | 56 | 62 | 70 | 71 | -------------------------------------------------------------------------------- /images/beater.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 29 | 31 | 51 | 56 | 60 | 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beater", 3 | "description": "beater: bouzuya's easy test runner. it is inspired by eater.", 4 | "version": "9.0.1", 5 | "author": { 6 | "name": "bouzuya", 7 | "email": "m@bouzuya.net", 8 | "url": "https://bouzuya.net/" 9 | }, 10 | "babel": { 11 | "presets": [ 12 | [ 13 | "@babel/preset-env", 14 | { 15 | "targets": { 16 | "node": "10" 17 | } 18 | } 19 | ], 20 | "babel-preset-power-assert" 21 | ] 22 | }, 23 | "bugs": { 24 | "url": "https://github.com/bouzuya/beater/issues" 25 | }, 26 | "dependencies": { 27 | "beater-reporter": "^2.0.2", 28 | "beater-tap-reporter": "^4.0.1" 29 | }, 30 | "devDependencies": { 31 | "@babel/cli": "^7.8.4", 32 | "@babel/core": "^7.8.4", 33 | "@babel/preset-env": "^7.8.4", 34 | "@types/node": "^10.17.19", 35 | "@types/power-assert": "^1.5.1", 36 | "@types/proxyquire": "^1.3.28", 37 | "@types/sinon": "^9.0.0", 38 | "@typescript-eslint/eslint-plugin": "^2.27.0", 39 | "@typescript-eslint/parser": "^2.27.0", 40 | "babel-preset-power-assert": "^3.0.0", 41 | "eslint": "^6.8.0", 42 | "eslint-config-prettier": "^6.9.0", 43 | "npm-run-all": "^4.1.5", 44 | "nyc": "^15.0.1", 45 | "power-assert": "^1.6.1", 46 | "prettier": "^2.0.4", 47 | "proxyquire": "^2.1.3", 48 | "rimraf": "^3.0.1", 49 | "sinon": "^9.0.2", 50 | "typescript": "^3.7.3", 51 | "watch": "^1.0.2" 52 | }, 53 | "files": [ 54 | "lib" 55 | ], 56 | "homepage": "https://github.com/bouzuya/beater", 57 | "keywords": [ 58 | "beater", 59 | "bouzuya", 60 | "eater", 61 | "runner", 62 | "test", 63 | "testing" 64 | ], 65 | "license": "MIT", 66 | "main": "lib/index.js", 67 | "repository": { 68 | "type": "git", 69 | "url": "https://github.com/bouzuya/beater.git" 70 | }, 71 | "scripts": { 72 | "build": "npm-run-all -s build:format build:lint build:es2015 build:es5 build:lib", 73 | "build:es2015": "tsc", 74 | "build:es5": "babel --out-dir .tmp/es5/ --source-maps inline .tmp/es2015/", 75 | "build:format": "prettier --check '{src,test}/**/*.ts'", 76 | "build:lib": "babel --copy-files --no-comments --out-dir lib/ .tmp/es2015/src/", 77 | "build:lint": "eslint '{src,test}/**/*.ts'", 78 | "clean": "rimraf .nyc_output .tmp coverage lib", 79 | "format": "prettier --write '{src,test}/**/*.ts'", 80 | "prepare": "npm-run-all -s \"clean\" \"build\"", 81 | "test": "nyc --exclude .tmp/es5/test --source-map false node .tmp/es5/test/index.js", 82 | "watch": "npm-run-all -p \"watch:*\"", 83 | "watch:es2015": "npm run build:es2015 -- --watch", 84 | "watch:es5": "npm run build:es5 -- --watch", 85 | "watch:lib": "watch --wait 2 'npm run build:lib' '.tmp/es5/src/'", 86 | "watch:test": "watch --wait 2 'npm test' '.tmp/es5/src' '.tmp/es5/test'" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Error, Test, TestReporter, TestResult } from "beater-reporter"; 2 | import { run } from "./run"; 3 | import { runWithOptions } from "./run-with-options"; 4 | import { Run } from "./type/run"; 5 | import { RunOptions } from "./type/run-options"; 6 | import { RunWithOptions } from "./type/run-with-options"; 7 | 8 | export { 9 | Error, 10 | Run, 11 | RunOptions, 12 | RunWithOptions, 13 | Test, 14 | TestReporter, 15 | TestResult, 16 | run, 17 | runWithOptions, 18 | }; 19 | -------------------------------------------------------------------------------- /src/run-with-options.ts: -------------------------------------------------------------------------------- 1 | import { Error, Test, TestResult } from "beater-reporter"; 2 | import { Run } from "./type/run"; 3 | import { RunOptions } from "./type/run-options"; 4 | import { RunWithOptions } from "./type/run-with-options"; 5 | 6 | interface RunTest { 7 | (test: Test): Promise; 8 | } 9 | 10 | const parseStack = ( 11 | stack: string | null 12 | ): { 13 | columnNumber: number; 14 | fileName: string; 15 | lineNumber: number; 16 | } | null => { 17 | if (stack === null) return null; 18 | const line = stack.split("\n")[1]; 19 | if (typeof line === "undefined") return null; 20 | const match = line.match(/\(?([^:]+):(\d+):(\d+)\)?$/); 21 | if (match === null) return null; 22 | const fileName = match[1]; 23 | const lineNumber = parseInt(match[2], 10); 24 | const columnNumber = parseInt(match[3], 10); 25 | return { columnNumber, fileName, lineNumber }; 26 | }; 27 | 28 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 29 | const error = (e: any): Error => { 30 | if (typeof e === "object") { 31 | const name: string = typeof e.name === "string" ? e.name : "Error"; 32 | const message: string = typeof e.message === "string" ? e.message : ""; 33 | const stack = typeof e.stack === "string" ? e.stack : null; 34 | const parsedStack = parseStack(stack); 35 | return { 36 | columnNumber: 37 | typeof e.columnNumber === "number" 38 | ? e.columnNumber 39 | : parsedStack?.columnNumber ?? null, 40 | fileName: 41 | typeof e.fileName === "string" 42 | ? e.fileName 43 | : parsedStack?.fileName ?? null, 44 | lineNumber: 45 | typeof e.lineNumber === "number" 46 | ? e.lineNumber 47 | : parsedStack?.lineNumber ?? null, 48 | message, 49 | name, 50 | stack, 51 | }; 52 | } else { 53 | const name = "Error"; 54 | const message = String(e); 55 | return { 56 | columnNumber: null, 57 | lineNumber: null, 58 | fileName: null, 59 | message, 60 | name, 61 | stack: null, 62 | }; 63 | } 64 | }; 65 | 66 | const callTestFn = async (test: Test): Promise => { 67 | try { 68 | await test(); 69 | return { test }; 70 | } catch (e) { 71 | return { error: error(e), test }; 72 | } 73 | }; 74 | 75 | const runTestWithOptions = (options: RunOptions): RunTest => { 76 | return async (test: Test): Promise => { 77 | const { reporter } = options; 78 | reporter.testStarted(test); 79 | const result = await callTestFn(test); 80 | reporter.testFinished(result); 81 | return result; 82 | }; 83 | }; 84 | 85 | const runWithOptions: RunWithOptions = (options: RunOptions): Run => { 86 | const runTest = runTestWithOptions(options); 87 | return async (tests: Test[]): Promise => { 88 | const { reporter } = options; 89 | reporter.started(tests); 90 | const results: TestResult[] = []; // mutable 91 | await tests.reduce(async (promise, test) => { 92 | await promise; 93 | const result = await runTest(test); 94 | results.push(result); 95 | }, Promise.resolve()); 96 | reporter.finished(results); 97 | return results.some(({ error }) => typeof error !== "undefined") 98 | ? Promise.reject(results) 99 | : Promise.resolve(results); 100 | }; 101 | }; 102 | 103 | export { runWithOptions }; 104 | -------------------------------------------------------------------------------- /src/run.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestResult } from "beater-reporter"; 2 | import { reporter as tapReporter } from "beater-tap-reporter"; 3 | import { Run } from "./type/run"; 4 | import { runWithOptions } from "./run-with-options"; 5 | 6 | const run: Run = (tests: Test[]): Promise => { 7 | const defaultOptions = { reporter: tapReporter() }; 8 | return runWithOptions(defaultOptions)(tests); 9 | }; 10 | 11 | export { run }; 12 | -------------------------------------------------------------------------------- /src/type/run-options.ts: -------------------------------------------------------------------------------- 1 | import { TestReporter } from "beater-reporter"; 2 | 3 | export interface RunOptions { 4 | reporter: TestReporter; 5 | } 6 | -------------------------------------------------------------------------------- /src/type/run-with-options.ts: -------------------------------------------------------------------------------- 1 | import { Run } from "./run"; 2 | import { RunOptions } from "./run-options"; 3 | 4 | export interface RunWithOptions { 5 | (options: RunOptions): Run; 6 | } 7 | -------------------------------------------------------------------------------- /src/type/run.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestResult } from "beater-reporter"; 2 | 3 | export interface Run { 4 | (tests: Test[]): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /test/concurrent.ts: -------------------------------------------------------------------------------- 1 | import assert from "power-assert"; 2 | import { Test, runWithOptions, TestResult } from "../src"; 3 | import * as fModule from "./concurrent/f"; 4 | import { g } from "./concurrent/g"; 5 | import { sandboxed, test } from "./helper"; 6 | 7 | const category = "concurrent - "; 8 | const tests: Test[] = [ 9 | test(category + "concurrent problem", async () => { 10 | return new Promise((resolve, reject) => { 11 | runWithOptions({ 12 | reporter: { 13 | finished(results: TestResult[]): void { 14 | const errorResult = results.find( 15 | ({ error }) => typeof error !== "undefined" 16 | ); 17 | if (typeof errorResult === "undefined") { 18 | resolve(); 19 | } else { 20 | reject(errorResult.error); 21 | } 22 | }, 23 | started(_tests: Test[]): void { 24 | // do nothing 25 | }, 26 | testFinished(_result: TestResult): void { 27 | // do nothing 28 | }, 29 | testStarted(_test: Test): void { 30 | // do nothing 31 | }, 32 | }, 33 | })([ 34 | test( 35 | "1", 36 | sandboxed(async ({ sandbox }) => { 37 | const f = sandbox.stub(fModule, "f"); 38 | await new Promise((resolve) => setTimeout(resolve, 0)); 39 | g(); 40 | assert(f.callCount === 1); 41 | }) 42 | ), 43 | test( 44 | "2", 45 | sandboxed(async ({ sandbox }) => { 46 | const f = sandbox.stub(fModule, "f"); 47 | await new Promise((resolve) => setTimeout(resolve, 0)); 48 | g(); 49 | assert(f.callCount === 1); 50 | }) 51 | ), 52 | ]); 53 | }); 54 | }), 55 | ]; 56 | 57 | export { tests }; 58 | -------------------------------------------------------------------------------- /test/concurrent/f.ts: -------------------------------------------------------------------------------- 1 | const f = (s: string): void => console.log(s); 2 | 3 | export { f }; 4 | -------------------------------------------------------------------------------- /test/concurrent/g.ts: -------------------------------------------------------------------------------- 1 | import { f } from "./f"; 2 | 3 | const g = (): void => f("hello"); 4 | 5 | export { g }; 6 | -------------------------------------------------------------------------------- /test/example.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/camelcase */ 2 | /* eslint-disable @typescript-eslint/explicit-function-return-type */ 3 | import assert from "power-assert"; 4 | 5 | const test1 = function simple_test() { 6 | assert(1 === 1); 7 | }; 8 | 9 | const test2 = function promise_test() { 10 | return new Promise((resolve) => { 11 | assert(1 === 1); 12 | resolve(); 13 | }); 14 | }; 15 | 16 | const test3 = async function async_fn_test() { 17 | await new Promise((resolve) => setTimeout(resolve, 0)); 18 | assert(1 === 1); 19 | }; 20 | 21 | const exampleTests = [test1, test2, test3]; 22 | 23 | export { exampleTests as tests }; 24 | -------------------------------------------------------------------------------- /test/helper/index.ts: -------------------------------------------------------------------------------- 1 | import { sandboxedTest } from "./sandboxed-test"; 2 | import { slowTest, slowTestFilter } from "./slow-test"; 3 | import { namedTest } from "./named-test"; 4 | 5 | const test = namedTest; 6 | 7 | export { 8 | test as named, 9 | sandboxedTest as sandboxed, 10 | slowTest as slow, 11 | slowTestFilter, 12 | test, 13 | }; 14 | -------------------------------------------------------------------------------- /test/helper/named-test.ts: -------------------------------------------------------------------------------- 1 | import { Test } from "../../src"; 2 | 3 | const namedTest = (name: string, fn: Test): Test => { 4 | Object.defineProperty(fn, "name", { 5 | configurable: false, 6 | enumerable: true, 7 | value: name, 8 | writable: false, 9 | }); 10 | return fn; 11 | }; 12 | 13 | export { namedTest }; 14 | -------------------------------------------------------------------------------- /test/helper/sandboxed-test.ts: -------------------------------------------------------------------------------- 1 | import sinon from "sinon"; 2 | import { Test } from "../../src"; 3 | 4 | const sandboxedTest = ( 5 | test: (context: { sandbox: sinon.SinonSandbox }) => void 6 | ): Test => { 7 | return async (): Promise => { 8 | const sandbox = sinon.createSandbox(); 9 | try { 10 | return await test({ sandbox }); 11 | } finally { 12 | sandbox.restore(); 13 | } 14 | }; 15 | }; 16 | 17 | export { sandboxedTest }; 18 | -------------------------------------------------------------------------------- /test/helper/slow-test.ts: -------------------------------------------------------------------------------- 1 | import { Test } from "../../src"; 2 | 3 | const slowTestKey = Symbol(); 4 | 5 | const slowTest = (test: Test): Test => { 6 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 7 | Object.defineProperty(test as any, slowTestKey, { 8 | configurable: false, 9 | enumerable: false, 10 | value: true, 11 | writable: false, 12 | }); 13 | return test; 14 | }; 15 | 16 | const slowTestFilter = (runSlowTests: boolean): ((test: Test) => boolean) => 17 | runSlowTests 18 | ? (_): boolean => true 19 | : // eslint-disable-next-line @typescript-eslint/no-explicit-any 20 | (test): boolean => (test as any)[slowTestKey] !== true; 21 | 22 | export { slowTest, slowTestFilter }; 23 | -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | import { run, Test } from "../src"; 2 | import { tests as concurrentTests } from "./concurrent"; 3 | import { tests as exampleTests } from "./example"; 4 | import { tests as manyTests } from "./many-test"; 5 | import { tests as runTests } from "./run"; 6 | import { tests as runWithOptionsTests } from "./run-with-options"; 7 | import { tests as testTests } from "./test"; 8 | import { slowTestFilter } from "./helper"; 9 | 10 | const tests: Test[] = [ 11 | ...concurrentTests, 12 | ...exampleTests, 13 | ...manyTests, 14 | ...runTests, 15 | ...runWithOptionsTests, 16 | ...testTests, 17 | ].filter(slowTestFilter(typeof process.env.RUN_SLOW_TEST !== "undefined")); 18 | 19 | run(tests).catch((_) => process.exit(1)); 20 | -------------------------------------------------------------------------------- /test/many-test.ts: -------------------------------------------------------------------------------- 1 | import assert from "power-assert"; 2 | import { Test } from "../src"; 3 | import { named, slow } from "./helper"; 4 | 5 | const tests: Test[] = []; // mutable 6 | for (let i = 0; i < 100000; i++) { 7 | tests.push(slow(named(`test${i + 1}`, () => assert(1 === 1)))); 8 | } 9 | 10 | export { tests }; 11 | -------------------------------------------------------------------------------- /test/run-with-options.ts: -------------------------------------------------------------------------------- 1 | import assert from "power-assert"; 2 | import sinon from "sinon"; 3 | import { Test, TestReporter, runWithOptions } from "../src"; 4 | import { test } from "./helper"; 5 | 6 | const reporterStub = (): TestReporter & { 7 | finished: sinon.SinonStub; 8 | started: sinon.SinonStub; 9 | testFinished: sinon.SinonStub; 10 | testStarted: sinon.SinonStub; 11 | } => { 12 | return { 13 | finished: sinon.stub(), 14 | started: sinon.stub(), 15 | testFinished: sinon.stub(), 16 | testStarted: sinon.stub(), 17 | }; 18 | }; 19 | 20 | const category1 = "runWithOptions() - "; 21 | const tests: Test[] = [ 22 | test(category1 + "no tests", () => { 23 | const reporter = reporterStub(); 24 | return runWithOptions({ reporter })([]).then((results) => { 25 | assert(results.length === 0); 26 | assert(reporter.finished.callCount === 1); 27 | assert(reporter.finished.getCall(0).args.length === 1); 28 | assert(reporter.finished.getCall(0).args[0].length === 0); // [] 29 | assert(reporter.started.callCount === 1); 30 | assert(reporter.started.getCall(0).args.length === 1); 31 | assert(reporter.started.getCall(0).args[0].length === 0); // [] 32 | assert(reporter.testFinished.callCount === 0); 33 | assert(reporter.testStarted.callCount === 0); 34 | }); 35 | }), 36 | test(category1 + "1 test", () => { 37 | const reporter = reporterStub(); 38 | const test1 = test("name1", () => void 0); 39 | return runWithOptions({ reporter })([test1]).then((results) => { 40 | assert(results.length === 1); 41 | assert(reporter.finished.callCount === 1); 42 | assert(reporter.finished.getCall(0).args.length === 1); 43 | assert(reporter.finished.getCall(0).args[0].length === 1); // [result1] 44 | assert(reporter.finished.getCall(0).args[0][0].test === test1); 45 | assert(reporter.finished.getCall(0).args[0][0].error === void 0); 46 | assert(reporter.started.callCount === 1); 47 | assert(reporter.started.getCall(0).args.length === 1); 48 | assert(reporter.started.getCall(0).args[0].length === 1); // [test1] 49 | assert(reporter.started.getCall(0).args[0][0] === test1); 50 | assert(reporter.testFinished.callCount === 1); 51 | assert(reporter.testFinished.getCall(0).args.length === 1); // result1 52 | assert(reporter.testFinished.getCall(0).args[0].test === test1); 53 | assert(reporter.testFinished.getCall(0).args[0].error === void 0); 54 | assert(reporter.testStarted.callCount === 1); 55 | assert(reporter.testStarted.getCall(0).args.length === 1); // test1 56 | assert(reporter.testStarted.getCall(0).args[0] === test1); 57 | }); 58 | }), 59 | test(category1 + "2 tests", () => { 60 | const reporter = reporterStub(); 61 | const fn1 = sinon.stub(); 62 | const fn2 = sinon.stub(); 63 | const test1 = test("name1", fn1); 64 | const test2 = test("name2", fn2); 65 | return runWithOptions({ reporter })([test1, test2]).then((results) => { 66 | assert(results.length === 2); 67 | assert(results[0].test === test1); // .test is private 68 | assert(results[1].test === test2); // .test is private 69 | assert(typeof results[0].error === "undefined"); // .error is private 70 | assert(typeof results[1].error === "undefined"); // .error is private 71 | assert(fn1.callCount === 1); 72 | assert(fn1.getCall(0).args.length === 0); 73 | assert(fn2.callCount === 1); 74 | assert(fn2.getCall(0).args.length === 0); 75 | }); 76 | }), 77 | test(category1 + "failure", () => { 78 | const reporter = reporterStub(); 79 | const fn1 = sinon.stub().throws(new Error("hoge")); 80 | const test1 = test("name1", fn1); 81 | return runWithOptions({ reporter })([test1]).then( 82 | () => assert.fail(), 83 | (results) => { 84 | assert(results.length === 1); 85 | assert(results[0].test === test1); // .test is private 86 | assert(typeof results[0].error !== "undefined"); // .error is private 87 | assert(results[0].error.name === "Error"); 88 | assert(results[0].error.message === "hoge"); 89 | assert(results[0].error.stack.length > 0); 90 | assert(fn1.callCount === 1); 91 | assert(fn1.getCall(0).args.length === 0); 92 | } 93 | ); 94 | }), 95 | test(category1 + "failure (not Error)", () => { 96 | const reporter = reporterStub(); 97 | const fn1 = sinon.stub().throws(123); 98 | const test1 = test("name1", fn1); 99 | return runWithOptions({ reporter })([test1]).then( 100 | () => assert.fail(), 101 | (results) => { 102 | assert(results.length === 1); 103 | assert(results[0].test === test1); // .test is private 104 | assert(typeof results[0].error !== "undefined"); // .error is private 105 | assert(fn1.callCount === 1); 106 | assert(fn1.getCall(0).args.length === 0); 107 | } 108 | ); 109 | }), 110 | 111 | test(category1 + "error (number)", () => { 112 | const reporter = reporterStub(); 113 | const fn1 = sinon.stub().throws(123); 114 | const test1 = test("name1", fn1); 115 | return runWithOptions({ reporter })([test1]).then( 116 | () => assert.fail(), 117 | (results) => { 118 | const { error } = results[0]; 119 | assert(error.columnNumber === null); 120 | assert(error.fileName === null); 121 | assert(error.lineNumber === null); 122 | assert(error.name === "Error"); 123 | assert(error.message === "123"); 124 | assert(error.stack === null); 125 | } 126 | ); 127 | }), 128 | 129 | test(category1 + "error (object && not Error)", () => { 130 | const reporter = reporterStub(); 131 | const fn1 = sinon.stub().throws({ 132 | columnNumber: 123, 133 | fileName: "foo.js", 134 | lineNumber: 456, 135 | }); 136 | const test1 = test("name1", fn1); 137 | return runWithOptions({ reporter })([test1]).then( 138 | () => assert.fail(), 139 | (results) => { 140 | const { error } = results[0]; 141 | assert(error.columnNumber === 123); 142 | assert(error.fileName === "foo.js"); 143 | assert(error.lineNumber === 456); 144 | assert(error.name === "Error"); 145 | assert(error.message === ""); 146 | assert(error.stack === null); 147 | } 148 | ); 149 | }), 150 | 151 | test(category1 + "error (Error)", () => { 152 | const reporter = reporterStub(); 153 | const fn1 = sinon.stub().throws(new Error("message1")); 154 | const test1 = test("name1", fn1); 155 | return runWithOptions({ reporter })([test1]).then( 156 | () => assert.fail(), 157 | (results) => { 158 | const { error } = results[0]; 159 | assert(typeof error.columnNumber === "number"); 160 | assert(typeof error.fileName === "string"); 161 | assert(typeof error.lineNumber === "number"); 162 | assert(error.name === "Error"); 163 | assert(error.message === "message1"); 164 | assert(error.stack.match(/^Error: message1\n {4}at/) !== null); 165 | } 166 | ); 167 | }), 168 | ]; 169 | 170 | export { tests }; 171 | -------------------------------------------------------------------------------- /test/run.ts: -------------------------------------------------------------------------------- 1 | import * as beaterTapReporter from "beater-tap-reporter"; 2 | import assert from "power-assert"; 3 | import sinon from "sinon"; 4 | import { Test, TestResult } from "../src"; 5 | import { run } from "../src/run"; 6 | import * as runWithOptions from "../src/run-with-options"; 7 | import { test } from "./helper"; 8 | 9 | const sandboxFixture = ( 10 | fn: (sandbox: sinon.SinonSandbox) => Promise 11 | ) => async (): Promise => { 12 | const sandbox = sinon.createSandbox(); 13 | try { 14 | await fn(sandbox); 15 | } finally { 16 | sandbox.restore(); 17 | } 18 | }; 19 | 20 | const category = "run() - "; 21 | const tests: Test[] = [ 22 | test( 23 | category + "call runWithOptions", 24 | sandboxFixture(async (sandbox) => { 25 | const results: TestResult[] = []; 26 | const reporter = { 27 | finished(): void { 28 | // do nothing 29 | }, 30 | started(): void { 31 | // do nothing 32 | }, 33 | testFinished(): void { 34 | // do nothing 35 | }, 36 | testStarted(): void { 37 | // do nothing 38 | }, 39 | }; 40 | const reporterStub = sandbox 41 | .stub(beaterTapReporter, "reporter") 42 | .returns(reporter); 43 | const runWithOptionsStub = sandbox 44 | .stub(runWithOptions, "runWithOptions") 45 | .returns(() => Promise.resolve(results)); 46 | 47 | assert.deepStrictEqual(await run([]), results); 48 | 49 | assert(runWithOptionsStub.callCount === 1); 50 | assert.deepStrictEqual(runWithOptionsStub.getCall(0).args, [ 51 | { reporter }, 52 | ]); 53 | assert(reporterStub.callCount === 1); 54 | }) 55 | ), 56 | ]; 57 | 58 | export { tests }; 59 | -------------------------------------------------------------------------------- /test/test.ts: -------------------------------------------------------------------------------- 1 | import assert from "power-assert"; 2 | import sinon from "sinon"; 3 | import { Test } from "../src"; 4 | import { test } from "./helper"; 5 | 6 | const category = "test() - "; 7 | const tests: Test[] = [ 8 | test(category + "name", () => { 9 | const name = "name1"; 10 | const o = test(name, () => void 0); 11 | assert(o.name === "name1"); 12 | }), 13 | test(category + "fn failure", () => { 14 | const error = new Error("hoge"); 15 | const fn = sinon.stub().throws(error); 16 | const o = test("", fn); 17 | return Promise.resolve() 18 | .then(() => o()) 19 | .then( 20 | () => assert.fail(), 21 | (e) => assert(e === error) 22 | ); 23 | }), 24 | test(category + "fn failure (not Error)", () => { 25 | const error = 123; 26 | const fn = sinon.stub().throws(error); 27 | const o = test("", fn); 28 | return Promise.resolve() 29 | .then(() => o()) 30 | .then( 31 | () => assert.fail(), 32 | (e) => assert(e === 123) 33 | ); 34 | }), 35 | test(category + "fn success", () => { 36 | const fn = sinon.stub().returns(void 0); 37 | const o = test("", fn); 38 | return Promise.resolve() 39 | .then(() => o()) 40 | .then(() => { 41 | assert(fn.callCount === 1); 42 | assert(fn.getCall(0).args.length === 0); 43 | }); 44 | }), 45 | test(category + "fn promise failure", () => { 46 | const error = new Error("hoge"); 47 | const fn = sinon.stub().returns(Promise.reject(error)); 48 | const o = test("", fn); 49 | return Promise.resolve() 50 | .then(() => o()) 51 | .then( 52 | () => assert.fail(), 53 | (e) => assert(e === error) 54 | ); 55 | }), 56 | test(category + "fn promise success", () => { 57 | const fn = sinon.stub().returns(Promise.resolve()); 58 | const o = test("", fn); 59 | return Promise.resolve() 60 | .then(() => o()) 61 | .then(() => { 62 | assert(fn.callCount === 1); 63 | assert(fn.getCall(0).args.length === 0); 64 | }); 65 | }), 66 | ]; 67 | 68 | export { tests }; 69 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "esModuleInterop": true, 6 | "inlineSourceMap": true, 7 | "module": "es2015", 8 | "moduleResolution": "node", 9 | "noFallthroughCasesInSwitch": true, 10 | "noImplicitAny": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "outDir": "./.tmp/es2015/", 16 | "removeComments": true, 17 | "rootDir": "./", 18 | "sourceMap": false, 19 | "strict": true, 20 | "target": "es2015" 21 | }, 22 | "files": [ 23 | "./src/index.ts", 24 | "./test/index.ts" 25 | ] 26 | } 27 | --------------------------------------------------------------------------------