├── .gitignore ├── 0x00-ES6_basic ├── .eslintrc.js ├── 0-constants.js ├── 1-block-scoped.js ├── 10-loops.js ├── 100-createIteratorObject.js ├── 101-iterateThroughObject.js ├── 11-createEmployeesObject.js ├── 12-createReportObject.js ├── 2-arrow.js ├── 3-default-parameter.js ├── 4-rest-parameter.js ├── 5-spread-operator.js ├── 6-string-interpolation.js ├── 7-getBudgetObject.js ├── 8-getBudgetCurrentYear.js ├── 9-getFullBudget.js ├── README.md ├── babel.config.js ├── package-lock.json └── package.json ├── 0x01-ES6_promise ├── .eslintrc.js ├── 0-promise.js ├── 1-promise.js ├── 100-await.js ├── 2-then.js ├── 3-all.js ├── 4-user-promise.js ├── 5-photo-reject.js ├── 6-final-user.js ├── 7-load_balancer.js ├── 8-try.js ├── 9-try.js ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json └── utils.js ├── 0x02-ES6_classes ├── .eslintrc.js ├── 0-classroom.js ├── 1-make_classrooms.js ├── 10-car.js ├── 100-evcar.js ├── 2-hbtn_course.js ├── 3-currency.js ├── 4-pricing.js ├── 5-building.js ├── 6-sky_high.js ├── 7-airport.js ├── 8-hbtn_class.js ├── 9-hoisting.js ├── README.md ├── babel.config.js ├── package-lock.json └── package.json ├── 0x03-ES6_data_manipulation ├── .eslintrc.js ├── 0-get_list_students.js ├── 1-get_list_student_ids.js ├── 10-update_uniq_items.js ├── 100-weak.js ├── 2-get_students_by_loc.js ├── 3-get_ids_sum.js ├── 4-update_grade_by_city.js ├── 5-typed_arrays.js ├── 6-set.js ├── 7-has_array_values.js ├── 8-clean_set.js ├── 9-groceries_list.js ├── README.md ├── babel.config.js ├── package-lock.json └── package.json ├── 0x04-TypeScript ├── README.md ├── task_0 │ ├── .eslintrc.js │ ├── js │ │ └── main.ts │ ├── package.json │ ├── tsconfig.json │ ├── webpack.config.js │ └── yarn.lock ├── task_1 │ ├── .eslintrc.js │ ├── js │ │ └── main.ts │ ├── package.json │ ├── tsconfig.json │ ├── webpack.config.js │ └── yarn.lock ├── task_2 │ ├── .eslintrc.js │ ├── js │ │ └── main.ts │ ├── package.json │ ├── tsconfig.json │ ├── webpack.config.js │ └── yarn.lock ├── task_3 │ ├── .eslintrc.js │ ├── js │ │ ├── crud.d.ts │ │ ├── crud.js │ │ ├── interface.ts │ │ └── main.ts │ ├── package.json │ ├── tsconfig.json │ ├── webpack.config.js │ └── yarn.lock ├── task_4 │ ├── .eslintrc.js │ ├── js │ │ ├── main.ts │ │ └── subjects │ │ │ ├── Cpp.ts │ │ │ ├── Java.ts │ │ │ ├── React.ts │ │ │ ├── Subject.ts │ │ │ └── Teacher.ts │ ├── package.json │ ├── tsconfig.json │ ├── webpack.config.js │ └── yarn.lock └── task_5 │ ├── .eslintrc.js │ ├── js │ └── main.ts │ ├── package.json │ ├── tsconfig.json │ ├── webpack.config.js │ └── yarn.lock ├── 0x05-Node_JS_basic ├── .babelrc ├── .eslintrc.js ├── 0-console.js ├── 1-stdin.js ├── 2-read_file.js ├── 3-read_file_async.js ├── 4-http.js ├── 5-http.js ├── 6-http_express.js ├── 7-http_express.js ├── README.md ├── babel.config.js ├── database.csv ├── full_server │ ├── controllers │ │ ├── AppController.js │ │ └── StudentsController.js │ ├── routes │ │ └── index.js │ ├── server.js │ └── utils.js ├── package.json └── yarn.lock ├── 0x06-unittests_in_js ├── .babelrc ├── .eslintrc.js ├── 0-calcul.js ├── 0-calcul.test.js ├── 1-calcul.js ├── 1-calcul.test.js ├── 10-api │ ├── api.js │ ├── api.test.js │ └── package.json ├── 2-calcul_chai.js ├── 2-calcul_chai.test.js ├── 3-payment.js ├── 3-payment.test.js ├── 4-payment.js ├── 4-payment.test.js ├── 5-payment.js ├── 5-payment.test.js ├── 6-payment_token.js ├── 6-payment_token.test.js ├── 7-skip.test.js ├── 8-api │ ├── api.js │ ├── api.test.js │ └── package.json ├── 9-api │ ├── api.js │ ├── api.test.js │ └── package.json ├── README.md ├── babel.config.js ├── package.json ├── utils.js └── yarn.lock ├── LICENSE └── README.md /.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 | -------------------------------------------------------------------------------- /0x00-ES6_basic/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: false, 4 | es6: true, 5 | jest: true, 6 | }, 7 | extends: ["airbnb-base", "plugin:jest/all"], 8 | globals: { 9 | Atomics: "readonly", 10 | SharedArrayBuffer: "readonly", 11 | }, 12 | parserOptions: { 13 | ecmaVersion: 2018, 14 | sourceType: "module", 15 | }, 16 | plugins: ["jest"], 17 | rules: { 18 | "no-console": "off", 19 | "no-shadow": "off", 20 | "no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"], 21 | }, 22 | overrides: [ 23 | { 24 | files: ["*.js"], 25 | excludedFiles: "babel.config.js", 26 | }, 27 | ], 28 | }; 29 | -------------------------------------------------------------------------------- /0x00-ES6_basic/0-constants.js: -------------------------------------------------------------------------------- 1 | export function taskFirst() { 2 | const task = 'I prefer const when I can.'; 3 | return task; 4 | } 5 | 6 | export function getLast() { 7 | return ' is okay'; 8 | } 9 | 10 | export function taskNext() { 11 | let combination = 'But sometimes let'; 12 | combination += getLast(); 13 | 14 | return combination; 15 | } 16 | -------------------------------------------------------------------------------- /0x00-ES6_basic/1-block-scoped.js: -------------------------------------------------------------------------------- 1 | export default function taskBlock(trueOrFalse) { 2 | const task = false; 3 | const task2 = true; 4 | 5 | if (trueOrFalse) { 6 | const task = true; // eslint-disable-line no-unused-vars 7 | const task2 = false; // eslint-disable-line no-unused-vars 8 | } 9 | 10 | return [task, task2]; 11 | } 12 | -------------------------------------------------------------------------------- /0x00-ES6_basic/10-loops.js: -------------------------------------------------------------------------------- 1 | export default function appendToEachArrayValue(array, appendString) { 2 | const newArray = []; 3 | for (const value of array) { 4 | newArray.push(appendString + value); 5 | } 6 | 7 | return newArray; 8 | } 9 | -------------------------------------------------------------------------------- /0x00-ES6_basic/100-createIteratorObject.js: -------------------------------------------------------------------------------- 1 | export default function createIteratorObject(report) { 2 | return (function* _() { 3 | for (const department of Object.values(report.allEmployees)) { 4 | for (const employee of department) { 5 | yield employee; 6 | } 7 | } 8 | }()); 9 | } 10 | -------------------------------------------------------------------------------- /0x00-ES6_basic/101-iterateThroughObject.js: -------------------------------------------------------------------------------- 1 | export default function iterateThroughObject(reportWithIterator) { 2 | const employees = []; 3 | 4 | for (const employee of reportWithIterator) { 5 | employees.push(employee); 6 | } 7 | 8 | return employees.join(' | '); 9 | } 10 | -------------------------------------------------------------------------------- /0x00-ES6_basic/11-createEmployeesObject.js: -------------------------------------------------------------------------------- 1 | export default function createEmployeesObject(departmentName, employees) { 2 | return { 3 | [departmentName]: [ 4 | ...employees, 5 | ], 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /0x00-ES6_basic/12-createReportObject.js: -------------------------------------------------------------------------------- 1 | export default function createReportObject(employeesList) { 2 | return { 3 | allEmployees: employeesList, 4 | getNumberOfDepartments(employeesList) { 5 | return Object.keys(employeesList).length; 6 | }, 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /0x00-ES6_basic/2-arrow.js: -------------------------------------------------------------------------------- 1 | export default function getNeighborhoodsList() { 2 | this.sanFranciscoNeighborhoods = ['SOMA', 'Union Square']; 3 | 4 | const self = this; 5 | this.addNeighborhood = (newNeighborhood) => { 6 | self.sanFranciscoNeighborhoods.push(newNeighborhood); 7 | return self.sanFranciscoNeighborhoods; 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /0x00-ES6_basic/3-default-parameter.js: -------------------------------------------------------------------------------- 1 | export default function getSumOfHoods(initialNumber, expansion1989 = 89, expansion2019 = 19) { 2 | return initialNumber + expansion1989 + expansion2019; 3 | } 4 | -------------------------------------------------------------------------------- /0x00-ES6_basic/4-rest-parameter.js: -------------------------------------------------------------------------------- 1 | export default function returnHowManyArguments(...args) { 2 | return args.length; 3 | } 4 | -------------------------------------------------------------------------------- /0x00-ES6_basic/5-spread-operator.js: -------------------------------------------------------------------------------- 1 | export default function concatArrays(array1, array2, string) { 2 | return [...array1, ...array2, ...string]; 3 | } 4 | -------------------------------------------------------------------------------- /0x00-ES6_basic/6-string-interpolation.js: -------------------------------------------------------------------------------- 1 | export default function getSanFranciscoDescription() { 2 | const year = 2017; 3 | const budget = { 4 | income: '$119,868', 5 | gdp: '$154.2 billion', 6 | capita: '$178,479', 7 | }; 8 | 9 | return `As of ${year}, it was the seventh-highest income county in the United States\ 10 | , with a per capita personal income of ${budget.income}. As of 2015, San Francisco \ 11 | proper had a GDP of ${budget.gdp}, and a GDP per capita of ${budget.capita}.`; 12 | } 13 | -------------------------------------------------------------------------------- /0x00-ES6_basic/7-getBudgetObject.js: -------------------------------------------------------------------------------- 1 | export default function getBudgetObject(income, gdp, capita) { 2 | const budget = { 3 | income, 4 | gdp, 5 | capita, 6 | }; 7 | 8 | return budget; 9 | } 10 | -------------------------------------------------------------------------------- /0x00-ES6_basic/8-getBudgetCurrentYear.js: -------------------------------------------------------------------------------- 1 | function getCurrentYear() { 2 | const date = new Date(); 3 | return date.getFullYear(); 4 | } 5 | 6 | export default function getBudgetForCurrentYear(income, gdp, capita) { 7 | const budget = { 8 | [`income-${getCurrentYear()}`]: income, 9 | [`gdp-${getCurrentYear()}`]: gdp, 10 | [`capita-${getCurrentYear()}`]: capita, 11 | }; 12 | 13 | return budget; 14 | } 15 | -------------------------------------------------------------------------------- /0x00-ES6_basic/9-getFullBudget.js: -------------------------------------------------------------------------------- 1 | import getBudgetObject from './7-getBudgetObject'; 2 | 3 | export default function getFullBudgetObject(income, gdp, capita) { 4 | const budget = getBudgetObject(income, gdp, capita); 5 | const fullBudget = { 6 | ...budget, 7 | getIncomeInDollars(income) { 8 | return `$${income}`; 9 | }, 10 | getIncomeInEuros(income) { 11 | return `${income} euros`; 12 | }, 13 | }; 14 | 15 | return fullBudget; 16 | } 17 | -------------------------------------------------------------------------------- /0x00-ES6_basic/README.md: -------------------------------------------------------------------------------- 1 | # ES6 Basics 2 | 3 | This project contains tasks for learning the basics of ECMAScript 2015 (ES6). 4 | 5 | ## Tasks To Complete 6 | 7 | + [x] 0. **Const or let?**
[0-constants.js](0-constants.js) contains a script that meets the following requirements. 8 | + For the code below, make the following modifications: 9 | + function `taskFirst` to instantiate variables using `const`. 10 | + function `taskNext` to instantiate variables using `let`. 11 | ```js 12 | export function taskFirst() { 13 | var task = 'I prefer const when I can.'; 14 | return task; 15 | } 16 | 17 | export function getLast() { 18 | return ' is okay'; 19 | } 20 | 21 | export function taskNext() { 22 | var combination = 'But sometimes let'; 23 | combination += getLast(); 24 | 25 | return combination; 26 | } 27 | ``` 28 | 29 | + [x] 1. **Block Scope**
[1-block-scoped.js](1-block-scoped.js) contains a script that meets the following requirements. 30 | + For the code below, modify the variables inside the function `taskBlock` so that the variables aren't overwritten inside the conditional block. 31 | ```js 32 | export default function taskBlock(trueOrFalse) { 33 | var task = false; 34 | var task2 = true; 35 | 36 | if (trueOrFalse) { 37 | var task = true; 38 | var task2 = false; 39 | } 40 | 41 | return [task, task2]; 42 | } 43 | ``` 44 | 45 | + [x] 2. **Arrow functions**
[2-arrow.js](2-arrow.js) contains a script that meets the following requirements. 46 | + For the code below, rewrite the following standard function to use ES6's arrow syntax of the function `add`. 47 | ```js 48 | export default function getNeighborhoodsList() { 49 | this.sanFranciscoNeighborhoods = ['SOMA', 'Union Square']; 50 | 51 | const self = this; 52 | this.addNeighborhood = function add(newNeighborhood) { 53 | self.sanFranciscoNeighborhoods.push(newNeighborhood); 54 | return self.sanFranciscoNeighborhoods; 55 | }; 56 | } 57 | ``` 58 | 59 | + [x] 3. **Parameter defaults**
[3-default-parameter.js](3-default-parameter.js) contains a script that meets the following requirements. 60 | + For the code below, condense the internals of the following function to 1 line - without changing the name of each function/variable. 61 | ```js 62 | export default function getSumOfHoods(initialNumber, expansion1989, expansion2019) { 63 | if (expansion1989 === undefined) { 64 | expansion1989 = 89; 65 | } 66 | 67 | if (expansion2019 === undefined) { 68 | expansion2019 = 19; 69 | } 70 | return initialNumber + expansion1989 + expansion2019; 71 | } 72 | ``` 73 | 74 | + [x] 4. **Rest parameter syntax for functions**
[4-rest-parameter.js](4-rest-parameter.js) contains a script that meets the following requirements. 75 | + For the code below, modify the following function to return the number of arguments passed to it using the rest parameter syntax. 76 | ```js 77 | export default function returnHowManyArguments() { 78 | 79 | } 80 | ``` 81 | 82 | + [x] 5. **The wonders of spread syntax**
[5-spread-operator.js](5-spread-operator.js) contains a script that meets the following requirements. 83 | + For the code below, using spread syntax, concatenate 2 arrays and each character of a string by modifying the function below. The function body should be one line long. 84 | ```js 85 | export default function concatArrays(array1, array2, string) { 86 | } 87 | ``` 88 | 89 | + [x] 6. **Take advantage of template literals**
[6-string-interpolation.js](6-string-interpolation.js) contains a script that meets the following requirements. 90 | + For the code below, rewrite the return statement to use a template literal so you can the substitute the variables you’ve defined. 91 | ```js 92 | export default function getSanFranciscoDescription() { 93 | const year = 2017; 94 | const budget = { 95 | income: '$119,868', 96 | gdp: '$154.2 billion', 97 | capita: '$178,479', 98 | }; 99 | 100 | return 'As of ' + year + ', it was the seventh-highest income county in the United States' \ 101 | ', with a per capita personal income of ' + budget.income + '. As of 2015, San Francisco' \ 102 | ' proper had a GDP of ' + budget.gdp + ', and a GDP per capita of ' + budget.capita + '.'; 103 | } 104 | ``` 105 | 106 | + [x] 7. **Object property value shorthand syntax**
[7-getBudgetObject.js](7-getBudgetObject.js) contains a script that meets the following requirements. 107 | + For the code below, modify the following function’s `budget` object to simply use the keyname instead. 108 | ```js 109 | export default function getBudgetObject(income, gdp, capita) { 110 | const budget = { 111 | income: income, 112 | gdp: gdp, 113 | capita: capita, 114 | }; 115 | 116 | return budget; 117 | } 118 | ``` 119 | 120 | + [x] 8. **No need to create empty objects before adding in properties**
[8-getBudgetCurrentYear.js](8-getBudgetCurrentYear.js) contains a script that meets the following requirements. 121 | + For the code below, rewrite the `getBudgetForCurrentYear` function to use ES6 computed property names on the `budget` object. 122 | ```js 123 | function getCurrentYear() { 124 | const date = new Date(); 125 | return date.getFullYear(); 126 | } 127 | 128 | export default function getBudgetForCurrentYear(income, gdp, capita) { 129 | const budget = {}; 130 | 131 | budget[`income-${getCurrentYear()}`] = income; 132 | budget[`gdp-${getCurrentYear()}`] = gdp; 133 | budget[`capita-${getCurrentYear()}`] = capita; 134 | 135 | return budget; 136 | } 137 | ``` 138 | 139 | + [x] 9. **ES6 method properties**
[9-getFullBudget.js](9-getFullBudget.js) contains a script that meets the following requirements. 140 | + For the code below, rewrite `getFullBudgetObject` to use ES6 method properties in the `fullBudget` object. 141 | ```js 142 | import getBudgetObject from './7-getBudgetObject.js'; 143 | 144 | export default function getFullBudgetObject(income, gdp, capita) { 145 | const budget = getBudgetObject(income, gdp, capita); 146 | const fullBudget = { 147 | ...budget, 148 | getIncomeInDollars: function (income) { 149 | return `$${income}`; 150 | }, 151 | getIncomeInEuros: function (income) { 152 | return `${income} euros`; 153 | }, 154 | }; 155 | 156 | return fullBudget; 157 | } 158 | ``` 159 | 160 | + [x] 10. **For...of Loops**
[10-loops.js](10-loops.js) contains a script that meets the following requirements. 161 | + For the code below, rewrite the function `appendToEachArrayValue` to use ES6’s `for...of` operator. And don’t forget that `var` is not ES6-friendly. 162 | ```js 163 | export default function appendToEachArrayValue(array, appendString) { 164 | for (var idx in array) { 165 | var value = array[idx]; 166 | array[idx] = appendString + value; 167 | } 168 | 169 | return array; 170 | } 171 | ``` 172 | 173 | + [x] 11. **Iterator**
[11-createEmployeesObject.js](11-createEmployeesObject.js) contains a script that meets the following requirements. 174 | + Write a function named `createEmployeesObject` that will receive two arguments: 175 | + `departmentName` (String). 176 | + `employees` (Array of Strings). 177 | ```js 178 | export default function createEmployeesObject(departmentName, employees) { 179 | 180 | } 181 | ``` 182 | The function should return an object with the following format: 183 | ```php 184 | { 185 | $departmentName: [ 186 | $employees, 187 | ], 188 | } 189 | ``` 190 | 191 | + [x] 12. **Let's create a report object**
[12-createReportObject.js](12-createReportObject.js) contains a script that meets the following requirements. 192 | + Write a function named `createReportObject` whose parameter, `employeesList`, is the return value of the previous function `createEmployeesObject`. 193 | ```js 194 | export default function createReportObject(employeesList) { 195 | 196 | } 197 | ``` 198 | + `createReportObject` should return an object containing the key `allEmployees` and a method property called `getNumberOfDepartments`. 199 | + `allEmployees` is a key that maps to an object containing the department name and a list of all the employees in that department. If you’re having trouble, use the spread syntax. 200 | + The method property receives employeesList and returns the number of departments. 201 | 202 | + [x] 13. **Iterating through report objects**
[100-createIteratorObject.js](100-createIteratorObject.js) contains a script that meets the following requirements. 203 | + Write a function named `createIteratorObject`, that will take into argument a report Object created with the previous function `createReportObject`. 204 | ```js 205 | export default function createIteratorObject(report) { 206 | 207 | } 208 | ``` 209 | + This function will return an iterator to go through every employee in every department. 210 | 211 | + [x] 14. **Iterate through object**
[101-iterateThroughObject.js](101-iterateThroughObject.js) contains a script that meets the following requirements. 212 | + Write a function named `iterateThroughObject`. The function’s parameter `reportWithIterator` is the return value from `createIteratorObject`. 213 | ```js 214 | export default function iterateThroughObject(reportWithIterator) { 215 | 216 | } 217 | ``` 218 | + It should return every employee name in a string, separated by ` | `. 219 | -------------------------------------------------------------------------------- /0x00-ES6_basic/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current', 8 | }, 9 | }, 10 | ], 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /0x00-ES6_basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "lint": "./node_modules/.bin/eslint", 4 | "check-lint": "lint [0-9]*.js", 5 | "dev": "npx babel-node", 6 | "test": "jest", 7 | "full-test": "./node_modules/.bin/eslint [0-9]*.js && jest" 8 | }, 9 | "devDependencies": { 10 | "@babel/core": "^7.6.0", 11 | "@babel/node": "^7.8.0", 12 | "@babel/preset-env": "^7.6.0", 13 | "eslint": "^6.4.0", 14 | "eslint-config-airbnb-base": "^14.0.0", 15 | "eslint-plugin-import": "^2.18.2", 16 | "eslint-plugin-jest": "^22.17.0", 17 | "jest": "^24.9.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /0x01-ES6_promise/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: false, 4 | es6: true, 5 | jest: true, 6 | }, 7 | extends: ["airbnb-base", "plugin:jest/all"], 8 | globals: { 9 | Atomics: "readonly", 10 | SharedArrayBuffer: "readonly", 11 | }, 12 | parserOptions: { 13 | ecmaVersion: 2018, 14 | sourceType: "module", 15 | }, 16 | plugins: ["jest"], 17 | rules: { 18 | "no-console": "off", 19 | "no-shadow": "off", 20 | "no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"], 21 | }, 22 | overrides: [ 23 | { 24 | files: ["*.js"], 25 | excludedFiles: "babel.config.js", 26 | }, 27 | ], 28 | }; 29 | -------------------------------------------------------------------------------- /0x01-ES6_promise/0-promise.js: -------------------------------------------------------------------------------- 1 | export default function getResponseFromAPI() { 2 | return new Promise(() => {}); 3 | } 4 | -------------------------------------------------------------------------------- /0x01-ES6_promise/1-promise.js: -------------------------------------------------------------------------------- 1 | export default function getFullResponseFromAPI(success) { 2 | return new Promise((resolve, reject) => { 3 | if (success) { 4 | resolve({ 5 | status: 200, 6 | body: 'Success', 7 | }); 8 | } else { 9 | reject(new Error('The fake API is not working currently')); 10 | } 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /0x01-ES6_promise/100-await.js: -------------------------------------------------------------------------------- 1 | import { uploadPhoto, createUser } from './utils'; 2 | 3 | export default async function asyncUploadUser() { 4 | let res = {}; 5 | 6 | try { 7 | const photo = await uploadPhoto(); 8 | const user = await createUser(); 9 | res = { photo, user }; 10 | } catch (err) { 11 | res = { photo: null, user: null }; 12 | } 13 | return res; 14 | } 15 | -------------------------------------------------------------------------------- /0x01-ES6_promise/2-then.js: -------------------------------------------------------------------------------- 1 | export default function handleResponseFromAPI(promise) { 2 | return promise 3 | .then(() => ({ status: 200, body: 'success' })) 4 | .catch(() => new Error()) 5 | .finally(() => console.log('Got a response from the API')); 6 | } 7 | -------------------------------------------------------------------------------- /0x01-ES6_promise/3-all.js: -------------------------------------------------------------------------------- 1 | import { uploadPhoto, createUser } from './utils'; 2 | 3 | export default function handleProfileSignup() { 4 | return Promise 5 | .all([uploadPhoto(), createUser()]) 6 | .then((res) => { 7 | console.log(`${res[0].body} ${res[1].firstName} ${res[1].lastName}`); 8 | }) 9 | .catch(() => console.log('Signup system offline')); 10 | } 11 | -------------------------------------------------------------------------------- /0x01-ES6_promise/4-user-promise.js: -------------------------------------------------------------------------------- 1 | export default function signUpUser(firstName, lastName) { 2 | return Promise.resolve({ firstName, lastName }); 3 | } 4 | -------------------------------------------------------------------------------- /0x01-ES6_promise/5-photo-reject.js: -------------------------------------------------------------------------------- 1 | export default function uploadPhoto(filename) { 2 | return Promise.reject(new Error(`${filename} cannot be processed`)); 3 | } 4 | -------------------------------------------------------------------------------- /0x01-ES6_promise/6-final-user.js: -------------------------------------------------------------------------------- 1 | import signUpUser from './4-user-promise'; 2 | import uploadPhoto from './5-photo-reject'; 3 | 4 | export default async function handleProfileSignup(firstName, lastName, fileName) { 5 | return Promise 6 | .allSettled([signUpUser(firstName, lastName), uploadPhoto(fileName)]) 7 | .then((res) => ( 8 | res.map((o) => ({ 9 | status: o.status, 10 | value: o.status === 'fulfilled' ? o.value : String(o.reason), 11 | })) 12 | )); 13 | } 14 | -------------------------------------------------------------------------------- /0x01-ES6_promise/7-load_balancer.js: -------------------------------------------------------------------------------- 1 | export default function loadBalancer(chinaDownload, USDownload) { 2 | return Promise 3 | .race([chinaDownload, USDownload]) 4 | .then((res) => res); 5 | } 6 | -------------------------------------------------------------------------------- /0x01-ES6_promise/8-try.js: -------------------------------------------------------------------------------- 1 | export default function divideFunction(numerator, denominator) { 2 | if (denominator === 0) { 3 | throw Error('cannot divide by 0'); 4 | } else { 5 | return numerator / denominator; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /0x01-ES6_promise/9-try.js: -------------------------------------------------------------------------------- 1 | export default function guardrail(mathFunction) { 2 | const queue = []; 3 | 4 | try { 5 | queue.push(mathFunction()); 6 | } catch (err) { 7 | queue.push(String(err)); 8 | } finally { 9 | queue.push('Guardrail was processed'); 10 | } 11 | 12 | return queue; 13 | } 14 | -------------------------------------------------------------------------------- /0x01-ES6_promise/README.md: -------------------------------------------------------------------------------- 1 | # ES6 Promises 2 | 3 | This project contains tasks for learning to use Promises in ECMAScript 2015 (ES6). 4 | 5 | ## Tasks To Complete 6 | 7 | + [x] 0. **Keep every promise you make and only make promises you can keep**
[0-promise.js](0-promise.js) contains a script that exports a function with the prototype `function getResponseFromAPI()`, which returns a Promise. 8 | 9 | + [x] 1. **Don't make a promise...if you know you can't keep it**
[1-promise.js](1-promise.js) contains a script that exports a function with the prototype `getFullResponseFromAPI(success)`, which returns a Promise. The parameter (`success`) is a `boolean`. 10 | + When the argument is: 11 | + `true` 12 | + Resolve the promise by passing an object with 2 attributes: 13 | + `status`: `200` 14 | + `body`: `'Success'` 15 | + `false` 16 | + Reject the promise with an error object with the message `The fake API is not working currently`. 17 | 18 | + [x] 2. **Catch me if you can!**
[2-then.js](2-then.js) contains a script that exports a function with the prototype `function handleResponseFromAPI(promise)`, which appends three handlers to the `promise` argument. 19 | + When the Promise resolves, return an object with the following attributes: 20 | + `status`: `200`, 21 | + `body`: `'success'` 22 | + When the Promise rejects, return an empty `Error` object. 23 | + For every resolution, log `Got a response from the API` to the console. 24 | 25 | + [x] 3. **Handle multiple successful promises**
[3-all.js](3-all.js) contains a script that meets the following requirements. 26 | + Import `uploadPhoto` and `createUser` from [utils.js](utils.js). 27 | + Use the prototype below to collectively resolve all promises and log `body firstName lastName` to the console. The functions in [utils.js](utils.js) return Promises. 28 | ```js 29 | function handleProfileSignup() 30 | ``` 31 | + In the event of an error, log `Signup system offline` to the console. 32 | 33 | + [x] 4. **Simple promise**
[4-user-promise.js](4-user-promise.js) contains a script that exports a function with the prototype `function signUpUser(firstName, lastName)`, which returns a resolved promise with the object shown below. 34 | ```js 35 | { 36 | firstName: value, 37 | lastName: value, 38 | } 39 | ``` 40 | 41 | + [x] 5. **Reject the promises**
[5-photo-reject.js](5-photo-reject.js) contains a script that exports a function with the prototype `function uploadPhoto(filename)`, which returns a Promise rejecting with an Error and the string `$fileName cannot be processed`, where `fileName` is a string. 42 | 43 | + [x] 6. **Handle multiple promises**
[6-final-user.js](6-final-user.js) contains a script that meets the following requirements. 44 | + Import `signUpUser` from [4-user-promise.js](4-user-promise.js) and `uploadPhoto` from [5-photo-reject.js](5-photo-reject.js). 45 | + Export a function named `handleProfileSignup` that accepts three arguments `firstName` (string), `lastName` (string), and `fileName` (string) and calls the two other functions (`signUpUser` and `uploadPhoto`). 46 | + When the promises are all settled it should return an array with the following structure: 47 | ```js 48 | [ 49 | { 50 | status: status_of_the_promise, 51 | value: value || reason // value or error returned by the Promise 52 | }, 53 | ... 54 | ] 55 | ``` 56 | 57 | + [x] 7. **Load balancer**
[7-load_balancer.js](7-load_balancer.js) contains a script that exports a function with the prototype `function loadBalancer(chinaDownload, USDownload)`, which returns the value returned by the promise that resolved the first, where `chinaDownload` and `USDownload` are Promises. 58 | 59 | + [x] 8. **Throw error / try catch**
[8-try.js](8-try.js) contains a script that meets the following requirements. 60 | + Exports a function with the prototype `function divideFunction(numerator, denominator)`, where `numerator` and `denominator` are numbers. 61 | + When the `denominator` argument is equal to 0, the function should throw a new error with the message `cannot divide by 0`. 62 | + Otherwise it should return the `numerator` divided by the `denominator`. 63 | 64 | + [x] 9. **Throw an error**
[9-try.js](9-try.js) contains a script that meets the following requirements. 65 | + Export a function named `guardrail` that accepts a function argument called `mathFunction`. 66 | + The `guardrail` function should create and return an array named `queue`. 67 | + When the `mathFunction` function is executed, the value returned by the function should be appended to the `queue`. If this function throws an error, the error message should be appended to the `queue`. 68 | + In every case, the message `Guardrail was processed` should be added to the queue. 69 | 70 | + [x] 10. **Await / Async**
[100-await.js](100-await.js) contains a script that meets the following requirements. 71 | + Import `uploadPhoto` and `createUser` from [utils.js](utils.js). 72 | + Export an async function named `asyncUploadUser` that will call the two functions imported above and return an object with the following format: 73 | ```js 74 | { 75 | photo: response_from_uploadPhoto_function, 76 | user: response_from_createUser_function, 77 | } 78 | ``` 79 | + Import `uploadPhoto` and `createUser` from [utils.js](utils.js). 80 | + If one of the async function fails, return an empty object as shown below: 81 | ```js 82 | { 83 | photo: null, 84 | user: null, 85 | } 86 | ``` 87 | -------------------------------------------------------------------------------- /0x01-ES6_promise/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current', 8 | }, 9 | }, 10 | ], 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /0x01-ES6_promise/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "lint": "./node_modules/.bin/eslint", 4 | "check-lint": "lint [0-9]*.js", 5 | "dev": "npx babel-node", 6 | "test": "jest", 7 | "full-test": "./node_modules/.bin/eslint [0-9]*.js && jest" 8 | }, 9 | "devDependencies": { 10 | "@babel/core": "^7.6.0", 11 | "@babel/node": "^7.8.0", 12 | "@babel/preset-env": "^7.6.0", 13 | "eslint": "^6.4.0", 14 | "eslint-config-airbnb-base": "^14.0.0", 15 | "eslint-plugin-import": "^2.18.2", 16 | "eslint-plugin-jest": "^22.17.0", 17 | "jest": "^24.9.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /0x01-ES6_promise/utils.js: -------------------------------------------------------------------------------- 1 | export function uploadPhoto() { 2 | return Promise.resolve({ 3 | status: 200, 4 | body: 'photo-profile-1', 5 | }); 6 | } 7 | 8 | export function createUser() { 9 | return Promise.resolve({ 10 | firstName: 'Guillaume', 11 | lastName: 'Salva', 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /0x02-ES6_classes/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: false, 4 | es6: true, 5 | jest: true, 6 | }, 7 | extends: ["airbnb-base", "plugin:jest/all"], 8 | globals: { 9 | Atomics: "readonly", 10 | SharedArrayBuffer: "readonly", 11 | }, 12 | parserOptions: { 13 | ecmaVersion: 2018, 14 | sourceType: "module", 15 | }, 16 | plugins: ["jest"], 17 | rules: { 18 | "max-classes-per-file": "off", 19 | "no-underscore-dangle": "off", 20 | "no-console": "off", 21 | "no-shadow": "off", 22 | "no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"], 23 | }, 24 | overrides: [ 25 | { 26 | files: ["*.js"], 27 | excludedFiles: "babel.config.js", 28 | }, 29 | ], 30 | }; 31 | -------------------------------------------------------------------------------- /0x02-ES6_classes/0-classroom.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents a class room. 3 | */ 4 | export default class ClassRoom { 5 | /** 6 | * Creates a new @see {@link ClassRoom}. 7 | * @param {Number} maxStudentsSize - The maximum number of 8 | * students in the class. 9 | */ 10 | constructor(maxStudentsSize) { 11 | this._maxStudentsSize = maxStudentsSize; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /0x02-ES6_classes/1-make_classrooms.js: -------------------------------------------------------------------------------- 1 | import ClassRoom from './0-classroom'; 2 | 3 | /** 4 | * Creates an array of {@link ClassRoom}s with a specific size. 5 | * @returns An array of {@link ClassRoom}s. 6 | */ 7 | export default function initializeRooms() { 8 | return [19, 20, 34].map((size) => new ClassRoom(size)); 9 | } 10 | -------------------------------------------------------------------------------- /0x02-ES6_classes/10-car.js: -------------------------------------------------------------------------------- 1 | export default class Car { 2 | constructor(brand, motor, color) { 3 | this.brand = brand; 4 | this.motor = motor; 5 | this.color = color; 6 | } 7 | 8 | get brand() { 9 | return this._brand; 10 | } 11 | 12 | set brand(value) { 13 | this._brand = value; 14 | } 15 | 16 | get motor() { 17 | return this._motor; 18 | } 19 | 20 | set motor(value) { 21 | this._motor = value; 22 | } 23 | 24 | get color() { 25 | return this._color; 26 | } 27 | 28 | set color(value) { 29 | this._color = value; 30 | } 31 | 32 | static get [Symbol.species]() { 33 | return this; 34 | } 35 | 36 | cloneCar() { 37 | const Species = this.constructor[Symbol.species]; 38 | 39 | return new Species(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /0x02-ES6_classes/100-evcar.js: -------------------------------------------------------------------------------- 1 | import Car from './10-car'; 2 | 3 | export default class EVCar extends Car { 4 | constructor(brand, motor, color, range) { 5 | super(brand, motor, color); 6 | this.range = range; 7 | } 8 | 9 | get range() { 10 | return this._range; 11 | } 12 | 13 | set range(value) { 14 | this._range = value; 15 | } 16 | 17 | cloneCar() { 18 | const Species = super.constructor[Symbol.species]; 19 | 20 | return new Species(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /0x02-ES6_classes/2-hbtn_course.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents a Holberton Course. 3 | */ 4 | export default class HolbertonCourse { 5 | /** 6 | * Creates a new @see {@link HolbertonCourse}. 7 | * 8 | * @param {String} name - The name of the course. 9 | * @param {Number} length - How long the course is (in months). 10 | * @param {String[]} students - The names of students in the course. 11 | */ 12 | constructor(name, length, students) { 13 | this.name = name; 14 | this.length = length; 15 | this.students = students; 16 | } 17 | 18 | /** 19 | * Gets the name of this course. 20 | */ 21 | get name() { 22 | return this._name; 23 | } 24 | 25 | /** 26 | * Sets the name of this course. 27 | */ 28 | set name(value) { 29 | if (typeof value !== 'string') { 30 | throw new TypeError('Name must be a string'); 31 | } 32 | this._name = value; 33 | } 34 | 35 | /** 36 | * Gets the length of this course (in months). 37 | */ 38 | get length() { 39 | return this._length; 40 | } 41 | 42 | /** 43 | * Sets the length of this course (in months). 44 | */ 45 | set length(value) { 46 | if (typeof value !== 'number') { 47 | throw new TypeError('Length must be a number'); 48 | } 49 | this._length = value; 50 | } 51 | 52 | /** 53 | * Gets the names of students in this course. 54 | */ 55 | get students() { 56 | return this._students; 57 | } 58 | 59 | /** 60 | * Sets the names of students in this course. 61 | */ 62 | set students(value) { 63 | if (!(value instanceof Array)) { 64 | throw new TypeError('Students must be an array of strings'); 65 | } 66 | if (!value.every((student) => typeof student === 'string')) { 67 | throw new TypeError('Students must be an array of strings'); 68 | } 69 | this._students = value; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /0x02-ES6_classes/3-currency.js: -------------------------------------------------------------------------------- 1 | export default class Currency { 2 | constructor(code, name) { 3 | this.code = code; 4 | this.name = name; 5 | } 6 | 7 | get code() { 8 | return this._code; 9 | } 10 | 11 | set code(value) { 12 | this._code = value; 13 | } 14 | 15 | get name() { 16 | return this._name; 17 | } 18 | 19 | set name(value) { 20 | this._name = value; 21 | } 22 | 23 | /** 24 | * Creates the full string representation of this Currency. 25 | * @returns {String} 26 | */ 27 | displayFullCurrency() { 28 | return `${this.name} (${this.code})`; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /0x02-ES6_classes/4-pricing.js: -------------------------------------------------------------------------------- 1 | import Currency from './3-currency'; 2 | 3 | export default class Pricing { 4 | constructor(amount, currency) { 5 | this.amount = amount; 6 | this.currency = currency; 7 | } 8 | 9 | get amount() { 10 | return this._amount; 11 | } 12 | 13 | set amount(value) { 14 | if (typeof value !== 'number') { 15 | throw new TypeError('amount must be a number'); 16 | } 17 | this._amount = value; 18 | } 19 | 20 | /** 21 | * @returns {Currency} 22 | */ 23 | get currency() { 24 | return this._currency; 25 | } 26 | 27 | /** 28 | * @param {Currency} value 29 | */ 30 | set currency(value) { 31 | if (!(value instanceof Currency)) { 32 | throw new TypeError('currency must be a Currency'); 33 | } 34 | this._currency = value; 35 | } 36 | 37 | displayFullPrice() { 38 | return `${this.amount} ${this.currency.name} (${this.currency.code})`; 39 | } 40 | 41 | static convertPrice(amount, conversionRate) { 42 | if (typeof amount !== 'number') { 43 | throw new TypeError('amount must be a number'); 44 | } 45 | if (typeof conversionRate !== 'number') { 46 | throw new TypeError('conversionRate must be a number'); 47 | } 48 | return amount * conversionRate; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /0x02-ES6_classes/5-building.js: -------------------------------------------------------------------------------- 1 | export default class Building { 2 | constructor(sqft) { 3 | this.sqft = sqft; 4 | if (this.constructor !== Building) { 5 | if (typeof this.evacuationWarningMessage !== 'function') { 6 | throw new Error( 7 | 'Class extending Building must override evacuationWarningMessage', 8 | ); 9 | } 10 | } 11 | } 12 | 13 | get sqft() { 14 | return this._sqft; 15 | } 16 | 17 | set sqft(value) { 18 | this._sqft = value; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /0x02-ES6_classes/6-sky_high.js: -------------------------------------------------------------------------------- 1 | import Building from './5-building'; 2 | 3 | export default class SkyHighBuilding extends Building { 4 | constructor(sqft, floors) { 5 | super(sqft); 6 | this.floors = floors; 7 | } 8 | 9 | get floors() { 10 | return this._floors; 11 | } 12 | 13 | set floors(value) { 14 | this._floors = value; 15 | } 16 | 17 | evacuationWarningMessage() { 18 | return `Evacuate slowly the ${this.floors} floors`; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /0x02-ES6_classes/7-airport.js: -------------------------------------------------------------------------------- 1 | export default class Airport { 2 | constructor(name, code) { 3 | this.name = name; 4 | this.code = code; 5 | } 6 | 7 | get name() { 8 | return this._name; 9 | } 10 | 11 | set name(value) { 12 | this._name = value; 13 | } 14 | 15 | get code() { 16 | return this._code; 17 | } 18 | 19 | set code(value) { 20 | this._code = value; 21 | } 22 | 23 | get [Symbol.toStringTag]() { 24 | return this._code; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /0x02-ES6_classes/8-hbtn_class.js: -------------------------------------------------------------------------------- 1 | export default class HolbertonClass { 2 | constructor(size, location) { 3 | this.size = size; 4 | this.location = location; 5 | } 6 | 7 | get size() { 8 | return this._size; 9 | } 10 | 11 | set size(value) { 12 | this._size = value; 13 | } 14 | 15 | get location() { 16 | return this._location; 17 | } 18 | 19 | set location(value) { 20 | this._location = value; 21 | } 22 | 23 | [Symbol.toPrimitive](hint) { 24 | if (hint === 'number') { 25 | return this.size; 26 | } 27 | if (hint === 'string') { 28 | return this.location; 29 | } 30 | return this; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /0x02-ES6_classes/9-hoisting.js: -------------------------------------------------------------------------------- 1 | export class HolbertonClass { 2 | constructor(year, location) { 3 | this._year = year; 4 | this._location = location; 5 | } 6 | 7 | get year() { 8 | return this._year; 9 | } 10 | 11 | get location() { 12 | return this._location; 13 | } 14 | } 15 | 16 | export class StudentHolberton { 17 | constructor(firstName, lastName, holbertonClass) { 18 | this._firstName = firstName; 19 | this._lastName = lastName; 20 | this._holbertonClass = holbertonClass; 21 | } 22 | 23 | get fullName() { 24 | return `${this._firstName} ${this._lastName}`; 25 | } 26 | 27 | get holbertonClass() { 28 | return this._holbertonClass; 29 | } 30 | 31 | get fullStudentDescription() { 32 | return `${this._firstName} ${this._lastName} - \ 33 | ${this._holbertonClass.year} - ${this._holbertonClass.location}`; 34 | } 35 | } 36 | 37 | const class2019 = new HolbertonClass(2019, 'San Francisco'); 38 | const class2020 = new HolbertonClass(2020, 'San Francisco'); 39 | 40 | const student1 = new StudentHolberton('Guillaume', 'Salva', class2020); 41 | const student2 = new StudentHolberton('John', 'Doe', class2020); 42 | const student3 = new StudentHolberton('Albert', 'Clinton', class2019); 43 | const student4 = new StudentHolberton('Donald', 'Bush', class2019); 44 | const student5 = new StudentHolberton('Jason', 'Sandler', class2019); 45 | 46 | const listOfStudents = [student1, student2, student3, student4, student5]; 47 | 48 | export default listOfStudents; 49 | -------------------------------------------------------------------------------- /0x02-ES6_classes/README.md: -------------------------------------------------------------------------------- 1 | # ES6 Classes 2 | 3 | This project contains tasks for learning to use classes in ECMAScript 2015 (ES6). 4 | 5 | ## Tasks To Complete 6 | 7 | + [x] 0. **You used to attend a place like this at some point**
[0-classroom.js](0-classroom.js) contains a script that exports a class named `ClassRoom` with the following requirements: 8 | + Prototype: `export default class ClassRoom`. 9 | + It should accept one attribute named `maxStudentsSize` (Number) and assigned to `_maxStudentsSize`. 10 | 11 | + [x] 1. **Let's make some classrooms**
[1-make_classrooms.js](1-make_classrooms.js) contains a script that meets the following requirements: 12 | + Import the `ClassRoom` class from [0-classroom.js](0-classroom.js). 13 | + Export a function named `initializeRooms`. It should return an array of 3 `ClassRoom` objects with the sizes 19, 20, and 34 (in this order). 14 | 15 | + [x] 2. **A Course, Getters, and Setters**
[2-hbtn_course.js](2-hbtn_course.js) contains a script that exports a class named `HolbertonCourse` with the following requirements: 16 | + Constructor arguments: 17 | + `name` (String). 18 | + `length` (Number). 19 | + `students` (array of Strings). 20 | + Make sure to verify the type of attributes during object creation. 21 | + Each attribute must be stored in an "underscore" attribute version (ex: `name` is stored in `_name`). 22 | + Implement a getter and setter for each attribute. 23 | 24 | + [x] 3. **Methods, static methods, computed methods names..... MONEY**
[3-currency.js](3-currency.js) contains a script that exports a class named `Currency` with the following requirements: 25 | + Constructor arguments: 26 | + `code` (String). 27 | + `name` (String). 28 | + Each attribute must be stored in an "underscore" attribute version (ex: `name` is stored in `_name`). 29 | + Implement a getter and setter for each attribute. 30 | + Implement a method named `displayFullCurrency` that will return the attributes in the format `name (code)`. 31 | 32 | + [x] 4. **Pricing**
[4-pricing.js](4-pricing.js) contains a script that meets the following requirements: 33 | + Import the class `Currency` from [3-currency.js](3-currency.js). 34 | + Export a class named `Pricing` that meets the following requirements: 35 | + Constructor arguments: 36 | + `amount` (Number). 37 | + `currency` (Currency). 38 | + Each attribute must be stored in an "underscore" attribute version (ex: `name` is stored in `_name`). 39 | + Implement a getter and setter for each attribute. 40 | + Implement a method named `displayFullPrice` that returns the attributes in the format `amount currency_name (currency_code)`. 41 | + Implement a static method named `convertPrice`. It should accept two arguments: `amount` (Number), `conversionRate` (Number). The function should return the amount multiplied by the conversion rate. 42 | 43 | + [x] 5. **A Building**
[5-building.js](5-building.js) contains a script that exports a class named `Building` with the following requirements: 44 | + Constructor arguments: 45 | + `sqft` (Number). 46 | + Each attribute must be stored in an "underscore" attribute version (ex: `name` is stored in `_name`). 47 | + Implement a getter for each attribute. 48 | + Consider this class as an abstract class. And make sure that any class that extends from it should implement a method named `evacuationWarningMessage`. 49 | + If a class that extends from it does not have a `evacuationWarningMessage` method, throw an error with the message `Class extending Building must override evacuationWarningMessage`. 50 | 51 | + [x] 6. **Inheritance**
[6-sky_high.js](6-sky_high.js) contains a script that meets the following requirements: 52 | + Import `Building` from [4-building.js](4-building.js). 53 | + Export a class named `SkyHighBuilding` that extends from `Building` and meets the following requirements: 54 | + Constructor arguments: 55 | + `sqft` (Number) (must be assigned to the parent class `Building`). 56 | + `floors` (Number). 57 | + Each attribute must be stored in an "underscore" attribute version (ex: `name` is stored in `_name`). 58 | + Implement a getter for each attribute. 59 | + Override the method named `evacuationWarningMessage` and return the following string `Evacuate slowly the NUMBER_OF_FLOORS floors`. 60 | 61 | + [x] 7. **Airport**
[7-airport.js](7-airport.js) contains a script that exports a class named `Airport` with the following requirements: 62 | + Constructor arguments: 63 | + `name` (String). 64 | + `code` (String). 65 | + Each attribute must be stored in an "underscore" attribute version (ex: `name` is stored in `_name`). 66 | + The default string description of the class should return the airport `code` (example below). 67 | 68 | + [x] 8. **Primitive - Holberton Class**
[8-hbtn_class.js](8-hbtn_class.js) contains a script that exports a class named `HolbertonClass` with the following requirements: 69 | + Constructor arguments: 70 | + `size` (Number). 71 | + `location` (String). 72 | + Each attribute must be stored in an "underscore" attribute version (ex: `name` is stored in `_name`). 73 | + When the class is cast into a `Number`, it should return the size. 74 | + When the class is cast into a `String`, it should return the location. 75 | 76 | + [x] 9. **Hoisting**
[9-hoisting.js](9-hoisting.js) contains a fixed and working copy of the code below: 77 | ```js 78 | const class2019 = new HolbertonClass(2019, 'San Francisco'); 79 | const class2020 = new HolbertonClass(2020, 'San Francisco'); 80 | 81 | export class HolbertonClass { 82 | constructor(year, location) { 83 | this._year = year; 84 | this._location = location; 85 | } 86 | 87 | get year() { 88 | return this._year; 89 | } 90 | 91 | get location() { 92 | return this._location; 93 | } 94 | } 95 | 96 | const student1 = new StudentHolberton('Guillaume', 'Salva', class2020); 97 | const student2 = new StudentHolberton('John', 'Doe', class2020); 98 | const student3 = new StudentHolberton('Albert', 'Clinton', class2019); 99 | const student4 = new StudentHolberton('Donald', 'Bush', class2019); 100 | const student5 = new StudentHolberton('Jason', 'Sandler', class2019); 101 | 102 | export class StudentHolberton { 103 | constructor(firstName, lastName) { 104 | this._firstName = firstName; 105 | this._lastName = lastName; 106 | this._holbertonClass = holbertonClass; 107 | } 108 | 109 | get fullName() { 110 | return `${this._firstName} ${this._lastName}`; 111 | } 112 | 113 | get holbertonClass() { 114 | return this.holbertonClass; 115 | } 116 | 117 | get fullStudentDescription() { 118 | return `${self._firstName} ${self._lastName} - ${self._holbertonClass.year} - ${self._holbertonClass.location}`; 119 | } 120 | } 121 | 122 | 123 | export const listOfStudents = [student1, student2, student3, student4, student5]; 124 | ``` 125 | 126 | + [x] 10. **Vroom**
[10-car.js](10-car.js) contains a script that exports a class named `Car` with the following requirements: 127 | + Constructor arguments: 128 | + `brand` (String). 129 | + `motor` (String). 130 | + `color` (String). 131 | + Each attribute must be stored in an "underscore" attribute version (ex: `name` is stored in `_name`). 132 | + Add a method named `cloneCar`. This method should return a new object of the class. 133 | 134 | + [x] 11. **EVCar**
[100-evcar.js](100-evcar.js) contains a script that meets the following requirements: 135 | + Import `Car` from [10-car.js](10-car.js). 136 | + Export a class named `EVCar` that extends the `Car` class and meets the following requirements: 137 | + Constructor arguments: 138 | + `brand` (String). 139 | + `motor` (String). 140 | + `color` (String). 141 | + `range` (String). 142 | + Each attribute must be stored in an "underscore" attribute version (ex: `name` is stored in `_name`). 143 | + For privacy reasons, when `cloneCar` is called on an `EVCar` object, the object returned should be an instance of `Car` instead of `EVCar`. 144 | -------------------------------------------------------------------------------- /0x02-ES6_classes/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current', 8 | }, 9 | }, 10 | ], 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /0x02-ES6_classes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "lint": "./node_modules/.bin/eslint", 4 | "check-lint": "lint [0-9]*.js", 5 | "dev": "npx babel-node", 6 | "test": "jest", 7 | "full-test": "./node_modules/.bin/eslint [0-9]*.js && jest" 8 | }, 9 | "devDependencies": { 10 | "@babel/core": "^7.6.0", 11 | "@babel/node": "^7.8.0", 12 | "@babel/preset-env": "^7.6.0", 13 | "eslint": "^6.4.0", 14 | "eslint-config-airbnb-base": "^14.0.0", 15 | "eslint-plugin-import": "^2.18.2", 16 | "eslint-plugin-jest": "^22.17.0", 17 | "jest": "^24.9.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: false, 4 | es6: true, 5 | jest: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | 'plugin:jest/all', 10 | ], 11 | globals: { 12 | Atomics: 'readonly', 13 | SharedArrayBuffer: 'readonly', 14 | }, 15 | parserOptions: { 16 | ecmaVersion: 2018, 17 | sourceType: 'module', 18 | }, 19 | plugins: ['jest'], 20 | rules: { 21 | 'max-classes-per-file': 'off', 22 | 'no-underscore-dangle': 'off', 23 | 'no-console': 'off', 24 | 'no-shadow': 'off', 25 | 'no-restricted-syntax': [ 26 | 'error', 27 | 'LabeledStatement', 28 | 'WithStatement', 29 | ], 30 | }, 31 | overrides:[ 32 | { 33 | files: ['*.js'], 34 | excludedFiles: 'babel.config.js', 35 | } 36 | ] 37 | }; 38 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/0-get_list_students.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Retrieves a list of students. 3 | * @author Bezaleel Olakunori 4 | * @returns {{id: Number, firstName: String, location: String}[]} 5 | */ 6 | export default function getListStudents() { 7 | return [ 8 | { id: 1, firstName: 'Guillaume', location: 'San Francisco' }, 9 | { id: 2, firstName: 'James', location: 'Columbia' }, 10 | { id: 5, firstName: 'Serena', location: 'San Francisco' }, 11 | ]; 12 | } 13 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/1-get_list_student_ids.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Retrieves ids from a list of students. 3 | * @param {{ 4 | * id: Number, 5 | * firstName: String, 6 | * location: String 7 | * }[]} students - The list of students. 8 | * @author Bezaleel Olakunori 9 | * @returns 10 | */ 11 | export default function getListStudentIds(students) { 12 | if (students instanceof Array) { 13 | return students.map((student) => student.id); 14 | } 15 | return []; 16 | } 17 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/10-update_uniq_items.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Changes the quantity of unique grocery items to 100. 3 | * @param {Map} map - A map of the name of a 4 | * grocery and its quantity. 5 | * @author Bezaleel Olakunori 6 | */ 7 | export default function updateUniqueItems(map) { 8 | if (!(map instanceof Map)) { 9 | throw new Error('Cannot process'); 10 | } 11 | map.forEach((value, key) => { 12 | if (value === 1) { 13 | map.set(key, 100); 14 | } 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/100-weak.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A weak map of endpoints and the number of calls made. 3 | */ 4 | export const weakMap = new WeakMap(); 5 | 6 | /** 7 | * The maximum number of calls for an endpoint. 8 | */ 9 | const MAX_ENDPOINT_CALLS = 5; 10 | 11 | /** 12 | * Tracks the number of calls made to an API's endpoint. 13 | * @param {{ 14 | * protocol: String, 15 | * name: String, 16 | * }} endpoint - The endpoint to make a request to. 17 | * @author Bezaleel Olakunori 18 | */ 19 | export function queryAPI(endpoint) { 20 | if (!weakMap.has(endpoint)) { 21 | weakMap.set(endpoint, 0); 22 | } 23 | weakMap.set(endpoint, weakMap.get(endpoint) + 1); 24 | if (weakMap.get(endpoint) >= MAX_ENDPOINT_CALLS) { 25 | throw new Error('Endpoint load is high'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/2-get_students_by_loc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Retrieves students in a given location. 3 | * @param {{ 4 | * id: Number, 5 | * firstName: String, 6 | * location: String 7 | * }[]} students - The list of students. 8 | * @param {String} city - The location. 9 | * @author Bezaleel Olakunori 10 | * @returns 11 | */ 12 | export default function getStudentsByLocation(students, city) { 13 | if (students instanceof Array) { 14 | return students.filter((student) => student.location === city); 15 | } 16 | return []; 17 | } 18 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/3-get_ids_sum.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Retrieves the sum of ids of a list of students. 3 | * @param {{ 4 | * id: Number, 5 | * firstName: String, 6 | * location: String 7 | * }[]} students - The list of students. 8 | * @author Bezaleel Olakunori 9 | * @returns {Number} 10 | */ 11 | export default function getStudentIdsSum(students) { 12 | if (students instanceof Array) { 13 | return students.reduce( 14 | (prevStudent, curStudent) => prevStudent.id || prevStudent + curStudent.id, 15 | 0, 16 | ); 17 | } 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/4-update_grade_by_city.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Updates the grades of a list of students in a given city. 3 | * @param {{ 4 | * id: Number, 5 | * firstName: String, 6 | * location: String 7 | * }[]} students - The list of students. 8 | * @param {*} city - The city of students. 9 | * @param {{ 10 | * studentId: Number, 11 | * grade: Number, 12 | * }[]} newGrades - The new grades to be given to a student. 13 | * @author Bezaleel Olakunori 14 | * @returns {{id: Number, firstName: String, location: String}[]} 15 | */ 16 | export default function updateStudentGradeByCity(students, city, newGrades) { 17 | const defaultGrade = { grade: 'N/A' }; 18 | 19 | if (students instanceof Array) { 20 | return students 21 | .filter((student) => student.location === city) 22 | .map((student) => ({ 23 | id: student.id, 24 | firstName: student.firstName, 25 | location: student.location, 26 | grade: (newGrades 27 | .filter((grade) => grade.studentId === student.id) 28 | .pop() || defaultGrade).grade, 29 | })); 30 | } 31 | return []; 32 | } 33 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/5-typed_arrays.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates a buffer array with a given position set to a given value. 3 | * @param {Number} length - The length of the buffer. 4 | * @param {Number} position - The position to modify. 5 | * @param {Number} value - The value to be stored in the position. 6 | * @author Bezaleel Olakunori 7 | * @returns {DataView} 8 | */ 9 | export default function createInt8TypedArray(length, position, value) { 10 | if (position >= length) { 11 | throw new Error('Position outside range'); 12 | } 13 | const buf = new DataView(new ArrayBuffer(length), 0, length); 14 | buf.setInt8(position, value); 15 | return buf; 16 | } 17 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/6-set.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates a set from an array. 3 | * @param {Array} array - The source array. 4 | * @author Bezaleel Olakunori 5 | * @returns {Set} 6 | */ 7 | export default function setFromArray(array) { 8 | return new Set(array); 9 | } 10 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/7-has_array_values.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if a set contains each element in an array. 3 | * @param {Set} set - The collection of unique items. 4 | * @param {*} array - The array of items. 5 | * @author Bezaleel Olakunori 6 | * @returns {Boolean} 7 | */ 8 | export default function hasValuesFromArray(set, array) { 9 | return array.every((value) => set.has(value)); 10 | } 11 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/8-clean_set.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Joins a set of strings with a dash after stripping the strings of 3 | * a leading sub string. 4 | * @param {Set} set - A collection of strings. 5 | * @param {String} startString - The string to strip from the beginning 6 | * of each item in the set. 7 | * @author Bezaleel Olakunori 8 | * @returns {String} 9 | */ 10 | export default function cleanSet(set, startString) { 11 | const parts = []; 12 | if (!set || !startString || !(set instanceof Set) || typeof startString !== 'string') { 13 | return ''; 14 | } 15 | for (const value of set.values()) { 16 | if (typeof value === 'string' && value.startsWith(startString)) { 17 | const valueSubStr = value.substring(startString.length); 18 | 19 | if (valueSubStr && valueSubStr !== value) { 20 | parts.push(valueSubStr); 21 | } 22 | } 23 | } 24 | return parts.join('-'); 25 | } 26 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/9-groceries_list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Retrieves a map of grocery names and their quantity. 3 | * @author Bezaleel Olakunori 4 | * @returns {Map} 5 | */ 6 | export default function groceriesList() { 7 | const values = [ 8 | ['Apples', 10], 9 | ['Tomatoes', 10], 10 | ['Pasta', 1], 11 | ['Rice', 1], 12 | ['Banana', 5], 13 | ]; 14 | return new Map(values); 15 | } 16 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/README.md: -------------------------------------------------------------------------------- 1 | # ES6 Data Manipulation 2 | 3 | This project contains tasks for learning to manipulate data in ECMAScript 2015 (ES6). 4 | 5 | ## Tasks To Complete 6 | 7 | + [x] 0. **Basic list of objects**
[0-get_list_students.js](0-get_list_students.js) contains a script that exports a function named `getListStudents` with the following requirements: 8 | + Returns an array of objects. 9 | + Each object should have three attributes: `id` (Number), `firstName` (String), and `location` (String). 10 | + The array contains the following students in the order they're listed: 11 | + `Guillaume`, id: `1`, in `San Francisco`. 12 | + `James`, id: `2`, in `Columbia`. 13 | + `Serena`, id: `5`, in `San Francisco`. 14 | 15 | + [x] 1. **More mapping**
[1-get_list_student_ids.js](1-get_list_student_ids.js) contains a script that exports a function named `getListStudentIds` with the following requirements: 16 | + Returns an array of ids from a list of object. 17 | + This function is taking one argument which is an array of objects - and this array is in the same format as the return value of `getListStudents` from the previous task. 18 | + If the argument is not an array, the function returns an empty array. 19 | + You must use the map function on the array. 20 | 21 | + [x] 2. **Filter**
[2-get_students_by_loc.js](2-get_students_by_loc.js) contains a script that exports a function named `getStudentsByLocation` with the following requirements: 22 | + Returns an array of objects who are located in a specific city. 23 | + It should accept a list of students (from `getListStudents`) and a `city` (string) as parameters. 24 | + You must use the `filter` function on the array. 25 | 26 | + [x] 3. **Reduce**
[3-get_ids_sum.js](3-get_ids_sum.js) contains a script that exports a function named `getStudentIdsSum` with the following requirements: 27 | + Returns the sum of all the student ids. 28 | + It should accept a list of students (from `getListStudents`) as a parameter. 29 | + You must use the `reduce` function on the array. 30 | 31 | + [x] 4. **Combine**
[4-update_grade_by_city.js](4-update_grade_by_city.js) contains a script that exports a function named `updateStudentGradeByCity` with the following requirements: 32 | + Returns an array of students for a specific city with their new grade. 33 | + It should accept a list of students (from `getListStudents`), a `city` (String), and `newGrades` (Array of “grade” objects) as parameters. 34 | + `newGrades` is an array of objects with this format: 35 | ```js 36 | { 37 | studentId: Number, 38 | grade: Number, 39 | } 40 | ``` 41 | 42 | + [x] 5. **Typed Arrays**
[5-typed_arrays.js](5-typed_arrays.js) contains a script that exports a function named `createInt8TypedArray` with the following requirements: 43 | + Returns a new `ArrayBuffer` with an `Int8` value at a specific position. 44 | + It should accept three arguments: `length` (Number), `position` (Number), and `value` (Number). 45 | + If adding the value is not possible the error `Position outside range` should be thrown. 46 | 47 | + [x] 6. **Set data structure**
[6-set.js](6-set.js) contains a script that exports a function named `setFromArray` with the following requirements: 48 | + Returns a `Set` from an array. 49 | + It accepts an argument (Array, of any kind of element). 50 | 51 | + [x] 7. **More set data structure**
[7-has_array_values.js](7-has_array_values.js) contains a script that exports a function named `hasValuesFromArray` with the following requirements: 52 | + Returns a boolean if all the elements in the array exist within the set. 53 | + It accepts two arguments: a `set` (Set) and an `array` (Array). 54 | 55 | + [x] 8. **Clean set**
[8-clean_set.js](8-clean_set.js) contains a script that exports a function named `cleanSet` with the following requirements: 56 | + Returns a string of all the set values that start with a specific string (`startString`). 57 | + It accepts two arguments: a `set` (Set) and a `startString` (String). 58 | + When a value starts with `startString` you only append the rest of the string. The string contains all the values of the set separated by `-`. 59 | 60 | + [x] 9. **Map data structure**
[9-groceries_list.js](9-groceries_list.js) contains a script that exports a function named `groceriesList` with the following requirements: 61 | + Returns a map of groceries with the following items (name, quantity): 62 | ```cs 63 | "Apples", 10 64 | "Tomatoes", 10 65 | "Pasta", 1 66 | "Rice", 1 67 | "Banana", 5 68 | ``` 69 | 70 | + [x] 10. **More map data structure**
[10-update_uniq_items.js](10-update_uniq_items.js) contains a script that exports a function named `updateUniqueItems` with the following requirements: 71 | + Returns an updated map for all items with initial quantity at 1. 72 | + It should accept a map as an argument. The map it accepts for argument is similar to the map you create in the previous task. 73 | + For each entry of the map where the quantity is 1, update the quantity to 100. If updating the quantity is not possible (argument is not a map) the error `Cannot process` should be thrown. 74 | 75 | + [x] 11. **Weak link data structure**
[100-weak.js](100-weak.js) contains a script that meets the following requirements: 76 | + Export a `const` instance of `WeakMap` and name it `weakMap`. 77 | + Export a new function named `queryAPI`. It should accept an endpoint argument like so: 78 | ```js 79 | { 80 | protocol: 'http', 81 | name: 'getUsers', 82 | } 83 | ``` 84 | + Track within the `weakMap` the number of times `queryAPI` is called for each endpoint. 85 | + When the number of queries is >= 5 throw an error with the message `Endpoint load is high`. 86 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current', 8 | }, 9 | }, 10 | ], 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /0x03-ES6_data_manipulation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "lint": "./node_modules/.bin/eslint", 4 | "check-lint": "lint [0-9]*.js", 5 | "dev": "npx babel-node", 6 | "test": "jest", 7 | "full-test": "./node_modules/.bin/eslint [0-9]*.js && jest" 8 | }, 9 | "devDependencies": { 10 | "@babel/core": "^7.6.0", 11 | "@babel/node": "^7.8.0", 12 | "@babel/preset-env": "^7.6.0", 13 | "eslint": "^6.4.0", 14 | "eslint-config-airbnb-base": "^14.0.0", 15 | "eslint-plugin-import": "^2.18.2", 16 | "eslint-plugin-jest": "^22.17.0", 17 | "jest": "^24.9.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /0x04-TypeScript/README.md: -------------------------------------------------------------------------------- 1 | # TypeScript 2 | 3 | This project contains tasks for learning to use TypeScript. 4 | 5 | ## Tasks To Complete 6 | 7 | + [x] 0. **Creating an interface for a student**
[task_0/js/main.ts](task_0/js/main.ts) contains a script that meets the following requirements: 8 | + Write an interface named `Student` that accepts the following elements: `firstName(string)`, `lastName(string)`, `age(number)`, and `location(string)`. 9 | + Create two students, and create an array named `studentsList` containing the two variables. 10 | + Using Vanilla Javascript, render a table and for each elements in the array, append a new row to the table. 11 | + Each row should contain the first name of the student and the location. 12 | + When running, Webpack should return `No type errors found`. 13 | + Every variable should use TypeScript when possible. 14 | 15 | + [x] 1. **Let's build a Teacher interface**
[task_1/js/main.ts](task_1/js/main.ts) contains a script that meets the following requirements: 16 | + `firstName(string)` and `lastName(string)`. These two attributes should only be modifiable when a Teacher is first initialized. 17 | + `fullTimeEmployee(boolean)` this attribute should always be defined. 18 | + `yearsOfExperience(number)` this attribute is optional. 19 | + `location(string)` this attribute should always be defined. 20 | + Add the possibility to add any attribute to the Object like `contract(boolean)` without specifying the name of the attribute. 21 | 22 | + [x] 2. **Extending the Teacher class**
[task_1/js/main.ts](task_1/js/main.ts) contains the following updates: 23 | + Write an interface named `Directors` that extends `Teacher`. It requires an attribute named `numberOfReports(number)`. 24 | 25 | + [x] 3. **Printing teachers**
[task_1/js/main.ts](task_1/js/main.ts) contains the following updates: 26 | + Write a function `printTeacher`: 27 | + It accepts two arguments `firstName` and `lastName`. 28 | + It returns the first letter of the `firstName` and the full `lastName`. 29 | + Example: `printTeacher("John", "Doe") -> J. Doe`. 30 | + Write an interface for the function named `printTeacherFunction`. 31 | 32 | + [x] 4. **Writing a class**
[task_1/js/main.ts](task_1/js/main.ts) contains the following updates: 33 | + Write a Class named `StudentClass`: 34 | + The constructor accepts `firstName(string)` and `lastName(string)` arguments. 35 | + The class has a method named `workOnHomework` that return the string `Currently working`. 36 | + The class has a method named `displayName`. It returns the firstName of the student. 37 | + The constructor of the class should be described through an Interface. 38 | + The class should be described through an Interface. 39 | + When running `npm run build`, no TypeScript error should be displayed. 40 | + Every variable should use TypeScript when possible. 41 | 42 | + [x] 5. **Advanced types Part 1**
[task_2/js/main.ts](task_2/js/main.ts) contains a script that meets the following requirements: 43 | + Create the `DirectorInterface` interface with the 3 expected methods: 44 | + `workFromHome()` returning a string. 45 | + `getCoffeeBreak()` returning a string. 46 | + `workDirectorTasks()` returning a string. 47 | + Create the `TeacherInterface` interface with the 3 expected methods: 48 | + `workFromHome()` returning a string. 49 | + `getCoffeeBreak()` returning a string. 50 | + `workTeacherTasks()` returning a string. 51 | + Create a class `Director` that will implement `DirectorInterface`: 52 | + `workFromHome` should return the string `Working from home`. 53 | + `getToWork` should return the string `Getting a coffee break`. 54 | + `workDirectorTasks` should return the string `Getting to director tasks`. 55 | + Create a class `Teacher` that will implement `TeacherInterface`: 56 | + `workFromHome` should return the string `Cannot work from home`. 57 | + `getCoffeeBreak` should return the string `Cannot have a break`. 58 | + `workTeacherTasks` should return the string `Getting to work`. 59 | + Create a function `createEmployee` with the following requirements: 60 | + It can return either a `Director` or a `Teacher` instance. 61 | + It accepts 1 arguments: 62 | + `salary`(either number or string). 63 | + If `salary` is a number and less than 500 - It should return a new `Teacher`. Otherwise it should return a `Director`. 64 | 65 | + [x] 6. **Creating functions specific to employees**
[task_2/js/main.ts](task_2/js/main.ts) contains the following updates: 66 | + Write a function `isDirector`: 67 | + It accepts `employee` as an argument. 68 | + It will be used as a type predicate and if the employee is a director. 69 | + Write a function `executeWork`: 70 | + It accepts `employee` as an argument. 71 | + If the employee is a `Director`, it will call `workDirectorTasks`. 72 | + If the employee is a `Teacher`, it will call `workTeacherTasks`. 73 | 74 | + [x] 7. **String literal types**
[task_2/js/main.ts](task_2/js/main.ts) contains the following updates: 75 | + Write a String literal type named `Subjects` that allows a variable to have the value `Math` or `History` only. 76 | + Write a function named `teachClass`: 77 | + It takes `todayClass` as an argument. 78 | + It will return the string `Teaching Math` if `todayClass` is `Math`. 79 | + It will return the string `Teaching History` if `todayClass` is `History`. 80 | 81 | + [x] 8. **Ambient Namespaces** 82 | + [task_3/js/interfaces.ts](task_3/js/interfaces.ts) contains a script that meets the following requirements: 83 | + Export a type `RowID` and set it equal to `number`. 84 | + Export an interface `RowElement` that contains these 3 fields: 85 | + `firstName`: `string`. 86 | + `lastName`: `string`. 87 | + `age?`: `number`. 88 | + You are building the next part of the application architecture. The goal is to save the entities to a database. Because of time constraints, you can't write a connector to the database, and you decided to download a library called `crud.js`. Copy the file, which is shown below, into the [task_3/js](task_3/js) directory. 89 | ```js 90 | export function insertRow(row) { 91 | console.log('Insert row', row); 92 | return Math.floor(Math.random() * Math.floor(1000)); 93 | } 94 | 95 | export function deleteRow(rowId) { 96 | console.log('Delete row id', rowId); 97 | return; 98 | } 99 | 100 | export function updateRow(rowId, row) { 101 | console.log(`Update row ${rowId}`, row); 102 | 103 | return rowId; 104 | } 105 | ``` 106 | + [task_3/js/crud.d.ts](task_3/js/crud.d.ts) is an ambient file that meets the following requirements: 107 | + Export the type declarations for each crud function. 108 | + At the top of the file, import `RowID` and `RowElement` from [task_3/js/interfaces.ts](task_3/js/interfaces.ts). 109 | + [task_3/js/main.ts](task_3/js/main.ts) contains a script that meets the following requirements: 110 | + At the top of the file create a [triple slash directive](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html) that includes all the dependencies from [task_3/js/crud.d.ts](task_3/js/crud.d.ts). 111 | + Import the `RowID` type and `RowElement` from [task_3/js/interfaces.ts](task_3/js/interfaces.ts). 112 | + Import everything from [task_3/js/crud.js](task_3/js/crud.js) as `CRUD`. 113 | + Create an object called `row` with the type `RowElement` with the fields set to these values: 114 | + `firstName`: `Guillaume`. 115 | + `lastName`: `Salva`. 116 | + Create a `const` variable named `newRowID` with the type `RowID` and assign it the value of the `insertRow` command. 117 | + Next, create a `const` variable named `updatedRow` with the type `RowElement` and update `row` with an age field set to `23`. 118 | + Finally, call the `updateRow` and `deleteRow` commands. 119 | 120 | + [x] 9. **Namespace & Declaration merging** 121 | + [task_4/js/subjects/Teacher.ts](task_4/js/subjects/Teacher.ts) contains a script that meets the following requirements: 122 | + Export a `Teacher` interface in a namespace named `Subjects`. 123 | + The interface requires `firstName` and `lastName` as string. 124 | + [task_4/js/subjects/Subject.ts](task_4/js/subjects/Subject.ts) contains a script that meets the following requirements: 125 | + Export a `Subject` class in a namespace named `Subjects`. 126 | + The class has one attribute `teacher` that implements the `Teacher` interface. 127 | + the class has one setter method `setTeacher` that accepts a `teacher` in argument (and as setter, set the instance attribute `teacher` with it). 128 | + [task_4/js/subjects/Cpp.ts](task_4/js/subjects/Cpp.ts) contains a script that meets the following requirements: 129 | + Using declaration merging, add a new optional attribute `experienceTeachingC` (number) to the `Teacher` interface in a namespace named `Subjects`. 130 | + Export a class `Cpp` extending from `Subject` in a namespace named `Subjects`. 131 | + Write a method named `getRequirements` that will return a string `Here is the list of requirements for Cpp`. 132 | + Write a method named `getAvailableTeacher` that will return a string `Available Teacher: `. 133 | + If the teacher doesn't have any experience in teaching C, then the method should return a string `No available teacher`. 134 | + [task_4/js/subjects/React.ts](task_4/js/subjects/React.ts) contains a script that meets the following requirements: 135 | + Export a `React` class in a namespace named `Subjects`. 136 | + Add a new attribute `experienceTeachingReact?` (number) to the `Teacher` interface. 137 | + In the class, write a method named `getRequirements` that will return a string `Here is the list of requirements for React`. 138 | + Write a method named `getAvailableTeacher` that will return a string `Available Teacher: `. 139 | + If the teacher doesn't have any experience in teaching React, then the method should return a string `No available teacher`. 140 | + [task_4/js/subjects/Java.ts](task_4/js/subjects/Java.ts) contains a script that meets the following requirements: 141 | + Export a `Java` class in a namespace named `Subjects`. 142 | + Add a new attribute `experienceTeachingJava?` (number) to the `Teacher` interface. 143 | + In the class, write a method named `getRequirements` that will return a string `Here is the list of requirements for Java`. 144 | + Write a method named `getAvailableTeacher` that will return a string `Available Teacher: `. 145 | + If the teacher doesn't have any experience in teaching Java, then the method should return a string `No available teacher`. 146 | 147 | + [x] 10. **Update task_4/js/main.ts**
[task_4/js/main.ts](task_4/js/main.ts) contains the following updates: 148 | + Create and export a constant `cpp` for Cpp Subjects. 149 | + Create and export a constant `java` for Java Subjects. 150 | + Create and export a constant `react` for React Subjects. 151 | + Create and export one Teacher object `cTeacher` with `experienceTeachingC = 10`. 152 | + For Cpp subject, log to the console `C++`, set `cTeacher` as the teacher, call the two methods `getRequirements` and `getAvailableTeacher` and print the strings they return. 153 | + For Java subject, log to the console `Java`, set `cTeacher` as the teacher, call the two methods `getRequirements` and `getAvailableTeacher`, and print the strings they return. 154 | + For React subject, log to the console `React`, set `cTeacher` as the teacher, call the two methods `getRequirements` and `getAvailableTeacher`, and print the strings they return. 155 | 156 | + [x] 11. **Brand convention & Nominal typing**
[task_5/js/main.ts](task_5/js/main.ts) contains a script that meets the following requirements: 157 | + Create two interfaces `MajorCredits` and `MinorCredits`: 158 | + Each interface defines a number named `credits`. 159 | + Add a brand property to each interface in order to uniquely identify each of them. 160 | + Create two functions named `sumMajorCredits` and `sumMinorCredits`: 161 | + Each function takes two arguments `subject1` and `subject2`. 162 | + `sumMajorCredits` returns `MajorCredits` value and `sumMinorCredits` returns `MinorCredits` value. 163 | + Each function sums the credits of the two subjects. 164 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_0/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: [ 4 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from @typescript-eslint/eslint-plugin 5 | ], 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | sourceType: 'module', 9 | }, 10 | rules: { 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_0/js/main.ts: -------------------------------------------------------------------------------- 1 | export interface Student { 2 | firstName: string; 3 | lastName: string; 4 | age: number; 5 | location: string; 6 | } 7 | const studentA: Student = { 8 | firstName: "Harry", 9 | lastName: "Yoon", 10 | age: 22, 11 | location: "Seoul, South Korea", 12 | }; 13 | const studentB: Student = { 14 | firstName: "Anjali", 15 | lastName: "Prajapati", 16 | age: 22, 17 | location: "Lodz, Poland", 18 | }; 19 | 20 | const studentsList: Array = [ 21 | studentA, 22 | studentB, 23 | ]; 24 | const styleSheet = ` 25 | html { 26 | margin: 0; 27 | height: 100%; 28 | } 29 | body { 30 | box-sizing: border-box; 31 | display: flex; 32 | align-items: center; 33 | justify-content: center; 34 | height: 80%; 35 | margin: 10%; 36 | } 37 | table { 38 | border-collapse: collapse; 39 | } 40 | thead { 41 | font-weight: bold; 42 | } 43 | td { 44 | padding: 10px; 45 | border: 1px solid gray; 46 | cursor: pointer; 47 | } 48 | td:hover { 49 | background: gainsboro; 50 | } 51 | 52 | td:nth-child(1) { 53 | text-align: center; 54 | } 55 | `; 56 | 57 | /** 58 | * Displays information about students in a table. 59 | * @param students The list of students to display. 60 | * @author Bezaleel Olakunori 61 | */ 62 | export const displayStudents = (students: Array): void => { 63 | const table = document.createElement('table'); 64 | const tableHead = document.createElement('thead'); 65 | const headRow = document.createElement('tr'); 66 | const tableBody = document.createElement('tbody'); 67 | headRow.insertAdjacentHTML('beforeend', 'FirstNameLocation${student.firstName}`); 74 | bodyRow.insertAdjacentHTML('beforeend', `${student.location}`); 75 | tableBody.insertAdjacentElement('beforeend', bodyRow); 76 | } 77 | 78 | table.insertAdjacentElement('beforeend', tableHead); 79 | table.insertAdjacentElement('beforeend', tableBody); 80 | document.body.insertAdjacentElement('beforeend', table); 81 | }; 82 | 83 | displayStudents(studentsList); 84 | const styleSheetElement = document.createElement('style'); 85 | styleSheetElement.innerHTML = styleSheet; 86 | document.head.insertAdjacentElement('beforeend', styleSheetElement); 87 | document.title = 'Task 0'; 88 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_0/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript_dependencies", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start-dev": "webpack-dev-server --open", 8 | "build": "webpack", 9 | "test": "jest" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "@babel/plugin-proposal-export-default-from": "^7.5.2", 16 | "@babel/preset-typescript": "^7.7.2", 17 | "@types/jest": "^24.0.23", 18 | "@typescript-eslint/eslint-plugin": "^2.4.0", 19 | "@typescript-eslint/parser": "^2.4.0", 20 | "clean-webpack-plugin": "^3.0.0", 21 | "fork-ts-checker-webpack-plugin": "^1.5.1", 22 | "html-webpack-plugin": "^3.2.0", 23 | "jest": "^24.9.0", 24 | "source-map": "^0.7.3", 25 | "ts-jest": "^24.1.0", 26 | "ts-loader": "^6.2.0", 27 | "typescript": "^3.6.4", 28 | "webpack": "^4.41.2", 29 | "webpack-cli": "^3.3.9", 30 | "webpack-dev-server": "^3.8.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_0/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "sourceMap": true, 5 | "noImplicitAny": true, 6 | "module": "es6", 7 | "target": "es5", 8 | "allowJs": true, 9 | "moduleResolution": "node" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_0/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 4 | const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); 5 | 6 | module.exports = { 7 | entry: "./js/main.ts", 8 | devtool: "inline-source-map", 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.tsx?$/, 13 | loader: 'ts-loader', 14 | options: { 15 | transpileOnly: true 16 | } 17 | } 18 | ] 19 | }, 20 | resolve: { 21 | extensions: [".tsx", ".ts", ".js"] 22 | }, 23 | devServer: { 24 | contentBase: "./dist" 25 | }, 26 | plugins: [ 27 | new ForkTsCheckerWebpackPlugin(), 28 | new CleanWebpackPlugin(), 29 | new HtmlWebpackPlugin({ 30 | title: "Development" 31 | }) 32 | ], 33 | output: { 34 | filename: "bundle.js", 35 | path: path.resolve(__dirname, "dist") 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_1/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: [ 4 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from @typescript-eslint/eslint-plugin 5 | ], 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | sourceType: 'module', 9 | }, 10 | rules: { 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_1/js/main.ts: -------------------------------------------------------------------------------- 1 | export interface Teacher { 2 | readonly firstName: string; 3 | readonly lastName: string; 4 | fullTimeEmployee: boolean; 5 | yearsOfExperience?: number; 6 | location: string; 7 | [index:string]: any; 8 | } 9 | 10 | export interface Directors extends Teacher { 11 | numberOfReports: number; 12 | } 13 | 14 | export interface printTeacherFunction { 15 | (firstName: string, lastName: string): string; 16 | } 17 | 18 | export function printTeacher(firstName: string, lastName: string): string { 19 | return `${firstName[0]}. ${lastName}`; 20 | } 21 | 22 | export interface IStudentClassConstructor { 23 | new (firstName: string, lastName: string): IStudentClass; 24 | } 25 | 26 | export interface IStudentClass { 27 | workOnHomework(): string; 28 | displayName(): string; 29 | } 30 | 31 | export class StudentClass implements IStudentClass { 32 | private _firstName!: string; 33 | private _lastName!: string; 34 | 35 | constructor(firstName: string, lastName: string) { 36 | this._firstName = firstName; 37 | this._lastName = lastName; 38 | } 39 | 40 | workOnHomework() { 41 | return 'Currently working'; 42 | } 43 | 44 | displayName() { 45 | return this._firstName; 46 | } 47 | } 48 | 49 | export function createStudent(ctor: IStudentClassConstructor, firstName: string, lastName: string): IStudentClass { 50 | return new ctor(firstName, lastName); 51 | } 52 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript_dependencies", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start-dev": "webpack-dev-server --open", 8 | "build": "webpack", 9 | "test": "jest" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "@babel/plugin-proposal-export-default-from": "^7.5.2", 16 | "@babel/preset-typescript": "^7.7.2", 17 | "@types/jest": "^24.0.23", 18 | "@typescript-eslint/eslint-plugin": "^2.4.0", 19 | "@typescript-eslint/parser": "^2.4.0", 20 | "clean-webpack-plugin": "^3.0.0", 21 | "fork-ts-checker-webpack-plugin": "^1.5.1", 22 | "html-webpack-plugin": "^3.2.0", 23 | "jest": "^24.9.0", 24 | "source-map": "^0.7.3", 25 | "ts-jest": "^24.1.0", 26 | "ts-loader": "^6.2.0", 27 | "typescript": "^3.6.4", 28 | "webpack": "^4.41.2", 29 | "webpack-cli": "^3.3.9", 30 | "webpack-dev-server": "^3.8.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "sourceMap": true, 5 | "noImplicitAny": true, 6 | "module": "es6", 7 | "target": "es5", 8 | "allowJs": true, 9 | "moduleResolution": "node" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_1/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 4 | const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); 5 | 6 | module.exports = { 7 | entry: "./js/main.ts", 8 | devtool: "inline-source-map", 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.tsx?$/, 13 | loader: 'ts-loader', 14 | options: { 15 | transpileOnly: true 16 | } 17 | } 18 | ] 19 | }, 20 | resolve: { 21 | extensions: [".tsx", ".ts", ".js"] 22 | }, 23 | devServer: { 24 | contentBase: "./dist" 25 | }, 26 | plugins: [ 27 | new ForkTsCheckerWebpackPlugin(), 28 | new CleanWebpackPlugin(), 29 | new HtmlWebpackPlugin({ 30 | title: "Development" 31 | }) 32 | ], 33 | output: { 34 | filename: "bundle.js", 35 | path: path.resolve(__dirname, "dist") 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_2/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: [ 4 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from @typescript-eslint/eslint-plugin 5 | ], 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | sourceType: 'module', 9 | }, 10 | rules: { 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_2/js/main.ts: -------------------------------------------------------------------------------- 1 | export interface DirectorInterface { 2 | workFromHome(): string; 3 | getCoffeeBreak(): string; 4 | workDirectorTasks(): string; 5 | } 6 | 7 | export interface TeacherInterface { 8 | workFromHome(): string; 9 | getCoffeeBreak(): string; 10 | workTeacherTasks(): string; 11 | } 12 | 13 | export class Director implements DirectorInterface { 14 | workFromHome() { 15 | return 'Working from home'; 16 | } 17 | 18 | getCoffeeBreak() { 19 | return 'Getting a coffee break'; 20 | } 21 | 22 | workDirectorTasks() { 23 | return 'Getting to director tasks'; 24 | } 25 | } 26 | 27 | export class Teacher implements TeacherInterface { 28 | workFromHome() { 29 | return 'Cannot work from home'; 30 | } 31 | 32 | getCoffeeBreak() { 33 | return 'Cannot have a break'; 34 | } 35 | 36 | workTeacherTasks() { 37 | return 'Getting to work'; 38 | } 39 | } 40 | 41 | export function createEmployee(salary: (number | string)): (Director | Teacher) { 42 | if (typeof salary === 'number' && salary < 500) { 43 | return new Teacher(); 44 | } 45 | return new Director(); 46 | } 47 | 48 | export function isDirector(employee: (Director | Teacher)) { 49 | return employee instanceof Director; 50 | } 51 | 52 | export function executeWork(employee: (Director | Teacher)) { 53 | if (isDirector(employee)) { 54 | return (employee as Director).workDirectorTasks(); 55 | } 56 | return (employee as Teacher).workTeacherTasks(); 57 | } 58 | 59 | export type Subjects = ('Math' | 'History'); 60 | 61 | export function teachClass(todayClass: Subjects): string { 62 | if (todayClass === 'Math') { 63 | return 'Teaching Math'; 64 | } 65 | if (todayClass === 'History') { 66 | return 'Teaching History'; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript_dependencies", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start-dev": "webpack-dev-server --open", 8 | "build": "webpack", 9 | "test": "jest" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "@babel/plugin-proposal-export-default-from": "^7.5.2", 16 | "@babel/preset-typescript": "^7.7.2", 17 | "@types/jest": "^24.0.23", 18 | "@typescript-eslint/eslint-plugin": "^2.4.0", 19 | "@typescript-eslint/parser": "^2.4.0", 20 | "clean-webpack-plugin": "^3.0.0", 21 | "fork-ts-checker-webpack-plugin": "^1.5.1", 22 | "html-webpack-plugin": "^3.2.0", 23 | "jest": "^24.9.0", 24 | "source-map": "^0.7.3", 25 | "ts-jest": "^24.1.0", 26 | "ts-loader": "^6.2.0", 27 | "typescript": "^3.6.4", 28 | "webpack": "^4.41.2", 29 | "webpack-cli": "^3.3.9", 30 | "webpack-dev-server": "^3.8.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "sourceMap": true, 5 | "noImplicitAny": true, 6 | "module": "es6", 7 | "target": "es5", 8 | "allowJs": true, 9 | "moduleResolution": "node" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_2/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 4 | const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); 5 | 6 | module.exports = { 7 | entry: "./js/main.ts", 8 | devtool: "inline-source-map", 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.tsx?$/, 13 | loader: 'ts-loader', 14 | options: { 15 | transpileOnly: true 16 | } 17 | } 18 | ] 19 | }, 20 | resolve: { 21 | extensions: [".tsx", ".ts", ".js"] 22 | }, 23 | devServer: { 24 | contentBase: "./dist" 25 | }, 26 | plugins: [ 27 | new ForkTsCheckerWebpackPlugin(), 28 | new CleanWebpackPlugin(), 29 | new HtmlWebpackPlugin({ 30 | title: "Development" 31 | }) 32 | ], 33 | output: { 34 | filename: "bundle.js", 35 | path: path.resolve(__dirname, "dist") 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_3/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: [ 4 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from @typescript-eslint/eslint-plugin 5 | ], 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | sourceType: 'module', 9 | }, 10 | rules: { 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_3/js/crud.d.ts: -------------------------------------------------------------------------------- 1 | import { RowID, RowElement } from './interface'; 2 | 3 | export function insertRow(row: RowElement): number; 4 | 5 | export function deleteRow(rowId: RowID): void; 6 | 7 | export function updateRow(rowId: RowID, row: RowElement): number; 8 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_3/js/crud.js: -------------------------------------------------------------------------------- 1 | export function insertRow(row) { 2 | console.log('Insert row', row); 3 | return Math.floor(Math.random() * Math.floor(1000)); 4 | } 5 | 6 | export function deleteRow(rowId) { 7 | console.log('Delete row id', rowId); 8 | return; 9 | } 10 | 11 | export function updateRow(rowId, row) { 12 | console.log(`Update row ${rowId}`, row); 13 | 14 | return rowId; 15 | } 16 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_3/js/interface.ts: -------------------------------------------------------------------------------- 1 | export type RowID = number; 2 | 3 | export interface RowElement { 4 | firstName: string; 5 | lastName: string; 6 | age?: number; 7 | } 8 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_3/js/main.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { RowID, RowElement } from './interface'; 3 | import * as CRUD from './crud'; 4 | 5 | const row: RowElement = { 6 | firstName: 'Guillaume', 7 | lastName: 'Salva', 8 | }; 9 | const newRowID: RowID = CRUD.insertRow(row); 10 | const updatedRow: RowElement = {...row, age: 23}; 11 | CRUD.updateRow(newRowID, updatedRow); 12 | CRUD.deleteRow(newRowID); 13 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript_dependencies", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start-dev": "webpack-dev-server --open", 8 | "build": "webpack", 9 | "test": "jest" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "@babel/plugin-proposal-export-default-from": "^7.5.2", 16 | "@babel/preset-typescript": "^7.7.2", 17 | "@types/jest": "^24.0.23", 18 | "@typescript-eslint/eslint-plugin": "^2.4.0", 19 | "@typescript-eslint/parser": "^2.4.0", 20 | "clean-webpack-plugin": "^3.0.0", 21 | "fork-ts-checker-webpack-plugin": "^1.5.1", 22 | "html-webpack-plugin": "^3.2.0", 23 | "jest": "^24.9.0", 24 | "source-map": "^0.7.3", 25 | "ts-jest": "^24.1.0", 26 | "ts-loader": "^6.2.0", 27 | "typescript": "^3.6.4", 28 | "webpack": "^4.41.2", 29 | "webpack-cli": "^3.3.9", 30 | "webpack-dev-server": "^3.8.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "sourceMap": true, 5 | "noImplicitAny": true, 6 | "module": "es6", 7 | "target": "es5", 8 | "allowJs": true, 9 | "moduleResolution": "node" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_3/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 4 | const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); 5 | 6 | module.exports = { 7 | entry: "./js/main.ts", 8 | devtool: "inline-source-map", 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.tsx?$/, 13 | loader: 'ts-loader', 14 | options: { 15 | transpileOnly: true 16 | } 17 | } 18 | ] 19 | }, 20 | resolve: { 21 | extensions: [".tsx", ".ts", ".js"] 22 | }, 23 | devServer: { 24 | contentBase: "./dist" 25 | }, 26 | plugins: [ 27 | new ForkTsCheckerWebpackPlugin(), 28 | new CleanWebpackPlugin(), 29 | new HtmlWebpackPlugin({ 30 | title: "Development" 31 | }) 32 | ], 33 | output: { 34 | filename: "bundle.js", 35 | path: path.resolve(__dirname, "dist") 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_4/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: [ 4 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from @typescript-eslint/eslint-plugin 5 | ], 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | sourceType: 'module', 9 | }, 10 | rules: { 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_4/js/main.ts: -------------------------------------------------------------------------------- 1 | export const cpp: Subjects.Cpp = new Subjects.Cpp(); 2 | export const java: Subjects.Java = new Subjects.Java(); 3 | export const react: Subjects.React = new Subjects.React(); 4 | export const cTeacher: Subjects.Teacher = { 5 | firstName: 'Dennis', 6 | lastName: 'Ritchie', 7 | experienceTeachingC: 10, 8 | }; 9 | 10 | console.log('C++'); 11 | cpp.setTeacher = cTeacher; 12 | console.log(cpp.getRequirements()); 13 | console.log(cpp.getAvailableTeacher()); 14 | 15 | console.log('Java'); 16 | java.setTeacher = cTeacher; 17 | console.log(java.getRequirements()); 18 | console.log(java.getAvailableTeacher()); 19 | 20 | console.log('React'); 21 | react.setTeacher = cTeacher; 22 | console.log(react.getRequirements()); 23 | console.log(react.getAvailableTeacher()); 24 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_4/js/subjects/Cpp.ts: -------------------------------------------------------------------------------- 1 | namespace Subjects { 2 | export interface Teacher { 3 | experienceTeachingC?: number; 4 | } 5 | 6 | export class Cpp extends Subjects.Subject { 7 | getRequirements(): string { 8 | return 'Here is the list of requirements for Cpp'; 9 | } 10 | 11 | getAvailableTeacher(): string { 12 | if (!this.teacher || this.teacher.experienceTeachingC <= 0) { 13 | return 'No available teacher'; 14 | } 15 | return `Available Teacher: ${this.teacher.firstName}`; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_4/js/subjects/Java.ts: -------------------------------------------------------------------------------- 1 | namespace Subjects { 2 | export interface Teacher { 3 | experienceTeachingJava?: number; 4 | } 5 | 6 | export class Java extends Subjects.Subject { 7 | getRequirements(): string { 8 | return 'Here is the list of requirements for Java'; 9 | } 10 | 11 | getAvailableTeacher(): string { 12 | if (!this.teacher || this.teacher.experienceTeachingJava <= 0) { 13 | return 'No available teacher'; 14 | } 15 | return `Available Teacher: ${this.teacher.firstName}`; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_4/js/subjects/React.ts: -------------------------------------------------------------------------------- 1 | namespace Subjects { 2 | export interface Teacher { 3 | experienceTeachingReact?: number; 4 | } 5 | 6 | export class React extends Subjects.Subject { 7 | getRequirements(): string { 8 | return 'Here is the list of requirements for React'; 9 | } 10 | 11 | getAvailableTeacher(): string { 12 | if (!this.teacher || this.teacher.experienceTeachingReact <= 0) { 13 | return 'No available teacher'; 14 | } 15 | return `Available Teacher: ${this.teacher.firstName}`; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_4/js/subjects/Subject.ts: -------------------------------------------------------------------------------- 1 | namespace Subjects { 2 | export class Subject { 3 | teacher: Subjects.Teacher; 4 | 5 | set setTeacher(teacher: Subjects.Teacher) { 6 | this.teacher = teacher; 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_4/js/subjects/Teacher.ts: -------------------------------------------------------------------------------- 1 | namespace Subjects { 2 | export interface Teacher { 3 | firstName: string; 4 | lastName: string; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_4/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "task_4", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start-dev": "webpack-dev-server --open", 8 | "build": "webpack", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "@babel/plugin-proposal-export-default-from": "^7.5.2", 16 | "@babel/preset-typescript": "^7.7.2", 17 | "@types/jest": "^24.0.23", 18 | "@typescript-eslint/eslint-plugin": "^2.4.0", 19 | "@typescript-eslint/parser": "^2.4.0", 20 | "clean-webpack-plugin": "^3.0.0", 21 | "fork-ts-checker-webpack-plugin": "^1.5.1", 22 | "html-webpack-plugin": "^3.2.0", 23 | "jest": "^24.9.0", 24 | "source-map": "^0.7.3", 25 | "ts-jest": "^24.1.0", 26 | "ts-loader": "^6.2.0", 27 | "typescript": "^3.6.4", 28 | "webpack": "^4.41.2", 29 | "webpack-cli": "^3.3.9", 30 | "webpack-dev-server": "^3.8.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_4/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "sourceMap": true, 5 | "noImplicitAny": true, 6 | "module": "es6", 7 | "target": "es5", 8 | "allowJs": true, 9 | "moduleResolution": "node" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_4/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 4 | const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); 5 | 6 | module.exports = { 7 | entry: "./js/main.ts", 8 | devtool: "inline-source-map", 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.tsx?$/, 13 | loader: 'ts-loader', 14 | options: { 15 | transpileOnly: true 16 | } 17 | } 18 | ] 19 | }, 20 | resolve: { 21 | extensions: [".tsx", ".ts", ".js"] 22 | }, 23 | devServer: { 24 | contentBase: "./dist" 25 | }, 26 | plugins: [ 27 | new ForkTsCheckerWebpackPlugin(), 28 | new CleanWebpackPlugin(), 29 | new HtmlWebpackPlugin({ 30 | title: "Development" 31 | }) 32 | ], 33 | output: { 34 | filename: "bundle.js", 35 | path: path.resolve(__dirname, "dist") 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_5/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: [ 4 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from @typescript-eslint/eslint-plugin 5 | ], 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | sourceType: 'module', 9 | }, 10 | rules: { 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_5/js/main.ts: -------------------------------------------------------------------------------- 1 | export interface MajorCredits { 2 | credits: number & { __brand: 'MajorCredits.credits' }; 3 | } 4 | 5 | export interface MinorCredits { 6 | credits: number & { __brand: 'MinorCredits.credits' }; 7 | } 8 | 9 | export function sumMajorCredits(subject1: MajorCredits, subject2: MajorCredits): MajorCredits { 10 | return { credits: subject1.credits + subject2.credits } as MajorCredits; 11 | } 12 | 13 | export function sumMinorCredits(subject1: MinorCredits, subject2: MinorCredits): MinorCredits { 14 | return { credits: subject1.credits + subject2.credits } as MinorCredits; 15 | } 16 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_5/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript_dependencies", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start-dev": "webpack-dev-server --open", 8 | "build": "webpack", 9 | "test": "jest" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "@babel/plugin-proposal-export-default-from": "^7.5.2", 16 | "@babel/preset-typescript": "^7.7.2", 17 | "@types/jest": "^24.0.23", 18 | "@typescript-eslint/eslint-plugin": "^2.4.0", 19 | "@typescript-eslint/parser": "^2.4.0", 20 | "clean-webpack-plugin": "^3.0.0", 21 | "fork-ts-checker-webpack-plugin": "^1.5.1", 22 | "html-webpack-plugin": "^3.2.0", 23 | "jest": "^24.9.0", 24 | "source-map": "^0.7.3", 25 | "ts-jest": "^24.1.0", 26 | "ts-loader": "^6.2.0", 27 | "typescript": "^3.6.4", 28 | "webpack": "^4.41.2", 29 | "webpack-cli": "^3.3.9", 30 | "webpack-dev-server": "^3.8.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_5/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "sourceMap": true, 5 | "noImplicitAny": true, 6 | "module": "es6", 7 | "target": "es5", 8 | "allowJs": true, 9 | "moduleResolution": "node" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /0x04-TypeScript/task_5/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 4 | const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); 5 | 6 | module.exports = { 7 | entry: "./js/main.ts", 8 | devtool: "inline-source-map", 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.tsx?$/, 13 | loader: 'ts-loader', 14 | options: { 15 | transpileOnly: true 16 | } 17 | } 18 | ] 19 | }, 20 | resolve: { 21 | extensions: [".tsx", ".ts", ".js"] 22 | }, 23 | devServer: { 24 | contentBase: "./dist" 25 | }, 26 | plugins: [ 27 | new ForkTsCheckerWebpackPlugin(), 28 | new CleanWebpackPlugin(), 29 | new HtmlWebpackPlugin({ 30 | title: "Development" 31 | }) 32 | ], 33 | output: { 34 | filename: "bundle.js", 35 | path: path.resolve(__dirname, "dist") 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["env", {"exclude": ["transform-regenerator"]}]] 3 | } 4 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: false, 4 | es6: true, 5 | jest: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | 'plugin:jest/all', 10 | ], 11 | globals: { 12 | Atomics: 'readonly', 13 | SharedArrayBuffer: 'readonly', 14 | }, 15 | parserOptions: { 16 | ecmaVersion: 2018, 17 | sourceType: 'module', 18 | }, 19 | plugins: ['jest'], 20 | rules: { 21 | 'max-classes-per-file': 'off', 22 | 'no-underscore-dangle': 'off', 23 | 'no-console': 'off', 24 | 'no-shadow': 'off', 25 | 'no-restricted-syntax': [ 26 | 'error', 27 | 'LabeledStatement', 28 | 'WithStatement', 29 | ], 30 | }, 31 | overrides:[ 32 | { 33 | files: ['*.js'], 34 | excludedFiles: 'babel.config.js', 35 | } 36 | ] 37 | }; 38 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/0-console.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Displays a message on the STDOUT. 3 | * @param {String} msg The message to display. 4 | * @author Bezaleel Olakunori 5 | */ 6 | const displayMessage = (msg) => { 7 | console.log(msg); 8 | }; 9 | 10 | module.exports = displayMessage; 11 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/1-stdin.js: -------------------------------------------------------------------------------- 1 | process.stdout.write('Welcome to Holberton School, what is your name?\n'); 2 | 3 | process.stdin.on('readable', () => { 4 | const chunk = process.stdin.read(); 5 | 6 | if (chunk) { 7 | process.stdout.write(`Your name is: ${chunk}`); 8 | } 9 | }); 10 | 11 | process.stdin.on('end', () => { 12 | process.stdout.write('This important software is now closing\n'); 13 | }); 14 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/2-read_file.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | /** 4 | * Counts the students in a CSV data file. 5 | * @param {String} dataPath The path to the CSV data file. 6 | * @author Bezaleel Olakunori 7 | */ 8 | const countStudents = (dataPath) => { 9 | if (!fs.existsSync(dataPath)) { 10 | throw new Error('Cannot load the database'); 11 | } 12 | if (!fs.statSync(dataPath).isFile()) { 13 | throw new Error('Cannot load the database'); 14 | } 15 | const fileLines = fs 16 | .readFileSync(dataPath, 'utf-8') 17 | .toString('utf-8') 18 | .trim() 19 | .split('\n'); 20 | const studentGroups = {}; 21 | const dbFieldNames = fileLines[0].split(','); 22 | const studentPropNames = dbFieldNames.slice(0, dbFieldNames.length - 1); 23 | 24 | for (const line of fileLines.slice(1)) { 25 | const studentRecord = line.split(','); 26 | const studentPropValues = studentRecord.slice(0, studentRecord.length - 1); 27 | const field = studentRecord[studentRecord.length - 1]; 28 | if (!Object.keys(studentGroups).includes(field)) { 29 | studentGroups[field] = []; 30 | } 31 | const studentEntries = studentPropNames 32 | .map((propName, idx) => [propName, studentPropValues[idx]]); 33 | studentGroups[field].push(Object.fromEntries(studentEntries)); 34 | } 35 | 36 | const totalStudents = Object 37 | .values(studentGroups) 38 | .reduce((pre, cur) => (pre || []).length + cur.length); 39 | console.log(`Number of students: ${totalStudents}`); 40 | for (const [field, group] of Object.entries(studentGroups)) { 41 | const studentNames = group.map((student) => student.firstname).join(', '); 42 | console.log(`Number of students in ${field}: ${group.length}. List: ${studentNames}`); 43 | } 44 | }; 45 | 46 | module.exports = countStudents; 47 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/3-read_file_async.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | /** 4 | * Counts the students in a CSV data file. 5 | * @param {String} dataPath The path to the CSV data file. 6 | * @author Bezaleel Olakunori 7 | */ 8 | const countStudents = (dataPath) => new Promise((resolve, reject) => { 9 | fs.readFile(dataPath, 'utf-8', (err, data) => { 10 | if (err) { 11 | reject(new Error('Cannot load the database')); 12 | } 13 | if (data) { 14 | const fileLines = data 15 | .toString('utf-8') 16 | .trim() 17 | .split('\n'); 18 | const studentGroups = {}; 19 | const dbFieldNames = fileLines[0].split(','); 20 | const studentPropNames = dbFieldNames 21 | .slice(0, dbFieldNames.length - 1); 22 | 23 | for (const line of fileLines.slice(1)) { 24 | const studentRecord = line.split(','); 25 | const studentPropValues = studentRecord 26 | .slice(0, studentRecord.length - 1); 27 | const field = studentRecord[studentRecord.length - 1]; 28 | if (!Object.keys(studentGroups).includes(field)) { 29 | studentGroups[field] = []; 30 | } 31 | const studentEntries = studentPropNames 32 | .map((propName, idx) => [propName, studentPropValues[idx]]); 33 | studentGroups[field].push(Object.fromEntries(studentEntries)); 34 | } 35 | 36 | const totalStudents = Object 37 | .values(studentGroups) 38 | .reduce((pre, cur) => (pre || []).length + cur.length); 39 | console.log(`Number of students: ${totalStudents}`); 40 | for (const [field, group] of Object.entries(studentGroups)) { 41 | const studentNames = group.map((student) => student.firstname).join(', '); 42 | console.log(`Number of students in ${field}: ${group.length}. List: ${studentNames}`); 43 | } 44 | resolve(true); 45 | } 46 | }); 47 | }); 48 | 49 | module.exports = countStudents; 50 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/4-http.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | const PORT = 1245; 4 | const HOST = 'localhost'; 5 | const app = http.createServer(); 6 | 7 | app.on('request', (_, res) => { 8 | const responseText = 'Hello Holberton School!'; 9 | 10 | res.setHeader('Content-Type', 'text/plain'); 11 | res.setHeader('Content-Length', responseText.length); 12 | res.statusCode = 200; 13 | res.write(Buffer.from(responseText)); 14 | }); 15 | 16 | app.listen(PORT, HOST, () => { 17 | process.stdout.write(`Server listening at -> http://${HOST}:${PORT}\n`); 18 | }); 19 | 20 | module.exports = app; 21 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/5-http.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const fs = require('fs'); 3 | 4 | const PORT = 1245; 5 | const HOST = 'localhost'; 6 | const app = http.createServer(); 7 | const DB_FILE = process.argv.length > 2 ? process.argv[2] : ''; 8 | 9 | /** 10 | * Counts the students in a CSV data file. 11 | * @param {String} dataPath The path to the CSV data file. 12 | * @author Bezaleel Olakunori 13 | */ 14 | const countStudents = (dataPath) => new Promise((resolve, reject) => { 15 | if (!dataPath) { 16 | reject(new Error('Cannot load the database')); 17 | } 18 | if (dataPath) { 19 | fs.readFile(dataPath, (err, data) => { 20 | if (err) { 21 | reject(new Error('Cannot load the database')); 22 | } 23 | if (data) { 24 | const reportParts = []; 25 | const fileLines = data.toString('utf-8').trim().split('\n'); 26 | const studentGroups = {}; 27 | const dbFieldNames = fileLines[0].split(','); 28 | const studentPropNames = dbFieldNames.slice( 29 | 0, 30 | dbFieldNames.length - 1, 31 | ); 32 | 33 | for (const line of fileLines.slice(1)) { 34 | const studentRecord = line.split(','); 35 | const studentPropValues = studentRecord.slice( 36 | 0, 37 | studentRecord.length - 1, 38 | ); 39 | const field = studentRecord[studentRecord.length - 1]; 40 | if (!Object.keys(studentGroups).includes(field)) { 41 | studentGroups[field] = []; 42 | } 43 | const studentEntries = studentPropNames.map((propName, idx) => [ 44 | propName, 45 | studentPropValues[idx], 46 | ]); 47 | studentGroups[field].push(Object.fromEntries(studentEntries)); 48 | } 49 | 50 | const totalStudents = Object.values(studentGroups).reduce( 51 | (pre, cur) => (pre || []).length + cur.length, 52 | ); 53 | reportParts.push(`Number of students: ${totalStudents}`); 54 | for (const [field, group] of Object.entries(studentGroups)) { 55 | reportParts.push([ 56 | `Number of students in ${field}: ${group.length}.`, 57 | 'List:', 58 | group.map((student) => student.firstname).join(', '), 59 | ].join(' ')); 60 | } 61 | resolve(reportParts.join('\n')); 62 | } 63 | }); 64 | } 65 | }); 66 | 67 | const SERVER_ROUTE_HANDLERS = [ 68 | { 69 | route: '/', 70 | handler(_, res) { 71 | const responseText = 'Hello Holberton School!'; 72 | 73 | res.setHeader('Content-Type', 'text/plain'); 74 | res.setHeader('Content-Length', responseText.length); 75 | res.statusCode = 200; 76 | res.write(Buffer.from(responseText)); 77 | }, 78 | }, 79 | { 80 | route: '/students', 81 | handler(_, res) { 82 | const responseParts = ['This is the list of our students']; 83 | 84 | countStudents(DB_FILE) 85 | .then((report) => { 86 | responseParts.push(report); 87 | const responseText = responseParts.join('\n'); 88 | res.setHeader('Content-Type', 'text/plain'); 89 | res.setHeader('Content-Length', responseText.length); 90 | res.statusCode = 200; 91 | res.write(Buffer.from(responseText)); 92 | }) 93 | .catch((err) => { 94 | responseParts.push(err instanceof Error ? err.message : err.toString()); 95 | const responseText = responseParts.join('\n'); 96 | res.setHeader('Content-Type', 'text/plain'); 97 | res.setHeader('Content-Length', responseText.length); 98 | res.statusCode = 200; 99 | res.write(Buffer.from(responseText)); 100 | }); 101 | }, 102 | }, 103 | ]; 104 | 105 | app.on('request', (req, res) => { 106 | for (const routeHandler of SERVER_ROUTE_HANDLERS) { 107 | if (routeHandler.route === req.url) { 108 | routeHandler.handler(req, res); 109 | break; 110 | } 111 | } 112 | }); 113 | 114 | app.listen(PORT, HOST, () => { 115 | process.stdout.write(`Server listening at -> http://${HOST}:${PORT}\n`); 116 | }); 117 | 118 | module.exports = app; 119 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/6-http_express.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const app = express(); 4 | const PORT = 1245; 5 | 6 | app.get('/', (_, res) => { 7 | res.send('Hello Holberton School!'); 8 | }); 9 | 10 | app.listen(PORT, () => { 11 | console.log(`Server listening on PORT ${PORT}`); 12 | }); 13 | 14 | module.exports = app; 15 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/7-http_express.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const fs = require('fs'); 3 | 4 | const app = express(); 5 | const PORT = 1245; 6 | const DB_FILE = process.argv.length > 2 ? process.argv[2] : ''; 7 | 8 | /** 9 | * Counts the students in a CSV data file. 10 | * @param {String} dataPath The path to the CSV data file. 11 | * @author Bezaleel Olakunori 12 | */ 13 | const countStudents = (dataPath) => new Promise((resolve, reject) => { 14 | if (!dataPath) { 15 | reject(new Error('Cannot load the database')); 16 | } 17 | if (dataPath) { 18 | fs.readFile(dataPath, (err, data) => { 19 | if (err) { 20 | reject(new Error('Cannot load the database')); 21 | } 22 | if (data) { 23 | const reportParts = []; 24 | const fileLines = data.toString('utf-8').trim().split('\n'); 25 | const studentGroups = {}; 26 | const dbFieldNames = fileLines[0].split(','); 27 | const studentPropNames = dbFieldNames.slice( 28 | 0, 29 | dbFieldNames.length - 1, 30 | ); 31 | 32 | for (const line of fileLines.slice(1)) { 33 | const studentRecord = line.split(','); 34 | const studentPropValues = studentRecord.slice( 35 | 0, 36 | studentRecord.length - 1, 37 | ); 38 | const field = studentRecord[studentRecord.length - 1]; 39 | if (!Object.keys(studentGroups).includes(field)) { 40 | studentGroups[field] = []; 41 | } 42 | const studentEntries = studentPropNames.map((propName, idx) => [ 43 | propName, 44 | studentPropValues[idx], 45 | ]); 46 | studentGroups[field].push(Object.fromEntries(studentEntries)); 47 | } 48 | 49 | const totalStudents = Object.values(studentGroups).reduce( 50 | (pre, cur) => (pre || []).length + cur.length, 51 | ); 52 | reportParts.push(`Number of students: ${totalStudents}`); 53 | for (const [field, group] of Object.entries(studentGroups)) { 54 | reportParts.push([ 55 | `Number of students in ${field}: ${group.length}.`, 56 | 'List:', 57 | group.map((student) => student.firstname).join(', '), 58 | ].join(' ')); 59 | } 60 | resolve(reportParts.join('\n')); 61 | } 62 | }); 63 | } 64 | }); 65 | 66 | app.get('/', (_, res) => { 67 | res.send('Hello Holberton School!'); 68 | }); 69 | 70 | app.get('/students', (_, res) => { 71 | const responseParts = ['This is the list of our students']; 72 | 73 | countStudents(DB_FILE) 74 | .then((report) => { 75 | responseParts.push(report); 76 | const responseText = responseParts.join('\n'); 77 | res.setHeader('Content-Type', 'text/plain'); 78 | res.setHeader('Content-Length', responseText.length); 79 | res.statusCode = 200; 80 | res.write(Buffer.from(responseText)); 81 | }) 82 | .catch((err) => { 83 | responseParts.push(err instanceof Error ? err.message : err.toString()); 84 | const responseText = responseParts.join('\n'); 85 | res.setHeader('Content-Type', 'text/plain'); 86 | res.setHeader('Content-Length', responseText.length); 87 | res.statusCode = 200; 88 | res.write(Buffer.from(responseText)); 89 | }); 90 | }); 91 | 92 | app.listen(PORT, () => { 93 | console.log(`Server listening on PORT ${PORT}`); 94 | }); 95 | 96 | module.exports = app; 97 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/README.md: -------------------------------------------------------------------------------- 1 | # NodeJS Basics 2 | 3 | This project contains tasks for learning to the basics of NodeJS. 4 | 5 | ## Tasks To Complete 6 | 7 | + [x] 0. **Executing basic javascript with Node JS**
[0-console.js](0-console.js) contains a module that exports a function named `displayMessage` that prints in `STDOUT` the string argument. 8 | 9 | + [x] 1. **Using Process stdin**
[1-stdin.js](1-stdin.js) contains a script that will be executed through the command line with the following requirements: 10 | + It should display the message `Welcome to Holberton School, what is your name?` (followed by a new line). 11 | + The user should be able to input their name on a new line. 12 | + The program should display `Your name is: INPUT`. 13 | + When the user ends the program, it should display `This important software is now closing` (followed by a new line). 14 | 15 | + [x] 2. **Reading a file synchronously with Node JS**
[2-read_file.js](2-read_file.js) contains a module that exports a function `countStudents` with the following requirements: 16 | + Create a function named `countStudents`. It should accept a path in argument. 17 | + The script should attempt to read the database file synchronously. 18 | + The database file has the same format as [database.csv](database.csv). 19 | + If the database is not available, it should throw an error with the text `Cannot load the database`. 20 | + If the database is available, it should log the following message to the console `Number of students: NUMBER_OF_STUDENTS`. 21 | + It should log the number of students in each field, and the list with the following format: `Number of students in FIELD: 6. List: LIST_OF_FIRSTNAMES`. 22 | + CSV file can contain empty lines (at the end) - and they are not a valid student! 23 | 24 | + [x] 3. **Reading a file asynchronously with Node JS**
[3-read_file_async.js](3-read_file_async.js) contains a module that exports a function `countStudents` with the following requirements: 25 | + Create a function named `countStudents`. It should accept a path in argument (same as in [2-read_file.js](2-read_file.js)). 26 | + The script should attempt to read the database file asynchronously. 27 | + The database file has the same format as [database.csv](database.csv). 28 | + The function should return a Promise. 29 | + If the database is not available, it should throw an error with the text `Cannot load the database`. 30 | + If the database is available, it should log the following message to the console `Number of students: NUMBER_OF_STUDENTS`. 31 | + It should log the number of students in each field, and the list with the following format: `Number of students in FIELD: 6. List: LIST_OF_FIRSTNAMES`. 32 | + CSV file can contain empty lines (at the end) - and they are not a valid student! 33 | 34 | + [x] 4. **Create a small HTTP server using Node's HTTP module**
[4-http.js](4-http.js) contains a script that creates and exports a small HTTP server using the `http` module with the following requirements: 35 | + It should be assigned to the variable `app`, which must be exported. 36 | + HTTP server should listen on port 1245. 37 | + Displays `Hello Holberton School!` in the page body for any endpoint as plain text. 38 | 39 | + [x] 5. **Create a more complex HTTP server using Node's HTTP module**
[5-http.js](5-http.js) contains a script that creates and exports a small HTTP server using the `http` module with the following requirements: 40 | + It should be assigned to the variable `app`, which must be exported. 41 | + HTTP server should listen on port 1245. 42 | + It should return plain text. 43 | + When the URL path is `/`, it should display `Hello Holberton School!` in the page body. 44 | + When the URL path is `/students`, it should display `This is the list of our students` followed by the same content as the file [3-read_file_async.js](3-read_file_async.js) (with and without the database) - the name of the database must be passed as argument of the file. 45 | + CSV file can contain empty lines (at the end) - and they are not a valid student! 46 | 47 | + [x] 6. **Create a small HTTP server using Express**
[6-http_express.js](6-http_express.js) contains a script that creates and exports a small HTTP server using the Express module with the following requirements: 48 | + It should be assigned to the variable `app`, which must be exported. 49 | + HTTP server should listen on port 1245. 50 | + Displays `Hello Holberton School!` in the page body for the endpoint `/`. 51 | 52 | + [x] 7. **Create a more complex HTTP server using Express**
[7-http_express.js](7-http_express.js) contains a script creates and exports a small HTTP server using the Express module with the following requirements: 53 | + It should be assigned to the variable `app`, which must be exported. 54 | + HTTP server should listen on port 1245. 55 | + It should return plain text. 56 | + When the URL path is `/`, it should display Hello Holberton School! in the page body. 57 | + When the URL path is `/students`, it should display This is the list of our students followed by the same content as the file [3-read_file_async.js](3-read_file_async.js) (with and without the database) - the name of the database must be passed as argument of the file. 58 | + CSV file can contain empty lines (at the end) - and they are not a valid student! 59 | 60 | + [x] 8. **Organize a complex HTTP server using Express**
Writing every part of a server within a single file is not sustainable. Create a full server in a directory named [`full_server`](full_server) with the requirements listed below.
Since you have used ES6 and Babel in the past projects, let's use `babel-node` to allow to use ES6 functions like `import` or `export`. 61 | + **8.1 Organize the structure of the server** 62 | + Create 2 directories within the [full_server](full_server) folder: 63 | + [controllers](full_server/controllers/). 64 | + [routes](full_server/routes/). 65 | + Create a file [full_server/utils.js](full_server/utils.js) with a function named readDatabase that accepts a file path as an argument: 66 | + It should read the database asynchronously. 67 | + It should return a promise. 68 | + When the file is not accessible, it should reject the promise with the error. 69 | + When the file can be read, it should return an object of arrays of the firstname of students per field. 70 | + **8.2 Write the App controller**
Inside the file [full_server/controllers/AppController.js](full_server/controllers/AppController.js): 71 | + Create a class named `AppController`. Add a static method named `getHomepage`. 72 | + The method accepts `request` and `response` as argument. It returns a 200 status and the message `Hello Holberton School!`. 73 | + **8.3 Write the Students controller**
Inside the file [full_server/controllers/StudentsController.js](full_server/controllers/StudentsController.js), create a class named `StudentsController`. Add two static methods: 74 | + The first one is `getAllStudents`: 75 | + The method accepts `request` and `response` as argument. 76 | + It should return a status 200. 77 | + It calls the function `readDatabase` from the [utils](full_server/utils.js) file, and display in the page: 78 | + First line: `This is the list of our students`. 79 | + And for each field (order by alphabetic order case insensitive), a line that displays the number of students in the field, and the list of first names (ordered by appearance in the database file) with the following format: `Number of students in FIELD: 6. List: LIST_OF_FIRSTNAMES`. 80 | + If the database is not available, it should return a status 500 and the error message `Cannot load the database`. 81 | + The second one is `getAllStudentsByMajor`: 82 | + The method accepts `request` and `response` as argument. 83 | + It should return a status 200. 84 | + It uses a parameter that the user can pass to the browser `major`. The `major` can only be `CS` or `SWE`. If the user is passing another parameter, the server should return a 500 and the error `Major parameter must be CS or SWE`. 85 | + It calls the function `readDatabase` from the [utils](full_server/utils.js) file, and displays in the page the list of first names for the students (ordered by appearance in the database file) in the specified field `List: LIST_OF_FIRSTNAMES_IN_THE_FIELD`. 86 | + If the database is not available, it should return a status 500 and the error message `Cannot load the database`. 87 | + **8.4 Write the routes**
Inside the file [full_server/routes/index.js](full_server/routes/index.js): 88 | + Link the route `/` to the `AppController`. 89 | + Link the route `/students` and `/students/:major` to the `StudentsController`. 90 | + **8.5 Write the server reusing everything you created**
Inside the file named [full_server/server.js](full_server/server.js), create a small Express server: 91 | + It should use the routes defined in [full_server/routes/index.js](full_server/routes/index.js). 92 | + It should use the port `1245`. 93 | + **8.6 Update `package.json` (if you are running it from inside the folder `full_server`)** 94 | + If you are starting node from inside of the folder [full_server](full_server/), you will have to update the command `dev` by: `nodemon --exec babel-node --presets babel-preset-env ./server.js ../database.csv`. 95 | + **Warning:** 96 | + Don't forget to export your express app at the end of [server.js](full_server/server.js) (`export default app;`). 97 | + The database filename is passed as argument of the [server.js](full_server/server.js) BUT, for the purpose of testing, you should retrieve this filename at the execution or when it's needed (when `getAllStudents` or `getAllStudentsByMajor` are called for example). 98 | + If you want to add test to validate your integration, you will need to add this file: [.babelrc](.babelrc). 99 | ```js 100 | { 101 | "presets": [["env", {"exclude": ["transform-regenerator"]}]] 102 | } 103 | ``` 104 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current', 8 | }, 9 | }, 10 | ], 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/database.csv: -------------------------------------------------------------------------------- 1 | firstname,lastname,age,field 2 | Johann,Kerbrou,30,CS 3 | Guillaume,Salou,30,SWE 4 | Arielle,Salou,20,CS 5 | Jonathan,Benou,30,CS 6 | Emmanuel,Turlou,40,CS 7 | Guillaume,Plessous,35,CS 8 | Joseph,Crisou,34,SWE 9 | Paul,Schneider,60,SWE 10 | Tommy,Schoul,32,SWE 11 | Katie,Shirou,21,CS 12 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/full_server/controllers/AppController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains the miscellaneous route handlers. 3 | * @author Bezaleel Olakunori 4 | */ 5 | class AppController { 6 | static getHomepage(request, response) { 7 | response.status(200).send('Hello Holberton School!'); 8 | } 9 | } 10 | 11 | export default AppController; 12 | module.exports = AppController; 13 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/full_server/controllers/StudentsController.js: -------------------------------------------------------------------------------- 1 | import readDatabase from '../utils'; 2 | 3 | /** 4 | * The list of supported majors. 5 | */ 6 | const VALID_MAJORS = ['CS', 'SWE']; 7 | 8 | /** 9 | * Contains the student-related route handlers. 10 | * @author Bezaleel Olakunori 11 | */ 12 | class StudentsController { 13 | static getAllStudents(request, response) { 14 | const dataPath = process.argv.length > 2 ? process.argv[2] : ''; 15 | 16 | readDatabase(dataPath) 17 | .then((studentGroups) => { 18 | const responseParts = ['This is the list of our students']; 19 | // A comparison function for ordering a list of strings in ascending 20 | // order by alphabetic order and case insensitive 21 | const cmpFxn = (a, b) => { 22 | if (a[0].toLowerCase() < b[0].toLowerCase()) { 23 | return -1; 24 | } 25 | if (a[0].toLowerCase() > b[0].toLowerCase()) { 26 | return 1; 27 | } 28 | return 0; 29 | }; 30 | 31 | for (const [field, group] of Object.entries(studentGroups).sort(cmpFxn)) { 32 | responseParts.push([ 33 | `Number of students in ${field}: ${group.length}.`, 34 | 'List:', 35 | group.map((student) => student.firstname).join(', '), 36 | ].join(' ')); 37 | } 38 | response.status(200).send(responseParts.join('\n')); 39 | }) 40 | .catch((err) => { 41 | response 42 | .status(500) 43 | .send(err instanceof Error ? err.message : err.toString()); 44 | }); 45 | } 46 | 47 | static getAllStudentsByMajor(request, response) { 48 | const dataPath = process.argv.length > 2 ? process.argv[2] : ''; 49 | const { major } = request.params; 50 | 51 | if (!VALID_MAJORS.includes(major)) { 52 | response.status(500).send('Major parameter must be CS or SWE'); 53 | return; 54 | } 55 | readDatabase(dataPath) 56 | .then((studentGroups) => { 57 | let responseText = ''; 58 | 59 | if (Object.keys(studentGroups).includes(major)) { 60 | const group = studentGroups[major]; 61 | responseText = `List: ${group.map((student) => student.firstname).join(', ')}`; 62 | } 63 | response.status(200).send(responseText); 64 | }) 65 | .catch((err) => { 66 | response 67 | .status(500) 68 | .send(err instanceof Error ? err.message : err.toString()); 69 | }); 70 | } 71 | } 72 | 73 | export default StudentsController; 74 | module.exports = StudentsController; 75 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/full_server/routes/index.js: -------------------------------------------------------------------------------- 1 | import AppController from '../controllers/AppController'; 2 | import StudentsController from '../controllers/StudentsController'; 3 | 4 | /** 5 | * Binds the routes to the appropriate handler in the 6 | * given Express application. 7 | * @param {Express} app The Express application. 8 | * @author Bezaleel Olakunori 9 | */ 10 | const mapRoutes = (app) => { 11 | app.get('/', AppController.getHomepage); 12 | app.get('/students', StudentsController.getAllStudents); 13 | app.get('/students/:major', StudentsController.getAllStudentsByMajor); 14 | }; 15 | 16 | export default mapRoutes; 17 | module.exports = mapRoutes; 18 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/full_server/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import mapRoutes from './routes'; 3 | 4 | const app = express(); 5 | const PORT = 1245; 6 | 7 | mapRoutes(app); 8 | app.listen(PORT, () => { 9 | console.log(`Server listening on PORT ${PORT}`); 10 | }); 11 | 12 | export default app; 13 | module.exports = app; 14 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/full_server/utils.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | 3 | /** 4 | * Reads the data of students in a CSV data file. 5 | * @param {String} dataPath The path to the CSV data file. 6 | * @author Bezaleel Olakunori 7 | * @returns {Promise<{ 8 | * String: {firstname: String, lastname: String, age: number}[] 9 | * }>} 10 | */ 11 | const readDatabase = (dataPath) => new Promise((resolve, reject) => { 12 | if (!dataPath) { 13 | reject(new Error('Cannot load the database')); 14 | } 15 | if (dataPath) { 16 | fs.readFile(dataPath, (err, data) => { 17 | if (err) { 18 | reject(new Error('Cannot load the database')); 19 | } 20 | if (data) { 21 | const fileLines = data 22 | .toString('utf-8') 23 | .trim() 24 | .split('\n'); 25 | const studentGroups = {}; 26 | const dbFieldNames = fileLines[0].split(','); 27 | const studentPropNames = dbFieldNames 28 | .slice(0, dbFieldNames.length - 1); 29 | 30 | for (const line of fileLines.slice(1)) { 31 | const studentRecord = line.split(','); 32 | const studentPropValues = studentRecord 33 | .slice(0, studentRecord.length - 1); 34 | const field = studentRecord[studentRecord.length - 1]; 35 | if (!Object.keys(studentGroups).includes(field)) { 36 | studentGroups[field] = []; 37 | } 38 | const studentEntries = studentPropNames 39 | .map((propName, idx) => [propName, studentPropValues[idx]]); 40 | studentGroups[field].push(Object.fromEntries(studentEntries)); 41 | } 42 | resolve(studentGroups); 43 | } 44 | }); 45 | } 46 | }); 47 | 48 | export default readDatabase; 49 | module.exports = readDatabase; 50 | -------------------------------------------------------------------------------- /0x05-Node_JS_basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node_js_basics", 3 | "version": "1.0.0", 4 | "description": "A simple project for learning the basics of Node.js for server-side apps.", 5 | "main": "index.js", 6 | "author": "Bezaleel Olakunori ", 7 | "private": true, 8 | "license": "MIT", 9 | "scripts": { 10 | "lint": "./node_modules/.bin/eslint", 11 | "check-lint": "lint [0-9]*.js", 12 | "lint-all": "./node_modules/.bin/eslint [0-9]*.js", 13 | "lint-fix-all": "./node_modules/.bin/eslint --fix [0-9]*.js", 14 | "test": "./node_modules/mocha/bin/mocha --require babel-register --exit", 15 | "dev": "nodemon --exec babel-node --presets babel-preset-env ./full_server/server.js ./database.csv" 16 | }, 17 | "dependencies": { 18 | "chai-http": "^4.3.0", 19 | "express": "^4.17.1" 20 | }, 21 | "devDependencies": { 22 | "babel-cli": "^6.26.0", 23 | "babel-preset-env": "^1.7.0", 24 | "nodemon": "^2.0.2", 25 | "eslint": "^6.4.0", 26 | "eslint-config-airbnb-base": "^14.0.0", 27 | "eslint-plugin-import": "^2.18.2", 28 | "eslint-plugin-jest": "^22.17.0", 29 | "chai": "^4.2.0", 30 | "mocha": "^6.2.2", 31 | "request": "^2.88.0", 32 | "sinon": "^7.5.0" 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "https://github.com/B3zaleel/alx-backend-javascript" 37 | }, 38 | "bugs": { 39 | "url": "https://github.com/B3zaleel/alx-backend-javascript/tree/main/0x05-Node_JS_basic" 40 | }, 41 | "homepage": "https://github.com/B3zaleel/alx-backend-javascript/blob/main/0x05-Node_JS_basic/README.md", 42 | "engines": { 43 | "node": "16.x", 44 | "npm": "8.x", 45 | "yarn": "1.x" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["env", {"exclude": ["transform-regenerator"]}]] 3 | } 4 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: false, 4 | es6: true, 5 | jest: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | 'plugin:jest/all', 10 | ], 11 | globals: { 12 | Atomics: 'readonly', 13 | SharedArrayBuffer: 'readonly', 14 | }, 15 | parserOptions: { 16 | ecmaVersion: 2018, 17 | sourceType: 'module', 18 | }, 19 | plugins: ['jest'], 20 | rules: { 21 | 'max-classes-per-file': 'off', 22 | 'no-underscore-dangle': 'off', 23 | 'no-console': 'off', 24 | 'no-shadow': 'off', 25 | 'no-restricted-syntax': [ 26 | 'error', 27 | 'LabeledStatement', 28 | 'WithStatement', 29 | ], 30 | }, 31 | overrides:[ 32 | { 33 | files: ['*.js'], 34 | excludedFiles: 'babel.config.js', 35 | } 36 | ] 37 | }; 38 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/0-calcul.js: -------------------------------------------------------------------------------- 1 | const calculateNumber = (a, b) => Math.round(a) + Math.round(b); 2 | 3 | module.exports = calculateNumber; 4 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/0-calcul.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const calculateNumber = require('./0-calcul'); 3 | 4 | describe('calculateNumber', () => { 5 | it('floating point whole numbers', () => { 6 | assert.strictEqual(calculateNumber(1.0, 2.0), 3); 7 | }); 8 | 9 | it('rounding down b\'s floating point fractional number', () => { 10 | assert.strictEqual(calculateNumber(1.0, 2.4), 3); 11 | }); 12 | 13 | it('rounding down a and b\'s floating point fractional number', () => { 14 | assert.strictEqual(calculateNumber(1.4, 2.4), 3); 15 | }); 16 | 17 | it('rounding down a\'s floating point fractional number', () => { 18 | assert.strictEqual(calculateNumber(1.4, 2.0), 3); 19 | }); 20 | 21 | it('rounding up b\'s floating point fractional numbers', () => { 22 | assert.strictEqual(calculateNumber(1.0, 2.5), 4); 23 | }); 24 | 25 | it('rounding up a and b\'s floating point fractional numbers', () => { 26 | assert.strictEqual(calculateNumber(2.6, 2.5), 6); 27 | }); 28 | 29 | it('rounding up a\'s floating point fractional numbers', () => { 30 | assert.strictEqual(calculateNumber(2.6, 2.0), 5); 31 | }); 32 | 33 | it('rounding down a and b floating point fractional numbers with trailing 9\'s', () => { 34 | assert.strictEqual(calculateNumber(2.499999, 3.499999), 5); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/1-calcul.js: -------------------------------------------------------------------------------- 1 | const calculateNumber = (type, a, b) => { 2 | if (type === 'SUM') { 3 | return Math.round(a) + Math.round(b); 4 | } 5 | if (type === 'SUBTRACT') { 6 | return Math.round(a) - Math.round(b); 7 | } 8 | if (type === 'DIVIDE') { 9 | return Math.round(b) === 0 ? 'Error' : Math.round(a) / Math.round(b); 10 | } 11 | return 0; 12 | }; 13 | 14 | module.exports = calculateNumber; 15 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/1-calcul.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const calculateNumber = require('./1-calcul'); 3 | 4 | describe('calculateNumber', () => { 5 | describe('type == "SUM"', () => { 6 | it('equal positive numbers', () => { 7 | assert.strictEqual(calculateNumber('SUM', 2.0, 2.0), 4); 8 | }); 9 | 10 | it('equal positive numbers (alternate)', () => { 11 | assert.strictEqual(calculateNumber('SUM', 2.3, 1.8), 4); 12 | }); 13 | 14 | it('equal negative numbers', () => { 15 | assert.strictEqual(calculateNumber('SUM', -2.0, -2.0), -4); 16 | }); 17 | 18 | it('equal negative numbers (alternate)', () => { 19 | assert.strictEqual(calculateNumber('SUM', -2.3, -1.8), -4); 20 | }); 21 | 22 | it('negative and positive numbers', () => { 23 | assert.strictEqual(calculateNumber('SUM', -2.0, 2.0), 0); 24 | }); 25 | 26 | it('positive and negative numbers', () => { 27 | assert.strictEqual(calculateNumber('SUM', 2.0, -2.0), 0); 28 | }); 29 | 30 | it('0 and 0', () => { 31 | assert.strictEqual(calculateNumber('SUM', 0.0, 0.0), 0); 32 | }); 33 | }); 34 | 35 | describe('type == "SUBTRACT"', () => { 36 | it('equal positive numbers', () => { 37 | assert.strictEqual(calculateNumber('SUBTRACT', 2.0, 2.0), 0); 38 | }); 39 | 40 | it('equal positive numbers (alternate)', () => { 41 | assert.strictEqual(calculateNumber('SUBTRACT', 2.3, 1.8), 0); 42 | }); 43 | 44 | it('equal negative numbers', () => { 45 | assert.strictEqual(calculateNumber('SUBTRACT', -2.0, -2.0), 0); 46 | }); 47 | 48 | it('equal negative numbers (alternate)', () => { 49 | assert.strictEqual(calculateNumber('SUBTRACT', -2.3, -1.8), 0); 50 | }); 51 | 52 | it('negative and positive numbers', () => { 53 | assert.strictEqual(calculateNumber('SUBTRACT', -2.0, 2.0), -4.0); 54 | }); 55 | 56 | it('positive and negative numbers', () => { 57 | assert.strictEqual(calculateNumber('SUBTRACT', 2.0, -2.0), 4.0); 58 | }); 59 | 60 | it('0 and 0', () => { 61 | assert.strictEqual(calculateNumber('SUBTRACT', 0.0, 0.0), 0); 62 | }); 63 | }); 64 | 65 | describe('type == "DIVIDE"', () => { 66 | it('positive numbers', () => { 67 | assert.strictEqual(calculateNumber('DIVIDE', 8.0, 2.0), 4.0); 68 | }); 69 | 70 | it('numbers with different signs', () => { 71 | assert.strictEqual(calculateNumber('DIVIDE', -7.0, 2.0), -3.5); 72 | }); 73 | 74 | it('numbers with different signs (alternate)', () => { 75 | assert.strictEqual(calculateNumber('DIVIDE', 7.0, -2.0), -3.5); 76 | }); 77 | 78 | it('negative numbers', () => { 79 | assert.strictEqual(calculateNumber('DIVIDE', -7.0, -2.0), 3.5); 80 | }); 81 | 82 | it('equal positive numbers', () => { 83 | assert.strictEqual(calculateNumber('DIVIDE', 2.0, 2.0), 1); 84 | }); 85 | 86 | it('equal negative numbers', () => { 87 | assert.strictEqual(calculateNumber('DIVIDE', -2.0, -2.0), 1); 88 | }); 89 | 90 | it('equal rounded up numbers', () => { 91 | assert.strictEqual(calculateNumber('DIVIDE', 2.6, 3.0), 1); 92 | }); 93 | 94 | it('equal rounded down numbers', () => { 95 | assert.strictEqual(calculateNumber('DIVIDE', 2.4, 2.0), 1); 96 | }); 97 | 98 | it('0 and positive number', () => { 99 | assert.strictEqual(calculateNumber('DIVIDE', 0.0, 5.0), 0); 100 | }); 101 | 102 | it('0 and negative number', () => { 103 | assert.strictEqual(calculateNumber('DIVIDE', 0.0, -5.0), -0); 104 | }); 105 | 106 | it('positive number and 0', () => { 107 | assert.strictEqual(calculateNumber('DIVIDE', 5.0, 0), 'Error'); 108 | }); 109 | 110 | it('positive number and number rounded down to 0', () => { 111 | assert.strictEqual(calculateNumber('DIVIDE', 5.0, 0.2), 'Error'); 112 | }); 113 | 114 | it('positive number and number rounded up to 0', () => { 115 | assert.strictEqual(calculateNumber('DIVIDE', 5.0, -0.2), 'Error'); 116 | }); 117 | 118 | it('negative number and 0', () => { 119 | assert.strictEqual(calculateNumber('DIVIDE', -5.0, 0), 'Error'); 120 | }); 121 | 122 | it('negative number and number rounded down to zero', () => { 123 | assert.strictEqual(calculateNumber('DIVIDE', -5.0, 0.2), 'Error'); 124 | }); 125 | 126 | it('negative number and number rounded up to zero', () => { 127 | assert.strictEqual(calculateNumber('DIVIDE', -5.0, -0.2), 'Error'); 128 | }); 129 | 130 | it('0 and 0', () => { 131 | assert.strictEqual(calculateNumber('DIVIDE', 0.0, 0.0), 'Error'); 132 | }); 133 | }); 134 | }); 135 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/10-api/api.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const app = express(); 4 | const PORT = 7865; 5 | 6 | app.use(express.json()); 7 | 8 | app.get('/', (_req, res) => { 9 | res.send('Welcome to the payment system'); 10 | }); 11 | 12 | app.get('/cart/:id(\\d+)', (req, res) => { 13 | const id = req.params.id; 14 | 15 | res.send(`Payment methods for cart ${id}`); 16 | }); 17 | 18 | app.get('/available_payments', (_req, res) => { 19 | res.json({ payment_methods: { credit_cards: true, paypal: false } }); 20 | }); 21 | 22 | app.post('/login', (req, res) => { 23 | let username = ''; 24 | 25 | if (req.body) { 26 | username = req.body.userName; 27 | } 28 | 29 | res.send(`Welcome ${username}`); 30 | }); 31 | 32 | app.listen(PORT, () => { 33 | console.log(`API available on localhost port ${PORT}`); 34 | }); 35 | 36 | module.exports = app; 37 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/10-api/api.test.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | const { expect } = require('chai'); 3 | 4 | describe('API integration test', () => { 5 | const API_URL = 'http://localhost:7865'; 6 | 7 | it('GET / returns correct response', (done) => { 8 | request.get(`${API_URL}/`, (_err, res, body) => { 9 | expect(res.statusCode).to.be.equal(200); 10 | expect(body).to.be.equal('Welcome to the payment system'); 11 | done(); 12 | }); 13 | }); 14 | 15 | it('GET /cart/:id returns correct response for valid :id', (done) => { 16 | request.get(`${API_URL}/cart/47`, (_err, res, body) => { 17 | expect(res.statusCode).to.be.equal(200); 18 | expect(body).to.be.equal('Payment methods for cart 47'); 19 | done(); 20 | }); 21 | }); 22 | 23 | it('GET /cart/:id returns 404 response for negative number values in :id', (done) => { 24 | request.get(`${API_URL}/cart/-47`, (_err, res, _body) => { 25 | expect(res.statusCode).to.be.equal(404); 26 | done(); 27 | }); 28 | }); 29 | 30 | it('GET /cart/:id returns 404 response for non-numeric values in :id', (done) => { 31 | request.get(`${API_URL}/cart/d200-44a5-9de6`, (_err, res, _body) => { 32 | expect(res.statusCode).to.be.equal(404); 33 | done(); 34 | }); 35 | }); 36 | 37 | it('POST /login returns valid response', (done) => { 38 | request.post(`${API_URL}/login`, {json: {userName: 'Pinkbrook'}}, (_err, res, body) => { 39 | expect(res.statusCode).to.be.equal(200); 40 | expect(body).to.be.equal('Welcome Pinkbrook'); 41 | done(); 42 | }); 43 | }); 44 | 45 | it('GET /available_payments returns valid response', (done) => { 46 | request.get(`${API_URL}/available_payments`, (_err, res, body) => { 47 | expect(res.statusCode).to.be.equal(200); 48 | expect(JSON.parse(body)) 49 | .to.be.deep.equal({payment_methods: {credit_cards: true, paypal: false}}); 50 | done(); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/10-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "10-api", 3 | "version": "1.0.0", 4 | "description": "A simple API for learning basic integration testing in NodeJS.", 5 | "main": "api.js", 6 | "author": "Bezaleel Olakunori ", 7 | "private": true, 8 | "license": "MIT", 9 | "scripts": { 10 | "test": "node ./node_modules/mocha/bin/mocha" 11 | }, 12 | "dependencies": { 13 | "express": "^4.17.1" 14 | }, 15 | "devDependencies": { 16 | "chai": "^4.2.0", 17 | "mocha": "^6.2.2", 18 | "request": "^2.88.0", 19 | "sinon": "^7.5.0" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/B3zaleel/alx-backend-javascript" 24 | }, 25 | "bugs": { 26 | "url": "https://github.com/B3zaleel/alx-backend-javascript/issues" 27 | }, 28 | "homepage": "https://github.com/B3zaleel/alx-backend-javascript/tree/main/0x06-unittests_in_js/10-api", 29 | "engines": { 30 | "node": "16.x", 31 | "npm": "8.x", 32 | "yarn": "1.x" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/2-calcul_chai.js: -------------------------------------------------------------------------------- 1 | const calculateNumber = (type, a, b) => { 2 | if (type === 'SUM') { 3 | return Math.round(a) + Math.round(b); 4 | } 5 | if (type === 'SUBTRACT') { 6 | return Math.round(a) - Math.round(b); 7 | } 8 | if (type === 'DIVIDE') { 9 | return Math.round(b) === 0 ? 'Error' : Math.round(a) / Math.round(b); 10 | } 11 | return 0; 12 | }; 13 | 14 | module.exports = calculateNumber; 15 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/2-calcul_chai.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const calculateNumber = require('./2-calcul_chai'); 3 | 4 | describe('calculateNumber', () => { 5 | describe('type == "SUM"', () => { 6 | it('equal positive numbers', () => { 7 | expect(calculateNumber('SUM', 2.0, 2.0)).to.equal(4); 8 | }); 9 | 10 | it('equal positive numbers (alternate)', () => { 11 | expect(calculateNumber('SUM', 2.3, 1.8)).to.equal(4); 12 | }); 13 | 14 | it('equal negative numbers', () => { 15 | expect(calculateNumber('SUM', -2.0, -2.0)).to.equal(-4); 16 | }); 17 | 18 | it('equal negative numbers (alternate)', () => { 19 | expect(calculateNumber('SUM', -2.3, -1.8)).to.equal(-4); 20 | }); 21 | 22 | it('negative and positive numbers', () => { 23 | expect(calculateNumber('SUM', -2.0, 2.0)).to.equal(0); 24 | }); 25 | 26 | it('positive and negative numbers', () => { 27 | expect(calculateNumber('SUM', 2.0, -2.0)).to.equal(0); 28 | }); 29 | 30 | it('0 and 0', () => { 31 | expect(calculateNumber('SUM', 0.0, 0.0)).to.equal(0); 32 | }); 33 | }); 34 | 35 | describe('type == "SUBTRACT"', () => { 36 | it('equal positive numbers', () => { 37 | expect(calculateNumber('SUBTRACT', 2.0, 2.0)).to.equal(0); 38 | }); 39 | 40 | it('equal positive numbers (alternate)', () => { 41 | expect(calculateNumber('SUBTRACT', 2.3, 1.8)).to.equal(0); 42 | }); 43 | 44 | it('equal negative numbers', () => { 45 | expect(calculateNumber('SUBTRACT', -2.0, -2.0)).to.equal(0); 46 | }); 47 | 48 | it('equal negative numbers (alternate)', () => { 49 | expect(calculateNumber('SUBTRACT', -2.3, -1.8)).to.equal(0); 50 | }); 51 | 52 | it('negative and positive numbers', () => { 53 | expect(calculateNumber('SUBTRACT', -2.0, 2.0)).to.equal(-4.0); 54 | }); 55 | 56 | it('positive and negative numbers', () => { 57 | expect(calculateNumber('SUBTRACT', 2.0, -2.0)).to.equal(4.0); 58 | }); 59 | 60 | it('0 and 0', () => { 61 | expect(calculateNumber('SUBTRACT', 0.0, 0.0)).to.equal(0); 62 | }); 63 | }); 64 | 65 | describe('type == "DIVIDE"', () => { 66 | it('positive numbers', () => { 67 | expect(calculateNumber('DIVIDE', 8.0, 2.0)).to.equal(4.0); 68 | }); 69 | 70 | it('numbers with different signs', () => { 71 | expect(calculateNumber('DIVIDE', -7.0, 2.0)).to.equal(-3.5); 72 | }); 73 | 74 | it('numbers with different signs (alternate)', () => { 75 | expect(calculateNumber('DIVIDE', 7.0, -2.0)).to.equal(-3.5); 76 | }); 77 | 78 | it('negative numbers', () => { 79 | expect(calculateNumber('DIVIDE', -7.0, -2.0)).to.equal(3.5); 80 | }); 81 | 82 | it('equal positive numbers', () => { 83 | expect(calculateNumber('DIVIDE', 2.0, 2.0)).to.equal(1); 84 | }); 85 | 86 | it('equal negative numbers', () => { 87 | expect(calculateNumber('DIVIDE', -2.0, -2.0)).to.equal(1); 88 | }); 89 | 90 | it('equal rounded up numbers', () => { 91 | expect(calculateNumber('DIVIDE', 2.6, 3.0)).to.equal(1); 92 | }); 93 | 94 | it('equal rounded down numbers', () => { 95 | expect(calculateNumber('DIVIDE', 2.4, 2.0)).to.equal(1); 96 | }); 97 | 98 | it('0 and positive number', () => { 99 | expect(calculateNumber('DIVIDE', 0.0, 5.0)).to.equal(0); 100 | }); 101 | 102 | it('0 and negative number', () => { 103 | expect(calculateNumber('DIVIDE', 0.0, -5.0)).to.equal(-0); 104 | }); 105 | 106 | it('positive number and 0', () => { 107 | expect(calculateNumber('DIVIDE', 5.0, 0)).to.equal('Error'); 108 | }); 109 | 110 | it('positive number and number rounded down to 0', () => { 111 | expect(calculateNumber('DIVIDE', 5.0, 0.2)).to.equal('Error'); 112 | }); 113 | 114 | it('positive number and number rounded up to 0', () => { 115 | expect(calculateNumber('DIVIDE', 5.0, -0.2)).to.equal('Error'); 116 | }); 117 | 118 | it('negative number and 0', () => { 119 | expect(calculateNumber('DIVIDE', -5.0, 0)).to.equal('Error'); 120 | }); 121 | 122 | it('negative number and number rounded down to zero', () => { 123 | expect(calculateNumber('DIVIDE', -5.0, 0.2)).to.equal('Error'); 124 | }); 125 | 126 | it('negative number and number rounded up to zero', () => { 127 | expect(calculateNumber('DIVIDE', -5.0, -0.2)).to.equal('Error'); 128 | }); 129 | 130 | it('0 and 0', () => { 131 | expect(calculateNumber('DIVIDE', 0.0, 0.0)).to.equal('Error'); 132 | }); 133 | }); 134 | }); 135 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/3-payment.js: -------------------------------------------------------------------------------- 1 | const Utils = require('./utils'); 2 | 3 | const sendPaymentRequestToApi = (totalAmount, totalShipping) => { 4 | const totalCost = Utils.calculateNumber('SUM', totalAmount, totalShipping); 5 | console.log(`The total is: ${totalCost}`); 6 | }; 7 | 8 | module.exports = sendPaymentRequestToApi; 9 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/3-payment.test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon'); 2 | const Utils = require('./utils'); 3 | const { expect } = require('chai'); 4 | const sendPaymentRequestToApi = require('./3-payment'); 5 | 6 | describe('sendPaymentRequestToApi', () => { 7 | it('sendPaymentRequestToApi uses the calculateNumber method of Utils', () => { 8 | const bigBrother = sinon.spy(Utils); 9 | 10 | sendPaymentRequestToApi(100, 20); 11 | expect(bigBrother.calculateNumber.calledWith('SUM', 100, 20)).to.be.true; 12 | expect(bigBrother.calculateNumber.callCount).to.be.equal(1); 13 | bigBrother.calculateNumber.restore(); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/4-payment.js: -------------------------------------------------------------------------------- 1 | const Utils = require('./utils'); 2 | 3 | const sendPaymentRequestToApi = (totalAmount, totalShipping) => { 4 | const totalCost = Utils.calculateNumber('SUM', totalAmount, totalShipping); 5 | console.log(`The total is: ${totalCost}`); 6 | }; 7 | 8 | module.exports = sendPaymentRequestToApi; 9 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/4-payment.test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon'); 2 | const Utils = require('./utils'); 3 | const { expect } = require('chai'); 4 | const sendPaymentRequestToApi = require('./4-payment'); 5 | 6 | describe('sendPaymentRequestToApi', () => { 7 | it('sendPaymentRequestToApi calls console.log with the right arguments', () => { 8 | const bigBrother = sinon.spy(console); 9 | const dummy = sinon.stub(Utils, 'calculateNumber'); 10 | 11 | dummy.returns(10); 12 | sendPaymentRequestToApi(100, 20); 13 | expect(dummy.calledWith('SUM', 100, 20)).to.be.true; 14 | expect(dummy.callCount).to.be.equal(1); 15 | expect(bigBrother.log.calledWith('The total is: 10')).to.be.true; 16 | expect(bigBrother.log.callCount).to.be.equal(1); 17 | dummy.restore(); 18 | bigBrother.log.restore(); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/5-payment.js: -------------------------------------------------------------------------------- 1 | const Utils = require('./utils'); 2 | 3 | const sendPaymentRequestToApi = (totalAmount, totalShipping) => { 4 | const totalCost = Utils.calculateNumber('SUM', totalAmount, totalShipping); 5 | console.log(`The total is: ${totalCost}`); 6 | }; 7 | 8 | module.exports = sendPaymentRequestToApi; 9 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/5-payment.test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon'); 2 | const { expect } = require('chai'); 3 | const sendPaymentRequestToApi = require('./5-payment'); 4 | 5 | describe('sendPaymentRequestToApi', () => { 6 | let bigBrother; 7 | 8 | beforeEach(() => { 9 | if (!bigBrother) { 10 | bigBrother = sinon.spy(console); 11 | } 12 | }); 13 | 14 | afterEach(() => { 15 | bigBrother.log.resetHistory(); 16 | }); 17 | 18 | it('sendPaymentRequestToApi(100, 20) logs "The total is: 120" to the console', () => { 19 | sendPaymentRequestToApi(100, 20); 20 | expect(bigBrother.log.calledWith('The total is: 120')).to.be.true; 21 | expect(bigBrother.log.calledOnce).to.be.true; 22 | }); 23 | 24 | it('sendPaymentRequestToApi(10, 10) logs "The total is: 20" to the console', () => { 25 | sendPaymentRequestToApi(10, 10); 26 | expect(bigBrother.log.calledWith('The total is: 20')).to.be.true; 27 | expect(bigBrother.log.calledOnce).to.be.true; 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/6-payment_token.js: -------------------------------------------------------------------------------- 1 | const getPaymentTokenFromAPI = (success) => new Promise((resolve, _reject) => { 2 | if (success) { 3 | resolve({data: 'Successful response from the API'}); 4 | } 5 | }); 6 | 7 | module.exports = getPaymentTokenFromAPI; 8 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/6-payment_token.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const getPaymentTokenFromAPI = require('./6-payment_token'); 3 | 4 | describe('getPaymentTokenFromAPI', () => { 5 | it('getPaymentTokenFromAPI(success), where success == true', (done) => { 6 | getPaymentTokenFromAPI(true) 7 | .then((res) => { 8 | expect(res).to.deep.equal({data: 'Successful response from the API'}); 9 | done(); 10 | }); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/7-skip.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | 3 | describe('Testing numbers', () => { 4 | it('1 is equal to 1', () => { 5 | expect(1 === 1).to.be.true; 6 | }); 7 | 8 | it('2 is equal to 2', () => { 9 | expect(2 === 2).to.be.true; 10 | }); 11 | 12 | it.skip('1 is equal to 3', () => { 13 | expect(1 === 3).to.be.true; 14 | }); 15 | 16 | it('3 is equal to 3', () => { 17 | expect(3 === 3).to.be.true; 18 | }); 19 | 20 | it('4 is equal to 4', () => { 21 | expect(4 === 4).to.be.true; 22 | }); 23 | 24 | it('5 is equal to 5', () => { 25 | expect(5 === 5).to.be.true; 26 | }); 27 | 28 | it('6 is equal to 6', () => { 29 | expect(6 === 6).to.be.true; 30 | }); 31 | 32 | it('7 is equal to 7', () => { 33 | expect(7 === 7).to.be.true; 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/8-api/api.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const app = express(); 4 | const PORT = 7865; 5 | 6 | app.get('/', (_, res) => { 7 | res.send('Welcome to the payment system'); 8 | }); 9 | 10 | app.listen(PORT, () => { 11 | console.log(`API available on localhost port ${PORT}`); 12 | }); 13 | 14 | module.exports = app; 15 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/8-api/api.test.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | const { expect } = require('chai'); 3 | 4 | describe('API integration test', () => { 5 | const API_URL = 'http://localhost:7865'; 6 | 7 | it('GET / returns correct response', (done) => { 8 | request.get(`${API_URL}/`, (_err, res, body) => { 9 | expect(res.statusCode).to.be.equal(200); 10 | expect(body).to.be.equal('Welcome to the payment system'); 11 | done(); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/8-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "8-api", 3 | "version": "1.0.0", 4 | "description": "A simple API for learning basic integration testing in NodeJS.", 5 | "main": "api.js", 6 | "author": "Bezaleel Olakunori ", 7 | "private": true, 8 | "license": "MIT", 9 | "scripts": { 10 | "test": "node ./node_modules/mocha/bin/mocha" 11 | }, 12 | "dependencies": { 13 | "express": "^4.17.1" 14 | }, 15 | "devDependencies": { 16 | "chai": "^4.2.0", 17 | "mocha": "^6.2.2", 18 | "request": "^2.88.0", 19 | "sinon": "^7.5.0" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/B3zaleel/alx-backend-javascript" 24 | }, 25 | "bugs": { 26 | "url": "https://github.com/B3zaleel/alx-backend-javascript/issues" 27 | }, 28 | "homepage": "https://github.com/B3zaleel/alx-backend-javascript/tree/main/0x06-unittests_in_js/8-api", 29 | "engines": { 30 | "node": "16.x", 31 | "npm": "8.x", 32 | "yarn": "1.x" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/9-api/api.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const app = express(); 4 | const PORT = 7865; 5 | 6 | app.get('/', (_, res) => { 7 | res.send('Welcome to the payment system'); 8 | }); 9 | 10 | app.get('/cart/:id(\\d+)', (req, res) => { 11 | const id = req.params.id; 12 | 13 | res.send(`Payment methods for cart ${id}`); 14 | }); 15 | 16 | app.listen(PORT, () => { 17 | console.log(`API available on localhost port ${PORT}`); 18 | }); 19 | 20 | module.exports = app; 21 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/9-api/api.test.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | const { expect } = require('chai'); 3 | 4 | describe('API integration test', () => { 5 | const API_URL = 'http://localhost:7865'; 6 | 7 | it('GET / returns correct response', (done) => { 8 | request.get(`${API_URL}/`, (_err, res, body) => { 9 | expect(res.statusCode).to.be.equal(200); 10 | expect(body).to.be.equal('Welcome to the payment system'); 11 | done(); 12 | }); 13 | }); 14 | 15 | it('GET /cart/:id returns correct response for valid :id', (done) => { 16 | request.get(`${API_URL}/cart/47`, (_err, res, body) => { 17 | expect(res.statusCode).to.be.equal(200); 18 | expect(body).to.be.equal('Payment methods for cart 47'); 19 | done(); 20 | }); 21 | }); 22 | 23 | it('GET /cart/:id returns 404 response for negative number values in :id', (done) => { 24 | request.get(`${API_URL}/cart/-47`, (_err, res, _body) => { 25 | expect(res.statusCode).to.be.equal(404); 26 | done(); 27 | }); 28 | }); 29 | 30 | it('GET /cart/:id returns 404 response for non-numeric values in :id', (done) => { 31 | request.get(`${API_URL}/cart/d200-44a5-9de6`, (_err, res, _body) => { 32 | expect(res.statusCode).to.be.equal(404); 33 | done(); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/9-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "9-api", 3 | "version": "1.0.0", 4 | "description": "A simple API for learning basic integration testing in NodeJS.", 5 | "main": "api.js", 6 | "author": "Bezaleel Olakunori ", 7 | "private": true, 8 | "license": "MIT", 9 | "scripts": { 10 | "test": "node ./node_modules/mocha/bin/mocha" 11 | }, 12 | "dependencies": { 13 | "express": "^4.17.1" 14 | }, 15 | "devDependencies": { 16 | "chai": "^4.2.0", 17 | "mocha": "^6.2.2", 18 | "request": "^2.88.0", 19 | "sinon": "^7.5.0" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/B3zaleel/alx-backend-javascript" 24 | }, 25 | "bugs": { 26 | "url": "https://github.com/B3zaleel/alx-backend-javascript/issues" 27 | }, 28 | "homepage": "https://github.com/B3zaleel/alx-backend-javascript/tree/main/0x06-unittests_in_js/9-api", 29 | "engines": { 30 | "node": "16.x", 31 | "npm": "8.x", 32 | "yarn": "1.x" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/README.md: -------------------------------------------------------------------------------- 1 | # Unittests in JS 2 | 3 | This project contains tasks for learning to create unittests in NodeJS. 4 | 5 | ## Learning Objectives 6 | 7 | + How to use Mocha to write a test suite. 8 | + How to use different assertion libraries (Node or Chai). 9 | + How to present long test suites. 10 | + When and how to use spies. 11 | + When and how to use stubs. 12 | + What are hooks and when to use them. 13 | + Unit testing with Async functions. 14 | + How to write integration tests with a small node server. 15 | 16 | ## Tasks To Complete 17 | 18 | + [x] 0. **Basic test with Mocha and Node assertion library** 19 | + **Install Mocha using npm:** 20 | + Set up a scripts in your [`package.json`](package.json) to quickly run Mocha using `npm test`. 21 | + You have to use `assert`. 22 | + **Create a new file named [`0-calcul.js`](0-calcul.js):** 23 | + Create a function named `calculateNumber`. It should accepts two arguments (number) `a` and `b`. 24 | + The function should round `a` and `b` and return the sum of it. 25 | + **Test cases:** 26 | + Create a file [`0-calcul.test.js`](0-calcul.test.js) that contains test cases of this function 27 | + You can assume `a` and `b` are always number. 28 | + Tests should be around the "rounded" part. 29 | + **Tips:** 30 | + For the sake of the example, this test suite is slightly extreme and probably not needed. 31 | + However, remember that your tests should not only verify what a function is supposed to do, but also the edge cases. 32 | + **Requirements:** 33 | + You have to use `assert`. 34 | + You should be able to run the test suite using `npm test 0-calcul.test.js`. 35 | + Every test should pass without any warning. 36 | + **Expected output:** 37 | ```powershell 38 | > const calculateNumber = require("./0-calcul.js"); 39 | > calculateNumber(1, 3) 40 | 4 41 | > calculateNumber(1, 3.7) 42 | 5 43 | > calculateNumber(1.2, 3.7) 44 | 5 45 | > calculateNumber(1.5, 3.7) 46 | 6 47 | > 48 | ``` 49 | + **Run test:** 50 | ```powershell 51 | bob@dylan:~$ npm test 0-calcul.test.js 52 | 53 | > task_0@1.0.0 test /root 54 | > ./node_modules/mocha/bin/mocha "0-calcul.test.js" 55 | 56 | calculateNumber 57 | ✓ ... 58 | ✓ ... 59 | ✓ ... 60 | ... 61 | 62 | 130 passing (35ms) 63 | bob@dylan:~$ 64 | ``` 65 | 66 | + [x] 1. **Combining descriptions** 67 | + **Create a new file named [`1-calcul.js`](1-calcul.js):** 68 | + Upgrade the function you created in the previous task ([`0-calcul.js`](0-calcul.js)). 69 | + Add a new argument named `type` at first argument of the function. `type` can be `SUM`, `SUBTRACT`, or `DIVIDE` (string). 70 | + When type is `SUM`, round the two numbers, and add `a` to `b`. 71 | + When type is `SUBTRACT`, round the two numbers, and subtract `b` from `a`. 72 | + When type is `DIVIDE`, round the two numbers, and divide `a` with `b` - if the rounded value of `b` is equal to 0, return the string `Error`. 73 | + **Test cases:** 74 | + Create a file [`1-calcul.test.js`](1-calcul.test.js) that contains test cases of this function. 75 | + You can assume `a` and `b` are always number. 76 | + Usage of `describe` will help you to organize your test cases. 77 | + **Tips:** 78 | + For the sake of the example, this test suite is slightly extreme and probably not needed. 79 | + However, remember that your tests should not only verify what a function is supposed to do, but also the edge cases. 80 | + **Requirements:** 81 | + You have to use `assert`. 82 | + You should be able to run the test suite using `npm test 1-calcul.test.js`. 83 | + Every test should pass without any warning. 84 | + **Expected output:** 85 | ```powershell 86 | > const calculateNumber = require("./1-calcul.js"); 87 | > calculateNumber('SUM', 1.4, 4.5) 88 | 6 89 | > calculateNumber('SUBTRACT', 1.4, 4.5) 90 | -4 91 | > calculateNumber('DIVIDE', 1.4, 4.5) 92 | 0.2 93 | > calculateNumber('DIVIDE', 1.4, 0) 94 | 'Error' 95 | ``` 96 | 97 | + [x] 2. **Basic test using Chai assertion library** 98 | + While using Node assert library is completely valid, a lot of developers prefer to have a behavior driven development style. This type being easier to read and therefore to maintain. 99 | + **Let's install Chai with npm:** 100 | + Copy the file [`1-calcul.js`](1-calcul.js) in a new file [`2-calcul_chai.js`](2-calcul_chai.js) (same content, same behavior). 101 | + Copy the file [`1-calcul.test.js`](1-calcul.test.js) in a new file [`2-calcul_chai.test.js`](2-calcul_chai.test.js). 102 | + Rewrite the test suite, using `expect` from `Chai`. 103 | + **Tips:** 104 | + Remember that test coverage is always difficult to maintain. Using an easier style for your tests will help you. 105 | + The easier your tests are to read and understand, the more other engineers will be able to fix them when they are modifying your code. 106 | + **Requirements:** 107 | + You should be able to run the test suite using `npm test 2-calcul_chai.test.js`. 108 | + Every test should pass without any warning. 109 | 110 | + [x] 3. **Spies** 111 | + Spies are a useful wrapper that will execute the wrapped function, and log useful information (e.g. was it called, with what arguments). Sinon is a library allowing you to create spies. 112 | + **Let's install Sinon with npm:** 113 | + Create a new file named [`utils.js`](utils.js). 114 | + Create a new module named `Utils`. 115 | + Create a property named `calculateNumber` and paste your previous code in the function. 116 | + Export the Utils module. 117 | + **Create a new file named [`3-payment.js`](3-payment.js):** 118 | + Create a new function named `sendPaymentRequestToApi`. The function takes two arguments `totalAmount`, and `totalShipping`. 119 | The function calls the `Utils.calculateNumber` function with type `SUM`, `totalAmount` as `a`, `totalShipping` as `b`, and displays in the console the message `The total is: `. 120 | + **Create a new file named [`3-payment.test.js`](3-payment.test.js) and add a new suite named `sendPaymentRequestToApi`:** 121 | + By using `sinon.spy`, make sure the math used for `sendPaymentRequestToApi(100, 20)` is the same as `Utils.calculateNumber('SUM', 100, 20)` (validate the usage of the `Utils` function). 122 | + **Requirements:** 123 | + You should be able to run the test suite using `npm test 3-payment.test.js`. 124 | + Every test should pass without any warning. 125 | + You should use a `spy` to complete this exercise. 126 | + **Tips:** 127 | + Remember to always restore a spy after using it in a test, it will prevent you from having weird behaviors. 128 | + Spies are really useful and allow you to focus only on what your code is doing and not the downstream APIs or functions. 129 | + Remember that integration test is different from unit test. Your unit test should test your code, not the code of a different function. 130 | 131 | + [x] 4. **Stubs** 132 | + Stubs are similar to spies. Except that you can provide a different implementation of the function you are wrapping. Sinon can be used as well for stubs. 133 | + **Create a new file [`4-payment.js`](4-payment.js), and copy the code from [`3-payment.js`](3-payment.js) (same content, same behavior).** 134 | + **Create a new file [`4-payment.test.js`](4-payment.test.js), and copy the code from [`3-payment.test.js`](3-payment.test.js):** 135 | + Imagine that calling the function `Utils.calculateNumber` is actually calling an API or a very expensive method. You don't necessarily want to do that on every test run. 136 | + Stub the function `Utils.calculateNumber` to always return the same number `10`. 137 | + Verify that the stub is being called with `type = SUM`, `a = 100`, and `b = 20`. 138 | + Add a spy to verify that `console.log` is logging the correct message `The total is: 10`. 139 | + **Requirements:** 140 | + You should be able to run the test suite using `npm test 4-payment.test.js`. 141 | + Every test should pass without any warning. 142 | + You should use a `stub` to complete this exercise. 143 | + Do not forget to restore the spy and the stub. 144 | + **Tips:** 145 | + Using stubs allows you to greatly speed up your test. When executing thousands of tests, saving a few seconds is important. 146 | + Using stubs allows you to control specific edge case (e.g a function throwing an error or returning a specific result like a number or a timestamp). 147 | 148 | + [x] 5. **Hooks** 149 | + Hooks are useful functions that can be called before execute one or all tests in a suite. 150 | + **Copy the code from [`4-payment.js`](4-payment.js) into a new file [`5-payment.js`](5-payment.js): (same content/same behavior).** 151 | + **Create a new file [`5-payment.test.js`](5-payment.test.js):** 152 | + Inside the same `describe`, create 2 tests: 153 | + The first test will call `sendPaymentRequestToAPI` with 100, and 20: 154 | + Verify that the console is logging the string `The total is: 120`. 155 | + Verify that the console is only called once. 156 | + The second test will call `sendPaymentRequestToAPI` with 10, and 10: 157 | + Verify that the console is logging the string `The total is: 20`. 158 | + Verify that the console is only called once. 159 | + **Requirements:** 160 | + You should be able to run the test suite using `npm test 5-payment.test.js`. 161 | + Every test should pass without any warning. 162 | + You should use only one `spy` to complete this exercise. 163 | + You should use a `beforeEach` and a `afterEach` hooks to complete this exercise. 164 | 165 | + [x] 6. **Async tests with done** 166 | + Look into how to support async testing, for example when waiting for the answer of an API or from a Promise. 167 | + **Create a new file [`6-payment_token.js`](6-payment_token.js):** 168 | Create a new function named `getPaymentTokenFromAPI`. 169 | The function will take an argument called `success` (boolean). 170 | + When `success` is true, it should return a resolved promise with the object `{data: 'Successful response from the API'}`. 171 | + Otherwise, the function is doing nothing. 172 | + **Create a new file [`6-payment_token.test.js`](6-payment_token.test.js) and write a test suite named `getPaymentTokenFromAPI`:** 173 | + How to test the result of `getPaymentTokenFromAPI(true)`? 174 | + **Tips:** 175 | + You should be extremely careful when working with async testing. Without calling `done` properly, your test could be always passing even if what you are actually testing is never executed. 176 | + **Requirements:** 177 | + You should be able to run the test suite using `npm test 6-payment_token.test.js`. 178 | + Every test should pass without any warning. 179 | + You should use the `done` callback to execute this test. 180 | 181 | + [x] 7. **Skip** 182 | + When you have a long list of tests, and you can't figure out why a test is breaking, avoid commenting out a test, or removing it. **Skip** it instead, and file a ticket to come back to it as soon as possible. 183 | + You will be using this file, conveniently named [`7-skip.test.js`](7-skip.test.js): 184 | ```js 185 | const { expect } = require('chai'); 186 | 187 | describe('Testing numbers', () => { 188 | it('1 is equal to 1', () => { 189 | expect(1 === 1).to.be.true; 190 | }); 191 | 192 | it('2 is equal to 2', () => { 193 | expect(2 === 2).to.be.true; 194 | }); 195 | 196 | it('1 is equal to 3', () => { 197 | expect(1 === 3).to.be.true; 198 | }); 199 | 200 | it('3 is equal to 3', () => { 201 | expect(3 === 3).to.be.true; 202 | }); 203 | 204 | it('4 is equal to 4', () => { 205 | expect(4 === 4).to.be.true; 206 | }); 207 | 208 | it('5 is equal to 5', () => { 209 | expect(5 === 5).to.be.true; 210 | }); 211 | 212 | it('6 is equal to 6', () => { 213 | expect(6 === 6).to.be.true; 214 | }); 215 | 216 | it('7 is equal to 7', () => { 217 | expect(7 === 7).to.be.true; 218 | }); 219 | }); 220 | ``` 221 | + **Using the file [`7-skip.test.js`](7-skip.test.js):** 222 | + Make the test suite pass **without** fixing or removing the failing test. 223 | + `it` description **must stay** the same. 224 | + **Tips:** 225 | + Skipping is also very helpful when you only want to execute the test in a particular case (specific environment, or when an API is not behaving correctly). 226 | + **Requirements:** 227 | + You should be able to run the test suite using `npm test 7-skip.test.js`. 228 | + Every test should pass without any warning. 229 | 230 | + [x] 8. **Basic Integration testing** 231 | + In a folder [`8-api`](8-api/) located at the root of the project directory, copy this `package.json` over: 232 | ```json 233 | { 234 | "name": "8-api", 235 | "version": "1.0.0", 236 | "description": "", 237 | "main": "index.js", 238 | "scripts": { 239 | "test": "./node_modules/mocha/bin/mocha" 240 | }, 241 | "author": "", 242 | "license": "ISC", 243 | "dependencies": { 244 | "express": "^4.17.1" 245 | }, 246 | "devDependencies": { 247 | "chai": "^4.2.0", 248 | "mocha": "^6.2.2", 249 | "request": "^2.88.0", 250 | "sinon": "^7.5.0" 251 | } 252 | } 253 | ``` 254 | + **Create a new file [`api.js`](api.js):** 255 | + By using `express`, create an instance of `express` called `app`. 256 | + Listen to port 7865 and log `API available on localhost port 7865` to the browser console when the `express` server is started. 257 | + For the route `GET /`, return the message `Welcome to the payment system`. 258 | + **Create a new file [`api.test.js`](api.test.js):** 259 | + Create one suite for the index page: 260 | + Correct status code? 261 | + Correct result? 262 | + Other? 263 | + **Server:** 264 | + Terminal 1: 265 | ```powershell 266 | bob@dylan:~/8-api$ node api.js 267 | API available on localhost port 7865 268 | ``` 269 | + Terminal 2: 270 | ```powershell 271 | bob@dylan:~/8-api$ curl http://localhost:7865 ; echo "" 272 | Welcome to the payment system 273 | bob@dylan:~/8-api$ 274 | bob@dylan:~/8-api$ npm test api.test.js 275 | 276 | > 8-api@1.0.0 test /root/8-api 277 | > ./node_modules/mocha/bin/mocha "api.test.js" 278 | 279 | 280 | 281 | Index page 282 | ✓ ... 283 | ✓ ... 284 | ... 285 | 286 | 23 passing (256ms) 287 | 288 | bob@dylan:~/8-api$ 289 | ``` 290 | + **Tips:** 291 | + Since this is an integration test, you will need to have your node server running for the test to pass. 292 | + You can use the module `request`. 293 | + **Requirements:** 294 | + You should be able to run the test suite using `yarn test 8-api/api.test.js`. 295 | + Every test should pass without any warnings. 296 | 297 | + [x] 9. **Regex integration testing** 298 | + In a folder [`9-api`](9-api/), reusing the previous project in [`8-api`](8-api/) (`package.json`, `api.js` and `api.test.js`). 299 | + **Modify the file [`api.js`](api.js):** 300 | + Add a new endpoint: `GET /cart/:id`. 301 | + `:id` must be only a number (validation must be in the route definition). 302 | + When accessed, the endpoint should return `Payment methods for cart :id`. 303 | + **Modify the file [`api.test.js`](api.test.js):** 304 | + Add a new test suite for the cart page: 305 | + Correct status code when `:id` is a number? 306 | + Correct status code when `:id` is NOT a number (=> 404)? 307 | + etc. 308 | + **Server:** 309 | + Terminal 1: 310 | ```powershell 311 | bob@dylan:~$ node api.js 312 | API available on localhost port 7865 313 | ``` 314 | + Terminal 2: 315 | ```powershell 316 | bob@dylan:~$ curl http://localhost:7865/cart/12 ; echo "" 317 | Payment methods for cart 12 318 | bob@dylan:~$ 319 | bob@dylan:~$ curl http://localhost:7865/cart/hello -v 320 | * Trying 127.0.0.1... 321 | * TCP_NODELAY set 322 | * Connected to localhost (127.0.0.1) port 7865 (#0) 323 | > GET /cart/hello HTTP/1.1 324 | > Host: localhost:7865 325 | > User-Agent: curl/7.58.0 326 | > Accept: */* 327 | > 328 | < HTTP/1.1 404 Not Found 329 | < X-Powered-By: Express 330 | < Content-Security-Policy: default-src 'none' 331 | < X-Content-Type-Options: nosniff 332 | < Content-Type: text/html; charset=utf-8 333 | < Content-Length: 149 334 | < Date: Wed, 15 Jul 2020 08:33:44 GMT 335 | < Connection: keep-alive 336 | < 337 | 338 | 339 | 340 | 341 | Error 342 | 343 | 344 |
Cannot GET /cart/hello
345 | 346 | 347 | * Connection #0 to host localhost left intact 348 | bob@dylan:~$ 349 | ``` 350 | + **Tips:** 351 | + You will need to add a small regex in your path to support the usecase. 352 | + **Requirements:** 353 | + You should be able to run the test suite using `yarn test 9-api/api.test.js`. 354 | + Every test should pass without any warning. 355 | 356 | + [x] 10. **Deep equality & Post integration testing** 357 | + In a folder [`10-api`](10-api/), reusing the previous project in [`9-api`](9-api/) (`package.json`, `api.js` and `api.test.js`). 358 | + **Modify the file [`api.js`](api.js):** 359 | + Add an endpoint `GET /available_payments` that returns an object with the following structure: 360 | ```js 361 | { 362 | payment_methods: { 363 | credit_cards: true, 364 | paypal: false 365 | } 366 | } 367 | ``` 368 | + Add an endpoint `POST /login` that returns the message `Welcome :username`, where `:username` is the value of the body variable `userName`. 369 | + **Modify the file [`api.test.js`](api.test.js):** 370 | + Add a test suite for the `/login` endpoint. 371 | + Add a test suite for the `/available_payments` endpoint. 372 | + **Server:** 373 | + Terminal 1: 374 | ```powershell 375 | bob@dylan:~$ node api.js 376 | API available on localhost port 7865 377 | ``` 378 | + Terminal 2: 379 | ```powershell 380 | bob@dylan:~$ curl http://localhost:7865/available_payments ; echo "" 381 | {"payment_methods":{"credit_cards":true,"paypal":false}} 382 | bob@dylan:~$ 383 | bob@dylan:~$ curl -XPOST http://localhost:7865/login -d '{ "userName": "Betty" }' -H 'Content-Type: application/json' ; echo "" 384 | Welcome Betty 385 | bob@dylan:~$ 386 | ``` 387 | + **Tips:** 388 | + Look at deep equality to compare objects. 389 | + **Requirements:** 390 | + You should be able to run the test suite using `yarn test 10-api/api.test.js`. 391 | + Every test should pass without any warning. 392 | + Your server should not display any error. 393 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current', 8 | }, 9 | }, 10 | ], 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unittests_in_js", 3 | "version": "1.0.0", 4 | "description": "A project for learning the basics of unittests in Node.js.", 5 | "author": "Bezaleel Olakunori ", 6 | "private": true, 7 | "license": "MIT", 8 | "scripts": { 9 | "lint": "./node_modules/.bin/eslint", 10 | "lint-all": "./node_modules/.bin/eslint [0-9]*.js", 11 | "lint-fix-all": "./node_modules/.bin/eslint --fix [0-9]*.js", 12 | "test": "node ./node_modules/mocha/bin/mocha --require babel-register --exit", 13 | "dev": "nodemon --exec babel-node --presets babel-preset-env ./full_server/server.js ./database.csv" 14 | }, 15 | "dependencies": { 16 | "chai-http": "^4.3.0", 17 | "express": "^4.17.1" 18 | }, 19 | "devDependencies": { 20 | "babel-cli": "^6.26.0", 21 | "babel-preset-env": "^1.7.0", 22 | "nodemon": "^2.0.2", 23 | "eslint": "^6.4.0", 24 | "eslint-config-airbnb-base": "^14.0.0", 25 | "eslint-plugin-import": "^2.18.2", 26 | "eslint-plugin-jest": "^22.17.0", 27 | "chai": "^4.2.0", 28 | "mocha": "^6.2.2", 29 | "request": "^2.88.0", 30 | "sinon": "^7.5.0" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "https://github.com/B3zaleel/alx-backend-javascript" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/B3zaleel/alx-backend-javascript/issues" 38 | }, 39 | "homepage": "https://github.com/B3zaleel/alx-backend-javascript/blob/main/0x06-unittests_in_js/README.md", 40 | "engines": { 41 | "node": "16.x", 42 | "npm": "8.x", 43 | "yarn": "1.x" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /0x06-unittests_in_js/utils.js: -------------------------------------------------------------------------------- 1 | const Utils = { 2 | calculateNumber(type, a, b) { 3 | if (type === 'SUM') { 4 | return Math.round(a) + Math.round(b); 5 | } 6 | if (type === 'SUBTRACT') { 7 | return Math.round(a) - Math.round(b); 8 | } 9 | if (type === 'DIVIDE') { 10 | return Math.round(b) === 0 ? 'Error' : Math.round(a) / Math.round(b); 11 | } 12 | return 0; 13 | }, 14 | }; 15 | 16 | module.exports = Utils; 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Bezaleel Olakunori 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ALX Backend JavaScript 2 | 3 | ![Repo size](https://img.shields.io/github/repo-size/B3zaleel/alx-backend-javascript) 4 | ![Repo License](https://img.shields.io/github/license/B3zaleel/alx-backend-javascript.svg) 5 | ![Latest commit](https://img.shields.io/github/last-commit/B3zaleel/alx-backend-javascript/main?style=round-square) 6 | 7 | This repo contains projects for learning back end development concepts with __JavaScript__. 8 | --------------------------------------------------------------------------------