├── .babelrc ├── .eslintrc.json ├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── package.json ├── src ├── bingo │ └── bingo.js ├── fibonacci │ └── fibonacci.js ├── find-the-rigth-triangle │ └── find-the-right-triangle.js ├── fizz-buzz │ └── fizz-buzz.js ├── greeting │ └── greeting.js ├── index.js ├── maxibon │ └── maxibon.js ├── potter │ └── potter.js ├── prime-factors │ └── prime-factors.js ├── rock-paper-scissors │ └── rock-paper-scissors.js ├── roman-numerals │ └── roman-numerals.js └── string-calculator │ └── string-calculator.js ├── test ├── bingo │ └── bingo.spec.js ├── bootload.js ├── es6katas │ ├── 1-TemplateStringsTest.js │ ├── 10-DestructuringArraysTest.js │ ├── 11-DestructuringStringsTest.js │ ├── 12-DestructuringObjectsTest.js │ ├── 13-DestructuringDefaults.js │ ├── 14-DestructuringParametersTest.js │ ├── 15-DestructuringAssignTest.js │ ├── 16-ObjectLiteralComputedPropertiesTest.js │ ├── 17-UnicodeInStringsTest.js │ ├── 18-RestAsParameterTest.js │ ├── 19-RestWithDestructuringTest.js │ ├── 2-TemplateStringsMultilineTest.js │ ├── 20-SpreadWithArraysTest.js │ ├── 21-SpreadWithStringsTest.js │ ├── 22-ClassCreationTest.js │ ├── 23-ClassAccessorsTest.js │ ├── 24-ClassStaticKeywordTest.js │ ├── 25-ClassExtendsTest.js │ ├── 26-ClassMoreExtendsTest.js │ ├── 27-ClassSuperInsideAMethodTest.js │ ├── 28-ClassSuperInConstructorTest.js │ ├── 29-ArrayFromTest.js │ ├── 3-TemplateStringsTaggedTest.js │ ├── 30-ArrayOfStaticMethodTest.js │ ├── 31-ArrayFillTest.js │ ├── 32-ArrayFindTest.js │ ├── 33-ArrayFindIndexTest.js │ ├── 34-SymbolTest.js │ ├── 35-SymbolForTest.js │ ├── 36-KeyForTest.js │ ├── 37-IteratorIterableArrayTest.js │ ├── 38-IteratorIterableStringTest.js │ ├── 39-IteratorCustomTest.js │ ├── 4-TemplateStringsRawStringTest.js │ ├── 40-IteratorExampleUsageTest.js │ ├── 41-ArrayEntriesTest.js │ ├── 42-ArrayKeysTest.js │ ├── 44-MapBasicsTest.js │ ├── 45-MapGetTest.js │ ├── 46-MapSetTest.js │ ├── 47-SetBasicsTest.js │ ├── 48-SetAddTest.js │ ├── 5-ArrowFunctionsBasicTest.js │ ├── 53-MapInitializeTest.js │ ├── 54-ObjectIsTest.js │ ├── 55-NumberIsIntegerTest.js │ ├── 57-DefaultParametersTest.js │ ├── 58-ReflectBasicsTest.js │ ├── 59-ReflectApplyTest.js │ ├── 6-ArrowFunctionsBindingTest.js │ ├── 60-ReflectGetPrototypeOfTest.js │ ├── 61-ModulesImportTest.js │ ├── 62-MapHasTest.js │ ├── 63-StringIncludesTest.js │ ├── 64-SetDeleteTest.js │ ├── 65-SetApiOverviewTest.js │ ├── 66-ObjectLiteralGetterTest.js │ ├── 67-ObjectLiteralTest.js │ ├── 68-ReflectConstructTest.js │ ├── 69-ReflectDefinePropertyTest.js │ ├── 7-BlockScopeLetTest.js │ ├── 70-SetClearTest.js │ ├── 71-StringRepeatTest.js │ ├── 72-StringStrtsWithTest.js │ ├── 74-StringEndsWithTest.js │ ├── 76-PromiseCreationTest.js │ ├── 77-PromiseChainingTest.js │ ├── 78-PromiseApiOverviewTest.js │ ├── 79-PromiseCatchTest.js │ ├── 8-BlockScopeConstTest.js │ └── 9-ObjectLiteralsBasicsTest.js ├── fibonacci │ └── fibonacci.spec.js ├── find-the-right-triangle │ └── find-the-right-triangle.spec.js ├── fizz-buzz │ └── fizz-buzz.spec.js ├── greeting │ └── greeting.spec.js ├── maxibon │ ├── developer.spec.js │ └── karumiHQs.spec.js ├── potter │ └── potter.spec.js ├── prime-factors │ └── prime-factors.spec.js ├── rock-paper-scissors │ └── rock-paper-scissors.spec.js ├── roman-numerals │ └── roman-numerals.spec.js └── string-calculator │ └── stringCalculator.spec.js ├── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "browser": true, 5 | "node": true, 6 | "mocha": true 7 | }, 8 | "rules": { 9 | "prettier/prettier": "error" 10 | }, 11 | "plugins": [ 12 | "prettier" 13 | ], 14 | "parserOptions": { 15 | "ecmaVersion": 6, 16 | "sourceType": "module", 17 | "ecmaFeatures": { 18 | "jsx": true 19 | } 20 | }, 21 | "extends": [ 22 | "eslint:recommended", 23 | "prettier", 24 | "plugin:import/errors", 25 | "plugin:import/warnings" 26 | ], 27 | "globals": { 28 | "assert": true, 29 | "expect": true, 30 | "jsc": true 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [pedrovgs] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # Distribution 61 | /dist 62 | 63 | # Visual Studio Code 64 | .vscode 65 | 66 | # IntelliJ 67 | .idea 68 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | cache: 2 | yarn: true 3 | 4 | before_install: 5 | - yarn --version 6 | 7 | script: 8 | - yarn install 9 | - yarn eslint src 10 | - yarn eslint test 11 | - yarn test 12 | - yarn webpack -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaScriptKatas [![Build Status](https://travis-ci.org/pedrovgs/JavaScriptKatas.svg?branch=master)](https://travis-ci.org/pedrovgs/JavaScriptKatas) 2 | 3 | JavaScript 4 | 5 | JavaScript training repository used to practice and learn by solving some common katas. 6 | 7 | ### List of katas: 8 | 9 | * [ES6 Katas.](http://es6katas.org/) 10 | * [FizzBuzz.](http://codingdojo.org/KataFizzBuzz/) 11 | * [String calculator.](http://osherove.com/tdd-kata-1/) 12 | * [Find the right triangle.](https://gist.github.com/pedrovgs/32189838165fbe5c9e773ede534b97f4) 13 | * [Rock paper scissors.](http://agilekatas.co.uk/katas/RockPaperScissors-Kata) 14 | * [Bingo.](http://agilekatas.co.uk/katas/Bingo-Kata) 15 | * [Potter.](http://codingdojo.org/kata/Potter/) 16 | * [Roman numerals.](http://codingdojo.org/kata/RomanNumerals/) 17 | * [Prime factors.](https://www.rubyplus.com/articles/511-TDD-Beyond-Basics-Prime-Factors-Kata) 18 | * [Fibonacci.](https://medium.com/@chmeese/fibonacci-kata-93773b30dbb2#.3opu63eoj) 19 | * [Maxibon.](https://github.com/Karumi/MaxibonKataKotlin) 20 | * [Greeting.](https://github.com/testdouble/contributing-tests/wiki/Greeting-Kata) 21 | 22 | ### Executing tests: 23 | 24 | This project contains some tests written using [Mocha](https://mochajs.org/). You can easily run the tests by executing any of the following commands: 25 | 26 | ``` 27 | yarn test //Executes every test. 28 | yarn test -w //Waits for code changes and executes every test. 29 | yarn test -g "String calculator spec*" //Executes tests matching with the regex passed as param. 30 | ``` 31 | 32 | If you are using IntelliJ IDEA, WebStorm or any other IDE to run your tests you'll need to configure the mocha execution options adding the following configuration: 33 | 34 | ``` 35 | --recursive --require ./test/bootload.js --require babel-register 36 | ``` 37 | 38 | This configuration is the same you can find in the project ``package.json`` configuration. 39 | 40 | ### Packaging: 41 | 42 | This project uses [webpack](https://webpack.js.org) and [babel](https://babeljs.io) in order to pack a simple ``bundle.js`` file, with all the repository code, fully backward compatible with some old JavaScript versions. If you want to pack all this code into a single file you can easily do it by just running: 43 | 44 | ``` 45 | yarn webpack 46 | ``` 47 | 48 | ### Linter: 49 | 50 | This repository uses [eslint](https://eslint.org/) in order to check if the js code written matches the checkstyle configured. You can check if everything is ok by executing ``yarn eslint src`` and automatically fix the issues by executing ``yarn eslint --fix src`` if needed. 51 | 52 | Developed By 53 | ------------ 54 | 55 | * Pedro Vicente Gómez Sánchez - 56 | 57 | 58 | Follow me on Twitter 59 | 60 | 61 | Add me to Linkedin 62 | 63 | 64 | License 65 | ------- 66 | 67 | Copyright 2017 Pedro Vicente Gómez Sánchez 68 | 69 | Licensed under the GNU General Public License, Version 3 (the "License"); 70 | you may not use this file except in compliance with the License. 71 | You may obtain a copy of the License at 72 | 73 | http://www.gnu.org/licenses/gpl-3.0.en.html 74 | 75 | Unless required by applicable law or agreed to in writing, software 76 | distributed under the License is distributed on an "AS IS" BASIS, 77 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 78 | See the License for the specific language governing permissions and 79 | limitations under the License. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript-katas", 3 | "version": "1.0.0", 4 | "description": "JavaScript training repository used to learn and practice by solving some common katas.", 5 | "main": "src/index.js", 6 | "repository": "git@github.com:pedrovgs/JavaScriptKatas.git", 7 | "author": "pedrovgs ", 8 | "license": "GPL-3.0", 9 | "private": false, 10 | "scripts": { 11 | "test": "mocha --recursive --require ./test/bootload.js --require babel-register" 12 | }, 13 | "devDependencies": { 14 | "babel-core": "^6.26.0", 15 | "babel-loader": "^7.1.2", 16 | "babel-polyfill": "^6.26.0", 17 | "babel-preset-env": "^1.6.1", 18 | "chai": "^4.1.2", 19 | "eslint": "^4.10.0", 20 | "eslint-config-prettier": "^2.7.0", 21 | "eslint-plugin-import": "^2.8.0", 22 | "eslint-plugin-prettier": "^2.3.1", 23 | "jsverify": "^0.8.3", 24 | "mocha": "^4.0.1", 25 | "mocha-eslint": "^4.1.0", 26 | "prettier": "^1.7.4", 27 | "prettier-eslint-cli": "^4.4.0", 28 | "webpack": "^3.8.1" 29 | }, 30 | "dependencies": { 31 | "enumify": "^1.0.4" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/bingo/bingo.js: -------------------------------------------------------------------------------- 1 | export const usBingoNumbers = [...Array(75).keys()].map(key => key + 1); 2 | 3 | export class Game { 4 | constructor(availableNumbers, calledNumbers) { 5 | this.availableNumbers = availableNumbers; 6 | this.calledNumbers = calledNumbers; 7 | } 8 | } 9 | 10 | export class Card { 11 | constructor(numbers) { 12 | this.numbers = numbers; 13 | } 14 | } 15 | 16 | export function callNumber(game) { 17 | const availableNumbers = game.availableNumbers; 18 | const numbersLeft = availableNumbers.length; 19 | const calledNumber = availableNumbers[generateRandomInt(0, numbersLeft - 1)]; 20 | const updatedGame = Object.assign({}, game); 21 | updatedGame.availableNumbers = updatedGame.availableNumbers.filter( 22 | number => number !== calledNumber 23 | ); 24 | updatedGame.calledNumbers.push(calledNumber); 25 | return [calledNumber, updatedGame]; 26 | } 27 | 28 | export function generateGame() { 29 | return new Game(usBingoNumbers.slice(), []); 30 | } 31 | 32 | export function generateCard() { 33 | let cardPossibleNumbers = usBingoNumbers.slice(); 34 | const cardNumbers = []; 35 | for (let i = 0; i < 24; i++) { 36 | const cardNumberIndex = generateRandomInt(0, cardPossibleNumbers.length); 37 | const cardNumber = cardPossibleNumbers[cardNumberIndex]; 38 | cardPossibleNumbers = cardPossibleNumbers.filter(x => x !== cardNumber); 39 | cardNumbers.push(cardNumber); 40 | } 41 | return new Card(cardNumbers); 42 | } 43 | 44 | export function isWinnerCard(card, game) { 45 | return card.numbers.every(number => game.calledNumbers.includes(number)); 46 | } 47 | 48 | function generateRandomInt(min, max) { 49 | return parseInt(Math.random() * (max - min) + min); 50 | } 51 | -------------------------------------------------------------------------------- /src/fibonacci/fibonacci.js: -------------------------------------------------------------------------------- 1 | export function fibonacci(n) { 2 | if (n === 0) { 3 | return 0; 4 | } else if (n === 1) { 5 | return 1; 6 | } else { 7 | return fibonacci(n - 1) + fibonacci(n - 2); 8 | } 9 | } 10 | 11 | export function fibonacciTailRec(n) { 12 | return fibonacciTailRecInner(n, 1, 0); 13 | } 14 | 15 | function fibonacciTailRecInner(n, a, b) { 16 | if (n === 0) { 17 | return b; 18 | } else { 19 | return fibonacciTailRecInner(n - 1, a + b, a); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/find-the-rigth-triangle/find-the-right-triangle.js: -------------------------------------------------------------------------------- 1 | function findRightTriangles() { 2 | const triangles = []; 3 | for (let c = 1; c <= 10; c++) { 4 | for (let a = 1; a <= c; a++) { 5 | for (let b = 1; b <= a; b++) { 6 | if (isRightTriangle(a, b, c)) { 7 | triangles.push([a, b, c]); 8 | } 9 | } 10 | } 11 | } 12 | return triangles; 13 | } 14 | 15 | function isRightTriangle(a, b, c) { 16 | return Math.pow(a, 2) + Math.pow(b, 2) === Math.pow(c, 2) && a + b + c === 24; 17 | } 18 | 19 | export default findRightTriangles; 20 | -------------------------------------------------------------------------------- /src/fizz-buzz/fizz-buzz.js: -------------------------------------------------------------------------------- 1 | function fizzBuzz(value) { 2 | const isMultipleOf3 = isMultipleOf(value, 3); 3 | const isMultipleOf5 = isMultipleOf(value, 5); 4 | if (isMultipleOf3 && isMultipleOf5) return "FizzBuzz"; 5 | else if (isMultipleOf3) return "Fizz"; 6 | else if (isMultipleOf5) return "Buzz"; 7 | else return value.toString(); 8 | } 9 | 10 | function isMultipleOf(x, y) { 11 | return x % y == 0; 12 | } 13 | 14 | export default fizzBuzz; 15 | -------------------------------------------------------------------------------- /src/greeting/greeting.js: -------------------------------------------------------------------------------- 1 | export function greet(...names) { 2 | names = splitNamesIfNeeded(names); 3 | if (isYelling(names)) { 4 | return yellingGreet(names); 5 | } else { 6 | const regularNames = names.filter(name => !isYelling(name)); 7 | const yellingNames = names.filter(name => isYelling(name)); 8 | if (regularNames.length > 0 && yellingNames.length > 0) { 9 | return yellingAndRegularGreet(regularNames, yellingNames); 10 | } else { 11 | return regularGreet(names); 12 | } 13 | } 14 | } 15 | 16 | function splitNamesIfNeeded(names) { 17 | if (typeof names === void 0 || names.length === 0) { 18 | return names; 19 | } 20 | return names.map(name => splitName(name)).reduce(function(a, b) { 21 | return a.concat(b); 22 | }); 23 | } 24 | 25 | function splitName(name) { 26 | if (isAQuote(name)) { 27 | name = name.split('"').join(""); 28 | return [name]; 29 | } else { 30 | return name.split(","); 31 | } 32 | } 33 | 34 | function isAQuote(name) { 35 | return name.match('"*"') !== null; 36 | } 37 | 38 | function yellingGreet(names) { 39 | return `HELLO ${names}!`; 40 | } 41 | 42 | function regularGreet(names) { 43 | return `Hello, ${joinNames(names)}.`; 44 | } 45 | 46 | function yellingAndRegularGreet(regularNames, yellingNames) { 47 | return `${regularGreet(regularNames)} AND ${yellingGreet(yellingNames)}`; 48 | } 49 | 50 | function isYelling(names) { 51 | const isDefined = typeof names !== void 0; 52 | const isAnArray = Array.isArray(names); 53 | if (isDefined && isAnArray && names.length > 0) { 54 | return names.every(name => name === `${name}`.toUpperCase()); 55 | } else if (isDefined && !isAnArray) { 56 | return names === names.toUpperCase(); 57 | } else { 58 | return false; 59 | } 60 | } 61 | 62 | function joinNames(names) { 63 | if (typeof names === "undefined" || names.length === 0) { 64 | return "my friend"; 65 | } else if (names.length === 1) { 66 | return names; 67 | } else if (names.length === 2) { 68 | return names.join(" and "); 69 | } else { 70 | const firstNames = names.slice(0, names.length - 1).join(", "); 71 | return `${firstNames}, and ${names[names.length - 1]}`; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrovgs/JavaScriptKatas/767270642aec15a3f9adf5d9377e496631c9110e/src/index.js -------------------------------------------------------------------------------- /src/maxibon/maxibon.js: -------------------------------------------------------------------------------- 1 | export class Developer { 2 | constructor(name, maxibonsToGrab) { 3 | this.name = name; 4 | this.maxibonsToGrab = Math.max(0, maxibonsToGrab); 5 | } 6 | } 7 | 8 | export const Pedro = new Developer("Pedro", 3); 9 | export const Fran = new Developer("Fran", 1); 10 | export const Davide = new Developer("Davide", 0); 11 | export const Sergio = new Developer("Sergio", 2); 12 | export const Jorge = new Developer("Jorge", 1); 13 | 14 | export class KarumiHQs { 15 | constructor(chat = new ConsoleChat()) { 16 | this.chat = chat; 17 | this.maxibonsLeft = 10; 18 | } 19 | 20 | openFridge(devs) { 21 | if (Array.isArray(devs)) { 22 | openFridge(devs, this); 23 | } else { 24 | openFridge([devs], this); 25 | } 26 | } 27 | } 28 | 29 | function openFridge(devs, office) { 30 | devs.forEach(dev => { 31 | office.maxibonsLeft = Math.max(0, office.maxibonsLeft - dev.maxibonsToGrab); 32 | if (office.maxibonsLeft <= 2) { 33 | office.maxibonsLeft += 10; 34 | office.chat.sendMessage( 35 | `Hi guys, I'm ${dev.name}. We need more maxibons!` 36 | ); 37 | } 38 | }); 39 | } 40 | 41 | class ConsoleChat { 42 | sendMessage(message) { 43 | console.log(message); // eslint-disable-line 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/potter/potter.js: -------------------------------------------------------------------------------- 1 | import { Enum } from "enumify"; 2 | 3 | export class HarryPotterBook extends Enum {} 4 | HarryPotterBook.initEnum(["BOOK_1", "BOOK_2", "BOOK_3", "BOOK_4", "BOOK_5"]); 5 | 6 | export function calculatePrice(cart) { 7 | const booksGroupedByBookNumber = groupBooks(cart); 8 | return calculatePriceBasedOnTheNumberOfBooks(booksGroupedByBookNumber); 9 | } 10 | 11 | function groupBooks(cart) { 12 | const groupedBooks = new Map(); 13 | cart.forEach(value => { 14 | if (groupedBooks.has(value)) { 15 | const currentValue = groupedBooks.get(value); 16 | groupedBooks.set(value, currentValue + 1); 17 | } else { 18 | groupedBooks.set(value, 1); 19 | } 20 | }); 21 | return Array.of(...groupedBooks.values()); 22 | } 23 | 24 | function calculatePriceBasedOnTheNumberOfBooks(books) { 25 | const oneBookPrice = 8.0; 26 | const numberOfDifferentBooks = books.filter(x => x > 0).length; 27 | if (numberOfDifferentBooks === 0) { 28 | return 0.0; 29 | } else if (numberOfDifferentBooks === 1) { 30 | return oneBookPrice * books[0]; 31 | } else if (numberOfDifferentBooks === 2) { 32 | return ( 33 | oneBookPrice * 2 * 0.95 + 34 | calculatePriceBasedOnTheNumberOfBooks(removeDiscountedBooks(books)) 35 | ); 36 | } else if (numberOfDifferentBooks === 3) { 37 | return ( 38 | oneBookPrice * 3 * 0.9 + 39 | calculatePriceBasedOnTheNumberOfBooks(removeDiscountedBooks(books)) 40 | ); 41 | } else if (numberOfDifferentBooks === 4) { 42 | return ( 43 | oneBookPrice * 4 * 0.8 + 44 | calculatePriceBasedOnTheNumberOfBooks(removeDiscountedBooks(books)) 45 | ); 46 | } else if (numberOfDifferentBooks === 5) { 47 | return ( 48 | oneBookPrice * 5 * 0.75 + 49 | calculatePriceBasedOnTheNumberOfBooks(removeDiscountedBooks(books)) 50 | ); 51 | } 52 | } 53 | 54 | function removeDiscountedBooks(books) { 55 | return books.map(x => x - 1); 56 | } 57 | -------------------------------------------------------------------------------- /src/prime-factors/prime-factors.js: -------------------------------------------------------------------------------- 1 | function primeFactors(n) { 2 | if (n <= 1) { 3 | return []; 4 | } else { 5 | const firstPrimeLowerThanN = firstPrimeLowerThan(n); 6 | if (firstPrimeLowerThanN) { 7 | return [firstPrimeLowerThanN, ...primeFactors(n / firstPrimeLowerThanN)]; 8 | } else { 9 | return [n]; 10 | } 11 | } 12 | } 13 | 14 | function firstPrimeLowerThan(n) { 15 | for (let i = 2; i < n; i++) { 16 | if (n % i === 0) { 17 | return i; 18 | } 19 | } 20 | } 21 | 22 | export default primeFactors; 23 | -------------------------------------------------------------------------------- /src/rock-paper-scissors/rock-paper-scissors.js: -------------------------------------------------------------------------------- 1 | import { Enum } from "enumify"; 2 | 3 | export class Gesture extends Enum {} 4 | Gesture.initEnum(["PAPER", "ROCK", "SCISSORS"]); 5 | 6 | export class Player { 7 | constructor(name, gesture) { 8 | this.name = name; 9 | this.gesture = gesture; 10 | } 11 | } 12 | 13 | export class Result extends Enum {} 14 | Result.initEnum(["PLAYER_1_WINS", "PLAYER_2_WINS", "DRAW"]); 15 | 16 | const player1WinsResults = [ 17 | [Gesture.ROCK, Gesture.SCISSORS], 18 | [Gesture.SCISSORS, Gesture.PAPER], 19 | [Gesture.PAPER, Gesture.ROCK] 20 | ]; 21 | const player2WinsResults = player1WinsResults.map(game => 22 | game.slice().reverse() 23 | ); 24 | 25 | export function whoWins(player1, player2) { 26 | const match = [player1.gesture, player2.gesture]; 27 | if (winsPlayer1(match)) { 28 | return Result.PLAYER_1_WINS; 29 | } else if (winsPlayer2(match)) { 30 | return Result.PLAYER_2_WINS; 31 | } else { 32 | return Result.DRAW; 33 | } 34 | } 35 | 36 | function winsPlayer1(match) { 37 | return wins(player1WinsResults, match); 38 | } 39 | 40 | function winsPlayer2(match) { 41 | return wins(player2WinsResults, match); 42 | } 43 | 44 | function wins(winningGames, match) { 45 | return winningGames.some(possibleGame => 46 | possibleGame.every((v, i) => v === match[i]) 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /src/roman-numerals/roman-numerals.js: -------------------------------------------------------------------------------- 1 | import { Enum } from "enumify"; 2 | 3 | export class RomanNumeral extends Enum {} 4 | RomanNumeral.initEnum([ 5 | "I", 6 | "IV", 7 | "V", 8 | "IX", 9 | "X", 10 | "XL", 11 | "XLIV", 12 | "L", 13 | "XC", 14 | "C", 15 | "CD", 16 | "D", 17 | "CM", 18 | "M" 19 | ]); 20 | 21 | const romanValues = [ 22 | [RomanNumeral.I, 1], 23 | [RomanNumeral.IV, 4], 24 | [RomanNumeral.V, 5], 25 | [RomanNumeral.IX, 9], 26 | [RomanNumeral.X, 10], 27 | [RomanNumeral.XL, 40], 28 | [RomanNumeral.XLIV, 44], 29 | [RomanNumeral.L, 50], 30 | [RomanNumeral.XC, 90], 31 | [RomanNumeral.C, 100], 32 | [RomanNumeral.CD, 400], 33 | [RomanNumeral.D, 500], 34 | [RomanNumeral.CM, 900], 35 | [RomanNumeral.M, 1000] 36 | ]; 37 | 38 | export function toArabic(roman) { 39 | return toArabicInner(roman.toUpperCase(), 0); 40 | } 41 | 42 | export function toRoman(arabic) { 43 | return toRomanInner(arabic, ""); 44 | } 45 | 46 | function toArabicInner(roman, acc) { 47 | if (roman.length === 0) { 48 | return acc; 49 | } else if (roman.length === 1) { 50 | return acc + extractRomanValue(roman); 51 | } else { 52 | const combinedRomanNumeral = roman.substring(0, 2); 53 | if (isRomanNumeral(combinedRomanNumeral)) { 54 | const updatedAcc = acc + extractRomanValue(combinedRomanNumeral); 55 | const restOfTheRomanNumber = roman.slice(2, roman.length); 56 | return toArabicInner(restOfTheRomanNumber, updatedAcc); 57 | } else { 58 | const updatedAcc = acc + extractRomanValue(roman.substring(0, 1)); 59 | const restOfTheRomanNumber = roman.slice(1, roman.length); 60 | return toArabicInner(restOfTheRomanNumber, updatedAcc); 61 | } 62 | } 63 | } 64 | 65 | function toRomanInner(arabic, acc) { 66 | if (arabic === 0) { 67 | return acc; 68 | } else { 69 | const closerRoman = extractCloserRoman(arabic); 70 | const closerRomanValue = closerRoman[1]; 71 | return toRomanInner(arabic - closerRomanValue, acc + closerRoman[0].name); 72 | } 73 | } 74 | 75 | function extractCloserRoman(arabic) { 76 | let possibleValues = romanValues.filter(x => arabic - x[1] >= 0); 77 | return possibleValues[possibleValues.length - 1]; 78 | } 79 | 80 | function isRomanNumeral(romanNumeral) { 81 | return typeof RomanNumeral.enumValueOf(romanNumeral) !== "undefined"; 82 | } 83 | 84 | function extractRomanValue(romanNumber) { 85 | return romanValues.find( 86 | v => v[0] === RomanNumeral.enumValueOf(romanNumber) 87 | )[1]; 88 | } 89 | -------------------------------------------------------------------------------- /src/string-calculator/string-calculator.js: -------------------------------------------------------------------------------- 1 | function add(value) { 2 | if (containsCustomDelimiter(value)) { 3 | const customDelimiter = extractCustomDelimiter(value); 4 | return addWithDelimiters(removeCustomDelimiter(value, customDelimiter), [ 5 | customDelimiter 6 | ]); 7 | } else { 8 | return addWithDelimiters(value, [",", "\n"]); 9 | } 10 | } 11 | 12 | function containsCustomDelimiter(value) { 13 | return value.startsWith("//"); 14 | } 15 | 16 | function extractCustomDelimiter(value) { 17 | return value.split("//")[1][0]; 18 | } 19 | 20 | function removeCustomDelimiter(value, delimiter) { 21 | const startIndex = value.indexOf(delimiter); 22 | return value.substring(startIndex + 1, value.length); 23 | } 24 | 25 | function addWithDelimiters(value, delimiters) { 26 | if (!value) { 27 | return 0; 28 | } 29 | const fixedDelimiter = " "; 30 | const sum = unifyDelimiters(value, fixedDelimiter, delimiters) 31 | .split(fixedDelimiter) 32 | .map(value => parseInt(value)) 33 | .filter(value => Number.isInteger(value)) 34 | .reduce((acc, value) => parseInt(acc) + parseInt(value)); 35 | return parseInt(sum); 36 | } 37 | 38 | function unifyDelimiters(value, fixedDelimiter, delimiters) { 39 | let unifiedDelimitersValue = value; 40 | delimiters.forEach(d => { 41 | unifiedDelimitersValue = unifiedDelimitersValue 42 | .split(d) 43 | .join(fixedDelimiter); 44 | }); 45 | return unifiedDelimitersValue; 46 | } 47 | 48 | export default add; 49 | -------------------------------------------------------------------------------- /test/bingo/bingo.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | Card, 3 | Game, 4 | callNumber, 5 | generateGame, 6 | generateCard, 7 | usBingoNumbers, 8 | isWinnerCard 9 | } from "../../src/bingo/bingo.js"; 10 | 11 | describe("Bingo spec", () => { 12 | jsc.property( 13 | "should return a called number between 1 and 75", 14 | arbitraryNumberOfCallNumberInvokations(), 15 | numberOfInvokations => { 16 | const game = generateGame(); 17 | 18 | const executions = [...Array(numberOfInvokations).keys()].map(() => { 19 | const [calledNumber] = callNumber(game); 20 | return calledNumber >= 1 && calledNumber <= 75; 21 | }); 22 | return executions.every(execution => execution === true); 23 | } 24 | ); 25 | 26 | jsc.property( 27 | "should return a an updated game with the called number as part of the calledNumbers list and not as part of the available numbers", 28 | arbitraryNumberOfCallNumberInvokations(), 29 | numberOfInvokations => { 30 | let game = generateGame(); 31 | 32 | const calledNumbers = [...Array(numberOfInvokations).keys()].map(() => { 33 | const result = callNumber(game); 34 | game = result[1]; 35 | return result[0]; 36 | }); 37 | 38 | const numbersAreCalled = calledNumbers.every(calledNumber => 39 | game.calledNumbers.includes(calledNumber) 40 | ); 41 | const numbersAreNotAvailable = calledNumbers.every( 42 | calledNumber => !game.availableNumbers.includes(calledNumber) 43 | ); 44 | return numbersAreCalled && numbersAreNotAvailable; 45 | } 46 | ); 47 | 48 | jsc.property( 49 | "should generate cards with 24 values between 1 and 75", 50 | arbitraryNumberOfGenerateCardInvokations(), 51 | numberOfInvokations => { 52 | const cards = [...Array(numberOfInvokations).keys()].map(() => 53 | generateCard() 54 | ); 55 | return cards.every( 56 | card => 57 | card.numbers.length == 24 && 58 | card.numbers.every(number => number >= 1 && number <= 75) 59 | ); 60 | } 61 | ); 62 | 63 | jsc.property( 64 | "when cards are generated does not contain repeated values", 65 | arbitraryNumberOfGenerateCardInvokations(), 66 | numberOfInvokations => { 67 | const cards = [...Array(numberOfInvokations).keys()].map(() => 68 | generateCard() 69 | ); 70 | return cards.every(card => 71 | card.numbers.every(number => 72 | card.numbers.filter(x => (x === number.length) === 1) 73 | ) 74 | ); 75 | } 76 | ); 77 | 78 | jsc.property( 79 | "a card is the winner if its 24 numbers have bein called", 80 | arbitraryGame(), 81 | game => { 82 | const winnerCard = new Card(game.calledNumbers); 83 | return isWinnerCard(winnerCard, game); 84 | } 85 | ); 86 | 87 | it("any card will be the winner if the game calls 75 numbers", () => { 88 | let game = generateGame(); 89 | const card = generateCard(); 90 | 91 | for (let i = 0; i < 75; i++) { 92 | game = callNumber(game)[1]; 93 | } 94 | 95 | expect(isWinnerCard(card, game)).to.equal(true); 96 | }); 97 | 98 | it("won't be winner cards if just 23 numbers are called", () => { 99 | let game = generateGame(); 100 | const card = generateCard(); 101 | 102 | for (let i = 0; i < 23; i++) { 103 | game = callNumber(game)[1]; 104 | } 105 | 106 | expect(isWinnerCard(card, game)).to.equal(false); 107 | }); 108 | 109 | function arbitraryGame(minNumberOfcalledNumbers = 24) { 110 | return jsc 111 | .integer(1, Math.min(minNumberOfcalledNumbers, 75)) 112 | .smap(numberOfCalledNumbers => 113 | generateGameWithCalledNumbers(numberOfCalledNumbers) 114 | ); 115 | } 116 | 117 | function generateGameWithCalledNumbers(numberOfCalledNumbers) { 118 | let availableNumbers = usBingoNumbers.slice(); 119 | const calledNumbers = []; 120 | for (let i = 0; i < numberOfCalledNumbers; i++) { 121 | const calledNumber = 122 | availableNumbers[generateRandomInt(0, availableNumbers.length - 1)]; 123 | availableNumbers = availableNumbers.filter(x => x !== calledNumber); 124 | calledNumbers.push(calledNumber); 125 | } 126 | return new Game(availableNumbers, calledNumbers); 127 | } 128 | 129 | function arbitraryNumberOfCallNumberInvokations() { 130 | return jsc.integer(1, 75); 131 | } 132 | 133 | function arbitraryNumberOfGenerateCardInvokations() { 134 | return jsc.integer(1, 50); 135 | } 136 | 137 | function generateRandomInt(min, max) { 138 | return parseInt(Math.random() * (max - min) + min); 139 | } 140 | }); 141 | -------------------------------------------------------------------------------- /test/bootload.js: -------------------------------------------------------------------------------- 1 | global.assert = require("assert"); 2 | global.expect = require("chai").expect; 3 | global.jsc = require("jsverify"); 4 | const mocha = require("mocha"); 5 | global.describe = mocha.describe; 6 | global.it = mocha.it; 7 | -------------------------------------------------------------------------------- /test/es6katas/1-TemplateStringsTest.js: -------------------------------------------------------------------------------- 1 | // 1: template strings - basics 2 | // To do: make all tests pass, leave the asserts unchanged! 3 | 4 | describe("a template string, is wrapped in ` (backticks) instead of ' or \"", function() { 5 | describe("by default, behaves like a normal string", function() { 6 | it("just surrounded by backticks", function() { 7 | var str = `like a string`; 8 | assert.equal(str, "like a string"); 9 | }); 10 | }); 11 | 12 | var x = 42; 13 | var y = 23; 14 | 15 | describe('can evaluate variables, which are wrapped in "${" and "}"', function() { 16 | it('e.g. a simple variable "${x}" just gets evaluated', function() { 17 | var evaluated = `x=${x}`; 18 | assert.equal(evaluated, "x=" + x); 19 | }); 20 | 21 | it("multiple variables get evaluated too", function() { 22 | var evaluated = `${x}+${y}`; 23 | assert.equal(evaluated, x + "+" + y); 24 | }); 25 | }); 26 | 27 | describe('can evaluate any expression, wrapped inside "${...}"', function() { 28 | it('all inside "${...}" gets evaluated', function() { 29 | var evaluated = `${x + y}`; 30 | assert.equal(evaluated, x + y); 31 | }); 32 | 33 | it('inside "${...}" can also be a function call', function() { 34 | function getDomain() { 35 | return "tddbin.com"; 36 | } 37 | var evaluated = `${getDomain()}`; 38 | assert.equal(evaluated, "tddbin.com"); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/es6katas/10-DestructuringArraysTest.js: -------------------------------------------------------------------------------- 1 | // 10: destructuring - array 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("destructuring arrays makes shorter code", () => { 5 | it("extract value from array, e.g. extract 0 into x like so `let [x] = [0];`", () => { 6 | let [firstValue] = [1]; 7 | assert.strictEqual(firstValue, 1); 8 | }); 9 | 10 | it("swap two variables, in one operation", () => { 11 | let [x, y] = ["ax", "why"]; 12 | [x, y] = [y, x]; 13 | assert.deepEqual([x, y], ["why", "ax"]); 14 | }); 15 | 16 | it("leading commas", () => { 17 | const all = ["ax", "why", "zet"]; 18 | const [, , z] = all; 19 | assert.equal(z, "zet"); 20 | }); 21 | 22 | it("extract from nested arrays", () => { 23 | const user = [["Some", "One"], 23]; 24 | const [[firstName, surname], age] = user; 25 | 26 | const expected = "Some One = 23 years"; 27 | assert.equal(`${firstName} ${surname} = ${age} years`, expected); 28 | }); 29 | 30 | it("chained assignments", () => { 31 | let c, d; 32 | let [a, b] = ([c, d] = [1, 2]); 33 | assert.deepEqual([a, b, c, d], [1, 2, 1, 2]); 34 | }); 35 | 36 | it("in for-of loop", () => { 37 | for (var [, a, b] of [[0, 1, 2]]) { // eslint-disable-line 38 | } 39 | assert.deepEqual([a, b], [1, 2]); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/es6katas/11-DestructuringStringsTest.js: -------------------------------------------------------------------------------- 1 | // 11: destructuring - string 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("destructuring also works on strings", () => { 5 | it("destructure every character", () => { 6 | let [a, b, c] = "abc"; 7 | assert.deepEqual([a, b, c], ["a", "b", "c"]); 8 | }); 9 | 10 | it("missing characters are undefined", () => { 11 | const [a, b, c] = "ab"; // eslint-disable-line 12 | assert.equal(c, void 0); 13 | }); 14 | 15 | it("unicode character work too", () => { 16 | const [space, coffee] = "a☕"; // eslint-disable-line 17 | assert.equal(coffee, "\u{2615}"); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/es6katas/12-DestructuringObjectsTest.js: -------------------------------------------------------------------------------- 1 | // 12: destructuring - object 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("destructuring objects", () => { 5 | it("is simple", () => { 6 | const { x } = { x: 1 }; 7 | assert.equal(x, 1); 8 | }); 9 | 10 | describe("nested", () => { 11 | it("multiple objects", () => { 12 | const magic = { first: 23, second: 42 }; 13 | const { magic: { second } } = { magic }; 14 | assert.equal(second, 42); 15 | }); 16 | it("object and array", () => { 17 | const { z: [, x] } = { z: [23, 42] }; 18 | assert.equal(x, 42); 19 | }); 20 | it("array and object", () => { 21 | const [, [{ lang }]] = [null, [{ env: "browser", lang: "ES6" }]]; 22 | assert.equal(lang, "ES6"); 23 | }); 24 | }); 25 | 26 | describe("interesting", () => { 27 | it("missing refs become undefined", () => { 28 | const { z } = { x: 1, y: 2 }; 29 | assert.equal(z, void 0); 30 | }); 31 | 32 | it("destructure from builtins (string)", () => { 33 | const { substr } = "1"; 34 | assert.equal(substr, String.prototype.substr); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/es6katas/13-DestructuringDefaults.js: -------------------------------------------------------------------------------- 1 | // 13: destructuring - defaults 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("destructuring can also have default values", () => { 5 | it("for an empty array", () => { 6 | const [a = 1] = []; 7 | assert.equal(a, 1); 8 | }); 9 | 10 | it("for a missing value", () => { 11 | const [b = 2] = [undefined, 1, 3]; 12 | assert.equal(b, 2); 13 | }); 14 | 15 | it("in an object", () => { 16 | const { b = 2 } = { a: 1 }; 17 | assert.equal(b, 2); 18 | }); 19 | 20 | it("if the value is undefined", () => { 21 | const { b = 2 } = { a: 1, b: void 0 }; 22 | assert.strictEqual(b, 2); 23 | }); 24 | 25 | it("also a string works with defaults", () => { 26 | const [a, b = 2] = "1"; 27 | assert.equal(a, "1"); 28 | assert.equal(b, 2); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/es6katas/14-DestructuringParametersTest.js: -------------------------------------------------------------------------------- 1 | // 14: destructuring - parameters 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("destructuring function parameters", () => { 5 | describe("destruct parameters", () => { 6 | it("multiple params from object", () => { 7 | const fn = ({ id, name }) => { 8 | assert.equal(id, 42); 9 | assert.equal(name, "Wolfram"); 10 | }; 11 | const user = { name: "Wolfram", id: 42 }; 12 | fn(user); 13 | }); 14 | 15 | it("multiple params from array/object", () => { 16 | const fn = ([, { name }]) => { 17 | assert.equal(name, "Alice"); 18 | }; 19 | const users = [{ name: "nobody" }, { name: "Alice", id: 42 }]; 20 | fn(users); 21 | }); 22 | }); 23 | 24 | describe("default values", () => { 25 | it("for simple values", () => { 26 | const fn = (id, name = "Bob") => { 27 | assert.strictEqual(id, 23); 28 | assert.strictEqual(name, "Bob"); 29 | }; 30 | fn(23); 31 | }); 32 | 33 | it("for a missing array value", () => { 34 | const defaultUser = { id: 23, name: "Joe" }; 35 | const fn = ([user]) => { 36 | assert.deepEqual(user, defaultUser); 37 | }; 38 | fn([defaultUser]); 39 | }); 40 | 41 | it("mix of parameter types", () => { 42 | const fn = (id, [arr], { obj }) => { 43 | assert.equal(id, 1); 44 | assert.equal(arr, 2); 45 | assert.equal(obj, 3); 46 | }; 47 | fn(1, [2], { obj: 3 }); 48 | }); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /test/es6katas/15-DestructuringAssignTest.js: -------------------------------------------------------------------------------- 1 | // 15: destructuring - assign 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("assign object property values to new variables while destructuring", () => { 5 | describe("for simple objects", function() { 6 | it("use a colon after the property name, like so `propertyName: newName`", () => { 7 | const { x: y } = { x: 1 }; 8 | assert.equal(y, 1); 9 | }); 10 | 11 | it("assign a new name and give it a default value using `= `", () => { 12 | const { x: y = 42 } = { y: 23 }; 13 | assert.equal(y, 42); 14 | }); 15 | }); 16 | 17 | describe("for function parameter names", function() { 18 | it("do it the same way, with a colon behind it", () => { 19 | const fn = ({ x: y }) => { 20 | assert.equal(y, 1); 21 | }; 22 | fn({ x: 1 }); 23 | }); 24 | 25 | it("giving it a default value is possible too, like above", () => { 26 | const fn = ({ x: y = 3 }) => { 27 | assert.equal(y, 3); 28 | }; 29 | fn({}); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/es6katas/16-ObjectLiteralComputedPropertiesTest.js: -------------------------------------------------------------------------------- 1 | // 16: object-literal - computed properties 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("Object literal properties may be computed values", () => { 5 | it("a computed property `x` needs to be surrounded by `[]`", () => { 6 | const propertyName = "x"; 7 | const obj = { [propertyName]: 1 }; 8 | assert.equal(obj.x, 1); 9 | }); 10 | 11 | it("can also get a function assigned", () => { 12 | const key = "func"; 13 | const obj = { [key]: () => "seven" }; 14 | assert.equal(obj.func(), "seven"); 15 | }); 16 | 17 | it("the key may also be the result of a function call", () => { 18 | const getName = () => "propertyName"; 19 | const obj = { 20 | [getName()]() { 21 | return "seven"; 22 | } 23 | }; 24 | assert.equal(obj.propertyName(), "seven"); 25 | }); 26 | 27 | it("the key can also be constructed by an expression", () => { 28 | const what = "tyName"; 29 | const obj = { ["proper" + what]: true }; 30 | assert.equal("propertyName" in obj, true); 31 | }); 32 | 33 | it("accessor keys can be computed names too", () => { 34 | const obj = { 35 | get ["key"]() { 36 | return 1; 37 | } 38 | }; 39 | assert.equal(obj.key, 1); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/es6katas/17-UnicodeInStringsTest.js: -------------------------------------------------------------------------------- 1 | // 17: unicode - in strings 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("unicode strings", () => { 5 | it("are \\u prefixed", () => { 6 | const nuclear = "\u2622"; 7 | assert.equal(nuclear, "☢"); 8 | }); 9 | 10 | it("value is 4 bytes/digits", () => { 11 | const nuclear = "\u2622"; 12 | assert.equal(`no more ${nuclear}`, "no more ☢"); 13 | }); 14 | 15 | it("value is hexadecimal", () => { 16 | const nuclear = `\u006E\u006F more \u2622`; 17 | assert.equal(nuclear, "no more ☢"); 18 | }); 19 | 20 | it("curly braces may surround the value", () => { 21 | const nuclear = `\u{0000000006E}\u{00006F} more \u2622`; 22 | assert.equal(nuclear, "no more ☢"); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/es6katas/18-RestAsParameterTest.js: -------------------------------------------------------------------------------- 1 | // 18: rest - as-parameter 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("rest in function params", () => { 5 | it("must be the last parameter", () => { 6 | const fn = (veryLast, ...rest) => { 7 | assert.deepEqual([1, 2], rest); 8 | }; 9 | fn(0, 1, 2); 10 | }); 11 | 12 | it("can be used to get all other parameters", () => { 13 | const fn = (firstParam, secondParam, rest) => { 14 | assert.deepEqual([3, 4], rest); 15 | }; 16 | fn(1, 2, [3, 4]); 17 | }); 18 | 19 | it("makes `arguments` obsolete", () => { 20 | const fn = (...args) => { 21 | assert.deepEqual([42, "twenty three", "win"], args); 22 | }; 23 | fn(42, "twenty three", "win"); 24 | }); 25 | 26 | it("eliminate `arguments`!!!", () => { 27 | const fn = (...args) => args; 28 | const [, ...rest] = fn(1, 2, 3); 29 | assert.deepEqual([2, 3], rest); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/es6katas/19-RestWithDestructuringTest.js: -------------------------------------------------------------------------------- 1 | // 19: rest - with-destructuring 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("rest with destructuring", () => { 5 | it("rest parameter must be last", () => { 6 | const [...all] = [1, 2, 3, 4]; 7 | assert.deepEqual(all, [1, 2, 3, 4]); 8 | }); 9 | 10 | it("assign rest of an array to a variable", () => { 11 | const [, ...all] = [1, 2, 3, 4]; 12 | assert.deepEqual(all, [2, 3, 4]); 13 | }); 14 | 15 | // the following are actually using `spread` ... oops, to be fixed 16 | it("concat differently", () => { 17 | const theEnd = [3, 4]; 18 | const allInOne = [1, 2, ...[...theEnd]]; 19 | assert.deepEqual(allInOne, [1, 2, 3, 4]); 20 | }); 21 | 22 | it("`apply` made simple, even for constructors", () => { 23 | const theDate = [2015, 1, 1]; 24 | const date = new Date(...theDate); 25 | assert.deepEqual(new Date(2015, 1, 1), date); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/es6katas/2-TemplateStringsMultilineTest.js: -------------------------------------------------------------------------------- 1 | // 2: template strings - multiline 2 | // To do: make all tests pass, leave the asserts unchanged! 3 | 4 | describe("template string, can contain multiline content", function() { 5 | it("a normal string can`t span across multiple lines", function() { 6 | var normalString = `line1\nline2`; 7 | assert.equal(normalString, "line1\nline2"); 8 | }); 9 | 10 | it("wrapped in backticks it can span over multiple lines", function() { 11 | var templateString = `line1 12 | line2`; 13 | assert.equal(templateString, "line1\nline2"); 14 | }); 15 | 16 | it("even over more than two lines", function() { 17 | var multiline = `line 1 18 | line 2 19 | line 3 20 | line 4`; 21 | assert.equal(multiline.split("\n").length, 4); 22 | }); 23 | 24 | describe("and expressions inside work too", function() { 25 | var x = 42; 26 | 27 | it("like simple variables", function() { 28 | var multiline = `line 1 29 | ${x}`; 30 | assert.equal(multiline, "line 1\n 42"); 31 | }); 32 | 33 | it("also here spaces matter", function() { 34 | var multiline = ` 35 | ${x}`; 36 | assert.equal(multiline, "\n42"); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/es6katas/20-SpreadWithArraysTest.js: -------------------------------------------------------------------------------- 1 | // 20: spread - with-arrays 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("spread with arrays", () => { 5 | it("extracts each array item", function() { 6 | const [a, b] = [...[1, 2]]; 7 | assert.equal(a, 1); 8 | assert.equal(b, 2); 9 | }); 10 | 11 | it("in combination with rest", function() { 12 | const [a, b, ...rest] = [...[1, 2, 3, 4, 5]]; 13 | assert.equal(a, 1); 14 | assert.equal(b, 2); 15 | assert.deepEqual(rest, [3, 4, 5]); 16 | }); 17 | 18 | it("spreading into the rest", function() { 19 | const [, ...rest] = [...[, 1, 2, 3, 4, 5]]; // eslint-disable-line 20 | assert.deepEqual(rest, [1, 2, 3, 4, 5]); 21 | }); 22 | 23 | describe("used as function parameter", () => { 24 | it("prefix with `...` to spread as function params", function() { 25 | const magicNumbers = [1, 2]; 26 | const fn = (magicA, magicB) => { 27 | assert.deepEqual(magicNumbers[0], magicA); 28 | assert.deepEqual(magicNumbers[1], magicB); 29 | }; 30 | fn(...magicNumbers); 31 | }); 32 | 33 | it("pass an array of numbers to Math.max()", function() { 34 | const max = Math.max(...[23, 0, 42, 41]); 35 | assert.equal(max, 42); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/es6katas/21-SpreadWithStringsTest.js: -------------------------------------------------------------------------------- 1 | // 21: spread - with-strings 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("spread with strings", () => { 5 | it("simply spread each char of a string", function() { 6 | const [a, b] = [..."ab"]; 7 | assert.equal(a, "a"); 8 | assert.equal(b, "b"); 9 | }); 10 | 11 | it("extracts each array item", function() { 12 | const [, a, b] = ["a", ..."12"]; 13 | assert.equal(a, 1); 14 | assert.equal(b, 2); 15 | }); 16 | 17 | it("works anywhere inside an array (must not be last)", function() { 18 | const letters = ["a", ..."bcd", "e", "f"]; 19 | assert.equal(letters.length, 6); 20 | }); 21 | 22 | it("dont confuse with the rest operator", function() { 23 | const [...rest] = [..."1234", ..."5"]; 24 | assert.deepEqual(rest, [1, 2, 3, 4, 5]); 25 | }); 26 | 27 | it("passed as function parameter", function() { 28 | const max = Math.max(..."12345"); 29 | assert.deepEqual(max, 5); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/es6katas/22-ClassCreationTest.js: -------------------------------------------------------------------------------- 1 | // 22: class - creation 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("class creation", () => { 5 | it("is as simple as `class XXX {}`", function() { 6 | class TestClass {} 7 | 8 | const instance = new TestClass(); 9 | assert.equal(typeof instance, "object"); 10 | }); 11 | 12 | it("class is block scoped", () => { 13 | { 14 | class Inside {} // eslint-disable-line 15 | { 16 | class Inside {} // eslint-disable-line 17 | } 18 | } 19 | assert.equal(typeof Inside, "undefined"); 20 | }); 21 | 22 | it("special method is `constructor`", function() { 23 | class User { 24 | constructor(id) { 25 | this.id = id; 26 | } 27 | } 28 | 29 | const user = new User(42); 30 | assert.equal(user.id, 42); 31 | }); 32 | 33 | it("defining a method is simple", function() { 34 | class User { 35 | writesTests() { 36 | return false; 37 | } 38 | } 39 | 40 | const notATester = new User(); 41 | assert.equal(notATester.writesTests(), false); 42 | }); 43 | 44 | it("multiple methods need no commas (opposed to object notation)", function() { 45 | class User { 46 | wroteATest() { 47 | this.everWroteATest = true; 48 | } 49 | isLazy() { 50 | return !this.everWroteATest; 51 | } 52 | } 53 | 54 | const tester = new User(); 55 | assert.equal(tester.isLazy(), true); 56 | tester.wroteATest(); 57 | assert.equal(tester.isLazy(), false); 58 | }); 59 | 60 | it("anonymous class", () => { 61 | const classType = typeof function() {}; 62 | assert.equal(classType, "function"); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/es6katas/23-ClassAccessorsTest.js: -------------------------------------------------------------------------------- 1 | // 23: class - accessors 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("class accessors (getter and setter)", () => { 5 | it("only a getter is defined like a method prefixed with `get`", () => { 6 | class MyAccount { 7 | get balance() { 8 | return Infinity; 9 | } 10 | } 11 | 12 | assert.equal(new MyAccount().balance, Infinity); 13 | }); 14 | 15 | it("a setter has the prefix `set`", () => { 16 | class MyAccount { 17 | get balance() { 18 | return this.amount; 19 | } 20 | set balance(amount) { 21 | this.amount = amount; 22 | } 23 | } 24 | 25 | const account = new MyAccount(); 26 | account.balance = 23; 27 | assert.equal(account.balance, 23); 28 | }); 29 | 30 | describe("dynamic accessors", () => { 31 | it("a dynamic getter name is enclosed in [ and ]", function() { 32 | const balance = "yourMoney"; 33 | class YourAccount { 34 | get [balance]() { 35 | return -Infinity; 36 | } 37 | } 38 | 39 | assert.equal(new YourAccount().yourMoney, -Infinity); 40 | }); 41 | 42 | it("a dynamic setter name as well", function() { 43 | const propertyName = "balance"; 44 | class MyAccount { 45 | get [propertyName]() { 46 | return this.amount; 47 | } 48 | set [propertyName](amount) { 49 | this.amount = 23; 50 | } 51 | } 52 | 53 | const account = new MyAccount(); 54 | account.balance = 42; 55 | assert.equal(account.balance, 23); 56 | }); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/es6katas/24-ClassStaticKeywordTest.js: -------------------------------------------------------------------------------- 1 | // 24: class - static keyword 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("inside a class you can use the `static` keyword", () => { 5 | describe("for methods", () => { 6 | class IntegrationTest {} // eslint-disable-line 7 | class UnitTest {} 8 | 9 | it("a static method just has the prefix `static`", () => { 10 | class TestFactory { 11 | static makeTest() { 12 | return new UnitTest(); 13 | } 14 | } 15 | 16 | assert.ok(TestFactory.makeTest() instanceof UnitTest); 17 | }); 18 | 19 | it("the method name can be dynamic/computed at runtime", () => { 20 | const methodName = "createTest"; 21 | class TestFactory { 22 | static [methodName]() { 23 | return new UnitTest(); 24 | } 25 | } 26 | 27 | assert.ok(TestFactory.createTest() instanceof UnitTest); 28 | }); 29 | }); 30 | 31 | describe("for accessors", () => { 32 | it("a getter name can be static, just prefix it with `static`", () => { 33 | class UnitTest { 34 | static get testType() { 35 | return "unit"; 36 | } 37 | } 38 | 39 | assert.equal(UnitTest.testType, "unit"); 40 | }); 41 | 42 | it("even a static getter name can be dynamic/computed at runtime", () => { 43 | const type = "test" + "Type"; 44 | class IntegrationTest { 45 | static get [type]() { 46 | return "integration"; 47 | } 48 | } 49 | 50 | assert.ok("testType" in IntegrationTest); 51 | assert.equal(IntegrationTest.testType, "integration"); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/es6katas/25-ClassExtendsTest.js: -------------------------------------------------------------------------------- 1 | // 25: class - extends 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("classes can inherit from another", () => { 5 | describe("the default super class is Object", () => { 6 | it("class A is an instance of Object", () => { 7 | class A {} 8 | 9 | assert.equal(new A() instanceof Object, true); 10 | }); 11 | 12 | it("B extends A, B is also instance of Object", () => { 13 | class A {} 14 | class B extends A {} 15 | 16 | assert.equal(new B() instanceof A, true); 17 | assert.equal(new B() instanceof Object, true); 18 | }); 19 | }); 20 | 21 | describe("instance of", () => { 22 | it("when B inherits from A, `new B()` is also an instance of A", () => { 23 | class A {} 24 | class B extends A {} 25 | 26 | assert.equal(new B() instanceof A, true); 27 | }); 28 | 29 | it("extend over multiple levels", () => { 30 | class A {} 31 | class B extends A {} 32 | class C extends B {} 33 | 34 | let instance = new C(); 35 | assert.equal(instance instanceof A, true); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/es6katas/26-ClassMoreExtendsTest.js: -------------------------------------------------------------------------------- 1 | // 26: class - more-extends 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("class can inherit from another", () => { 5 | it('extend an `old style` "class", a function, still works', () => { 6 | class A {} 7 | class B extends A {} 8 | 9 | assert.equal(new B() instanceof A, true); 10 | }); 11 | 12 | describe("prototypes are as you know them", () => { 13 | class A {} 14 | class B extends A {} 15 | it("A is the prototype of B", () => { 16 | const isIt = A.isPrototypeOf(B); 17 | assert.equal(isIt, true); 18 | }); 19 | it("A`s prototype is also B`s prototype", () => { 20 | const proto = B.prototype; 21 | // Remember: don't touch the assert!!! :) 22 | assert.equal(A.prototype.isPrototypeOf(proto), true); 23 | }); 24 | }); 25 | 26 | describe("`extends` using an expression", () => { 27 | it("eg the inline assignment of the parent class", () => { 28 | let A; 29 | class B extends (A = class {}) {} 30 | 31 | assert.equal(new B() instanceof A, true); 32 | }); 33 | 34 | it("or calling a function that returns the parent class", () => { 35 | const returnParent = beNull => (beNull ? null : class {}); 36 | class B extends returnParent(true) {} 37 | 38 | assert.equal(Object.getPrototypeOf(B.prototype), null); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/es6katas/27-ClassSuperInsideAMethodTest.js: -------------------------------------------------------------------------------- 1 | // 27: class - super inside a method 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("inside a class use `super` to access parent methods", () => { 5 | it("use of `super` without `extends` fails (already when transpiling)", () => { 6 | class A { 7 | hasSuper() { 8 | return false; 9 | } 10 | } 11 | 12 | assert.equal(new A().hasSuper(), false); 13 | }); 14 | 15 | it("`super` with `extends` calls the method of the given name of the parent class", () => { 16 | class A { 17 | hasSuper() { 18 | return true; 19 | } 20 | } 21 | class B extends A { 22 | hasSuper() { 23 | return super.hasSuper(); 24 | } 25 | } 26 | 27 | assert.equal(new B().hasSuper(), true); 28 | }); 29 | 30 | it("when overridden a method does NOT automatically call its super method", () => { 31 | class A { 32 | hasSuper() { 33 | return true; 34 | } 35 | } 36 | class B extends A { 37 | hasSuper() { 38 | return undefined; 39 | } 40 | } 41 | 42 | assert.equal(new B().hasSuper(), void 0); 43 | }); 44 | 45 | it("`super` works across any number of levels of inheritance", () => { 46 | class A { 47 | iAmSuper() { 48 | return this.youAreSuper; 49 | } 50 | } 51 | class B extends A { 52 | constructor() { 53 | super(); 54 | this.youAreSuper = true; 55 | } 56 | } 57 | class C extends B { 58 | iAmSuper() { 59 | return super.iAmSuper(); 60 | } 61 | } 62 | 63 | assert.equal(new C().iAmSuper(), true); 64 | }); 65 | 66 | it("accessing an undefined member of the parent class returns `undefined`", () => { 67 | class A {} 68 | class B extends A { 69 | getMethod() { 70 | return super.attribute; 71 | } 72 | } 73 | 74 | assert.equal(new B().getMethod(), void 0); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /test/es6katas/28-ClassSuperInConstructorTest.js: -------------------------------------------------------------------------------- 1 | // 28: class - super in constructor 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("class", () => { 5 | it("if you `extend` a class, use `super()` to call the parent constructor", () => { 6 | class A { 7 | constructor() { 8 | this.levels = 1; 9 | } 10 | } 11 | class B extends A { 12 | constructor() { 13 | super(); 14 | this.levels++; 15 | } 16 | } 17 | 18 | assert.equal(new B().levels, 2); 19 | }); 20 | 21 | it("`super()` may also take params", () => { 22 | class A { 23 | constructor(startValue = 1, addTo = 1) { 24 | this.counter = startValue + addTo; 25 | } 26 | } 27 | class B extends A { 28 | constructor(...args) { 29 | super(...args); 30 | this.counter++; 31 | } 32 | } 33 | 34 | assert.equal(new B(42, 2).counter, 45); 35 | }); 36 | 37 | it("it is important where you place your `super()` call!", () => { 38 | class A { 39 | inc() { 40 | this.countUp = 1; 41 | } 42 | } 43 | class B extends A { 44 | inc() { 45 | this.countUp = 2; 46 | super.inc(); 47 | return this.countUp; 48 | } 49 | } 50 | 51 | assert.equal(new B().inc(), 1); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /test/es6katas/29-ArrayFromTest.js: -------------------------------------------------------------------------------- 1 | // 29: array - `Array.from` static method 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Array.from` converts an array-like object or list into an Array", () => { 5 | const arrayLike = { 0: "one", 1: "two", length: 2 }; 6 | 7 | it("call `Array.from` with an array-like object", function() { 8 | const arr = Array.from(arrayLike); 9 | 10 | assert.deepEqual(arr, ["one", "two"]); 11 | }); 12 | 13 | describe("custom conversion using a map function as second param", () => { 14 | it("we can modify the value before putting it in the array", function() { 15 | const arr = Array.from(arrayLike, value => value.toUpperCase()); 16 | assert.deepEqual(arr, ["ONE", "TWO"]); 17 | }); 18 | 19 | it("and we also get the object`s key as second parameter", function() { 20 | const arr = Array.from(arrayLike, (key, value) => `${value}=${key}`); 21 | assert.deepEqual(arr, ["0=one", "1=two"]); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/es6katas/3-TemplateStringsTaggedTest.js: -------------------------------------------------------------------------------- 1 | // 3: template strings - tagged 2 | // To do: make all tests pass, leave the asserts unchanged! 3 | 4 | describe("tagged template strings, are an advanced form of template strings", function() { 5 | it('syntax: prefix the template string with a function to call (without "()" around it)', function() { 6 | function tagFunction(s) { 7 | return s; 8 | } 9 | var evaluated = tagFunction`template string`; 10 | assert.equal(evaluated, "template string"); 11 | }); 12 | 13 | describe("the function can access each part of the template", function() { 14 | describe("the 1st parameter - receives only the pure strings of the template string", function() { 15 | function tagFunction(strings) { 16 | return strings; 17 | } 18 | 19 | it("the strings are an array", function() { 20 | var result = tagFunction`template string`; 21 | assert.deepEqual(tagFunction`template string`, result); 22 | }); 23 | 24 | it("expressions are NOT passed to it", function() { 25 | var tagged = tagFunction`one${23}two`; 26 | assert.deepEqual(tagged, ["one", "two"]); 27 | }); 28 | }); 29 | 30 | describe("the 2nd and following parameters - contain the values of the processed substitution", function() { 31 | var one = 1; 32 | var two = 2; 33 | var three = 3; 34 | it("the 2nd parameter contains the first expression`s value", function() { 35 | function firstValueOnly(strings, firstValue) { 36 | return firstValue; 37 | } 38 | assert.equal(firstValueOnly`uno ${one}, dos ${two}`, 1); 39 | }); 40 | 41 | it("the 3rd parameter contains the second expression`s value", function() { 42 | function firstValueOnly(strings, firstValue, secondValue) { 43 | return secondValue; 44 | } 45 | assert.equal(firstValueOnly`uno ${one}, dos ${two}`, 2); 46 | }); 47 | 48 | it("using ES6 rest syntax, all values can be accessed via one variable", function() { 49 | function valuesOnly(stringsArray, ...allValues) { 50 | // using the new ES6 rest syntax 51 | return allValues; 52 | } 53 | assert.deepEqual(valuesOnly`uno=${one}, dos=${two}, tres=${three}`, [ 54 | 1, 55 | 2, 56 | 3 57 | ]); 58 | }); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /test/es6katas/30-ArrayOfStaticMethodTest.js: -------------------------------------------------------------------------------- 1 | // 30: array - `Array.of` static method 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Array.of` creates an array with the given arguments as elements", () => { 5 | it("dont mix it up with `Array(10)`, where the argument is the array length", () => { 6 | const arr = Array.of(10); 7 | 8 | assert.deepEqual(arr, [10]); 9 | }); 10 | 11 | it("puts all arguments into array elements", () => { 12 | const arr = Array.of(1, 2); 13 | 14 | assert.deepEqual(arr, [1, 2]); 15 | }); 16 | 17 | it("takes any kind and number of arguments", () => { 18 | const starter = [[1, 2]]; 19 | const end = [3, "4"]; 20 | const arr = Array.of(...starter, ...end); 21 | 22 | assert.deepEqual(arr, [[1, 2], 3, "4"]); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/es6katas/31-ArrayFillTest.js: -------------------------------------------------------------------------------- 1 | // 31: array - `Array.prototype.fill` method 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Array.prototype.fill` can fill up an array with one value", () => { 5 | it("`fill(0)` will populate `0` into each array element", function() { 6 | const arr = new Array(3).fill(0); 7 | 8 | assert.deepEqual(arr, [0, 0, 0]); 9 | }); 10 | 11 | it("fill only changes content, adds no new elements", function() { 12 | const arr = [].fill(0); 13 | 14 | assert.deepEqual(arr, []); 15 | }); 16 | 17 | it("second parameter to `fill()` is the position where to start filling", function() { 18 | const fillPosition = 2; 19 | const arr = [1, 2, 3].fill(42, fillPosition); 20 | 21 | assert.deepEqual(arr, [1, 2, 42]); 22 | }); 23 | 24 | it("third parameter is the position where filling stops", function() { 25 | const fillStartAt = 1; 26 | const fillEndAt = 2; 27 | const arr = [1, 2, 3].fill(42, fillStartAt, fillEndAt); 28 | 29 | assert.deepEqual(arr, [1, 42, 3]); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/es6katas/32-ArrayFindTest.js: -------------------------------------------------------------------------------- 1 | // 32: array - `Array.prototype.find` 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Array.prototype.find` makes finding items in arrays easier", () => { 5 | it("takes a compare function", function() { 6 | const found = [false, true].find(item => item === true); 7 | 8 | assert.equal(found, true); 9 | }); 10 | 11 | it("returns the first value found", function() { 12 | const found = [0, 1, 2].find(item => item > 1); 13 | 14 | assert.equal(found, 2); 15 | }); 16 | 17 | it("returns `undefined` when nothing was found", function() { 18 | const found = [1, 3].find(item => item === 2); 19 | 20 | assert.equal(found, void 0); 21 | }); 22 | 23 | it("combined with destructuring complex compares become short", function() { 24 | const bob = { name: "Bob" }; 25 | const alice = { name: "Alice" }; 26 | const found = [bob, alice].find(({ name: { length } }) => length === 5); 27 | 28 | assert.equal(found, alice); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/es6katas/33-ArrayFindIndexTest.js: -------------------------------------------------------------------------------- 1 | // 33: array - `Array.prototype.findIndex` 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Array.prototype.findIndex` makes finding items in arrays easier", () => { 5 | it("takes a compare function, returns the index where it returned true", function() { 6 | const foundAt = [false, true].findIndex(item => item === true); 7 | 8 | assert.equal(foundAt, 1); 9 | }); 10 | 11 | it("returns the first position it was found at", function() { 12 | const foundAt = [0, 1, 1, 1].findIndex(item => item === 1); 13 | 14 | assert.equal(foundAt, 1); 15 | }); 16 | 17 | it("returns `-1` when nothing was found", function() { 18 | const foundAt = [1, 2, 3].findIndex(item => item < 1); 19 | 20 | assert.equal(foundAt, -1); 21 | }); 22 | 23 | it("the findIndex callback gets the item, index and array as arguments", function() { 24 | const three = 3; 25 | const containsThree = arr => arr.indexOf(three) > -1; 26 | function theSecondThree(item, index, arr) { 27 | const preceedingItems = arr.slice(0, index); 28 | return containsThree(preceedingItems); 29 | } 30 | const foundAt = [1, 1, 2, 3, 3, 3].findIndex(theSecondThree); 31 | 32 | assert.equal(foundAt, 4); 33 | }); 34 | 35 | it("combined with destructuring complex compares become short", function() { 36 | const bob = { name: "Bob" }; 37 | const alice = { name: "Alice" }; 38 | const foundAt = [bob, alice].findIndex( 39 | ({ name: { length } }) => length > 3 40 | ); 41 | 42 | assert.equal(foundAt, 1); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/es6katas/34-SymbolTest.js: -------------------------------------------------------------------------------- 1 | // 34: symbol 2 | // A symbol is a unique and immutable data type and may be used as an identifier for object properties 3 | // read more at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol 4 | 5 | // To do: make all tests pass, leave the assert lines unchanged! 6 | 7 | describe("Symbol", function() { 8 | it("every `Symbol()` is unique", function() { 9 | const sym1 = Symbol(); 10 | const sym2 = Symbol(); 11 | assert.notEqual(sym1, sym2); 12 | }); 13 | 14 | it("every `Symbol()` is unique, also with the same parameter", function() { 15 | const sym1 = Symbol("foo"); 16 | const sym2 = Symbol("foo"); 17 | assert.notEqual(sym1, sym2); 18 | }); 19 | 20 | it('`typeof Symbol()` returns "symbol"', function() { 21 | const theType = typeof Symbol(); 22 | assert.equal(theType, "symbol"); 23 | }); 24 | 25 | it("`new Symbol()` throws an exception, to prevent creation of Symbol wrapper objects", function() { 26 | function fn() { 27 | new Symbol(); // eslint-disable-line 28 | } 29 | assert.throws(fn); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/es6katas/35-SymbolForTest.js: -------------------------------------------------------------------------------- 1 | // 35: Symbol.for - retrieves or creates a runtime-wide symbol 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Symbol.for` for registering Symbols globally", function() { 5 | it("creates a new symbol (check via `typeof`)", function() { 6 | const symbolType = typeof Symbol.for("symbol name"); 7 | assert.equal(symbolType, "symbol"); 8 | }); 9 | 10 | it("stores the symbol in a runtime-wide registry and retrieves it from it", function() { 11 | const sym = Symbol.for("new symbol"); 12 | const sym1 = Symbol.for("new symbol"); 13 | 14 | assert.equal(sym, sym1); 15 | }); 16 | 17 | it("is different to `Symbol()` which creates a symbol every time and does not store it", function() { 18 | var globalSymbol = Symbol.for("new symbol"); 19 | var localSymbol = Symbol("new symbol"); 20 | 21 | assert.notEqual(globalSymbol, localSymbol); 22 | }); 23 | 24 | describe("`.toString()` on a Symbol", function() { 25 | const localSymbol = Symbol("new symbol"); 26 | const symbolFromRegistry = Symbol.for("new symbol"); 27 | 28 | it("also contains the key given to `Symbol.for()`", function() { 29 | const description = localSymbol.toString(); 30 | assert.equal(description, "Symbol(new symbol)"); 31 | }); 32 | 33 | describe("NOTE: the description of two different symbols", function() { 34 | it("might be the same", function() { 35 | const localDescription = localSymbol.toString(); 36 | const fromRegistryDescription = symbolFromRegistry.toString(); 37 | 38 | assert.equal(localDescription, fromRegistryDescription); 39 | }); 40 | 41 | it("but the symbols are not the same!", function() { 42 | assert.notEqual(localSymbol, symbolFromRegistry); 43 | }); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/es6katas/36-KeyForTest.js: -------------------------------------------------------------------------------- 1 | // 36: Symbol.keyFor - retrieves a shared symbol key from the global symbol registry 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Symbol.keyFor()` gets the symbol key for a given symbol", function() { 5 | const sym = Symbol.for("foo"); 6 | 7 | it("pass the symbol to `keyFor()` and you get its key", function() { 8 | const key = Symbol.keyFor(sym); 9 | 10 | assert.equal(key, "foo"); 11 | }); 12 | 13 | it("local symbols are not in the runtime-wide registry", function() { 14 | // hint: `Symbol()` creates a local symbol! 15 | const localSymbol = Symbol(); 16 | const key = Symbol.keyFor(localSymbol); 17 | 18 | assert.equal(key, void 0); 19 | }); 20 | 21 | it("predefined symbols are not in the runtime-wide registry either", function() { 22 | const key = Symbol.keyFor(Symbol.iterator); 23 | 24 | assert.equal(key, void 0); 25 | }); 26 | 27 | it("for non-Symbols throws an error", function() { 28 | function fn() { 29 | Symbol.keyFor(""); 30 | } 31 | 32 | assert.throws(fn); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/es6katas/37-IteratorIterableArrayTest.js: -------------------------------------------------------------------------------- 1 | // 37: iterator/iterable - array. 2 | // The iterator protocol defines a standard way to produce a sequence of values (either finite or infinite). 3 | // read more at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols 4 | 5 | // To do: make all tests pass, leave the assert lines unchanged! 6 | 7 | describe("array is a built-in iterable object", function() { 8 | const arr = ["a", "B", "see"]; 9 | 10 | describe("the iterator", function() { 11 | it("an array has an iterator, which is a function", function() { 12 | const iterator = arr[Symbol.iterator]; 13 | const theType = typeof iterator; 14 | const expected = "function"; 15 | 16 | assert.equal(theType, expected); 17 | }); 18 | 19 | it("can be looped with `for-of`, which expects an iterable", function() { 20 | let count = 0; 21 | for (let a of arr) { // eslint-disable-line 22 | count++; 23 | } 24 | 25 | assert.equal(count, arr.length); 26 | }); 27 | }); 28 | 29 | describe("the iterator protocol", function() { 30 | it("calling `next()` on an iterator returns an object according to the iterator protocol", function() { 31 | const iterator = arr[Symbol.iterator](); 32 | const firstItem = iterator.next(); 33 | 34 | assert.deepEqual(firstItem, { done: false, value: "a" }); 35 | }); 36 | 37 | it("the after-last element has done=true", function() { 38 | const arr = []; 39 | const iterator = arr[Symbol.iterator](); 40 | const afterLast = iterator.next(); 41 | 42 | assert.deepEqual(afterLast, { done: true, value: void 0 }); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/es6katas/38-IteratorIterableStringTest.js: -------------------------------------------------------------------------------- 1 | // 38: iterator/iterable - string. 2 | // The iterator protocol defines a standard way to produce a sequence of values (either finite or infinite). 3 | // read more at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols 4 | 5 | // To do: make all tests pass, leave the assert lines unchanged! 6 | 7 | describe("string is a built-in iterable object", function() { 8 | const s = "abc"; 9 | 10 | describe("string is iterable", function() { 11 | it("the string`s object key `Symbol.iterator` is a function", function() { 12 | const isA = typeof s[Symbol.iterator]; 13 | assert.equal(isA, "function"); 14 | }); 15 | it("use `Array.from()` to make an array out of any iterable", function() { 16 | const arr = Array.from(s); 17 | assert.deepEqual(arr, ["a", "b", "c"]); 18 | }); 19 | }); 20 | 21 | describe("a string`s iterator", function() { 22 | let iterator; 23 | beforeEach(function() { 24 | iterator = s[Symbol.iterator](); 25 | }); 26 | 27 | it("has a special string representation", function() { 28 | const description = iterator.toString(); 29 | assert.equal(description, "[object String Iterator]"); 30 | }); 31 | 32 | it("`iterator.next()` returns an object according to the iterator protocol", function() { 33 | const value = iterator.next(); 34 | assert.deepEqual(value, { done: false, value: "a" }); 35 | }); 36 | 37 | it("the after-last call to `iterator.next()` says done=true, no more elements", function() { 38 | iterator.next(); 39 | iterator.next(); 40 | iterator.next(); 41 | assert.equal(iterator.next().done, true); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/es6katas/39-IteratorCustomTest.js: -------------------------------------------------------------------------------- 1 | // 39: iterator - custom. Iterable is a protocol, when implemented allows objects 2 | // to customize their iteration behavior, such as what values are looped over in a for..of construct. 3 | // read more at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols 4 | 5 | // To do: make all tests pass, leave the assert lines unchanged! 6 | // Follow the hints of the failure messages! 7 | 8 | describe("A simple iterable without items inside, implementing the right protocol", () => { 9 | function iteratorFunction() { 10 | return { 11 | next() { 12 | return { 13 | done: true 14 | }; 15 | } 16 | }; 17 | } 18 | 19 | describe("the `iteratorFunction` needs to comply to the iterator protocol", function() { 20 | it("must return an object", function() { 21 | assert.equal(typeof iteratorFunction(), "object"); 22 | }); 23 | it("the object must have a function assigned to a key `next`", function() { 24 | assert.equal(typeof iteratorFunction().next, "function"); 25 | }); 26 | it("calling `next()` must return an object with `{done: true}`", function() { 27 | assert.deepEqual(iteratorFunction().next(), { done: true }); 28 | }); 29 | }); 30 | 31 | let iterable; 32 | beforeEach(function() { 33 | iterable = { 34 | [Symbol.iterator]: iteratorFunction 35 | }; 36 | }); 37 | 38 | describe("the iterable", function() { 39 | it("must be an object", function() { 40 | assert.equal(typeof iterable, "object"); 41 | }); 42 | it("must have the iterator function assigned to the key `Symbol.iterator`", function() { 43 | assert.equal(iterable[Symbol.iterator], iteratorFunction); 44 | }); 45 | }); 46 | 47 | describe("using the iterable", function() { 48 | it("it contains no values", function() { 49 | let values = ""; 50 | for (let value of iterable) { 51 | values += value; 52 | } 53 | assert.equal(values, ""); 54 | }); 55 | 56 | it("has no `.length` property", function() { 57 | const hasLengthProperty = "length" in iterable; 58 | assert.equal(hasLengthProperty, false); 59 | }); 60 | 61 | describe("can be converted to an array", function() { 62 | it("using `Array.from()`", function() { 63 | const arr = Array.from(iterable); 64 | assert.equal(Array.isArray(arr), true); 65 | }); 66 | 67 | it("where `.length` is still 0", function() { 68 | const arr = Array.from(iterable); 69 | const length = arr.length; 70 | assert.equal(length, 0); 71 | }); 72 | }); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/es6katas/4-TemplateStringsRawStringTest.js: -------------------------------------------------------------------------------- 1 | // 4: template strings - String.raw 2 | // To do: make all tests pass, leave the asserts unchanged! 3 | 4 | describe("on tagged template strings you can use the `raw` property like so `s.raw`", function() { 5 | it("the `raw` property accesses the string as it was entered", function() { 6 | function firstChar(strings) { 7 | return strings.raw; 8 | } 9 | assert.equal(firstChar`\n`, "\\n"); 10 | }); 11 | 12 | it("`raw` can access the backslash of a line-break", function() { 13 | function firstCharEntered(strings) { 14 | var lineBreak = strings.raw; 15 | return lineBreak[0][0]; 16 | } 17 | assert.equal(firstCharEntered`\n`, "\\"); 18 | }); 19 | 20 | describe("`String.raw` as a static function", function() { 21 | it("concats the raw strings", function() { 22 | var expected = "\\n"; 23 | assert.equal(String.raw`\n`, expected); 24 | }); 25 | 26 | it("two raw-templates-string-backslashes equal two escaped backslashes", function() { 27 | const TWO_BACKSLASHES = "\\\\"; 28 | assert.equal(String.raw`\\`, TWO_BACKSLASHES); 29 | }); 30 | 31 | it("works on unicodes too", function() { 32 | var smilie = "\\u{1F600}"; 33 | var actual = String.raw`\u{1F600}`; 34 | assert.equal(actual, smilie); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/es6katas/40-IteratorExampleUsageTest.js: -------------------------------------------------------------------------------- 1 | // 40: iterator - one example usage. Build an iterable and use it with some built-in ES6 constructs. 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | // Consumable users: 5 | // - `consumableUser` contains a consumable user, 6 | // - `anyLeft` tells if there is any user left that can be consumed. 7 | 8 | class ConsumableUsers { 9 | constructor() { 10 | this.users = ["Alice", "Bob"]; 11 | this.empty = false; 12 | } 13 | get nextUser() { 14 | if (this.users.length > 0) { 15 | return `user: ${this.users.shift()}`; 16 | } 17 | this.empty = true; 18 | return void 0; 19 | } 20 | get anyLeft() { 21 | return this.empty; 22 | } 23 | } 24 | 25 | describe("Iterator usages", () => { 26 | let usersIterable; 27 | beforeEach(function() { 28 | const consumableUsers = new ConsumableUsers(); 29 | function iteratorFunction() { 30 | return { 31 | next: function() { 32 | return { 33 | value: consumableUsers.nextUser, 34 | done: consumableUsers.anyLeft 35 | }; 36 | } 37 | }; 38 | } 39 | 40 | usersIterable = {}; 41 | usersIterable[Symbol.iterator] = iteratorFunction; 42 | }); 43 | 44 | describe("create an iterator/iterable", function() { 45 | it("the `usersIterable` should be iterable", function() { 46 | const isIterable = Symbol.iterator in usersIterable; 47 | assert.equal(isIterable, true); 48 | }); 49 | 50 | it("the iterator of `usersIterable` should return an object", function() { 51 | const iterator = usersIterable[Symbol.iterator](); 52 | assert.equal(typeof iterator, "object"); 53 | }); 54 | 55 | it("the iterator of `usersIterable` should have a next function", function() { 56 | const iterator = usersIterable[Symbol.iterator](); 57 | assert.equal(typeof iterator.next, "function"); 58 | }); 59 | }); 60 | 61 | describe("fill the iterable with content using `ConsumableUsers`", function() { 62 | describe("using the iterator", function() { 63 | let iterator; 64 | beforeEach(function() { 65 | iterator = usersIterable[Symbol.iterator](); 66 | }); 67 | it("should return `Alice` as first user", function() { 68 | const firstItem = iterator.next(); 69 | assert.deepEqual(firstItem, { value: "user: Alice", done: false }); 70 | }); 71 | it("should return `Bob` as second user", function() { 72 | iterator.next(); // drop the first item 73 | const secondItem = iterator.next(); 74 | assert.deepEqual(secondItem, { value: "user: Bob", done: false }); 75 | }); 76 | it("should return `done:true`, which means there are no more items", function() { 77 | iterator.next(); 78 | iterator.next(); 79 | const beyondLast = iterator.next(); 80 | assert.deepEqual(beyondLast, { value: void 0, done: true }); 81 | }); 82 | }); 83 | 84 | describe("using built-in constructs", function() { 85 | it("use `Array.from()` to convert an iterable to an array", function() { 86 | const users = Array.from(usersIterable); 87 | assert.deepEqual(users, ["user: Alice", "user: Bob"]); 88 | }); 89 | it("use for-of to loop over an iterable", function() { 90 | const users = []; 91 | for (let user of usersIterable) users.push(user); 92 | assert.deepEqual(users, ["user: Alice", "user: Bob"]); 93 | }); 94 | it("use the spread-operator to convert/add iterable to an array", function() { 95 | const users = ["noname", ...usersIterable]; 96 | assert.deepEqual(users, ["noname", "user: Alice", "user: Bob"]); 97 | }); 98 | it("destructure an iterable like an array", function() { 99 | const [firstUser, secondUser] = usersIterable; 100 | assert.equal(firstUser, "user: Alice"); 101 | assert.equal(secondUser, "user: Bob"); 102 | }); 103 | }); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /test/es6katas/41-ArrayEntriesTest.js: -------------------------------------------------------------------------------- 1 | // 41: array - entries 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`[].entries()` returns an iterator object with all entries", function() { 5 | it("returns key+value for each element", function() { 6 | const arr = ["a", "b", "c"]; 7 | const entriesAsArray = Array.from(arr.entries()); 8 | 9 | assert.deepEqual(entriesAsArray, [[0, "a"], [1, "b"], [2, "c"]]); 10 | }); 11 | 12 | it("empty elements contain the value `undefined`", function() { 13 | const arr = ["one"]; 14 | arr[2] = "three"; 15 | const secondValue = Array.from(arr.entries())[1]; 16 | 17 | assert.deepEqual(secondValue, [1, void 0]); 18 | }); 19 | 20 | describe("returns an iterable", function() { 21 | it("has `next()` to iterate", function() { 22 | const arr = ["one"]; 23 | const value = arr.entries().next().value; 24 | 25 | assert.deepEqual(value, [0, "one"]); 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/es6katas/42-ArrayKeysTest.js: -------------------------------------------------------------------------------- 1 | // 42: array - `Array.prototype.keys` 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Array.prototype.keys` returns an iterator for all keys in the array", () => { 5 | it("`keys()` returns an iterator", function() { 6 | const arr = ["a"]; 7 | const iterator = arr.keys(); 8 | 9 | assert.deepEqual(iterator.next(), { value: 0, done: false }); 10 | assert.deepEqual(iterator.next(), { value: void 0, done: true }); 11 | }); 12 | 13 | it("gets all keys", function() { 14 | const arr = [0, 1, 2]; 15 | const keys = Array.from(arr.keys()); 16 | 17 | assert.deepEqual(keys, [0, 1, 2]); 18 | }); 19 | 20 | it("empty array contains no keys", function() { 21 | const arr = []; 22 | const keys = [...arr.keys()]; 23 | 24 | assert.equal(keys.length, 0); 25 | }); 26 | 27 | it("a sparse array without real values has keys though", function() { 28 | const arr = [, ,]; // eslint-disable-line 29 | const keys = [...arr.keys()]; 30 | 31 | assert.deepEqual(keys, [0, 1]); 32 | }); 33 | 34 | it("also includes holes in sparse arrays", function() { 35 | const arr = ["a", , "c"]; // eslint-disable-line 36 | const keys = [...arr.keys()]; 37 | 38 | assert.deepEqual(keys, [0, 1, 2]); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/es6katas/44-MapBasicsTest.js: -------------------------------------------------------------------------------- 1 | // 44: Map - basics 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Map` is a key/value map", function() { 5 | it("`Map` is a new global constructor function", function() { 6 | assert.equal(typeof Map, "function"); 7 | }); 8 | 9 | it("provides `new Map().set()` to add key+value pair, `get()` to read it by key", function() { 10 | let map = new Map(); 11 | map.set("key", "value"); 12 | const value = map.get("key"); 13 | 14 | assert.equal(value, "value"); 15 | }); 16 | 17 | it("`has()` tells if map has the given key", function() { 18 | let map = new Map(); 19 | map.set("key", "value"); 20 | const hasIt = map.has("key"); 21 | 22 | assert.equal(hasIt, true); 23 | }); 24 | 25 | it("a map is iterable", function() { 26 | let map = new Map(); 27 | map.set("1", "one"); 28 | map.set("2", "two"); 29 | const mapAsArray = Array.from(map); // hint: kata #29 http://tddbin.com/#?kata=es6/language/array-api/from 30 | 31 | assert.deepEqual(mapAsArray, [["1", "one"], ["2", "two"]]); 32 | }); 33 | 34 | it("complex types can be keys", function() { 35 | const obj = { x: 1 }; 36 | const otherObj = { x: 1 }; 37 | let map = new Map(); 38 | map.set(obj, ""); 39 | 40 | assert.equal(map.has(otherObj), false); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/es6katas/45-MapGetTest.js: -------------------------------------------------------------------------------- 1 | // 45: Map.prototype.get() 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Map.prototype.get` returns the element from the map for a key", function() { 5 | it("`get(key)` returns the value stored for this key", function() { 6 | let map = new Map(); 7 | map.set("key", "value"); 8 | 9 | const value = map.get("key"); 10 | assert.equal(value, "value"); 11 | }); 12 | 13 | it("multiple calls still return the same value", function() { 14 | let map = new Map(); 15 | map.set("value", "value"); 16 | 17 | var value = map.get(map.get(map.get("value"))); 18 | assert.equal(value, "value"); 19 | }); 20 | 21 | it("requires exactly the value as passed to `set()`", function() { 22 | let map = new Map(); 23 | const obj = {}; 24 | map.set(obj, "object is key"); 25 | 26 | assert.equal(map.get(obj), "object is key"); 27 | }); 28 | 29 | it("leave out the key, and you get the value set for the key `undefined` (void 0)", function() { 30 | let map = new Map(); 31 | map.set(void 0, "yo"); 32 | 33 | const value = map.get(); 34 | assert.equal(value, "yo"); 35 | }); 36 | 37 | it("returns undefined for an unknown key", function() { 38 | let map = new Map(); 39 | 40 | const value = map.get(); 41 | assert.equal(value, void 0); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/es6katas/46-MapSetTest.js: -------------------------------------------------------------------------------- 1 | // 46: Map.prototype.set() 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Map.prototype.set` adds a new element with key and value to a Map", function() { 5 | it("simplest use case is `set(key, value)` and `get(key)`", function() { 6 | let map = new Map(); 7 | map.set("key", "value"); 8 | 9 | assert.equal(map.get("key"), "value"); 10 | }); 11 | 12 | it("the key can be a complex type too", function() { 13 | const noop = function() {}; 14 | let map = new Map(); 15 | map.set(noop, "the real noop"); 16 | 17 | assert.equal(map.get(noop), "the real noop"); 18 | }); 19 | 20 | it("calling `set()` again with the same key replaces the value", function() { 21 | let map = new Map(); 22 | map.set("key", "value"); 23 | map.set("key", "value1"); 24 | 25 | assert.equal(map.get("key"), "value1"); 26 | }); 27 | 28 | it("`set()` returns the map object, it`s chainable", function() { 29 | let map = new Map(); 30 | map 31 | .set(1, "one") 32 | .set(2, "two") 33 | .set(3, "three"); 34 | 35 | assert.deepEqual([...map.keys()], [1, 2, 3]); 36 | assert.deepEqual([...map.values()], ["one", "two", "three"]); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/es6katas/47-SetBasicsTest.js: -------------------------------------------------------------------------------- 1 | // 47: Set - basics 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Set` lets you store unique values of any type", function() { 5 | it("`Set` is a new global constructor function", function() { 6 | assert.equal(typeof Set, "function"); 7 | }); 8 | 9 | it("every value in a set is unique", function() { 10 | let set = new Set(); 11 | 12 | set.add(1); 13 | set.add(2); 14 | const expectedSize = 2; 15 | 16 | assert.equal(set.size, expectedSize); 17 | }); 18 | 19 | it('the string "1" is different to the number 1', function() { 20 | let set = new Set(); 21 | set.add(1); 22 | set.add("1"); 23 | 24 | assert.equal(set.size, 2); 25 | }); 26 | 27 | it("even NaN is equal to NaN", function() { 28 | let set = new Set(); 29 | set.add(NaN); 30 | set.add(NaN); 31 | 32 | assert.equal(set.size, 1); 33 | }); 34 | 35 | it("+0 and -0 are seen as equal", function() { 36 | let set = new Set(); 37 | set.add(+0); 38 | set.add(-0); 39 | 40 | assert.deepEqual([...set.values()], [+0]); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/es6katas/48-SetAddTest.js: -------------------------------------------------------------------------------- 1 | // 48: Set - add 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`add()` appends a new element to the end of a Set object.", function() { 5 | let set; 6 | beforeEach(() => (set = new Set())); 7 | 8 | it("adds every value, of any type, only once", function() { 9 | const fn = () => {}; 10 | 11 | set.add(1); 12 | set.add(1); 13 | set.add(fn); 14 | set.add(fn); 15 | 16 | assert.equal(set.size, 2); 17 | }); 18 | 19 | it("is chainable", function() { 20 | set.add(1).add(2); 21 | 22 | assert.equal(set.has(2), true); 23 | }); 24 | 25 | it("call without params adds undefined", function() { 26 | set.add(); 27 | 28 | assert.equal(set.has(void 0), true); 29 | }); 30 | 31 | it("0, -0 and +0 are equal", function() { 32 | set.add(0); 33 | set.add(-0); 34 | set.add(+0); 35 | 36 | assert.equal(set.has(+0), true); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/es6katas/5-ArrowFunctionsBasicTest.js: -------------------------------------------------------------------------------- 1 | // 5: arrow functions - basics 2 | // To do: make all tests pass, leave the asserts unchanged! 3 | 4 | describe("arrow functions", function() { 5 | it("are shorter to write", function() { 6 | var func = () => { 7 | return "I am func"; 8 | }; 9 | assert.equal(func(), "I am func"); 10 | }); 11 | 12 | it("a single expression, without curly braces returns too", function() { 13 | var func = () => "I return too"; 14 | assert.equal(func(), "I return too"); 15 | }); 16 | 17 | it("one parameter can be written without parens", () => { 18 | var func = param => param - 1; 19 | assert.equal(func(25), 24); 20 | }); 21 | 22 | it("many params require parens", () => { 23 | var func = (param, param1) => param + param1; 24 | assert.equal(func(23, 42), 23 + 42); 25 | }); 26 | 27 | it("body needs parens to return an object", () => { 28 | var func = () => { 29 | return { iAm: "an object" }; 30 | }; 31 | assert.deepEqual(func(), { iAm: "an object" }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/es6katas/53-MapInitializeTest.js: -------------------------------------------------------------------------------- 1 | // 53: Map - initialize 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("initialize a `Map`", function() { 5 | it("a `new Map()` is empty, has size=0", function() { 6 | const mapSize = new Map().size; 7 | assert.equal(mapSize, 0); 8 | }); 9 | 10 | it("init Map with `[[]]` has a size=1", function() { 11 | const mapSize = new Map([[]]).size; 12 | 13 | assert.equal(mapSize, 1); 14 | }); 15 | 16 | it("init a Map with `[[1]]` is the same as `map.set(1, void 0)`", function() { 17 | let map1 = new Map([[1]]); 18 | let map2 = new Map().set(1, void 0); 19 | 20 | assertMapsEqual(map1, map2); 21 | }); 22 | 23 | it("init Map with multiple key+value pairs", function() { 24 | const pair1 = [1, "one"]; 25 | const pair2 = [2, "two"]; 26 | 27 | const map = new Map([pair1, pair2]); 28 | 29 | assertMapsEqual(map, new Map().set(...pair1).set(...pair2)); 30 | }); 31 | 32 | it("keys are unique, the last one is used", function() { 33 | const pair1 = [1, "one"]; 34 | const pair2 = [1, "uno"]; 35 | const pair3 = [1, "eins"]; 36 | const pair4 = [2, "two"]; 37 | 38 | const map = new Map([pair1, pair2, pair3, pair4]); 39 | 40 | assertMapsEqual(map, new Map().set(...pair3).set(...pair4)); 41 | }); 42 | 43 | it("init Map from an Object, is a bit of work", function() { 44 | let map = new Map(); 45 | const obj = { x: 1, y: 2 }; 46 | const keys = Object.keys(obj); 47 | keys.forEach(key => map.set(key, obj[key])); 48 | 49 | const expectedEntries = [["x", 1], ["y", 2]]; 50 | assertMapsEqual(map, expectedEntries); 51 | }); 52 | }); 53 | 54 | function mapToArray(map) { 55 | return Array.from(map); 56 | } 57 | function assertMapsEqual(map1, map2) { 58 | assert.deepEqual(mapToArray(map1), mapToArray(map2)); 59 | } 60 | -------------------------------------------------------------------------------- /test/es6katas/54-ObjectIsTest.js: -------------------------------------------------------------------------------- 1 | // 54: Object - is 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Object.is()` determines whether two values are the same", function() { 5 | describe("scalar values", function() { 6 | it("1 is the same as 1", function() { 7 | const areSame = Object.is(1, 1); 8 | assert.equal(areSame, true); 9 | }); 10 | it('int 1 is different to string "1"', function() { 11 | const areSame = Object.is(1, "1"); 12 | assert.equal(areSame, false); 13 | }); 14 | it("strings just have to match", function() { 15 | const areSame = Object.is("one", "one"); 16 | assert.equal(areSame, true); 17 | }); 18 | it("+0 is not the same as -0", function() { 19 | const areSame = false; 20 | assert.equal(Object.is(+0, -0), areSame); 21 | }); 22 | it("NaN is the same as NaN", function() { 23 | const number = NaN; 24 | assert.equal(Object.is(NaN, number), true); 25 | }); 26 | }); 27 | 28 | describe("coercion, as in `==` and `===`, does NOT apply", function() { 29 | it("+0 != -0", function() { 30 | const coerced = +0 === -0; // eslint-disable-line 31 | const isSame = !Object.is(+0, -0); 32 | assert.equal(isSame, coerced); 33 | }); 34 | it("empty string and `false` are not the same", function() { 35 | const emptyString = ""; 36 | const isSame = !Object.is(emptyString, false); 37 | assert.equal(isSame, emptyString == false); 38 | }); 39 | it("NaN", function() { 40 | const coerced = NaN == NaN; // eslint-disable-line 41 | const isSame = !Object.is(NaN, NaN); 42 | assert.equal(isSame, coerced); 43 | }); 44 | it("NaN 0/0", function() { 45 | const isSame = Object.is(NaN, 0 / 0); 46 | assert.equal(isSame, true); 47 | }); 48 | }); 49 | 50 | describe("complex values", function() { 51 | it("`{}` is just not the same as `{}`", function() { 52 | const areSame = false; 53 | assert.equal(Object.is({}, {}), areSame); 54 | }); 55 | it("Map", function() { 56 | let map1 = new Map([[1, "one"]]); 57 | let map2 = new Map([[1, "one"]]); 58 | const areSame = Object.is(map1, map2); 59 | assert.equal(areSame, false); 60 | }); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /test/es6katas/55-NumberIsIntegerTest.js: -------------------------------------------------------------------------------- 1 | // 55: Number - isInteger 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Number.isInteger()` determines if a value is an integer", function() { 5 | const isTrue = what => assert.equal(what, true); 6 | const isFalse = what => assert.equal(what, false); 7 | 8 | it("`isInteger` is a static function on `Number`", function() { 9 | const whatType = "function"; 10 | assert.equal(typeof Number.isInteger, whatType); 11 | }); 12 | 13 | describe("zero in different ways", function() { 14 | it("0 is an integer", function() { 15 | const zero = 0; 16 | isTrue(Number.isInteger(zero)); 17 | }); 18 | it("0.000", function() { 19 | isTrue(Number.isInteger(0.0)); 20 | }); 21 | it('the string "0" is NOT an integer', function() { 22 | const stringZero = "0"; 23 | isFalse(Number.isInteger(stringZero)); 24 | }); 25 | }); 26 | 27 | describe("one in different ways", function() { 28 | it("0.111 + 0.889", function() { 29 | const rest = 0.889; 30 | isTrue(Number.isInteger(0.111 + rest)); 31 | }); 32 | it("0.5 + 0.2 + 0.2 + 0.1 = 1 ... isn`t it?", function() { 33 | const oneOrNot = 0.5 + 0.2 + 0.2 + 0.1; 34 | isFalse(Number.isInteger(oneOrNot)); 35 | }); 36 | it('parseInt`ed "1" is an integer', function() { 37 | const convertedToInt = Number.parseInt("1.01"); 38 | isTrue(Number.isInteger(convertedToInt)); 39 | }); 40 | }); 41 | 42 | describe("what is not an integer", function() { 43 | it("`Number()` is an integer", function() { 44 | const numberOne = Number(); 45 | isTrue(Number.isInteger(numberOne)); 46 | }); 47 | it("`{}` is NOT an integer", function() { 48 | const isit = Number.isInteger({}); 49 | isFalse(isit); 50 | }); 51 | it("`0.1` is not an integer", function() { 52 | const isit = Number.isInteger(0.1); 53 | isFalse(isit); 54 | }); 55 | it("`Number.Infinity` is not an integer", function() { 56 | const isit = Number.isInteger(Number.POSITIVE_INFINITY); 57 | isFalse(isit); 58 | }); 59 | it("`NaN` is not an integer", function() { 60 | const isit = Number.isInteger(NaN); 61 | isFalse(isit); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/es6katas/57-DefaultParametersTest.js: -------------------------------------------------------------------------------- 1 | // 57: Default parameters - basics 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("default parameters make function parameters more flexible", () => { 5 | it("define it using an assignment to the parameter `function(param=1){}`", function() { 6 | let number = (int = 0) => int; 7 | 8 | assert.equal(number(), 0); 9 | }); 10 | 11 | it("it is used when undefined is passed", function() { 12 | let number = function(int = 23) { 13 | return int; 14 | }; 15 | const param = 23; 16 | 17 | assert.equal(number(param), 23); 18 | }); 19 | 20 | it("it is not used when a value is given", function() { 21 | function xhr(method = "GET") { 22 | return method; 23 | } 24 | 25 | assert.equal(xhr("POST"), "POST"); 26 | }); 27 | 28 | it("it is evaluated at run time", function() { 29 | let defaultValue = 42; 30 | function xhr(method = `value: ${defaultValue}`) { 31 | return method; 32 | } 33 | 34 | assert.equal(xhr(), "value: 42"); 35 | defaultValue = 43; 36 | }); 37 | 38 | it("it can also be a function", function() { 39 | let defaultValue = function() {}; 40 | function fn(value = defaultValue()) { 41 | return value; 42 | } 43 | 44 | assert.equal(fn(), defaultValue()); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/es6katas/58-ReflectBasicsTest.js: -------------------------------------------------------------------------------- 1 | // 58: Reflect - basics 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Reflect` basics", function() { 5 | describe("Reflect is special, it is different to e.g. `Object`", function() { 6 | it("is not a function", function() { 7 | const expectedType = "object"; 8 | assert.equal(typeof Reflect, expectedType); 9 | }); 10 | 11 | it("it can not be instantiated", function() { 12 | const tryToConstruct = () => { 13 | new Reflect(); 14 | }; 15 | assert.throws(tryToConstruct, TypeError); 16 | }); 17 | 18 | it("has no `call` method (as opposed to e.g. Object)", function() { 19 | const expected = "undefined"; 20 | assert.equal(typeof Reflect.call, expected); 21 | }); 22 | }); 23 | 24 | describe("some `Reflect` usages", function() { 25 | it("`Reflect.construct()` is like new", function() { 26 | class Class {} 27 | 28 | assert.equal(Reflect.construct(Class, []) instanceof Class, true); 29 | }); 30 | 31 | it("`Reflect.get()` returns a property`s value", function() { 32 | let obj = { x: 23 }; 33 | assert.equal(Reflect.get(obj, "x"), 23); 34 | }); 35 | 36 | it("`Reflect.has()` is like `in` just as a function", function() { 37 | let obj = { x: 11 }; 38 | assert.equal(Reflect.has(obj, "x"), true); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/es6katas/59-ReflectApplyTest.js: -------------------------------------------------------------------------------- 1 | // 59: Reflect - apply 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Reflect.apply` calls a target function", function() { 5 | it("it is a static method", function() { 6 | const expectedType = "function"; 7 | 8 | assert.equal(typeof Reflect.apply, expectedType); 9 | }); 10 | 11 | it("passing it a non-callable throws a TypeError", function() { 12 | let applyOnUncallable = () => { 13 | Reflect.apply([]); 14 | }; 15 | 16 | assert.throws(applyOnUncallable, TypeError); 17 | }); 18 | 19 | it("the 3rd argument is an array of parameters passed to the call", function() { 20 | let emptyArrayWithFiveElements = Reflect.apply(Array, this, [5]); 21 | 22 | assert.deepEqual(emptyArrayWithFiveElements.fill(42), [42, 42, 42, 42, 42]); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/es6katas/6-ArrowFunctionsBindingTest.js: -------------------------------------------------------------------------------- 1 | // 6: arrow functions - binding 2 | // To do: make all tests pass, leave the asserts unchanged! 3 | 4 | class LexicallyBound { 5 | getFunction() { 6 | return () => { 7 | return this; 8 | }; 9 | } 10 | 11 | getArgumentsFunction() { 12 | return () => { 13 | return arguments; 14 | }; 15 | } 16 | } 17 | 18 | describe("arrow functions have lexical `this`, no dynamic `this`", () => { 19 | it("bound at definition time, use `=>` ", function() { 20 | var bound = new LexicallyBound(); 21 | var fn = bound.getFunction(); 22 | 23 | assert.strictEqual(fn(), bound); 24 | }); 25 | 26 | it("can NOT bind a different context", function() { 27 | var bound = new LexicallyBound(); 28 | var fn = bound.getFunction(); 29 | var anotherObj = {}; 30 | var expected = bound; 31 | 32 | assert.strictEqual(fn.call(anotherObj), expected); 33 | }); 34 | 35 | it("`arguments` doesnt work inside arrow functions", function() { 36 | var bound = new LexicallyBound(); 37 | var fn = bound.getArgumentsFunction(); 38 | 39 | assert.equal(fn(1, 2).length, 0); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/es6katas/60-ReflectGetPrototypeOfTest.js: -------------------------------------------------------------------------------- 1 | // 60: Reflect - getPrototypeOf 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Reflect.getPrototypeOf` returns the prototype", function() { 5 | it("works like `Object.getPrototypeOf`", function() { 6 | const viaObject = Object.getPrototypeOf({}); 7 | const viaReflect = Reflect.getPrototypeOf({}); 8 | 9 | assert.strictEqual(viaObject, viaReflect); 10 | }); 11 | 12 | it("throw TypeError for a non-object", function() { 13 | let fn = () => { 14 | Reflect.getPrototypeOf(); 15 | }; 16 | assert.throws(fn, TypeError); 17 | }); 18 | 19 | it("a `new Set()` has a prototype", function() { 20 | const aSet = new Set(); 21 | 22 | assert.equal(Reflect.getPrototypeOf(aSet), Set.prototype); 23 | }); 24 | 25 | it("for a class, it is `Klass.prototype`", function() { 26 | class Klass {} 27 | const proto = Reflect.getPrototypeOf(new Klass()); 28 | 29 | assert.equal(proto, Klass.prototype); 30 | }); 31 | 32 | it("for a old-style class, works too", function() { 33 | function Klass() {} 34 | const proto = Reflect.getPrototypeOf(new Klass()); 35 | 36 | assert.equal(proto, Klass.prototype); 37 | }); 38 | 39 | it("an array has a prototype too", function() { 40 | let arr = []; 41 | const expectedProto = Array.prototype; 42 | 43 | assert.equal(Reflect.getPrototypeOf(arr), expectedProto); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/es6katas/61-ModulesImportTest.js: -------------------------------------------------------------------------------- 1 | // 61: modules - import 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | import { equal, deepEqual, notEqual } from "assert"; // eslint-disable-line 5 | import { equal as myEqual, default as myAssert } from "assert"; // eslint-disable-line 6 | 7 | describe("use `import` to import functions that have been exported (somewhere else)", function() { 8 | describe("the import statement", function() { 9 | it("is only allowed on the root level", function() { 10 | // try to comment this out, it will yell at you :) 11 | // import assert from 'assert'; // eslint-disabled-line 12 | }); 13 | 14 | it('import an entire module using `import from ""`', function() { 15 | // this can't fail, since `assert` is imported by default 16 | assert.equal(typeof assert, "function"); 17 | }); 18 | }); 19 | 20 | describe("import members", function() { 21 | it('import a single member, using `import {} from "module"`', function() { 22 | assert.strictEqual(equal, assert.equal); 23 | }); 24 | describe("separate multiple members with a comma", function() { 25 | it("`deepEqual` from the assert module", () => { 26 | assert.strictEqual(deepEqual, assert.deepEqual); 27 | }); 28 | it("`notEqual` from the assert module", () => { 29 | assert.strictEqual(notEqual, assert.notEqual); 30 | }); 31 | }); 32 | }); 33 | 34 | describe("alias imports", function() { 35 | it("using `member as alias` as memberName", function() { 36 | assert.strictEqual(myEqual, assert.equal); 37 | }); 38 | it("rename the default export of a module, using `default as alias` as memberName", function() { 39 | assert.strictEqual(myAssert, assert); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/es6katas/62-MapHasTest.js: -------------------------------------------------------------------------------- 1 | // 62: Map - `has()` 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`map.has()` indicates whether an element with a key exists", function() { 5 | it("finds nothing in an empty map", function() { 6 | let map = new Map(); 7 | const hasKey = map.has(void 0); 8 | assert.equal(hasKey, false); 9 | }); 10 | 11 | it("finds an element by it`s key", function() { 12 | let map = new Map([["key", "VALUE"]]); 13 | const hasKey = map.has("key"); 14 | assert.equal(hasKey, true); 15 | }); 16 | 17 | it("finds `undefined` as key too", function() { 18 | let map = new Map([[void 0, "not defined key"]]); 19 | const hasUndefinedAsKey = map.has(void 0); 20 | assert.equal(hasUndefinedAsKey, true); 21 | }); 22 | 23 | it("does not coerce keys", function() { 24 | let map = new Map([[1, "one"]]); 25 | const findsStringOne = false; 26 | assert.equal(map.has("1"), findsStringOne); 27 | }); 28 | 29 | it("after removal (using `map.delete()`) it doesnt find the element anymore", function() { 30 | let map = new Map([[1, "one"]]); 31 | map.delete(1); 32 | assert.equal(map.has(1), false); 33 | }); 34 | 35 | it("adding an item (using `map.set(key, value)`) later will make `has()` return true", function() { 36 | let map = new Map(); 37 | map.set(void 0, 1); 38 | assert.equal(map.has(void 0), true); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/es6katas/63-StringIncludesTest.js: -------------------------------------------------------------------------------- 1 | // 63: String - `includes()` 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`string.includes()` finds string within another string", function() { 5 | describe("find a single character", function() { 6 | it("in a three char string", function() { 7 | const searchString = "x"; 8 | assert.equal("xyz".includes(searchString), true); 9 | }); 10 | it("reports false if character was not found", function() { 11 | const expected = false; 12 | assert.equal("xyz".includes("abc"), expected); 13 | }); 14 | }); 15 | 16 | describe("find a string", function() { 17 | it("that matches exactly", function() { 18 | const findSome = findMe => "xyz".includes(findMe); 19 | assert.equal(findSome("xyz"), true); 20 | }); 21 | }); 22 | 23 | describe("search for an empty string, is always true", function() { 24 | it("in an empty string", function() { 25 | const emptyString = ""; 26 | assert.equal("".includes(emptyString), true); 27 | }); 28 | it("in `abc`", function() { 29 | const actual = "abc".includes(""); 30 | assert.equal(actual, true); 31 | }); 32 | }); 33 | 34 | describe("special/corner cases", function() { 35 | it("search for `undefined` in a string fails", function() { 36 | const findInAbc = what => "abc".includes(what); 37 | assert.equal(findInAbc(void 0), false); 38 | }); 39 | it("searches case-sensitive", function() { 40 | const findInAbc = what => "abc".includes(what); 41 | assert.equal(findInAbc("A"), false); 42 | }); 43 | it("must NOT be a regular expression", function() { 44 | const regExp = /.+/; 45 | assert.throws(() => { 46 | "".includes(regExp); 47 | }); 48 | }); 49 | describe('coerces the searched "thing" into a string', function() { 50 | it("e.g. from a number", function() { 51 | const actual = "123".includes(123); 52 | assert.equal(actual, true); 53 | }); 54 | it("e.g. from an array", function() { 55 | const actual = "123".includes([1]); 56 | assert.equal(actual, true); 57 | }); 58 | it("e.g. from an object, with a `toString()` method", function() { 59 | const objWithToString = { toString: () => "123" }; 60 | assert.equal("123".includes(objWithToString), true); 61 | }); 62 | }); 63 | }); 64 | 65 | describe("takes a position from where to start searching", function() { 66 | it("does not find `a` after position 1 in `abc`", function() { 67 | const position = 1; 68 | assert.equal("abc".includes("a", position), false); 69 | }); 70 | it("even the position gets coerced", function() { 71 | const findAtPosition = pos => "xyz".includes("z", pos); 72 | assert.equal(findAtPosition("2"), true); 73 | }); 74 | describe("invalid positions get converted to 0", function() { 75 | it("e.g. `undefined`", function() { 76 | const findAtPosition = (pos = 0) => "xyz".includes("x", pos); 77 | assert.equal(findAtPosition(void 0), true); 78 | }); 79 | it("negative numbers", function() { 80 | const findAtPosition = pos => "xyz".includes("x", pos); 81 | assert.equal(findAtPosition(-2), true); 82 | }); 83 | it("NaN", function() { 84 | const findAtPosition = pos => "xyz".includes("x", pos); 85 | assert.equal(findAtPosition(NaN), true); 86 | }); 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /test/es6katas/64-SetDeleteTest.js: -------------------------------------------------------------------------------- 1 | // 64: Set - delete 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`set.delete()` deletes an element from a set", function() { 5 | let set; 6 | beforeEach(() => (set = new Set())); 7 | 8 | describe("use `delete()` to delete an element", function() { 9 | beforeEach(function() { 10 | set 11 | .add("one") 12 | .add("two") 13 | .add("three"); 14 | }); 15 | it("`delete()` returns `true` when the element was found", function() { 16 | const returns = set.delete("one"); 17 | assert.strictEqual(returns, true); 18 | }); 19 | it("and the size decreases", function() { 20 | set.delete("one"); 21 | assert.equal(set.size, 2); 22 | }); 23 | }); 24 | 25 | describe("if nothing was deleted (no element with the given value was found)", function() { 26 | it("returns `false`", function() { 27 | set.add("one"); 28 | const returns = set.delete("1"); 29 | 30 | assert.equal(returns, false); 31 | }); 32 | }); 33 | 34 | describe("`undefined` is a valid value in a set", function() { 35 | it("deleting it, when it is not in the set, returns `false` too", function() { 36 | assert.equal(set.delete(void 0), false); 37 | }); 38 | 39 | it("`delete()` removes it, when its in the set", function() { 40 | set.add(); 41 | assert.equal(set.delete(), true); 42 | }); 43 | }); 44 | 45 | describe("the value does NOT get casted", function() { 46 | it('number 1 is different to string "1"', function() { 47 | set.add(1); 48 | assert.equal(set.delete("1"), false); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/es6katas/65-SetApiOverviewTest.js: -------------------------------------------------------------------------------- 1 | // 65: Set - API overview 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Set` API overview", function() { 5 | const api = [ 6 | "size", 7 | "add", 8 | "clear", 9 | "delete", 10 | "entries", 11 | "forEach", 12 | "has", 13 | "keys", 14 | "values" 15 | ]; 16 | let set; 17 | beforeEach(function() { 18 | set = new Set(api); 19 | }); 20 | 21 | it("a Set can be created from an array", function() { 22 | let set = new Set(api); 23 | assert.deepEqual(Array.from(set), api); 24 | }); 25 | 26 | it("`size` is the number of values", function() { 27 | const theSize = set.size; 28 | assert.equal(theSize, api.length); 29 | }); 30 | 31 | it("`add()` appends the given value", function() { 32 | // hint: to make the example consistent you can add the `Symbol.iterator` to `set` 33 | // strongly speaking it is missing in the API. 34 | set.add(Symbol.iterator); 35 | assert.equal(set.size, api.length + 1); 36 | }); 37 | 38 | it("`clear()` removes all elements", function() { 39 | set.clear(); 40 | assert.equal(set.size, 0); 41 | }); 42 | 43 | it("`delete()` removes the given value", function() { 44 | set.delete(api[1]); 45 | assert.equal(set.size, api.length - 1); 46 | }); 47 | 48 | it("`entries()` returns an iterator for all values", function() { 49 | const expectedEntries = api.map(entry => [entry, entry]); 50 | const actualEntries = set.entries(); 51 | assert.deepEqual([...actualEntries], expectedEntries); 52 | }); 53 | 54 | it("`forEach()` calls a callback for each value", function() { 55 | let values = []; 56 | set.forEach(value => { 57 | values.push(value); 58 | }); 59 | assert.deepEqual(values, api); 60 | }); 61 | 62 | it("`has()` returns true if the given value is in the set", function() { 63 | const propertyName = "size"; 64 | assert.equal(set.has(propertyName), true); 65 | }); 66 | 67 | describe("returns an iterator that contains all values", function() { 68 | // in order to be alike to `Map` `keys()` and `values()` are essentially the same thing for a `Set`. 69 | it("`keys()`", function() { 70 | const allKeys = set.keys(); 71 | assert.deepEqual([...allKeys], api); 72 | }); 73 | 74 | it("`values()`", function() { 75 | const allValues = set.values(); 76 | assert.deepEqual([...allValues], api); 77 | }); 78 | 79 | it("`[Symbol.iterator]()`", function() { 80 | const iteratorKey = Symbol.iterator; 81 | assert.deepEqual([...set[iteratorKey]()], api); 82 | }); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /test/es6katas/66-ObjectLiteralGetterTest.js: -------------------------------------------------------------------------------- 1 | // 66: object-literal - getter 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("An object literal can also contain getters", () => { 5 | it("just prefix the property with `get` (and make it a function)", function() { 6 | const obj = { 7 | get x() { 8 | return "ax"; 9 | } 10 | }; 11 | 12 | assert.equal(obj.x, "ax"); 13 | }); 14 | 15 | it("must have NO parameters", function() { 16 | const obj = { 17 | get x() { 18 | return "ax"; 19 | } 20 | }; 21 | 22 | assert.equal(obj.x, "ax"); 23 | }); 24 | 25 | it("can be a computed property (an expression enclosed in `[]`)", function() { 26 | const keyName = "x"; 27 | const obj = { 28 | get [keyName]() { 29 | return "ax"; 30 | } 31 | }; 32 | 33 | assert.equal(obj.x, "ax"); 34 | }); 35 | 36 | it("can be removed using delete", function() { 37 | const obj = { 38 | get x() { 39 | return "ax"; 40 | } 41 | }; 42 | delete obj.x; 43 | assert.equal(obj.x, void 0); 44 | }); 45 | 46 | // The following dont seem to work in the current transpiler version 47 | // but should be correct, as stated here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get 48 | // It might be corrected later, new knowledge welcome. 49 | 50 | //it('must not overlap with a pure property', function() { 51 | // const obj = { 52 | // x: 1, 53 | // get x() { return 'ax'; } 54 | // }; 55 | // 56 | // assert.equal(obj.x, 'ax'); 57 | //}); 58 | // 59 | //it('multiple `get` for the same property are not allowed', function() { 60 | // const obj = { 61 | // x: 1, 62 | // get x() { return 'ax'; }, 63 | // get x() { return 'ax1'; } 64 | // }; 65 | // 66 | // assert.equal(obj.x, 'ax'); 67 | //}); 68 | }); 69 | -------------------------------------------------------------------------------- /test/es6katas/67-ObjectLiteralTest.js: -------------------------------------------------------------------------------- 1 | // 67: object-literal - setter 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("An object literal can also contain setters", () => { 5 | describe("defining: a setter", function() { 6 | it("by prefixing the property with `set` (and make it a function)", function() { 7 | let theX = null; 8 | const obj = { 9 | set x(newX) { 10 | theX = newX; 11 | } 12 | }; 13 | 14 | obj.x = "the new X"; 15 | assert.equal(theX, "the new X"); 16 | }); 17 | it("must have exactly one parameter", function() { 18 | let setterCalledWith = void 0; 19 | const obj = { 20 | set x(newX) { 21 | if (arguments.length === 1) { 22 | setterCalledWith = arguments[0]; 23 | } 24 | } 25 | }; 26 | 27 | assert.equal((obj.x = "new value"), setterCalledWith); 28 | }); 29 | it("can be a computed property (an expression enclosed in `[]`)", function() { 30 | const publicPropertyName = "x"; 31 | const privatePropertyName = "_" + publicPropertyName; 32 | const obj = { 33 | [privatePropertyName]: "null", 34 | set [publicPropertyName](newValue) { 35 | this[privatePropertyName] = newValue; 36 | } 37 | }; 38 | 39 | obj.x = "axe"; 40 | assert.equal(obj._x, "axe"); 41 | }); 42 | }); 43 | 44 | describe("working with/on the setter", function() { 45 | it("you can use `delete` to remove the property (including it`s setter)", function() { 46 | let setterCalled = false; 47 | const obj = { 48 | set x(param) { 49 | setterCalled = true; 50 | } 51 | }; 52 | 53 | delete obj.x; 54 | 55 | obj.x = true; 56 | assert.equal(setterCalled, false); 57 | }); 58 | }); 59 | 60 | // TODO 61 | // The following dont seem to work in the current transpiler version 62 | // but should be correct, as stated here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set 63 | // It might be corrected later, new knowledge welcome. 64 | // it('must not overlap with a pure property', function() { 65 | // const obj = { 66 | // x: 1, 67 | // set x(val) { return 'ax'; } 68 | // }; 69 | // assert.equal(obj.x, 'ax'); 70 | // }); 71 | // it('multiple `set` for the same property are not allowed', function() { 72 | // const obj = { 73 | // x: 1, 74 | // set x(v) { return 'ax'; }, 75 | // set x(v) { return 'ax1'; } 76 | // }; 77 | // assert.equal(obj.x, 'ax'); 78 | // }); 79 | }); 80 | -------------------------------------------------------------------------------- /test/es6katas/68-ReflectConstructTest.js: -------------------------------------------------------------------------------- 1 | // 68: Reflect - construct 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Reflect.construct` is the `new` operator as a function", function() { 5 | describe("the function itself", function() { 6 | it("is static on the `Reflect` object", function() { 7 | const name = "construct"; 8 | assert.equal(name in Reflect, true); 9 | }); 10 | it("is of type `function`", function() { 11 | const expectedType = "function"; 12 | assert.equal(typeof Reflect.construct, expectedType); 13 | }); 14 | }); 15 | 16 | describe("the 1st parameter is the constructor to be invoked", function() { 17 | it("fails when given a number as constructor", function() { 18 | let aNumber = Number(); 19 | assert.throws(() => { 20 | Reflect.construct(aNumber, []); 21 | }, TypeError); 22 | }); 23 | it("works giving a function", function() { 24 | let aFunction = () => {}; 25 | assert.doesNotThrow(() => { 26 | Reflect.construct(aFunction, []); 27 | }); 28 | }); 29 | it("works giving a class", function() { 30 | const aClass = class {}; 31 | assert.doesNotThrow(() => { 32 | Reflect.construct(aClass, []); 33 | }); 34 | }); 35 | }); 36 | 37 | describe("the 2nd parameter is a list of arguments, that will be passed to the constructor", function() { 38 | const aClass = class {}; 39 | it("fails when it`s not an array(-like), e.g. a number", function() { 40 | let aNumber = Number(); 41 | assert.throws(() => { 42 | Reflect.construct(aClass, aNumber); 43 | }, TypeError); 44 | }); 45 | it("works with an array-like object (the `length` property look up should not throw)", function() { 46 | let arrayLike = { get length() {} }; 47 | assert.doesNotThrow(() => { 48 | Reflect.construct(aClass, arrayLike); 49 | }); 50 | }); 51 | it("works with a real array", function() { 52 | let realArray = []; 53 | assert.doesNotThrow(() => { 54 | Reflect.construct(aClass, realArray); 55 | }); 56 | }); 57 | }); 58 | 59 | describe("in use", function() { 60 | it("giving it a class it returns an instance of this class", function() { 61 | class Constructable {} 62 | let instance = Reflect.construct(Constructable, []); 63 | 64 | assert.equal(instance instanceof Constructable, true); 65 | }); 66 | 67 | describe("the list of arguments are passed to the constructor as given", function() { 68 | class Constructable { 69 | constructor(...args) { 70 | this.args = args; 71 | } 72 | } 73 | it("if none given, nothing is passed", function() { 74 | let instance = Reflect.construct(Constructable, []); 75 | 76 | assert.deepEqual(instance.args, []); 77 | }); 78 | it("passing an array, all args of any type are passed", function() { 79 | const argumentsList = ["arg1", ["arg2.1", "arg2.2"], { arg: 3 }]; 80 | let instance = Reflect.construct(Constructable, argumentsList); 81 | 82 | assert.deepEqual(instance.args, argumentsList); 83 | }); 84 | }); 85 | }); 86 | 87 | describe("the length property", function() { 88 | it("of `Reflect.construct` is 2", function() { 89 | let expected = 2; 90 | assert.equal(Reflect.construct.length, expected); 91 | }); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /test/es6katas/69-ReflectDefinePropertyTest.js: -------------------------------------------------------------------------------- 1 | // 69: Reflect - defineProperty 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Reflect.defineProperty()` is like `Object.defineProperty()` but returns a Boolean.", function() { 5 | describe("the function itself", function() { 6 | it("is static on the `Reflect` object", function() { 7 | const name = "defineProperty"; 8 | assert.equal(name in Reflect, true); 9 | }); 10 | it("is of type `function`", function() { 11 | const expectedType = "function"; 12 | assert.equal(typeof Reflect.defineProperty, expectedType); 13 | }); 14 | }); 15 | 16 | describe("the 1st parameter is the object on which to define a property", function() { 17 | it("fails if it is not an object", function() { 18 | let noObj; 19 | assert.throws(() => { 20 | Reflect.defineProperty(noObj, "property", { value: "value" }); 21 | }, TypeError); 22 | }); 23 | it("accepts an object", function() { 24 | let obj = {}; 25 | assert.doesNotThrow(() => { 26 | Reflect.defineProperty(obj, "property", { value: "value" }); 27 | }); 28 | }); 29 | it("accepts an instance (of a class)", function() { 30 | let instance = new class {}(); 31 | assert.doesNotThrow(() => { 32 | Reflect.defineProperty(instance, "property", { value: "value" }); 33 | }); 34 | }); 35 | }); 36 | 37 | describe("2nd parameter is the name of the property to be defined on the object (normally a string)", function() { 38 | it("works with a `normal` string", function() { 39 | let obj = {}; 40 | Reflect.defineProperty(obj, "prop", {}); 41 | assert.equal("prop" in obj, true); 42 | }); 43 | it("a number gets converted into a string", function() { 44 | let obj = {}; 45 | Reflect.defineProperty(obj, 1, {}); 46 | assert.equal("1" in obj, true); 47 | }); 48 | it("`undefined` also gets converted into a string (watch out!)", function() { 49 | let obj = {}; 50 | let undef = void 0; 51 | Reflect.defineProperty(obj, undef, {}); 52 | assert.equal("undefined" in obj, true); 53 | }); 54 | it("it can be a symbol", function() { 55 | let obj = {}; 56 | const sym = Symbol.for("prop"); 57 | Reflect.defineProperty(obj, sym, {}); 58 | assert.equal(sym in obj, true); 59 | }); 60 | }); 61 | 62 | describe("the `value` is part of the 3rd parameter, given as a property in an object `{value: ...}`", function() { 63 | // The entire complexity of the 3rd parameter might be covered in a later kata. 64 | 65 | it("contains the initial value of the property, as an object in the property `value`", function() { 66 | let obj = {}; 67 | Reflect.defineProperty(obj, "prop", { value: "property value" }); 68 | assert.equal(obj.prop, "property value"); 69 | }); 70 | it("can be of any type (even itself)", function() { 71 | let obj = {}; 72 | Reflect.defineProperty(obj, "prop", { value: obj }); 73 | assert.equal(obj.prop, obj); 74 | }); 75 | }); 76 | 77 | describe("the return value of the function indicates wether the property was defined successfully", function() { 78 | describe("returns true", function() { 79 | it("when the property was created (which requires the 3rd parameter too!!!)", function() { 80 | let instance = new class {}(); 81 | const wasPropertyDefined = Reflect.defineProperty(instance, void 0, { 82 | value: true 83 | }); 84 | assert.equal(wasPropertyDefined, true); 85 | }); 86 | it("no matter what the value of the property is (just the 3rd param has to exist as `{}`)", function() { 87 | let instance = new class {}(); 88 | const wasPropertyDefined = Reflect.defineProperty(instance, void 0, {}); 89 | assert.equal(wasPropertyDefined, true); 90 | }); 91 | }); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /test/es6katas/7-BlockScopeLetTest.js: -------------------------------------------------------------------------------- 1 | // 7: block scope - let 2 | // To do: make all tests pass, leave the asserts unchanged! 3 | 4 | describe("`let` restricts the scope of the variable to the current block", () => { 5 | describe("`let` vs. `var`", () => { 6 | it("`var` works as usual", () => { 7 | var varX = false; 8 | if (true) { // eslint-disable-line 9 | var varX = true; // eslint-disable-line 10 | } 11 | assert.equal(varX, true); 12 | }); 13 | 14 | it("`let` restricts scope to inside the block", () => { 15 | if (true) { // eslint-disable-line 16 | let letX = true; // eslint-disable-line 17 | } 18 | assert.throws(() => console.log(letX)); // eslint-disable-line 19 | }); 20 | }); 21 | 22 | describe("`let` usage", () => { 23 | it("`let` use in `for` loops", () => { 24 | let obj = { x: 1 }; 25 | for (let key in obj) { // eslint-disable-line 26 | } 27 | assert.throws(() => console.log(key)); // eslint-disable-line 28 | }); 29 | 30 | it("create artifical scope, using curly braces", () => { 31 | { 32 | let letX = true; // eslint-disable-line 33 | } 34 | assert.throws(() => console.log(letX)); // eslint-disable-line 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/es6katas/70-SetClearTest.js: -------------------------------------------------------------------------------- 1 | // 70: Set - clear 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`clear()` removes all elements from a Set object.", function() { 5 | let set; 6 | beforeEach(() => (set = new Set())); 7 | 8 | it("`set.size` becomes 0", function() { 9 | set.add("one").add(2); 10 | set.clear(); 11 | 12 | const expectedSize = 0; 13 | assert.equal(set.size, expectedSize); 14 | }); 15 | 16 | it("the iterator `set.entries()` will not contain any items", function() { 17 | set.add("one").add(2); 18 | 19 | set.clear(); 20 | 21 | const { done } = set.entries().next(); 22 | assert.equal(done, true); 23 | }); 24 | 25 | it("any call to `set.has()` returns false", function() { 26 | set.add("one").add(2); 27 | 28 | set.clear(); 29 | 30 | assert.deepEqual(set.has(2), false); 31 | }); 32 | 33 | it("returns `undefined`", function() { 34 | var expectedReturn = void 0; 35 | assert.equal(set.clear(), expectedReturn); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/es6katas/71-StringRepeatTest.js: -------------------------------------------------------------------------------- 1 | // 71: String - `repeat()` 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`str.repeat(x)` appends `x` copies of `str` to each other and returns it", function() { 5 | describe("pass the count to `str.repeat(count)`", function() { 6 | it("for `1` the string stays the same", function() { 7 | const what = "one".repeat(1); 8 | assert.equal(what, "one"); 9 | }); 10 | it("for `3` the string `x` becomes `xxx`", function() { 11 | const actual = "x".repeat(3); 12 | assert.equal(actual, "xxx"); 13 | }); 14 | it("for `0` an empty string is returned", function() { 15 | const dontRepeat = 0; 16 | assert.equal("shrink".repeat(dontRepeat), ""); 17 | }); 18 | 19 | it('the count is not an int, such as "3", it gets coerced to an int', function() { 20 | const repeated = "three".repeat("3"); 21 | assert.equal(repeated, "threethreethree"); 22 | }); 23 | }); 24 | 25 | describe("throws an error for", function() { 26 | it("a count of <0", function() { 27 | const belowZero = -1; 28 | assert.throws(() => { 29 | "".repeat(belowZero); 30 | }, RangeError); 31 | }); 32 | it("a count of +Infinty", function() { 33 | let infinity = Infinity; 34 | assert.throws(() => { 35 | "".repeat(infinity); 36 | }, RangeError); 37 | }); 38 | }); 39 | 40 | describe("accepts everything that can be coerced to a string", function() { 41 | it("e.g. a boolean", function() { 42 | let aBool = false; 43 | assert.equal(String.prototype.repeat.call(aBool, 2), "falsefalse"); 44 | }); 45 | it("e.g. a number", function() { 46 | let aNumber = "1"; 47 | assert.equal(String.prototype.repeat.call(aNumber, 2), "11"); 48 | }); 49 | }); 50 | 51 | describe("for my own (string) class", function() { 52 | it("calls `toString()` to make it a string", function() { 53 | class MyString { 54 | toString() { 55 | return "my string"; 56 | } 57 | } 58 | 59 | const expectedString = "my string"; 60 | 61 | assert.equal(String(new MyString()).repeat(1), expectedString); 62 | }); 63 | it("`toString()` is only called once", function() { 64 | let counter = 1; 65 | class X { 66 | toString() { 67 | return counter++; 68 | } 69 | } 70 | 71 | let repeated = String(new X()).repeat(2); 72 | 73 | assert.equal(repeated, "11"); 74 | }); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /test/es6katas/72-StringStrtsWithTest.js: -------------------------------------------------------------------------------- 1 | // 72: String - `startsWith()` 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`str.startsWith(searchString)` determines whether `str` begins with `searchString`.", function() { 5 | const s = "the string s"; 6 | 7 | describe("1st parameter, the string to search for", function() { 8 | it("works with just a character", function() { 9 | const actual = s.startsWith("t"); 10 | assert.equal(actual, true); 11 | }); 12 | it("works with a string", function() { 13 | const expected = true; 14 | assert.equal(s.startsWith("the"), expected); 15 | }); 16 | it("works with unicode characters", function() { 17 | const nuclear = "☢ NO"; 18 | assert.equal(nuclear.startsWith("☢"), true); 19 | }); 20 | it("a regular expression throws a TypeError", function() { 21 | const aRegExp = /./; 22 | assert.throws(() => { 23 | "".startsWith(aRegExp); 24 | }, TypeError); 25 | }); 26 | }); 27 | 28 | describe("2nd parameter, the position where to start searching from", function() { 29 | it('find "str" at position 4', function() { 30 | const position = 4; 31 | assert.equal(s.startsWith("str", position), true); 32 | }); 33 | it("`undefined` is the same as 0", function() { 34 | const _undefined_ = "0"; 35 | assert.equal(s.startsWith("the", _undefined_), true); 36 | }); 37 | it("the parameter gets coerced to an int", function() { 38 | const position = "4"; 39 | assert.equal(s.startsWith("str", position), true); 40 | }); 41 | it("a value larger than the string`s length, returns false", function() { 42 | const expected = false; 43 | assert.equal(s.startsWith(" ", s.length + 1), expected); 44 | }); 45 | }); 46 | 47 | describe("transfer the functionality to other objects", function() { 48 | const startsWith = (...args) => String.prototype.startsWith.call(...args); 49 | 50 | it("e.g. a boolean", function() { 51 | let aBool = true; 52 | assert.equal(startsWith(!aBool, "false"), true); 53 | }); 54 | it("e.g. a number", function() { 55 | let aNumber = 1900; 56 | assert.equal(startsWith(aNumber + 84, "1984"), true); 57 | }); 58 | it("also using the position works", function() { 59 | const position = 1; 60 | assert.equal(startsWith(1994, "99", position), true); 61 | }); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /test/es6katas/74-StringEndsWithTest.js: -------------------------------------------------------------------------------- 1 | // 74: String - `endsWith()` 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`str.endsWith(searchString)` determines whether `str` ends with `searchString`.", function() { 5 | const s = "el fin"; 6 | 7 | describe("1st parameter, the string to search for", function() { 8 | it("works with just a character", function() { 9 | const doesEndWith = s.endsWith("n"); 10 | assert.equal(doesEndWith, true); 11 | }); 12 | it("works with a string", function() { 13 | const expected = true; 14 | assert.equal(s.endsWith("fin"), expected); 15 | }); 16 | it("works with unicode characters", function() { 17 | const nuclear = "NO ☢"; 18 | assert.equal(nuclear.endsWith("☢"), true); 19 | }); 20 | it("a regular expression throws a TypeError", function() { 21 | const aRegExp = /the/; 22 | assert.throws(() => { 23 | "".endsWith(aRegExp); 24 | }, TypeError); 25 | }); 26 | }); 27 | 28 | describe("2nd parameter, searches within this string as if this string were only this long", function() { 29 | it('find "el" at a substring of the length 2', function() { 30 | const endPos = 2; 31 | assert.equal(s.endsWith("el", endPos), true); 32 | }); 33 | it("`undefined` uses the entire string", function() { 34 | const _undefined_ = void 0; 35 | assert.equal(s.endsWith("fin", _undefined_), true); 36 | }); 37 | it("the parameter gets coerced to an int", function() { 38 | const position = "5"; 39 | assert.equal(s.endsWith("fi", position), true); 40 | }); 41 | describe("value less than 0", function() { 42 | it("returns `true`, when searching for an empty string", function() { 43 | const emptyString = ""; 44 | assert.equal("1".endsWith(emptyString, -1), true); 45 | }); 46 | it("return `false`, when searching for a non-empty string", function() { 47 | const notEmpty = "not-empty string"; 48 | assert.equal("1".endsWith(notEmpty, -1), false); 49 | }); 50 | }); 51 | }); 52 | 53 | describe("transfer the functionality to other objects", function() { 54 | const endsWith = (...args) => String.prototype.endsWith.call(...args); 55 | 56 | it("e.g. a boolean", function() { 57 | let aBool = true; 58 | assert.equal(endsWith(!aBool, "lse"), true); 59 | }); 60 | it("e.g. a number", function() { 61 | let aNumber = 84; 62 | assert.equal(endsWith(aNumber + 1900, 84), true); 63 | }); 64 | it("also using the position works", function() { 65 | const position = 3; 66 | assert.equal(endsWith(1994, "99", position), true); 67 | }); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /test/es6katas/76-PromiseCreationTest.js: -------------------------------------------------------------------------------- 1 | // 76: Promise - creation 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("a promise can be created in multiple ways", function() { 5 | describe("creating a promise fails when", function() { 6 | it("using `Promise` as a function", function() { 7 | function callPromiseAsFunction() { 8 | return new Promise(); 9 | } 10 | assert.throws(callPromiseAsFunction); 11 | }); 12 | 13 | it("no parameter is passed", function() { 14 | function promiseWithoutParams() { 15 | new Promise(); 16 | } 17 | assert.throws(promiseWithoutParams); 18 | }); 19 | 20 | it("passing a non-callable throws too", function() { 21 | const notAFunction = 1; 22 | assert.throws(() => { 23 | new Promise(notAFunction); 24 | }); 25 | }); 26 | }); 27 | 28 | describe("most commonly Promises get created using the constructor", function() { 29 | it("by passing a resolve function to it", function() { 30 | const promise = new Promise(resolve => resolve()); 31 | return promise; 32 | }); 33 | 34 | it("by passing a resolve and a reject function to it", function(done) { 35 | const promise = new Promise((resolve, reject) => reject()); 36 | 37 | promise 38 | .then(() => done(new Error("Expected promise to be rejected."))) 39 | .catch(done); 40 | }); 41 | }); 42 | 43 | describe("`Promise.all()` returns a promise that resolves when all given promises resolve", function() { 44 | it("returns all results", function(done) { 45 | const promise = Promise.all([ 46 | new Promise(resolve => resolve(1)), 47 | new Promise(resolve => resolve(2)) 48 | ]); 49 | 50 | promise 51 | .then(value => { 52 | assert.deepEqual(value, [1, 2]); 53 | done(); 54 | }) 55 | .catch(e => done(new Error(e))); 56 | }); 57 | 58 | it("is rejected if one rejects", function(done) { 59 | const promise = Promise.all([new Promise((resolve, reject) => reject())]); 60 | 61 | promise.then(() => done(new NotRejectedError())).catch(() => done()); 62 | }); 63 | }); 64 | 65 | describe("`Promise.race()` returns the first settled promise", function() { 66 | it("if it resolves first, the promises resolves", function(done) { 67 | const earlyResolvingPromise = new Promise(resolve => resolve("1st :)")); 68 | const promise = Promise.race([earlyResolvingPromise]); 69 | 70 | promise 71 | .then(value => { 72 | assert.deepEqual(value, "1st :)"); 73 | done(); 74 | }) 75 | .catch(e => 76 | done(new Error("Expected to resolve, but failed with: " + e)) 77 | ); 78 | }); 79 | 80 | it("if one of the given promises rejects first, the returned promise is rejected", function( 81 | done 82 | ) { 83 | const earlyRejectedPromise = new Promise((resolve, reject) => 84 | reject("I am a rejector") 85 | ); 86 | const lateResolvingPromise = new Promise(resolve => 87 | setTimeout(resolve, 10) 88 | ); 89 | const promise = Promise.race([ 90 | earlyRejectedPromise, 91 | lateResolvingPromise 92 | ]); 93 | 94 | promise 95 | .then(() => done(new NotRejectedError())) 96 | .catch(value => { 97 | assert.equal(value, "I am a rejector"); 98 | done(); 99 | }) 100 | .catch(done); 101 | }); 102 | }); 103 | 104 | describe("`Promise.resolve()` returns a resolving promise", function() { 105 | it("if no value given, it resolves with `undefined`", function(done) { 106 | const promise = Promise.resolve(); 107 | 108 | promise 109 | .then(value => { 110 | assert.deepEqual(value, void 0); 111 | done(); 112 | }) 113 | .catch(e => 114 | done(new Error("Expected to resolve, but failed with: " + e)) 115 | ); 116 | }); 117 | 118 | it("resolves with the given value", function(done) { 119 | const promise = Promise.resolve("quick resolve"); 120 | 121 | promise 122 | .then(value => { 123 | assert.equal(value, "quick resolve"); 124 | done(); 125 | }) 126 | .catch(e => done(e)); 127 | }); 128 | }); 129 | 130 | describe("`Promise.reject()` returns a rejecting promise", function() { 131 | it("if no value given, it rejects with `undefined`", function(done) { 132 | const promise = Promise.reject(); 133 | 134 | promise 135 | .then(() => done(new NotRejectedError())) 136 | .catch(value => { 137 | assert.deepEqual(value, void 0); 138 | done(); 139 | }) 140 | .catch(done); 141 | }); 142 | 143 | it("the parameter passed to `reject()` can be used in the `.catch()`", function( 144 | done 145 | ) { 146 | const promise = Promise.reject("quick reject"); 147 | 148 | promise 149 | .then(() => done(new NotRejectedError())) 150 | .catch(value => { 151 | assert.deepEqual(value, "quick reject"); 152 | done(); 153 | }) 154 | .catch(done); 155 | }); 156 | }); 157 | }); 158 | 159 | class NotRejectedError extends Error { 160 | constructor() { 161 | super(); 162 | this.message = "Expected promise to be rejected."; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /test/es6katas/77-PromiseChainingTest.js: -------------------------------------------------------------------------------- 1 | // 77: Promise - chaining 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("chaining multiple promises can enhance readability", () => { 5 | describe("prerequisites for understanding", function() { 6 | it("reminder: the test passes when a fulfilled promise is returned", function() { 7 | return Promise.resolve("I should fulfill."); 8 | }); 9 | 10 | it("a function given to `then()` fulfills (if it doesnt throw)", function() { 11 | const beNice = () => { 12 | return "I am nice"; 13 | }; 14 | return Promise.resolve() 15 | .then(beNice) 16 | .then(niceMessage => assert.equal(niceMessage, "I am nice")); 17 | }); 18 | }); 19 | 20 | describe("chain promises", function() { 21 | const removeMultipleSpaces = string => string.replace(/\s+/g, " "); 22 | 23 | it("`then()` receives the result of the promise it was called on", function() { 24 | const wordsPromise = Promise.resolve( 25 | "one space between each word" 26 | ); 27 | return wordsPromise 28 | .then(string => removeMultipleSpaces(string)) 29 | .then(actual => { 30 | assert.equal(actual, "one space between each word"); 31 | }); 32 | }); 33 | 34 | const appendPeriod = string => `${string}.`; 35 | 36 | it("multiple `then()`s can be chained", function() { 37 | const wordsPromise = Promise.resolve("Sentence without an end"); 38 | return wordsPromise.then(s => removeMultipleSpaces(s)).then(actual => { 39 | assert.equal(actual, "Sentence without an end"); 40 | }); 41 | }); 42 | 43 | const trim = string => string.replace(/^\s+/, "").replace(/\s+$/, ""); 44 | 45 | it("order of the `then()`s matters", function() { 46 | const wordsPromise = Promise.resolve("Sentence without an end "); 47 | return wordsPromise 48 | .then(removeMultipleSpaces) 49 | .then(trim) 50 | .then(actual => { 51 | assert.equal(actual, "Sentence without an end"); 52 | }); 53 | }); 54 | 55 | const asyncUpperCaseStart = (string, onDone) => { 56 | const format = () => onDone(string[0].toUpperCase() + string.substr(1)); 57 | setTimeout(format, 100); 58 | }; 59 | 60 | it("any of the things given to `then()` can resolve asynchronously (the real power of Promises)", function() { 61 | const wordsPromise = Promise.resolve("sentence without an end"); 62 | wordsPromise 63 | .then(string => new Promise(resolve => asyncUpperCaseStart)) // eslint-disable-line 64 | .then( 65 | string => 66 | new Promise(resolve => 67 | setTimeout(() => resolve(appendPeriod(string)), 100) 68 | ) 69 | ) 70 | .then(actual => { 71 | assert.equal(actual, "Sentence without an end."); 72 | }); 73 | }); 74 | 75 | it("also asynchronously, the order still matters, promises wait, but don`t block", function() { 76 | const wordsPromise = Promise.resolve("trailing space "); 77 | wordsPromise 78 | .then( 79 | string => new Promise(resolve => asyncUpperCaseStart(string, resolve)) 80 | ) 81 | .then( 82 | string => 83 | new Promise(resolve => 84 | setTimeout(() => resolve(appendPeriod(string)), 100) 85 | ) 86 | ) 87 | .then( 88 | string => 89 | new Promise(resolve => setTimeout(() => resolve(trim(string)), 100)) 90 | ) 91 | .then(actual => { 92 | assert.equal(actual, "Trailing space."); 93 | }); 94 | }); 95 | }); 96 | }); 97 | -------------------------------------------------------------------------------- /test/es6katas/78-PromiseApiOverviewTest.js: -------------------------------------------------------------------------------- 1 | // 78: Promise - API overview 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("`Promise` API overview", function() { 5 | it("`new Promise()` requires a function as param", () => { 6 | const param = () => {}; 7 | assert.doesNotThrow(() => { 8 | new Promise(param); 9 | }); 10 | }); 11 | 12 | describe("resolving a promise", () => { 13 | // reminder: the test passes when a fulfilled promise is returned 14 | it("via constructor parameter `new Promise((resolve) => { resolve(); })`", () => { 15 | const param = resolve => { 16 | resolve(); 17 | }; 18 | return new Promise(param); 19 | }); 20 | it("using `Promise.resolve()`", () => { 21 | return Promise.resolve("all fine"); 22 | }); 23 | }); 24 | 25 | describe("a rejected promise", () => { 26 | it("using the constructor parameter", done => { 27 | const promise = new Promise((resolve, reject) => { 28 | reject(); 29 | }); 30 | promise 31 | .then(() => done(new Error("The promise is expected to be rejected."))) 32 | .catch(() => done()); 33 | }); 34 | it("via `Promise.reject()`", done => { 35 | const promise = Promise.reject(); 36 | promise 37 | .then(() => done(new Error("The promise is expected to be rejected."))) 38 | .catch(() => done()); 39 | }); 40 | }); 41 | 42 | const resolvingPromise = Promise.resolve(); 43 | const rejectingPromise = Promise.reject(); 44 | 45 | describe("`Promise.all()`", () => { 46 | it("`Promise.all([p1, p2])` resolves when all promises resolve", () => 47 | Promise.all([resolvingPromise, resolvingPromise])); 48 | it("`Promise.all([p1, p2])` rejects when a promise is rejected", done => { 49 | Promise.all([rejectingPromise]) 50 | .then(() => done(new Error("The promise is expected to be rejected."))) 51 | .catch(() => done()); 52 | }); 53 | }); 54 | 55 | describe("`Promise.race()`", () => { 56 | it("`Promise.race([p1, p2])` resolves when one of the promises resolves", () => 57 | Promise.race([resolvingPromise, resolvingPromise])); 58 | it("`Promise.race([p1, p2])` rejects when one of the promises rejects", done => { 59 | Promise.race([rejectingPromise, resolvingPromise]) 60 | .then(() => done(new Error("The promise is expected to be rejected."))) 61 | .catch(() => done()); 62 | }); 63 | it("`Promise.race([p1, p2])` order matters (and timing)", () => 64 | Promise.race([resolvingPromise, rejectingPromise])); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /test/es6katas/79-PromiseCatchTest.js: -------------------------------------------------------------------------------- 1 | // 79: Promise - catch 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | // Here we use promises to trigger, don't modify the block with the 4 | // returning promise! 5 | 6 | describe("`catch()` returns a Promise and deals with rejected cases only", () => { 7 | describe("prerequisites for understanding", () => { 8 | it("*return* a fulfilled promise, to pass a test", done => { 9 | Promise.resolve(); 10 | done(); 11 | assert(false); // Don't touch! Make the test pass in the line above! 12 | }); 13 | 14 | it("reminder: the test passes when a fulfilled promise is returned", () => { 15 | return Promise.resolve("I should fulfill."); 16 | }); 17 | }); 18 | 19 | describe("`catch` method basics", () => { 20 | it("is an instance method", () => { 21 | const p = Promise.resolve(); 22 | assert.equal(typeof p.catch, "function"); 23 | }); 24 | 25 | it("catches only promise rejections", done => { 26 | const promise = Promise.reject(); 27 | promise 28 | .then(() => { 29 | done("Should not be called!"); 30 | }) 31 | .catch(done); 32 | }); 33 | 34 | it("returns a new promise", () => { 35 | const whatToReturn = () => Promise.resolve(); 36 | const promise = Promise.reject(); 37 | return promise.catch(() => whatToReturn()); 38 | }); 39 | 40 | it("converts it`s return value into a promise", () => { 41 | const p = Promise.resolve("promise?"); 42 | const p1 = p.catch(() => void 0); 43 | 44 | return p1.then(result => assert.equal("promise?", result)); 45 | }); 46 | 47 | it("the first parameter is the rejection reason", () => { 48 | const p = Promise.reject("oops"); 49 | 50 | return p.catch(reason => { 51 | assert.equal(reason, "oops"); 52 | }); 53 | }); 54 | }); 55 | 56 | describe("multiple `catch`es", () => { 57 | it("only the first `catch` is called", () => { 58 | const p = Promise.reject("1"); 59 | const p1 = p 60 | .catch(reason => `${reason} AND 2`) 61 | .catch(reason => `${reason} AND 3`); 62 | 63 | return p1.then(result => assert.equal(result, "1 AND 2")); 64 | }); 65 | 66 | it("if a `catch` throws, the next `catch` catches it", () => { 67 | const p = Promise.reject("1"); 68 | const p1 = p 69 | .catch(reason => { 70 | throw Error(`${reason} AND 2`); 71 | }) 72 | .catch(err => { 73 | throw Error(`${err.message} AND 3`); 74 | }) 75 | .catch(err => err.message); 76 | 77 | return p1.then(result => assert.equal(result, "1 AND 2 AND 3")); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /test/es6katas/8-BlockScopeConstTest.js: -------------------------------------------------------------------------------- 1 | // 8: block scope - const 2 | // To do: make all tests pass, leave the asserts unchanged! 3 | 4 | describe("`const` is like `let` plus read-only", () => { 5 | describe("scalar values are read-only", () => { 6 | it("number", () => { 7 | const constNum = 0; 8 | //constNum = 1; This is going to crash 9 | assert.equal(constNum, 0); 10 | }); 11 | 12 | it("string", () => { 13 | const constString = "I am a const"; 14 | //constString = "Cant change you?"; This is going to crash 15 | assert.equal(constString, "I am a const"); 16 | }); 17 | }); 18 | 19 | const notChangeable = 23; 20 | 21 | it("const scope leaks too", () => { 22 | assert.equal(notChangeable, 23); 23 | }); 24 | 25 | describe("complex types are NOT fully read-only", () => { 26 | it("array", () => { 27 | const arr = [42, 23]; 28 | arr[0] = 0; 29 | assert.equal(arr[0], 0); 30 | }); 31 | 32 | it("object", () => { 33 | const obj = { x: 1 }; 34 | obj.x = 2; 35 | assert.equal(obj.x, 2); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/es6katas/9-ObjectLiteralsBasicsTest.js: -------------------------------------------------------------------------------- 1 | // 9: object-literals - basics 2 | // To do: make all tests pass, leave the assert lines unchanged! 3 | 4 | describe("The object literal allows for new shorthands", () => { 5 | const x = 1; 6 | const y = 2; 7 | 8 | describe("with variables", () => { 9 | it("the short version for `{x: x}` is {x}", () => { 10 | const short = { y }; 11 | assert.deepEqual(short, { y: y }); 12 | }); 13 | it("works with multiple variables too", () => { 14 | const short = { x, y }; 15 | assert.deepEqual(short, { x: x, y: y }); 16 | }); 17 | }); 18 | 19 | describe("with methods", () => { 20 | const func = () => func; 21 | 22 | it("using the name only uses it as key", () => { 23 | const short = { func }; 24 | assert.deepEqual(short, { func: func }); 25 | }); 26 | 27 | it("a different key must be given explicitly, just like before ES6", () => { 28 | const short = { otherKey: func }; 29 | assert.deepEqual(short, { otherKey: func }); 30 | }); 31 | 32 | it("inline functions, can written as `obj={func(){}}` instead of `obj={func:function(){}}`", () => { 33 | const short = { 34 | inlineFunc() { 35 | return "I am inline"; 36 | } 37 | }; 38 | assert.deepEqual(short.inlineFunc(), "I am inline"); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/fibonacci/fibonacci.spec.js: -------------------------------------------------------------------------------- 1 | import { fibonacci, fibonacciTailRec } from "../../src/fibonacci/fibonacci"; 2 | 3 | describe("Fibonacci spec", () => { 4 | jsc.property( 5 | "every fibonacci element can be expressed as the sum of the two previous items", 6 | arbitrarySmallPositiveInteger(), 7 | n => { 8 | const x = fibonacci(n); 9 | const y = fibonacci(n - 1); 10 | const z = fibonacci(n - 2); 11 | return x === y + z; 12 | } 13 | ); 14 | 15 | jsc.property( 16 | "fibonacci tail recursive version returns the same value the regular one does", 17 | arbitrarySmallPositiveInteger(), 18 | n => { 19 | return fibonacciTailRec(n) === fibonacci(n); 20 | } 21 | ); 22 | 23 | jsc.property( 24 | "tail recursive implementation should support bigger numbers", 25 | arbitraryPositiveInteger(), 26 | n => { 27 | const x = fibonacciTailRec(n); 28 | const y = fibonacciTailRec(n - 1); 29 | const z = fibonacciTailRec(n - 2); 30 | return x === y + z; 31 | } 32 | ); 33 | 34 | function arbitrarySmallPositiveInteger() { 35 | return jsc.integer(2, 20); 36 | } 37 | 38 | function arbitraryPositiveInteger() { 39 | return jsc.integer(2, 2000); 40 | } 41 | }); 42 | -------------------------------------------------------------------------------- /test/find-the-right-triangle/find-the-right-triangle.spec.js: -------------------------------------------------------------------------------- 1 | import findTheRightTriangles from "../../src/find-the-rigth-triangle/find-the-right-triangle"; 2 | 3 | describe("Find the right triangle spec", () => { 4 | it("returns a non empty array because right triangles exist", () => { 5 | const triangles = findTheRightTriangles(); 6 | 7 | expect(triangles).to.not.have.lengthOf(0); 8 | }); 9 | 10 | it("every side of each triangle is less than or equal to 10", () => { 11 | const triangles = findTheRightTriangles(); 12 | 13 | expect( 14 | triangles.every( 15 | triangle => triangle[0] <= 10 && triangle[1] <= 10 && triangle[2] <= 10 16 | ) 17 | ).to.equal(true); 18 | }); 19 | 20 | it("the perimeter of every triangle is equal to 24", () => { 21 | const triangles = findTheRightTriangles(); 22 | 23 | expect( 24 | triangles.every( 25 | triangle => triangle[0] + triangle[1] + triangle[2] === 24 26 | ) 27 | ).to.equal(true); 28 | }); 29 | 30 | it("every triangle is a right triangle", () => { 31 | const triangles = findTheRightTriangles(); 32 | 33 | expect( 34 | triangles.every( 35 | triangle => 36 | Math.pow(triangle[0], 2) + Math.pow(triangle[1], 2) === 37 | Math.pow(triangle[2], 2) 38 | ) 39 | ).to.equal(true); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/fizz-buzz/fizz-buzz.spec.js: -------------------------------------------------------------------------------- 1 | import fizzBuzz from "../../src/fizz-buzz/fizz-buzz"; 2 | 3 | describe("FizzBuzz spec", () => { 4 | it("should return the value passed as String if it's not multiple of 3 or 5", () => { 5 | const result = fizzBuzz(1); 6 | 7 | expect(result).to.equal("1"); 8 | }); 9 | 10 | it("should return Fizz if the value passed is multiple of three", () => { 11 | const result = fizzBuzz(3); 12 | 13 | expect(result).to.equal("Fizz"); 14 | }); 15 | 16 | it("should return Buzz if the value passed is multiple of five", () => { 17 | const result = fizzBuzz(5); 18 | 19 | expect(result).to.equal("Buzz"); 20 | }); 21 | 22 | it("should return FizzBUzz if the value passed is multiple of three and five", () => { 23 | const result = fizzBuzz(15); 24 | 25 | expect(result).to.equal("FizzBuzz"); 26 | }); 27 | 28 | const arbitraryMultipleOfThree = jsc.suchthat(multipleOf(3), value => { 29 | return value % 3 == 0 && value % 5 != 0; 30 | }); 31 | const arbitraryMultipleOfFive = jsc.suchthat(multipleOf(5), value => { 32 | return value % 5 == 0 && value % 3 != 0; 33 | }); 34 | const arbitraryMultipleOfThreeAndFive = multipleOf(15); 35 | 36 | function multipleOf(x) { 37 | return jsc.integer.smap(value => Math.abs(value) * x); 38 | } 39 | 40 | function arbitraryNotMultipleOf(...values) { 41 | return jsc.suchthat( 42 | jsc.integer.smap(x => Math.abs(x)), 43 | value => values.filter(x => value % x == 0).length == 0 44 | ); 45 | } 46 | 47 | jsc.property( 48 | "values not multiple of 3 or 5 should be returned as a string", 49 | arbitraryNotMultipleOf(3, 5), 50 | value => { 51 | return fizzBuzz(value) === value.toString(); 52 | } 53 | ); 54 | 55 | jsc.property( 56 | "values multiple of 3 should return Fizz", 57 | arbitraryMultipleOfThree, 58 | value => { 59 | return fizzBuzz(value) === "Fizz"; 60 | } 61 | ); 62 | 63 | jsc.property( 64 | "values multiple of 5 should return Buzz", 65 | arbitraryMultipleOfFive, 66 | value => { 67 | return fizzBuzz(value) === "Buzz"; 68 | } 69 | ); 70 | 71 | jsc.property( 72 | "values multiple of 3 and 5 should return FizzBuzz", 73 | arbitraryMultipleOfThreeAndFive, 74 | value => { 75 | return fizzBuzz(value) === "FizzBuzz"; 76 | } 77 | ); 78 | }); 79 | -------------------------------------------------------------------------------- /test/greeting/greeting.spec.js: -------------------------------------------------------------------------------- 1 | import { greet } from "../../src/greeting/greeting"; 2 | 3 | describe("Greetings kata", () => { 4 | it("should say hello to bob if the name is bob", () => { 5 | expect(greet("Bob")).to.equal("Hello, Bob."); 6 | }); 7 | 8 | it("should say hello to my friend if the input is not defined", () => { 9 | expect(greet()).to.equal("Hello, my friend."); 10 | }); 11 | 12 | it("should say hello yelling to JERRY if he's yelling", () => { 13 | expect(greet("JERRY")).to.equal("HELLO JERRY!"); 14 | }); 15 | 16 | it("should say hello to every member in the group of two people if we greet more than one person", () => { 17 | expect(greet("Jill", "Jane")).to.equal("Hello, Jill and Jane."); 18 | }); 19 | 20 | it("should say hello to my three friends using Oxford coma", () => { 21 | const result = greet("Amy", "Brian", "Charlotte"); 22 | expect(result).to.equal("Hello, Amy, Brian, and Charlotte."); 23 | }); 24 | 25 | it("should say hello to a group of friends when someone is yelling and some others not", () => { 26 | const result = greet("Amy", "BRIAN", "Charlotte"); 27 | expect(result).to.equal("Hello, Amy and Charlotte. AND HELLO BRIAN!"); 28 | }); 29 | 30 | it("should split names containing , as two names", () => { 31 | const result = greet("Amy", "Brian", "Char,lotte"); 32 | expect(result).to.equal("Hello, Amy, Brian, Char, and lotte."); 33 | }); 34 | 35 | it('should handle names rounded with "" as a single name', () => { 36 | const result = greet("Bob", '"Charlie, Dianne"'); 37 | expect(result).to.equal("Hello, Bob and Charlie, Dianne."); 38 | }); 39 | 40 | jsc.property( 41 | "says hello using the name passed as parameter", 42 | arbitraryLowerCaseRegularName(), 43 | name => { 44 | return `Hello, ${name}.` === greet(name); 45 | } 46 | ); 47 | 48 | jsc.property( 49 | "if the user of the lib is yelling, we should yell back", 50 | arbitraryLowerCaseRegularName().smap(name => name.toUpperCase()), 51 | name => { 52 | return `HELLO ${name}!` === greet(name); 53 | } 54 | ); 55 | 56 | jsc.property( 57 | "should say hello to a bunch of people using Oxford coma", 58 | arbitraryListOfNamesGreaterThan(2), 59 | names => { 60 | const result = greet(...names); 61 | const containsOxfordComa = result.indexOf(", and") !== -1; 62 | const containsAsManyComasAsNames = 63 | result.split(", ").length - 1 === names.length; 64 | const containsEveryNameButTheLastOneFollowedByAComa = names 65 | .slice(0, -1) 66 | .every(name => result.indexOf(name + ", ") !== -1); 67 | return ( 68 | containsOxfordComa && 69 | containsAsManyComasAsNames && 70 | containsEveryNameButTheLastOneFollowedByAComa 71 | ); 72 | } 73 | ); 74 | 75 | jsc.property( 76 | "the greet contains every name passed as parameter", 77 | jsc.nearray(arbitraryLowerCaseRegularName()), 78 | names => { 79 | const result = greet(...names); 80 | return names.every(name => result.indexOf(name) !== -1); 81 | } 82 | ); 83 | }); 84 | 85 | function arbitraryLowerCaseRegularName() { 86 | return jsc.suchthat( 87 | jsc.asciinestring, 88 | string => 89 | `${string}`.toUpperCase() !== string && 90 | string.indexOf(",") === -1 && 91 | string.indexOf('"') === -1 92 | ); 93 | } 94 | 95 | function arbitraryListOfNamesGreaterThan(minimumListSize) { 96 | return jsc.suchthat( 97 | jsc.nearray(arbitraryLowerCaseRegularName()), 98 | names => names.length > minimumListSize 99 | ); 100 | } 101 | -------------------------------------------------------------------------------- /test/maxibon/developer.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | Developer, 3 | Pedro, 4 | Fran, 5 | Davide, 6 | Sergio, 7 | Jorge 8 | } from "../../src/maxibon/maxibon"; 9 | 10 | describe("Developer spec", () => { 11 | jsc.property( 12 | "developers can't grab a negative number of maxibons", 13 | jsc.string, 14 | jsc.integer, 15 | (name, maxibonsToGrab) => { 16 | const dev = new Developer(name, maxibonsToGrab); 17 | return dev.maxibonsToGrab >= 0; 18 | } 19 | ); 20 | 21 | it("assign the number of maxibons specified to every developer", () => { 22 | expect(Pedro.maxibonsToGrab).to.equal(3); 23 | expect(Fran.maxibonsToGrab).to.equal(1); 24 | expect(Davide.maxibonsToGrab).to.equal(0); 25 | expect(Sergio.maxibonsToGrab).to.equal(2); 26 | expect(Jorge.maxibonsToGrab).to.equal(1); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/maxibon/karumiHQs.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | Developer, 3 | KarumiHQs, 4 | Pedro, 5 | Davide, 6 | Sergio, 7 | Fran 8 | } from "../../src/maxibon/maxibon"; 9 | 10 | describe("KarumiHQs", () => { 11 | it("starts the day with 10 maxibons", () => { 12 | const office = new KarumiHQs(); 13 | 14 | expect(office.maxibonsLeft).to.equal(10); 15 | }); 16 | 17 | jsc.property( 18 | "always has more than two maxibons in the fridge", 19 | arbitraryDeveloper(), 20 | dev => { 21 | const office = new KarumiHQs(); 22 | office.openFridge(dev); 23 | return office.maxibonsLeft > 2; 24 | } 25 | ); 26 | 27 | jsc.property( 28 | "request 10 more maxibons using the chat if there are less than 3 in the fridge", 29 | arbitraryHungryDeveloper(), 30 | dev => { 31 | const chat = new MockChat(); 32 | const office = new KarumiHQs(chat); 33 | 34 | office.openFridge(dev); 35 | 36 | return ( 37 | chat.messageSent === `Hi guys, I'm ${dev.name}. We need more maxibons!` 38 | ); 39 | } 40 | ); 41 | 42 | jsc.property( 43 | "never request more maxibons to the team using the chat if there are more than 2 in the fridge", 44 | arbitraryNotSoHungryDeveloper(), 45 | dev => { 46 | const chat = new MockChat(); 47 | const office = new KarumiHQs(chat); 48 | 49 | office.openFridge(dev); 50 | 51 | return typeof chat.messageSent === "undefined"; 52 | } 53 | ); 54 | 55 | jsc.property( 56 | "always has more than two maxibons in the fridge even if some karumies grab maxibons in group", 57 | arbitraryDevelopers(), 58 | devs => { 59 | const office = new KarumiHQs(); 60 | office.openFridge(devs); 61 | return office.maxibonsLeft > 2; 62 | } 63 | ); 64 | 65 | const testScenarios = [ 66 | [[Pedro], 7], 67 | [[Pedro, Davide], 7], 68 | [[Pedro, Davide, Fran, Sergio], 4], 69 | [[Pedro, Davide, Fran, Sergio, Pedro, Davide, Fran, Sergio], 8] 70 | ]; 71 | 72 | testScenarios.forEach(testScenario => { 73 | let devs = testScenario[0]; 74 | let expectedMaxibonsLeft = testScenario[1]; 75 | it(`if ${devs} grab some maxibons, the number of maxibons left is ${expectedMaxibonsLeft}`, () => { 76 | const office = new KarumiHQs(); 77 | 78 | office.openFridge(devs); 79 | 80 | expect(office.maxibonsLeft).to.equal(expectedMaxibonsLeft); 81 | }); 82 | }); 83 | 84 | function arbitraryDeveloper() { 85 | return jsc.integer.smap(maxibonsToGrab => { 86 | const name = jsc.sampler(jsc.string)(); 87 | return new Developer(name, maxibonsToGrab); 88 | }); 89 | } 90 | 91 | function arbitraryDevelopers() { 92 | return jsc.nearray(arbitraryDeveloper()); 93 | } 94 | 95 | function arbitraryHungryDeveloper() { 96 | return jsc.integer(8, Number.MAX_SAFE_INTEGER).smap(maxibonsToGrab => { 97 | const name = jsc.sampler(jsc.string)(); 98 | return new Developer(name, maxibonsToGrab); 99 | }); 100 | } 101 | 102 | function arbitraryNotSoHungryDeveloper() { 103 | return jsc.integer(0, 7).smap(maxibonsToGrab => { 104 | const name = jsc.sampler(jsc.string)(); 105 | return new Developer(name, maxibonsToGrab); 106 | }); 107 | } 108 | 109 | class MockChat { 110 | sendMessage(message) { 111 | this.messageSent = message; 112 | } 113 | } 114 | }); 115 | -------------------------------------------------------------------------------- /test/potter/potter.spec.js: -------------------------------------------------------------------------------- 1 | import { calculatePrice, HarryPotterBook } from "../../src/potter/potter"; 2 | 3 | describe("Potter spec", () => { 4 | it("an empty cart is free", () => { 5 | expect(calculatePrice([])).to.equal(0); 6 | }); 7 | 8 | jsc.property("just one book costs 8.0 euros", arbitraryBook(), book => { 9 | return calculatePrice([book]) === 8.0; 10 | }); 11 | 12 | jsc.property( 13 | "if there are two different books the cost of every repeated book is a 5% less", 14 | arbitraryDifferentBooks(2), 15 | cart => { 16 | return calculatePrice(cart) === 15.2; 17 | } 18 | ); 19 | 20 | jsc.property( 21 | "if there are three different books the cost of every repeated book is a 10% less", 22 | arbitraryDifferentBooks(3), 23 | cart => { 24 | return calculatePrice(cart) === 21.6; 25 | } 26 | ); 27 | 28 | jsc.property( 29 | "if there are four different books the cost of every repeated book is a 20% less", 30 | arbitraryDifferentBooks(4), 31 | cart => { 32 | return calculatePrice(cart) === 25.6; 33 | } 34 | ); 35 | 36 | jsc.property( 37 | "if there are five different books the cost of every repeated book is a 25% less", 38 | arbitraryDifferentBooks(5), 39 | cart => { 40 | return calculatePrice(cart) === 30.0; 41 | } 42 | ); 43 | 44 | jsc.property( 45 | "if all the books are the same there is no discount", 46 | arbitrarySameBooks(), 47 | cart => { 48 | return calculatePrice(cart) === cart.length * 8.0; 49 | } 50 | ); 51 | 52 | it("2 book1, 2 book2, 2 book2, 2 book3, 1 book4 and 1 book5 costs 51.60 EUR", () => { 53 | const cart = [ 54 | HarryPotterBook.BOOK_1, 55 | HarryPotterBook.BOOK_1, 56 | HarryPotterBook.BOOK_2, 57 | HarryPotterBook.BOOK_2, 58 | HarryPotterBook.BOOK_3, 59 | HarryPotterBook.BOOK_3, 60 | HarryPotterBook.BOOK_4, 61 | HarryPotterBook.BOOK_5 62 | ]; 63 | 64 | expect(calculatePrice(cart)).to.equal(51.6); 65 | }); 66 | 67 | it("2 book1, 1 book2 costs 23,2 EUR", () => { 68 | const cart = [ 69 | HarryPotterBook.BOOK_1, 70 | HarryPotterBook.BOOK_1, 71 | HarryPotterBook.BOOK_2 72 | ]; 73 | 74 | expect(calculatePrice(cart)).to.equal(23.2); 75 | }); 76 | 77 | function arbitraryBook() { 78 | return jsc.suchthat( 79 | jsc.oneof( 80 | jsc.constant(HarryPotterBook.BOOK_1), 81 | jsc.constant(HarryPotterBook.BOOK_2), 82 | jsc.constant(HarryPotterBook.BOOK_3), 83 | jsc.constant(HarryPotterBook.BOOK_4), 84 | jsc.constant(HarryPotterBook.BOOK_5) 85 | ), 86 | gesture => typeof gesture !== "undefined" 87 | ); 88 | } 89 | 90 | function arbitrarySameBooks() { 91 | const book = jsc.sampler(arbitraryBook())(); 92 | return jsc.array(jsc.constant(book)); 93 | } 94 | 95 | function arbitraryDifferentBooks(numberOfDifferentBooks) { 96 | return jsc.suchthat( 97 | jsc.nearray(jsc.elements(HarryPotterBook.enumValues)), 98 | array => 99 | array.length === numberOfDifferentBooks && 100 | array.every(x => array.filter(y => y === x).length === 1) 101 | ); 102 | } 103 | }); 104 | -------------------------------------------------------------------------------- /test/prime-factors/prime-factors.spec.js: -------------------------------------------------------------------------------- 1 | import primeFactors from "../../src/prime-factors/prime-factors"; 2 | 3 | describe("Prime factors spec", () => { 4 | jsc.property( 5 | "positive numbers have at least one prime factor", 6 | arbitraryPositiveInteger(), 7 | x => { 8 | const factors = primeFactors(x); 9 | return factors.length >= 1; 10 | } 11 | ); 12 | 13 | jsc.property( 14 | "prime numbers have just one prime factor equal to the prime number", 15 | arbitraryPrimeNumber(), 16 | x => { 17 | return primeFactors(x).length === 1; 18 | } 19 | ); 20 | 21 | jsc.property( 22 | "the product of a number prime factors is equal to the number", 23 | arbitraryPositiveInteger(), 24 | x => { 25 | return primeFactors(x).reduce((acc, x) => acc * x) === x; 26 | } 27 | ); 28 | 29 | jsc.property( 30 | "if the number is not prime the number of prime factors is at least two", 31 | arbitraryNotPrimeNumber(), 32 | x => { 33 | return primeFactors(x).length >= 2; 34 | } 35 | ); 36 | 37 | function arbitraryPositiveInteger() { 38 | return jsc.integer.smap(x => Math.abs(x) + 2); 39 | } 40 | 41 | function arbitraryPrimeNumber() { 42 | return jsc.suchthat(arbitraryPositiveInteger(), x => isPrime(x)); 43 | } 44 | 45 | function arbitraryNotPrimeNumber() { 46 | return jsc.suchthat(arbitraryPositiveInteger(), x => !isPrime(x)); 47 | } 48 | 49 | function isPrime(x) { 50 | const numbers = [...Array(x).keys()].map((v, i) => i); 51 | return numbers.filter(y => x % y === 0).length === 1; 52 | } 53 | }); 54 | -------------------------------------------------------------------------------- /test/rock-paper-scissors/rock-paper-scissors.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | Gesture, 3 | Player, 4 | Result, 5 | whoWins 6 | } from "../../src/rock-paper-scissors/rock-paper-scissors"; 7 | 8 | describe("Rock paper scissors spec", () => { 9 | const tests = [ 10 | { 11 | p1: new Player("p1", Gesture.ROCK), 12 | p2: new Player("p2", Gesture.SCISSORS), 13 | expectedResult: Result.PLAYER_1_WINS 14 | }, 15 | { 16 | p1: new Player("p1", Gesture.PAPER), 17 | p2: new Player("p2", Gesture.ROCK), 18 | expectedResult: Result.PLAYER_1_WINS 19 | }, 20 | { 21 | p1: new Player("p1", Gesture.SCISSORS), 22 | p2: new Player("p2", Gesture.PAPER), 23 | expectedResult: Result.PLAYER_1_WINS 24 | }, 25 | { 26 | p1: new Player("p1", Gesture.SCISSORS), 27 | p2: new Player("p2", Gesture.ROCK), 28 | expectedResult: Result.PLAYER_2_WINS 29 | }, 30 | { 31 | p1: new Player("p1", Gesture.ROCK), 32 | p2: new Player("p2", Gesture.PAPER), 33 | expectedResult: Result.PLAYER_2_WINS 34 | }, 35 | { 36 | p1: new Player("p1", Gesture.PAPER), 37 | p2: new Player("p2", Gesture.SCISSORS), 38 | expectedResult: Result.PLAYER_2_WINS 39 | } 40 | ]; 41 | 42 | tests.forEach(testScenario => { 43 | const p1 = testScenario.p1; 44 | const p2 = testScenario.p2; 45 | const expectedResult = testScenario.expectedResult; 46 | it(`${p1.gesture.name} vs ${p2.gesture 47 | .name} should finish the game as ${expectedResult.name}`, () => { 48 | expect(whoWins(p1, p2)).to.equal(expectedResult); 49 | }); 50 | }); 51 | 52 | jsc.property( 53 | "returns draw if the gesture is the same", 54 | arbitraryGesture(), 55 | gesture => { 56 | const p1 = new Player("p1", gesture); 57 | const p2 = new Player("p2", gesture); 58 | return whoWins(p1, p2) === Result.DRAW; 59 | } 60 | ); 61 | 62 | function arbitraryGesture() { 63 | return jsc.suchthat( 64 | jsc.oneof( 65 | jsc.constant(Gesture.ROCK), 66 | jsc.constant(Gesture.Paper), 67 | jsc.constant(Gesture.SCISSORS) 68 | ), 69 | gesture => typeof gesture !== "undefined" 70 | ); 71 | } 72 | }); 73 | -------------------------------------------------------------------------------- /test/roman-numerals/roman-numerals.spec.js: -------------------------------------------------------------------------------- 1 | import { toRoman, toArabic } from "../../src/roman-numerals/roman-numerals"; 2 | 3 | describe("Roman numerals spec", () => { 4 | jsc.property( 5 | "translate from arabic to numeral and back to roman should return the same value", 6 | arbitraryArabic(), 7 | arabic => { 8 | const romanNumeral = toRoman(arabic); 9 | return arabic === toArabic(romanNumeral); 10 | } 11 | ); 12 | 13 | jsc.property( 14 | "arabic numbers greater than zero returns non empty string as roman numeral", 15 | arbitraryNonZeroArabic(), 16 | arabic => { 17 | return toRoman(arabic).length > 0; 18 | } 19 | ); 20 | 21 | jsc.property( 22 | "roman numerals V, D or L can never be repeated", 23 | arbitraryArabic(), 24 | arabic => { 25 | const romanNumeral = toRoman(arabic); 26 | const containsAsMuchOneV = containsAsMuchOne(romanNumeral, "V"); 27 | const containsAsMuchOneD = containsAsMuchOne(romanNumeral, "D"); 28 | const containsAsMuchOneL = containsAsMuchOne(romanNumeral, "L"); 29 | return containsAsMuchOneV && containsAsMuchOneD && containsAsMuchOneL; 30 | } 31 | ); 32 | 33 | function containsAsMuchOne(string, value) { 34 | return Array.of(string).filter(s => s === value).length <= 1; 35 | } 36 | 37 | function arbitraryArabic() { 38 | return jsc.integer(0, 3000); 39 | } 40 | 41 | function arbitraryNonZeroArabic() { 42 | return jsc.suchthat(arbitraryArabic(), x => x !== 0); 43 | } 44 | }); 45 | -------------------------------------------------------------------------------- /test/string-calculator/stringCalculator.spec.js: -------------------------------------------------------------------------------- 1 | import stringCalculator from "../../src/string-calculator/string-calculator"; 2 | 3 | describe("String calculator spec", () => { 4 | it("should return 0 for an empty string", () => { 5 | const result = stringCalculator(""); 6 | 7 | expect(result).to.equal(0); 8 | }); 9 | 10 | it("should return the number inside the string if it just contains one number", () => { 11 | const result = stringCalculator("1"); 12 | 13 | expect(result).to.equal(1); 14 | }); 15 | 16 | it("should return the sum of two consecutive numbers separated by ','", () => { 17 | const result = stringCalculator("1,1"); 18 | 19 | expect(result).to.equal(2); 20 | }); 21 | 22 | it("should return the sum of two consecutive numbers separated by '\n'", () => { 23 | const result = stringCalculator("2\n3"); 24 | 25 | expect(result).to.equal(5); 26 | }); 27 | 28 | it("should return a big list of numbers separated by ','", () => { 29 | const result = stringCalculator("1,1,1,1,1,1,1,1,1,1,1"); 30 | 31 | expect(result).to.equal(11); 32 | }); 33 | 34 | it("should return 0 if the delimiter is customized but there are no values", () => { 35 | const result = stringCalculator("//x"); 36 | 37 | expect(result).to.equal(0); 38 | }); 39 | 40 | it("should return 1 if the delimiter is customized and the number inside the string is 1", () => { 41 | const result = stringCalculator("//x1"); 42 | 43 | expect(result).to.equal(1); 44 | }); 45 | 46 | it("should return the sum of big numbers separated by a custom delimiter", () => { 47 | const result = stringCalculator( 48 | "//&1665212997&1662109966&139852790&-866795925" 49 | ); 50 | 51 | expect(result).to.equal(2600379828); 52 | }); 53 | 54 | jsc.property( 55 | "result is always greater than zero if the array is not empty and full of positive values", 56 | arbitraryNonEmptyStringFullOfPositiveNumbersSeparatedByComas(), 57 | input => { 58 | return stringCalculator(input) > 0; 59 | } 60 | ); 61 | 62 | function arbitraryNonEmptyStringFullOfPositiveNumbersSeparatedByComas() { 63 | return jsc.nearray(arbitraryPositiveInt()).smap(array => array.join(",")); 64 | } 65 | 66 | function arbitraryPositiveInt() { 67 | return jsc.int32.smap(x => Math.abs(x)); 68 | } 69 | }); 70 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | entry: "./src/index.js", 5 | output: { 6 | path: path.resolve(__dirname, "dist"), 7 | filename: "bundle.js" 8 | }, 9 | module: { 10 | loaders: [ 11 | { 12 | test: /\.js$/, 13 | exclude: /(node_modules|bower_components)/, 14 | use: [ 15 | { 16 | loader: "babel-loader", 17 | options: { 18 | presets: ["env"] 19 | } 20 | } 21 | ] 22 | } 23 | ] 24 | } 25 | }; 26 | --------------------------------------------------------------------------------