├── .gitignore ├── README.md └── examples ├── .DS_Store ├── classic ├── tdd-bowling-game │ └── Bowling Game Kata.ppt ├── tdd-calculator │ ├── .eslintignore │ ├── .eslintrc │ ├── .gitignore │ ├── .prettierrc │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── README.md │ ├── build │ │ └── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── calculator.spec.ts │ │ ├── calculator.ts │ │ └── index.ts ├── tdd-elevator │ ├── .eslintignore │ ├── .eslintrc │ ├── .gitignore │ ├── .prettierrc │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── README.md │ ├── build │ │ └── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── call.ts │ │ ├── elevator.feature │ │ ├── elevator.spec.ts │ │ ├── elevator.ts │ │ ├── floor.ts │ │ └── utils │ │ ├── guard.ts │ │ └── numUtils.ts ├── tdd-fibonacci │ ├── .eslintignore │ ├── .eslintrc │ ├── .gitignore │ ├── .prettierrc │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── README.md │ ├── build │ │ └── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── index.spec.ts │ │ └── index.ts ├── tdd-fizzbuzz │ ├── .eslintignore │ ├── .eslintrc │ ├── .gitignore │ ├── .prettierrc │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── README.md │ ├── build │ │ └── index.js │ ├── jest.config.js │ ├── nodemon.json │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── fizzbuzz.spec.ts │ │ ├── fizzbuzz.ts │ │ └── index.ts │ └── tsconfig.json ├── tdd-palindrome │ ├── .eslintignore │ ├── .eslintrc │ ├── .gitignore │ ├── .prettierrc │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── README.md │ ├── build │ │ └── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── index.spec.ts │ │ └── index.ts ├── tdd-password-validator │ ├── .eslintignore │ ├── .eslintrc │ ├── .gitignore │ ├── .prettierrc │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── README.md │ ├── build │ │ └── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── index.spec.ts │ │ └── index.ts ├── tdd-recently-used-list │ └── README.md ├── tdd-stats-calculator │ ├── .eslintignore │ ├── .eslintrc │ ├── .gitignore │ ├── .prettierrc │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── README.md │ ├── build │ │ └── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── calculator.spec.ts │ │ └── calculator.ts ├── tdd-tennis │ └── README.md └── tdd-tic-tac-toe │ ├── .eslintignore │ ├── .eslintrc │ ├── .gitignore │ ├── .prettierrc │ ├── .vscode │ ├── launch.json │ └── tasks.json │ ├── README.md │ ├── build │ └── index.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ └── src │ ├── game.spec.ts │ ├── game.ts │ ├── gameState.ts │ ├── grid.ts │ ├── player.ts │ └── position.ts └── mockist └── outside-in-tdd ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .prettierrc ├── README.md ├── config.ts ├── package-lock.json ├── package.json ├── src └── makeOffer │ ├── makeOffer.spec.ts │ ├── makeOffer.ts │ └── notificationSpy.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Microbundle cache 58 | .rpt2_cache/ 59 | .rts2_cache_cjs/ 60 | .rts2_cache_es/ 61 | .rts2_cache_umd/ 62 | 63 | # Optional REPL history 64 | .node_repl_history 65 | 66 | # Output of 'npm pack' 67 | *.tgz 68 | 69 | # Yarn Integrity file 70 | .yarn-integrity 71 | 72 | # dotenv environment variables file 73 | .env 74 | .env.test 75 | .env.production 76 | 77 | # parcel-bundler cache (https://parceljs.org/) 78 | .cache 79 | .parcel-cache 80 | 81 | # Next.js build output 82 | .next 83 | out 84 | 85 | # Nuxt.js build / generate output 86 | .nuxt 87 | dist 88 | 89 | # Gatsby files 90 | .cache/ 91 | # Comment in the public line in if your project uses Gatsby and not Next.js 92 | # https://nextjs.org/blog/next-9-1#public-directory-support 93 | # public 94 | 95 | # vuepress build output 96 | .vuepress/dist 97 | 98 | # Serverless directories 99 | .serverless/ 100 | 101 | # FuseBox cache 102 | .fusebox/ 103 | 104 | # DynamoDB Local files 105 | .dynamodb/ 106 | 107 | # TernJS port file 108 | .tern-port 109 | 110 | # Stores VSCode versions used for testing VSCode extensions 111 | .vscode-test 112 | 113 | # yarn v2 114 | .yarn/cache 115 | .yarn/unplugged 116 | .yarn/build-state.yml 117 | .yarn/install-state.gz 118 | .pnp.* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # solidbook-tdd-examples 2 | 3 | > Coding katas/TDD examples from [solidbook.io](https://solidbook.io). 4 | 5 | ## About this (learning TDD) 6 | 7 | TDD is such an important technique, but it's one of the most challenging software design techniques to master. There's a lot of confusion, misdirection, and partial explanations. Many of us have tried TDD but gave up because it was too hard or felt like it took too much time. I believe that's a symptom of not fully grasping how TDD is to be used in the real world. 8 | 9 | In [Part III - Phronesis](https://wiki.solidbook.io/Part-III-Phronesis-60b174f15da34fdcb5bc8a099436a9bb), we start over. We start from scratch. Specifically, we: 10 | 11 | 1. Understand the nature of complexity and features 12 | 2. Understand how to identify behavior in features 13 | 3. Fix our understanding of object-oriented programming 14 | 4. Understand the difference between core code and infrastructure code 15 | 5. Understand how object-oriented architectures work 16 | 17 | With this foundation, in [Part IV - Test-Driven Development Basics](https://wiki.solidbook.io/Part-IV-Test-Driven-Development-Basics-106d72a39186498095782038da13a97d), [Part V - Object-Oriented Design (With Tests)](https://wiki.solidbook.io/Part-V-Object-Oriented-Design-(With-Tests)-7c9c10572cc54aa195e8ec661cf88312), and [Part X - Advanced Test-Driven Development](https://wiki.solidbook.io/Part-X-Advanced-Test-Driven-Development-d395e30edf1448219e1f0d3681b24aaf) we continue with: 18 | 19 | 6. Practicing the Classic TDD school of thought on many katas 20 | 7. Practicing the Mockist TDD school of thought on many katas 21 | 22 | At this point, you'll have an understanding of how and to what extent to use TDD in a variety of contexts (ie: the front-end, in the back-end, with E2E tests, as unit tests, and so on). 23 | 24 | Finally, the path to master involves one step: 25 | 26 | 8. Practice. And lots of it (hundreds). 27 | 28 | Let's begin. Make sure you've read [Part III - Phronesis](https://wiki.solidbook.io/Part-III-Phronesis-60b174f15da34fdcb5bc8a099436a9bb) first. 29 | 30 | ## Classic TDD examples/katas 31 | 32 | Classic TDD, created originally by Kent Beck, is also known as the Detroit/Chicago school of thought for TDD. What makes Classic TDD _classic_ is the absence of mocking (found in the _Mockist_/London-style form of TDD). In Classic TDD, we verify our classes or functions by testing them exactly as they occur without mocking out dependencies. This means that if some class we wish to test relied on the use of a database, we'd be testing that class with the database connection as well. While this gives you a greater level of confidence that your code is working correctly, for code involving [infrastructure code](https://khalilstemmler.com/articles/test-driven-development/how-to-test-code-coupled-to-apis-or-databases/#Core-code-and-infrastructure-code), it makes test setup and teardown harder (specifically with respect to unit tests) and it makes them run slower as well slower. 33 | 34 | To start our TDD journey, we focus on mastering the Classic TDD school of thought. We are solely focused on solving problems that exclusively involve [core code](https://khalilstemmler.com/articles/test-driven-development/how-to-test-code-coupled-to-apis-or-databases/#Core-code-and-infrastructure-code) (no infrastructure). 35 | 36 | ### Part IV: Test-Driven Development Basics 37 | 38 | **29. Getting Started with Classic Test-Driven Development** 39 | - [Palindrome](https://github.com/stemmlerjs/solidbook-tdd-examples/tree/main/examples/classic/tdd-palindrome) 40 | - [FizzBuzz](https://github.com/stemmlerjs/solidbook-tdd-examples/tree/main/examples/classic/tdd-fizzbuzz) 41 | - [Nth Fibonacci](https://github.com/stemmlerjs/solidbook-tdd-examples/tree/main/examples/classic/tdd-fibonacci) 42 | 43 | 44 | **30. Working Backwards using Arrange-Act-Assert** 45 | - [Stats Calculator](https://github.com/stemmlerjs/solidbook-tdd-examples/tree/main/examples/classic/tdd-stats-calculator) 46 | - [Password Validator](https://github.com/stemmlerjs/solidbook-tdd-examples/tree/main/examples/classic/tdd-password-validator) 47 | 48 | **31. Avoiding Impasses with the Transformation Priority Premise** 49 | - [Nth Fibonacci (again)](https://github.com/stemmlerjs/solidbook-tdd-examples/tree/main/examples/classic/tdd-fibonacci) 50 | - [Recently Used List](https://github.com/stemmlerjs/solidbook-tdd-examples/tree/main/examples/classic/tdd-recently-used-list) 51 | - [Tennis](https://github.com/stemmlerjs/solidbook-tdd-examples/tree/main/examples/classic/tdd-tennis) 52 | 53 | ## Mockist TDD examples/katas 54 | 55 | > Coming soon 56 | -------------------------------------------------------------------------------- /examples/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stemmlerjs/solidbook-tdd-examples/7f49c55c4ae5a3dc73067ead99587e0d21cd4c7f/examples/.DS_Store -------------------------------------------------------------------------------- /examples/classic/tdd-bowling-game/Bowling Game Kata.ppt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stemmlerjs/solidbook-tdd-examples/7f49c55c4ae5a3dc73067ead99587e0d21cd4c7f/examples/classic/tdd-bowling-game/Bowling Game Kata.ppt -------------------------------------------------------------------------------- /examples/classic/tdd-calculator/.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | build 5 | # don't lint nyc coverage output 6 | coverage 7 | -------------------------------------------------------------------------------- /examples/classic/tdd-calculator/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint", 6 | "prettier", 7 | "jest" 8 | ], 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:@typescript-eslint/recommended", 12 | "prettier" 13 | ], 14 | "rules": { 15 | "no-console": 1, 16 | "prettier/prettier": 2 17 | }, 18 | "env": { 19 | "browser": true, 20 | "node": true, 21 | "jest/globals": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/classic/tdd-calculator/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and not Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # Stores VSCode versions used for testing VSCode extensions 107 | .vscode-test 108 | -------------------------------------------------------------------------------- /examples/classic/tdd-calculator/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 80 6 | } -------------------------------------------------------------------------------- /examples/classic/tdd-calculator/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Debug TypeScript in Node.js", 8 | "preLaunchTask": "npm: build", 9 | "program": "${workspaceFolder}/src/index.ts", 10 | "protocol": "inspector", 11 | "outFiles": [ 12 | "${workspaceFolder}/dist/**/*.js" 13 | ], 14 | "sourceMaps": true, 15 | "smartStep": true, 16 | "internalConsoleOptions": "openOnSessionStart" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /examples/classic/tdd-calculator/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "typescript", 8 | "tsconfig": "simple-typescript-starter/tsconfig.json", 9 | "problemMatcher": [ 10 | "$tsc" 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /examples/classic/tdd-calculator/README.md: -------------------------------------------------------------------------------- 1 | # Fizzbuzz 2 | 3 | > Write a function that takes numbers from 1 to 100 and outputs them as a string, but for multiples of three it returns “Fizz” instead of the number, and for multiples of five it returns “Buzz.” For numbers that are multiples of both three and five, it returns “FizzBuzz.” 4 | 5 | -------------------------------------------------------------------------------- /examples/classic/tdd-calculator/build/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | console.log('Hello world!'); 3 | -------------------------------------------------------------------------------- /examples/classic/tdd-calculator/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.ts?$': 'ts-jest' 4 | }, 5 | testEnvironment: 'node', 6 | testRegex: './src/.*\\.(test|spec)?\\.(ts|ts)$', 7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 8 | "roots": [ 9 | "/src" 10 | ] 11 | }; -------------------------------------------------------------------------------- /examples/classic/tdd-calculator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-starter", 3 | "version": "1.0.0", 4 | "description": "A basic typescript app starter for newbies in 2019.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "rimraf ./build && tsc", 8 | "start:dev": "nodemon", 9 | "start": "npm run build && node build/index.js", 10 | "lint": "eslint . --ext .ts", 11 | "prettier-format": "run-script-os", 12 | "prettier-format:win32": "prettier --config .prettierrc \"./src/**/*.ts\" --write", 13 | "prettier-format:darwin:linux": "prettier --config .prettierrc 'src/**/*.ts' --write", 14 | "prettier-format:default": "prettier --config .prettierrc 'src/**/*.ts' --write", 15 | "prettier-watch": "run-script-os", 16 | "prettier-watch:win32": "onchange \"src/**/*.ts\" -- prettier --write {{changed}}", 17 | "prettier-watch:darwin:linux": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 18 | "prettier-watch:default": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 19 | "test": "jest", 20 | "test:dev": "jest --watchAll" 21 | }, 22 | "husky": { 23 | "hooks": { 24 | "pre-commit": "npm run test && npm run prettier-format && npm run lint" 25 | } 26 | }, 27 | "keywords": [], 28 | "author": "", 29 | "license": "ISC", 30 | "devDependencies": { 31 | "@types/jest": "^26.0.20", 32 | "@types/node": "^12.7.2", 33 | "@typescript-eslint/eslint-plugin": "^2.21.0", 34 | "@typescript-eslint/parser": "^2.21.0", 35 | "eslint": "^6.8.0", 36 | "eslint-config-prettier": "^6.10.0", 37 | "eslint-plugin-prettier": "^3.1.2", 38 | "husky": "^4.2.3", 39 | "nodemon": "^1.19.1", 40 | "onchange": "^6.1.0", 41 | "prettier": "^1.19.1", 42 | "rimraf": "^3.0.0", 43 | "run-script-os": "^1.1.1", 44 | "ts-node": "^8.3.0", 45 | "typescript": "^4.0.3" 46 | }, 47 | "dependencies": { 48 | "eslint-plugin-jest": "^24.1.0", 49 | "jest": "^26.5.3", 50 | "ts-jest": "^26.4.1" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/classic/tdd-calculator/src/calculator.spec.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Calculator } from "./calculator"; 3 | 4 | describe('calculator', () => { 5 | 6 | let calculator = new Calculator(); 7 | 8 | test('should exist', () => { 9 | expect(calculator).toBeDefined(); 10 | }); 11 | 12 | test('should be able to add 0, 1, or 2 numbers separated by a comma', () => { 13 | expect(calculator.add('1,1')).toEqual(2) 14 | }); 15 | 16 | test('should return 0 if no numbers provided', () => { 17 | expect(calculator.add('')).toEqual(0); 18 | }) 19 | 20 | test('should be able to return the result if only 1 number is provided', () => { 21 | expect(calculator.add('3')).toEqual(3) 22 | }) 23 | 24 | test('should be able to return the result if 2 numbers are provided', () => { 25 | expect(calculator.add('43,2')).toEqual(45) 26 | }) 27 | 28 | test('should throw an error if more than two numbers are provided', () => { 29 | // Need to wrap execution in a function to catch the error 30 | expect(() => calculator.add('1,2,3')).toThrowError(); 31 | }); 32 | 33 | test('should also throw an error if really long sequence of numbers provided', () => { 34 | expect(() => calculator.add('1,2,3,454,56,56,5656,5656,56')).toThrowError(); 35 | }) 36 | 37 | test('should throw an error if we pass in several consecutive commas', () => { 38 | expect(() => calculator.add('1,,2')).toThrowError("Invalid format"); 39 | }) 40 | 41 | test('should throw error if we pass in letters', () => { 42 | expect(() => calculator.add('a,b')).toThrow('Non-numeric characters found') 43 | }); 44 | 45 | }); 46 | -------------------------------------------------------------------------------- /examples/classic/tdd-calculator/src/calculator.ts: -------------------------------------------------------------------------------- 1 | 2 | export class Calculator { 3 | 4 | private isNumber (string: string): boolean { 5 | return !isNaN(parseInt(string)); 6 | } 7 | 8 | private isEmptyString (string: string): boolean { 9 | return string === ""; 10 | } 11 | 12 | private hasNonNumericCharacters (addendsArray: string[]): boolean { 13 | const found = addendsArray.find((addend) => !this.isNumber(addend) && !this.isEmptyString(addend)) 14 | return !!found; 15 | } 16 | 17 | private hasConsecutiveComments (string: string): boolean { 18 | return string.indexOf(",,") !== -1 19 | } 20 | 21 | public add (numbers: string): number { 22 | const addends = numbers.split(','); 23 | let sum = 0; 24 | 25 | if (this.hasNonNumericCharacters(addends)) 26 | throw new Error("Non-numeric characters found") 27 | 28 | if (this.hasConsecutiveComments(numbers)) 29 | throw new Error("Invalid format") 30 | 31 | if (addends.length > 2) 32 | throw new Error("You can only pass 0, 1, or 2 numbers to add"); 33 | 34 | addends.forEach((num) => sum += Number(num)); 35 | return sum; 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /examples/classic/tdd-calculator/src/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stemmlerjs/solidbook-tdd-examples/7f49c55c4ae5a3dc73067ead99587e0d21cd4c7f/examples/classic/tdd-calculator/src/index.ts -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | build 5 | # don't lint nyc coverage output 6 | coverage 7 | -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint", 6 | "prettier", 7 | "jest" 8 | ], 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:@typescript-eslint/recommended", 12 | "prettier" 13 | ], 14 | "rules": { 15 | "no-console": 1, 16 | "prettier/prettier": 2 17 | }, 18 | "env": { 19 | "browser": true, 20 | "node": true, 21 | "jest/globals": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and not Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # Stores VSCode versions used for testing VSCode extensions 107 | .vscode-test 108 | -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 80 6 | } -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Debug TypeScript in Node.js", 8 | "preLaunchTask": "npm: build", 9 | "program": "${workspaceFolder}/src/index.ts", 10 | "protocol": "inspector", 11 | "outFiles": [ 12 | "${workspaceFolder}/dist/**/*.js" 13 | ], 14 | "sourceMaps": true, 15 | "smartStep": true, 16 | "internalConsoleOptions": "openOnSessionStart" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "typescript", 8 | "tsconfig": "simple-typescript-starter/tsconfig.json", 9 | "problemMatcher": [ 10 | "$tsc" 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/README.md: -------------------------------------------------------------------------------- 1 | # Elevator 2 | 3 | > Implement a controller for an elevator system considering the following requirements. For evaluating purpose, assume that it takes one second to move the elevator from one floor to another and the doors stay open for three seconds at every stop. 4 | 5 | - The building has 5 total floors including basement and ground. 6 | - The elevator can be called at any floor only when it is not in use via one call button. 7 | 8 | - Given the elevator is positioned on the ground floor 9 | - When there is a call from floor3 to go to basement 10 | - And there is a call from ground to go to basement 11 | - And there is a call from floor2 to go to basement 12 | - And there is a call from floor1 to go to floor 3 13 | - Then the doors should open at floor3, basement, ground, basement, floor2, basement, floor1 and floor3 in this order 14 | 15 | -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/build/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | console.log('Hello world!'); 3 | -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.ts?$': 'ts-jest' 4 | }, 5 | testEnvironment: 'node', 6 | testRegex: './src/.*\\.(test|spec)?\\.(ts|ts)$', 7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 8 | "roots": [ 9 | "/src" 10 | ] 11 | }; -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-starter", 3 | "version": "1.0.0", 4 | "description": "A basic typescript app starter for newbies in 2019.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "rimraf ./build && tsc", 8 | "start:dev": "nodemon", 9 | "start": "npm run build && node build/index.js", 10 | "lint": "eslint . --ext .ts", 11 | "prettier-format": "run-script-os", 12 | "prettier-format:win32": "prettier --config .prettierrc \"./src/**/*.ts\" --write", 13 | "prettier-format:darwin:linux": "prettier --config .prettierrc 'src/**/*.ts' --write", 14 | "prettier-format:default": "prettier --config .prettierrc 'src/**/*.ts' --write", 15 | "prettier-watch": "run-script-os", 16 | "prettier-watch:win32": "onchange \"src/**/*.ts\" -- prettier --write {{changed}}", 17 | "prettier-watch:darwin:linux": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 18 | "prettier-watch:default": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 19 | "test": "jest", 20 | "test:dev": "jest --watchAll" 21 | }, 22 | "husky": { 23 | "hooks": { 24 | "pre-commit": "npm run test && npm run prettier-format && npm run lint" 25 | } 26 | }, 27 | "keywords": [], 28 | "author": "", 29 | "license": "ISC", 30 | "devDependencies": { 31 | "@types/jest": "^26.0.20", 32 | "@types/node": "^12.7.2", 33 | "@typescript-eslint/eslint-plugin": "^2.21.0", 34 | "@typescript-eslint/parser": "^2.21.0", 35 | "eslint": "^6.8.0", 36 | "eslint-config-prettier": "^6.10.0", 37 | "eslint-plugin-prettier": "^3.1.2", 38 | "husky": "^4.2.3", 39 | "nodemon": "^1.19.1", 40 | "onchange": "^6.1.0", 41 | "prettier": "^1.19.1", 42 | "rimraf": "^3.0.0", 43 | "run-script-os": "^1.1.1", 44 | "ts-node": "^8.3.0", 45 | "typescript": "^4.0.3" 46 | }, 47 | "dependencies": { 48 | "eslint-plugin-jest": "^24.1.0", 49 | "jest": "^26.5.3", 50 | "jest-cucumber": "^3.0.1", 51 | "ts-jest": "^26.4.1" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/src/call.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Floor } from "./floor"; 3 | import { Guard } from "./utils/guard"; 4 | 5 | export class Call { 6 | private from: Floor; 7 | private to: Floor; 8 | 9 | private constructor(from: Floor, to: Floor) { 10 | this.from = from; 11 | this.to = to; 12 | } 13 | 14 | getStartingFloor(): Floor { 15 | return this.from; 16 | } 17 | 18 | getDestinationFloor(): Floor { 19 | return this.to; 20 | } 21 | 22 | public static create(from: Floor, to: Floor): Call { 23 | Guard.againstNullOrUndefined(from, 'Must provide a from position value'); 24 | Guard.againstNullOrUndefined(to, 'Must provide a to position value'); 25 | 26 | return new Call(from, to); 27 | } 28 | } -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/src/elevator.feature: -------------------------------------------------------------------------------- 1 | Feature: Calling the elevator 2 | 3 | Scenario: Four separate calls at one time 4 | Given The elevator is positioned on the ground floor 5 | When The following calls are made 6 | | Floor | To | 7 | | 3 | basement | 8 | | ground | basement | 9 | | 2 | basement | 10 | | 1 | 3 | 11 | Then Then the doors should open at floor3, basement, ground, basement, floor2, basement, floor1 and floor3 in this order 12 | 13 | -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/src/elevator.spec.ts: -------------------------------------------------------------------------------- 1 | import { loadFeature, defineFeature } from 'jest-cucumber'; 2 | import { Elevator } from './elevator'; 3 | import { Floor } from './floor'; 4 | 5 | const feature = loadFeature('src/elevator.feature'); 6 | 7 | defineFeature(feature, test => { 8 | let elevator: Elevator; 9 | 10 | test('Four separate calls at one time', ({ given, when, then }) => { 11 | given('The elevator is positioned on the ground floor', () => { 12 | elevator = new Elevator(Floor.create('ground')); 13 | }); 14 | 15 | when('The following calls are made', calls => { 16 | for (const call of calls) { 17 | elevator.call({ from: call.Floor, to: call.To }); 18 | } 19 | expect(elevator.getCurrentCalls()).toHaveLength(4); 20 | }); 21 | 22 | then( 23 | /^Then the doors should open at floor(\d+), basement, ground, basement, floor(\d+), basement, floor(\d+) and floor(\d+) in this order$/, 24 | (arg0, arg1, arg2, arg3) => { 25 | let floorsOpenedAt: number[] = []; 26 | 27 | while (elevator.hasCalls()) { 28 | elevator.handleCall(); 29 | } 30 | 31 | floorsOpenedAt = elevator 32 | .getEvents() 33 | .filter(e => e.type === 'DoorsOpenedAndClosed') 34 | .map(e => e.value.floor); 35 | 36 | expect(floorsOpenedAt).toEqual([ 37 | Number(arg0), 38 | -1, 39 | 0, 40 | -1, 41 | Number(arg1), 42 | -1, 43 | Number(arg2), 44 | Number(arg3), 45 | ]); 46 | }, 47 | ); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/src/elevator.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Call } from './call'; 3 | import { Floor } from './floor'; 4 | 5 | export type FloorValue = 'ground' | 'basement' | number; 6 | 7 | export type Direction = 'Up' | 'Down' | 'None'; 8 | 9 | export enum Floors { 10 | basement = -1, 11 | ground = 0, 12 | floor1 = 1, 13 | floor2 = 2, 14 | floor3 = 3 15 | } 16 | 17 | interface Event { 18 | type: string; 19 | value: any; 20 | timestamp: number; 21 | } 22 | 23 | export interface FloorChanged extends Event { 24 | type: 'FloorChanged'; 25 | value: { 26 | start: number; 27 | end: number; 28 | }; 29 | } 30 | 31 | export interface DoorsOpenedAndClosed extends Event { 32 | type: 'DoorsOpenedAndClosed'; 33 | value: { 34 | floor: number; 35 | }; 36 | } 37 | 38 | export class Elevator { 39 | private floor: Floor; 40 | private currentCalls: Call[]; 41 | private elapsedTimeInSeconds: number; 42 | private events: Event[]; 43 | 44 | constructor (startingFloor: Floor) { 45 | this.floor = startingFloor; 46 | this.currentCalls = []; 47 | this.elapsedTimeInSeconds = 0; 48 | this.events = []; 49 | } 50 | 51 | getCurrentCalls(): Call[] { 52 | return this.currentCalls; 53 | } 54 | 55 | hasCalls(): boolean { 56 | return this.currentCalls.length !== 0; 57 | } 58 | 59 | getCurrentFloor(): Floor { 60 | return this.floor; 61 | } 62 | 63 | getEvents(): Event[] { 64 | return this.events; 65 | } 66 | 67 | private determineDirection(start: number, dest: number): Direction { 68 | if (start < dest) { 69 | return 'Up'; 70 | } 71 | 72 | if (start > dest) { 73 | return 'Down'; 74 | } 75 | 76 | return 'None'; 77 | } 78 | 79 | handleCall(): void { 80 | const nextCall = this.currentCalls.splice(0, 1)[0]; 81 | 82 | const startingFloorValue = nextCall.getStartingFloor().getValue(); 83 | const destinationFloorValue = nextCall.getDestinationFloor().getValue(); 84 | 85 | const startingDirection = this.determineDirection( 86 | this.getCurrentFloor().getValue(), 87 | startingFloorValue, 88 | ); 89 | const endingDirection = this.determineDirection( 90 | startingFloorValue, 91 | destinationFloorValue, 92 | ); 93 | 94 | const startingDistance = Math.abs( 95 | this.getCurrentFloor().getValue() - startingFloorValue, 96 | ); 97 | 98 | const endingDistance = Math.abs(startingFloorValue - destinationFloorValue); 99 | 100 | for (let i = 0; i < startingDistance; i++) { 101 | this.advance(startingDirection); 102 | } 103 | 104 | this.openAndClose(); 105 | 106 | for (let i = 0; i < endingDistance; i++) { 107 | this.advance(endingDirection); 108 | } 109 | 110 | this.openAndClose(); 111 | } 112 | 113 | private advance(direction: Direction): void { 114 | this.elapsedTimeInSeconds++; 115 | 116 | let newFloor: Floor; 117 | 118 | if (direction === 'Up') { 119 | newFloor = this.floor.ascend(); 120 | } 121 | 122 | if (direction === 'Down') { 123 | newFloor = this.floor.descend(); 124 | } 125 | 126 | this.events.push({ 127 | type: 'FloorChanged', 128 | timestamp: this.elapsedTimeInSeconds, 129 | value: { 130 | start: this.floor.getValue(), 131 | end: newFloor.getValue(), 132 | }, 133 | } as FloorChanged); 134 | 135 | this.floor = newFloor; 136 | } 137 | 138 | private openAndClose(): void { 139 | this.elapsedTimeInSeconds += 3; 140 | 141 | this.events.push({ 142 | type: 'DoorsOpenedAndClosed', 143 | timestamp: this.elapsedTimeInSeconds, 144 | value: { 145 | floor: this.floor.getValue(), 146 | }, 147 | } as DoorsOpenedAndClosed); 148 | } 149 | 150 | call({ from, to }: { from: FloorValue; to: FloorValue }): void { 151 | const fromFloor = Floor.create(from); 152 | const downFloor = Floor.create(to); 153 | 154 | const call = Call.create(fromFloor, downFloor); 155 | this.currentCalls.push(call); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/src/floor.ts: -------------------------------------------------------------------------------- 1 | 2 | import { FloorValue } from "./elevator"; 3 | import { Guard } from "./utils/guard"; 4 | import { NumUtils } from "./utils/numUtils"; 5 | 6 | export class Floor { 7 | private static MinFloor = -1; 8 | private static MaxFloor = 5; 9 | 10 | private value: number; 11 | 12 | private constructor(value: number) { 13 | this.value = value; 14 | } 15 | 16 | getValue(): number { 17 | return this.value; 18 | } 19 | 20 | ascend(): Floor { 21 | if (this.value === Floor.MaxFloor) { 22 | throw new Error("Can't ascend further past top floor"); 23 | } 24 | 25 | return new Floor(this.value + 1); 26 | } 27 | 28 | descend(): Floor { 29 | if (this.value === Floor.MinFloor) { 30 | throw new Error("Can't descend further past basement"); 31 | } 32 | 33 | return new Floor(this.value - 1); 34 | } 35 | 36 | public static create(value: FloorValue): Floor { 37 | Guard.againstNullOrUndefined(value, 'Must provide a position value'); 38 | 39 | if (typeof value === 'number' && !NumUtils.greaterThanOrEq(value, -1)) { 40 | throw new Error('Must be greater than or equal to -1'); 41 | } 42 | 43 | return value === 'ground' 44 | ? new Floor(0) 45 | : value === 'basement' 46 | ? new Floor(-1) 47 | : new Floor(Number(value)); 48 | } 49 | } -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/src/utils/guard.ts: -------------------------------------------------------------------------------- 1 | export class Guard { 2 | public static againstNullOrUndefined(obj: any, message: string): void { 3 | if (obj === undefined || obj === null) { 4 | throw new Error(message); 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/classic/tdd-elevator/src/utils/numUtils.ts: -------------------------------------------------------------------------------- 1 | export class NumUtils { 2 | public static greaterThanOrEq( 3 | num: number, 4 | isGreaterThanOrEq: number, 5 | ): boolean { 6 | return num >= isGreaterThanOrEq; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/classic/tdd-fibonacci/.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | build 5 | # don't lint nyc coverage output 6 | coverage 7 | -------------------------------------------------------------------------------- /examples/classic/tdd-fibonacci/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint", 6 | "prettier", 7 | "jest" 8 | ], 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:@typescript-eslint/recommended", 12 | "prettier" 13 | ], 14 | "rules": { 15 | "no-console": 1, 16 | "prettier/prettier": 2 17 | }, 18 | "env": { 19 | "browser": true, 20 | "node": true, 21 | "jest/globals": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/classic/tdd-fibonacci/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and not Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # Stores VSCode versions used for testing VSCode extensions 107 | .vscode-test 108 | -------------------------------------------------------------------------------- /examples/classic/tdd-fibonacci/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 80 6 | } -------------------------------------------------------------------------------- /examples/classic/tdd-fibonacci/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Debug TypeScript in Node.js", 8 | "preLaunchTask": "npm: build", 9 | "program": "${workspaceFolder}/src/index.ts", 10 | "protocol": "inspector", 11 | "outFiles": [ 12 | "${workspaceFolder}/dist/**/*.js" 13 | ], 14 | "sourceMaps": true, 15 | "smartStep": true, 16 | "internalConsoleOptions": "openOnSessionStart" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /examples/classic/tdd-fibonacci/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "typescript", 8 | "tsconfig": "simple-typescript-starter/tsconfig.json", 9 | "problemMatcher": [ 10 | "$tsc" 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /examples/classic/tdd-fibonacci/README.md: -------------------------------------------------------------------------------- 1 | # Fibonacci 2 | 3 | > The Fibonacci sequence is an interesting mathematical phenomenon. Starting from 0 and 1, each number is the sum of the two preceding ones. This means that the first ten numbers in the sequence are 0, 1, 1, 2, 3, 5, 8, 13, 21, 34. 4 | 5 | > Notice that the 2 can be found by adding the two numbers before it (1 + 1)? And notice that the 3 can be found by adding the two numbers before it as well (1 + 2)? Assuming indexes start at 0, write a function (or a class) that accepts any positive integer index and generates the Fibonacci number for that nth position. 6 | 7 | > Example: fibonacci(3) should return 2. 8 | 9 | -------------------------------------------------------------------------------- /examples/classic/tdd-fibonacci/build/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | console.log('Hello world!'); 3 | -------------------------------------------------------------------------------- /examples/classic/tdd-fibonacci/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.ts?$': 'ts-jest' 4 | }, 5 | testEnvironment: 'node', 6 | testRegex: './src/.*\\.(test|spec)?\\.(ts|ts)$', 7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 8 | "roots": [ 9 | "/src" 10 | ] 11 | }; -------------------------------------------------------------------------------- /examples/classic/tdd-fibonacci/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-starter", 3 | "version": "1.0.0", 4 | "description": "A basic typescript app starter for newbies in 2019.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "rimraf ./build && tsc", 8 | "start:dev": "nodemon", 9 | "start": "npm run build && node build/index.js", 10 | "lint": "eslint . --ext .ts", 11 | "prettier-format": "run-script-os", 12 | "prettier-format:win32": "prettier --config .prettierrc \"./src/**/*.ts\" --write", 13 | "prettier-format:darwin:linux": "prettier --config .prettierrc 'src/**/*.ts' --write", 14 | "prettier-format:default": "prettier --config .prettierrc 'src/**/*.ts' --write", 15 | "prettier-watch": "run-script-os", 16 | "prettier-watch:win32": "onchange \"src/**/*.ts\" -- prettier --write {{changed}}", 17 | "prettier-watch:darwin:linux": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 18 | "prettier-watch:default": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 19 | "test": "jest", 20 | "test:dev": "jest --watchAll" 21 | }, 22 | "husky": { 23 | "hooks": { 24 | "pre-commit": "npm run test && npm run prettier-format && npm run lint" 25 | } 26 | }, 27 | "keywords": [], 28 | "author": "", 29 | "license": "ISC", 30 | "devDependencies": { 31 | "@types/jest": "^26.0.20", 32 | "@types/node": "^12.7.2", 33 | "@typescript-eslint/eslint-plugin": "^2.21.0", 34 | "@typescript-eslint/parser": "^2.21.0", 35 | "eslint": "^6.8.0", 36 | "eslint-config-prettier": "^6.10.0", 37 | "eslint-plugin-prettier": "^3.1.2", 38 | "husky": "^4.2.3", 39 | "nodemon": "^1.19.1", 40 | "onchange": "^6.1.0", 41 | "prettier": "^1.19.1", 42 | "rimraf": "^3.0.0", 43 | "run-script-os": "^1.1.1", 44 | "ts-node": "^8.3.0", 45 | "typescript": "^4.0.3" 46 | }, 47 | "dependencies": { 48 | "eslint-plugin-jest": "^24.1.0", 49 | "jest": "^26.5.3", 50 | "ts-jest": "^26.4.1" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/classic/tdd-fibonacci/src/index.spec.ts: -------------------------------------------------------------------------------- 1 | 2 | import { fibonacci } from './index' 3 | 4 | describe('fibonacci', () => { 5 | 6 | it('returns 0 for sequence position 0', () => { 7 | expect(fibonacci(0)).toEqual(0) 8 | }) 9 | 10 | it('returns 1 for sequence position 1', () => { 11 | expect(fibonacci(1)).toEqual(1) 12 | }); 13 | 14 | it('returns 1 for sequence position 2', () => { 15 | expect(fibonacci(2)).toEqual(1) 16 | }); 17 | 18 | it('returns 2 for sequence position 3', () => { 19 | expect(fibonacci(3)).toEqual(2) 20 | }); 21 | 22 | it('returns 3 for sequence position 4', () => { 23 | expect(fibonacci(4)).toEqual(3) 24 | }); 25 | 26 | it('returns 5 for sequence position 5', () => { 27 | expect(fibonacci(5)).toEqual(5) 28 | }); 29 | 30 | it('returns 8 for sequence position 6', () => { 31 | expect(fibonacci(6)).toEqual(8) 32 | }); 33 | 34 | it('returns 13 for sequence position 7', () => { 35 | expect(fibonacci(7)).toEqual(13) 36 | }); 37 | 38 | it('returns 21 for sequence position 8', () => { 39 | expect(fibonacci(8)).toEqual(21) 40 | }); 41 | 42 | it('returns 34 for sequence position 9', () => { 43 | expect(fibonacci(9)).toEqual(34) 44 | }); 45 | 46 | }) -------------------------------------------------------------------------------- /examples/classic/tdd-fibonacci/src/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export function fibonacci (index: number) { 3 | return index === 0 || index === 1 4 | ? index 5 | : fibonacci(index - 1) + fibonacci(index - 2); 6 | } -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | build 5 | # don't lint nyc coverage output 6 | coverage 7 | -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint", 6 | "prettier", 7 | "jest" 8 | ], 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:@typescript-eslint/recommended", 12 | "prettier" 13 | ], 14 | "rules": { 15 | "no-console": 1, 16 | "prettier/prettier": 2 17 | }, 18 | "env": { 19 | "browser": true, 20 | "node": true, 21 | "jest/globals": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and not Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # Stores VSCode versions used for testing VSCode extensions 107 | .vscode-test 108 | -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 80 6 | } -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Debug TypeScript in Node.js", 8 | "preLaunchTask": "npm: build", 9 | "program": "${workspaceFolder}/src/index.ts", 10 | "protocol": "inspector", 11 | "outFiles": [ 12 | "${workspaceFolder}/dist/**/*.js" 13 | ], 14 | "sourceMaps": true, 15 | "smartStep": true, 16 | "internalConsoleOptions": "openOnSessionStart" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "typescript", 8 | "tsconfig": "simple-typescript-starter/tsconfig.json", 9 | "problemMatcher": [ 10 | "$tsc" 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/README.md: -------------------------------------------------------------------------------- 1 | # Fizzbuzz 2 | 3 | > Write a function that takes numbers from 1 to 100 and outputs them as a string, but for multiples of three it returns “Fizz” instead of the number, and for multiples of five it returns “Buzz.” For numbers that are multiples of both three and five, it returns “FizzBuzz.” 4 | 5 | -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/build/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | console.log('Hello world!'); 3 | -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.ts?$': 'ts-jest' 4 | }, 5 | testEnvironment: 'node', 6 | testRegex: './src/.*\\.(test|spec)?\\.(ts|ts)$', 7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 8 | "roots": [ 9 | "/src" 10 | ] 11 | }; -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": ".ts,.js", 4 | "ignore": [], 5 | "exec": "ts-node ./src/index.ts" 6 | } -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-starter", 3 | "version": "1.0.0", 4 | "description": "A basic typescript app starter for newbies in 2019.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "rimraf ./build && tsc", 8 | "start:dev": "nodemon", 9 | "start": "npm run build && node build/index.js", 10 | "lint": "eslint . --ext .ts", 11 | "prettier-format": "run-script-os", 12 | "prettier-format:win32": "prettier --config .prettierrc \"./src/**/*.ts\" --write", 13 | "prettier-format:darwin:linux": "prettier --config .prettierrc 'src/**/*.ts' --write", 14 | "prettier-format:default": "prettier --config .prettierrc 'src/**/*.ts' --write", 15 | "prettier-watch": "run-script-os", 16 | "prettier-watch:win32": "onchange \"src/**/*.ts\" -- prettier --write {{changed}}", 17 | "prettier-watch:darwin:linux": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 18 | "prettier-watch:default": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 19 | "test": "jest", 20 | "test:dev": "jest --watchAll" 21 | }, 22 | "husky": { 23 | "hooks": { 24 | "pre-commit": "npm run test && npm run prettier-format && npm run lint" 25 | } 26 | }, 27 | "keywords": [], 28 | "author": "", 29 | "license": "ISC", 30 | "devDependencies": { 31 | "@types/jest": "^26.0.20", 32 | "@types/node": "^12.7.2", 33 | "@typescript-eslint/eslint-plugin": "^2.21.0", 34 | "@typescript-eslint/parser": "^2.21.0", 35 | "eslint": "^6.8.0", 36 | "eslint-config-prettier": "^6.10.0", 37 | "eslint-plugin-prettier": "^3.1.2", 38 | "husky": "^4.2.3", 39 | "nodemon": "^1.19.1", 40 | "onchange": "^6.1.0", 41 | "prettier": "^1.19.1", 42 | "rimraf": "^3.0.0", 43 | "run-script-os": "^1.1.1", 44 | "ts-node": "^8.3.0", 45 | "typescript": "^4.0.3" 46 | }, 47 | "dependencies": { 48 | "eslint-plugin-jest": "^24.1.0", 49 | "jest": "^26.5.3", 50 | "ts-jest": "^26.4.1" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/src/fizzbuzz.spec.ts: -------------------------------------------------------------------------------- 1 | 2 | import { fizzbuzz } from './fizzbuzz' 3 | 4 | describe('fizzbuzz', () => { 5 | 6 | test('should return a string when given two numbers', () => { 7 | expect(typeof fizzbuzz(1, 2)).toBe('string') 8 | }); 9 | 10 | test('should throw an error if either numbers are outside of 1-100 range', () => { 11 | expect(() => fizzbuzz(0, 101)).toThrowError(); 12 | }); 13 | 14 | test('should still throw an error if only one number is outside of range', () => { 15 | expect(() => fizzbuzz(1, 200)).toThrowError(); 16 | }) 17 | 18 | test('should still return a string if numbers are exactly on 1 to 100 range', () => { 19 | expect(typeof fizzbuzz(1, 100)).toBe('string') 20 | }) 21 | 22 | test('should return the word "Fizz" if either number is a multiple of 3', () => { 23 | expect(fizzbuzz(3, 4)).toEqual('Fizz') 24 | }) 25 | 26 | test('should return the word "Fizz" if only one number is a multiple of 3', () => { 27 | expect(fizzbuzz(3, 5)).toEqual('Fizz') 28 | }) 29 | 30 | test('should return an empty string if neither is a multiple of 3', () => { 31 | expect(fizzbuzz(1, 1)).toEqual('') 32 | }) 33 | 34 | test('should return the word "Buzz" if either number is a multiple of 5', () => { 35 | expect(fizzbuzz(1, 5)).toEqual('Buzz') 36 | }) 37 | 38 | test('should return the word "Fizzbuzz" for numbers that are multiples of 3 AND 5', () => { 39 | expect(fizzbuzz(1, 15)).toEqual('Fizzbuzz') 40 | }) 41 | 42 | }) -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/src/fizzbuzz.ts: -------------------------------------------------------------------------------- 1 | 2 | const isWithinRange = (number: number) => { 3 | if (number < 1 || number > 100) { 4 | return false; 5 | } else { 6 | return true; 7 | } 8 | } 9 | 10 | const isMultipleOf = function (divisor: number, number: number) : boolean { 11 | return number % divisor === 0; 12 | } 13 | 14 | export function fizzbuzz (numOne: number, numTwo: number): string { 15 | 16 | if (!isWithinRange(numOne) || !isWithinRange(numTwo)) 17 | throw new Error ("Numbers not within 1-100 range") 18 | 19 | const isMultipleOfThreeFound = isMultipleOf(3, numOne) || isMultipleOf(3, numTwo); 20 | const isMultipleOfFiveFound = isMultipleOf(5, numOne) || isMultipleOf(5, numTwo); 21 | 22 | if (isMultipleOfThreeFound && isMultipleOfFiveFound) { 23 | return "Fizzbuzz" 24 | } 25 | 26 | if (isMultipleOfThreeFound) { 27 | return "Fizz" 28 | } 29 | 30 | if (isMultipleOfFiveFound) { 31 | return "Buzz" 32 | } 33 | 34 | return ""; 35 | } -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/src/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export function fizzBuzz (number: number) { 3 | return "1" 4 | } -------------------------------------------------------------------------------- /examples/classic/tdd-fizzbuzz/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "lib": ["es6"], 6 | "allowJs": true, 7 | "outDir": "build", 8 | "rootDir": "src", 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "esModuleInterop": true, 12 | "resolveJsonModule": true, 13 | "types": ["node", "@types/jest"], 14 | "typeRoots" : ["./node_modules/@types"], 15 | }, 16 | "include": [ 17 | "src/**/*" 18 | ], 19 | "exclude" : [ 20 | "src/**/*.spec.ts" 21 | ] 22 | } -------------------------------------------------------------------------------- /examples/classic/tdd-palindrome/.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | build 5 | # don't lint nyc coverage output 6 | coverage 7 | -------------------------------------------------------------------------------- /examples/classic/tdd-palindrome/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint", 6 | "prettier", 7 | "jest" 8 | ], 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:@typescript-eslint/recommended", 12 | "prettier" 13 | ], 14 | "rules": { 15 | "no-console": 1, 16 | "prettier/prettier": 2 17 | }, 18 | "env": { 19 | "browser": true, 20 | "node": true, 21 | "jest/globals": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/classic/tdd-palindrome/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and not Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # Stores VSCode versions used for testing VSCode extensions 107 | .vscode-test 108 | -------------------------------------------------------------------------------- /examples/classic/tdd-palindrome/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 80 6 | } -------------------------------------------------------------------------------- /examples/classic/tdd-palindrome/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Debug TypeScript in Node.js", 8 | "preLaunchTask": "npm: build", 9 | "program": "${workspaceFolder}/src/index.ts", 10 | "protocol": "inspector", 11 | "outFiles": [ 12 | "${workspaceFolder}/dist/**/*.js" 13 | ], 14 | "sourceMaps": true, 15 | "smartStep": true, 16 | "internalConsoleOptions": "openOnSessionStart" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /examples/classic/tdd-palindrome/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "typescript", 8 | "tsconfig": "simple-typescript-starter/tsconfig.json", 9 | "problemMatcher": [ 10 | "$tsc" 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /examples/classic/tdd-palindrome/README.md: -------------------------------------------------------------------------------- 1 | # Fizzbuzz 2 | 3 | > Write a function that takes numbers from 1 to 100 and outputs them as a string, but for multiples of three it returns “Fizz” instead of the number, and for multiples of five it returns “Buzz.” For numbers that are multiples of both three and five, it returns “FizzBuzz.” 4 | 5 | -------------------------------------------------------------------------------- /examples/classic/tdd-palindrome/build/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | console.log('Hello world!'); 3 | -------------------------------------------------------------------------------- /examples/classic/tdd-palindrome/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.ts?$': 'ts-jest' 4 | }, 5 | testEnvironment: 'node', 6 | testRegex: './src/.*\\.(test|spec)?\\.(ts|ts)$', 7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 8 | "roots": [ 9 | "/src" 10 | ] 11 | }; -------------------------------------------------------------------------------- /examples/classic/tdd-palindrome/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-starter", 3 | "version": "1.0.0", 4 | "description": "A basic typescript app starter for newbies in 2019.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "rimraf ./build && tsc", 8 | "start:dev": "nodemon", 9 | "start": "npm run build && node build/index.js", 10 | "lint": "eslint . --ext .ts", 11 | "prettier-format": "run-script-os", 12 | "prettier-format:win32": "prettier --config .prettierrc \"./src/**/*.ts\" --write", 13 | "prettier-format:darwin:linux": "prettier --config .prettierrc 'src/**/*.ts' --write", 14 | "prettier-format:default": "prettier --config .prettierrc 'src/**/*.ts' --write", 15 | "prettier-watch": "run-script-os", 16 | "prettier-watch:win32": "onchange \"src/**/*.ts\" -- prettier --write {{changed}}", 17 | "prettier-watch:darwin:linux": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 18 | "prettier-watch:default": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 19 | "test": "jest", 20 | "test:dev": "jest --watchAll" 21 | }, 22 | "husky": { 23 | "hooks": { 24 | "pre-commit": "npm run test && npm run prettier-format && npm run lint" 25 | } 26 | }, 27 | "keywords": [], 28 | "author": "", 29 | "license": "ISC", 30 | "devDependencies": { 31 | "@types/jest": "^26.0.20", 32 | "@types/node": "^12.7.2", 33 | "@typescript-eslint/eslint-plugin": "^2.21.0", 34 | "@typescript-eslint/parser": "^2.21.0", 35 | "eslint": "^6.8.0", 36 | "eslint-config-prettier": "^6.10.0", 37 | "eslint-plugin-prettier": "^3.1.2", 38 | "husky": "^4.2.3", 39 | "nodemon": "^1.19.1", 40 | "onchange": "^6.1.0", 41 | "prettier": "^1.19.1", 42 | "rimraf": "^3.0.0", 43 | "run-script-os": "^1.1.1", 44 | "ts-node": "^8.3.0", 45 | "typescript": "^4.0.3" 46 | }, 47 | "dependencies": { 48 | "eslint-plugin-jest": "^24.1.0", 49 | "jest": "^26.5.3", 50 | "ts-jest": "^26.4.1" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/classic/tdd-palindrome/src/index.spec.ts: -------------------------------------------------------------------------------- 1 | 2 | describe('fizzbuzz', () => { 3 | 4 | test('s', async () => { 5 | expect("1").toBe(1); 6 | }); 7 | 8 | }); 9 | -------------------------------------------------------------------------------- /examples/classic/tdd-palindrome/src/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export function fizzBuzz (number: number) { 3 | return "1" 4 | } -------------------------------------------------------------------------------- /examples/classic/tdd-password-validator/.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | build 5 | # don't lint nyc coverage output 6 | coverage 7 | -------------------------------------------------------------------------------- /examples/classic/tdd-password-validator/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint", 6 | "prettier", 7 | "jest" 8 | ], 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:@typescript-eslint/recommended", 12 | "prettier" 13 | ], 14 | "rules": { 15 | "no-console": 1, 16 | "prettier/prettier": 2 17 | }, 18 | "env": { 19 | "browser": true, 20 | "node": true, 21 | "jest/globals": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/classic/tdd-password-validator/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and not Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # Stores VSCode versions used for testing VSCode extensions 107 | .vscode-test 108 | -------------------------------------------------------------------------------- /examples/classic/tdd-password-validator/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 80 6 | } -------------------------------------------------------------------------------- /examples/classic/tdd-password-validator/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Debug TypeScript in Node.js", 8 | "preLaunchTask": "npm: build", 9 | "program": "${workspaceFolder}/src/index.ts", 10 | "protocol": "inspector", 11 | "outFiles": [ 12 | "${workspaceFolder}/dist/**/*.js" 13 | ], 14 | "sourceMaps": true, 15 | "smartStep": true, 16 | "internalConsoleOptions": "openOnSessionStart" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /examples/classic/tdd-password-validator/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "typescript", 8 | "tsconfig": "simple-typescript-starter/tsconfig.json", 9 | "problemMatcher": [ 10 | "$tsc" 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /examples/classic/tdd-password-validator/README.md: -------------------------------------------------------------------------------- 1 | # Password validator 2 | 3 | Write a function (or a class) for validating passwords. Passwords must meet the following criteria: 4 | 5 | - Between 5 and 15 characters long 6 | - Contains at least one digit 7 | - Contains at least one upper case letter 8 | 9 | Return an object containing a boolean result and an errors key that — when provided with an invalid password — contains an error message for all errors in occurrence. There can be multiple errors at a single time. -------------------------------------------------------------------------------- /examples/classic/tdd-password-validator/build/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | console.log('Hello world!'); 3 | -------------------------------------------------------------------------------- /examples/classic/tdd-password-validator/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.ts?$': 'ts-jest' 4 | }, 5 | testEnvironment: 'node', 6 | testRegex: './src/.*\\.(test|spec)?\\.(ts|ts)$', 7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 8 | "roots": [ 9 | "/src" 10 | ] 11 | }; -------------------------------------------------------------------------------- /examples/classic/tdd-password-validator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-starter", 3 | "version": "1.0.0", 4 | "description": "A basic typescript app starter for newbies in 2019.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "rimraf ./build && tsc", 8 | "start:dev": "nodemon", 9 | "start": "npm run build && node build/index.js", 10 | "lint": "eslint . --ext .ts", 11 | "prettier-format": "run-script-os", 12 | "prettier-format:win32": "prettier --config .prettierrc \"./src/**/*.ts\" --write", 13 | "prettier-format:darwin:linux": "prettier --config .prettierrc 'src/**/*.ts' --write", 14 | "prettier-format:default": "prettier --config .prettierrc 'src/**/*.ts' --write", 15 | "prettier-watch": "run-script-os", 16 | "prettier-watch:win32": "onchange \"src/**/*.ts\" -- prettier --write {{changed}}", 17 | "prettier-watch:darwin:linux": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 18 | "prettier-watch:default": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 19 | "test": "jest", 20 | "test:dev": "jest --watchAll" 21 | }, 22 | "husky": { 23 | "hooks": { 24 | "pre-commit": "npm run test && npm run prettier-format && npm run lint" 25 | } 26 | }, 27 | "keywords": [], 28 | "author": "", 29 | "license": "ISC", 30 | "devDependencies": { 31 | "@types/jest": "^26.0.20", 32 | "@types/node": "^12.7.2", 33 | "@typescript-eslint/eslint-plugin": "^2.21.0", 34 | "@typescript-eslint/parser": "^2.21.0", 35 | "eslint": "^6.8.0", 36 | "eslint-config-prettier": "^6.10.0", 37 | "eslint-plugin-prettier": "^3.1.2", 38 | "husky": "^4.2.3", 39 | "nodemon": "^1.19.1", 40 | "onchange": "^6.1.0", 41 | "prettier": "^1.19.1", 42 | "rimraf": "^3.0.0", 43 | "run-script-os": "^1.1.1", 44 | "ts-node": "^8.3.0", 45 | "typescript": "^4.0.3" 46 | }, 47 | "dependencies": { 48 | "eslint-plugin-jest": "^24.1.0", 49 | "jest": "^26.5.3", 50 | "ts-jest": "^26.4.1" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/classic/tdd-password-validator/src/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { PasswordValidator } from "."; 2 | 3 | describe('password calculator', () => { 4 | let result; 5 | let passwordValidator: PasswordValidator; 6 | 7 | beforeEach(() => { 8 | result = undefined; 9 | passwordValidator = new PasswordValidator(); 10 | }) 11 | 12 | describe ('length within 5 and 15 characters long', () => { 13 | 14 | it ("knows that 'man' is not valid out of bounds", () => { 15 | result = passwordValidator.validate('man'); 16 | 17 | expect(result.success).toBeFalsy(); 18 | expect(result.errors).toContain('InvalidLengthError') 19 | }); 20 | 21 | it ("knows that 'hello' is valid in bounds", () => { 22 | result = passwordValidator.validate('hello'); 23 | 24 | expect(result.success).toBeTruthy(); 25 | }); 26 | 27 | it ("knows that 'hellohello123' is valid in bounds", () => { 28 | result = passwordValidator.validate('hellohello123'); 29 | 30 | expect(result.success).toBeTruthy(); 31 | }) 32 | 33 | it ("knows that 'hellohellohello12' is invalid out of bounds", () => { 34 | result = passwordValidator.validate('hellohellohello12'); 35 | 36 | expect(result.success).toBeFalsy(); 37 | expect(result.errors).toContain('InvalidLengthError') 38 | }) 39 | }) 40 | 41 | // .. continue (contains one digit) 42 | // .. continue (one upper case letter) 43 | }) -------------------------------------------------------------------------------- /examples/classic/tdd-password-validator/src/index.ts: -------------------------------------------------------------------------------- 1 | 2 | type InvalidPasswordError = 'InvalidLengthError' 3 | 4 | type Result = { 5 | success: boolean; 6 | errors?: InvalidPasswordError[]; 7 | } 8 | 9 | export class PasswordValidator { 10 | validate (password: string) : Result { 11 | if (password.length < 5 || password.length > 15) { 12 | return { 13 | success: false, 14 | errors: ['InvalidLengthError'] 15 | } 16 | } 17 | 18 | return { 19 | success: true 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /examples/classic/tdd-recently-used-list/README.md: -------------------------------------------------------------------------------- 1 | # Recently Used List 2 | 3 | Develop a recently-used-list class to hold strings uniquely in Last-In-First-Out order. 4 | 5 | The most recently added item is first, the least recently added item is last. 6 | 7 | Items can be looked up by index, which counts from zero. 8 | 9 | Items in the list are unique, so duplicate insertions are moved rather than added. 10 | 11 | A recently-used-list is initially empty. 12 | 13 | > Optional extras: 14 | 15 | - Null insertions (empty strings) are not allowed. 16 | 17 | - A bounded capacity can be specified, so there is an upper limit to the number of items contained, with the least recently added items dropped on overflow. 18 | 19 | > More tests: 20 | 21 | - While getting items by index, supplied index-value should be within the bounds of List [eg. if maximum item counts of list is 5 then supplied index is less than 4 as index starts from 0 (zero)] 22 | - Negative index value not allowed [>0] 23 | - Size limit is must if not supplied make 5 as default [0-4] -------------------------------------------------------------------------------- /examples/classic/tdd-stats-calculator/.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | build 5 | # don't lint nyc coverage output 6 | coverage 7 | -------------------------------------------------------------------------------- /examples/classic/tdd-stats-calculator/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint", 6 | "prettier", 7 | "jest" 8 | ], 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:@typescript-eslint/recommended", 12 | "prettier" 13 | ], 14 | "rules": { 15 | "no-console": 1, 16 | "prettier/prettier": 2 17 | }, 18 | "env": { 19 | "browser": true, 20 | "node": true, 21 | "jest/globals": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/classic/tdd-stats-calculator/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and not Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # Stores VSCode versions used for testing VSCode extensions 107 | .vscode-test 108 | -------------------------------------------------------------------------------- /examples/classic/tdd-stats-calculator/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 80 6 | } -------------------------------------------------------------------------------- /examples/classic/tdd-stats-calculator/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Debug TypeScript in Node.js", 8 | "preLaunchTask": "npm: build", 9 | "program": "${workspaceFolder}/src/index.ts", 10 | "protocol": "inspector", 11 | "outFiles": [ 12 | "${workspaceFolder}/dist/**/*.js" 13 | ], 14 | "sourceMaps": true, 15 | "smartStep": true, 16 | "internalConsoleOptions": "openOnSessionStart" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /examples/classic/tdd-stats-calculator/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "typescript", 8 | "tsconfig": "simple-typescript-starter/tsconfig.json", 9 | "problemMatcher": [ 10 | "$tsc" 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /examples/classic/tdd-stats-calculator/README.md: -------------------------------------------------------------------------------- 1 | # Calculator Statistics 2 | 3 | > Your task is to process a sequence of integer numbers to determine the following statistics: 4 | 5 | Without using system Math library functions, process a sequence of integers to determine the following statistics: 6 | - minimum value 7 | - maximum value 8 | - number of elements in the sequence 9 | - average value 10 | 11 | For example: [2, 4, 21, -8, 53, 40] 12 | - minimum value = -8 13 | - maximum value = 53 14 | - number of elements in the sequence = 6 15 | - average value = 18.666666666667 16 | -------------------------------------------------------------------------------- /examples/classic/tdd-stats-calculator/build/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | console.log('Hello world!'); 3 | -------------------------------------------------------------------------------- /examples/classic/tdd-stats-calculator/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.ts?$': 'ts-jest' 4 | }, 5 | testEnvironment: 'node', 6 | testRegex: './src/.*\\.(test|spec)?\\.(ts|ts)$', 7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 8 | "roots": [ 9 | "/src" 10 | ] 11 | }; -------------------------------------------------------------------------------- /examples/classic/tdd-stats-calculator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-starter", 3 | "version": "1.0.0", 4 | "description": "A basic typescript app starter for newbies in 2019.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "rimraf ./build && tsc", 8 | "start:dev": "nodemon", 9 | "start": "npm run build && node build/index.js", 10 | "lint": "eslint . --ext .ts", 11 | "prettier-format": "run-script-os", 12 | "prettier-format:win32": "prettier --config .prettierrc \"./src/**/*.ts\" --write", 13 | "prettier-format:darwin:linux": "prettier --config .prettierrc 'src/**/*.ts' --write", 14 | "prettier-format:default": "prettier --config .prettierrc 'src/**/*.ts' --write", 15 | "prettier-watch": "run-script-os", 16 | "prettier-watch:win32": "onchange \"src/**/*.ts\" -- prettier --write {{changed}}", 17 | "prettier-watch:darwin:linux": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 18 | "prettier-watch:default": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 19 | "test": "jest", 20 | "test:dev": "jest --watchAll" 21 | }, 22 | "husky": { 23 | "hooks": { 24 | "pre-commit": "npm run test && npm run prettier-format && npm run lint" 25 | } 26 | }, 27 | "keywords": [], 28 | "author": "", 29 | "license": "ISC", 30 | "devDependencies": { 31 | "@types/jest": "^26.0.20", 32 | "@types/node": "^12.7.2", 33 | "@typescript-eslint/eslint-plugin": "^2.21.0", 34 | "@typescript-eslint/parser": "^2.21.0", 35 | "eslint": "^6.8.0", 36 | "eslint-config-prettier": "^6.10.0", 37 | "eslint-plugin-prettier": "^3.1.2", 38 | "husky": "^4.2.3", 39 | "nodemon": "^1.19.1", 40 | "onchange": "^6.1.0", 41 | "prettier": "^1.19.1", 42 | "rimraf": "^3.0.0", 43 | "run-script-os": "^1.1.1", 44 | "ts-node": "^8.3.0", 45 | "typescript": "^4.0.3" 46 | }, 47 | "dependencies": { 48 | "eslint-plugin-jest": "^24.1.0", 49 | "jest": "^26.5.3", 50 | "ts-jest": "^26.4.1" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/classic/tdd-stats-calculator/src/calculator.spec.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Calculator, StatsResult } from './calculator' 3 | 4 | describe('calculator', () => { 5 | 6 | test('should calculate minimum value from a range of numbers', () => { 7 | let result: StatsResult; 8 | let calculator = new Calculator(); 9 | 10 | result = calculator.processStats([2, 4, 21, -8, 53, 40]); 11 | 12 | expect(result.min).toEqual(-8) 13 | }); 14 | 15 | // .. Continue 16 | 17 | }) -------------------------------------------------------------------------------- /examples/classic/tdd-stats-calculator/src/calculator.ts: -------------------------------------------------------------------------------- 1 | 2 | export type StatsResult = { 3 | min: number; 4 | } 5 | 6 | 7 | export class Calculator { 8 | 9 | private getMinValue (numbers: number[]): number { 10 | let minValue; 11 | 12 | if (numbers.length == 0) { 13 | return -1; 14 | } 15 | 16 | numbers.forEach((number) => { 17 | if (!minValue) minValue = number; 18 | if (number < minValue) { 19 | minValue = number; 20 | } 21 | }) 22 | 23 | return minValue; 24 | } 25 | 26 | processStats (numbers: number[]): StatsResult { 27 | return { 28 | min: this.getMinValue(numbers) 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /examples/classic/tdd-tennis/README.md: -------------------------------------------------------------------------------- 1 | 2 | https://codingdojo.org/kata/Tennis/ 3 | 4 | 5 | This Kata is about implementing a simple tennis game. I came up with it while thinking about Wii tennis, where they have simplified tennis, so each set is one game. 6 | 7 | The scoring system is rather simple: 8 | 9 | Each player can have either of these points in one game “love” “15” “30” “40” 10 | If you have 40 and you win the point you win the game, however there are special rules. 11 | If both have 40 the players are “deuce”. 12 | If the game is in deuce, the winner of a point will have advantage 13 | If the player with advantage wins the ball he wins the game 14 | If the player without advantage wins they are back at deuce. 15 | Alternate description of the rules per Wikipedia (http://en.wikipedia.org/wiki/Tennis#Scoring ): 16 | 17 | A game is won by the first player to have won at least four points in total and at least two points more than the opponent. 18 | The running score of each game is described in a manner peculiar to tennis: scores from zero to three points are described as “love”, “15”, “30”, and “40” respectively. 19 | If at least three points have been scored by each player, and the scores are equal, the score is “deuce”. 20 | If at least three points have been scored by each side and a player has one more point than his opponent, the score of the game is “advantage” for the player in the lead. 21 | 22 | 23 | https://www.tenniscanada.com/play/tennis-101/points-and-rules/ 24 | 25 | -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | build 5 | # don't lint nyc coverage output 6 | coverage 7 | -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint", 6 | "prettier", 7 | "jest" 8 | ], 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:@typescript-eslint/recommended", 12 | "prettier" 13 | ], 14 | "rules": { 15 | "no-console": 1, 16 | "prettier/prettier": 2 17 | }, 18 | "env": { 19 | "browser": true, 20 | "node": true, 21 | "jest/globals": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and not Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # Stores VSCode versions used for testing VSCode extensions 107 | .vscode-test 108 | -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 80 6 | } -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Debug TypeScript in Node.js", 8 | "preLaunchTask": "npm: build", 9 | "program": "${workspaceFolder}/src/index.ts", 10 | "protocol": "inspector", 11 | "outFiles": [ 12 | "${workspaceFolder}/dist/**/*.js" 13 | ], 14 | "sourceMaps": true, 15 | "smartStep": true, 16 | "internalConsoleOptions": "openOnSessionStart" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "typescript", 8 | "tsconfig": "simple-typescript-starter/tsconfig.json", 9 | "problemMatcher": [ 10 | "$tsc" 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/README.md: -------------------------------------------------------------------------------- 1 | # Tic-tac toe 2 | 3 | > X always goes first. 4 | > Players alternate placing X’s and O’s on the board. 5 | > Players cannot play on a played position. 6 | > A player with three X’s or O’s in a row (horizontally, vertically, diagonally) wins. 7 | > If all nine squares are filled and neither player achieves three in a row, the game is a draw. -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/build/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | console.log('Hello world!'); 3 | -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.ts?$': 'ts-jest' 4 | }, 5 | testEnvironment: 'node', 6 | testRegex: './src/.*\\.(test|spec)?\\.(ts|ts)$', 7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 8 | "roots": [ 9 | "/src" 10 | ] 11 | }; -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-starter", 3 | "version": "1.0.0", 4 | "description": "A basic typescript app starter for newbies in 2019.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "rimraf ./build && tsc", 8 | "start:dev": "nodemon", 9 | "start": "npm run build && node build/index.js", 10 | "lint": "eslint . --ext .ts", 11 | "prettier-format": "run-script-os", 12 | "prettier-format:win32": "prettier --config .prettierrc \"./src/**/*.ts\" --write", 13 | "prettier-format:darwin:linux": "prettier --config .prettierrc 'src/**/*.ts' --write", 14 | "prettier-format:default": "prettier --config .prettierrc 'src/**/*.ts' --write", 15 | "prettier-watch": "run-script-os", 16 | "prettier-watch:win32": "onchange \"src/**/*.ts\" -- prettier --write {{changed}}", 17 | "prettier-watch:darwin:linux": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 18 | "prettier-watch:default": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 19 | "test": "jest", 20 | "test:dev": "jest --watchAll" 21 | }, 22 | "husky": { 23 | "hooks": { 24 | "pre-commit": "npm run test && npm run prettier-format && npm run lint" 25 | } 26 | }, 27 | "keywords": [], 28 | "author": "", 29 | "license": "ISC", 30 | "devDependencies": { 31 | "@types/jest": "^26.0.20", 32 | "@types/node": "^12.7.2", 33 | "@typescript-eslint/eslint-plugin": "^2.21.0", 34 | "@typescript-eslint/parser": "^2.21.0", 35 | "eslint": "^6.8.0", 36 | "eslint-config-prettier": "^6.10.0", 37 | "eslint-plugin-prettier": "^3.1.2", 38 | "husky": "^4.2.3", 39 | "nodemon": "^1.19.1", 40 | "onchange": "^6.1.0", 41 | "prettier": "^1.19.1", 42 | "rimraf": "^3.0.0", 43 | "run-script-os": "^1.1.1", 44 | "ts-node": "^8.3.0", 45 | "typescript": "^4.0.3" 46 | }, 47 | "dependencies": { 48 | "eslint-plugin-jest": "^24.1.0", 49 | "jest": "^26.5.3", 50 | "ts-jest": "^26.4.1" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/src/game.spec.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * - game 4 | * - board 5 | * - players 6 | * - turn 7 | * - marking (place a marking on the board) 8 | */ 9 | 10 | import { Game } from "./game"; 11 | 12 | describe('game', () => { 13 | 14 | let game; 15 | 16 | beforeEach(() => { 17 | game = new Game(); 18 | }) 19 | 20 | test('should know that it is Xs turn first', () => { 21 | let turn = game.getCurrentTurn(); 22 | expect(turn).toEqual('X') 23 | }) 24 | 25 | test("should know that it's O's turn to go after X", () => { 26 | game.chooseMark({ row: 0, column: 0 }) 27 | let turn = game.getCurrentTurn(); 28 | expect(turn).toBe('O') 29 | }); 30 | 31 | test("should continue to alternate between turns", () => { 32 | 33 | game.chooseMark({ row: 0, column: 0 }) 34 | 35 | game.chooseMark({ row: 0, column: 1 }) 36 | 37 | let turn = game.getCurrentTurn(); 38 | 39 | expect(turn).toEqual('X') 40 | 41 | game.chooseMark({ row: 0, column: 2 }) 42 | 43 | turn = game.getCurrentTurn(); 44 | 45 | expect(turn).toEqual('O') 46 | }); 47 | 48 | test('should not let players place on the same spot', () => { 49 | 50 | game.chooseMark({ row: 0, column: 0 }); 51 | 52 | let playOnSameSpot = () => game.chooseMark({ row: 0, column: 0 }); 53 | 54 | expect(() => playOnSameSpot()).toThrowError(); 55 | 56 | }); 57 | 58 | test('should not let players play outside of the 3x3 grid', () => { 59 | 60 | let movePlayedOutsideGrid = () => game.chooseMark({ row: -1, column: 1 }); 61 | 62 | expect(() => movePlayedOutsideGrid()).toThrowError(); 63 | 64 | }) 65 | 66 | test('should know that three Xs in a horizontal row wins', () => { 67 | // Row, col 68 | game.chooseMark(0,0); // X 69 | game.chooseMark(1,0); // O 70 | game.chooseMark(0,1); // X 71 | game.chooseMark(1,1); // O 72 | game.chooseMark(0,2); // X 73 | 74 | 75 | expect(game.getGameState()).toBe('X wins') 76 | expect(game.getWinner('X')) 77 | }) 78 | 79 | // then vertical 80 | // then diagonal 81 | 82 | 83 | }) -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/src/game.ts: -------------------------------------------------------------------------------- 1 | 2 | import { GameState, State } from "./gameState"; 3 | import { Grid } from "./grid"; 4 | import { Player } from "./player"; 5 | import { Position } from "./position"; 6 | 7 | export class Game { 8 | 9 | private currentTurn: Player; 10 | private grid: Grid; 11 | private gameState: GameState; 12 | 13 | constructor () { 14 | this.currentTurn = 'X'; 15 | this.gameState = new GameState(); 16 | this.grid = new Grid(); 17 | } 18 | 19 | getCurrentTurn (): Player { 20 | return this.currentTurn; 21 | } 22 | 23 | getInstructions (): string { 24 | return "Player x: Place a marking on the board" 25 | } 26 | 27 | updateCurrentTurn (): void { 28 | if (this.currentTurn === 'X') { 29 | this.currentTurn = 'O' 30 | } else { 31 | this.currentTurn = 'X' 32 | } 33 | } 34 | 35 | isPositionMarked (position: Position): boolean { 36 | return !!this.grid.getMarkAtPosition(position); 37 | } 38 | 39 | isPositionOutsideGrid (position: Position): boolean { 40 | if (position.row < 0 || position.row > 2 || position.column < 0 || position.column > 2) { 41 | return true; 42 | } else { 43 | return false; 44 | } 45 | } 46 | 47 | private horizontal (position: Position, rowValue?: string): string { 48 | if (this.isPositionOutsideGrid(position)) { 49 | return rowValue; 50 | } 51 | 52 | return this.grid.getMarkAtPosition(position) + this.horizontal({ 53 | row: position.row + 1, 54 | column: position.column 55 | }) 56 | } 57 | 58 | private detectWinner (): void { 59 | // Let's only detect horizontal right now 60 | const row = this.horizontal({ row: 0, column: 0 }); 61 | console.log(row); 62 | 63 | } 64 | 65 | getGameState (): State { 66 | return this.gameState.getGameState(); 67 | } 68 | 69 | chooseMark (position: Position): void { 70 | if(this.isPositionOutsideGrid(position)) { 71 | throw new Error(`Position: ${position.row},${position.column} is outside of the grid.`) 72 | } 73 | 74 | if (this.isPositionMarked(position)) { 75 | throw new Error(`Position: ${position.row},${position.column} has already been marked.`) 76 | } 77 | 78 | this.grid.setMarkAtPosition(position, this.getCurrentTurn()); 79 | 80 | this.detectWinner(); 81 | 82 | this.updateCurrentTurn(); 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/src/gameState.ts: -------------------------------------------------------------------------------- 1 | 2 | export type State = 'Ongoing' | 'X wins' | 'Y wins' | 'draw' 3 | 4 | export class GameState { 5 | 6 | private state: State; 7 | 8 | constructor () { 9 | this.state = 'Ongoing'; 10 | } 11 | 12 | getGameState (): State { 13 | return this.state; 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/src/grid.ts: -------------------------------------------------------------------------------- 1 | import { Player } from "./player"; 2 | import { Position } from "./position"; 3 | 4 | export class Grid { 5 | 6 | private grid: string[][]; 7 | 8 | constructor () { 9 | this.setup(); 10 | } 11 | 12 | getMarkAtPosition (position: Position): string | undefined { 13 | return this.grid[position.row][position.column]; 14 | } 15 | 16 | setMarkAtPosition (position: Position, mark: Player): void { 17 | this.grid[position.row][position.column] = mark; 18 | } 19 | 20 | setup () { 21 | this.grid = new Array(3); 22 | 23 | for (var i = 0; i < this.grid.length; i++) { 24 | this.grid[i] = new Array(3); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/src/player.ts: -------------------------------------------------------------------------------- 1 | 2 | export type Player = 'X' | 'O' -------------------------------------------------------------------------------- /examples/classic/tdd-tic-tac-toe/src/position.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface Position { 3 | row: number; 4 | column: number 5 | } -------------------------------------------------------------------------------- /examples/mockist/outside-in-tdd/.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | build 5 | # don't lint nyc coverage output 6 | coverage 7 | -------------------------------------------------------------------------------- /examples/mockist/outside-in-tdd/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint", 6 | "prettier", 7 | "jest" 8 | ], 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:@typescript-eslint/recommended", 12 | "prettier" 13 | ], 14 | "rules": { 15 | "no-console": 1, 16 | "prettier/prettier": 2 17 | }, 18 | "env": { 19 | "browser": true, 20 | "node": true, 21 | "jest/globals": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/mockist/outside-in-tdd/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and not Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # Stores VSCode versions used for testing VSCode extensions 107 | .vscode-test 108 | -------------------------------------------------------------------------------- /examples/mockist/outside-in-tdd/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 80 6 | } -------------------------------------------------------------------------------- /examples/mockist/outside-in-tdd/README.md: -------------------------------------------------------------------------------- 1 | # Elevator 2 | 3 | > Implement a controller for an elevator system considering the following requirements. For evaluating purpose, assume that it takes one second to move the elevator from one floor to another and the doors stay open for three seconds at every stop. 4 | 5 | - The building has 5 total floors including basement and ground. 6 | - The elevator can be called at any floor only when it is not in use via one call button. 7 | 8 | - Given the elevator is positioned on the ground floor 9 | - When there is a call from floor3 to go to basement 10 | - And there is a call from ground to go to basement 11 | - And there is a call from floor2 to go to basement 12 | - And there is a call from floor1 to go to floor 3 13 | - Then the doors should open at floor3, basement, ground, basement, floor2, basement, floor1 and floor3 in this order 14 | 15 | -------------------------------------------------------------------------------- /examples/mockist/outside-in-tdd/config.ts: -------------------------------------------------------------------------------- 1 | import 'jest-ts-auto-mock' -------------------------------------------------------------------------------- /examples/mockist/outside-in-tdd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-starter", 3 | "version": "1.0.0", 4 | "description": "A basic typescript app starter for newbies in 2019.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "rimraf ./build && tsc", 8 | "start:dev": "nodemon", 9 | "start": "npm run build && node build/index.js", 10 | "lint": "eslint . --ext .ts", 11 | "prettier-format": "run-script-os", 12 | "prettier-format:win32": "prettier --config .prettierrc \"./src/**/*.ts\" --write", 13 | "prettier-format:darwin:linux": "prettier --config .prettierrc 'src/**/*.ts' --write", 14 | "prettier-format:default": "prettier --config .prettierrc 'src/**/*.ts' --write", 15 | "prettier-watch": "run-script-os", 16 | "prettier-watch:win32": "onchange \"src/**/*.ts\" -- prettier --write {{changed}}", 17 | "prettier-watch:darwin:linux": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 18 | "prettier-watch:default": "onchange 'src/**/*.ts' -- prettier --write {{changed}}", 19 | "test": "jest", 20 | "test:dev": "jest --watchAll" 21 | }, 22 | "husky": { 23 | "hooks": { 24 | "pre-commit": "npm run test && npm run prettier-format" 25 | } 26 | }, 27 | "keywords": [], 28 | "author": "", 29 | "license": "ISC", 30 | "devDependencies": { 31 | "@types/jest": "^26.0.20", 32 | "@types/node": "^12.7.2", 33 | "@typescript-eslint/eslint-plugin": "^2.21.0", 34 | "@typescript-eslint/parser": "^2.21.0", 35 | "eslint": "^6.8.0", 36 | "eslint-config-prettier": "^6.10.0", 37 | "eslint-plugin-prettier": "^3.1.2", 38 | "husky": "^4.2.3", 39 | "jest": "^26.6.3", 40 | "jest-ts-auto-mock": "^2.0.0", 41 | "moq.ts": "^7.3.4", 42 | "nodemon": "^1.19.1", 43 | "onchange": "^6.1.0", 44 | "prettier": "^1.19.1", 45 | "rimraf": "^3.0.0", 46 | "run-script-os": "^1.1.1", 47 | "ts-auto-mock": "^3.3.0", 48 | "ts-jest": "^26.5.6", 49 | "ts-node": "^8.3.0", 50 | "ttypescript": "^1.5.12", 51 | "typescript": "^4.3.5" 52 | }, 53 | "dependencies": { 54 | "eslint-plugin-jest": "^24.1.0", 55 | "jest-cucumber": "^3.0.1" 56 | }, 57 | "jest": { 58 | "setupFiles": [ 59 | "config.ts" 60 | ], 61 | "transform": { 62 | ".(ts|tsx)": "ts-jest" 63 | }, 64 | "testRegex": "./src/.*\\.(test|spec)?\\.(ts|ts)$", 65 | "moduleFileExtensions": [ 66 | "ts", 67 | "tsx", 68 | "js", 69 | "json" 70 | ], 71 | "globals": { 72 | "ts-jest": { 73 | "compiler": "ttypescript" 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /examples/mockist/outside-in-tdd/src/makeOffer/makeOffer.spec.ts: -------------------------------------------------------------------------------- 1 | import { ITradesRepo, IVinylRepo, MakeOffer } from './makeOffer'; 2 | import { NotificationsSpy } from './notificationSpy'; 3 | import { createMock } from 'ts-auto-mock'; 4 | 5 | describe('makeOffer', () => { 6 | describe(`Given a vinyl exists and is available for trade`, () => { 7 | describe(`When a trader wants to place an offer using money`, () => { 8 | test(`Then the offer should get created and an email should be sent to the vinyl owner`, async () => { 9 | // Collaborator #1 - a vinyl repo. Technically, this is acting as a "fake" because 10 | // the definition of a fake is an object that replaces a dependency that doesn't 11 | // yet exist. We know that it will be used to fetch data (and test doubles that return 12 | // data from databases called stubs), but we're not going to bother with that right now in this 13 | // test. 14 | 15 | let fakeVinylRepo = createMock(); 16 | 17 | // Collaborator #2 - trades repo. 18 | 19 | let mockTradesRepo = createMock(); 20 | 21 | // Collaborator #3 - We need a notifications service. 22 | // Something for sending email. This collaborator has a 23 | // different responsibility from the previous two. With the previous two, they stand 24 | // in as objects that we use to issue QUERIES to get data that we'll use to do the work in the 25 | // use case. With this one, we'll be issuing a COMMAND - probably something like 26 | // `sendEmail`. Because we want to assert against an OUTGOING interaction, that makes 27 | // it a type of mock. Mocks help to emulate and examine outgoing interactions. 28 | // Even more specifically, because we'll write it by hand, that makes it a "spy" 29 | // (a type of mock). Complicated, I know. 30 | 31 | let notificationsSpy = new NotificationsSpy(); 32 | 33 | // Create the subject - MakeOffer 34 | 35 | let makeOffer = new MakeOffer( 36 | fakeVinylRepo, 37 | mockTradesRepo, 38 | notificationsSpy, 39 | ); 40 | 41 | // Act out the behavior 42 | 43 | let result = await makeOffer.execute({ 44 | vinylId: '123', 45 | tradeType: 'money', 46 | amountInCents: 100 * 35, 47 | }); 48 | 49 | // All of this should fail initially. 50 | // We can also assert that the result is truthy and that the email 51 | // which gets sent, gets sent to the correct user. 52 | 53 | expect(result.isSuccess()).toBeTruthy(); 54 | expect(mockTradesRepo.saveOffer).toHaveBeenCalled(); 55 | expect(notificationsSpy.getEmailsSent().length).toEqual(1); 56 | expect(notificationsSpy.emailWasSentFor()).toEqual(result.getValue().offerId); 57 | }); 58 | }); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /examples/mockist/outside-in-tdd/src/makeOffer/makeOffer.ts: -------------------------------------------------------------------------------- 1 | type Nothing = ''; 2 | type Trader = { name: string; id: string }; 3 | 4 | export type Email = { originatingId: string }; 5 | 6 | export interface IVinylRepo { 7 | getVinylOwner(vinylId: string): Promise; 8 | isVinylAvailableForTrade(vinylId: string): Promise; 9 | } 10 | 11 | export interface ITradesRepo { 12 | saveOffer(offer: any): Promise; 13 | } 14 | 15 | export interface INotificationService { 16 | sendEmail(email: Email): Promise; 17 | } 18 | 19 | export class MakeOffer { 20 | constructor( 21 | private vinylRepo: IVinylRepo, 22 | private tradesRepo: ITradesRepo, 23 | private notificationService: INotificationService, 24 | ) {} 25 | async execute(request: any) { 26 | const owner = await this.vinylRepo.getVinylOwner(''); 27 | const available = await this.vinylRepo.isVinylAvailableForTrade(''); 28 | 29 | this.tradesRepo.saveOffer({ 30 | vinylId: '123', 31 | tradeType: 'money', 32 | amountInCents: 100 * 35, 33 | }); 34 | 35 | this.notificationService.sendEmail({}); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/mockist/outside-in-tdd/src/makeOffer/notificationSpy.ts: -------------------------------------------------------------------------------- 1 | import { Email, INotificationService } from './makeOffer'; 2 | 3 | export class NotificationsSpy implements INotificationService { 4 | private emailsSent: Email[]; 5 | 6 | constructor() { 7 | this.emailsSent = []; 8 | } 9 | 10 | public async sendEmail(email: Email): Promise { 11 | this.emailsSent.push(email); 12 | } 13 | 14 | getEmailsSent() { 15 | return this.emailsSent; 16 | } 17 | 18 | emailWasSentFor () { 19 | if (this.getEmailsSent().length === 0) throw new Error('No emails sent'); 20 | if (this.getEmailsSent().length > 1) throw new Error("More than one email sent"); 21 | return this.getEmailsSent()[0].originatingId; 22 | } 23 | 24 | async hello() {} 25 | } 26 | -------------------------------------------------------------------------------- /examples/mockist/outside-in-tdd/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "plugins": [ 4 | { 5 | "transform": "ts-auto-mock/transformer", 6 | "cacheBetweenTests": false 7 | } 8 | ], 9 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 10 | 11 | /* Basic Options */ 12 | // "incremental": true, /* Enable incremental compilation */ 13 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ 14 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 15 | // "lib": [], /* Specify library files to be included in the compilation. */ 16 | // "allowJs": true, /* Allow javascript files to be compiled. */ 17 | // "checkJs": true, /* Report errors in .js files. */ 18 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ 19 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 20 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 21 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 22 | // "outFile": "./", /* Concatenate and emit output to single file. */ 23 | // "outDir": "./", /* Redirect output structure to the directory. */ 24 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 25 | // "composite": true, /* Enable project compilation */ 26 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 27 | // "removeComments": true, /* Do not emit comments to output. */ 28 | // "noEmit": true, /* Do not emit outputs. */ 29 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 30 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 31 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 32 | 33 | /* Strict Type-Checking Options */ 34 | "strict": true, /* Enable all strict type-checking options. */ 35 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 36 | // "strictNullChecks": true, /* Enable strict null checks. */ 37 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 38 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 39 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 40 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 41 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 42 | 43 | /* Additional Checks */ 44 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 45 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 46 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 47 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 48 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 49 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ 50 | // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ 51 | 52 | /* Module Resolution Options */ 53 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 54 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 55 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 56 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 57 | // "typeRoots": [], /* List of folders to include type definitions from. */ 58 | // "types": [], /* Type declaration files to be included in compilation. */ 59 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 60 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 61 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 62 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 63 | 64 | /* Source Map Options */ 65 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 66 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 67 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 68 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 69 | 70 | /* Experimental Options */ 71 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 72 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 73 | 74 | /* Advanced Options */ 75 | "skipLibCheck": true, /* Skip type checking of declaration files. */ 76 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 77 | } 78 | } 79 | --------------------------------------------------------------------------------