├── .gitignore ├── package.json ├── src ├── index.js └── replacements.js ├── bin └── should-up ├── test └── should-up.js ├── README.md ├── yarn.lock └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "should-up", 3 | "version": "1.0.0", 4 | "description": "De-clutter your unit test descriptions", 5 | "author": "Henry Smith ", 6 | "license": "Apache 2.0", 7 | "keywords": [ 8 | "tdd", 9 | "cleanup" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/spotify/should-up" 14 | }, 15 | "bin": { 16 | "should-up": "bin/should-up" 17 | }, 18 | "dependencies": { 19 | "command-line-args": "^4.0.2", 20 | "recursive-readdir-sync": "^1.0.6" 21 | }, 22 | "devDependencies": { 23 | "expect.js": "^0.3.1", 24 | "mocha": "^3.3.0" 25 | }, 26 | "scripts": { 27 | "test": "mocha test/*" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Spotify AB. All rights reserved. 2 | // 3 | // The contents of this file are licensed under the Apache License, Version 2.0 4 | // (the "License"); you may not use this file except in compliance with the 5 | // License. You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations under 13 | // the License. 14 | 15 | const replacements = require('./replacements'); 16 | 17 | function convert(input) { 18 | let output = input; 19 | 20 | output = output.replace(/^([ ]*it\(['"])(.*)$/gm, (match, it, description) => { 21 | 22 | Object.keys(replacements).forEach(from => { 23 | const to = replacements[from]; 24 | from = new RegExp(`\\b${from}\\b`); 25 | description = description.replace(from, to); 26 | }); 27 | 28 | return it + description; 29 | }); 30 | 31 | 32 | return output; 33 | } 34 | 35 | module.exports = convert; 36 | -------------------------------------------------------------------------------- /bin/should-up: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Copyright 2017 Spotify AB. All rights reserved. 4 | // 5 | // The contents of this file are licensed under the Apache License, Version 2.0 6 | // (the "License"); you may not use this file except in compliance with the 7 | // License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | // License for the specific language governing permissions and limitations under 15 | // the License. 16 | 17 | const path = require('path'); 18 | const fs = require('fs'); 19 | const src = path.join(path.dirname(fs.realpathSync(__filename)), '../src'); 20 | const shouldUp = require(src); 21 | 22 | const commandLineArgs = require('command-line-args'); 23 | const options = commandLineArgs([ 24 | { name: 'targets', type: String, multiple: true, defaultOption: true }, 25 | ]); 26 | const targets = []; 27 | 28 | const scan = require('recursive-readdir-sync') 29 | options.targets.forEach(target => { 30 | if (fs.statSync(target).isDirectory()) { 31 | targets.push.apply(targets, scan(target)); 32 | } else { 33 | targets.push(target); 34 | } 35 | }); 36 | 37 | targets.forEach(target => { 38 | const input = fs.readFileSync(target, 'utf-8'); 39 | const output = shouldUp(input); 40 | 41 | if (input !== output) { 42 | fs.writeFileSync(target, output, 'utf-8'); 43 | } 44 | }); 45 | -------------------------------------------------------------------------------- /test/should-up.js: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Spotify AB. All rights reserved. 2 | // 3 | // The contents of this file are licensed under the Apache License, Version 2.0 4 | // (the "License"); you may not use this file except in compliance with the 5 | // License. You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations under 13 | // the License. 14 | 15 | const expect = require('expect.js'); 16 | const shouldUp = require('../src'); 17 | 18 | describe('should-up', () => { 19 | it('removes the "should" prefix', () => { 20 | const input = ` 21 | it('should clear the cache', function () { 22 | expect(cache.empty).to.be(true); 23 | }) 24 | `; 25 | 26 | const expected = ` 27 | it('clears the cache', function () { 28 | expect(cache.empty).to.be(true); 29 | }) 30 | `; 31 | 32 | expect(shouldUp(input)).to.be(expected); 33 | }); 34 | 35 | // In this example "should download" would match the "should do" replacement 36 | // unless the regex ignores non-word boundary matches. This is important, 37 | // since if this wasn't handled correctly it'd get replaced with something 38 | // like "it('doeswnload the file')" and that would be very bad. 39 | it('ignores matches that are not on word boundaries', () => { 40 | const input = ` 41 | it('should download the file', function () { 42 | expect(download.complete).to.be(true); 43 | }); 44 | 45 | it('should do another thing', function () { 46 | expect(download.complete).to.be(true); 47 | }); 48 | `; 49 | 50 | const expected = ` 51 | it('should download the file', function () { 52 | expect(download.complete).to.be(true); 53 | }); 54 | 55 | it('does another thing', function () { 56 | expect(download.complete).to.be(true); 57 | }); 58 | `; 59 | 60 | expect(shouldUp(input)).to.be(expected); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Should Up 2 | ========= 3 | 4 | Remove all those "should" prefixes cluttering up your JS unit test descriptions! 5 | 6 | When you run `should-up`, it'll go through all the files in the directory you specify, cleaning up as many test descriptions as it can. Afterwards, the changes should look something like this. 7 | 8 | 9 | 10 | 11 | 12 | 22 | 23 | 31 | 32 | 33 | 34 |
BeforeAfter
13 | 14 |
describe('TodoApp', () => {
 15 |   it('should store reminders in local storage');
 16 |   it('should set reminders as done when clicked');
 17 |   it('should sync with the cloud');
 18 | });
 19 | 
20 | 21 |
24 |
describe('TodoApp', () => {
 25 |   it('stores reminders in local storage');
 26 |   it('sets reminders as done when clicked');
 27 |   it('syncs with the cloud');
 28 | });
 29 | 
30 |
35 | 36 | Installation 37 | ------------ 38 | 39 | ```bash 40 | npm install -g should-up 41 | ``` 42 | 43 | Usage 44 | ----- 45 | 46 | ``` 47 | should-up /path/to/your/tests 48 | ``` 49 | 50 | Rationale 51 | --------- 52 | 53 | As you can see from the following highly scientific table of data, there's a pretty strong negative correlation between the readability of your test descriptions and the amount of meaningless filler in them. 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 73 | 74 | 75 | 76 | 77 | 78 | 86 | 87 | 88 | 89 | 90 | 99 | 100 | 101 | 102 | 103 | 111 | 112 | 113 | 114 |
ReadabilityAmount of meaningless filler
Total Disaster
⭐️
66 |
Widget
 67 |   ✓ should always correctly contain a button whenever appropriate
 68 |   ✓ should always correctly dispatch an event on click whenever appropriate
 69 |   ✓ should always correctly show the name of the user whenever appropriate
 70 |   ✓ should always correctly show the type of widget  whenever appropriate
 71 | 
72 |
Still Very Bad
⭐️⭐️
79 |
Widget
 80 |   ✓ should always correctly contain a button
 81 |   ✓ should always correctly dispatch an event on click
 82 |   ✓ should always correctly show the name of the user
 83 |   ✓ should always correctly show the type of widget
 84 | 
85 |
You Are Here →
⭐️⭐️⭐️⭐️
91 |

 92 | Widget
 93 |   ✓ should contain a button
 94 |   ✓ should dispatch an event on click
 95 |   ✓ should show the name of the user
 96 |   ✓ should show the type of widget
 97 | 
98 |
All Killer No Filler
⭐️⭐️⭐️⭐️⭐️
104 |
Widget
105 |   ✓ contains a button
106 |   ✓ dispatches an event on click
107 |   ✓ shows the name of the user
108 |   ✓ shows the type of widget
109 | 
110 |
115 | 116 | Anyway, when we start everything with the word "should", it sounds like we're not really 100% sure about what our code does. 117 | Like maybe we're even a little bit pessimistic that it works at all, but we're crossing our fingers and hoping for the best anyway. 118 | 119 | > Well... it _should_ dispatch an event on click, but I dunno. 120 | > Just... look, you can run it if you want, but don't come to me if it breaks is all I'm saying. 121 | 122 | When you say something like `it('dispatches an event on click')` instead, it's literally a more enjoyable thing to write. 123 | It makes you feel like you're more certain about what your code's doing, and also more proud of your concise new unit test descriptions. 124 | 125 | Contributing 126 | ------------ 127 | 128 | This project adheres to the [Open Code of Conduct][code-of-conduct]. 129 | By participating, you are expected to honor this code. 130 | 131 | License 132 | ------- 133 | 134 | [Apache 2.0](LICENSE) 135 | 136 | [code-of-conduct]: https://github.com/spotify/code-of-conduct/blob/master/code-of-conduct.md 137 | -------------------------------------------------------------------------------- /src/replacements.js: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Spotify AB. All rights reserved. 2 | // 3 | // The contents of this file are licensed under the Apache License, Version 2.0 4 | // (the "License"); you may not use this file except in compliance with the 5 | // License. You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations under 13 | // the License. 14 | 15 | module.exports = { 16 | "should accept": "accepts", 17 | "should accumulate": "accumulates", 18 | "should activate": "activates", 19 | "should add": "adds", 20 | "should advance": "advances", 21 | "should allow": "allows", 22 | "should allow": "allows", 23 | "should alter": "alters", 24 | "should append": "appends", 25 | "should apply": "applies", 26 | "should assess": "assesses", 27 | "should assign": "assigns", 28 | "should attach": "attaches", 29 | "should batch": "batches", 30 | "should be": "is", 31 | "should bind": "binds", 32 | "should broadcast": "broadcasts", 33 | "should calculate": "calculates", 34 | "should call": "calls", 35 | "should call": "calls", 36 | "should cancel": "cancels", 37 | "should change": "changes", 38 | "should check": "checks", 39 | "should chunk": "chunks", 40 | "should clean": "cleans", 41 | "should clear": "clears", 42 | "should clone": "clones", 43 | "should close": "closes", 44 | "should compute": "computes", 45 | "should construct": "constructs", 46 | "should contain": "contains", 47 | "should convert": "converts", 48 | "should correctly create": "creates", 49 | "should correctly extract": "extracts", 50 | "should correctly handle": "handles", 51 | "should correctly remove": "removes", 52 | "should correctly report": "reports", 53 | "should create": "creates", 54 | "should deal": "deals", 55 | "should decrease": "decreases", 56 | "should dedupe": "dedupes", 57 | "should default": "defaults", 58 | "should delegate": "delegates", 59 | "should delete": "deletes", 60 | "should deregister": "deregisters", 61 | "should destroy": "destroys", 62 | "should detach": "detaches", 63 | "should detect": "detects", 64 | "should diff": "diffs", 65 | "should disable": "disables", 66 | "should discard": "discards", 67 | "should dispatch": "dispatches", 68 | "should display": "displays", 69 | "should disregard": "disregards", 70 | "should do": "does", 71 | "should drop": "drops", 72 | "should emit": "emits", 73 | "should enable": "enables", 74 | "should encode": "encodes", 75 | "should ensure": "ensures", 76 | "should error": "errors", 77 | "should execute": "executes", 78 | "should exit": "exits", 79 | "should expand": "expands", 80 | "should export": "exports", 81 | "should expose": "exposes", 82 | "should fail": "fails", 83 | "should fallback": "falls back", 84 | "should fetch": "fetches", 85 | "should filter": "filters", 86 | "should find": "finds", 87 | "should finish": "finishes", 88 | "should fire": "fires", 89 | "should focus": "focuses", 90 | "should follow": "follows", 91 | "should format": "formats", 92 | "should get": "gets", 93 | "should give": "gives", 94 | "should go": "goes", 95 | "should handle": "handles", 96 | "should have": "has", 97 | "should have": "has", 98 | "should hide": "hides", 99 | "should highlight": "highlights", 100 | "should ignore": "ignores", 101 | "should implement": "implements", 102 | "should include": "includes", 103 | "should increase": "increases", 104 | "should increment": "increments", 105 | "should initialize": "initializes", 106 | "should inject": "injects", 107 | "should insert": "inserts", 108 | "should instantiate": "instantiates", 109 | "should integrate": "integrates", 110 | "should invoke": "invokes", 111 | "should keep": "keeps", 112 | "should kill": "kills", 113 | "should leave": "leaves", 114 | "should let": "lets", 115 | "should listen": "listens", 116 | "should load": "loads", 117 | "should log": "logs", 118 | "should log": "logs", 119 | "should make": "makes", 120 | "should map": "maps", 121 | "should match": "matches", 122 | "should modify": "modifies", 123 | "should mount": "mounts", 124 | "should move": "moves", 125 | "should namespace": "namespaces", 126 | "should navigate": "navigates", 127 | "should not": "does not", 128 | "should notify": "notifies", 129 | "should open": "opens", 130 | "should output": "outputs", 131 | "should parse": "parses", 132 | "should pass": "passes", 133 | "should perform": "performs", 134 | "should persist": "persists", 135 | "should pick": "picks", 136 | "should play": "plays", 137 | "should post": "posts", 138 | "should prevent": "prevents", 139 | "should process": "processes", 140 | "should produce": "produces", 141 | "should propagate": "propagates", 142 | "should provide": "provides", 143 | "should publish": "publishes", 144 | "should query": "queries", 145 | "should queue": "queues", 146 | "should read": "reads", 147 | "should realise": "realises", 148 | "should refresh": "refreshes", 149 | "should register": "registers", 150 | "should reject": "rejects", 151 | "should remove": "removes", 152 | "should render": "renders", 153 | "should replace": "replaces", 154 | "should report": "reports", 155 | "should reposition": "repositions", 156 | "should request": "requests", 157 | "should reset": "resets", 158 | "should resolve": "resolves", 159 | "should resort": "resorts", 160 | "should respect": "respects", 161 | "should respond": "responds", 162 | "should restore": "restores", 163 | "should restrict": "restricts", 164 | "should resume": "resumes", 165 | "should retrieve": "retrieves", 166 | "should return": "returns", 167 | "should reverse": "reverses", 168 | "should revert": "reverts", 169 | "should run": "runs", 170 | "should save": "saves", 171 | "should say": "says", 172 | "should schedule": "schedules", 173 | "should scroll": "scrolls", 174 | "should select": "selects", 175 | "should send": "sends", 176 | "should serialize": "serializes", 177 | "should set": "sets", 178 | "should show": "shows", 179 | "should simplify": "simplifies", 180 | "should skip": "skips", 181 | "should sort": "sorts", 182 | "should split": "splits", 183 | "should start": "starts", 184 | "should stop": "stops", 185 | "should store": "stores", 186 | "should stream": "streams", 187 | "should stringify": "stringifies", 188 | "should strip": "strips", 189 | "should subscribe": "subscribes", 190 | "should subtract": "subtracts", 191 | "should succeed": "succeeds", 192 | "should supply": "supplies", 193 | "should support": "supports", 194 | "should support": "supports", 195 | "should swap": "swaps", 196 | "should switch": "switches", 197 | "should sync": "syncs", 198 | "should take": "takes", 199 | "should tell": "tells", 200 | "should throw": "throws", 201 | "should toggle": "toggles", 202 | "should touch": "touches", 203 | "should track": "tracks", 204 | "should transform": "transforms", 205 | "should transition": "transitions", 206 | "should translate": "translates", 207 | "should treat": "treats", 208 | "should trigger": "triggers", 209 | "should trim": "trims", 210 | "should try": "tries", 211 | "should unfollow": "unfollows", 212 | "should unregister": "unregisters", 213 | "should unstick": "unsticks", 214 | "should unsubscribe": "unsubscribes", 215 | "should unwrap": "unwraps", 216 | "should update": "updates", 217 | "should update": "updates", 218 | "should uppercase": "uppercases", 219 | "should use": "uses", 220 | "should wait": "waits", 221 | "should watch": "watches", 222 | "should work": "works", 223 | "should wrap": "wraps", 224 | "should zero-pad": "zero-pads", 225 | }; 226 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | array-back@^1.0.3, array-back@^1.0.4: 6 | version "1.0.4" 7 | resolved "https://registry.yarnpkg.com/array-back/-/array-back-1.0.4.tgz#644ba7f095f7ffcf7c43b5f0dc39d3c1f03c063b" 8 | dependencies: 9 | typical "^2.6.0" 10 | 11 | balanced-match@^0.4.1: 12 | version "0.4.2" 13 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" 14 | 15 | brace-expansion@^1.0.0: 16 | version "1.1.7" 17 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" 18 | dependencies: 19 | balanced-match "^0.4.1" 20 | concat-map "0.0.1" 21 | 22 | browser-stdout@1.3.0: 23 | version "1.3.0" 24 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" 25 | 26 | command-line-args@^4.0.2: 27 | version "4.0.2" 28 | resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-4.0.2.tgz#a99c2f28ceabcf26ac56d38e78b600ea3b57e650" 29 | dependencies: 30 | array-back "^1.0.4" 31 | find-replace "^1.0.3" 32 | typical "^2.6.0" 33 | 34 | commander@2.9.0: 35 | version "2.9.0" 36 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" 37 | dependencies: 38 | graceful-readlink ">= 1.0.0" 39 | 40 | concat-map@0.0.1: 41 | version "0.0.1" 42 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 43 | 44 | debug@2.6.0: 45 | version "2.6.0" 46 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" 47 | dependencies: 48 | ms "0.7.2" 49 | 50 | diff@3.2.0: 51 | version "3.2.0" 52 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" 53 | 54 | escape-string-regexp@1.0.5: 55 | version "1.0.5" 56 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 57 | 58 | expect.js@^0.3.1: 59 | version "0.3.1" 60 | resolved "https://registry.yarnpkg.com/expect.js/-/expect.js-0.3.1.tgz#b0a59a0d2eff5437544ebf0ceaa6015841d09b5b" 61 | 62 | find-replace@^1.0.3: 63 | version "1.0.3" 64 | resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-1.0.3.tgz#b88e7364d2d9c959559f388c66670d6130441fa0" 65 | dependencies: 66 | array-back "^1.0.4" 67 | test-value "^2.1.0" 68 | 69 | fs.realpath@^1.0.0: 70 | version "1.0.0" 71 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 72 | 73 | glob@7.1.1: 74 | version "7.1.1" 75 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" 76 | dependencies: 77 | fs.realpath "^1.0.0" 78 | inflight "^1.0.4" 79 | inherits "2" 80 | minimatch "^3.0.2" 81 | once "^1.3.0" 82 | path-is-absolute "^1.0.0" 83 | 84 | "graceful-readlink@>= 1.0.0": 85 | version "1.0.1" 86 | resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" 87 | 88 | growl@1.9.2: 89 | version "1.9.2" 90 | resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" 91 | 92 | has-flag@^1.0.0: 93 | version "1.0.0" 94 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" 95 | 96 | inflight@^1.0.4: 97 | version "1.0.6" 98 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 99 | dependencies: 100 | once "^1.3.0" 101 | wrappy "1" 102 | 103 | inherits@2: 104 | version "2.0.3" 105 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 106 | 107 | json3@3.3.2: 108 | version "3.3.2" 109 | resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" 110 | 111 | lodash._baseassign@^3.0.0: 112 | version "3.2.0" 113 | resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" 114 | dependencies: 115 | lodash._basecopy "^3.0.0" 116 | lodash.keys "^3.0.0" 117 | 118 | lodash._basecopy@^3.0.0: 119 | version "3.0.1" 120 | resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" 121 | 122 | lodash._basecreate@^3.0.0: 123 | version "3.0.3" 124 | resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" 125 | 126 | lodash._getnative@^3.0.0: 127 | version "3.9.1" 128 | resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" 129 | 130 | lodash._isiterateecall@^3.0.0: 131 | version "3.0.9" 132 | resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" 133 | 134 | lodash.create@3.1.1: 135 | version "3.1.1" 136 | resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" 137 | dependencies: 138 | lodash._baseassign "^3.0.0" 139 | lodash._basecreate "^3.0.0" 140 | lodash._isiterateecall "^3.0.0" 141 | 142 | lodash.isarguments@^3.0.0: 143 | version "3.1.0" 144 | resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 145 | 146 | lodash.isarray@^3.0.0: 147 | version "3.0.4" 148 | resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" 149 | 150 | lodash.keys@^3.0.0: 151 | version "3.1.2" 152 | resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" 153 | dependencies: 154 | lodash._getnative "^3.0.0" 155 | lodash.isarguments "^3.0.0" 156 | lodash.isarray "^3.0.0" 157 | 158 | minimatch@^3.0.2: 159 | version "3.0.3" 160 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" 161 | dependencies: 162 | brace-expansion "^1.0.0" 163 | 164 | minimist@0.0.8: 165 | version "0.0.8" 166 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 167 | 168 | mkdirp@0.5.1: 169 | version "0.5.1" 170 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 171 | dependencies: 172 | minimist "0.0.8" 173 | 174 | mocha@^3.3.0: 175 | version "3.3.0" 176 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.3.0.tgz#d29b7428d3f52c82e2e65df1ecb7064e1aabbfb5" 177 | dependencies: 178 | browser-stdout "1.3.0" 179 | commander "2.9.0" 180 | debug "2.6.0" 181 | diff "3.2.0" 182 | escape-string-regexp "1.0.5" 183 | glob "7.1.1" 184 | growl "1.9.2" 185 | json3 "3.3.2" 186 | lodash.create "3.1.1" 187 | mkdirp "0.5.1" 188 | supports-color "3.1.2" 189 | 190 | ms@0.7.2: 191 | version "0.7.2" 192 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" 193 | 194 | once@^1.3.0: 195 | version "1.4.0" 196 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 197 | dependencies: 198 | wrappy "1" 199 | 200 | path-is-absolute@^1.0.0: 201 | version "1.0.1" 202 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 203 | 204 | recursive-readdir-sync@^1.0.6: 205 | version "1.0.6" 206 | resolved "https://registry.yarnpkg.com/recursive-readdir-sync/-/recursive-readdir-sync-1.0.6.tgz#1dbf6d32f3c5bb8d3cde97a6c588d547a9e13d56" 207 | 208 | supports-color@3.1.2: 209 | version "3.1.2" 210 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" 211 | dependencies: 212 | has-flag "^1.0.0" 213 | 214 | test-value@^2.1.0: 215 | version "2.1.0" 216 | resolved "https://registry.yarnpkg.com/test-value/-/test-value-2.1.0.tgz#11da6ff670f3471a73b625ca4f3fdcf7bb748291" 217 | dependencies: 218 | array-back "^1.0.3" 219 | typical "^2.6.0" 220 | 221 | typical@^2.6.0: 222 | version "2.6.0" 223 | resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.0.tgz#89d51554ab139848a65bcc2c8772f8fb450c40ed" 224 | 225 | wrappy@1: 226 | version "1.0.2" 227 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 228 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2017 Spotify AB 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------