├── .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', 'FirstName | Location${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 | 
4 | 
5 | 
6 |
7 | This repo contains projects for learning back end development concepts with __JavaScript__.
8 |
--------------------------------------------------------------------------------