├── .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 |
71 |
--------------------------------------------------------------------------------
/images/beater.svg:
--------------------------------------------------------------------------------
1 |
2 |
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 |
--------------------------------------------------------------------------------