├── .gitignore ├── README.md ├── awkward-dependencies-examples └── course-duration │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ └── course.js │ ├── stryker.config.json │ └── test │ └── course.test.js ├── coffee-machine ├── .babelrc ├── .gitignore ├── README.md ├── package-lock.json ├── package.json └── src │ └── .keep ├── fizzbuzz ├── .babelrc ├── .gitignore ├── README.md ├── package-lock.json ├── package.json └── src │ ├── fizzbuzz.js │ └── fizzbuzz.test.js ├── password-validator ├── .babelrc ├── .gitignore ├── README.md ├── package-lock.json ├── package.json └── src │ ├── passwordValidator.js │ └── passwordValidator.test.js └── tire-pressure-variation ├── README.md ├── babel.config.js ├── jest.config.js ├── package-lock.json ├── package.json ├── src ├── .gitkeep └── .keep ├── stryker.conf.json ├── test └── alarm.test.js └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Katas 2 | 3 | ## Fizz Buzz 4 | Kata to start doing TDD. 5 | 6 | ## Password validator 7 | Kata to practice the importance of examples list and test order. 8 | 9 | ## Coffee Machine 10 | Kata to practice TDD with test doubles. 11 | 12 | ## Awkward dependencies examples. 13 | Exercises to detect violations of FIRS (side-effects and sources of non determinism). 14 | -------------------------------------------------------------------------------- /awkward-dependencies-examples/course-duration/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": { 7 | "node": "current" 8 | } 9 | } 10 | ] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /awkward-dependencies-examples/course-duration/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | coverage 4 | reports 5 | # stryker temp files 6 | .stryker-tmp 7 | -------------------------------------------------------------------------------- /awkward-dependencies-examples/course-duration/README.md: -------------------------------------------------------------------------------- 1 | # Course Duration 2 | 3 | To practice using test doubles -------------------------------------------------------------------------------- /awkward-dependencies-examples/course-duration/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "course-description", 3 | "version": "", 4 | "description": "", 5 | "dependencies": { 6 | "process": "^0.11.10" 7 | }, 8 | "devDependencies": { 9 | "@babel/core": "^7.20.5", 10 | "@babel/preset-env": "^7.20.2", 11 | "@stryker-mutator/core": "^7.3.0", 12 | "@stryker-mutator/jest-runner": "^7.3.0", 13 | "babel-jest": "^29.3.1", 14 | "jest": "^29.3.1" 15 | }, 16 | "scripts": { 17 | "test": "jest", 18 | "test:watch": "jest --watch", 19 | "test:coverage": "jest --coverage", 20 | "test:mutants": "stryker run" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /awkward-dependencies-examples/course-duration/src/course.js: -------------------------------------------------------------------------------- 1 | import {env} from "process"; 2 | 3 | export class Course { 4 | constructor(name) { 5 | this.name = name; 6 | this.durationInMinutes = 0; 7 | } 8 | 9 | start() { 10 | this.startTime = Date.now(); 11 | } 12 | 13 | end() { 14 | const endTime = Date.now(); 15 | this.durationInMinutes = (endTime - this.startTime) / (1000 * 60); 16 | } 17 | 18 | isShort() { 19 | const tenMinutes = 10 * 60; 20 | return this.durationInMinutes < tenMinutes; 21 | } 22 | 23 | isLong() { 24 | return !this.isShort(); 25 | } 26 | 27 | getTitle() { 28 | return `${this.name} course in ${this.#getCollege()} college`; 29 | } 30 | 31 | #getCollege() { 32 | return env.college ?? "not found"; 33 | } 34 | } 35 | 36 | 37 | -------------------------------------------------------------------------------- /awkward-dependencies-examples/course-duration/stryker.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json", 3 | "_comment": "This config was generated using 'stryker init'. Please take a look at: https://stryker-mutator.io/docs/stryker-js/configuration/ for more information.", 4 | "packageManager": "npm", 5 | "reporters": [ 6 | "html", 7 | "clear-text", 8 | "progress" 9 | ], 10 | "testRunner": "jest", 11 | "testRunner_comment": "Take a look at (missing 'homepage' URL in package.json) for information about the jest plugin.", 12 | "coverageAnalysis": "off" 13 | } 14 | -------------------------------------------------------------------------------- /awkward-dependencies-examples/course-duration/test/course.test.js: -------------------------------------------------------------------------------- 1 | import {Course} from "../src/course.js"; 2 | 3 | describe('Course', () => { 4 | let course; 5 | 6 | beforeEach(() => { 7 | course = new Course("macramé"); 8 | }); 9 | 10 | it('identifies short courses', () => { 11 | course.start(); 12 | course.end(); 13 | 14 | expect(course.isShort()).toBeTruthy(); 15 | }); 16 | 17 | it.todo('identifies long courses',); 18 | 19 | it.todo('knows the course title'); 20 | }); -------------------------------------------------------------------------------- /coffee-machine/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current', 8 | }, 9 | }, 10 | ], 11 | ], 12 | } -------------------------------------------------------------------------------- /coffee-machine/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist -------------------------------------------------------------------------------- /coffee-machine/README.md: -------------------------------------------------------------------------------- 1 | # Project 2 | In this Coffee Machine Project, your task is to implement the logic (starting from a simple class) that translates orders from customers of the coffee machine to the drink maker. Your code will use the drink maker protocol to send commands to the drink maker. 3 | 4 | ## Example of using test doubles 5 | [Use of test doubles with jest](https://gist.github.com/trikitrok/0a65ba016d59d8c9d405ec66f522a0b6) 6 | 7 | ## Constraint 8 | ### Public interface of CoffeeMachine 9 | 10 | ```javascript 11 | selectCoffee() 12 | selectTea() 13 | selectChocolate() 14 | addOneSpoonOfSugar() 15 | makeDrink() 16 | addMoney(amount) 17 | ``` 18 | 19 | # First iteration - Making drinks 20 | In this iteration, your task is to implement the logic (starting from a simple class) that translates orders from customers of the coffee machine to the drink maker. Your code will use the drink maker protocol (see below) to send commands to the drink maker. 21 | 22 | The coffee machine can serves 3 type of drinks: tea, coffee, chocolate. 23 | 24 | ## Use cases 25 | Your product owner has delivered the stories and here they are: 26 | - The drink maker should receive the correct instructions for my coffee / tea / chocolate order. 27 | - I want to be able to send instructions to the drink maker to add one or two sugars. 28 | - When my order contains sugar the drink maker should add a stick (touillette) with it. 29 | 30 | ## Drink maker protocol 31 | The drink maker receives string commands from your code to make the drinks. It can also deliver info messages to the customer if ordered so. The instructions it receives follow this format: 32 | 33 | "T:1:0" (Drink maker makes 1 tea with 1 sugar and a stick) 34 | "H::" (Drink maker makes 1 chocolate with no sugar and therefore no stick) 35 | "C:2:0" (Drink maker makes 1 coffee with 2 sugars and a stick) 36 | "M:message-content" (Drink maker forwards any message received onto the coffee machine interface for the customer to see) 37 | 38 | -------------------------------------------------------------------------------- /coffee-machine/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coffee-machine", 3 | "version": "0.1.5", 4 | "description": "", 5 | "keywords": [], 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "@babel/core": "^7.20.5", 9 | "@babel/preset-env": "^7.20.2", 10 | "babel-jest": "^29.3.1", 11 | "jest": "^29.3.1" 12 | }, 13 | "scripts": { 14 | "test": "jest", 15 | "test:watch": "jest --watch" 16 | }, 17 | "jest": { 18 | "watchPathIgnorePatterns": [ 19 | "node_modules" 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /coffee-machine/src/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codesai/curso-tdd-js/ae9034571e2c687c409fad13099e1669774af5a2/coffee-machine/src/.keep -------------------------------------------------------------------------------- /fizzbuzz/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current', 8 | }, 9 | }, 10 | ], 11 | ], 12 | } -------------------------------------------------------------------------------- /fizzbuzz/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist -------------------------------------------------------------------------------- /fizzbuzz/README.md: -------------------------------------------------------------------------------- 1 | # Goal 2 | 3 | Write a program that returns the numbers from 1 to 100. But for multiples of three return "Fizz" instead of the number and for the multiples of five return "Buzz". For numbers which are multiples of both three and five return "FizzBuzz". 4 | 5 | # Sample output 6 | 7 | 1 8 | 2 9 | Fizz 10 | 4 11 | Buzz 12 | Fizz 13 | 7 14 | 8 15 | Fizz 16 | Buzz 17 | 11 18 | Fizz 19 | 13 20 | 14 21 | FizzBuzz 22 | 16 23 | 17 24 | Fizz 25 | 19 26 | Buzz 27 | ... etc up to 100 -------------------------------------------------------------------------------- /fizzbuzz/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fizbuzz", 3 | "version": "0.1.5", 4 | "description": "", 5 | "keywords": [], 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "@babel/core": "^7.20.5", 9 | "@babel/preset-env": "^7.20.2", 10 | "babel-jest": "^29.3.1", 11 | "jest": "^29.3.1" 12 | }, 13 | "scripts": { 14 | "test": "jest", 15 | "test:watch": "jest --watch" 16 | }, 17 | "jest": { 18 | "watchPathIgnorePatterns": [ 19 | "node_modules" 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /fizzbuzz/src/fizzbuzz.js: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return false; 3 | } 4 | -------------------------------------------------------------------------------- /fizzbuzz/src/fizzbuzz.test.js: -------------------------------------------------------------------------------- 1 | import fizzbuzz from "./fizzbuzz"; 2 | 3 | describe('Fizzbuzz', () => { 4 | test("canary test", () => { 5 | expect(fizzbuzz()).toEqual(true); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /password-validator/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current', 8 | }, 9 | }, 10 | ], 11 | ], 12 | } -------------------------------------------------------------------------------- /password-validator/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist -------------------------------------------------------------------------------- /password-validator/README.md: -------------------------------------------------------------------------------- 1 | # Goal 2 | We want to ensure that our users' passwords have the following rules: 3 | 4 | - Have more than 8 characters 5 | - Contains at least a capital letter 6 | - Contains at least a lowercase 7 | - Contains at least number 8 | - Contains at least an underscore 9 | 10 | # Key 11 | This kata shows the importance of: 12 | 13 | * Selecting good examples 14 | 15 | * Selecting the test order 16 | 17 | * Having good assertions (testing only one thing and being always true) 18 | -------------------------------------------------------------------------------- /password-validator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "password-validator", 3 | "version": "0.1.5", 4 | "description": "", 5 | "keywords": [], 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "@babel/core": "^7.20.5", 9 | "@babel/preset-env": "^7.20.2", 10 | "babel-jest": "^29.3.1", 11 | "jest": "^29.3.1" 12 | }, 13 | "scripts": { 14 | "test": "jest", 15 | "test:watch": "jest --watch" 16 | }, 17 | "jest": { 18 | "watchPathIgnorePatterns": [ 19 | "node_modules" 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /password-validator/src/passwordValidator.js: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return true; 3 | } 4 | -------------------------------------------------------------------------------- /password-validator/src/passwordValidator.test.js: -------------------------------------------------------------------------------- 1 | import passwordValidator from "./passwordValidator"; 2 | 3 | describe('Password validator', () => { 4 | test("canary test", () => { 5 | expect(passwordValidator('foo')).toEqual(false); 6 | }); 7 | }); -------------------------------------------------------------------------------- /tire-pressure-variation/README.md: -------------------------------------------------------------------------------- 1 | # Tire Pressure Monitoring System Variation 2 | 3 | [Requirements](https://gist.github.com/trikitrok/e0dccffff284511e736a53a59d853e31) 4 | 5 | ### Constraints. 6 | 7 | Aside from its constructor this is the only public method of the `Alarm` class: 8 | 9 | `check()` 10 | 11 | `Alarm` cannot have any accessor methods to see its state, and all its fields must be private. 12 | 13 | ### Tools 14 | 15 | [Use of test doubles with jest](https://gist.github.com/trikitrok/0a65ba016d59d8c9d405ec66f522a0b6) 16 | -------------------------------------------------------------------------------- /tire-pressure-variation/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['@babel/preset-env', 4 | {targets: {node: 'current'}}], 5 | '@babel/preset-typescript'], 6 | }; -------------------------------------------------------------------------------- /tire-pressure-variation/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ 2 | module.exports = { 3 | preset: 'ts-jest', 4 | testEnvironment: 'node', 5 | coverageDirectory: "reports/coverage", 6 | coverageReporters: ["lcov", "text"], 7 | }; -------------------------------------------------------------------------------- /tire-pressure-variation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-tire-pressure-variation", 3 | "version": "0.0.1", 4 | "description": "Tire Pressure Variation", 5 | "main": "build/index.js", 6 | "keywords": [], 7 | "devDependencies": { 8 | "@babel/core": "^7.18.10", 9 | "@babel/preset-env": "^7.18.10", 10 | "@babel/preset-typescript": "^7.18.6", 11 | "@stryker-mutator/typescript-checker": "^6.2.2", 12 | "@types/jest": "^28.1.6", 13 | "babel-jest": "^28.1.3", 14 | "jest": "^28.1.3", 15 | "ts-jest": "^28.0.8", 16 | "ts-mockito": "^2.6.1", 17 | "typescript": "^4.7.4" 18 | }, 19 | "scripts": { 20 | "compile": "tsc --project tsconfig.json", 21 | "test": "jest", 22 | "test:watch": "jest --watch", 23 | "test:coverage": "jest --coverage", 24 | "test:mutants": "stryker run" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tire-pressure-variation/src/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codesai/curso-tdd-js/ae9034571e2c687c409fad13099e1669774af5a2/tire-pressure-variation/src/.gitkeep -------------------------------------------------------------------------------- /tire-pressure-variation/src/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codesai/curso-tdd-js/ae9034571e2c687c409fad13099e1669774af5a2/tire-pressure-variation/src/.keep -------------------------------------------------------------------------------- /tire-pressure-variation/stryker.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "checkers": ["typescript"], 3 | "tsconfigFile": "tsconfig.json", 4 | "timeoutMS": 5000, 5 | "timeoutFactor": 1.5 6 | } -------------------------------------------------------------------------------- /tire-pressure-variation/test/alarm.test.js: -------------------------------------------------------------------------------- 1 | describe('Alarm', () => { 2 | it('fix me', () => { 3 | expect(true).toBe(false); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /tire-pressure-variation/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "build", 4 | "module": "umd", 5 | "target": "es6", 6 | "sourceMap": true, 7 | "noImplicitAny": true, 8 | "strictNullChecks": true, 9 | "removeComments": false, 10 | "moduleResolution": "node", 11 | "esModuleInterop": true, 12 | "declaration": true 13 | }, 14 | "exclude": [ 15 | "node_modules", 16 | "build" 17 | ] 18 | } 19 | --------------------------------------------------------------------------------