diff --git a/01 Setup.md b/01 Setup.md index 6b139c2a..a1a4e284 100644 --- a/01 Setup.md +++ b/01 Setup.md @@ -13,6 +13,37 @@ Press CTRL-SHIFT-P and a selection box should appear at the top of the editor. T Now the integrated terminal uses the _Bash Shell_ meaning you can use the standard *nix bash commands! +# If _Bash Shell_ is _not_ listed on your Windows 10 machine + +On some versions of Windows 10, some extra configuration is required before Bash Shell is able to be selected within VS Code. If this is the case on your machine, follow the below troubleshooting steps before reattempting to change your shell as above. + +To start, we need to make sure Developer Mode is enabled in the Update and Security section of Windows 10. Press **WIN+R** (Windows Key and R simultaneosly) to open a Run dialog. In the dialog enter **ms-settings:developers** followed by the enter key. The window shown below should appear on screen. + +![Enabling Windows 10 Developer Mode](exercises/.images/developerMode.PNG) + +Select the **Developer mode** radio button and **yes** on any succeeding dialog boxes. Wait for any neccessary installations to complete. + +Once that has completed, we need to enable the WSL (Windows Subsystem for Linux). Press **WIN+R**, in the dialog enter **powershell** followed by the enter key. In the powershell window copy and paste the following: + +``` +Start-Process powershell -Verb runAs +exit +``` + +Press the enter key and accept the UAC (User Account Control) dialog. A new powershell window will launch with Administrator rights. + +Copy and paste the following: + +``` +Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux +``` + +Press the enter key and wait for the process to complete. Once complete, restart your computer before continuing. + +Finally, we need to install [Ubuntu from the Microsoft Store](https://www.microsoft.com/en-gb/p/ubuntu/9nblggh4msv6?activetab=pivot:overviewtab). Click the blue **Get** button and accept any dialogs to launch the Microsoft Store and begin the installation. Once Ubuntu for WSL (Windows Subsystem for Linux) has installed, restart Windows. + +Once restarted, reattempt to enable Bash Shell in VS Code as per the section above. + ## 1 Forking the Foundation Materials You should start by logging in to the University GitHub server using your university username and password. Make sure you don't log into GitHub.com! diff --git a/02 HTTP.md b/02 HTTP.md index e62998c8..c6ff259f 100644 --- a/02 HTTP.md +++ b/02 HTTP.md @@ -85,7 +85,7 @@ Start the server and access the `/books/1` route. What is displayed in the web b 2. Note that it is itself a JavaScript object and that it contains a single property called `index` which matches the name of the URL segment. 3. The value of this property is the string we added to the URL. 4. This index value is used to look up the title of the book in the array, the book title is stored in a constant. -5. Finally the book title is send to the web browser. +5. Finally the book title is sent to the web browser. #### 1.3.1 Core Knowledge diff --git a/05 JavaScript.md b/05 JavaScript.md index 0cf27839..83177f67 100644 --- a/05 JavaScript.md +++ b/05 JavaScript.md @@ -98,8 +98,9 @@ const sqr = num => num * num 1. Refactor the `remainder2` function expression to take advantage of the implicit return (you will need to reduce it to a single line of code). 2. Compare this to the original version: which is more _readable_? 3. Create a function expression that takes two string parameters and returns the longest string and assign this to a constant called `longest. check this works correctly. -4. Modify the function expression so that it can handle any number of string parameters (use a _rest parameter_). -5. Can you reduce this function expression to a single line (hint: explore using the [reduce function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) combined with the [ternary operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator))? +4. Modify the function expression so that it can handle any number of string parameters (use a _rest parameter_). (hint: you will need to use a [`for...in`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in) statement to loop through the strings. How does this differ from a [`for...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) statement?) +5. Use a [ternary operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator) instead of the `if` statement in the loop. +6. Finally use the [`reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) method to replace the `for...in` loop to reduce the function to a single line. ## 2 Callbacks diff --git a/06 Code Quality.md b/06 Code Quality.md index ca40be68..36673e70 100644 --- a/06 Code Quality.md +++ b/06 Code Quality.md @@ -244,7 +244,7 @@ In the example above we store the data returned from the `getData` promise in th ### 6.4 Test Your Knowledge -Run the `promises.js` script, its functionality should be familiar to the `currency.js` script you worked with in chapter 3. +Run the `promises.js` script located in the otherScripts folder. Its functionality should be familiar to the `currency.js` script you worked with in chapter 3. Open the `promises.js` script and study the code carefully. Notice that it defines 5 promises and chains them together. You are going to extend the functionality by defining some additional promises and adding them to the promise chain. @@ -376,7 +376,7 @@ both `printObjectPromise` and `printObjectAsync` behave in exactly the same mann ### 7.3 Test Your Knowledge -Run the `asyncFunctions.js` script. Note that it works in the same way as the previous ones. Open the script and study it carefully. +Run the `asyncFunctions.js` script, located in the otherScripts folder. Note that it works in the same way as the previous ones. Open the script and study it carefully. 1. modify the script to ask for the currency to convert to and display only the one conversion rate. 2. instead of printing the exchange rate, ask for the amount to be converted and them return the equivalent in the chosen currency diff --git a/exercises/.images/developerMode.PNG b/exercises/.images/developerMode.PNG new file mode 100644 index 00000000..e287f8c4 Binary files /dev/null and b/exercises/.images/developerMode.PNG differ diff --git a/exercises/06_code_quality/nestedCallbacks.js b/exercises/06_code_quality/nestedCallbacks.js new file mode 100644 index 00000000..cd3fcc12 --- /dev/null +++ b/exercises/06_code_quality/nestedCallbacks.js @@ -0,0 +1,61 @@ + +'use strict' + +const request = require('request') + +getInput('enter base currency', (err, base) => { + if (err) { + console.log(err.message) + process.exit() + } + base = base.trim() + checkValidCurrencyCode(base, err => { + if (err) { + console.log(err.message) + process.exit() + } + getData(`http://api.fixer.io/latest?base=${base}`, (err, data) => { + if (err) { + console.log(err.message) + process.exit() + } + const obj = JSON.parse(data) + printObject(obj) + process.exit() + }) + }) +}) + +function getInput(prompt, callback) { + try { + process.stdin.resume() + process.stdin.setEncoding('utf8') + process.stdout.write(`${prompt}: `) + process.stdin.on('data', text => callback(null, text)) + } catch(err) { + callback(err) + } +} + +function checkValidCurrencyCode(code, callback) { + code = code.trim() + request('http://api.fixer.io/latest', (err, res, body) => { + if (err) callback(new Error('invalid API call')) + const rates = JSON.parse(body).rates + if (!rates.hasOwnProperty(code)) callback(new Error(`invalid currency code ${code}`)) + callback(null, true) + }) +} + +function getData(url, callback) { + request(url, (err, res, body) => { + if (err) callback(new Error('invalid API call')) + callback(null, body) + }) +} + +function printObject(data) { + const indent = 2 + const str = JSON.stringify(data, null, indent) + console.log(str) +} diff --git a/exercises/07_unit_testing/otherScripts/addressFinder.js b/exercises/07_unit_testing/otherScripts/addressFinder.js new file mode 100644 index 00000000..e158d201 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/addressFinder.js @@ -0,0 +1,32 @@ + +'use strict' + +const request = require('request') + +try { + if (process.argv.length < 3) { + throw 'missing parameter' + } + let address = process.argv[2] + /* we need to remove the single quotes from the string */ + address = address.replace(/'/g,'') + console.log(address) + const url = `https://maps.googleapis.com/maps/api/geocode/json?region=gb&units=metric&appid=44c39f3fa462f86b3fc88f5678e5c5ff&address=${address}` + console.log(url) + request.get( url, (err, res, body) => { + if (err) { + throw 'could not complete request' + } + const json = JSON.parse(body) + console.log(JSON.stringify(json, null, 2)) + try { + if (json.status === 'ZERO_RESULTS') { + throw 'no results found' + } + } catch(err) { + console.log(err) + } + }) +} catch(err) { + console.log(err) +} \ No newline at end of file diff --git a/exercises/07_unit_testing/otherScripts/arrowFunctions.js b/exercises/07_unit_testing/otherScripts/arrowFunctions.js new file mode 100644 index 00000000..47c6af30 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/arrowFunctions.js @@ -0,0 +1,41 @@ + +'use strict' + +/* eslint no-magic-numbers: 0, arrow-body-style: 0 */ + +// all four of these functions contain the same functionality. + +// traditional JavaScript function that takes an ECMA rest param and returns the total +// no use made of functional programming principles. +function add(...num) { + let total = 0 + + for (let i=0; i< num.length; i++) { + total += num[i] + } + return total +} + +// simple ECMAScript 5 compatible anonymous function assigned to a constant. +// Array.prototype.reduce used to eliminate the loop from the previous example. +const add2 = function(...num) { + return num.reduce( (acc, val) => acc + val) +} + +// arrow function syntax used instead of the traditional anonymous function declaration. +// normally a single parameter would not be enclosed in braces but these are needed for rest params. +const add3 = (...num) => { + return num.reduce( (acc, val) => acc + val) +} + +// if the body of the function contains a single line of code that returns a value +// the braces and return statement are not required. +const add4 = (...num) => num.reduce( (acc, val) => acc + val) + +console.log(add(1, 2, 3)) + +console.log(add2(1, 2, 3)) + +console.log(add3(1, 2, 3)) + +console.log(add4(1, 2, 3)) diff --git a/exercises/07_unit_testing/otherScripts/asyncDemo.js b/exercises/07_unit_testing/otherScripts/asyncDemo.js new file mode 100644 index 00000000..d2e18c5b --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/asyncDemo.js @@ -0,0 +1,35 @@ + +'use strict' + +const asyncFunc = (name, callback) => { + const names = ['colin', 'bob'] + if (names.indexOf(name) > -1) { + // success + const result = 'found' + return callback(null, result) + } else { + // error + const message = 'name not found' + return callback(message) + } +} + +// check username exists +const check = name => { + asyncFunc(name, (err, result) => { + if (err) { + // it didn't work i.e. not found + console.error(err) + throw new Error('broke looking for name') + } else { + // success: i.e. result has a value + console.log('It was found.') + console.log(result) + } + }) +} + +check('colin') +check('nobody') + +// (err, data) => {} is common callback signature \ No newline at end of file diff --git a/exercises/07_unit_testing/otherScripts/asyncFunctions.js b/exercises/07_unit_testing/otherScripts/asyncFunctions.js new file mode 100644 index 00000000..7d758f93 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/asyncFunctions.js @@ -0,0 +1,50 @@ + +'use strict' + +const request = require('request') + +async function main() { + try { + const base = await getInput('enter base currency') + await checkValidCurrencyCode(base) + const data = await getData(`http://api.fixer.io/latest?base=${base}`) + await printObject(data) + process.exit() + } catch (err) { + console.log(`error: ${err.message}`) + } +} + +const getInput = prompt => new Promise( (resolve) => { + process.stdin.resume() + process.stdin.setEncoding('utf8') + process.stdout.write(`${prompt}: `) + process.stdin.on('data', text => resolve(text)) +}) + +const checkValidCurrencyCode = code => new Promise( (resolve, reject) => { + code = code.trim() + request('http://api.fixer.io/latest', (err, res, body) => { + if (err) reject(new Error('invalid API call')) + const rates = JSON.parse(body).rates + if (!rates.hasOwnProperty(code)) it.throw(new Error(`invalid currency code ${code}`)) + resolve() + }) +}) + +const getData = url => new Promise( (resolve, reject) => { + request(url, (err, res, body) => { + if (err) reject(new Error('invalid API call')) + resolve(body) + }) +}) + +const printObject = data => new Promise( (resolve) => { + const indent = 2 + data = JSON.parse(data) + const str = JSON.stringify(data, null, indent) + console.log(str) + resolve() +}) + +main() diff --git a/exercises/07_unit_testing/otherScripts/books.js b/exercises/07_unit_testing/otherScripts/books.js new file mode 100755 index 00000000..83bd49aa --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/books.js @@ -0,0 +1,25 @@ +#!/usr/bin/env node + +'use strict' + +function Book(isbn, title) { + this.isbn = isbn + this.title = title + this.year = null + Object.defineProperty(this, 'published', { + get: () => this.year, + set: year => this.year = year + }) + Object.defineProperty(this, 'summary', { + get: () => `${this.title} (${this.isbn}). Published ${this.year}.` + }) +} + +const b = new Book('1491943122', 'Learning Node') +if (b instanceof Book) console.log('its a Book') +console.log(`the b object is a '${typeof b}'`) +console.log(b.published) // prints null +b.year = 2016 +console.log(b.published) // prints 2016 +console.log(b) +console.log(b.summary) diff --git a/exercises/07_unit_testing/otherScripts/classes/employee.js b/exercises/07_unit_testing/otherScripts/classes/employee.js new file mode 100755 index 00000000..4e7716f9 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/classes/employee.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node +/* eslint no-magic-numbers: 0 */ + +'use strict' + +const Person = require('./person') + +module.exports = class Employee extends Person { + + constructor(firstname, lastname, grade = 1) { + super(firstname, lastname) + this.joinedDate = new Date() + this.grade = grade + } + + calculateSalary(months = 1) { + return this.grade * 1000 * months + } + +} diff --git a/exercises/07_unit_testing/otherScripts/classes/employeeTest.js b/exercises/07_unit_testing/otherScripts/classes/employeeTest.js new file mode 100755 index 00000000..dbfde4c0 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/classes/employeeTest.js @@ -0,0 +1,22 @@ +#!/usr/bin/env node +/* eslint no-magic-numbers: 0 */ + +'use strict' + +const Employee = require('./employee') + +try { + const worker = new Employee('John', 'Doe') + console.log(worker.name) + + const salary = worker.calculateSalary() + console.log(salary) + + const manager = new Employee('Peter', 'Piper', 4) + console.log(manager.name) + console.log(manager.calculateSalary(6)) + console.log(manager) + +} catch(err) { + console.log(`ERROR: ${err}`) +} \ No newline at end of file diff --git a/exercises/07_unit_testing/otherScripts/classes/person.js b/exercises/07_unit_testing/otherScripts/classes/person.js new file mode 100755 index 00000000..1c84087e --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/classes/person.js @@ -0,0 +1,45 @@ +#!/usr/bin/env node + +'use strict' + +/** Class representing a person */ +module.exports = class Person { + + /** + * Create a person + * @param {string} firstname - the person's first name + * @param {string} lastname - the person's last name + */ + constructor(firstname, lastname) { + if (firstname === undefined || lastname === undefined) { + throw new Error('missing parameter') + } + this.first = firstname + this.last = lastname + } + + /** + * Set the person's first name + * @param {string} name - the person's first name + */ + set firstName(name) { + this.first = name + } + + /** + * Set the person's last name + * @param {string} name - the person's last name + */ + set lastName(name) { + this.last = name + } + + /** + * Get the person's full name + * @return {string} the person's full name + */ + get name() { + return `${this.first} ${this.last}` + } + +} diff --git a/exercises/07_unit_testing/otherScripts/classes/personTest.js b/exercises/07_unit_testing/otherScripts/classes/personTest.js new file mode 100755 index 00000000..3cdd030a --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/classes/personTest.js @@ -0,0 +1,16 @@ +#!/usr/bin/env node + +'use strict' + +const Person = require('./person') +try { + const person = new Person('Andy', 'Capp') + console.log(person.name) + person.lastName = 'Pandy' + console.log(JSON.stringify(person, null, 2)) + + const badPerson = new Person('anon') + console.log(badPerson) +} catch(err) { + console.log(`ERROR: ${err}`) +} \ No newline at end of file diff --git a/exercises/07_unit_testing/otherScripts/coffee/betterCoffee.js b/exercises/07_unit_testing/otherScripts/coffee/betterCoffee.js new file mode 100755 index 00000000..21cc4908 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/coffee/betterCoffee.js @@ -0,0 +1,73 @@ +#!/usr/bin/env node + +'use strict' + +/* eslint no-magic-numbers: 0 */ + +function Coffee(roast, ounces = 8) { + + const privateData = {} + + privateData.size = { + small: 8, + medium: 12, + large: 16 + } + + if (roast === undefined) { + throw new Error('missing roast type') + } + + privateData.roast = roast + privateData.ounces = ounces + + function getSize() { + if (this.ounces === this.size.small) { + return 'small' + } else if (this.ounces === this.size.medium) { + return 'medium' + } else if (this.ounces === this.size.large) { + return 'large' + } + } + + return { + order: { + get: () => { + let msg + switch (getSize()) { + case 'small': + case 'medium': + case 'large': + msg = `You've ordered a ${getSize()} ${this.roast} coffee.` + break + default: + msg = `We don't have a ${this.roast} in that size!` + break + } + return msg + } + } + } + +} // end function Coffee + +try { + const coffee = new Coffee('House Blend', 12) + console.log(coffee.order) + console.log(coffee) + + const darkRoast = new Coffee('Dark Roast', 16) + console.log(darkRoast.order) + + const specialBlend = new Coffee('Special Blend', 200) + console.log(specialBlend.order) + + const kenyan = new Coffee('Kenyan') + console.log(kenyan.order) + + const anon = new Coffee() + console.log(anon.order) +} catch(err) { + console.log(`ERROR: ${err}`) +} \ No newline at end of file diff --git a/exercises/07_unit_testing/otherScripts/coffee/coffee.js b/exercises/07_unit_testing/otherScripts/coffee/coffee.js new file mode 100755 index 00000000..29341898 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/coffee/coffee.js @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +'use strict' + +const small = 8 +const medium = 12 +const large = 16 + +module.exports = function(roast, ounces = small) { // can't use arrow functions here... + if (roast === undefined) { + throw new Error('missing roast type') + } + this.roast = roast + this.ounces = ounces + this.getSize = () => { + if (this.ounces === small) { + return 'small' + } else if (this.ounces === medium) { + return 'medium' + } else if (this.ounces === large) { + return 'large' + } + } + this.order = () => { + let msg + switch (this.getSize()) { + case 'small': + case 'medium': + case 'large': + msg = `You've ordered a ${this.getSize()} ${this.roast} coffee.` + break + default: + msg = `We don't have a ${this.roast} in that size!` + break + } + return msg + } +} diff --git a/exercises/07_unit_testing/otherScripts/coffee/coffee2.js b/exercises/07_unit_testing/otherScripts/coffee/coffee2.js new file mode 100755 index 00000000..858f1170 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/coffee/coffee2.js @@ -0,0 +1,68 @@ +#!/usr/bin/env node +/* eslint no-magic-numbers: 0 */ + +'use strict' + +function Coffee(roast, ounces = this.size.small) { + + this.size = { + small: 8, + medium: 12, + large: 16 + } + + if (roast === undefined) { + throw new Error('missing roast type') + } + + this.roast = roast + this.ounces = ounces + + this.getSize = () => { + if (this.ounces === this.size.small) { + return 'small' + } else if (this.ounces === this.size.medium) { + return 'medium' + } else if (this.ounces === this.size.large) { + return 'large' + } + } + + Object.defineProperty(this, 'order', { + get: () => { + let msg + switch (this.getSize()) { + case 'small': + case 'medium': + case 'large': + msg = `You've ordered a ${this.getSize()} ${this.roast} coffee.` + break + default: + msg = `We don't have a ${this.roast} in that size!` + break + } + return msg + } + }) + +} // end function Coffee + +try { + const coffee = new Coffee('House Blend', 12) + console.log(coffee.order) + console.log(coffee) + + const darkRoast = new Coffee('Dark Roast', 16) + console.log(darkRoast.order) + + const specialBlend = new Coffee('Special Blend', 200) + console.log(specialBlend.order) + + const kenyan = new Coffee('Kenyan') + console.log(kenyan.order) + + const anon = new Coffee() + console.log(anon.order) +} catch(err) { + console.log(`ERROR: ${err}`) +} diff --git a/exercises/07_unit_testing/otherScripts/coffee/index.js b/exercises/07_unit_testing/otherScripts/coffee/index.js new file mode 100755 index 00000000..4fa9e769 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/coffee/index.js @@ -0,0 +1,25 @@ +#!/usr/bin/env node +/* eslint no-magic-numbers: 0 */ + +'use strict' + +const Coffee = require('./coffee') + +try { + const coffee = new Coffee('House Blend', 12) + console.log(coffee.order()) + + const darkRoast = new Coffee('Dark Roast', 16) + console.log(darkRoast.order()) + + const specialBlend = new Coffee('Special Blend', 200) + console.log(specialBlend.order()) + + const kenyan = new Coffee('Kenyan') + console.log(kenyan.order()) + + const anon = new Coffee() + console.log(anon.order()) +} catch(err) { + console.log(`ERROR: ${err}`) +} diff --git a/exercises/07_unit_testing/otherScripts/counter.js b/exercises/07_unit_testing/otherScripts/counter.js new file mode 100755 index 00000000..233793b5 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/counter.js @@ -0,0 +1,33 @@ +#!/usr/bin/env node + +//'use strict' + +(function() { + console.log('Woohoo!') +})() + +// simple closure example (using an IIFE) +const add = ( () => { + let count = 0 + return () => count += 1 +})() + +add() +add() +console.log(add()) + +// IIFE returning multiple functions via an object +(() => { + let count = 0 + return { + increment: () => count += 1, + display: () => count, + reset: () => count = 0 + } +})() + +counter.increment() +counter.increment() +console.log(counter.display()) +counter.reset() +console.log(counter.display()) diff --git a/exercises/07_unit_testing/otherScripts/countries.md b/exercises/07_unit_testing/otherScripts/countries.md new file mode 100644 index 00000000..ca84a337 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/countries.md @@ -0,0 +1,11 @@ + +'use strict' + +const request = require('sync-request') + +function checkCountryCode(code = 'GB') { + const res = request('GET', `https://restcountries.eu/rest/v2/alpha/${code}`) + if (res.statusCode === 400) { + throw new Error('invalid country code') + } +} diff --git a/exercises/07_unit_testing/otherScripts/currency.js b/exercises/07_unit_testing/otherScripts/currency.js new file mode 100644 index 00000000..c833a560 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/currency.js @@ -0,0 +1,23 @@ + +'use strict' + +const request = require('request') + +try { + if (process.argv.length < 3) { + throw 'missing parameter' + } + const symbol = process.argv[2].toUpperCase() + const url = `http://api.fixer.io/latest?symbols=${symbol}` + console.log(url) + request.get( url, (err, res, body) => { + if (err) { + throw 'could not complete request' + } + const json = JSON.parse(body) + const output = JSON.stringify(json.rates, null, 2) + console.log(output) + }) +} catch(err) { + console.log(err) +} diff --git a/exercises/07_unit_testing/otherScripts/dogs/dog.js b/exercises/07_unit_testing/otherScripts/dogs/dog.js new file mode 100755 index 00000000..9fd8cc52 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/dogs/dog.js @@ -0,0 +1,41 @@ +#!/usr/bin/env node +/* eslint no-magic-numbers: 0 */ + +'use strict' + +function Dog(name, breed, weight) { + this.name = name + this.breed = breed + this.weight = weight +} + +Dog.prototype.species = 'Canine' +Dog.prototype.sitting = false + +Dog.prototype.bark = function() { + const maxWeight = 25 + if (this.weight > maxWeight) { + console.log(`${this.name} says Woof!`) + } else { + console.log(`${this.name} says Yip!`) + } +} + +Dog.prototype.run = function() { + console.log('Run!') +} + +Dog.prototype.wag = function() { + console.log('Wag!') +} + +Dog.prototype.sit = function() { + if (this.sitting) { + console.log(`${this.name} is already sitting`) + } else { + this.sitting = true + console.log(`${this.name} is now sitting`) + } +} + +module.exports = Dog diff --git a/exercises/07_unit_testing/otherScripts/dogs/index.js b/exercises/07_unit_testing/otherScripts/dogs/index.js new file mode 100755 index 00000000..29b20278 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/dogs/index.js @@ -0,0 +1,35 @@ +#!/usr/bin/env node +/* eslint no-magic-numbers: 0 */ + +'use strict' + +const Dog = require('./dog') + +const fido = new Dog('Fido', 'Mixed', 38) +const fluffy = new Dog('Fluffy', 'Poodle', 30) +const spot = new Dog('Spot', 'Chihuahua', 10) +const barnaby = new Dog('Barnaby', 'Basset Hound', 55) + +spot.bark = function() { + console.log(`${this.name} says WOOF!`) +} + +fido.bark() +fido.run() +fido.wag() + +fluffy.bark() +fluffy.run() +fluffy.wag() + +spot.bark() +spot.run() +spot.wag() + +barnaby.sit() +barnaby.sit() +spot.sit() +spot.sit() + +console.log(`Does spot have a sitting property? ${spot.hasOwnProperty('sitting')}`) +console.log(`Does fido have a sitting property? ${fido.hasOwnProperty('sitting')}`) \ No newline at end of file diff --git a/exercises/07_unit_testing/otherScripts/employee.js b/exercises/07_unit_testing/otherScripts/employee.js new file mode 100755 index 00000000..dd2753de --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/employee.js @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +'use strict' + +const employee = { + firstName: 'Colin', + 'last name': 'Stephen', + 'department': 'Computing', + employment: { + department: 'Computing', + startDate: '20120815' + } +} + +console.log(employee) +const firstName = employee.firstName +const lastName = employee['last name'] +const dept = employee.employment.department +const grade = employee.employment.grade + +console.log(`${firstName} ${lastName} in ${dept} is at grade ${grade}`) + +console.log(employee.department) +employee.department = 'Computer Science' +console.log(employee.department) + +employee.grade = 4 +console.log(employee) +console.log(grade) + +delete employee.department +console.log(employee) + +// const nonExistentObject.postCode // throws "TypeError" +// const addressObject = employee.address // returns undefined +// const postCode = employee.address.postCode // throws "TypeError" +const postCode = employee.address && employee.address.postCode +console.log(postCode) diff --git a/exercises/07_unit_testing/otherScripts/flights.js b/exercises/07_unit_testing/otherScripts/flights.js new file mode 100755 index 00000000..642d922a --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/flights.js @@ -0,0 +1,16 @@ +#!/usr/bin/env node + +'use strict' + +// example of a nested function +function flights(airline, startCode, endCode) { + return function printDetails() { + return `you are flying ${airline} from ${startCode} to ${endCode}` + } +} + +const flight = flights('KLM', 'BHX', 'JFK') +const flightSummary = flight() +console.log(flightSummary) + +// https://www.sitepoint.com/javascript-closures-demystified/ diff --git a/exercises/07_unit_testing/otherScripts/functional.js b/exercises/07_unit_testing/otherScripts/functional.js new file mode 100644 index 00000000..451944d2 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/functional.js @@ -0,0 +1,90 @@ +#!/usr/bin/env node +/* eslint no-magic-numbers: 0, arrow-body-style: 0 */ + +'use strict' + +// the examples uses these two arrays +const names = ['Mark', 'John', 'Stephen', 'James', 'William', 'John', 'Peter', 'Mark'] +const data = ['Coventry', 3.14159, 'Computer', 42, true, 365.25, 101] + +function makeUpperCase(name) { + return name.toUpperCase() +} + +const upper1 = names.map(makeUpperCase) + +console.log('upper1') +console.log(upper1) + +const upper2 = names.map( value => { + return value.toUpperCase() +}) + +console.log(upper2) + +const upper3 = names.map( value => value.toUpperCase() ) + +console.log(upper3) + +const integers = data.filter( val => Number.isInteger(val) ) + +console.log(integers) + +const strings = data.filter( val => typeof val === 'string') + +console.log(strings) + +const floats = data.filter( val => typeof val === 'number' && val % 1 !== 0) + +console.log(floats) + +const long2 = names.reduce( (acc, val) => { + if (val.length > acc.length) { + return val + } else { + return acc + } +}) + +console.log(long2) + +const long3 = names.reduce( (acc, val) => val.length > acc.length ? val : acc) + +console.log(long3) + +/* challenge: return the largest integer by chaining filter and reduce */ + +/* The Array.reduce() function takes an optional second parameter which allows you to +define a default value for the accumulator. In this example we wrap use the Array.map() +function to loop through the names. We then use the Array.reduce() function with the +accumulator defaulting to an empty array. */ + +const rev1 = names.map( value => { + const rev = value.split('').reduce( (acc, val) => { + acc.unshift(val) + return acc + }, []) + return rev.join('') +}) + +console.log(rev1) + +// rewritten using a different approach. + +function reverse(acc, val) { + return val + acc +} + +const rev = 'william'.split('').reduce(reverse, '') + +console.log(rev) + +/* As with all functional programming, with a few tweaks we can turn this into a single +line. The example below uses a different approach to reversing the strings, can you +figure out how this is done? */ + +const reverse2 = names.map( value => value.split('').reduce( (acc, val) => val + acc, '')) + +console.log(reverse2) + +/* challenge: use the Array.reduce() function to remove duplicates in the names array */ diff --git a/exercises/07_unit_testing/otherScripts/generator.js b/exercises/07_unit_testing/otherScripts/generator.js new file mode 100644 index 00000000..d493e3af --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/generator.js @@ -0,0 +1,37 @@ +'use strict' + +const request = require('request') + +/** + * wrapper to make sure the callback invokes the generator next() method + * @param {String} url - the url to callback + * @returns {undefined} - the yeild + */ +function makeAPICall(url) { + request.get( url, (err, res, body) => { + if (err) it.throw(new Error(err)) + it.next(JSON.parse(body)) + }) +} + +/** + * the generator + * @param {String} symbol - the currency symbol to process + * @returns {undefined} - the yield + */ +function *main(symbol) { + try { + const url = `http://api.fixer.io/latest?symbols=${symbol}` + const result = yield makeAPICall(url) + + console.log(JSON.stringify(result, null, 2)) + } catch(err) { + console.log(err.message) + } +} + +const it = main('GBP') + +it.next() + +// https://davidwalsh.name/async-generators diff --git a/exercises/07_unit_testing/otherScripts/generators.js b/exercises/07_unit_testing/otherScripts/generators.js new file mode 100644 index 00000000..6b20b965 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/generators.js @@ -0,0 +1,56 @@ + +// this example shows how to use a genrerator to simplify async code +// after understanding this you should move onto promises +// based on code by https://davidwalsh.name/async-generators + +'use strict' + +const request = require('request') + +function *main() { + try { + const base = yield getInput('enter base currency') + yield checkValidCurrencyCode(base) + const body = yield getData(`http://api.fixer.io/latest?base=${base}`) + const obj = JSON.parse(body) + printObject(obj) + } catch(err) { + console.log(err.message) + } + process.exit() +} + +function getInput(prompt) { + process.stdin.resume() + process.stdin.setEncoding('utf8') + process.stdout.write(`${prompt}: `) + process.stdin.on('data', text => it.next(text)) +} + +function checkValidCurrencyCode(code) { + code = code.trim() + request('http://api.fixer.io/latest', (err, res, body) => { + if (err) it.throw(new Error('invalid API call')) + const rates = JSON.parse(body).rates + if (!rates.hasOwnProperty(code)) it.throw(new Error(`invalid currency code ${code}`)) + it.next() + }) +} + +function getData(url) { + // the async call is 'hidden' in this function + request(url, (err, res, body) => { + if (err) it.throw(new Error('invalid API call')) + it.next(body) + }) + // the function does not return anything +} + +function printObject(data) { + const indent = 2 + const str = JSON.stringify(data, null, indent) + console.log(str) +} + +const it = main() +it.next() diff --git a/exercises/07_unit_testing/otherScripts/iteratingObjects.js b/exercises/07_unit_testing/otherScripts/iteratingObjects.js new file mode 100755 index 00000000..6e058ab6 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/iteratingObjects.js @@ -0,0 +1,15 @@ +#!/usr/bin/env node + +'use strict' + +const abbreviations = { + json: 'javascript object notation', + yaml: 'yet another markup language', + xml: 'extensible markup language', + csv: 'comma-separated values' +} + +for(const abbr in abbreviations) { + console.log(abbr) + console.log(abbreviations[abbr]) +} diff --git a/exercises/07_unit_testing/otherScripts/iterator.js b/exercises/07_unit_testing/otherScripts/iterator.js new file mode 100644 index 00000000..de3550d6 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/iterator.js @@ -0,0 +1,20 @@ + +'use strict' +// https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Iterators_and_Generators + +const makeIterator = array => { + let nextIndex = 0 + + return { + next: () => { + if (nextIndex < array.length) return {value: array[nextIndex++], done: false} + return {done: true} + } + } +} + +const it = makeIterator(['red', 'orange']) + +console.log(it.next().value) +console.log(it.next().value) +console.log(it.next().done) diff --git a/exercises/07_unit_testing/otherScripts/moreCurrency.js b/exercises/07_unit_testing/otherScripts/moreCurrency.js new file mode 100755 index 00000000..c80db492 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/moreCurrency.js @@ -0,0 +1,26 @@ +#!/usr/bin/env node + +'use strict' + +const readline = require('readline-sync') +const request = require('sync-request') + +// the first step is to get a JavaScript object containing the exchange rates +const base = 'GBP' +const url = `http://api.fixer.io/latest?base=${base}` +const res = request('GET', url) +const rates = JSON.parse(String(res.getBody())).rates +console.log(JSON.stringify(rates, null, 2)) + +// ask the user for the currency code, convert to uppercase and remove whitespace. Notice the methods are 'chained' +const code = String(readline.question('enter required currency code: ')).toUpperCase().trim() +console.log(`you chose code: "${code}"`) + +// does the currency code exist as a key in the rates object? +if (rates.hasOwnProperty(code)) { + console.log(`currency code ${code} is valid`) + const exchange = rates[code] + console.log(`exchange rate between ${base} and ${code} is ${exchange}`) +} else { + console.log(`the code ${code} is not valid`) +} diff --git a/exercises/07_unit_testing/otherScripts/moreFunctional.js b/exercises/07_unit_testing/otherScripts/moreFunctional.js new file mode 100644 index 00000000..fbb9c31e --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/moreFunctional.js @@ -0,0 +1,26 @@ + +'use strict' + +const arr = ['Steve', 'Alex', 'Dan', 'Alice', 'Bob'] + +arr.forEach( (element, index) => console.log(`${index}: ${element}`)) + +for(let i=0; i< arr.length; i++) { + console.log(arr[i]) +} + +const newArray = [] + +for(let i=0; i< arr.length; i++) { + if (arr[i].charAt(0) === 'A') { + newArray.push(arr[i]) + } +} + +console.log(newArray) + +console.log(arr.filter( name => name.charAt(0) === 'A')) + +//const print = item => console.log(item) + +//arr.forEach( item => print(item) ) diff --git a/exercises/07_unit_testing/otherScripts/morePromises.js b/exercises/07_unit_testing/otherScripts/morePromises.js new file mode 100644 index 00000000..c4d97678 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/morePromises.js @@ -0,0 +1,42 @@ + +'use strict' + +const request = require('request') + +try { + if (process.argv.length < 3) { + throw 'missing parameter' + } + const symbol = process.argv[2].toUpperCase() + getData(symbol) + .then( (result) => { + print(result) + }).catch( (error) => { + console.log(error) + }) +} catch(err) { + console.log(err) +} + +function getData(symbol) { + return new Promise( (resolve, reject) => { + const url = `http://api.fixer.io/latest?symbols=${symbol}` + request.get( url, (err, res, body) => { + if (err) { + reject('could not complete request') + } + const json = JSON.parse(body) + resolve(json) + }) + }) +} + +function print(data) { + return new Promise( (resolve, reject) => { + if (data.rates === undefined) { + reject('missing rates array') + } + console.log(data.rates) + resolve(data.rates) + }) +} diff --git a/exercises/07_unit_testing/otherScripts/nestedCallbacks.js b/exercises/07_unit_testing/otherScripts/nestedCallbacks.js new file mode 100644 index 00000000..cd3fcc12 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/nestedCallbacks.js @@ -0,0 +1,61 @@ + +'use strict' + +const request = require('request') + +getInput('enter base currency', (err, base) => { + if (err) { + console.log(err.message) + process.exit() + } + base = base.trim() + checkValidCurrencyCode(base, err => { + if (err) { + console.log(err.message) + process.exit() + } + getData(`http://api.fixer.io/latest?base=${base}`, (err, data) => { + if (err) { + console.log(err.message) + process.exit() + } + const obj = JSON.parse(data) + printObject(obj) + process.exit() + }) + }) +}) + +function getInput(prompt, callback) { + try { + process.stdin.resume() + process.stdin.setEncoding('utf8') + process.stdout.write(`${prompt}: `) + process.stdin.on('data', text => callback(null, text)) + } catch(err) { + callback(err) + } +} + +function checkValidCurrencyCode(code, callback) { + code = code.trim() + request('http://api.fixer.io/latest', (err, res, body) => { + if (err) callback(new Error('invalid API call')) + const rates = JSON.parse(body).rates + if (!rates.hasOwnProperty(code)) callback(new Error(`invalid currency code ${code}`)) + callback(null, true) + }) +} + +function getData(url, callback) { + request(url, (err, res, body) => { + if (err) callback(new Error('invalid API call')) + callback(null, body) + }) +} + +function printObject(data) { + const indent = 2 + const str = JSON.stringify(data, null, indent) + console.log(str) +} diff --git a/exercises/07_unit_testing/otherScripts/objectMethods.js b/exercises/07_unit_testing/otherScripts/objectMethods.js new file mode 100755 index 00000000..4b5ef0b1 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/objectMethods.js @@ -0,0 +1,54 @@ +#!/usr/bin/env node +/* eslint no-magic-numbers: 0 */ + +'use strict' + +const fiat = { + make: 'Fiat', + model: '500', + year: 1957, + color: 'Medium Blue', + passengers: 2, + convertible: false, + mileage: 88000, + fuel: 0, + started: false, + start: function() { + console.log('trying to start car...') + if (this.fuel === 0) { + console.log('The car is on empty, fill up before starting!') + } else { + console.log('car started') + this.started = true + } + }, + stop: function() { + console.log('car stopping...') + this.started = false + }, + drive: function() { + if (this.started) { + if (this.fuel > 0) { + console.log(this.make+' '+this.model + ' goes zoom zoom!') + this.fuel = this.fuel - 1 + } else { + console.log('Uh oh, out of fuel.') + this.stop() + } + } else { + console.log('You need to start the engine first.') + } + }, + addFuel: function(amount) { + console.log('adding '+amount+' units of fuel') + this.fuel = this.fuel+amount + } +} + +fiat.start() +fiat.drive() +fiat.addFuel(2) +fiat.start() +fiat.drive() +fiat.drive() +fiat.drive() diff --git a/exercises/07_unit_testing/otherScripts/promiseLoop.js b/exercises/07_unit_testing/otherScripts/promiseLoop.js new file mode 100644 index 00000000..767d9853 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/promiseLoop.js @@ -0,0 +1,57 @@ +'use strict' + +const readline = require('readline-sync') +const request = require('request') +const cheerio = require('cheerio') + +const author = String(readline.question('author name: ')).trim() + +getQuotes(author, (err, data) => { + try { + if (err) throw err + console.log(data) + } catch(err) { + console.log(`ERROR: ${err.message}`) + } +}) + +console.log('EOF') + +function getQuotes(author, callback) { + const page = 1 + scraper(author, page, (err, data) => { + if (err) return callback(err) + return callback(null, data) + }) +} + +function getPageCount(author) { + const pageCount = 10 + return new Promise( (resolve, reject) => { + const url = `http://www.quotationspage.com/search.php3?startsearch=Search&homesearch=${author}` + request.get(url, (err, res, body) => { + if (err) reject(new Error('page access error')) + const $ = cheerio.load(body, {decodeEntities: false}) + if ($('p:contains("No quotations found")').length) reject(new Error('invalid name')) + const quoteCount = $('small').text().match(/\d+/g).pop() + const pages = Math.ceil(quoteCount / pageCount) + resolve(pages) + }) + }) +} + +function getQuotes(author, page) { + return new Promise((resolve, reject) => { + const url = `http://www.quotationspage.com/search.php3?startsearch=Search&homesearch=${author}&page=${page}` + request.get(url, (err, res, body) => { + if (err) reject(new Error('page access error')) + const $ = cheerio.load(body, {decodeEntities: false}) + if ($('p:contains("No quotations found")').length) reject(new Error('invalid name')) + const quotes = [] + $('dt.quote > a').each(function(i, element) { + quotes.push(element.children[0].data) + }) + resolve(quotes) + }) + }) +} diff --git a/exercises/07_unit_testing/otherScripts/promises.js b/exercises/07_unit_testing/otherScripts/promises.js new file mode 100644 index 00000000..74c13396 --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/promises.js @@ -0,0 +1,48 @@ + +'use strict' + +const request = require('request') + +const getInput = prompt => new Promise( (resolve) => { + process.stdin.resume() + process.stdin.setEncoding('utf8') + process.stdout.write(`${prompt}: `) + process.stdin.on('data', text => resolve(text)) +}) + +const checkValidCurrencyCode = code => new Promise( (resolve, reject) => { + code = code.trim() + request('http://api.fixer.io/latest', (err, res, body) => { + if (err) reject(new Error('invalid API call')) + const rates = JSON.parse(body).rates + if (!rates.hasOwnProperty(code)) reject(new Error(`invalid currency code ${code}`)) + resolve(code) + }) +}) + +const getData = code => new Promise( (resolve, reject) => { + request(`http://api.fixer.io/latest?base=${code}`, (err, res, body) => { + if (err) reject(new Error('invalid API call')) + resolve(body) + }) +}) + +const printObject = data => new Promise( resolve => { + const indent = 2 + data = JSON.parse(data) + const str = JSON.stringify(data, null, indent) + console.log(str) + resolve() +}) + +const exit = () => new Promise( () => { + process.exit() +}) + +getInput('enter base currency') + .then(checkValidCurrencyCode) + .then(getData) + .then(printObject) + .then(exit) + .catch( err => console.error(`error: ${err.message}`)) + .then(exit) diff --git a/exercises/07_unit_testing/otherScripts/promisesAll.js b/exercises/07_unit_testing/otherScripts/promisesAll.js new file mode 100644 index 00000000..2c579bbe --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/promisesAll.js @@ -0,0 +1,51 @@ + +'use strict' + +const request = require('request') + +const getInput = prompt => new Promise( (resolve) => { + process.stdin.resume() + process.stdin.setEncoding('utf8') + process.stdout.write(`${prompt}: `) + process.stdin.on('data', text => resolve(text)) +}) + +const checkValidCurrencyCode = code => new Promise( (resolve, reject) => { + code = code.trim() + request('http://api.fixer.io/latest', (err, res, body) => { + if (err) reject(new Error('invalid API call')) + const rates = JSON.parse(body).rates + if (!rates.hasOwnProperty(code)) reject(new Error(`invalid currency code ${code}`)) + resolve(code) + }) +}) + +const exit = () => new Promise( () => { + process.exit() +}) + +getInput('enter base currency') + .then(checkValidCurrencyCode) + .then( base => { + this.base = base + this.itemPromises = ['USD', 'HKD'].map( curr => new Promise((resolve, reject) => { + const url = `http://api.fixer.io/latest?base=${this.base}&symbols=${curr}` + request.get(url, (err, res, body) => { + if (err) reject(new Error(`could not get conversion rate for ${base}`)) + resolve(body) + }) + })) + }) + .then( () => Promise.all(this.itemPromises)) + .then( results => results.forEach( item => console.log(item))) + .then(exit) + .catch( err => console.log(`error: ${err.message}`)) + .then(exit) + +// const itemPromises = ['USD', 'GBP'].map( base => new Promise((resolve, reject) => { +// const url = `http://api.fixer.io/latest?symbols=${base}` +// request.get(url, (err, res, body) => { +// if (err) reject(new Error(`could not get conversion rate for ${base}`)) +// resolve(body) +// }) +// })) diff --git a/exercises/07_unit_testing/otherScripts/promisesRace.js b/exercises/07_unit_testing/otherScripts/promisesRace.js new file mode 100644 index 00000000..8e721dba --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/promisesRace.js @@ -0,0 +1,29 @@ + +'use strict' + +const request = require('request') + +const itemPromises = ['USD', 'GBP'].map( base => new Promise((resolve, reject) => { + const url = `http://api.fixer.io/latest?symbols=${base}` + + console.log(url) + request.get(url, (err, res, body) => { + if (err) reject(new Error(`could not get conversion rate for ${base}`)) + resolve(body) + }) +})) + +Promise.race(itemPromises) + .then( result => { + console.log(result) + }).catch( err => { + console.log(`error: ${err.message}`) + }) + +const orig = [1, 2, 3, 4, 5, 6] +const res = orig + .filter( num => num % 2 === 0) + .map( num => num * num) + .reduce( (acc, val) => acc + val, 10) + +console.log(res) diff --git a/exercises/07_unit_testing/otherScripts/weather.js b/exercises/07_unit_testing/otherScripts/weather.js new file mode 100644 index 00000000..f071f61a --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/weather.js @@ -0,0 +1,30 @@ + +const readline = require('readline-sync') +const request = require('request') +const url = 'http://api.openweathermap.org/data/2.5/weather' + +const location = String(readline.question('location: ')).trim()+',GB' //otherwise defaults to the US... +const query_string = {q: location, units: "metric", appid: "44c39f3fa462f86b3fc88f5678e5c5ff"} +request.get({url: url, qs: query_string}, (err, res, body) => { + try { + if (err) { + throw err + } else { + //console.log(body) + const json = JSON.parse(body) + if (json.cod !== 200) { // test with xxxxxxxxxxxxxxxxxxxx + throw 'location not found' + } + const pretty = JSON.stringify(json, null, 2) + console.log('STATUS CODE: '+res.statusCode) + console.log(pretty) + console.log('################') + console.log(json.name) + console.log(json.sys.country) + console.log(json.weather[0].description) + console.log('################') + } + } catch(err) { + console.log(err) + } +}) diff --git a/exercises/07_unit_testing/otherScripts/webWorker.js b/exercises/07_unit_testing/otherScripts/webWorker.js new file mode 100644 index 00000000..ece70fee --- /dev/null +++ b/exercises/07_unit_testing/otherScripts/webWorker.js @@ -0,0 +1,18 @@ + +'use strict' + +const Worker = require('webworker-threads').Worker + +const worker = new Worker( () => { + this.postMessage('sending a message from inside the worker') + this.onmessage = event => { + console.log(`received message from main thread: ${event.data}`) + this.close() + } +}) + +worker.onmessage = event => { + console.log(`received message from worker: ${event.data}`) +} + +worker.postMessage('sending message from main thread') diff --git a/exercises/07_unit_testing/todo/modules/todo.js b/exercises/07_unit_testing/todo/modules/todo.js index 5a034683..f4fdd7b8 100644 --- a/exercises/07_unit_testing/todo/modules/todo.js +++ b/exercises/07_unit_testing/todo/modules/todo.js @@ -9,7 +9,7 @@ module.exports.clear = () => { module.exports.add = (item, qty) => { qty = Number(qty) - if(isNaN(qty)) throw new Error('the quantity must be a number') + if(isNaN(qty)) throw new Error('qty must be a number') data.push({item: item, qty: qty}) }