├── .all-contributorsrc ├── .gitattributes ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── CONTRIBUTING.md ├── README.md ├── common ├── Mathy.js ├── helpers │ └── expect-to-be-valid-syntax.js ├── index.js ├── module1.js └── module2.js ├── exercises-final ├── 01_block-scoping.test.js ├── 02_template-literals.test.js ├── 03_new-apis.test.js ├── 04_destructure.test.js ├── 05_modules.test.js ├── 06_object-literal.test.js ├── 07_parameters.test.js ├── 08_spread.test.js ├── 09_arrow.test.js ├── 10_class.test.js ├── 11_set.test.js ├── 12_maps.test.js ├── 13_weakmap.test.js ├── 14_promises.test.js ├── 15_async-await.test.js ├── 16_es2016.test.js ├── 17_es2017.test.js ├── 18_public-class-fields.test.js ├── 19_symbols.test.js ├── 20_iterators.test.js ├── 21_generators.test.js ├── 22_reflect.test.js ├── 23_proxies.test.js └── jest.config.js ├── exercises ├── 01_block-scoping.test.js ├── 02_template-literals.test.js ├── 03_new-apis.test.js ├── 04_destructure.test.js ├── 05_modules.test.js ├── 06_object-literal.test.js ├── 07_parameters.test.js ├── 08_spread.test.js ├── 09_arrow.test.js ├── 10_class.test.js ├── 11_set.test.js ├── 12_maps.test.js ├── 13_weakmap.test.js ├── 14_promises.test.js ├── 15_async-await.test.js ├── 16_es2016.test.js ├── 17_es2017.test.js ├── 18_public-class-fields.test.js ├── 19_symbols.test.js ├── 20_iterators.test.js ├── 21_generators.test.js ├── 22_reflect.test.js ├── 23_proxies.test.js └── jest.config.js ├── lint-staged.config.js ├── other ├── CODE_OF_CONDUCT.md └── workshop-info.md ├── package.json ├── quizzes ├── .eslintrc ├── 01_block-scoping.js ├── 02_template-literals.js ├── 03_new-apis.js ├── 04_destructure.js ├── 05_modules.js ├── 06_object-literal.js ├── 07_parameters.js ├── 08_spread.js ├── 09_arrow.js ├── 10_class.js ├── 11_set.js ├── 12_maps.js ├── 13_weakmap.js ├── 14_promises.js ├── 15_async-await.js ├── 16_es2016.js ├── 17_es2017.js ├── 18_public-class-fields.js ├── 19_symbols.js ├── 20_iterators.js ├── helpers │ ├── module-a.js │ └── module-b.js ├── pretty-log.js └── run.js ├── scripts ├── autofill-feedback-email.js ├── install.js ├── verify.js └── workshop-setup.js └── templates ├── 01_block-scoping.test.js ├── 02_template-literals.test.js ├── 03_new-apis.test.js ├── 04_destructure.test.js ├── 05_modules.test.js ├── 06_object-literal.test.js ├── 07_parameters.test.js ├── 08_spread.test.js ├── 09_arrow.test.js ├── 10_class.test.js ├── 11_set.test.js ├── 12_maps.test.js ├── 13_weakmap.test.js ├── 14_promises.test.js ├── 15_async-await.test.js ├── 16_es2016.test.js ├── 17_es2017.test.js ├── 18_public-class-fields.test.js ├── 19_symbols.test.js ├── 20_iterators.test.js ├── 21_generators.test.js ├── 22_reflect.test.js ├── 23_proxies.test.js └── jest.config.js /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | *.ignored.* 4 | .opt-in 5 | .opt-out 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | node_modules 3 | coverage 4 | scripts/workshop-setup.js 5 | scripts/verify.js 6 | scripts/install.js 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": false, 6 | "singleQuote": true, 7 | "trailingComma": 'all', 8 | "bracketSpacing": false, 9 | "jsxBracketSameLine": false 10 | } 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 8 4 | install: echo "Installation happens in the setup script" 5 | cache: 6 | directories: 7 | - node_modules 8 | notifications: 9 | email: false 10 | branches: 11 | only: 12 | - master 13 | script: 14 | - npm run setup 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for being willing to contribute! 4 | 5 | **Working on your first Pull Request?** You can learn how from this _free_ series 6 | [How to Contribute to an Open Source Project on GitHub][egghead] 7 | 8 | ## Project setup 9 | 10 | 1. Fork and clone the repo 11 | 2. Run `npm run setup` to verify your system and install dependencies 12 | 3. Create a branch for your PR 13 | 14 | You can run `npm run` to see what scripts are available. 15 | 16 | ## Add yourself as a contributor 17 | 18 | This project follows the [all contributors][all-contributors] specification. 19 | To add yourself to the table of contributors on the `README.md`, please use the 20 | automated script as part of your PR: 21 | 22 | ```console 23 | npm run add-contributor 24 | ``` 25 | 26 | Follow the prompt. If you've already added yourself to the list and are making 27 | a new type of contribution, you can run it again and select the added 28 | contribution type. The table should be autogenerated when you run this, if it 29 | is not, it will be generated when you commit. 30 | 31 | ## Where to contribute 32 | 33 | This project uses [`split-guide`](https://github.com/kentcdodds/split-guide) to 34 | make maintaining the `exercises` and `exercises-final` files easier. The 35 | `exercises` and `exercise-final` folders are actually generated code via the 36 | `generate` script. So any changes you make should go in a `templates` directory. 37 | When you've made your changes, please run `npm run generate` to regenerate the 38 | exercises and commit those changes as part of your pull request. 39 | 40 | > Learn more about `split-guide` and contributing to this project with [this video](https://youtu.be/CSVOPw_5OT0) 41 | 42 | ### Development 43 | 44 | The best workflow for adding/changing tests is to run `npm run dev` which will 45 | fire up [`onchange`][onchange] for `split-guide` and the `exercises-final` tests 46 | in watch mode ([learn more][jest-watch]). With this going, you can open up the 47 | relevant files in the `templates` directory and start making changes. When you 48 | save your changes, the files will be generated and the `exercises-final` tests 49 | will be re-run. 50 | 51 | ### Extra Credit 52 | 53 | There are definitely more features we could add tests for, and you can also add 54 | tests to existing features. You can put these at the bottom in the 55 | "Extra Credit" section with `test.skip`. This way people who work through the 56 | solutions quickly can have something to solidify their learning further while 57 | others finish the core content. Please follow the instructions above when 58 | contributing. Thanks for your help! 59 | 60 | ### Quizzes 61 | 62 | There are a bunch of quizzes in the quizzes folder. It'd be awesome to get more 63 | of these! I recorded [a video](https://youtu.be/LlNGtPFK9OM) showing how it 64 | works. Pretty much just open the `quizzes` directory and follow the current 65 | conventions there! For development, just run `npm run quiz` and start 66 | developing and seeing the output! 67 | 68 | [egghead]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github 69 | [all-contributors]: https://github.com/kentcdodds/all-contributors 70 | [onchange]: https://npmjs.com/package/onchange 71 | [jest-watch]: https://egghead.io/lessons/javascript-use-jest-s-interactive-watch-mode?pl=testing-javascript-with-jest-a36c4074 72 | -------------------------------------------------------------------------------- /common/Mathy.js: -------------------------------------------------------------------------------- 1 | export {sqrt, square, diag} 2 | 3 | function sqrt(num) { 4 | return Math.sqrt(num) 5 | } 6 | 7 | function square(num) { 8 | return num * num 9 | } 10 | 11 | function diag(x, y) { 12 | return sqrt(square(x) + square(y)) 13 | } 14 | -------------------------------------------------------------------------------- /common/helpers/expect-to-be-valid-syntax.js: -------------------------------------------------------------------------------- 1 | import {transform} from 'babel-core' 2 | import {formatStackTrace, separateMessageFromStack} from 'jest-util' 3 | 4 | expect.extend({ 5 | toBeValidSyntax(actual, expected) { 6 | let error 7 | try { 8 | transform(actual) 9 | } catch (e) { 10 | error = e 11 | } 12 | if (!error) { 13 | return {pass: true} 14 | } 15 | const {message, stack} = separateMessageFromStack(error.stack) 16 | return { 17 | pass: false, 18 | message: 19 | this.utils.matcherHint('.toBeValidSyntax', 'codeString', '') + 20 | '\n\n' + 21 | `Expected valid syntax. But it was not.\n` + 22 | `Parsing the code in the string threw:\n` + 23 | this.utils.RECEIVED_COLOR( 24 | ' ' + 25 | message + 26 | formatStackTrace(stack, { 27 | noStackTrace: true, 28 | rootDir: process.cwd(), 29 | testRegex: '', 30 | }), 31 | ), 32 | } 33 | }, 34 | }) 35 | -------------------------------------------------------------------------------- /common/index.js: -------------------------------------------------------------------------------- 1 | export * from './module1' 2 | export * from './module2' 3 | -------------------------------------------------------------------------------- /common/module1.js: -------------------------------------------------------------------------------- 1 | export const variable1 = 'Bob' 2 | export const variable2 = 'Kent' 3 | -------------------------------------------------------------------------------- /common/module2.js: -------------------------------------------------------------------------------- 1 | export const variable3 = 222 2 | export const variable4 = false 3 | -------------------------------------------------------------------------------- /exercises-final/01_block-scoping.test.js: -------------------------------------------------------------------------------- 1 | const noop = () => {} 2 | test('can be used in place of `var`', () => { 3 | let bandName = 'Queen' 4 | let isBestBand = true 5 | expect(bandName).toBe('Queen') 6 | expect(isBestBand).toBe(true) 7 | }) 8 | 9 | test('can modify the value of a `let` variable even in the next block statement', () => { 10 | let releaseName = 'ES6' 11 | { 12 | releaseName = 'ES2015' 13 | } 14 | expect(releaseName).toBe('ES2015') 15 | }) 16 | 17 | test('cannot modify the value of a `const` variable', () => { 18 | function getReleaseName() { 19 | const releaseName = 'ES6' 20 | return releaseName 21 | } 22 | expect(getReleaseName).not.toThrow() 23 | }) 24 | 25 | test('is trapped inside of an `if` statement', () => { 26 | if (true) { 27 | // Change to `var` to `let`, so that b is scoped inside of the if-statement 28 | let b = 1 29 | } 30 | expect(() => noop(b)).toThrow('b is not defined') 31 | }) 32 | 33 | test(`can't redeclare using the same name`, () => { 34 | function doLoop() { 35 | for (let i = 0; i < 10; i++) { 36 | /* eslint no-empty:"off" */ 37 | } 38 | return i 39 | } 40 | 41 | expect(doLoop).toThrow('i is not defined') 42 | }) 43 | 44 | test('means that we can start using block statements', () => { 45 | // BLOCK STATEMENT 46 | { 47 | const d = 2 48 | } 49 | 50 | expect(() => noop('d', d)).toThrow('d is not defined') 51 | }) 52 | 53 | //////// Elaboration & Feedback ///////// 54 | test('I submitted my elaboration and feedback', () => { 55 | const submitted = true 56 | expect(true).toBe(submitted) 57 | }) 58 | //////////////////////////////// 59 | 60 | //////// EXTRA CREDIT //////// 61 | 62 | test.skip('means that we can declare constant with the same name in block statement', () => { 63 | const d = 5 64 | // BLOCK STATEMENT 65 | { 66 | const d = 10 67 | expect(d).toBe(10) 68 | } 69 | expect(d).toBe(5) 70 | }) 71 | 72 | // If you get this far, try adding a few more tests, 73 | // then file a pull request to add them to the extra credit! 74 | // Learn more here: http://kcd.im/es6-workshop-contributing 75 | 76 | /* eslint no-constant-condition:0 */ 77 | -------------------------------------------------------------------------------- /exercises-final/02_template-literals.test.js: -------------------------------------------------------------------------------- 1 | test('should support string interpolation', () => { 2 | const person = { 3 | name: 'Kent C. Dodds', 4 | friends: [ 5 | 'Brooke Dodds', 6 | 'Matt Zabriskie', 7 | 'Aaron Frost', 8 | 'Dave Geddes', 9 | 'Joe Eames', 10 | 'Ryan Florence', 11 | ], 12 | } 13 | const personsFriends = `${person.name} has ${ 14 | person.friends.length 15 | } friends: ${person.friends.join(', ')}` 16 | expect(personsFriends).toBe( 17 | 'Kent C. Dodds has 6 friends: Brooke Dodds, Matt Zabriskie, Aaron Frost, Dave Geddes, Joe Eames, Ryan Florence', 18 | ) 19 | }) 20 | 21 | test(`should support multi-line strings`, () => { 22 | const multiLine = ` 23 | How cool 24 | is this!? 25 | ` 26 | expect(multiLine).toBe('\n How cool\n is this!?\n ') 27 | }) 28 | 29 | test(`should support string escaping`, () => { 30 | expect(`Hi\nthere!`).toBe('Hi\nthere!') 31 | expect(`This is \`escaped\` backticks`).toBe('This is `escaped` backticks') 32 | }) 33 | 34 | //////// EXTRA CREDIT //////// 35 | 36 | // you likely won't often use tagging, but it can be handy! 37 | test.skip(`should call the tagging function`, () => { 38 | const noun = 'World' 39 | const emotion = 'happy' 40 | const result = tagIt`Hello ${noun}! Are you feeling ${emotion} today?` 41 | expect(result).toBe( 42 | 'Hello super-cool World! Are you feeling really happy today?', 43 | ) 44 | 45 | function tagIt(literalString, ...interpolatedParts) { 46 | const firstPart = `${literalString[0]}super-cool ${interpolatedParts[0]}` 47 | const lastPart = `${literalString[1]}really ${interpolatedParts[1]}${literalString[2]}` 48 | return `${firstPart}${lastPart}` 49 | } 50 | }) 51 | 52 | //////// Elaboration & Feedback ///////// 53 | test('I submitted my elaboration and feedback', () => { 54 | const submitted = true 55 | expect(true).toBe(submitted) 56 | }) 57 | //////////////////////////////// 58 | 59 | //////// EXTRA CREDIT //////// 60 | 61 | // If you get this far, try adding a few more tests, 62 | // then file a pull request to add them to the extra credit! 63 | // Learn more here: http://kcd.im/es6-workshop-contributing 64 | -------------------------------------------------------------------------------- /exercises-final/03_new-apis.test.js: -------------------------------------------------------------------------------- 1 | test(`should be easier to determine whether a string includes another`, () => { 2 | const sentence = 'It was the best of times. It was the worst of times' 3 | const result = sentence.includes('best of times') 4 | expect(result).toBe(true) 5 | }) 6 | 7 | test(`should be easier to repeat a string`, () => { 8 | const repeated = 'abc123' 9 | const result = repeated.repeat(4) 10 | expect(result).toBe('abc123abc123abc123abc123') 11 | }) 12 | 13 | test(`should be able to take an array-like object and convert it into an array`, () => { 14 | const obj = {length: 3, 0: 'a', 1: 'b', 2: 'c'} 15 | // this is even more handy with a NodeList like that returned from document.querySelector 16 | const result = Array.from(obj) 17 | expect(result).toEqual(['a', 'b', 'c']) 18 | }) 19 | 20 | test(`should be easier to fill an array with values`, () => { 21 | const originalArray = new Array(5) 22 | const result = originalArray.fill(3, 1) 23 | expect(result).toEqual([, 3, 3, 3, 3]) // eslint-disable-line no-sparse-arrays 24 | }) 25 | 26 | test(`should be easy to copy properties from one object to another`, () => { 27 | const source1 = { 28 | a: { 29 | b: 'c', 30 | m: [1, 2, 3], 31 | }, 32 | } 33 | const source2 = { 34 | d: false, 35 | z: 34, 36 | } 37 | const source3 = { 38 | z: 42, 39 | p: ['a', 'b', 'c'], 40 | } 41 | 42 | const target = { 43 | a: { 44 | q: 'r', 45 | m: [4, 5, 6], 46 | s: { 47 | t: 3, 48 | }, 49 | }, 50 | d: true, 51 | p: ['x', 'y', 'z'], 52 | } 53 | const result = Object.assign(target, source1, source2, source3) 54 | 55 | expect(result).toEqual({ 56 | a: { 57 | b: 'c', 58 | m: [1, 2, 3], 59 | }, 60 | d: false, 61 | z: 42, 62 | p: ['a', 'b', 'c'], 63 | }) 64 | 65 | // this is only here to indicate that the assignment is not deep 66 | expect(result).not.toEqual({ 67 | a: { 68 | b: 'c', 69 | m: [1, 2, 3], 70 | q: 'r', 71 | s: { 72 | t: 3, 73 | }, 74 | }, 75 | d: false, 76 | z: 42, 77 | p: ['a', 'b', 'c'], 78 | }) 79 | }) 80 | 81 | //////// Elaboration & Feedback ///////// 82 | test('I submitted my elaboration and feedback', () => { 83 | const submitted = true 84 | expect(true).toBe(submitted) 85 | }) 86 | //////////////////////////////// 87 | 88 | //////// EXTRA CREDIT //////// 89 | 90 | // If you get this far, try adding a few more tests, 91 | // then file a pull request to add them to the extra credit! 92 | // Learn more here: http://kcd.im/es6-workshop-contributing 93 | -------------------------------------------------------------------------------- /exercises-final/04_destructure.test.js: -------------------------------------------------------------------------------- 1 | const noop = () => {} 2 | 3 | function getAddress() { 4 | return { 5 | city: 'Salt Lake City', 6 | state: 'UT', 7 | zip: 84115, 8 | coords: { 9 | lat: 40.776608, 10 | long: -111.920485, 11 | }, 12 | } 13 | } 14 | 15 | function getNumbers() { 16 | return [1, 2, 3, 4, 5, 6] 17 | } 18 | 19 | function getNestedNumbers() { 20 | return [1, 2, [3, 4, [5, 6]]] 21 | } 22 | 23 | test('can be used to pull apart objects', () => { 24 | const {city, state, zip} = getAddress() 25 | expect(city).toBe('Salt Lake City') 26 | expect(state).toBe('UT') 27 | expect(zip).toBe(84115) 28 | }) 29 | 30 | test('sets missing values to undefined', () => { 31 | const {address} = getAddress() 32 | expect(address).toBeUndefined() 33 | }) 34 | 35 | test('can alias destructured variables', () => { 36 | const {city: c, state: s, zip: z} = getAddress() 37 | expect(c).toBe('Salt Lake City') 38 | expect(s).toBe('UT') 39 | expect(z).toBe(84115) 40 | expect(() => noop(city)).toThrow() 41 | expect(() => noop(state)).toThrow() 42 | expect(() => noop(zip)).toThrow() 43 | }) 44 | 45 | test('can destructure nested variables', () => { 46 | const { 47 | coords: {lat, long}, 48 | } = getAddress() 49 | expect(lat).toBe(40.776608) 50 | expect(long).toBe(-111.920485) 51 | expect(() => noop(coords)).toThrow() 52 | }) 53 | 54 | test('can be used to pull apart arrays', () => { 55 | const [one, two] = getNumbers() 56 | expect(one).toBe(1) 57 | expect(two).toBe(2) 58 | }) 59 | 60 | test('can skip indexes in arrays', () => { 61 | const [one, , three] = getNumbers() 62 | expect(one).toBe(1) 63 | expect(three).toBe(3) 64 | expect(() => noop(two)).toThrow() 65 | }) 66 | 67 | test('can reach nested arrays', () => { 68 | const [one, , [three, , [, six]]] = getNestedNumbers() 69 | expect(one).toBe(1) 70 | expect(three).toBe(3) 71 | expect(six).toBe(6) 72 | }) 73 | 74 | // MORE AT http://www.2ality.com/2015/01/es6-destructuring.html 75 | 76 | //////// Elaboration & Feedback ///////// 77 | test('I submitted my elaboration and feedback', () => { 78 | const submitted = true 79 | expect(true).toBe(submitted) 80 | }) 81 | //////////////////////////////// 82 | 83 | //////// EXTRA CREDIT //////// 84 | 85 | // If you get this far, try adding a few more tests, 86 | // then file a pull request to add them to the extra credit! 87 | // Learn more here: http://kcd.im/es6-workshop-contributing 88 | -------------------------------------------------------------------------------- /exercises-final/05_modules.test.js: -------------------------------------------------------------------------------- 1 | import * as Mathy from '../common/Mathy' 2 | import * as IndexImport from '../common' 3 | import _ from 'lodash' 4 | import {sqrt as mySqrt, square as mySquare} from '../common/Mathy' 5 | 6 | test('can import Mathy', () => { 7 | // this one's already done! You're welcome :) 8 | expect(Mathy.sqrt).toBeDefined() 9 | expect(Mathy.square).toBeDefined() 10 | expect(Mathy.diag).toBeDefined() 11 | }) 12 | 13 | test('can specify what to import, to only retain pieces of the import', () => { 14 | expect(mySqrt).toBeDefined() 15 | expect(mySquare).toBeDefined() 16 | expect(mySqrt).toBe(Mathy.sqrt) 17 | expect(mySquare).toBe(Mathy.square) 18 | }) 19 | 20 | test('can import from my node_modules', () => { 21 | expect(_).toBeDefined() 22 | }) 23 | 24 | //////// Elaboration & Feedback ///////// 25 | test('I submitted my elaboration and feedback', () => { 26 | const submitted = true 27 | expect(true).toBe(submitted) 28 | }) 29 | //////////////////////////////// 30 | 31 | //////// EXTRA CREDIT //////// 32 | test.skip('Index import', () => { 33 | //I have noticed that using index.js is pretty common pattern 34 | //If someone has been confused about that maybe this helps 35 | expect(IndexImport.variable1).toBe('Bob') 36 | expect(IndexImport.variable2).toBe('Kent') 37 | expect(IndexImport.variable3).toBe(222) 38 | expect(IndexImport.variable4).toBe(false) 39 | }) 40 | // If you get this far, try adding a few more tests, 41 | // then file a pull request to add them to the extra credit! 42 | // Learn more here: http://kcd.im/es6-workshop-contributing 43 | -------------------------------------------------------------------------------- /exercises-final/06_object-literal.test.js: -------------------------------------------------------------------------------- 1 | test('can use shorthand for property names', () => { 2 | function createMonster(name, power) { 3 | return { 4 | type: 'Monster', 5 | name, 6 | power, 7 | attack(target) { 8 | return `${this.name} attacked ${target.name}` 9 | }, 10 | } 11 | } 12 | 13 | const godzilla = createMonster('Godzilla', 1000) 14 | const mechaGodzilla = createMonster('MechaGodzilla', 5000) 15 | expect(godzilla.name).toBe('Godzilla') 16 | expect(godzilla.power).toBe(1000) 17 | expect(godzilla.attack(mechaGodzilla)).toBe('Godzilla attacked MechaGodzilla') 18 | }) 19 | 20 | test('can use expressions as property names', () => { 21 | function createCandy(type, description) { 22 | return { 23 | tasty: true, 24 | type, 25 | [type.toUpperCase() + type.length]: description, 26 | } 27 | } 28 | 29 | const twixDescription = 30 | 'Twix is a chocolate bar made by Mars, Inc., consisting of biscuit applied with other ' + 31 | 'confectionery toppings and coatings. Twix bars are packaged in pairs, although smaller single bars are available.' 32 | const twixType = 'twix' 33 | const snickers = createCandy('twix', twixDescription) 34 | expect(snickers.tasty).toBe(true) 35 | expect(snickers.type).toBe(twixType) 36 | expect(snickers.TWIX4).toBe(twixDescription) 37 | }) 38 | 39 | //////// Elaboration & Feedback ///////// 40 | test('I submitted my elaboration and feedback', () => { 41 | const submitted = true 42 | expect(true).toBe(submitted) 43 | }) 44 | //////////////////////////////// 45 | 46 | //////// EXTRA CREDIT //////// 47 | 48 | // If you get this far, try adding a few more tests, 49 | // then file a pull request to add them to the extra credit! 50 | // Learn more here: http://kcd.im/es6-workshop-contributing 51 | -------------------------------------------------------------------------------- /exercises-final/07_parameters.test.js: -------------------------------------------------------------------------------- 1 | test('can be triggered when the incoming argument is undefined', () => { 2 | function getName(name = 'Mercury') { 3 | return name 4 | } 5 | 6 | expect(getName('Aaron')).toBe('Aaron') 7 | expect(getName()).toBe('Mercury') 8 | expect(getName(undefined)).toBe('Mercury') 9 | expect(getName(null)).toBe(null) 10 | }) 11 | 12 | test(`aren't included in arguments`, () => { 13 | function getName(name = 'Mercury') { 14 | return arguments.length 15 | } 16 | 17 | expect(getName('Aaron')).toBe(1) 18 | expect(getName(null)).toBe(1) 19 | expect(getName()).toBe(0) 20 | }) 21 | 22 | test('can trigger a function call', () => { 23 | let triggerCount = 0 24 | 25 | function getName(name = getDefault()) { 26 | return name 27 | } 28 | 29 | function getDefault() { 30 | triggerCount++ 31 | return 'Mercury' 32 | } 33 | 34 | expect(triggerCount).toBe(0) 35 | expect(getName('Aaron')).toBe('Aaron') 36 | expect(getName()).toBe('Mercury') 37 | expect(getName(undefined)).toBe('Mercury') 38 | expect(triggerCount).toBe(2) 39 | }) 40 | 41 | test('catch non-specified params', () => { 42 | function resty(first, second, ...others) { 43 | return others 44 | } 45 | 46 | expect(resty().length).toBe(0) 47 | expect(resty(1).length).toBe(0) 48 | expect(resty(1, 2).length).toBe(0) 49 | expect(resty(1, 2, 3).length).toBe(1) 50 | expect( 51 | resty(1, 2, 3, undefined, 5, undefined, 7, undefined, 9, 10).length, 52 | ).toBe(8) 53 | }) 54 | 55 | test('has a different length than `arguments`', () => { 56 | function resty(first, second, ...others) { 57 | return others.length === arguments.length 58 | } 59 | 60 | expect(resty()).toBe(true) 61 | expect(resty(1)).toBe(false) 62 | expect(resty(1, 2)).toBe(false) 63 | expect(resty(1, 2, 3)).toBe(false) 64 | expect(resty(1, 2, 3, undefined, 5, undefined, 7, undefined, 9, 10)).toBe( 65 | false, 66 | ) 67 | }) 68 | 69 | test('is an actual array, unlike arguments', () => { 70 | function resty(...args) { 71 | return args 72 | } 73 | 74 | function argy() { 75 | return arguments 76 | } 77 | 78 | const args = argy(1, 2, 3) 79 | const rests = resty(1, 2, 3) 80 | 81 | expect(Object.getPrototypeOf(args) === Object.getPrototypeOf(rests)).toBe( 82 | false, 83 | ) 84 | expect(args.splice).toBe(undefined) 85 | expect(Object.getPrototypeOf(rests)).toBe(Array.prototype) 86 | expect(rests.splice).toBeDefined() 87 | expect(rests.splice).toBe(Array.prototype.splice) 88 | }) 89 | 90 | test('it can default all arguments, optionally', () => { 91 | function myFunction({name = 'Aaron', age = 35, favoriteBand = 'Queen'} = {}) { 92 | expect(name).toBeDefined() 93 | expect(age).toBeDefined() 94 | expect(favoriteBand).toBeDefined() 95 | } 96 | 97 | myFunction({name: 'Axel', age: 37, favoriteBand: 'Taylor Swift'}) 98 | myFunction({name: 'Axel', age: 37}) 99 | myFunction({name: 'Axel'}) 100 | myFunction({}) 101 | myFunction() 102 | }) 103 | 104 | //////// Elaboration & Feedback ///////// 105 | test('I submitted my elaboration and feedback', () => { 106 | const submitted = true 107 | expect(true).toBe(submitted) 108 | }) 109 | //////////////////////////////// 110 | 111 | //////// EXTRA CREDIT //////// 112 | 113 | // If you get this far, try adding a few more tests, 114 | // then file a pull request to add them to the extra credit! 115 | // Learn more here: http://kcd.im/es6-workshop-contributing 116 | 117 | /* 118 | eslint 119 | no-unused-vars:0 120 | prefer-rest-params:0 121 | */ 122 | -------------------------------------------------------------------------------- /exercises-final/08_spread.test.js: -------------------------------------------------------------------------------- 1 | test(`should be able to call a function and spread the arguments`, () => { 2 | const args = ['a', 'b', 'c'] 3 | let calls = 0 4 | myFunction(...args) 5 | expect(calls).toBe(1) 6 | 7 | function myFunction(a, b, c) { 8 | expect(a).toBe('a') 9 | expect(b).toBe('b') 10 | expect(c).toBe('c') 11 | calls++ 12 | } 13 | }) 14 | 15 | test(`should be easier to concatenate arrays`, () => { 16 | const array1 = [1, 2, 3] 17 | const result = [...array1, 4, 5, 6] 18 | expect(result).toEqual([1, 2, 3, 4, 5, 6]) 19 | }) 20 | 21 | test(`should be able to merge properties from objects`, () => { 22 | const obj1 = { 23 | foo: 'bar', 24 | baz: 'foobar', 25 | } 26 | const result = { 27 | ...obj1, 28 | eggs: 'spam', 29 | } 30 | expect(result).toEqual({ 31 | foo: 'bar', 32 | baz: 'foobar', 33 | eggs: 'spam', 34 | }) 35 | }) 36 | 37 | //////// Elaboration & Feedback ///////// 38 | test('I submitted my elaboration and feedback', () => { 39 | const submitted = true 40 | expect(true).toBe(submitted) 41 | }) 42 | //////////////////////////////// 43 | 44 | //////// EXTRA CREDIT //////// 45 | 46 | // If you get this far, try adding a few more tests, 47 | // then file a pull request to add them to the extra credit! 48 | // Learn more here: http://kcd.im/es6-workshop-contributing 49 | -------------------------------------------------------------------------------- /exercises-final/09_arrow.test.js: -------------------------------------------------------------------------------- 1 | test('can replace traditional functions', () => { 2 | let fnMultiply, arrowMultiply 3 | 4 | fnMultiply = function(a, b) { 5 | return a * b 6 | } 7 | 8 | arrowMultiply = (a, b) => a * b 9 | 10 | expect(fnMultiply(5, 5)).toBe(arrowMultiply(5, 5)) 11 | }) 12 | 13 | test('can replace traditional functions #2', () => { 14 | const nums = [2, 5, 10] 15 | const squares = nums.map(num => num * num) 16 | 17 | expect(squares.shift()).toBe(4) 18 | expect(squares.shift()).toBe(25) 19 | expect(squares.shift()).toBe(100) 20 | }) 21 | 22 | test('binds `this` to the eval scope, not the runtime scope', () => { 23 | const person = { 24 | name: 'Aaron', 25 | greetFriends: function(friends) { 26 | return friends.map(friend => this.name + ' greets to ' + friend) 27 | }, 28 | } 29 | 30 | const friendsArray = ['Naomi', 'Jojo', 'Ryan', 'Owen'] 31 | expect(() => person.greetFriends(friendsArray)).not.toThrow() 32 | }) 33 | 34 | test('can make array filter chains more manageable', () => { 35 | const data = [ 36 | {type: 'Widget', name: 'Sprocket', price: 10.0, qty: 3}, 37 | {type: 'Widget', name: 'Bracket', price: 1.0, qty: 5}, 38 | {type: 'Widget', name: 'Brace', price: 2.5, qty: 1}, 39 | {type: 'Widget', name: 'Sprocket', price: 4.0, qty: 2}, 40 | {type: 'Food', name: 'Gouda', price: 8.75, qty: 4}, 41 | {type: 'Food', name: 'Bacon', price: 3.5, qty: 3}, 42 | {type: 'CD', name: 'Queen Best Hits', price: 5.5, qty: 5}, 43 | {type: 'CD', name: 'Brittney Best Hits', price: 6.25, qty: 3}, 44 | {type: 'CD', name: 'JT Best Hits', price: 2.25, qty: 6}, 45 | ] 46 | 47 | const shoppingList = data 48 | .filter(d => d.type != 'Widget') // Remove Widgets 49 | .filter(d => d.price < 5) // Find only remaining items with price < 5 50 | .sort((a, b) => a.qty - b.qty) // Sort by quantity, desc 51 | .map(d => d.name) // Pull just the name from each item 52 | 53 | expect(shoppingList.shift()).toBe('Bacon') 54 | expect(shoppingList.shift()).toBe('JT Best Hits') 55 | }) 56 | 57 | //////// Elaboration & Feedback ///////// 58 | test('I submitted my elaboration and feedback', () => { 59 | const submitted = true 60 | expect(true).toBe(submitted) 61 | }) 62 | //////////////////////////////// 63 | 64 | //////// EXTRA CREDIT //////// 65 | 66 | // If you get this far, try adding a few more tests, 67 | // then file a pull request to add them to the extra credit! 68 | // Learn more here: http://kcd.im/es6-workshop-contributing 69 | -------------------------------------------------------------------------------- /exercises-final/10_class.test.js: -------------------------------------------------------------------------------- 1 | test('has a constructor for initialization', () => { 2 | class Animal { 3 | constructor(name) { 4 | this.name = name 5 | } 6 | } 7 | 8 | const animal = new Animal() 9 | const dog = new Animal('Dog') 10 | 11 | expect(animal.name).toBeUndefined() 12 | expect(dog.name).toBe('Dog') 13 | }) 14 | 15 | test('constructor can have default param values', () => { 16 | class Animal { 17 | constructor(name = 'Honey Badger') { 18 | this.name = name 19 | } 20 | } 21 | 22 | const animal = new Animal() 23 | const dog = new Animal('Dog') 24 | 25 | expect(animal.name).toBe('Honey Badger') 26 | expect(dog.name).toBe('Dog') 27 | }) 28 | 29 | test('can have instance methods', () => { 30 | class Animal { 31 | constructor(name = 'Honey Badger') { 32 | this.name = name 33 | } 34 | 35 | sayName() { 36 | return `My name is: ${this.name}` 37 | } 38 | } 39 | 40 | const animal = new Animal() 41 | 42 | expect(animal.sayName).toBeDefined() 43 | expect(Animal.sayName).toBeUndefined() 44 | expect(animal.sayName()).toBe('My name is: Honey Badger') 45 | }) 46 | 47 | test('can have static methods', () => { 48 | class Animal { 49 | constructor(name = 'Honey Badger') { 50 | this.name = name 51 | } 52 | 53 | sayName() { 54 | return `My name is: ${this.name}` 55 | } 56 | 57 | static create(name) { 58 | return new Animal(name) 59 | } 60 | } 61 | 62 | const animal = new Animal() 63 | 64 | expect(animal.create).toBeUndefined() 65 | expect(Animal.create).toBeDefined() 66 | }) 67 | 68 | test('can extend another class', () => { 69 | class Animal { 70 | constructor(name = 'Honey Badger') { 71 | this.name = name 72 | } 73 | } 74 | 75 | class Dog extends Animal { 76 | sayName() { 77 | return `My name is: ${this.name}` 78 | } 79 | } 80 | 81 | const dog = new Dog('Fido') 82 | 83 | expect(dog instanceof Dog).toBe(true) 84 | expect(dog instanceof Animal).toBe(true) 85 | expect(Animal.prototype.sayName).toBeUndefined() 86 | expect(Dog.prototype.sayName).toBeDefined() 87 | }) 88 | 89 | test('can use property setters and getters', () => { 90 | class Animal { 91 | set name(name) { 92 | this._name = name 93 | } 94 | get name() { 95 | return `${this._name} type of animal` 96 | } 97 | } 98 | 99 | const animal = new Animal() 100 | animal.name = 'Dog' 101 | expect(animal.name).toBe('Dog type of animal') 102 | animal.name = 'Cat' 103 | expect(animal.name).toBe('Cat type of animal') 104 | }) 105 | 106 | //////// Elaboration & Feedback ///////// 107 | test('I submitted my elaboration and feedback', () => { 108 | const submitted = true 109 | expect(true).toBe(submitted) 110 | }) 111 | //////////////////////////////// 112 | 113 | //////// EXTRA CREDIT //////// 114 | 115 | // If you get this far, try adding a few more tests, 116 | // then file a pull request to add them to the extra credit! 117 | // Learn more here: http://kcd.im/es6-workshop-contributing 118 | -------------------------------------------------------------------------------- /exercises-final/11_set.test.js: -------------------------------------------------------------------------------- 1 | test('has an add method and a has method', () => { 2 | const mySet = new Set() 3 | mySet.add(1) 4 | mySet.add(2) 5 | mySet.add(3) 6 | 7 | expect(mySet.has(1)).toBe(true) 8 | expect(mySet.has(2)).toBe(true) 9 | expect(mySet.has(3)).toBe(true) 10 | expect(mySet.has(4)).toBe(false) 11 | }) 12 | 13 | test('doesn`t allow duplicates', () => { 14 | const mySet = new Set() 15 | mySet.add(1) 16 | mySet.add(1) 17 | mySet.add(1) 18 | 19 | expect(mySet.has(1)).toBe(true) 20 | expect(mySet.has(2)).toBe(false) 21 | expect(mySet.has(3)).toBe(false) 22 | expect(mySet.has(4)).toBe(false) 23 | }) 24 | 25 | //////// Elaboration & Feedback ///////// 26 | test('I submitted my elaboration and feedback', () => { 27 | const submitted = true 28 | expect(true).toBe(submitted) 29 | }) 30 | //////////////////////////////// 31 | 32 | //////// EXTRA CREDIT //////// 33 | 34 | // If you get this far, try adding a few more tests, 35 | // then file a pull request to add them to the extra credit! 36 | // Learn more here: http://kcd.im/es6-workshop-contributing 37 | -------------------------------------------------------------------------------- /exercises-final/12_maps.test.js: -------------------------------------------------------------------------------- 1 | test('has a set method', () => { 2 | const myMap = new Map() 3 | myMap.set('name', 'Aaron') 4 | 5 | expect(myMap.get('name')).toBe('Aaron') 6 | }) 7 | 8 | test('can use objects as a key', () => { 9 | const user = {name: 'Aaron'} 10 | const value = {twitter: '@js_dev', gplus: '+AaronFrost'} 11 | 12 | const myMap = new Map() 13 | myMap.set(user, value) 14 | 15 | expect(myMap.has(user)).toBe(true) 16 | expect(myMap.get(user)).toBe(value) 17 | }) 18 | 19 | test(`doesn't coerce keys`, () => { 20 | const myMap = new Map() 21 | myMap.set(1, 'Aaron') 22 | expect(myMap.get('1')).toBe(undefined) 23 | myMap.set('1', 'Aaron') 24 | expect(myMap.get('1')).toBe('Aaron') 25 | }) 26 | 27 | //////// Elaboration & Feedback ///////// 28 | test('I submitted my elaboration and feedback', () => { 29 | const submitted = true 30 | expect(true).toBe(submitted) 31 | }) 32 | //////////////////////////////// 33 | 34 | //////// EXTRA CREDIT //////// 35 | 36 | // If you get this far, try adding a few more tests, 37 | // then file a pull request to add them to the extra credit! 38 | // Learn more here: http://kcd.im/es6-workshop-contributing 39 | -------------------------------------------------------------------------------- /exercises-final/13_weakmap.test.js: -------------------------------------------------------------------------------- 1 | test('has a set method', () => { 2 | const key = {name: 'Aaron'} 3 | const value = {twitter: '@js_dev', gplus: '+AaronFrost'} 4 | const myMap = new WeakMap() 5 | myMap.set(key, value) 6 | expect(myMap.has(key)).toBe(true) 7 | }) 8 | 9 | test(`should enable private members in classes`, () => { 10 | const privateData = new WeakMap() 11 | class Person { 12 | constructor(name, age) { 13 | privateData.set(this, {name, age}) 14 | } 15 | 16 | getName() { 17 | return privateData.get(this).name 18 | } 19 | 20 | getAge() { 21 | return privateData.get(this).age 22 | } 23 | } 24 | 25 | const person = new Person('Kent C. Dodds', 26) 26 | expect(person._name).toBeUndefined() 27 | expect(person.getName()).toBe('Kent C. Dodds') 28 | expect(person._age).toBeUndefined() 29 | expect(person.getAge()).toBe(26) 30 | }) 31 | 32 | //////// Elaboration & Feedback ///////// 33 | test('I submitted my elaboration and feedback', () => { 34 | const submitted = true 35 | expect(true).toBe(submitted) 36 | }) 37 | //////////////////////////////// 38 | 39 | //////// EXTRA CREDIT //////// 40 | 41 | // If you get this far, try adding a few more tests, 42 | // then file a pull request to add them to the extra credit! 43 | // Learn more here: http://kcd.im/es6-workshop-contributing 44 | -------------------------------------------------------------------------------- /exercises-final/14_promises.test.js: -------------------------------------------------------------------------------- 1 | test(`should resolve`, () => { 2 | return pickApple('ripe') 3 | .then( 4 | result => { 5 | expect(result).toBe('ripe apple') 6 | }, 7 | error => { 8 | throw new Error('this should not run') 9 | }, 10 | ) 11 | .catch(error => { 12 | throw new Error('this should not run') 13 | }) 14 | }) 15 | 16 | test(`should reject`, () => { 17 | return pickApple('unripe') 18 | .then( 19 | result => { 20 | throw new Error('this should not run') 21 | }, 22 | error => { 23 | expect(error).toBe('unripe apple') 24 | }, 25 | ) 26 | .catch(error => { 27 | throw new Error('this should not run') 28 | }) 29 | }) 30 | 31 | test(`errors can be caught`, () => { 32 | return pickApple() 33 | .then(result => { 34 | throw new Error('this should not run') 35 | }) 36 | .catch(error => { 37 | expect(error.message).toBe('out of apples') 38 | }) 39 | }) 40 | 41 | function pickApple(ripeness) { 42 | // Immediately return a promise which will eventually get resolved 43 | // or rejected by calling the corresponding function. 44 | return new Promise((resolve, reject) => { 45 | // Do something asynchronous. Could be AJAX, using a timeout here. 46 | setTimeout(() => { 47 | if (ripeness === 'ripe') { 48 | resolve('ripe apple') 49 | } else if (ripeness === 'unripe') { 50 | reject('unripe apple') 51 | } else { 52 | reject(new Error('out of apples')) 53 | } 54 | }) 55 | }) 56 | } 57 | 58 | //////// Elaboration & Feedback ///////// 59 | test('I submitted my elaboration and feedback', () => { 60 | const submitted = true 61 | expect(true).toBe(submitted) 62 | }) 63 | //////////////////////////////// 64 | 65 | //////// EXTRA CREDIT //////// 66 | 67 | // If you get this far, try adding a few more tests, 68 | // then file a pull request to add them to the extra credit! 69 | // Learn more here: http://kcd.im/es6-workshop-contributing 70 | -------------------------------------------------------------------------------- /exercises-final/15_async-await.test.js: -------------------------------------------------------------------------------- 1 | test('should work with resolved promises', async () => { 2 | const result = await doAsync() 3 | expect(result).toBe('resolved') 4 | }) 5 | 6 | test('should throw an error with a rejected promise', async () => { 7 | try { 8 | await doAsync(true) 9 | throw new Error('this should not run') 10 | } catch (error) { 11 | expect(error).toBe('rejected') 12 | } 13 | }) 14 | 15 | function doAsync(rejectPromise = false) { 16 | return new Promise((resolve, reject) => { 17 | setTimeout(() => { 18 | if (rejectPromise) { 19 | reject('rejected') 20 | } else { 21 | resolve('resolved') 22 | } 23 | }) 24 | }) 25 | } 26 | 27 | //////// Elaboration & Feedback ///////// 28 | test('I submitted my elaboration and feedback', () => { 29 | const submitted = true 30 | expect(true).toBe(submitted) 31 | }) 32 | //////////////////////////////// 33 | 34 | //////// EXTRA CREDIT //////// 35 | 36 | // If you get this far, try adding a few more tests, 37 | // then file a pull request to add them to the extra credit! 38 | // Learn more here: http://kcd.im/es6-workshop-contributing 39 | -------------------------------------------------------------------------------- /exercises-final/16_es2016.test.js: -------------------------------------------------------------------------------- 1 | test('the exponentiation operation can be used to raise a number to a power of another number', () => { 2 | const result = 3 ** 2 3 | expect(result).toBe(9) 4 | }) 5 | 6 | test('array.includes can be used to determine whether an item exists in an array', () => { 7 | const bestFriend = {name: 'Sindre Sorhus'} 8 | const greatFriends = [ 9 | bestFriend, 10 | {name: 'Dustan Kasten'}, 11 | {name: 'Sam Saccone'}, 12 | {name: 'Ingvar Stepanyan'}, 13 | ] 14 | const result = greatFriends.includes(bestFriend) 15 | expect(result).toBe(true) 16 | }) 17 | 18 | //////// Elaboration & Feedback ///////// 19 | test('I submitted my elaboration and feedback', () => { 20 | const submitted = true 21 | expect(true).toBe(submitted) 22 | }) 23 | //////////////////////////////// 24 | 25 | //////// EXTRA CREDIT //////// 26 | 27 | // If you get this far, try adding a few more tests, 28 | // then file a pull request to add them to the extra credit! 29 | // Learn more here: http://kcd.im/es6-workshop-contributing 30 | -------------------------------------------------------------------------------- /exercises-final/17_es2017.test.js: -------------------------------------------------------------------------------- 1 | test('String.prototype.padStart saves us from left-pad-gate', () => { 2 | const originalString = 'Worlds Finest' 3 | const result = originalString.padStart(17) 4 | expect(result).toBe(' Worlds Finest') 5 | }) 6 | 7 | test('String.prototype.padEnd (and padStart) can be given a string to pad with', () => { 8 | const originalString = 'Stronger Together' 9 | const result = originalString.padEnd(27, '-123') 10 | expect(result).toBe('Stronger Together-123-123-1') 11 | }) 12 | 13 | test('Object.values gets just the values of an object', () => { 14 | const show = { 15 | title: 'Supergirl', 16 | seasons: 1.2, 17 | characters: [ 18 | 'Supergirl', 19 | 'Cat Grant', 20 | 'Superman', 21 | 'Jimmy Olsen', 22 | 'Hank Henshaw', 23 | 'Winn Schott', 24 | 'Alex Danvers', 25 | ], 26 | } 27 | const result = Object.values(show) 28 | expect(result).toEqual([ 29 | 'Supergirl', 30 | 1.2, 31 | [ 32 | 'Supergirl', 33 | 'Cat Grant', 34 | 'Superman', 35 | 'Jimmy Olsen', 36 | 'Hank Henshaw', 37 | 'Winn Schott', 38 | 'Alex Danvers', 39 | ], 40 | ]) 41 | }) 42 | 43 | test('Object.entries gives an array of arrays as [key, value]', () => { 44 | const show = { 45 | title: 'The Flash', 46 | seasons: 2.2, 47 | characters: [ 48 | 'The Flash', 49 | 'Iris West', 50 | 'Caitlin Snow', 51 | 'Eddie Thawne', // 😢 52 | 'Cisco Ramon', 53 | 'Harrison Wells', 54 | 'Joe West', 55 | ], 56 | } 57 | const result = Object.entries(show) 58 | expect(result).toEqual([ 59 | ['title', 'The Flash'], 60 | ['seasons', 2.2], 61 | [ 62 | 'characters', 63 | [ 64 | 'The Flash', 65 | 'Iris West', 66 | 'Caitlin Snow', 67 | 'Eddie Thawne', 68 | 'Cisco Ramon', 69 | 'Harrison Wells', 70 | 'Joe West', 71 | ], 72 | ], 73 | ]) 74 | }) 75 | 76 | test('Trailing commas in function parameter lists and calls help us with git', () => { 77 | // becasue this is a syntax thing, we'll put it inside a string and use `eval` to make sure 78 | // that you get the syntax correct :) 79 | expect(` 80 | function foo( 81 | a, 82 | b, 83 | c, 84 | ) { 85 | log(a, b, c) 86 | } 87 | 88 | foo( 89 | 1, 90 | 2, 91 | 3, 92 | ) 93 | 94 | function bar( 95 | a, 96 | b, 97 | ...rest 98 | ) { 99 | log(a, b, ...rest) 100 | } 101 | bar( 102 | 1, 2, 3, 103 | 4, 5, 6, 104 | ) 105 | 106 | function log() { 107 | // do nothing :) 108 | } 109 | `).toBeValidSyntax() 110 | }) 111 | 112 | //////// Elaboration & Feedback ///////// 113 | test('I submitted my elaboration and feedback', () => { 114 | const submitted = true 115 | expect(true).toBe(submitted) 116 | }) 117 | //////////////////////////////// 118 | 119 | //////// EXTRA CREDIT //////// 120 | 121 | // If you get this far, try adding a few more tests, 122 | // then file a pull request to add them to the extra credit! 123 | // Learn more here: http://kcd.im/es6-workshop-contributing 124 | 125 | // there's also this fancy Object.getOwnPropertyDescriptors thing, 126 | // but you'll likely rarely use that directly... 127 | // https://github.com/tc39/proposal-object-getownpropertydescriptors 128 | // Maybe you could make a PR to add a test for this?! 129 | -------------------------------------------------------------------------------- /exercises-final/18_public-class-fields.test.js: -------------------------------------------------------------------------------- 1 | test('public class fields help us avoid .bind-ing everything', () => { 2 | class FakeReactComponent { 3 | constructor(props) { 4 | this.props = props 5 | this.setState = () => {} // just for fun 6 | } 7 | } 8 | 9 | class MyComponent extends FakeReactComponent { 10 | handleClick = ({target: {value}}) => { 11 | this.props.onClick(value) 12 | } 13 | render() { 14 | // weird JSX stuff here 15 | } 16 | // this is just so we can test things out 17 | testClick(value) { 18 | const fakeEvent = {target: {value}} 19 | this.handleClick(fakeEvent) 20 | } 21 | } 22 | 23 | const onClick = jest.fn() 24 | const myComponent = new MyComponent({onClick}) 25 | myComponent.testClick('hello world') 26 | expect(onClick).toHaveBeenCalledTimes(1) 27 | expect(onClick).toHaveBeenCalledWith('hello world') 28 | }) 29 | 30 | //////// Elaboration & Feedback ///////// 31 | test('I submitted my elaboration and feedback', () => { 32 | const submitted = true 33 | expect(true).toBe(submitted) 34 | }) 35 | //////////////////////////////// 36 | 37 | //////// EXTRA CREDIT //////// 38 | 39 | // If you get this far, try adding a few more tests, 40 | // then file a pull request to add them to the extra credit! 41 | // Learn more here: http://kcd.im/es6-workshop-contributing 42 | -------------------------------------------------------------------------------- /exercises-final/19_symbols.test.js: -------------------------------------------------------------------------------- 1 | test('creating symbols', () => { 2 | const symbol = Symbol() 3 | expect(typeof symbol).toBe('symbol') 4 | }) 5 | 6 | test('giving a symbol a description', () => { 7 | const symbol = Symbol('use the force') 8 | expect(String(symbol)).toBe('Symbol(use the force)') 9 | }) 10 | 11 | test('symbols are unique', () => { 12 | const s1 = Symbol() 13 | const s2 = Symbol() 14 | expect(s1 === s2).toBe(false) 15 | 16 | const s3 = Symbol('I am a symbol') 17 | const s4 = Symbol('I am a symbol') 18 | expect(s3 === s4).toBe(false) 19 | }) 20 | 21 | test('symbols on objects', () => { 22 | const symbol = Symbol('metadata') 23 | const game = { 24 | name: 'The Legend of Zelda', 25 | releaseDate: 'February 21, 1986', 26 | [symbol]: { 27 | fans: 'about a billion', 28 | }, 29 | } 30 | 31 | expect(JSON.parse(JSON.stringify(game))).toEqual({ 32 | name: 'The Legend of Zelda', 33 | releaseDate: 'February 21, 1986', 34 | }) 35 | expect(game[symbol]).toEqual({ 36 | fans: 'about a billion', 37 | }) 38 | }) 39 | 40 | //////// Elaboration & Feedback ///////// 41 | test('I submitted my elaboration and feedback', () => { 42 | const submitted = true 43 | expect(true).toBe(submitted) 44 | }) 45 | //////////////////////////////// 46 | 47 | //////// EXTRA CREDIT //////// 48 | 49 | // If you get this far, try adding a few more tests, 50 | // then file a pull request to add them to the extra credit! 51 | // Learn more here: http://kcd.im/es6-workshop-contributing 52 | -------------------------------------------------------------------------------- /exercises-final/20_iterators.test.js: -------------------------------------------------------------------------------- 1 | test('can get the iterator from an array', () => { 2 | const array = [1, 2, 3] 3 | const iterator = array[Symbol.iterator]() 4 | expect(typeof iterator.next === 'function').toBe(true) 5 | }) 6 | 7 | test('can next() the iterator multiple times', () => { 8 | const string = 'hello' // <-- YES, this is iterable! 9 | const iterator = string[Symbol.iterator]() 10 | expect(iterator.next()).toEqual({value: 'h', done: false}) 11 | expect(iterator.next()).toEqual({value: 'e', done: false}) 12 | expect(iterator.next()).toEqual({value: 'l', done: false}) 13 | expect(iterator.next()).toEqual({value: 'l', done: false}) 14 | expect(iterator.next()).toEqual({value: 'o', done: false}) 15 | expect(iterator.next()).toEqual({value: undefined, done: true}) 16 | expect(iterator.next()).toEqual({value: undefined, done: true}) 17 | }) 18 | 19 | test('can iterate over an interable with for .. of', () => { 20 | const array = [1, 2, 3] 21 | let sum = 0 22 | for (let val of array) { 23 | sum += val 24 | } 25 | expect(sum).toBe(6) 26 | }) 27 | 28 | test('can use the ... operator on the iterator', () => { 29 | const set = new Set([1, 2, 2, 3]) 30 | const [first, ...rest] = set 31 | expect(rest).toEqual([2, 3]) 32 | }) 33 | 34 | test('can create a custom iterator', () => { 35 | const randomRandomNumbersGenerator = { 36 | max: 20, 37 | min: 10, 38 | [Symbol.iterator]() { 39 | const random = () => 40 | Math.floor(Math.random() * (this.max - this.min)) + this.min 41 | const randomNumbers = random() 42 | let index = 0 43 | return { 44 | next() { 45 | const done = index >= randomNumbers 46 | const value = done ? undefined : random() 47 | index++ 48 | return {value, done} 49 | }, 50 | } 51 | }, 52 | } 53 | 54 | expect(iteratorWorks()).toBe(true) 55 | 56 | function iteratorWorks() { 57 | const randomNumbers = [...randomRandomNumbersGenerator] 58 | const {max, min} = randomRandomNumbersGenerator 59 | const tooManyNumbers = randomNumbers.length > max 60 | const tooFewNumbers = randomNumbers.length < min 61 | const numbersInBounds = randomNumbers.every(num => num <= max && num >= min) 62 | return !tooManyNumbers && !tooFewNumbers && numbersInBounds 63 | } 64 | }) 65 | 66 | test('can create a custom iterator with a generator', () => { 67 | const randomRandomNumbersGenerator = { 68 | max: 20, 69 | min: 10, 70 | *[Symbol.iterator]() { 71 | const random = () => 72 | Math.floor(Math.random() * (this.max - this.min)) + this.min 73 | const randomNumbers = random() 74 | for (var i = 0; i < randomNumbers; i++) { 75 | yield random() 76 | } 77 | }, 78 | } 79 | 80 | expect(iteratorWorks()).toBe(true) 81 | 82 | function iteratorWorks() { 83 | const randomNumbers = [...randomRandomNumbersGenerator] 84 | const {max, min} = randomRandomNumbersGenerator 85 | const tooManyNumbers = randomNumbers.length > max 86 | const tooFewNumbers = randomNumbers.length < min 87 | const numbersInBounds = randomNumbers.every(num => num <= max && num >= min) 88 | return !tooManyNumbers && !tooFewNumbers && numbersInBounds 89 | } 90 | }) 91 | 92 | //////// Elaboration & Feedback ///////// 93 | test('I submitted my elaboration and feedback', () => { 94 | const submitted = true 95 | expect(true).toBe(submitted) 96 | }) 97 | //////////////////////////////// 98 | 99 | //////// EXTRA CREDIT //////// 100 | 101 | test.skip('add custom iterator to built-in types', () => { 102 | // got this trick from getify's "ES6, the best parts" 103 | // on frontend masters. Pretty neat! (though, I wouldn't 104 | // recommend actually doing it...) 105 | Number.prototype[Symbol.iterator] = function* numberIterator() { 106 | for (let i = 0; i < this; i++) { 107 | yield i 108 | } 109 | } 110 | const num = 5 111 | const result = [...num] 112 | expect(result).toEqual([0, 1, 2, 3, 4]) 113 | }) 114 | 115 | // If you get this far, try adding a few more tests, 116 | // then file a pull request to add them to the extra credit! 117 | // Learn more here: http://kcd.im/es6-workshop-contributing 118 | -------------------------------------------------------------------------------- /exercises-final/21_generators.test.js: -------------------------------------------------------------------------------- 1 | test(`should yield objects with value and done properties`, () => { 2 | const odds = giveMeOneOddNumber() 3 | 4 | expect(typeof odds).toBe('object') 5 | expect(odds.next).toBeDefined() 6 | expect(odds.next().value).toBe(1) 7 | expect(odds.next().value).toBe(3) 8 | expect(odds.next().done).toBe(false) 9 | odds.next() 10 | expect(odds.next().value).toBe(9) 11 | expect(odds.next().done).toBe(true) 12 | 13 | function* giveMeOneOddNumber() { 14 | yield 1 15 | yield 3 16 | yield 5 17 | yield 7 18 | yield 9 19 | } 20 | }) 21 | 22 | test(`can be iterated over`, () => { 23 | function* giveMeOneEvenNumber() { 24 | yield 2 25 | yield 4 26 | yield 6 27 | yield 8 28 | } 29 | 30 | let sum = 0 31 | 32 | // BEWARE, THIS IS BLOCKING/SYNCHRONOUS! 33 | // Generators are not async/await, those may be in ES2016 34 | for (let even of giveMeOneEvenNumber()) { 35 | sum = sum + even 36 | } 37 | 38 | expect(sum).toBe(20) 39 | }) 40 | 41 | //////// Elaboration & Feedback ///////// 42 | test('I submitted my elaboration and feedback', () => { 43 | const submitted = true 44 | expect(true).toBe(submitted) 45 | }) 46 | //////////////////////////////// 47 | 48 | //////// EXTRA CREDIT //////// 49 | 50 | // If you get this far, try adding a few more tests, 51 | // then file a pull request to add them to the extra credit! 52 | // Learn more here: http://kcd.im/es6-workshop-contributing 53 | -------------------------------------------------------------------------------- /exercises-final/22_reflect.test.js: -------------------------------------------------------------------------------- 1 | test('Reflect.apply can be used to call a function', () => { 2 | const person = { 3 | name: 'Fred', 4 | sayHi(greeting, noun) { 5 | return `${greeting} ${noun}! My name is ${this.name}` 6 | }, 7 | } 8 | 9 | const result = Reflect.apply(person.sayHi, person, ['Hey there', 'Jaimee']) 10 | expect(result).toBe('Hey there Jaimee! My name is Fred') 11 | }) 12 | 13 | test('Reflect.deleteProperty can be used instead of the `delete` keyword', () => { 14 | const person = {name: 'Joan', age: 56} 15 | Reflect.defineProperty(person, 'protected', { 16 | configurable: false, 17 | value: 'YOU CANNOT GET RID OF ME!', 18 | }) 19 | const ageDeleted = Reflect.deleteProperty(person, 'age') 20 | const protectedDeleted = Reflect.deleteProperty(person, 'protected') 21 | expect(person.age).not.toBeDefined() 22 | expect(ageDeleted).toBe(true) 23 | expect(person.protected).toBe('YOU CANNOT GET RID OF ME!') 24 | expect(protectedDeleted).toBe(false) 25 | }) 26 | 27 | test(`Reflect.ownKeys returns the object's own (not inherited) keys (including symbols)`, () => { 28 | const exists = Symbol('existance') 29 | const person = {human: true, [exists]: true} 30 | const favoriteFeature = Symbol('Fav Feat') 31 | const kyle = { 32 | __proto__: person, 33 | awesome: true, 34 | [favoriteFeature]: 'destructuring', 35 | } 36 | Reflect.defineProperty(kyle, 'favoriteLanguage', { 37 | value: 'JS', 38 | configurable: false, 39 | enumerable: false, 40 | }) 41 | expect(Object.keys(kyle)).toEqual(['awesome']) 42 | expect(Object.getOwnPropertyNames(kyle)).toEqual([ 43 | 'awesome', 44 | 'favoriteLanguage', 45 | ]) 46 | expect(Object.getOwnPropertySymbols(kyle)).toEqual([favoriteFeature]) 47 | expect(Reflect.ownKeys(kyle)).toEqual([ 48 | 'awesome', 49 | 'favoriteLanguage', 50 | favoriteFeature, 51 | ]) 52 | }) 53 | 54 | //////// Elaboration & Feedback ///////// 55 | test('I submitted my elaboration and feedback', () => { 56 | const submitted = true 57 | expect(true).toBe(submitted) 58 | }) 59 | //////////////////////////////// 60 | 61 | //////// EXTRA CREDIT //////// 62 | 63 | // If you get this far, try adding a few more tests, 64 | // then file a pull request to add them to the extra credit! 65 | // Learn more here: http://kcd.im/es6-workshop-contributing 66 | -------------------------------------------------------------------------------- /exercises-final/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | setupTestFrameworkScriptFile: require.resolve( 3 | '../common/helpers/expect-to-be-valid-syntax.js', 4 | ), 5 | testURL: 'http://localhost/', 6 | } 7 | -------------------------------------------------------------------------------- /exercises/01_block-scoping.test.js: -------------------------------------------------------------------------------- 1 | const noop = () => {} 2 | test('can be used in place of `var`', () => { 3 | // Declare bandName using 'let' 4 | // Declare isBestBand using 'let' 5 | expect(bandName).toBe('Queen') 6 | expect(isBestBand).toBe(true) 7 | }) 8 | 9 | test('can modify the value of a `let` variable even in the next block statement', () => { 10 | let releaseName = 'ES6' 11 | { 12 | releaseName = 'ES2015' 13 | } 14 | expect(releaseName).toBe(/* ENTER YOUR GUESS HERE */) 15 | }) 16 | 17 | test('cannot modify the value of a `const` variable', () => { 18 | function getReleaseName() { 19 | // Pick your side. Do you call it ES6, or ES2015? 20 | // You cannot have `const` and reassign the value! 21 | const releaseName = 'ES6' // If you call it ES2015, then change this to let or var 22 | releaseName = 'ES2015' // If you call it ES6, then remove this reassignment 23 | return releaseName 24 | } 25 | expect(getReleaseName).not.toThrow() 26 | }) 27 | 28 | test('is trapped inside of an `if` statement', () => { 29 | if (true) { 30 | // Change to `var` to `let`, so that b is scoped inside of the if-statement 31 | var b = 1 32 | } 33 | expect(() => noop(b)).toThrow('b is not defined') 34 | }) 35 | 36 | test(`can't redeclare using the same name`, () => { 37 | function doLoop() { 38 | // Change loop counter to `let` so that it is trapped inside of the loop, and can't be returned. 39 | for (var i = 0; i < 10; i++) { 40 | /* eslint no-empty:"off" */ 41 | } 42 | return i 43 | } 44 | 45 | expect(doLoop).toThrow('i is not defined') 46 | }) 47 | 48 | test('means that we can start using block statements', () => { 49 | // BLOCK STATEMENT 50 | { 51 | // Change to `const` declaration 52 | var d = 2 53 | } 54 | 55 | expect(() => noop('d', d)).toThrow('d is not defined') 56 | }) 57 | 58 | //////// Elaboration & Feedback ///////// 59 | /* 60 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Block+Scoping&em= 61 | */ 62 | test('I submitted my elaboration and feedback', () => { 63 | const submitted = false // change this when you've submitted! 64 | expect(true).toBe(submitted) 65 | }) 66 | //////////////////////////////// 67 | 68 | //////// EXTRA CREDIT //////// 69 | 70 | test.skip('means that we can declare constant with the same name in block statement', () => { 71 | // Declare a 'd' using 'const', setting the value to 5 72 | // BLOCK STATEMENT 73 | { 74 | // Declare a 'd' using 'const', setting the value to 10 75 | expect(d).toBe(10) 76 | } 77 | expect(d).toBe(5) 78 | }) 79 | 80 | // If you get this far, try adding a few more tests, 81 | // then file a pull request to add them to the extra credit! 82 | // Learn more here: http://kcd.im/es6-workshop-contributing 83 | 84 | /* eslint no-constant-condition:0 */ 85 | -------------------------------------------------------------------------------- /exercises/02_template-literals.test.js: -------------------------------------------------------------------------------- 1 | test('should support string interpolation', () => { 2 | const person = { 3 | name: 'Kent C. Dodds', 4 | friends: [ 5 | 'Brooke Dodds', 6 | 'Matt Zabriskie', 7 | 'Aaron Frost', 8 | 'Dave Geddes', 9 | 'Joe Eames', 10 | 'Ryan Florence', 11 | ], 12 | } 13 | // construct a string using template literal string interpolation 14 | const personsFriends = `` 15 | expect(personsFriends).toBe( 16 | 'Kent C. Dodds has 6 friends: Brooke Dodds, Matt Zabriskie, Aaron Frost, Dave Geddes, Joe Eames, Ryan Florence', 17 | ) 18 | }) 19 | 20 | test(`should support multi-line strings`, () => { 21 | // construct a string with multiple lines without needing escaped newline characters 22 | const multiLine = `` 23 | expect(multiLine).toBe('\n How cool\n is this!?\n ') 24 | }) 25 | 26 | test(`should support string escaping`, () => { 27 | // properly escape a string in a template literal for each of these 28 | expect(``).toBe('Hi\nthere!') 29 | expect(``).toBe('This is `escaped` backticks') 30 | }) 31 | 32 | //////// EXTRA CREDIT //////// 33 | 34 | // you likely won't often use tagging, but it can be handy! 35 | test.skip(`should call the tagging function`, () => { 36 | const noun = 'World' 37 | const emotion = 'happy' 38 | const result = tagIt`Hello ${noun}! Are you feeling ${emotion} today?` 39 | expect(result).toBe( 40 | 'Hello super-cool World! Are you feeling really happy today?', 41 | ) 42 | 43 | function tagIt(literalString, ...interpolatedParts) { 44 | // implement this function to make the test pass 45 | return 'fixme' 46 | } 47 | }) 48 | 49 | //////// Elaboration & Feedback ///////// 50 | /* 51 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Template+Literals&em= 52 | */ 53 | test('I submitted my elaboration and feedback', () => { 54 | const submitted = false // change this when you've submitted! 55 | expect(true).toBe(submitted) 56 | }) 57 | //////////////////////////////// 58 | 59 | //////// EXTRA CREDIT //////// 60 | 61 | // If you get this far, try adding a few more tests, 62 | // then file a pull request to add them to the extra credit! 63 | // Learn more here: http://kcd.im/es6-workshop-contributing 64 | -------------------------------------------------------------------------------- /exercises/03_new-apis.test.js: -------------------------------------------------------------------------------- 1 | test(`should be easier to determine whether a string includes another`, () => { 2 | const sentence = 'It was the best of times. It was the worst of times' 3 | // create a variable called `result` that is assigned to a call of sentence.includes 4 | expect(result).toBe(true) 5 | }) 6 | 7 | test(`should be easier to repeat a string`, () => { 8 | const repeated = 'abc123' 9 | // create a variable called `result` that is the result of repeating the string 4 times 10 | expect(result).toBe('abc123abc123abc123abc123') 11 | }) 12 | 13 | test(`should be able to take an array-like object and convert it into an array`, () => { 14 | const obj = {length: 3, 0: 'a', 1: 'b', 2: 'c'} 15 | // this is even more handy with a NodeList like that returned from document.querySelector 16 | // create a variable called `result` and assign it to a call to Array.from 17 | expect(result).toEqual(['a', 'b', 'c']) 18 | }) 19 | 20 | test(`should be easier to fill an array with values`, () => { 21 | const originalArray = new Array(5) 22 | // create a variable called `result` and assign it to an array that's filled with 3s except for the first item. 23 | expect(result).toEqual([, 3, 3, 3, 3]) // eslint-disable-line no-sparse-arrays 24 | }) 25 | 26 | test(`should be easy to copy properties from one object to another`, () => { 27 | const source1 = { 28 | a: { 29 | b: 'c', 30 | m: [1, 2, 3], 31 | }, 32 | } 33 | const source2 = { 34 | d: false, 35 | z: 34, 36 | } 37 | const source3 = { 38 | z: 42, 39 | p: ['a', 'b', 'c'], 40 | } 41 | 42 | const target = { 43 | a: { 44 | q: 'r', 45 | m: [4, 5, 6], 46 | s: { 47 | t: 3, 48 | }, 49 | }, 50 | d: true, 51 | p: ['x', 'y', 'z'], 52 | } 53 | // merge the sources into the target using Object.assign 54 | 55 | expect(result).toEqual({ 56 | a: { 57 | b: 'c', 58 | m: [1, 2, 3], 59 | }, 60 | d: false, 61 | z: 42, 62 | p: ['a', 'b', 'c'], 63 | }) 64 | 65 | // this is only here to indicate that the assignment is not deep 66 | expect(result).not.toEqual({ 67 | a: { 68 | b: 'c', 69 | m: [1, 2, 3], 70 | q: 'r', 71 | s: { 72 | t: 3, 73 | }, 74 | }, 75 | d: false, 76 | z: 42, 77 | p: ['a', 'b', 'c'], 78 | }) 79 | }) 80 | 81 | //////// Elaboration & Feedback ///////// 82 | /* 83 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=New+APIS&em= 84 | */ 85 | test('I submitted my elaboration and feedback', () => { 86 | const submitted = false // change this when you've submitted! 87 | expect(true).toBe(submitted) 88 | }) 89 | //////////////////////////////// 90 | 91 | //////// EXTRA CREDIT //////// 92 | 93 | // If you get this far, try adding a few more tests, 94 | // then file a pull request to add them to the extra credit! 95 | // Learn more here: http://kcd.im/es6-workshop-contributing 96 | -------------------------------------------------------------------------------- /exercises/04_destructure.test.js: -------------------------------------------------------------------------------- 1 | const noop = () => {} 2 | 3 | function getAddress() { 4 | return { 5 | city: 'Salt Lake City', 6 | state: 'UT', 7 | zip: 84115, 8 | coords: { 9 | lat: 40.776608, 10 | long: -111.920485, 11 | }, 12 | } 13 | } 14 | 15 | function getNumbers() { 16 | return [1, 2, 3, 4, 5, 6] 17 | } 18 | 19 | function getNestedNumbers() { 20 | return [1, 2, [3, 4, [5, 6]]] 21 | } 22 | 23 | test('can be used to pull apart objects', () => { 24 | // Using destructuring, call `getAddress()` and create a 'city', 'state' and 'zip' variable. 25 | // const address = getAddress(); 26 | // const city = address.city; 27 | // const state = address.state; 28 | // const zip = address.zip; 29 | expect(city).toBe('Salt Lake City') 30 | expect(state).toBe('UT') 31 | expect(zip).toBe(84115) 32 | }) 33 | 34 | test('sets missing values to undefined', () => { 35 | // Using destructuring, call `getAddress()` and create an 'address' variable. 36 | expect(address).toBeUndefined() 37 | }) 38 | 39 | test('can alias destructured variables', () => { 40 | // Using destructuring, call `getAddress()` and pull the city, state and zip out, and alias them to c, s, z, respectively 41 | expect(c).toBe('Salt Lake City') 42 | expect(s).toBe('UT') 43 | expect(z).toBe(84115) 44 | expect(() => noop(city)).toThrow() 45 | expect(() => noop(state)).toThrow() 46 | expect(() => noop(zip)).toThrow() 47 | }) 48 | 49 | test('can destructure nested variables', () => { 50 | // Using destructuring, call `getAddress()` and create `lat` and `long` variables. 51 | expect(lat).toBe(40.776608) 52 | expect(long).toBe(-111.920485) 53 | expect(() => noop(coords)).toThrow() 54 | }) 55 | 56 | test('can be used to pull apart arrays', () => { 57 | // Call getNumbers and pull the first value out as `one` and the second as `two` 58 | expect(one).toBe(1) 59 | expect(two).toBe(2) 60 | }) 61 | 62 | test('can skip indexes in arrays', () => { 63 | // Call getNumbers and pull the first value out as `one` and the third as `three` 64 | expect(one).toBe(1) 65 | expect(three).toBe(3) 66 | expect(() => noop(two)).toThrow() 67 | }) 68 | 69 | test('can reach nested arrays', () => { 70 | // Call getNestedNumbers and pull the first value out as `one`, the 3 as `three` and 6 as `six`. 71 | expect(one).toBe(1) 72 | expect(three).toBe(3) 73 | expect(six).toBe(6) 74 | }) 75 | 76 | // MORE AT http://www.2ality.com/2015/01/es6-destructuring.html 77 | 78 | //////// Elaboration & Feedback ///////// 79 | /* 80 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Destructuring&em= 81 | */ 82 | test('I submitted my elaboration and feedback', () => { 83 | const submitted = false // change this when you've submitted! 84 | expect(true).toBe(submitted) 85 | }) 86 | //////////////////////////////// 87 | 88 | //////// EXTRA CREDIT //////// 89 | 90 | // If you get this far, try adding a few more tests, 91 | // then file a pull request to add them to the extra credit! 92 | // Learn more here: http://kcd.im/es6-workshop-contributing 93 | -------------------------------------------------------------------------------- /exercises/05_modules.test.js: -------------------------------------------------------------------------------- 1 | import * as Mathy from '../common/Mathy' 2 | import * as IndexImport from '../common' 3 | // WRITE YOUR IMPORT STATEMENTS HERE 4 | 5 | test('can import Mathy', () => { 6 | // this one's already done! You're welcome :) 7 | expect(Mathy.sqrt).toBeDefined() 8 | expect(Mathy.square).toBeDefined() 9 | expect(Mathy.diag).toBeDefined() 10 | }) 11 | 12 | test('can specify what to import, to only retain pieces of the import', () => { 13 | // Import `Mathy` again, but pull out only the `sqrt` as mySqrt, and `square` as mySquare 14 | expect(mySqrt).toBeDefined() 15 | expect(mySquare).toBeDefined() 16 | expect(mySqrt).toBe(Mathy.sqrt) 17 | expect(mySquare).toBe(Mathy.square) 18 | }) 19 | 20 | test('can import from my node_modules', () => { 21 | // import `lodash` 22 | expect(_).toBeDefined() 23 | }) 24 | 25 | //////// Elaboration & Feedback ///////// 26 | /* 27 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Modules&em= 28 | */ 29 | test('I submitted my elaboration and feedback', () => { 30 | const submitted = false // change this when you've submitted! 31 | expect(true).toBe(submitted) 32 | }) 33 | //////////////////////////////// 34 | 35 | //////// EXTRA CREDIT //////// 36 | test.skip('Index import', () => { 37 | //I have noticed that using index.js is pretty common pattern 38 | //If someone has been confused about that maybe this helps 39 | expect(IndexImport.variable1).toBe(/* ENTER YOUR GUESS HERE */) 40 | expect(IndexImport.variable2).toBe(/* ENTER YOUR GUESS HERE */) 41 | expect(IndexImport.variable3).toBe(/* ENTER YOUR GUESS HERE */) 42 | expect(IndexImport.variable4).toBe(/* ENTER YOUR GUESS HERE */) 43 | }) 44 | // If you get this far, try adding a few more tests, 45 | // then file a pull request to add them to the extra credit! 46 | // Learn more here: http://kcd.im/es6-workshop-contributing 47 | -------------------------------------------------------------------------------- /exercises/06_object-literal.test.js: -------------------------------------------------------------------------------- 1 | test('can use shorthand for property names', () => { 2 | function createMonster(name, power) { 3 | // Using NEW Object Literal Syntax, return a literal that will allow the tests to pass 4 | // return { 5 | // type: 'Monster', 6 | // name: name, 7 | // power: power, 8 | // attack: function (target){ 9 | // return `${this.name} attacked ${target.name}`; 10 | // } 11 | // } 12 | } 13 | 14 | const godzilla = createMonster('Godzilla', 1000) 15 | const mechaGodzilla = createMonster('MechaGodzilla', 5000) 16 | expect(godzilla.name).toBe('Godzilla') 17 | expect(godzilla.power).toBe(1000) 18 | expect(godzilla.attack(mechaGodzilla)).toBe('Godzilla attacked MechaGodzilla') 19 | }) 20 | 21 | test('can use expressions as property names', () => { 22 | function createCandy(type, description) { 23 | return { 24 | tasty: true, 25 | type, 26 | // add a expression as property name where the property name is the given type.toUpperCase() + type.length 27 | // sound contrived? It is... 😅 28 | } 29 | } 30 | 31 | const twixDescription = 32 | 'Twix is a chocolate bar made by Mars, Inc., consisting of biscuit applied with other ' + 33 | 'confectionery toppings and coatings. Twix bars are packaged in pairs, although smaller single bars are available.' 34 | const twixType = 'twix' 35 | const snickers = createCandy('twix', twixDescription) 36 | expect(snickers.tasty).toBe(true) 37 | expect(snickers.type).toBe(twixType) 38 | expect(snickers.TWIX4).toBe(twixDescription) 39 | }) 40 | 41 | //////// Elaboration & Feedback ///////// 42 | /* 43 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Object+Literals&em= 44 | */ 45 | test('I submitted my elaboration and feedback', () => { 46 | const submitted = false // change this when you've submitted! 47 | expect(true).toBe(submitted) 48 | }) 49 | //////////////////////////////// 50 | 51 | //////// EXTRA CREDIT //////// 52 | 53 | // If you get this far, try adding a few more tests, 54 | // then file a pull request to add them to the extra credit! 55 | // Learn more here: http://kcd.im/es6-workshop-contributing 56 | -------------------------------------------------------------------------------- /exercises/07_parameters.test.js: -------------------------------------------------------------------------------- 1 | test('can be triggered when the incoming argument is undefined', () => { 2 | function getName(name = 'Mercury') { 3 | return name 4 | } 5 | 6 | expect(getName('Aaron')).toBe(/*ENTER YOUR GUESS HERE*/) 7 | expect(getName(undefined)).toBe(/*ENTER YOUR GUESS HERE*/) 8 | expect(getName(null)).toBe(/*ENTER YOUR GUESS HERE*/) 9 | expect(getName()).toBe(/*ENTER YOUR GUESS HERE*/) 10 | }) 11 | 12 | test(`aren't included in arguments`, () => { 13 | function getName(name = 'Mercury') { 14 | return arguments.length 15 | } 16 | 17 | expect(getName('Aaron')).toBe(/*ENTER YOUR GUESS HERE*/) 18 | expect(getName(null)).toBe(/*ENTER YOUR GUESS HERE*/) 19 | expect(getName()).toBe(/*ENTER YOUR GUESS HERE*/) 20 | }) 21 | 22 | test('can trigger a function call', () => { 23 | let triggerCount = 0 24 | 25 | function getName(name = getDefault()) { 26 | return name 27 | } 28 | 29 | function getDefault() { 30 | triggerCount++ 31 | return 'Mercury' 32 | } 33 | 34 | expect(triggerCount).toBe(/*ENTER YOUR GUESS HERE*/) 35 | expect(getName('Aaron')).toBe(/*ENTER YOUR GUESS HERE*/) 36 | expect(getName()).toBe(/*ENTER YOUR GUESS HERE*/) 37 | expect(getName(undefined)).toBe(/*ENTER YOUR GUESS HERE*/) 38 | expect(triggerCount).toBe(/*ENTER YOUR GUESS HERE*/) 39 | }) 40 | 41 | test('catch non-specified params', () => { 42 | function resty(first, second, ...others) { 43 | return others 44 | } 45 | 46 | expect(resty().length).toBe(/*ENTER YOUR GUESS HERE*/) 47 | expect(resty(1).length).toBe(/*ENTER YOUR GUESS HERE*/) 48 | expect(resty(1, 2).length).toBe(/*ENTER YOUR GUESS HERE*/) 49 | expect(resty(1, 2, 3).length).toBe(/*ENTER YOUR GUESS HERE*/) 50 | expect( 51 | resty(1, 2, 3, undefined, 5, undefined, 7, undefined, 9, 10).length, 52 | ).toBe(/*ENTER YOUR GUESS HERE*/) 53 | }) 54 | 55 | test('has a different length than `arguments`', () => { 56 | function resty(first, second, ...others) { 57 | return others.length === arguments.length 58 | } 59 | 60 | expect(resty()).toBe(/*ENTER YOUR GUESS HERE*/) 61 | expect(resty(1)).toBe(/*ENTER YOUR GUESS HERE*/) 62 | expect(resty(1, 2)).toBe(/*ENTER YOUR GUESS HERE*/) 63 | expect(resty(1, 2, 3)).toBe(/*ENTER YOUR GUESS HERE*/) 64 | expect( 65 | resty(1, 2, 3, undefined, 5, undefined, 7, undefined, 9, 10), 66 | ).toBe(/*ENTER YOUR GUESS HERE*/) 67 | }) 68 | 69 | test('is an actual array, unlike arguments', () => { 70 | function resty(...args) { 71 | return args 72 | } 73 | 74 | function argy() { 75 | return arguments 76 | } 77 | 78 | const args = argy(1, 2, 3) 79 | const rests = resty(1, 2, 3) 80 | 81 | expect( 82 | Object.getPrototypeOf(args) === Object.getPrototypeOf(rests), 83 | ).toBe(/*ENTER YOUR GUESS HERE*/) 84 | expect(args.splice).toBe(/*ENTER YOUR GUESS HERE*/) 85 | expect(Object.getPrototypeOf(rests)).toBe(/*ENTER YOUR GUESS HERE*/) 86 | expect(rests.splice).toBeDefined() 87 | expect(rests.splice).toBe(/*ENTER YOUR GUESS HERE*/) 88 | }) 89 | 90 | test('it can default all arguments, optionally', () => { 91 | // Modify the method signature of `myFunction` to allow for 92 | // all args to be optional 93 | 94 | function myFunction({name, age, favoriteBand}) { 95 | expect(name).toBeDefined() 96 | expect(age).toBeDefined() 97 | expect(favoriteBand).toBeDefined() 98 | } 99 | 100 | myFunction({name: 'Axel', age: 37, favoriteBand: 'Taylor Swift'}) 101 | myFunction({name: 'Axel', age: 37}) 102 | myFunction({name: 'Axel'}) 103 | myFunction({}) 104 | myFunction() 105 | }) 106 | 107 | //////// Elaboration & Feedback ///////// 108 | /* 109 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Parameters&em= 110 | */ 111 | test('I submitted my elaboration and feedback', () => { 112 | const submitted = false // change this when you've submitted! 113 | expect(true).toBe(submitted) 114 | }) 115 | //////////////////////////////// 116 | 117 | //////// EXTRA CREDIT //////// 118 | 119 | // If you get this far, try adding a few more tests, 120 | // then file a pull request to add them to the extra credit! 121 | // Learn more here: http://kcd.im/es6-workshop-contributing 122 | 123 | /* 124 | eslint 125 | no-unused-vars:0 126 | prefer-rest-params:0 127 | */ 128 | -------------------------------------------------------------------------------- /exercises/08_spread.test.js: -------------------------------------------------------------------------------- 1 | test(`should be able to call a function and spread the arguments`, () => { 2 | const args = ['a', 'b', 'c'] 3 | let calls = 0 4 | // call myFunction using the spread operator with args 5 | expect(calls).toBe(1) 6 | 7 | function myFunction(a, b, c) { 8 | expect(a).toBe('a') 9 | expect(b).toBe('b') 10 | expect(c).toBe('c') 11 | calls++ 12 | } 13 | }) 14 | 15 | test(`should be easier to concatenate arrays`, () => { 16 | const array1 = [1, 2, 3] 17 | // create a result array that uses the spread operator to concatenate array1 with [4, 5, 6] 18 | expect(result).toEqual([1, 2, 3, 4, 5, 6]) 19 | }) 20 | 21 | test(`should be able to merge properties from objects`, () => { 22 | const obj1 = { 23 | foo: 'bar', 24 | baz: 'foobar', 25 | } 26 | // create a result object that uses the spread operator to add `eggs: 'spam'` to what exists in obj1 27 | expect(result).toEqual({ 28 | foo: 'bar', 29 | baz: 'foobar', 30 | eggs: 'spam', 31 | }) 32 | }) 33 | 34 | //////// Elaboration & Feedback ///////// 35 | /* 36 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Spread&em= 37 | */ 38 | test('I submitted my elaboration and feedback', () => { 39 | const submitted = false // change this when you've submitted! 40 | expect(true).toBe(submitted) 41 | }) 42 | //////////////////////////////// 43 | 44 | //////// EXTRA CREDIT //////// 45 | 46 | // If you get this far, try adding a few more tests, 47 | // then file a pull request to add them to the extra credit! 48 | // Learn more here: http://kcd.im/es6-workshop-contributing 49 | -------------------------------------------------------------------------------- /exercises/09_arrow.test.js: -------------------------------------------------------------------------------- 1 | test('can replace traditional functions', () => { 2 | let fnMultiply, arrowMultiply 3 | 4 | // Write two functions that take two params and return their product 5 | // For 'fnMultiply', set it equal to a regular function 6 | // For 'arrowMultiply', set it equal to an arrow function 7 | 8 | expect(fnMultiply(5, 5)).toBe(arrowMultiply(5, 5)) 9 | }) 10 | 11 | test('can replace traditional functions #2', () => { 12 | const nums = [2, 5, 10] 13 | // Replace the 'function' in this 'map' call with an arrow function. 14 | // Hint: you shouldn't have any braces or 'return' after you are done 15 | const squares = nums.map(function(num) { 16 | return num * num 17 | }) 18 | 19 | expect(squares.shift()).toBe(4) 20 | expect(squares.shift()).toBe(25) 21 | expect(squares.shift()).toBe(100) 22 | }) 23 | 24 | test('binds `this` to the eval scope, not the runtime scope', () => { 25 | // Change the person object. One of the functions should become an arrow to 26 | // allow for 'this' to retain context correctly 27 | const person = { 28 | name: 'Aaron', 29 | greetFriends: function(friends) { 30 | return friends.map(function(friend) { 31 | return this.name + ' greets to ' + friend 32 | }) 33 | }, 34 | } 35 | 36 | const friendsArray = ['Naomi', 'Jojo', 'Ryan', 'Owen'] 37 | expect(() => person.greetFriends(friendsArray)).not.toThrow() 38 | }) 39 | 40 | test('can make array filter chains more manageable', () => { 41 | const data = [ 42 | {type: 'Widget', name: 'Sprocket', price: 10.0, qty: 3}, 43 | {type: 'Widget', name: 'Bracket', price: 1.0, qty: 5}, 44 | {type: 'Widget', name: 'Brace', price: 2.5, qty: 1}, 45 | {type: 'Widget', name: 'Sprocket', price: 4.0, qty: 2}, 46 | {type: 'Food', name: 'Gouda', price: 8.75, qty: 4}, 47 | {type: 'Food', name: 'Bacon', price: 3.5, qty: 3}, 48 | {type: 'CD', name: 'Queen Best Hits', price: 5.5, qty: 5}, 49 | {type: 'CD', name: 'Brittney Best Hits', price: 6.25, qty: 3}, 50 | {type: 'CD', name: 'JT Best Hits', price: 2.25, qty: 6}, 51 | ] 52 | 53 | // REPLACE ALL REGULAR FUNCTIONS WITH ARROW FUNCTIONS 54 | const shoppingList = data 55 | .filter(function(d) { 56 | return d.type != 'Widget' 57 | }) // Remove Widgets 58 | .filter(function(d) { 59 | return d.price < 5 60 | }) // Find only remaining items with price < 5 61 | .sort(function(a, b) { 62 | return a.qty - b.qty 63 | }) // Sort by quantity, desc 64 | .map(function(d) { 65 | return d.name 66 | }) // Pull just the name from each item 67 | 68 | expect(shoppingList.shift()).toBe('Bacon') 69 | expect(shoppingList.shift()).toBe('JT Best Hits') 70 | }) 71 | 72 | //////// Elaboration & Feedback ///////// 73 | /* 74 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Arrow+Functions&em= 75 | */ 76 | test('I submitted my elaboration and feedback', () => { 77 | const submitted = false // change this when you've submitted! 78 | expect(true).toBe(submitted) 79 | }) 80 | //////////////////////////////// 81 | 82 | //////// EXTRA CREDIT //////// 83 | 84 | // If you get this far, try adding a few more tests, 85 | // then file a pull request to add them to the extra credit! 86 | // Learn more here: http://kcd.im/es6-workshop-contributing 87 | -------------------------------------------------------------------------------- /exercises/10_class.test.js: -------------------------------------------------------------------------------- 1 | test('has a constructor for initialization', () => { 2 | // Create an Animal class 3 | // Add a constructor that takes one param, the name. 4 | // Set this.name to the name passed in 5 | 6 | const animal = new Animal() 7 | const dog = new Animal('Dog') 8 | 9 | expect(animal.name).toBeUndefined() 10 | expect(dog.name).toBe('Dog') 11 | }) 12 | 13 | test('constructor can have default param values', () => { 14 | // Create an Animal class with a constructor 15 | // Make your class default (using default params) the name to 'Honey Badger' 16 | 17 | const animal = new Animal() 18 | const dog = new Animal('Dog') 19 | 20 | expect(animal.name).toBe('Honey Badger') 21 | expect(dog.name).toBe('Dog') 22 | }) 23 | 24 | test('can have instance methods', () => { 25 | // Create an Animal class, pass in the name to the constructor, and add a sayName function to the class definition 26 | 27 | const animal = new Animal() 28 | 29 | expect(animal.sayName).toBeDefined() 30 | expect(Animal.sayName).toBeUndefined() 31 | expect(animal.sayName()).toBe('My name is: Honey Badger') 32 | }) 33 | 34 | test('can have static methods', () => { 35 | // Create an Animal class, pass in the name to the constructor, 36 | // and add a create method that takes a name and returns an instance 37 | 38 | const animal = new Animal() 39 | 40 | expect(animal.create).toBeUndefined() 41 | expect(Animal.create).toBeDefined() 42 | }) 43 | 44 | test('can extend another class', () => { 45 | // Create an Animal class 46 | // Create a Dog class that extends Animal 47 | // Add sayName to Dog 48 | 49 | const dog = new Dog('Fido') 50 | 51 | expect(dog instanceof Dog).toBe(true) 52 | expect(dog instanceof Animal).toBe(true) 53 | expect(Animal.prototype.sayName).toBeUndefined() 54 | expect(Dog.prototype.sayName).toBeDefined() 55 | }) 56 | 57 | test('can use property setters and getters', () => { 58 | // Create an Animal class (don't pass name into constructor) 59 | // Add property setter for name 60 | // Add property getter for name 61 | 62 | const animal = new Animal() 63 | animal.name = 'Dog' 64 | expect(animal.name).toBe('Dog type of animal') 65 | animal.name = 'Cat' 66 | expect(animal.name).toBe('Cat type of animal') 67 | }) 68 | 69 | //////// Elaboration & Feedback ///////// 70 | /* 71 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Classes&em= 72 | */ 73 | test('I submitted my elaboration and feedback', () => { 74 | const submitted = false // change this when you've submitted! 75 | expect(true).toBe(submitted) 76 | }) 77 | //////////////////////////////// 78 | 79 | //////// EXTRA CREDIT //////// 80 | 81 | // If you get this far, try adding a few more tests, 82 | // then file a pull request to add them to the extra credit! 83 | // Learn more here: http://kcd.im/es6-workshop-contributing 84 | -------------------------------------------------------------------------------- /exercises/11_set.test.js: -------------------------------------------------------------------------------- 1 | test('has an add method and a has method', () => { 2 | // Create a new Set called 'mySet' 3 | // add the numbers 1, 2, and 3 to the set 4 | 5 | expect(mySet.has(1)).toBe(true) 6 | expect(mySet.has(2)).toBe(true) 7 | expect(mySet.has(3)).toBe(true) 8 | expect(mySet.has(4)).toBe(false) 9 | }) 10 | 11 | test('doesn`t allow duplicates', () => { 12 | // Create a new Set 13 | // add the following numbers to it, using set.add(num): 1, 1, 1 14 | 15 | expect(mySet.has(1)).toBe(true) 16 | expect(mySet.has(2)).toBe(false) 17 | expect(mySet.has(3)).toBe(false) 18 | expect(mySet.has(4)).toBe(false) 19 | }) 20 | 21 | //////// Elaboration & Feedback ///////// 22 | /* 23 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Sets&em= 24 | */ 25 | test('I submitted my elaboration and feedback', () => { 26 | const submitted = false // change this when you've submitted! 27 | expect(true).toBe(submitted) 28 | }) 29 | //////////////////////////////// 30 | 31 | //////// EXTRA CREDIT //////// 32 | 33 | // If you get this far, try adding a few more tests, 34 | // then file a pull request to add them to the extra credit! 35 | // Learn more here: http://kcd.im/es6-workshop-contributing 36 | -------------------------------------------------------------------------------- /exercises/12_maps.test.js: -------------------------------------------------------------------------------- 1 | test('has a set method', () => { 2 | // Create a new map called 'myMap' 3 | // add a new entry. Use 'name' as the key and 'Aaron' as the value 4 | 5 | expect(myMap.get('name')).toBe('Aaron') 6 | }) 7 | 8 | test('can use objects as a key', () => { 9 | const user = {name: 'Aaron'} 10 | const value = {twitter: '@js_dev', gplus: '+AaronFrost'} 11 | 12 | // Create a map called 'myMap' 13 | // add a new entry. Use user as the key, and value as the value 14 | 15 | expect(myMap.has(user)).toBe(true) 16 | expect(myMap.get(user)).toBe(value) 17 | }) 18 | 19 | test(`doesn't coerce keys`, () => { 20 | const myMap = new Map() 21 | myMap.set(1, 'Aaron') 22 | expect(myMap.get('1')).toBe(/*ENTER YOUR GUESS HERE*/) 23 | myMap.set('1', 'Aaron') 24 | expect(myMap.get('1')).toBe(/*ENTER YOUR GUESS HERE*/) 25 | }) 26 | 27 | //////// Elaboration & Feedback ///////// 28 | /* 29 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Maps&em= 30 | */ 31 | test('I submitted my elaboration and feedback', () => { 32 | const submitted = false // change this when you've submitted! 33 | expect(true).toBe(submitted) 34 | }) 35 | //////////////////////////////// 36 | 37 | //////// EXTRA CREDIT //////// 38 | 39 | // If you get this far, try adding a few more tests, 40 | // then file a pull request to add them to the extra credit! 41 | // Learn more here: http://kcd.im/es6-workshop-contributing 42 | -------------------------------------------------------------------------------- /exercises/13_weakmap.test.js: -------------------------------------------------------------------------------- 1 | test('has a set method', () => { 2 | const key = {name: 'Aaron'} 3 | const value = {twitter: '@js_dev', gplus: '+AaronFrost'} 4 | // Create a new WeakMap called 'myMap' 5 | // Add a new entry. Use key as the key and values as the value 6 | expect(myMap.has(key)).toBe(true) 7 | }) 8 | 9 | test(`should enable private members in classes`, () => { 10 | // If you make it this far, write a class with private member variables, using WeakMaps 11 | class Person { 12 | constructor(name, age) { 13 | this._name = name 14 | this._age = age 15 | } 16 | 17 | getName() { 18 | return this._name 19 | } 20 | 21 | getAge() { 22 | return this._age 23 | } 24 | } 25 | 26 | const person = new Person('Kent C. Dodds', 26) 27 | expect(person._name).toBeUndefined() 28 | expect(person.getName()).toBe('Kent C. Dodds') 29 | expect(person._age).toBeUndefined() 30 | expect(person.getAge()).toBe(26) 31 | }) 32 | 33 | //////// Elaboration & Feedback ///////// 34 | /* 35 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=WeakMaps&em= 36 | */ 37 | test('I submitted my elaboration and feedback', () => { 38 | const submitted = false // change this when you've submitted! 39 | expect(true).toBe(submitted) 40 | }) 41 | //////////////////////////////// 42 | 43 | //////// EXTRA CREDIT //////// 44 | 45 | // If you get this far, try adding a few more tests, 46 | // then file a pull request to add them to the extra credit! 47 | // Learn more here: http://kcd.im/es6-workshop-contributing 48 | -------------------------------------------------------------------------------- /exercises/14_promises.test.js: -------------------------------------------------------------------------------- 1 | // For each then() or catch() block, pick whether it should run or not 2 | // If it should run, tell which value it will receive in `result` or `error` 3 | // If it should not run, uncomment the error throwing statement 4 | test(`should resolve`, () => { 5 | return pickApple('ripe') 6 | .then( 7 | result => { 8 | // throw new Error('this should not run') 9 | // expect(result).toBe(/*ENTER GUESS HERE*/) 10 | throw new Error('assert or throw here') 11 | }, 12 | error => { 13 | // throw new Error('this should not run') 14 | // expect(error).toBe(/*ENTER GUESS HERE*/) 15 | throw new Error('assert or throw here') 16 | }, 17 | ) 18 | .catch(error => { 19 | // throw new Error('this should not run') 20 | // expect(error).toBe(/*ENTER GUESS HERE*/) 21 | throw new Error('assert or throw here') 22 | }) 23 | }) 24 | 25 | test(`should reject`, () => { 26 | return pickApple('unripe') 27 | .then( 28 | result => { 29 | // throw new Error('this should not run') 30 | // expect(result).toBe(/*ENTER GUESS HERE*/) 31 | throw new Error('assert or throw here') 32 | }, 33 | error => { 34 | // throw new Error('this should not run') 35 | // expect(error).toBe(/*ENTER GUESS HERE*/) 36 | throw new Error('assert or throw here') 37 | }, 38 | ) 39 | .catch(error => { 40 | // throw new Error('this should not run') 41 | // expect(error).toBe(/*ENTER GUESS HERE*/) 42 | throw new Error('assert or throw here') 43 | }) 44 | }) 45 | 46 | test(`errors can be caught`, () => { 47 | return pickApple() 48 | .then(result => { 49 | // throw new Error('this should not run') 50 | // expect(result).toBe(/*ENTER GUESS HERE*/) 51 | throw new Error('assert or throw here') 52 | }) 53 | .catch(error => { 54 | // throw new Error('this should not run') 55 | // expect(error).toBe(/*ENTER GUESS HERE*/) 56 | throw new Error('assert or throw here') 57 | }) 58 | }) 59 | 60 | function pickApple(ripeness) { 61 | // Immediately return a promise which will eventually get resolved 62 | // or rejected by calling the corresponding function. 63 | return new Promise((resolve, reject) => { 64 | // Do something asynchronous. Could be AJAX, using a timeout here. 65 | setTimeout(() => { 66 | if (ripeness === 'ripe') { 67 | resolve('ripe apple') 68 | } else if (ripeness === 'unripe') { 69 | reject('unripe apple') 70 | } else { 71 | reject(new Error('out of apples')) 72 | } 73 | }) 74 | }) 75 | } 76 | 77 | //////// Elaboration & Feedback ///////// 78 | /* 79 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Promises&em= 80 | */ 81 | test('I submitted my elaboration and feedback', () => { 82 | const submitted = false // change this when you've submitted! 83 | expect(true).toBe(submitted) 84 | }) 85 | //////////////////////////////// 86 | 87 | //////// EXTRA CREDIT //////// 88 | 89 | // If you get this far, try adding a few more tests, 90 | // then file a pull request to add them to the extra credit! 91 | // Learn more here: http://kcd.im/es6-workshop-contributing 92 | -------------------------------------------------------------------------------- /exercises/15_async-await.test.js: -------------------------------------------------------------------------------- 1 | // Rewrite all of these from promises to async/await 2 | // tip: you can turn the `it` callbacks to async functions by adding `async` to them :) 3 | test('should work with resolved promises', async () => { 4 | return doAsync().then(result => { 5 | expect(result).toBe('resolved') 6 | throw new Error( 7 | 'convert this to an async/await function and remove this error', 8 | ) 9 | }) 10 | }) 11 | 12 | test('should throw an error with a rejected promise', async () => { 13 | return doAsync(true).catch(error => { 14 | expect(error).toBe('rejected') 15 | throw new Error( 16 | 'convert this to an async/await function and remove this error', 17 | ) 18 | }) 19 | }) 20 | 21 | function doAsync(rejectPromise = false) { 22 | return new Promise((resolve, reject) => { 23 | setTimeout(() => { 24 | if (rejectPromise) { 25 | reject('rejected') 26 | } else { 27 | resolve('resolved') 28 | } 29 | }) 30 | }) 31 | } 32 | 33 | //////// Elaboration & Feedback ///////// 34 | /* 35 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Async/Await&em= 36 | */ 37 | test('I submitted my elaboration and feedback', () => { 38 | const submitted = false // change this when you've submitted! 39 | expect(true).toBe(submitted) 40 | }) 41 | //////////////////////////////// 42 | 43 | //////// EXTRA CREDIT //////// 44 | 45 | // If you get this far, try adding a few more tests, 46 | // then file a pull request to add them to the extra credit! 47 | // Learn more here: http://kcd.im/es6-workshop-contributing 48 | -------------------------------------------------------------------------------- /exercises/16_es2016.test.js: -------------------------------------------------------------------------------- 1 | test('the exponentiation operation can be used to raise a number to a power of another number', () => { 2 | // refactor this to use the exponentiation operator (**) 3 | const result = Math.pow(3, 2) 4 | expect(result).toBe(9) 5 | }) 6 | 7 | test('array.includes can be used to determine whether an item exists in an array', () => { 8 | const bestFriend = {name: 'Sindre Sorhus'} 9 | const greatFriends = [ 10 | bestFriend, 11 | {name: 'Dustan Kasten'}, 12 | {name: 'Sam Saccone'}, 13 | {name: 'Ingvar Stepanyan'}, 14 | ] 15 | // refactor this to use `includes` instead 16 | const result = greatFriends.indexOf(bestFriend) !== -1 17 | expect(result).toBe(true) 18 | }) 19 | 20 | //////// Elaboration & Feedback ///////// 21 | /* 22 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=ES2016&em= 23 | */ 24 | test('I submitted my elaboration and feedback', () => { 25 | const submitted = false // change this when you've submitted! 26 | expect(true).toBe(submitted) 27 | }) 28 | //////////////////////////////// 29 | 30 | //////// EXTRA CREDIT //////// 31 | 32 | // If you get this far, try adding a few more tests, 33 | // then file a pull request to add them to the extra credit! 34 | // Learn more here: http://kcd.im/es6-workshop-contributing 35 | -------------------------------------------------------------------------------- /exercises/17_es2017.test.js: -------------------------------------------------------------------------------- 1 | test('String.prototype.padStart saves us from left-pad-gate', () => { 2 | const originalString = 'Worlds Finest' 3 | // call padStart on this string to make the test pass 4 | const result = originalString 5 | expect(result).toBe(' Worlds Finest') 6 | }) 7 | 8 | test('String.prototype.padEnd (and padStart) can be given a string to pad with', () => { 9 | const originalString = 'Stronger Together' 10 | // call padEnd on this string to make the test pass 11 | expect(result).toBe('Stronger Together-123-123-1') 12 | }) 13 | 14 | test('Object.values gets just the values of an object', () => { 15 | const show = { 16 | title: 'Supergirl', 17 | seasons: 1.2, 18 | characters: [ 19 | 'Supergirl', 20 | 'Cat Grant', 21 | 'Superman', 22 | 'Jimmy Olsen', 23 | 'Hank Henshaw', 24 | 'Winn Schott', 25 | 'Alex Danvers', 26 | ], 27 | } 28 | // get the values of the show object as an array 29 | expect(result).toEqual([ 30 | 'Supergirl', 31 | 1.2, 32 | [ 33 | 'Supergirl', 34 | 'Cat Grant', 35 | 'Superman', 36 | 'Jimmy Olsen', 37 | 'Hank Henshaw', 38 | 'Winn Schott', 39 | 'Alex Danvers', 40 | ], 41 | ]) 42 | }) 43 | 44 | test('Object.entries gives an array of arrays as [key, value]', () => { 45 | const show = { 46 | title: 'The Flash', 47 | seasons: 2.2, 48 | characters: [ 49 | 'The Flash', 50 | 'Iris West', 51 | 'Caitlin Snow', 52 | 'Eddie Thawne', // 😢 53 | 'Cisco Ramon', 54 | 'Harrison Wells', 55 | 'Joe West', 56 | ], 57 | } 58 | // get a [key, value] array of the show object 59 | expect(result).toEqual([ 60 | ['title', 'The Flash'], 61 | ['seasons', 2.2], 62 | [ 63 | 'characters', 64 | [ 65 | 'The Flash', 66 | 'Iris West', 67 | 'Caitlin Snow', 68 | 'Eddie Thawne', 69 | 'Cisco Ramon', 70 | 'Harrison Wells', 71 | 'Joe West', 72 | ], 73 | ], 74 | ]) 75 | }) 76 | 77 | test('Trailing commas in function parameter lists and calls help us with git', () => { 78 | // becasue this is a syntax thing, we'll put it inside a string and use `eval` to make sure 79 | // that you get the syntax correct :) 80 | expect(` 81 | function foo( 82 | a, 83 | b, 84 | c 85 | ) { 86 | log(a, b, c) 87 | } 88 | 89 | foo( 90 | 1, 91 | 2, 92 | 3 93 | ) 94 | 95 | function bar( 96 | a, 97 | b, 98 | ...rest, 99 | ) { 100 | log(a, b, ...rest) 101 | } 102 | bar( 103 | 1, 2, 3, 104 | 4, 5, 6 105 | ) 106 | 107 | function log() { 108 | // do nothing :) 109 | } 110 | `).toBeValidSyntax() 111 | }) 112 | 113 | //////// Elaboration & Feedback ///////// 114 | /* 115 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=ES2017&em= 116 | */ 117 | test('I submitted my elaboration and feedback', () => { 118 | const submitted = false // change this when you've submitted! 119 | expect(true).toBe(submitted) 120 | }) 121 | //////////////////////////////// 122 | 123 | //////// EXTRA CREDIT //////// 124 | 125 | // If you get this far, try adding a few more tests, 126 | // then file a pull request to add them to the extra credit! 127 | // Learn more here: http://kcd.im/es6-workshop-contributing 128 | 129 | // there's also this fancy Object.getOwnPropertyDescriptors thing, 130 | // but you'll likely rarely use that directly... 131 | // https://github.com/tc39/proposal-object-getownpropertydescriptors 132 | // Maybe you could make a PR to add a test for this?! 133 | -------------------------------------------------------------------------------- /exercises/18_public-class-fields.test.js: -------------------------------------------------------------------------------- 1 | test('public class fields help us avoid .bind-ing everything', () => { 2 | class FakeReactComponent { 3 | constructor(props) { 4 | this.props = props 5 | this.setState = () => {} // just for fun 6 | } 7 | } 8 | 9 | class MyComponent extends FakeReactComponent { 10 | constructor(...args) { 11 | super(...args) 12 | // we don't want to have to do this... 13 | this.handleClick = this.handleClick.bind(this) // sad :-( 14 | } 15 | // convert this to a public class field so it's autobound 16 | handleClick({target: {value}}) { 17 | this.props.onClick(value) 18 | } 19 | render() { 20 | // weird JSX stuff here 21 | } 22 | // this is just so we can test things out 23 | testClick(value) { 24 | const fakeEvent = {target: {value}} 25 | this.handleClick(fakeEvent) 26 | } 27 | } 28 | 29 | const onClick = jest.fn() 30 | const myComponent = new MyComponent({onClick}) 31 | myComponent.testClick('hello world') 32 | expect(onClick).toHaveBeenCalledTimes(1) 33 | expect(onClick).toHaveBeenCalledWith('hello world') 34 | }) 35 | 36 | //////// Elaboration & Feedback ///////// 37 | /* 38 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Public+Class+Fields&em= 39 | */ 40 | test('I submitted my elaboration and feedback', () => { 41 | const submitted = false // change this when you've submitted! 42 | expect(true).toBe(submitted) 43 | }) 44 | //////////////////////////////// 45 | 46 | //////// EXTRA CREDIT //////// 47 | 48 | // If you get this far, try adding a few more tests, 49 | // then file a pull request to add them to the extra credit! 50 | // Learn more here: http://kcd.im/es6-workshop-contributing 51 | -------------------------------------------------------------------------------- /exercises/19_symbols.test.js: -------------------------------------------------------------------------------- 1 | test('creating symbols', () => { 2 | const symbol = 'I wanna be a symbol one day' 3 | expect(typeof symbol).toBe('symbol') 4 | }) 5 | 6 | test('giving a symbol a description', () => { 7 | const symbol = Symbol() // give me a label 8 | expect(String(symbol)).toBe('Symbol(use the force)') 9 | }) 10 | 11 | test('symbols are unique', () => { 12 | const s1 = Symbol() 13 | const s2 = Symbol() 14 | expect(s1 === s2).toBe(/* enter your guess here */) 15 | 16 | const s3 = Symbol('I am a symbol') 17 | const s4 = Symbol('I am a symbol') 18 | expect(s3 === s4).toBe(/* enter your guess here */) 19 | }) 20 | 21 | test('symbols on objects', () => { 22 | const symbol = Symbol('metadata') 23 | // make an object called `game` that 24 | // makes this test pass 25 | 26 | expect(JSON.parse(JSON.stringify(game))).toEqual({ 27 | name: 'The Legend of Zelda', 28 | releaseDate: 'February 21, 1986', 29 | }) 30 | expect(game[symbol]).toEqual({ 31 | fans: 'about a billion', 32 | }) 33 | }) 34 | 35 | //////// Elaboration & Feedback ///////// 36 | /* 37 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Symbols&em= 38 | */ 39 | test('I submitted my elaboration and feedback', () => { 40 | const submitted = false // change this when you've submitted! 41 | expect(true).toBe(submitted) 42 | }) 43 | //////////////////////////////// 44 | 45 | //////// EXTRA CREDIT //////// 46 | 47 | // If you get this far, try adding a few more tests, 48 | // then file a pull request to add them to the extra credit! 49 | // Learn more here: http://kcd.im/es6-workshop-contributing 50 | -------------------------------------------------------------------------------- /exercises/20_iterators.test.js: -------------------------------------------------------------------------------- 1 | test('can get the iterator from an array', () => { 2 | const array = [1, 2, 3] 3 | // DON'T PEAK AT THE NEXT TESTS! 4 | const iterator = '?' // how do you get the iterator? 5 | expect(typeof iterator.next === 'function').toBe(true) 6 | }) 7 | 8 | test('can next() the iterator multiple times', () => { 9 | const string = 'hello' // <-- YES, this is iterable! 10 | const iterator = string[Symbol.iterator]() 11 | expect(iterator.next()).toEqual(/* ENTER YOUR ANSWER HERE */) 12 | expect(iterator.next()).toEqual(/* ENTER YOUR ANSWER HERE */) 13 | expect(iterator.next()).toEqual(/* ENTER YOUR ANSWER HERE */) 14 | expect(iterator.next()).toEqual(/* ENTER YOUR ANSWER HERE */) 15 | expect(iterator.next()).toEqual(/* ENTER YOUR ANSWER HERE */) 16 | expect(iterator.next()).toEqual(/* ENTER YOUR ANSWER HERE */) 17 | expect(iterator.next()).toEqual(/* ENTER YOUR ANSWER HERE */) 18 | }) 19 | 20 | test('can iterate over an interable with for .. of', () => { 21 | const array = [1, 2, 3] 22 | let sum = 0 23 | // write a for .. of loop 24 | // that gets the sum of 25 | // all items in the array 26 | // ex: `sum += val` 27 | expect(sum).toBe(6) 28 | }) 29 | 30 | test('can use the ... operator on the iterator', () => { 31 | const set = new Set([1, 2, 2, 3]) 32 | // use destructuring and the ... operator to create a 33 | // `rest` variable that only has the last two items. 34 | const [rest] = set 35 | expect(rest).toEqual([2, 3]) 36 | }) 37 | 38 | test('can create a custom iterator', () => { 39 | const randomRandomNumbersGenerator = { 40 | max: 20, 41 | min: 10, 42 | // add an iterator function here which will use this object's 43 | // min and max values to generate a random number of numbers 44 | // within the min and max which are each random within the min 45 | // and max. 46 | // For example: [14, 18, 16, 14, 11, 19, 16, 15, 19, 18, 15] 47 | // Do it without using a generator function 48 | } 49 | 50 | expect(iteratorWorks()).toBe(true) 51 | 52 | function iteratorWorks() { 53 | const randomNumbers = [...randomRandomNumbersGenerator] 54 | const {max, min} = randomRandomNumbersGenerator 55 | const tooManyNumbers = randomNumbers.length > max 56 | const tooFewNumbers = randomNumbers.length < min 57 | const numbersInBounds = randomNumbers.every(num => num <= max && num >= min) 58 | return !tooManyNumbers && !tooFewNumbers && numbersInBounds 59 | } 60 | }) 61 | 62 | test('can create a custom iterator with a generator', () => { 63 | const randomRandomNumbersGenerator = { 64 | max: 20, 65 | min: 10, 66 | // rewrite the previous example as a generator function 67 | } 68 | 69 | expect(iteratorWorks()).toBe(true) 70 | 71 | function iteratorWorks() { 72 | const randomNumbers = [...randomRandomNumbersGenerator] 73 | const {max, min} = randomRandomNumbersGenerator 74 | const tooManyNumbers = randomNumbers.length > max 75 | const tooFewNumbers = randomNumbers.length < min 76 | const numbersInBounds = randomNumbers.every(num => num <= max && num >= min) 77 | return !tooManyNumbers && !tooFewNumbers && numbersInBounds 78 | } 79 | }) 80 | 81 | //////// Elaboration & Feedback ///////// 82 | /* 83 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Iterators&em= 84 | */ 85 | test('I submitted my elaboration and feedback', () => { 86 | const submitted = false // change this when you've submitted! 87 | expect(true).toBe(submitted) 88 | }) 89 | //////////////////////////////// 90 | 91 | //////// EXTRA CREDIT //////// 92 | 93 | test.skip('add custom iterator to built-in types', () => { 94 | // How could you make this work using a custom iterator? 95 | const num = 5 96 | const result = [...num] 97 | expect(result).toEqual([0, 1, 2, 3, 4]) 98 | }) 99 | 100 | // If you get this far, try adding a few more tests, 101 | // then file a pull request to add them to the extra credit! 102 | // Learn more here: http://kcd.im/es6-workshop-contributing 103 | -------------------------------------------------------------------------------- /exercises/21_generators.test.js: -------------------------------------------------------------------------------- 1 | test(`should yield objects with value and done properties`, () => { 2 | const odds = giveMeOneOddNumber() 3 | 4 | expect(odds.next().value).toBe(/* ENTER YOUR GUESS */) 5 | expect(odds.next().value).toBe(/* ENTER YOUR GUESS */) 6 | expect(odds.next().done).toBe(/* ENTER YOUR GUESS */) 7 | odds.next() 8 | expect(odds.next().value).toBe(/* ENTER YOUR GUESS */) 9 | expect(odds.next().done).toBe(/* ENTER YOUR GUESS */) 10 | 11 | function* giveMeOneOddNumber() { 12 | yield 1 13 | yield 3 14 | yield 5 15 | yield 7 16 | yield 9 17 | } 18 | }) 19 | 20 | test(`can be iterated over`, () => { 21 | function* giveMeOneEvenNumber() { 22 | yield 2 23 | yield 4 24 | yield 6 25 | yield 8 26 | } 27 | 28 | let sum = 0 29 | 30 | // BEWARE, THIS IS BLOCKING/SYNCHRONOUS! 31 | // Generators are not async/await, those may be in ES2016 32 | for (let even of giveMeOneEvenNumber()) { 33 | sum = sum + even 34 | } 35 | 36 | expect(sum).toBe(/* ENTER YOUR GUESS */) 37 | }) 38 | 39 | //////// Elaboration & Feedback ///////// 40 | /* 41 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Generators&em= 42 | */ 43 | test('I submitted my elaboration and feedback', () => { 44 | const submitted = false // change this when you've submitted! 45 | expect(true).toBe(submitted) 46 | }) 47 | //////////////////////////////// 48 | 49 | //////// EXTRA CREDIT //////// 50 | 51 | // If you get this far, try adding a few more tests, 52 | // then file a pull request to add them to the extra credit! 53 | // Learn more here: http://kcd.im/es6-workshop-contributing 54 | -------------------------------------------------------------------------------- /exercises/22_reflect.test.js: -------------------------------------------------------------------------------- 1 | test('Reflect.apply can be used to call a function', () => { 2 | const person = { 3 | name: 'Fred', 4 | sayHi(greeting, noun) { 5 | return `${greeting} ${noun}! My name is ${this.name}` 6 | }, 7 | } 8 | 9 | const result = null // use Reflect.apply to invoke person.sayHi 10 | expect(result).toBe('Hey there Jaimee! My name is Fred') 11 | }) 12 | 13 | test('Reflect.deleteProperty can be used instead of the `delete` keyword', () => { 14 | const person = {name: 'Joan', age: 56} 15 | Reflect.defineProperty(person, 'protected', { 16 | configurable: false, 17 | value: 'YOU CANNOT GET RID OF ME!', 18 | }) 19 | // use Reflect.deleteProperty to delete the age property from the person object 20 | const ageDeleted = delete person.age 21 | const protectedDeleted = delete person.protected 22 | expect(person.age).not.toBeDefined() 23 | expect(ageDeleted).toBe(true) 24 | expect(person.protected).toBe('YOU CANNOT GET RID OF ME!') 25 | expect(protectedDeleted).toBe(false) 26 | }) 27 | 28 | test(`Reflect.ownKeys returns the object's own (not inherited) keys (including symbols)`, () => { 29 | const exists = Symbol('existance') 30 | const person = {human: true, [exists]: true} 31 | const favoriteFeature = Symbol('Fav Feat') 32 | const kyle = { 33 | __proto__: person, 34 | awesome: true, 35 | [favoriteFeature]: 'destructuring', 36 | } 37 | Reflect.defineProperty(kyle, 'favoriteLanguage', { 38 | value: 'JS', 39 | configurable: false, 40 | enumerable: false, 41 | }) 42 | // hint, the keys will be in the order that they're added to the object 43 | // this will be the case for most environments, though it's generally not 44 | // a good idea to rely on this fact as it's not specified in the spec. 45 | expect(Object.keys(kyle)).toEqual([ 46 | /* ENTER YOUR GUESS */ 47 | ]) 48 | expect(Object.getOwnPropertyNames(kyle)).toEqual([ 49 | /* ENTER YOUR GUESS */ 50 | ]) 51 | expect(Object.getOwnPropertySymbols(kyle)).toEqual([ 52 | /* ENTER YOUR GUESS */ 53 | ]) 54 | expect(Reflect.ownKeys(kyle)).toEqual([ 55 | /* ENTER YOUR GUESS */ 56 | ]) 57 | }) 58 | 59 | //////// Elaboration & Feedback ///////// 60 | /* 61 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Reflect&em= 62 | */ 63 | test('I submitted my elaboration and feedback', () => { 64 | const submitted = false // change this when you've submitted! 65 | expect(true).toBe(submitted) 66 | }) 67 | //////////////////////////////// 68 | 69 | //////// EXTRA CREDIT //////// 70 | 71 | // If you get this far, try adding a few more tests, 72 | // then file a pull request to add them to the extra credit! 73 | // Learn more here: http://kcd.im/es6-workshop-contributing 74 | -------------------------------------------------------------------------------- /exercises/23_proxies.test.js: -------------------------------------------------------------------------------- 1 | // this is the object we'll be mucking around with and proxying 2 | function getCharacter() { 3 | return { 4 | _id: '9RKDLS02580GHCXNZLA0', 5 | password: 'isolemnlysweariamuptonogood', 6 | name: {first: 'Ron', last: 'Weasly'}, 7 | classes: [ 8 | {name: 'Divination', teacher: 'Sybill Trelawney'}, 9 | {name: 'Defence Against the Dark Arts', teacher: 'Dolores Umbridge'}, 10 | ], 11 | greet(greeting = 'Hi') { 12 | const {first, last} = this.name 13 | return `${greeting}! My name is ${first} ${last} and my ID is ${this._id} and my password is ${this.password}!` 14 | }, 15 | getTeachers() { 16 | return this.classes.map(({teacher}) => teacher) 17 | }, 18 | } 19 | } 20 | 21 | test('can wrap an existing object', () => { 22 | const character = getCharacter() 23 | const proxy = character 24 | expect(proxy).not.toBe(character) // referential equality 25 | expect(proxy).toEqual(character) // deep equality 26 | }) 27 | 28 | test('handler can intercept gets, sets, and deletes', () => { 29 | const character = getCharacter() 30 | 31 | const handler = {} 32 | const proxy = new Proxy(character, handler) 33 | 34 | // interact with the proxy 35 | proxy['classes.1.teacher'] = 'Severus Snape' // set deep 36 | proxy.awesome = 10 // set shallow 37 | delete proxy._id // delete "protected property" 38 | 39 | // make some assertions 40 | expect(proxy['classes.1.teacher']).toBe('Severus Snape') // get deep 41 | expect(proxy.awesome).toBe(10) // get shallow 42 | expect(proxy._id).toEqual('9RKDLS02580GHCXNZLA0') // property not deleted 43 | 44 | // clean up 45 | delete proxy.awesome // delete unprotected property 46 | expect(proxy.awesome).toBe(undefined) // property successfully deleted 47 | }) 48 | 49 | //////// EXTRA CREDIT //////// 50 | 51 | test.skip('can intercept function calls', () => { 52 | const character = getCharacter() 53 | 54 | const handler = {} 55 | // notice that `apply` only works for proxies on functions! 56 | character.greet = new Proxy(character.greet, handler) 57 | character.getTeachers = new Proxy(character.getTeachers, handler) 58 | const result = character.greet('Hey there') 59 | expect(result).not.toContain(character.password) 60 | expect(result).not.toContain(character._id) 61 | expect(character.getTeachers()).toEqual([ 62 | 'Sybill Trelawney', 63 | 'Dolores Umbridge', 64 | ]) 65 | }) 66 | 67 | test.skip('can be used to do some fancy stuff with arrays', () => { 68 | const characters = [ 69 | 'Harry Potter', 70 | 'Ron Weasly', 71 | 'Hermione Granger', 72 | 'Nevel Longbottom', 73 | 'Lavender Brown', 74 | 'Scabbers', 75 | 'Pigwidgeon', 76 | ] 77 | 78 | const handler = {} 79 | const proxy = new Proxy(characters, handler) 80 | expect(proxy[0]).toBe('Harry Potter') 81 | expect(proxy[-1]).toBe('Pigwidgeon') 82 | expect(proxy[-4]).toBe('Nevel Longbottom') 83 | }) 84 | 85 | //////// Elaboration & Feedback ///////// 86 | /* 87 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Proxies&em= 88 | */ 89 | test('I submitted my elaboration and feedback', () => { 90 | const submitted = false // change this when you've submitted! 91 | expect(true).toBe(submitted) 92 | }) 93 | //////////////////////////////// 94 | 95 | //////// EXTRA CREDIT //////// 96 | 97 | // If you get this far, try adding a few more tests, 98 | // then file a pull request to add them to the extra credit! 99 | // Learn more here: http://kcd.im/es6-workshop-contributing 100 | -------------------------------------------------------------------------------- /exercises/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | setupTestFrameworkScriptFile: require.resolve( 3 | '../common/helpers/expect-to-be-valid-syntax.js', 4 | ), 5 | testURL: 'http://localhost/', 6 | } 7 | -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | concurrent: false, 3 | linters: { 4 | '.all-contributorsrc': [ 5 | `kcd-scripts contributors generate`, 6 | 'git add README.md', 7 | ], 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /other/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [kent@doddsfamily.us](mailto:kent@doddsfamily.us). All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /other/workshop-info.md: -------------------------------------------------------------------------------- 1 | # The Workshop 2 | 3 | Here's some info about how the workshop should go... 4 | 5 | ## Order of things... 6 | 7 | 1. Pre-quizzes 8 | 2. Tests 9 | 3. Elaboration 10 | 4. Feedback Form 11 | 5. ? Help others 12 | 6. ? Make pull requests 13 | 14 | ## Communication 15 | 16 | * 🙋 (hand raising: for those of you here in person) 17 | * Do: 18 | * Ask questions 19 | * Answer my questions 20 | * Make well-meaning relevant-to-the-whole-group comments 21 | * Don't: 22 | * "well, actually" 23 | * [💬🌎 (group chat)](https://gitter.im/kentcdodds/es6-workshop) 24 | * Do: 25 | * Ask questions 26 | * Applies for if you're not here in person, or if you'd prefer to not draw attention to yourself. I'll be in the group chat during the exercises as well as other attendees who may be able to answer your question 27 | * Answer my questions 28 | * Answer other's questions 29 | * Don't: 30 | * Be distracting 31 | * [💬😀 (one-on-one chat)](https://gitter.im/kentcdodds) 32 | * Do: 33 | * Ask questions 34 | * If it's more comfortable for you that way. I'll try to answer during the exercises. 35 | * 📑 (surveys: at the end of each exercise) 36 | * Do: 37 | * Fill them out please! 38 | * [📧 (AMA)](http://kcd.im/ama) 39 | * Do: 40 | * Ask any question you'd like 41 | * [🐦 (Twitter @kentcdodds)](https://twitter.com/kentcdodds) 42 | * Do: 43 | * Follow me if you wanna 😄 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ES6-Workshop", 3 | "version": "0.0.0", 4 | "private": true, 5 | "description": "[![slides-badge][slides-badge]][slides] [![chat-badge][chat-badge]][chat] [![Build Status][build-badge]][build] [![Dependencies][dependencyci-badge]][dependencyci] [![MIT License][license-badge]][license] [![All Contributors](https://img.shields.io/badge/all_contributors-16-orange.svg?style=flat-square)](#contributors)", 6 | "bugs": { 7 | "url": "https://github.com/kentcdodds/es6-workshop/issues" 8 | }, 9 | "main": "index.js", 10 | "keywords": [ 11 | "workshop", 12 | "es6" 13 | ], 14 | "author": "Kent C. Dodds (http://kentcdodds.com/)", 15 | "license": "GPL-3.0", 16 | "homepage": "https://github.com/kentcdodds/es6-workshop", 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/kentcdodds/es6-workshop.git" 20 | }, 21 | "dependencies": {}, 22 | "devDependencies": { 23 | "babel-cli": "^6.26.0", 24 | "chalk": "^2.3.2", 25 | "chokidar": "^2.0.3", 26 | "concurrently": "^3.5.1", 27 | "eslint": "^4.19.1", 28 | "glob": "^7.1.2", 29 | "kcd-scripts": "^0.37.0", 30 | "lodash": "^4.17.5", 31 | "onchange": "^3.3.0", 32 | "pretty-format": "^22.4.3", 33 | "replace-in-file": "^3.4.0", 34 | "split-guide": "^3.0.0" 35 | }, 36 | "scripts": { 37 | "add-contributor": "kcd-scripts contributors add", 38 | "start": "npm run test:watch", 39 | "lint": "kcd-scripts lint", 40 | "precommit": "kcd-scripts precommit && npm run generate && kcd-scripts format && npm run validate && git add exercises exercises-final", 41 | "quiz": "babel-node quizzes/run.js", 42 | "test": "kcd-scripts test --config exercises/jest.config.js", 43 | "test:final": "kcd-scripts test --config exercises-final/jest.config.js", 44 | "dev": "concurrently --names \"generate,test:final\" --prefix \"[{name}]\" --prefix-colors \"bgGreen.reset.bodl,bgBlue.reset.bold\" \"npm run generate:watch --silent\" \"npm run test:final --silent\"", 45 | "generate": "split-guide generate --silent-success", 46 | "generate:watch": "onchange \"templates/**/*.*\" --initial -- npm run generate", 47 | "validate": "concurrently --names \"lint,test:final\" --prefix \"[{name}]\" --prefix-colors \"bgGreen.reset.bold,bgBlue.reset.bold\" \"npm run lint --silent\" \"npm run test:final --silent -- --no-watch\"", 48 | "setup": "node ./scripts/verify && node ./scripts/install && npm run validate && node ./scripts/autofill-feedback-email" 49 | }, 50 | "babel": { 51 | "presets": [ 52 | "kcd-scripts/babel" 53 | ] 54 | }, 55 | "eslintConfig": { 56 | "extends": [ 57 | "kentcdodds/possible-errors", 58 | "kentcdodds/es6/possible-errors", 59 | "kentcdodds/jest" 60 | ], 61 | "rules": { 62 | "no-console": "off", 63 | "jest/no-disabled-tests": "off" 64 | } 65 | }, 66 | "eslintIgnore": [ 67 | "exercises", 68 | "scripts", 69 | "other", 70 | "node_modules", 71 | "templates" 72 | ] 73 | } 74 | -------------------------------------------------------------------------------- /quizzes/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "kentcdodds/best-practices", 4 | "kentcdodds/stylistic", 5 | "kentcdodds/es6/best-practices", 6 | "kentcdodds/es6/stylistic", 7 | ], 8 | "rules": { 9 | "no-constant-condition": "off", 10 | "no-unused-vars": "off", 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /quizzes/01_block-scoping.js: -------------------------------------------------------------------------------- 1 | import log from './pretty-log' 2 | 3 | function basic() { 4 | // what is returned? 5 | let x = 100 6 | const y = 200 7 | return {x: x, y: y} 8 | } 9 | // log(basic()) 10 | 11 | function immutable() { 12 | // what is returned? 13 | const object = {a: 'b'} 14 | const array = [1, 2, 3, 4] 15 | 16 | object.a = 'q' 17 | array.splice(1, 1) 18 | return {object: object, array: array} 19 | } 20 | // log(immutable()) 21 | 22 | function immutableReference() { 23 | // what is returned? 24 | const object = {a: 'b'} 25 | // object = {a: 'q'} 26 | return object 27 | } 28 | // log(immutableReference()) 29 | 30 | function ifBlock() { 31 | // what is returned? 32 | if (3 > 1) { 33 | const x = 34 34 | let y = 43 35 | } 36 | return {x: x, y: y} 37 | } 38 | // log(ifBlock()) 39 | 40 | function block() { 41 | // what is returned? 42 | { 43 | // this is called a "block" ✨ 44 | const x = 42 45 | let y = 24 46 | } 47 | return {x: x, y: y} 48 | } 49 | // log(block()) 50 | 51 | function scoped() { 52 | // what is returned? 53 | let x = 33 54 | { 55 | const x = 123 56 | } 57 | return x 58 | } 59 | // log(scoped()) 60 | 61 | function veryScoped() { 62 | // what is returned? 63 | let x = 23 64 | { 65 | let x 66 | { 67 | x = 55 68 | } 69 | // let x = 45 // if this weren't commented out, this file would fail parsing 70 | } 71 | return x 72 | } 73 | // log(veryScoped()) 74 | 75 | function temporalDeadZone() { 76 | console.log(myVar) 77 | console.log(myLet) 78 | console.log(myConst) 79 | 80 | var myVar = 'var' 81 | let myLet = 'let' 82 | const myConst = 'const' 83 | return {myVar: myVar, myLet: myLet, myConst: myConst} 84 | } 85 | // log(temporalDeadZone()) 86 | 87 | function semiPractical() { 88 | // what is returned from this function? 89 | const myThings = ['thing1', 'thing2', 'red fish', 'blue fish'] 90 | const callbacks = [] 91 | for (var i = 0; i < myThings.length; i++) { 92 | callbacks.push(function thingGetter() { 93 | return myThings[i] 94 | }) 95 | } 96 | return callbacks.map(callback => callback()) 97 | } 98 | // log(semiPractical()) 99 | 100 | /* 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | SOLUTIONS ARE BELOW 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | */ 119 | 120 | function immutableReferenceSOLUTION() { 121 | // what is returned? 122 | let object = {a: 'b'} 123 | object = {a: 'q'} 124 | return object 125 | } 126 | // log(immutableReferenceSOLUTION()) 127 | 128 | function semiPracticalSOLUTION() { 129 | const myThings = ['thing1', 'thing2', 'red fish', 'blue fish'] 130 | const callbacks = [] 131 | for (let i = 0; i < myThings.length; i++) { 132 | callbacks.push(function thingGetter() { 133 | return myThings[i] 134 | }) 135 | } 136 | return callbacks.map(callback => callback()) 137 | } 138 | // log(semiPracticalSOLUTION()) 139 | 140 | /* 141 | eslint 142 | prefer-const:0, 143 | no-undef:0, 144 | no-shadow:0, 145 | no-lone-blocks:0, 146 | object-shorthand:0, 147 | vars-on-top:0, 148 | no-var:0, 149 | no-loop-func:0, 150 | no-const-assign:0, 151 | no-use-before-define:0 152 | */ 153 | -------------------------------------------------------------------------------- /quizzes/02_template-literals.js: -------------------------------------------------------------------------------- 1 | import log from './pretty-log' 2 | 3 | function interpolation() { 4 | // refactor this to a single template literal using interpolation 5 | // template literals use backticks instead of double/single quote 6 | // interpolation is done with ${variable} 7 | const greeting = 'Hello' 8 | const noun = 'World' 9 | return greeting.toUpperCase() + ', ' + noun + '!' 10 | } 11 | // log(interpolation()) 12 | 13 | function multiline() { 14 | // refactor to a single template literal with multiple lines 15 | return '\n' + ' this is on a new line\n' + ' ' 16 | } 17 | // log(multiline()) 18 | 19 | function escaping() { 20 | // refactor to a single template literal with multiple lines 21 | return 'This is `code`\nand a new line' 22 | } 23 | // log(escaping()) 24 | 25 | function tagging() { 26 | const greeting = 'Hey' 27 | const noun = 'human' 28 | return tag`I would like to say: "${greeting}, ${noun}" to you!` 29 | 30 | function tag() { 31 | // what are the arguments passed to this function? 32 | // notice that we're returning arguments. So... We can return ANYTHING. Not just a string! 33 | return arguments 34 | } 35 | } 36 | // log(tagging()) 37 | 38 | /* 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | SOLUTIONS ARE BELOW 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | */ 57 | 58 | function interpolationSOLUTION() { 59 | // refactor this to a single template literal using interpolation 60 | // template literals use backticks instead of double/single quote 61 | // interpolation is done with ${variable} 62 | const greeting = 'Hello' 63 | const noun = 'World' 64 | return `${greeting.toUpperCase()}, ${noun}!` 65 | } 66 | // log(interpolationSOLUTION()) 67 | 68 | function multilineSOLUTION() { 69 | // refactor to a single template literal with multiple lines 70 | return ` 71 | this is on a new line 72 | ` 73 | } 74 | // log(multilineSOLUTION()) 75 | 76 | function escapingSOLUTION() { 77 | // refactor to a single template literal with multiple lines 78 | return `This is \`code\`\nand a new line` 79 | } 80 | // log(escapingSOLUTION()) 81 | 82 | /* eslint prefer-template:0, prefer-rest-params:0, no-useless-concat:0 */ 83 | -------------------------------------------------------------------------------- /quizzes/03_new-apis.js: -------------------------------------------------------------------------------- 1 | import log from './pretty-log' 2 | 3 | function stringIncludes() { 4 | // refactor to String.includes 5 | const phrase = 'in the box' 6 | return phrase.indexOf('the') !== -1 7 | } 8 | // log(stringIncludes()) 9 | 10 | function stringRepeat() { 11 | // refactor to String.repeat 12 | const repeatCount = 20 13 | let marker = '' 14 | for (let i = 0; i < repeatCount; i++) { 15 | marker += '*' 16 | } 17 | return marker 18 | } 19 | // log(stringRepeat()) 20 | 21 | function arrayFrom() { 22 | // refactor to Array.from 23 | const arrayLike = {length: 2, 0: 'hello', 1: 'world'} 24 | return Array.prototype.slice.apply(arrayLike) 25 | } 26 | // log(arrayFrom()) 27 | 28 | function arrayFill() { 29 | // refactor to Array.fill 30 | const value = 'hey' 31 | const start = 1 32 | const end = 4 33 | const array = new Array(7) 34 | for (let i = 0; i < array.length; i++) { 35 | if (i >= start && i < end) { 36 | array[i] = value 37 | } 38 | } 39 | return array 40 | } 41 | // log(arrayFill()) 42 | 43 | function objectAssign() { 44 | // what will this return? 45 | const target = { 46 | a: 'a from target', 47 | c: 'c from target', 48 | e: 'e from target', 49 | g: 'g from target', 50 | i: 'i from target', 51 | k: 'k from target', 52 | } 53 | const object1 = {a: 'a from object1', c: 'c from object1'} 54 | const object2 = {c: 'c from object2', f: 'f from object2'} 55 | const object3 = {a: 'a from object3', i: 'i from object3'} 56 | Object.assign(target, object1, object2, object3) 57 | return target 58 | } 59 | // log(objectAssign()) 60 | 61 | /* 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | SOLUTIONS ARE BELOW 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | */ 80 | 81 | function stringIncludesSOLUTION() { 82 | // refactor to String.includes 83 | const phrase = 'in the box' 84 | return phrase.includes('the') 85 | } 86 | // log(stringIncludesSOLUTION()) 87 | 88 | function stringRepeatSOLUTION() { 89 | // refactor to String.repeat 90 | const repeatCount = 20 91 | return '*'.repeat(repeatCount) 92 | } 93 | // log(stringRepeatSOLUTION()) 94 | 95 | function arrayFromSOLUTION() { 96 | // refactor to Array.from 97 | const arrayLike = {length: 2, 0: 'hello', 1: 'world'} 98 | return Array.from(arrayLike) 99 | } 100 | // log(arrayFromSOLUTION()) 101 | 102 | function arrayFillSOLUTION() { 103 | // refactor to Array.fill 104 | const value = 'hey' 105 | const start = 1 106 | const end = 4 107 | const array = new Array(7) 108 | return array.fill(value, start, end) 109 | } 110 | // log(arrayFillSOLUTION()) 111 | -------------------------------------------------------------------------------- /quizzes/05_modules.js: -------------------------------------------------------------------------------- 1 | // we'll be importing stuff from './helpers/module-a' 2 | 3 | // 1. how could we import a module without doing 4 | // anything with what we get back? 5 | 6 | // 2. how could we import the default export? 7 | 8 | // 3. how could we import `theAnswer` and 9 | // `theQuestion` from './helpers/module-a'? 10 | 11 | // 4. how could we import `theAnswer` and rename it 12 | // to `fortyTwo`? 13 | 14 | // 5. how could we import `add` (default) and 15 | // `theQuestion` with a single import declaration? 16 | 17 | // 6. how could we avoid the default alias when 18 | // importing `add` and `theQuestion`? 19 | 20 | // 7. how could we import everything onto a single 21 | // object called `allTheThings` 22 | 23 | /* 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | SOLUTIONS ARE BELOW 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | */ 42 | 43 | // But they're commented out because JavaScript 44 | 45 | // 1. 46 | // import './helpers/module-a' 47 | 48 | // 2. 49 | // import add from './helpers/module-a' 50 | 51 | // 3. 52 | // import {theAnswer,theQuestion} from './helpers/module-a' 53 | 54 | // 4. 55 | // import {theAnswer as fourtyTwo} from './helpers/module-a' 56 | 57 | // 5. 58 | // import {default as add, theQuestion} from './helpers/module-a' 59 | 60 | // 6. 61 | // import add, {theQuestion} from './helpers/module-a' 62 | 63 | // 7. 64 | // import * as allTheThings from './helpers/module-a' 65 | -------------------------------------------------------------------------------- /quizzes/06_object-literal.js: -------------------------------------------------------------------------------- 1 | import log from './pretty-log' 2 | 3 | function shorthandPropertyNames() { 4 | // refactor with shortening the object literal 5 | // and removing reptition 6 | const red = Math.floor(Math.random() * 256) 7 | const green = Math.floor(Math.random() * 256) 8 | const blue = Math.floor(Math.random() * 256) 9 | 10 | return { 11 | red: red, 12 | green: green, 13 | blue: blue, 14 | } 15 | } 16 | // log(shorthandPropertyNames()) 17 | 18 | function proto() { 19 | // refactor using __proto__ :) 20 | const parent = { 21 | parentProp: 'parent', 22 | overriddenProp: 'parent overridden?', 23 | } 24 | const child = Object.create(parent, { 25 | childProp: { 26 | value: 'child', 27 | configurable: true, 28 | enumerable: true, 29 | writable: true, 30 | }, 31 | overriddenProp: { 32 | value: 'child overridden!', 33 | configurable: true, 34 | enumerable: true, 35 | writable: true, 36 | }, 37 | }) 38 | // getAllPropsInObject returns an object with 39 | // `inheritedProps` and `ownProps` objects. 40 | return getAllPropsInObject(child) 41 | } 42 | // log(proto()) 43 | 44 | function methodShorthand() { 45 | // refactor using method shorthand 46 | const greeter = { 47 | sayHi: function sayHi(name) { 48 | return `Hi ${name}` 49 | }, 50 | } 51 | return greeter.sayHi('Nancy') 52 | } 53 | // log(methodShorthand()) 54 | 55 | function methodSuperCalls() { 56 | // refactor fileTaxes on the child using method shorthand and use `super` instead 57 | const parent = { 58 | doFilingOfTaxes() { 59 | return 'taxes filed' 60 | }, 61 | fileTaxes: function fileTaxes() { 62 | return this.doFilingOfTaxes() 63 | }, 64 | } 65 | const child = { 66 | __proto__: parent, 67 | taxesFiledCount: 0, 68 | fileTaxes: function fileTaxes() { 69 | this.taxesFiledCount++ 70 | return Object.getPrototypeOf(this).fileTaxes.call(this) 71 | }, 72 | } 73 | const taxesResult = child.fileTaxes() 74 | return {taxesResult, taxesFiledCount: child.taxesFiledCount} 75 | } 76 | // log(methodSuperCalls()) 77 | 78 | function computedPropertyNames() { 79 | function getCar(make, model) { 80 | const car = {} 81 | car[make.toLowerCase()] = model 82 | return car 83 | } 84 | return getCar('Hyundai', 'Accent') 85 | } 86 | // log(computedPropertyNames()) 87 | 88 | // helpers 89 | 90 | function getAllPropsInObject(obj) { 91 | const allPropsObj = {ownProps: {}, inheritedProps: {}} 92 | for (const prop in obj) { 93 | if (obj.hasOwnProperty(prop)) { 94 | allPropsObj.ownProps[prop] = obj[prop] 95 | } else { 96 | allPropsObj.inheritedProps[prop] = obj[prop] 97 | } 98 | } 99 | return allPropsObj 100 | } 101 | 102 | /* 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | SOLUTIONS ARE BELOW 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | */ 121 | 122 | function shorthandPropertyNamesSOLUTION() { 123 | const red = Math.floor(Math.random() * 256) 124 | const green = Math.floor(Math.random() * 256) 125 | const blue = Math.floor(Math.random() * 256) 126 | 127 | return {red, green, blue} 128 | } 129 | // log(shorthandPropertyNamesSOLUTION()) 130 | 131 | function protoSOLUTION() { 132 | const parent = { 133 | parentProp: 'parent', 134 | overriddenProp: 'parent overridden?', 135 | } 136 | const child = { 137 | __proto__: parent, 138 | childProp: 'child', 139 | overriddenProp: 'child overridden!', 140 | } 141 | return getAllPropsInObject(child) 142 | } 143 | // log(protoSOLUTION()) 144 | 145 | function methodShorthandSOLUTION() { 146 | const greeter = { 147 | sayHi(name) { 148 | return `Hi ${name}` 149 | }, 150 | } 151 | return greeter.sayHi('Nancy') 152 | } 153 | // log(methodShorthandSOLUTION()) 154 | 155 | function methodSuperCallsSOLUTION() { 156 | const parent = { 157 | doFilingOfTaxes() { 158 | return 'taxes filed' 159 | }, 160 | fileTaxes: function fileTaxes() { 161 | return this.doFilingOfTaxes() 162 | }, 163 | } 164 | const child = { 165 | __proto__: parent, 166 | taxesFiledCount: 0, 167 | fileTaxes() { 168 | this.taxesFiledCount++ 169 | return super.fileTaxes() 170 | }, 171 | } 172 | const taxesResult = child.fileTaxes() 173 | return {taxesResult, taxesFiledCount: child.taxesFiledCount} 174 | } 175 | // log(methodSuperCallsSOLUTION()) 176 | 177 | function computedPropertyNamesSOLUTION() { 178 | function getCar(make, model) { 179 | return { 180 | [make.toLowerCase()]: model, 181 | } 182 | } 183 | return getCar('Hyundai', 'Accent') 184 | } 185 | // log(computedPropertyNamesSOLUTION()) 186 | 187 | /* eslint object-shorthand:0 */ 188 | -------------------------------------------------------------------------------- /quizzes/08_spread.js: -------------------------------------------------------------------------------- 1 | import log from './pretty-log' 2 | 3 | function spreadFunctionCall() { 4 | // TODO: return the maximum value in an array of integers 5 | // use spread operator and Math.max() in refactoring 6 | const arr = [5, 6, 8, 4, 9] 7 | return Math.max.apply(null, arr) 8 | } 9 | // log(spreadFunctionCall()) 10 | 11 | function concatArrays() { 12 | // TODO: flatten nested arrays of arbitrary levels of nesting 13 | // arr can be typically like this: [8, 9, [6, [5, [7], [45, 34, [2]]]]] 14 | // output should be [8, 9, 6, 5, 7, 45, 34, 2] 15 | // use spread operator in place of Array.prototype.concat() 16 | const arr = [8, 9, [6, [5, [7], [45, 34, [[[2]]], [[[[[[[[7]]]]], 90]]]]]]] 17 | return flatter(arr) 18 | 19 | function flatter(arg) { 20 | return arg.reduce((acc, item) => { 21 | if (Array.isArray(item)) { 22 | return acc.concat(flatter(item)) 23 | } 24 | return acc.concat([item]) 25 | }, []) 26 | } 27 | } 28 | // log(concatArrays()) 29 | 30 | function mergeObjects() { 31 | // refactor to object spread 32 | const obj1 = { 33 | a: 'a from obj1', 34 | b: 'b from obj1', 35 | c: 'c from obj1', 36 | d: { 37 | e: 'e from obj1', 38 | f: 'f from obj1', 39 | }, 40 | } 41 | const obj2 = { 42 | b: 'b from obj2', 43 | c: 'c from obj2', 44 | d: { 45 | g: 'g from obj2', 46 | h: 'g from obj2', 47 | }, 48 | } 49 | return Object.assign({}, obj1, obj2) 50 | } 51 | // log(mergeObjects()) 52 | 53 | function spreadString() { 54 | return join('--', 'PayPal') 55 | 56 | function join(delimiter, string) { 57 | return [...string].join(delimiter) 58 | } 59 | } 60 | // log(spreadString()) 61 | 62 | function restString() { 63 | // what does this return? 64 | const greeting = 'Hello world' 65 | const [h, e, l, l2, o, space, ...splitGreeting] = greeting 66 | return splitGreeting 67 | } 68 | // log(restString()) 69 | 70 | // SOLUTIONS ARE GIVEN BELOW 71 | // No peeking! 72 | 73 | function spreadFunctionCallSOLUTION() { 74 | const arr = [5, 6, 8, 4, 9] 75 | return Math.max(...arr) 76 | } 77 | // log(spreadFunctionCallSOLUTION()) 78 | 79 | function concatArraysSOLUTION() { 80 | const arr = [8, 9, [6, [5, [7], [45, 34, [[[2]]], [[[[[[[[7]]]]], 90]]]]]]] 81 | return flatter(arr) 82 | 83 | function flatter(arg) { 84 | return arg.reduce((acc, item) => { 85 | return Array.isArray(item) ? [...acc, ...flatter(item)] : [...acc, item] 86 | }, []) 87 | } 88 | } 89 | // log(concatArraysSOLUTION()) 90 | 91 | function mergeObjectsSOLUTION() { 92 | const obj1 = { 93 | a: 'a from obj1', 94 | b: 'b from obj1', 95 | c: 'c from obj1', 96 | d: { 97 | e: 'e from obj1', 98 | f: 'f from obj1', 99 | }, 100 | } 101 | const obj2 = { 102 | b: 'b from obj2', 103 | c: 'c from obj2', 104 | d: { 105 | g: 'g from obj2', 106 | h: 'g from obj2', 107 | }, 108 | } 109 | return { 110 | ...obj1, 111 | ...obj2, 112 | } 113 | } 114 | // log(mergeObjectsSOLUTION()) 115 | -------------------------------------------------------------------------------- /quizzes/11_set.js: -------------------------------------------------------------------------------- 1 | function basicSet() { 2 | // What do you think this will return? 3 | const set = new Set() 4 | set.add(1) 5 | set.add(2) 6 | set.add(1) 7 | return set 8 | } 9 | // console.log(basicSet()) 10 | 11 | function unique() { 12 | // What do you think this will return? 13 | const obj = {a: 45, b: 50} 14 | const s = new Set() 15 | s.add(obj) 16 | s.add({a: 45, b: 50}) 17 | return s 18 | } 19 | // console.log(unique()) 20 | 21 | function initializeSet() { 22 | const arr = [1, 5, 7, 2, 9, 4] 23 | // TODO: change the following line 24 | // so that the Set t has array's contents 25 | const set = new Set() 26 | return set 27 | } 28 | // console.log(initializeSet()) 29 | 30 | function setInfo() { 31 | const obj = {a: 42} 32 | const set = new Set([1, 2, obj, 3, 4]) 33 | // how do you get the size of the set? 34 | // how can you determine whether the set has a value? 35 | return { 36 | // fill these values in... 37 | size: set.length, 38 | hasObj: set.indexOf(obj) !== -1, 39 | hasFive: set.indexOf(5) !== -1, 40 | thirdItem: set[2], 41 | } 42 | } 43 | // log(setInfo()) 44 | 45 | function removeDuplicates() { 46 | // given an array 47 | // we have to remove duplicate entries 48 | const arr = [6, 8, 3, 9, 6, 5, 8, 2, 3, 9, 7, 7, 2, 1, 0, 8] 49 | 50 | // TODO: use a Set to do this 51 | // refactor the following 52 | const uniqueArr = arr.filter((elem, index) => arr.indexOf(elem) === index) 53 | 54 | return uniqueArr 55 | } 56 | // console.log(removeDuplicates()) 57 | 58 | function iterateSets() { 59 | const set = new Set([1, 2, 3, 4]) 60 | for (const item of set) { 61 | console.log(item) 62 | } 63 | } 64 | // iterateSets() 65 | 66 | function spreadSets() { 67 | // what does this return? 68 | const set = new Set([1, 2, 3, 4]) 69 | const array = [...set] 70 | return array 71 | } 72 | // console.log(spreadSets()) 73 | 74 | function restSet() { 75 | // what does this return? 76 | const set = new Set([1, 2, 3, 4]) 77 | const [first, second, ...rest] = set 78 | return rest 79 | } 80 | // console.log(restSet()) 81 | 82 | // SOLUTION BELOW 83 | // No peeking! 84 | 85 | function initializeSetSOLUTION() { 86 | const arr = [1, 5, 7, 2, 9, 4] 87 | const t = new Set(arr) 88 | return t 89 | } 90 | // console.log(initializeSetSOLUTION()) 91 | 92 | function setInfoSOLUTION() { 93 | const obj = {a: 42} 94 | const set = new Set([1, 2, obj, 3, 4]) 95 | const [, , thirdItem] = set 96 | return { 97 | size: set.size, 98 | hasObj: set.has(obj), 99 | hasFive: set.has(5), 100 | thirdItem, 101 | } 102 | } 103 | // log(setInfoSOLUTION()) 104 | 105 | function removeDuplicatesSOLUTION() { 106 | // given an array 107 | // we have to remove duplicate entries 108 | const arr = [6, 8, 3, 9, 6, 5, 8, 2, 3, 9, 7, 7, 2, 1, 0, 8] 109 | 110 | // TODO: use a Set to do this 111 | // refactor the following 112 | const set = new Set(arr) 113 | const uniqueArr = Array.from(set) 114 | return uniqueArr 115 | } 116 | // console.log(removeDuplicatesSOLUTION()) 117 | -------------------------------------------------------------------------------- /quizzes/12_maps.js: -------------------------------------------------------------------------------- 1 | function basicMap() { 2 | const map = new Map() 3 | // TODO: add "a" as key, 3 as value 4 | // TODO: key is {v: 5, j: 7}, value is 7 5 | // TODO: key is a noop function, value is 8 6 | return map 7 | } 8 | // console.log(basicMap()) 9 | 10 | function initializeMap() { 11 | // how could we create the same kind 12 | // of map like we have above, except 13 | // do it when we call new Map()? 14 | const map = new Map() 15 | return map 16 | } 17 | // console.log(initializeMap()) 18 | 19 | function uniquePair() { 20 | // what will this return? 21 | const map = new Map() 22 | const obj = {raspberriesGood: true} 23 | map.set(obj, {sortaGood: false}) 24 | map.set({raspberriesGood: true}, {sortaGood: false}) 25 | map.set(obj, {reallyGood: true}) 26 | return map 27 | } 28 | // console.log(uniquePair()) 29 | 30 | function mapInfo() { 31 | const objKey = {awesome: true} 32 | const map = new Map([[objKey, 42]]) 33 | // how do we get `objKey`? 34 | return { 35 | objValue: map[objKey], 36 | mapSize: Object.keys(map).length, 37 | hasTrue: map.hasOwnProperty(true), 38 | has32: map.hasOwnProperty(32), 39 | keys: Object.keys(map), 40 | values: Object.keys(map).map(key => map[key]), 41 | entries: Object.keys(map).map(key => [key, map[key]]), 42 | } 43 | } 44 | // console.log(mapInfo()) 45 | 46 | function removingItems() { 47 | const objKey = {awesome: true} 48 | const map = new Map([[objKey, 42], [{}, 'hey'], [45, true]]) 49 | const firstSize = map.size 50 | map.delete(objKey) 51 | const afterDeleteSize = map.size 52 | map.clear() 53 | const afterClearSize = map.size 54 | return {firstSize, afterDeleteSize, afterClearSize} 55 | } 56 | // console.log(removingItems()) 57 | 58 | function iterateMaps() { 59 | const map = new Map([ 60 | ['key1', 'value1'], 61 | ['key2', 'value2'], 62 | ['key3', 'value3'], 63 | ['key4', 'value4'], 64 | ]) 65 | for (const [key, value] of map) { 66 | console.log(key, value) 67 | } 68 | } 69 | // iterateMaps() 70 | 71 | function spreadMaps() { 72 | // what does this return? 73 | const map = new Map([ 74 | ['key1', 'value1'], 75 | ['key2', 'value2'], 76 | ['key3', 'value3'], 77 | ['key4', 'value4'], 78 | ]) 79 | const array = [...map] 80 | return array 81 | } 82 | // console.log(spreadMaps()) 83 | 84 | function restMap() { 85 | // what does this return? 86 | const map = new Map([ 87 | ['key1', 'value1'], 88 | ['key2', 'value2'], 89 | ['key3', 'value3'], 90 | ['key4', 'value4'], 91 | ]) 92 | const [first, second, ...rest] = map 93 | return rest 94 | } 95 | // console.log(restMap()) 96 | 97 | // SOLUTION BELOW 98 | // No peeking! 99 | 100 | function basicMapSOLUTION() { 101 | const map = new Map() 102 | map.set('a', 3) 103 | map.set({v: 5, j: 7}, 7) 104 | map.set(() => {}, 8) 105 | return map 106 | } 107 | // console.log(basicMapSOLUTION()) 108 | 109 | function initializeMapSOLUTION() { 110 | const map = new Map([['a', 3], [{v: 5, j: 7}, 7], [() => {}, 8]]) 111 | return map 112 | } 113 | // console.log(initializeMapSOLUTION()) 114 | 115 | function mapInfoSOLUTION() { 116 | const objKey = {awesome: true} 117 | const map = new Map([[objKey, 42], [4, 55], [true, false], [false, true]]) 118 | return { 119 | objValue: map.get(objKey), 120 | mapSize: map.size, 121 | hasTrue: map.has(true), 122 | has32: map.has(32), 123 | keys: map.keys(), 124 | values: map.values(), 125 | entries: map.entries(), 126 | } 127 | } 128 | // console.log(mapInfoSOLUTION()) 129 | -------------------------------------------------------------------------------- /quizzes/13_weakmap.js: -------------------------------------------------------------------------------- 1 | function weakMapInfo() { 2 | // what will this return? 3 | const obj = {iHaveYouNow: true} 4 | const weakMap = new WeakMap([ 5 | [{iDoNotHaveYouEver: true}, true], 6 | [obj, 'saweet'], 7 | ]) 8 | return { 9 | size: weakMap.size, 10 | objValue: weakMap.get(obj), 11 | values: weakMap.values, 12 | keys: weakMap.keys, 13 | entries: weakMap.entries, 14 | } 15 | } 16 | // console.log(weakMapInfo()) 17 | -------------------------------------------------------------------------------- /quizzes/14_promises.js: -------------------------------------------------------------------------------- 1 | // callbacks() 2 | // promises() 3 | 4 | function callbacks() { 5 | // refactor to promises 6 | 7 | // successful 8 | logResult(cb => { 9 | timeout(100, false, (error, result) => { 10 | // ignore handle error 11 | cb(null, `success: ${result}`) 12 | }) 13 | }) 14 | 15 | // failing 16 | logResult(cb => { 17 | timeout(100, true, (error, result) => { 18 | cb(`failure: ${error}`) 19 | // ignore success 20 | }) 21 | }) 22 | 23 | // error recovery 24 | logResult(cb => { 25 | timeout(200, true, (error, result) => { 26 | if (error) { 27 | // recoving from an error 28 | return cb(null, `Recovered from error: ${error}`) 29 | } 30 | cb(null, `${result} with stuff`) 31 | }) 32 | }) 33 | 34 | // wild example... 35 | logResult(cb => { 36 | timeout(100, false, (error1, result1) => { 37 | if (error1) { 38 | return catcher(error1) 39 | } 40 | timeout(200, false, (error2, result2) => { 41 | if (error2) { 42 | return catcher(error2) 43 | } 44 | catcher(new Error('hmm')) 45 | }) 46 | }) 47 | function catcher(ignoredError) { 48 | const combinedCallback = waitForAll(2, cb) 49 | timeout(200, false, combinedCallback) 50 | timeout(100, false, combinedCallback) 51 | } 52 | 53 | function waitForAll(number, finalCallback) { 54 | const allResults = [] 55 | let errored = false 56 | return function callback(error, result) { 57 | if (error) { 58 | // TODO: handle this... 59 | finalCallback(error) 60 | errored = true 61 | } 62 | if (errored) { 63 | return 64 | } 65 | allResults.push(result) 66 | if (allResults.length === number) { 67 | finalCallback(null, allResults) 68 | } 69 | } 70 | } 71 | }) 72 | 73 | function logResult(fn) { 74 | fn((error, result) => { 75 | if (error) { 76 | logError(error) 77 | } else if (result) { 78 | log(result) 79 | } 80 | }) 81 | } 82 | 83 | function timeout(duration, shouldError, callback) { 84 | setTimeout(() => { 85 | if (shouldError) { 86 | callback(`rejected after ${duration}ms`) 87 | } else { 88 | callback(null, `resolved after ${duration}ms`) 89 | } 90 | }, duration) 91 | } 92 | } 93 | 94 | function log(...args) { 95 | console.log(...args) 96 | } 97 | 98 | function logError(...args) { 99 | console.error(...args) 100 | } 101 | 102 | /* 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | SOLUTIONS ARE BELOW 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | */ 121 | 122 | function promises() { 123 | const successfulPromise = timeout(100).then(result => { 124 | return `success: ${result}` 125 | }) 126 | 127 | const failingPromise = timeout(100, true).then(null, error => { 128 | return Promise.reject(`failure: ${error}`) 129 | }) 130 | 131 | const caughtPromise = timeout(200, true).then( 132 | result => { 133 | return `${result} with stuff` 134 | }, 135 | error => { 136 | return Promise.resolve(`Recovered from error: ${error}`) 137 | }, 138 | ) 139 | 140 | const otherStuffPromise = timeout(100) 141 | .then(() => { 142 | return timeout(200) 143 | }) 144 | .then(() => { 145 | throw new Error('hmm') 146 | }) 147 | .catch(() => { 148 | return Promise.all([timeout(100), timeout(200)]) 149 | }) 150 | 151 | successfulPromise.then(log, logError) 152 | failingPromise.then(log, logError) 153 | caughtPromise.then(log, logError) 154 | otherStuffPromise.then(log, logError) 155 | 156 | function timeout(duration = 0, shouldReject = false) { 157 | return new Promise((resolve, reject) => { 158 | setTimeout(() => { 159 | if (shouldReject) { 160 | reject(`rejected after ${duration}ms`) 161 | } else { 162 | resolve(`resolved after ${duration}ms`) 163 | } 164 | }, duration) 165 | }) 166 | } 167 | } 168 | 169 | /* eslint consistent-return:0 */ 170 | 171 | /* 172 | Concepts to cover: 173 | - .then(successHandler) 174 | - .then(successHandler, errorHandler) 175 | - .catch(errorHandler) 176 | - Promise Tree (chaining) 177 | - state 178 | - pending 179 | - resolved 180 | - rejected 181 | - Promise.resolve 182 | - Promise.reject 183 | - returning a promise in successHandler 184 | - passing a promise to Promise.resolve 185 | - returning anything in an errorHandler 186 | - passing a promise to Promise.reject 187 | - error in successHandler 188 | - non-error in errorHandler 189 | - recovering from .catch 190 | */ 191 | -------------------------------------------------------------------------------- /quizzes/15_async-await.js: -------------------------------------------------------------------------------- 1 | // promises() 2 | // asyncAwaits() 3 | 4 | function log(...args) { 5 | console.log(...args) 6 | } 7 | 8 | function logError(...args) { 9 | console.error(...args) 10 | } 11 | 12 | // Define and use promise style asynchronous operations 13 | function promises() { 14 | // TODO: Rewrite as async 15 | const successfulPromise = timeout(100).then(result => `success: ${result}`) 16 | 17 | // TODO: Rewrite as async 18 | const failingPromise = timeout(200, true).then(null, error => 19 | Promise.reject(`failure: ${error}`), 20 | ) 21 | 22 | const recoveredPromise = timeout(300, true).then(null, error => 23 | Promise.resolve(`failed and recovered: ${error}`), 24 | ) 25 | 26 | successfulPromise.then(log, logError) 27 | failingPromise.then(log, logError) 28 | recoveredPromise.then(log, logError) 29 | } 30 | 31 | // This is the mothership of all things asynchronous 32 | function timeout(duration = 0, shouldReject = false) { 33 | return new Promise((resolve, reject) => { 34 | setTimeout(() => { 35 | if (shouldReject) { 36 | reject(`rejected after ${duration}ms`) 37 | } else { 38 | resolve(`resolved after ${duration}ms`) 39 | } 40 | }, duration) 41 | }) 42 | } 43 | 44 | // SOLUTIONS BELOW! 45 | 46 | function asyncAwaits() { 47 | async function successfulAsyncAwait() { 48 | const result = await timeout(100) 49 | return `success: ${result}` 50 | } 51 | 52 | async function failedAsyncAwait() { 53 | const result = await timeout(200, true) 54 | return `failed: ${result}` 55 | } 56 | 57 | async function recoveredAsyncAwait() { 58 | let result 59 | try { 60 | result = await timeout(300, true) 61 | return `failed: ${result}` // this would not be executed 62 | } catch (error) { 63 | return `failed and recovered: ${error}` 64 | } 65 | } 66 | successfulAsyncAwait().then(log, logError) 67 | failedAsyncAwait().then(log, logError) 68 | recoveredAsyncAwait().then(log, logError) 69 | } 70 | -------------------------------------------------------------------------------- /quizzes/16_es2016.js: -------------------------------------------------------------------------------- 1 | import log from './pretty-log' 2 | 3 | function arrayIncludes() { 4 | // refactor to `includes` 5 | const array = [1, 2, 3] 6 | return { 7 | includes2: array.indexOf(2) !== -1, 8 | includes4: array.indexOf(4) !== -1, 9 | } 10 | } 11 | // log(arrayIncludes()) 12 | 13 | function exponentiationOperator() { 14 | // refactor to the exponentiation operator 15 | return Math.pow(3, 4) 16 | } 17 | // log(exponentiationOperator()) 18 | 19 | // This is also kinda interesting... 20 | // Though not something you'll probably 21 | // run into often... 22 | // https://www.nczonline.net/blog/2016/10/the-ecmascript-2016-change-you-probably-dont-know/ 23 | 24 | // SOLUTION BELOW 25 | // No peeking! 26 | 27 | function arrayIncludesSOLUTION() { 28 | const array = [1, 2, 3] 29 | return { 30 | includes2: array.includes(2), 31 | includes4: array.includes(4), 32 | } 33 | } 34 | // log(arrayIncludesSOLUTION()) 35 | 36 | function exponentiationOperatorSOLUTION() { 37 | // refactor to the exponentiation operator 38 | return 3 ** 4 39 | } 40 | // log(exponentiationOperatorSOLUTION()) 41 | -------------------------------------------------------------------------------- /quizzes/17_es2017.js: -------------------------------------------------------------------------------- 1 | import log from './pretty-log' 2 | 3 | function padString() { 4 | const string = 'hello' 5 | // make string have padding on either side 6 | const startPadded = string.watBeginning(2) // maybe? 7 | const endPadded = startPadded.watEnding(2) // perhaps? 8 | return endPadded === ' hello ' 9 | } 10 | // log(padString()) 11 | 12 | function padStringWithPadString() { 13 | const string = 'hallo' 14 | // make the padString be 'sup ' 15 | const result = string.padStart(17) 16 | return result 17 | } 18 | // log(padStringWithPadString()) 19 | 20 | function objectValues() { 21 | // what does this return? 22 | const object = { 23 | a: 'a value', 24 | b: 'b value', 25 | c: { 26 | d: true, 27 | e: { 28 | f: 'f value', 29 | }, 30 | }, 31 | } 32 | return Object.values(object) 33 | } 34 | // log(objectValues()) 35 | 36 | function objectEntries() { 37 | // what does this return? 38 | const object = { 39 | a: 'a value', 40 | b: 'b value', 41 | c: { 42 | d: true, 43 | e: { 44 | f: 'f value', 45 | }, 46 | }, 47 | } 48 | return Object.entries(object) 49 | } 50 | // log(objectEntries()) 51 | 52 | function trailingCommas() { 53 | function totesLegit(a, b, c) { 54 | return [a, b, c] 55 | } 56 | 57 | return totesLegit('a', 'b', 'c') 58 | } 59 | // log(trailingCommas()) 60 | 61 | // SOLUTIONS BELOW! 62 | 63 | function padStringSOLUTION() { 64 | const string = 'hello' 65 | const startPadded = string.padStart(string.length + 2) 66 | const endPadded = startPadded.padEnd(startPadded.length + 2) 67 | return endPadded === ' hello ' 68 | } 69 | // log(padStringSOLUTION()) 70 | 71 | function padStringWithPadStringSOLUTION() { 72 | const string = 'hallo' 73 | // make the padString be 'sup ' 74 | const result = string.padStart(17, 'sup ') 75 | return result 76 | } 77 | // log(padStringWithPadStringSOLUTION()) 78 | -------------------------------------------------------------------------------- /quizzes/18_public-class-fields.js: -------------------------------------------------------------------------------- 1 | import log from './pretty-log' 2 | 3 | function basicExample() { 4 | // can you find the bug in this code? 5 | // how could we fix it? 6 | class Greeter { 7 | constructor(name, preference) { 8 | this.name = name 9 | this.preference = preference 10 | } 11 | greet(otherName) { 12 | return `Hello ${otherName}, my name is ${this.name}` 13 | } 14 | askAboutWeather() { 15 | const antiPreference = this.preference === 'cool' ? 'warm' : 'cool' 16 | return `Is it ${antiPreference} outside? I prefer it ${this.preference}.` 17 | } 18 | getInterestingCallbacks() { 19 | return { 20 | greet: this.greet, 21 | askAboutWeather: this.askAboutWeather, 22 | } 23 | } 24 | } 25 | const greeter = new Greeter('Ronald', 'cool') 26 | const interestingCallbacks = greeter.getInterestingCallbacks() 27 | return { 28 | greeting: interestingCallbacks.greet('Jane'), 29 | weatherQuestion: interestingCallbacks.askAboutWeather(), 30 | } 31 | } 32 | // log(basicExample()) 33 | 34 | // SOLUTIONS BELOW! 35 | 36 | function basicExampleSOLUTION() { 37 | class Greeter { 38 | constructor(name, preference) { 39 | this.name = name 40 | this.preference = preference 41 | } 42 | greet = otherName => { 43 | return `Hello ${otherName}, my name is ${this.name}` 44 | } 45 | askAboutWeather = () => { 46 | const antiPreference = this.preference === 'cool' ? 'warm' : 'cool' 47 | return `Is it ${antiPreference} outside? I prefer it ${this.preference}.` 48 | } 49 | getInterestingCallbacks() { 50 | return { 51 | greet: this.greet, 52 | askAboutWeather: this.askAboutWeather, 53 | } 54 | } 55 | } 56 | const greeter = new Greeter('Ronald', 'cool') 57 | const interestingCallbacks = greeter.getInterestingCallbacks() 58 | return { 59 | greeting: interestingCallbacks.greet('Jane'), 60 | weatherQuestion: interestingCallbacks.askAboutWeather(), 61 | } 62 | } 63 | log(basicExampleSOLUTION()) 64 | -------------------------------------------------------------------------------- /quizzes/19_symbols.js: -------------------------------------------------------------------------------- 1 | import log from './pretty-log' 2 | 3 | function createSymbol() { 4 | const mySymbol = new Symbol() 5 | return mySymbol 6 | } 7 | // log(createSymbol()) 8 | 9 | function symbolDescription() { 10 | // how would we give this symbol an identifier 11 | // for debugging purposes? 12 | const mySymbol = Symbol() 13 | return mySymbol 14 | } 15 | // log(symbolDescription()) 16 | 17 | function getSymbolsFromObject() { 18 | // what will this return? 19 | const secret = Symbol('not so secret') 20 | const object = { 21 | noSecrets: 'I have nothing to hide', 22 | [secret]: 'can you find me?', 23 | } 24 | return Object.keys(object) 25 | } 26 | // log(getSymbolsFromObject()) 27 | 28 | // More about Symbols: 29 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol 30 | 31 | // SOLUTIONS BELOW! 32 | 33 | function createSymbolSOLUTION() { 34 | const mySymbol = Symbol() 35 | return mySymbol 36 | } 37 | // log(createSymbolSOLUTION()) 38 | 39 | function symbolDescriptionSOLUTION() { 40 | // how would we give this symbol an identifier 41 | // for debugging purposes? 42 | const mySymbol = Symbol('My description') 43 | return mySymbol 44 | } 45 | // log(symbolDescriptionSOLUTION()) 46 | 47 | function getSymbolsFromObjectSOLUTION() { 48 | // what will this return? 49 | const secret = Symbol('not so secret') 50 | const object = { 51 | noSecrets: 'I have nothing to hide', 52 | [secret]: 'can you find me?', 53 | } 54 | return Object.getOwnPropertySymbols(object) 55 | } 56 | // log(getSymbolsFromObjectSOLUTION()) 57 | 58 | /* 59 | eslint 60 | no-new-symbol:0 61 | symbol-description:0 62 | */ 63 | -------------------------------------------------------------------------------- /quizzes/helpers/module-a.js: -------------------------------------------------------------------------------- 1 | // 1. how could we make `add` be the main export? 2 | function add(a, b) { 3 | return a + b 4 | } 5 | 6 | // 2. how could we make `foo` be an extra export? 7 | const foo = 'bar' 8 | 9 | // 3. how could we also export `theAnswer`? 10 | const theAnswer = 42 11 | 12 | // 4. how could we declare and export a variable 13 | // at declaration time? 14 | // const theQuestion = ['life', 'universe', 'everything'] 15 | 16 | // 5. how could we do all of these named exports 17 | // with a single declaration? 18 | 19 | // 6. how could we export `foo`, but make users 20 | // use the name `bar`? 21 | 22 | // 7. how could we give the default export an 23 | // alias? (like we did with the last one?) 24 | 25 | // 8. how could we do all of the exports (including 26 | // the default export) with a single declaration? 27 | 28 | // 9. how could we take all the exports from 29 | // './module-b' and re-export them as a part of 30 | // our own module? 31 | 32 | // 10. how could we take only _some_ of the exports 33 | // from './module-b' and re-export them as a part 34 | // of our own module? 35 | 36 | // 11. how could we take the default export from 37 | // './module-b' and re-export it with a different 38 | // name in our own module? 39 | 40 | /* 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | SOLUTIONS ARE BELOW 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | */ 59 | 60 | // But they're commented out because JavaScript 61 | 62 | // 1. 63 | // export default add 64 | 65 | // 2. 66 | // export {foo} 67 | 68 | // 3. 69 | // export {theAnswer} 70 | 71 | // 4. 72 | // export const theQuestion = ['life', 'universe', 'everything'] 73 | 74 | // 5. 75 | // export {foo, theAnswer, theQuestion} 76 | 77 | // 6. 78 | // export {foo as bar} 79 | 80 | // 7. 81 | // trick question. Not possible/necessary! 82 | 83 | // 8. 84 | // export {foo, theAnswer, theQuestion, add as default} 85 | 86 | // 9. 87 | // export * from './module-b' 88 | 89 | // 10. 90 | // export {default, e1, e3} from './module-b' 91 | 92 | // 11. 93 | // export {default as myOwnThing} from './module-b' 94 | -------------------------------------------------------------------------------- /quizzes/helpers/module-b.js: -------------------------------------------------------------------------------- 1 | export default function iAmDefault() { 2 | return 'hello world!' 3 | } 4 | export const ex1 = true 5 | export const es2 = [1, 2, 3] 6 | export const es3 = 45 7 | -------------------------------------------------------------------------------- /quizzes/pretty-log.js: -------------------------------------------------------------------------------- 1 | import prettyFormat from 'pretty-format' 2 | import chalk from 'chalk' 3 | 4 | export default log 5 | export {formatLog} 6 | 7 | function log(...objs) { 8 | if (!objs.length) { 9 | return 10 | } 11 | let thingToDir = objs 12 | if (objs.length === 1) { 13 | ;[thingToDir] = objs 14 | } 15 | if (typeof thingToDir === 'string') { 16 | formatLog(chalk.green(thingToDir)) 17 | return 18 | } 19 | console.dir(thingToDir, {depth: null, colors: true}) 20 | } 21 | 22 | function formatLog(...objs) { 23 | if (!objs.length) { 24 | return 25 | } 26 | const formattedObjs = objs.reduce((newArray, obj) => { 27 | newArray.push(prettyFormat(obj), '\n') 28 | return newArray 29 | }, []) 30 | console.log(...formattedObjs) 31 | } 32 | -------------------------------------------------------------------------------- /quizzes/run.js: -------------------------------------------------------------------------------- 1 | /* eslint no-case-declarations:0 */ 2 | import path from 'path' 3 | import chokidar from 'chokidar' 4 | import chalk from 'chalk' 5 | import {sync as globSync} from 'glob' 6 | 7 | let lastFileRun = null 8 | 9 | const cwd = __dirname 10 | const watcher = watchFiles() 11 | clearConsole() 12 | sayWatching() 13 | sayCommands() 14 | listenForInput() 15 | 16 | function watchFiles() { 17 | return chokidar 18 | .watch('*_*.js', {cwd, ignoreInitial: true}) 19 | .on('change', relativePath => { 20 | rerunFile(relativePath) 21 | }) 22 | } 23 | 24 | function listenForInput() { 25 | process.stdin.setEncoding('utf8') 26 | process.stdin.on('data', text => { 27 | /* eslint complexity:0 */ 28 | const input = text.trim() 29 | 30 | switch (input) { 31 | case 'clear': 32 | case 'c': 33 | clearConsole() 34 | sayWatching() 35 | break 36 | case 'quit': 37 | case 'q': 38 | watcher.close() 39 | process.stdin.pause() 40 | break 41 | case '': 42 | if (lastFileRun) { 43 | rerunFile(lastFileRun) 44 | } else { 45 | console.log( 46 | chalk.gray( 47 | 'No quiz file has been run yet. Enter a glob pattern or change a file first.', 48 | ), 49 | ) 50 | } 51 | break 52 | default: 53 | const [relativePathOfFirstMatchingFile] = globSync(`*${input}*`, {cwd}) 54 | rerunFile(relativePathOfFirstMatchingFile) 55 | } 56 | }) 57 | } 58 | 59 | function rerunFile(relativePath) { 60 | lastFileRun = relativePath 61 | const fullPath = path.resolve(cwd, relativePath) 62 | console.log('\n\n\n') 63 | console.log(chalk.gray(relativePath)) 64 | 65 | try { 66 | delete require.cache[fullPath] 67 | require(fullPath) // eslint-disable-line global-require 68 | } catch (e) { 69 | console.log(getRelevantStackTrace(e)) 70 | } 71 | } 72 | 73 | function clearConsole() { 74 | console.log('\x1Bc') 75 | } 76 | 77 | function sayWatching() { 78 | console.log(chalk.gray('watching for a quiz change')) 79 | } 80 | 81 | function sayCommands() { 82 | console.log( 83 | chalk.gray(`You can enter a few handy commands here: 84 | q, quit Quit the quizzes 85 | c, clear Clear the console 86 | filename Run the first file that matches your pattern 87 | Re-run the last run file\n`), 88 | ) 89 | } 90 | 91 | function getRelevantStackTrace({stack}) { 92 | const splitStack = stack.split('\n') 93 | const newStack = [splitStack[0]] // skip the first line which is the error message 94 | for (let i = 1; i < splitStack.length; i++) { 95 | const line = splitStack[i] 96 | const isCodeFrame = line.includes('|') 97 | // if it starts with a number then it's part of a code frame and we want that. 98 | if (line.includes(cwd) || isCodeFrame) { 99 | newStack.push(isCodeFrame ? line : chalk.red(line)) 100 | } else { 101 | newStack.push(' etc...') 102 | break 103 | } 104 | } 105 | return newStack.join('\n') 106 | } 107 | -------------------------------------------------------------------------------- /scripts/autofill-feedback-email.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const path = require('path') 3 | const inquirer = require('inquirer') 4 | const replace = require('replace-in-file') 5 | const isCI = require('is-ci') 6 | const spawn = require('cross-spawn') 7 | 8 | if (isCI) { 9 | console.log(`Not running autofill feedback as we're on CI`) 10 | } else { 11 | const prompt = inquirer.prompt([ 12 | { 13 | name: 'email', 14 | message: `what's your email address?`, 15 | validate(val) { 16 | if (!val) { 17 | // they don't want to do this... 18 | return true 19 | } else if (!val.includes('@')) { 20 | return 'email requires an @ sign...' 21 | } 22 | return true 23 | }, 24 | }, 25 | ]) 26 | const timeoutId = setTimeout(() => { 27 | console.log( 28 | `\n\nprompt timed out. No worries. Run \`node ./scripts/autofill-feedback-email.js\` if you'd like to try again`, 29 | ) 30 | prompt.ui.close() 31 | }, 15000) 32 | 33 | prompt.then(({email} = {}) => { 34 | clearTimeout(timeoutId) 35 | if (!email) { 36 | console.log(`Not autofilling email because none was provided`) 37 | return 38 | } 39 | const options = { 40 | files: [path.join(__dirname, '..', 'exercises/**/*.js')], 41 | from: /&em=\n/, 42 | to: `&em=${email}\n`, 43 | } 44 | 45 | replace(options).then( 46 | changedFiles => { 47 | console.log(`Updated ${changedFiles.length} with the email ${email}`) 48 | console.log( 49 | 'committing changes for you so your jest watch mode works nicely', 50 | ) 51 | spawn.sync('git', ['commit', '-am', 'email autofill', '--no-verify'], { 52 | stdio: 'inherit', 53 | }) 54 | }, 55 | error => { 56 | console.error('Failed to update files') 57 | console.error(error.stack) 58 | }, 59 | ) 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /scripts/install.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const path = require('path') 3 | const installDeps = require('./workshop-setup').installDeps 4 | 5 | installDeps([path.resolve(__dirname, '..')]).then( 6 | () => { 7 | console.log('👍 all dependencies installed') 8 | }, 9 | () => { 10 | // ignore, workshop-setup will log for us... 11 | }, 12 | ) 13 | -------------------------------------------------------------------------------- /scripts/verify.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | var verifySystem = require('./workshop-setup').verifySystem 4 | 5 | var verifyPromise = verifySystem([ 6 | verifySystem.validators.node('>=8.9.4'), 7 | verifySystem.validators.npm('>=5.6.0'), 8 | ]) 9 | 10 | verifyPromise.then( 11 | function() { 12 | // resolves if there are no errors 13 | console.log('🎉 Congrats! Your system is setup properly') 14 | console.log('You should be good to install and run things.') 15 | }, 16 | function(error) { 17 | // rejects if there are errors 18 | console.error(error) 19 | console.info( 20 | "\nIf you don't care about these warnings, go " + 21 | 'ahead and install dependencies with `node ./scripts/install`', 22 | ) 23 | process.exitCode = 1 24 | }, 25 | ) 26 | -------------------------------------------------------------------------------- /templates/01_block-scoping.test.js: -------------------------------------------------------------------------------- 1 | // COMMENT_START 2 | /* eslint no-const-assign:0 */ 3 | // COMMENT_END 4 | const noop = () => {} 5 | test('can be used in place of `var`', () => { 6 | // WORKSHOP_START 7 | // Declare bandName using 'let' 8 | // Declare isBestBand using 'let' 9 | // WORKSHOP_END 10 | // FINAL_START 11 | let bandName = 'Queen' 12 | let isBestBand = true 13 | // FINAL_END 14 | expect(bandName).toBe('Queen') 15 | expect(isBestBand).toBe(true) 16 | }) 17 | 18 | test('can modify the value of a `let` variable even in the next block statement', () => { 19 | let releaseName = 'ES6' 20 | { 21 | releaseName = 'ES2015' 22 | } 23 | // WORKSHOP_START 24 | expect(releaseName).toBe(/* ENTER YOUR GUESS HERE */) 25 | // WORKSHOP_END 26 | // FINAL_START 27 | expect(releaseName).toBe('ES2015') 28 | // FINAL_END 29 | }) 30 | 31 | test('cannot modify the value of a `const` variable', () => { 32 | function getReleaseName() { 33 | // WORKSHOP_START 34 | // Pick your side. Do you call it ES6, or ES2015? 35 | // You cannot have `const` and reassign the value! 36 | const releaseName = 'ES6' // If you call it ES2015, then change this to let or var 37 | releaseName = 'ES2015' // If you call it ES6, then remove this reassignment 38 | // WORKSHOP_END 39 | // FINAL_START 40 | const releaseName = 'ES6' 41 | // FINAL_END 42 | return releaseName 43 | } 44 | expect(getReleaseName).not.toThrow() 45 | }) 46 | 47 | test('is trapped inside of an `if` statement', () => { 48 | if (true) { 49 | // Change to `var` to `let`, so that b is scoped inside of the if-statement 50 | // WORKSHOP_START 51 | var b = 1 52 | // WORKSHOP_END 53 | // FINAL_START 54 | let b = 1 55 | // FINAL_END 56 | } 57 | expect(() => noop(b)).toThrow('b is not defined') 58 | }) 59 | 60 | test(`can't redeclare using the same name`, () => { 61 | function doLoop() { 62 | // WORKSHOP_START 63 | // Change loop counter to `let` so that it is trapped inside of the loop, and can't be returned. 64 | for (var i = 0; i < 10; i++) { 65 | /* eslint no-empty:"off" */ 66 | } 67 | // WORKSHOP_END 68 | // FINAL_START 69 | for (let i = 0; i < 10; i++) { 70 | /* eslint no-empty:"off" */ 71 | } 72 | // FINAL_END 73 | return i 74 | } 75 | 76 | expect(doLoop).toThrow('i is not defined') 77 | }) 78 | 79 | test('means that we can start using block statements', () => { 80 | // BLOCK STATEMENT 81 | { 82 | // WORKSHOP_START 83 | // Change to `const` declaration 84 | var d = 2 85 | // WORKSHOP_END 86 | // FINAL_START 87 | const d = 2 88 | // FINAL_END 89 | } 90 | 91 | expect(() => noop('d', d)).toThrow('d is not defined') 92 | }) 93 | 94 | //////// Elaboration & Feedback ///////// 95 | // WORKSHOP_START 96 | /* 97 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Block+Scoping&em= 98 | */ 99 | // WORKSHOP_END 100 | test('I submitted my elaboration and feedback', () => { 101 | // WORKSHOP_START 102 | const submitted = false // change this when you've submitted! 103 | // WORKSHOP_END 104 | // FINAL_START 105 | const submitted = true 106 | // FINAL_END 107 | expect(true).toBe(submitted) 108 | }) 109 | //////////////////////////////// 110 | 111 | //////// EXTRA CREDIT //////// 112 | 113 | test.skip('means that we can declare constant with the same name in block statement', () => { 114 | // WORKSHOP_START 115 | // Declare a 'd' using 'const', setting the value to 5 116 | // BLOCK STATEMENT 117 | { 118 | // Declare a 'd' using 'const', setting the value to 10 119 | expect(d).toBe(10) 120 | } 121 | expect(d).toBe(5) 122 | // WORKSHOP_END 123 | // FINAL_START 124 | const d = 5 125 | // BLOCK STATEMENT 126 | { 127 | const d = 10 128 | expect(d).toBe(10) 129 | } 130 | expect(d).toBe(5) 131 | // FINAL_END 132 | }) 133 | 134 | // If you get this far, try adding a few more tests, 135 | // then file a pull request to add them to the extra credit! 136 | // Learn more here: http://kcd.im/es6-workshop-contributing 137 | 138 | /* eslint no-constant-condition:0 */ 139 | -------------------------------------------------------------------------------- /templates/02_template-literals.test.js: -------------------------------------------------------------------------------- 1 | test('should support string interpolation', () => { 2 | const person = { 3 | name: 'Kent C. Dodds', 4 | friends: [ 5 | 'Brooke Dodds', 6 | 'Matt Zabriskie', 7 | 'Aaron Frost', 8 | 'Dave Geddes', 9 | 'Joe Eames', 10 | 'Ryan Florence', 11 | ], 12 | } 13 | // FINAL_START 14 | const personsFriends = `${person.name} has ${ 15 | person.friends.length 16 | } friends: ${person.friends.join(', ')}` 17 | // FINAL_END 18 | // WORKSHOP_START 19 | // construct a string using template literal string interpolation 20 | const personsFriends = `` 21 | // WORKSHOP_END 22 | expect(personsFriends).toBe( 23 | 'Kent C. Dodds has 6 friends: Brooke Dodds, Matt Zabriskie, Aaron Frost, Dave Geddes, Joe Eames, Ryan Florence', 24 | ) 25 | }) 26 | 27 | test(`should support multi-line strings`, () => { 28 | // FINAL_START 29 | const multiLine = ` 30 | How cool 31 | is this!? 32 | ` 33 | // FINAL_END 34 | // WORKSHOP_START 35 | // construct a string with multiple lines without needing escaped newline characters 36 | const multiLine = `` 37 | // WORKSHOP_END 38 | expect(multiLine).toBe('\n How cool\n is this!?\n ') 39 | }) 40 | 41 | test(`should support string escaping`, () => { 42 | // FINAL_START 43 | expect(`Hi\nthere!`).toBe('Hi\nthere!') 44 | expect(`This is \`escaped\` backticks`).toBe('This is `escaped` backticks') 45 | // FINAL_END 46 | // WORKSHOP_START 47 | // properly escape a string in a template literal for each of these 48 | expect(``).toBe('Hi\nthere!') 49 | expect(``).toBe('This is `escaped` backticks') 50 | // WORKSHOP_END 51 | }) 52 | 53 | //////// EXTRA CREDIT //////// 54 | 55 | // you likely won't often use tagging, but it can be handy! 56 | test.skip(`should call the tagging function`, () => { 57 | const noun = 'World' 58 | const emotion = 'happy' 59 | const result = tagIt`Hello ${noun}! Are you feeling ${emotion} today?` 60 | expect(result).toBe( 61 | 'Hello super-cool World! Are you feeling really happy today?', 62 | ) 63 | 64 | function tagIt(literalString, ...interpolatedParts) { 65 | // FINAL_START 66 | const firstPart = `${literalString[0]}super-cool ${interpolatedParts[0]}` 67 | const lastPart = `${literalString[1]}really ${interpolatedParts[1]}${ 68 | literalString[2] 69 | }` 70 | return `${firstPart}${lastPart}` 71 | // FINAL_END 72 | // COMMENT_START eslint trickery 73 | /* eslint no-unreachable:0 */ 74 | // COMMENT_END 75 | // WORKSHOP_START 76 | // implement this function to make the test pass 77 | return 'fixme' 78 | // WORKSHOP_END 79 | } 80 | }) 81 | 82 | //////// Elaboration & Feedback ///////// 83 | // WORKSHOP_START 84 | /* 85 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Template+Literals&em= 86 | */ 87 | // WORKSHOP_END 88 | test('I submitted my elaboration and feedback', () => { 89 | // WORKSHOP_START 90 | const submitted = false // change this when you've submitted! 91 | // WORKSHOP_END 92 | // FINAL_START 93 | const submitted = true 94 | // FINAL_END 95 | expect(true).toBe(submitted) 96 | }) 97 | //////////////////////////////// 98 | 99 | //////// EXTRA CREDIT //////// 100 | 101 | // If you get this far, try adding a few more tests, 102 | // then file a pull request to add them to the extra credit! 103 | // Learn more here: http://kcd.im/es6-workshop-contributing 104 | -------------------------------------------------------------------------------- /templates/03_new-apis.test.js: -------------------------------------------------------------------------------- 1 | test(`should be easier to determine whether a string includes another`, () => { 2 | const sentence = 'It was the best of times. It was the worst of times' 3 | // FINAL_START 4 | const result = sentence.includes('best of times') 5 | // FINAL_END 6 | // WORKSHOP_START 7 | // create a variable called `result` that is assigned to a call of sentence.includes 8 | // WORKSHOP_END 9 | expect(result).toBe(true) 10 | }) 11 | 12 | test(`should be easier to repeat a string`, () => { 13 | const repeated = 'abc123' 14 | // FINAL_START 15 | const result = repeated.repeat(4) 16 | // FINAL_END 17 | // WORKSHOP_START 18 | // create a variable called `result` that is the result of repeating the string 4 times 19 | // WORKSHOP_END 20 | expect(result).toBe('abc123abc123abc123abc123') 21 | }) 22 | 23 | test(`should be able to take an array-like object and convert it into an array`, () => { 24 | const obj = {length: 3, 0: 'a', 1: 'b', 2: 'c'} 25 | // this is even more handy with a NodeList like that returned from document.querySelector 26 | // FINAL_START 27 | const result = Array.from(obj) 28 | // FINAL_END 29 | // WORKSHOP_START 30 | // create a variable called `result` and assign it to a call to Array.from 31 | // WORKSHOP_END 32 | expect(result).toEqual(['a', 'b', 'c']) 33 | }) 34 | 35 | test(`should be easier to fill an array with values`, () => { 36 | const originalArray = new Array(5) 37 | // FINAL_START 38 | const result = originalArray.fill(3, 1) 39 | // FINAL_END 40 | // WORKSHOP_START 41 | // create a variable called `result` and assign it to an array that's filled with 3s except for the first item. 42 | // WORKSHOP_END 43 | expect(result).toEqual([, 3, 3, 3, 3]) // eslint-disable-line no-sparse-arrays 44 | }) 45 | 46 | test(`should be easy to copy properties from one object to another`, () => { 47 | const source1 = { 48 | a: { 49 | b: 'c', 50 | m: [1, 2, 3], 51 | }, 52 | } 53 | const source2 = { 54 | d: false, 55 | z: 34, 56 | } 57 | const source3 = { 58 | z: 42, 59 | p: ['a', 'b', 'c'], 60 | } 61 | 62 | const target = { 63 | a: { 64 | q: 'r', 65 | m: [4, 5, 6], 66 | s: { 67 | t: 3, 68 | }, 69 | }, 70 | d: true, 71 | p: ['x', 'y', 'z'], 72 | } 73 | // FINAL_START 74 | const result = Object.assign(target, source1, source2, source3) 75 | // FINAL_END 76 | // WORKSHOP_START 77 | // merge the sources into the target using Object.assign 78 | // WORKSHOP_END 79 | 80 | expect(result).toEqual({ 81 | a: { 82 | b: 'c', 83 | m: [1, 2, 3], 84 | }, 85 | d: false, 86 | z: 42, 87 | p: ['a', 'b', 'c'], 88 | }) 89 | 90 | // this is only here to indicate that the assignment is not deep 91 | expect(result).not.toEqual({ 92 | a: { 93 | b: 'c', 94 | m: [1, 2, 3], 95 | q: 'r', 96 | s: { 97 | t: 3, 98 | }, 99 | }, 100 | d: false, 101 | z: 42, 102 | p: ['a', 'b', 'c'], 103 | }) 104 | }) 105 | 106 | //////// Elaboration & Feedback ///////// 107 | // WORKSHOP_START 108 | /* 109 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=New+APIS&em= 110 | */ 111 | // WORKSHOP_END 112 | test('I submitted my elaboration and feedback', () => { 113 | // WORKSHOP_START 114 | const submitted = false // change this when you've submitted! 115 | // WORKSHOP_END 116 | // FINAL_START 117 | const submitted = true 118 | // FINAL_END 119 | expect(true).toBe(submitted) 120 | }) 121 | //////////////////////////////// 122 | 123 | //////// EXTRA CREDIT //////// 124 | 125 | // If you get this far, try adding a few more tests, 126 | // then file a pull request to add them to the extra credit! 127 | // Learn more here: http://kcd.im/es6-workshop-contributing 128 | -------------------------------------------------------------------------------- /templates/04_destructure.test.js: -------------------------------------------------------------------------------- 1 | const noop = () => {} 2 | 3 | function getAddress() { 4 | return { 5 | city: 'Salt Lake City', 6 | state: 'UT', 7 | zip: 84115, 8 | coords: { 9 | lat: 40.776608, 10 | long: -111.920485, 11 | }, 12 | } 13 | } 14 | 15 | function getNumbers() { 16 | return [1, 2, 3, 4, 5, 6] 17 | } 18 | 19 | function getNestedNumbers() { 20 | return [1, 2, [3, 4, [5, 6]]] 21 | } 22 | 23 | test('can be used to pull apart objects', () => { 24 | // FINAL_START 25 | const {city, state, zip} = getAddress() 26 | // FINAL_END 27 | // WORKSHOP_START 28 | // Using destructuring, call `getAddress()` and create a 'city', 'state' and 'zip' variable. 29 | // const address = getAddress(); 30 | // const city = address.city; 31 | // const state = address.state; 32 | // const zip = address.zip; 33 | // WORKSHOP_END 34 | expect(city).toBe('Salt Lake City') 35 | expect(state).toBe('UT') 36 | expect(zip).toBe(84115) 37 | }) 38 | 39 | test('sets missing values to undefined', () => { 40 | // FINAL_START 41 | const {address} = getAddress() 42 | // FINAL_END 43 | // WORKSHOP_START 44 | // Using destructuring, call `getAddress()` and create an 'address' variable. 45 | // WORKSHOP_END 46 | expect(address).toBeUndefined() 47 | }) 48 | 49 | test('can alias destructured variables', () => { 50 | // FINAL_START 51 | const {city: c, state: s, zip: z} = getAddress() 52 | // FINAL_END 53 | // WORKSHOP_START 54 | // Using destructuring, call `getAddress()` and pull the city, state and zip out, and alias them to c, s, z, respectively 55 | // WORKSHOP_END 56 | expect(c).toBe('Salt Lake City') 57 | expect(s).toBe('UT') 58 | expect(z).toBe(84115) 59 | expect(() => noop(city)).toThrow() 60 | expect(() => noop(state)).toThrow() 61 | expect(() => noop(zip)).toThrow() 62 | }) 63 | 64 | test('can destructure nested variables', () => { 65 | // FINAL_START 66 | const { 67 | coords: {lat, long}, 68 | } = getAddress() 69 | // FINAL_END 70 | // WORKSHOP_START 71 | // Using destructuring, call `getAddress()` and create `lat` and `long` variables. 72 | // WORKSHOP_END 73 | expect(lat).toBe(40.776608) 74 | expect(long).toBe(-111.920485) 75 | expect(() => noop(coords)).toThrow() 76 | }) 77 | 78 | test('can be used to pull apart arrays', () => { 79 | // FINAL_START 80 | const [one, two] = getNumbers() 81 | // FINAL_END 82 | // WORKSHOP_START 83 | // Call getNumbers and pull the first value out as `one` and the second as `two` 84 | // WORKSHOP_END 85 | expect(one).toBe(1) 86 | expect(two).toBe(2) 87 | }) 88 | 89 | test('can skip indexes in arrays', () => { 90 | // FINAL_START 91 | const [one, , three] = getNumbers() 92 | // FINAL_END 93 | // WORKSHOP_START 94 | // Call getNumbers and pull the first value out as `one` and the third as `three` 95 | // WORKSHOP_END 96 | expect(one).toBe(1) 97 | expect(three).toBe(3) 98 | expect(() => noop(two)).toThrow() 99 | }) 100 | 101 | test('can reach nested arrays', () => { 102 | // FINAL_START 103 | const [one, , [three, , [, six]]] = getNestedNumbers() 104 | // FINAL_END 105 | // WORKSHOP_START 106 | // Call getNestedNumbers and pull the first value out as `one`, the 3 as `three` and 6 as `six`. 107 | // WORKSHOP_END 108 | expect(one).toBe(1) 109 | expect(three).toBe(3) 110 | expect(six).toBe(6) 111 | }) 112 | 113 | // MORE AT http://www.2ality.com/2015/01/es6-destructuring.html 114 | 115 | //////// Elaboration & Feedback ///////// 116 | // WORKSHOP_START 117 | /* 118 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Destructuring&em= 119 | */ 120 | // WORKSHOP_END 121 | test('I submitted my elaboration and feedback', () => { 122 | // WORKSHOP_START 123 | const submitted = false // change this when you've submitted! 124 | // WORKSHOP_END 125 | // FINAL_START 126 | const submitted = true 127 | // FINAL_END 128 | expect(true).toBe(submitted) 129 | }) 130 | //////////////////////////////// 131 | 132 | //////// EXTRA CREDIT //////// 133 | 134 | // If you get this far, try adding a few more tests, 135 | // then file a pull request to add them to the extra credit! 136 | // Learn more here: http://kcd.im/es6-workshop-contributing 137 | -------------------------------------------------------------------------------- /templates/05_modules.test.js: -------------------------------------------------------------------------------- 1 | import * as Mathy from '../common/Mathy' 2 | import * as IndexImport from '../common' 3 | // FINAL_START 4 | import _ from 'lodash' 5 | import {sqrt as mySqrt, square as mySquare} from '../common/Mathy' 6 | // FINAL_END 7 | // WORKSHOP_START 8 | // WRITE YOUR IMPORT STATEMENTS HERE 9 | // WORKSHOP_END 10 | 11 | test('can import Mathy', () => { 12 | // this one's already done! You're welcome :) 13 | expect(Mathy.sqrt).toBeDefined() 14 | expect(Mathy.square).toBeDefined() 15 | expect(Mathy.diag).toBeDefined() 16 | }) 17 | 18 | test('can specify what to import, to only retain pieces of the import', () => { 19 | // WORKSHOP_START 20 | // Import `Mathy` again, but pull out only the `sqrt` as mySqrt, and `square` as mySquare 21 | // WORKSHOP_END 22 | expect(mySqrt).toBeDefined() 23 | expect(mySquare).toBeDefined() 24 | expect(mySqrt).toBe(Mathy.sqrt) 25 | expect(mySquare).toBe(Mathy.square) 26 | }) 27 | 28 | test('can import from my node_modules', () => { 29 | // WORKSHOP_START 30 | // import `lodash` 31 | // WORKSHOP_END 32 | expect(_).toBeDefined() 33 | }) 34 | 35 | //////// Elaboration & Feedback ///////// 36 | // WORKSHOP_START 37 | /* 38 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Modules&em= 39 | */ 40 | // WORKSHOP_END 41 | test('I submitted my elaboration and feedback', () => { 42 | // WORKSHOP_START 43 | const submitted = false // change this when you've submitted! 44 | // WORKSHOP_END 45 | // FINAL_START 46 | const submitted = true 47 | // FINAL_END 48 | expect(true).toBe(submitted) 49 | }) 50 | //////////////////////////////// 51 | 52 | //////// EXTRA CREDIT //////// 53 | test.skip('Index import', () => { 54 | //I have noticed that using index.js is pretty common pattern 55 | //If someone has been confused about that maybe this helps 56 | // WORKSHOP_START 57 | expect(IndexImport.variable1).toBe(/* ENTER YOUR GUESS HERE */) 58 | expect(IndexImport.variable2).toBe(/* ENTER YOUR GUESS HERE */) 59 | expect(IndexImport.variable3).toBe(/* ENTER YOUR GUESS HERE */) 60 | expect(IndexImport.variable4).toBe(/* ENTER YOUR GUESS HERE */) 61 | // WORKSHOP_END 62 | // FINAL_START 63 | expect(IndexImport.variable1).toBe('Bob') 64 | expect(IndexImport.variable2).toBe('Kent') 65 | expect(IndexImport.variable3).toBe(222) 66 | expect(IndexImport.variable4).toBe(false) 67 | // FINAL_END 68 | }) 69 | // If you get this far, try adding a few more tests, 70 | // then file a pull request to add them to the extra credit! 71 | // Learn more here: http://kcd.im/es6-workshop-contributing 72 | -------------------------------------------------------------------------------- /templates/06_object-literal.test.js: -------------------------------------------------------------------------------- 1 | test('can use shorthand for property names', () => { 2 | function createMonster(name, power) { 3 | // FINAL_START 4 | return { 5 | type: 'Monster', 6 | name, 7 | power, 8 | attack(target) { 9 | return `${this.name} attacked ${target.name}` 10 | }, 11 | } 12 | // FINAL_END 13 | // WORKSHOP_START 14 | // Using NEW Object Literal Syntax, return a literal that will allow the tests to pass 15 | // return { 16 | // type: 'Monster', 17 | // name: name, 18 | // power: power, 19 | // attack: function (target){ 20 | // return `${this.name} attacked ${target.name}`; 21 | // } 22 | // } 23 | // WORKSHOP_END 24 | } 25 | 26 | const godzilla = createMonster('Godzilla', 1000) 27 | const mechaGodzilla = createMonster('MechaGodzilla', 5000) 28 | expect(godzilla.name).toBe('Godzilla') 29 | expect(godzilla.power).toBe(1000) 30 | expect(godzilla.attack(mechaGodzilla)).toBe('Godzilla attacked MechaGodzilla') 31 | }) 32 | 33 | test('can use expressions as property names', () => { 34 | function createCandy(type, description) { 35 | return { 36 | tasty: true, 37 | type, 38 | // WORKSHOP_START 39 | // add a expression as property name where the property name is the given type.toUpperCase() + type.length 40 | // sound contrived? It is... 😅 41 | // WORKSHOP_END 42 | // FINAL_START 43 | [type.toUpperCase() + type.length]: description, 44 | // FINAL_END 45 | } 46 | } 47 | 48 | const twixDescription = 49 | 'Twix is a chocolate bar made by Mars, Inc., consisting of biscuit applied with other ' + 50 | 'confectionery toppings and coatings. Twix bars are packaged in pairs, although smaller single bars are available.' 51 | const twixType = 'twix' 52 | const snickers = createCandy('twix', twixDescription) 53 | expect(snickers.tasty).toBe(true) 54 | expect(snickers.type).toBe(twixType) 55 | expect(snickers.TWIX4).toBe(twixDescription) 56 | }) 57 | 58 | //////// Elaboration & Feedback ///////// 59 | // WORKSHOP_START 60 | /* 61 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Object+Literals&em= 62 | */ 63 | // WORKSHOP_END 64 | test('I submitted my elaboration and feedback', () => { 65 | // WORKSHOP_START 66 | const submitted = false // change this when you've submitted! 67 | // WORKSHOP_END 68 | // FINAL_START 69 | const submitted = true 70 | // FINAL_END 71 | expect(true).toBe(submitted) 72 | }) 73 | //////////////////////////////// 74 | 75 | //////// EXTRA CREDIT //////// 76 | 77 | // If you get this far, try adding a few more tests, 78 | // then file a pull request to add them to the extra credit! 79 | // Learn more here: http://kcd.im/es6-workshop-contributing 80 | -------------------------------------------------------------------------------- /templates/08_spread.test.js: -------------------------------------------------------------------------------- 1 | test(`should be able to call a function and spread the arguments`, () => { 2 | const args = ['a', 'b', 'c'] 3 | let calls = 0 4 | // FINAL_START 5 | myFunction(...args) 6 | // FINAL_END 7 | // WORKSHOP_START 8 | // call myFunction using the spread operator with args 9 | // WORKSHOP_END 10 | expect(calls).toBe(1) 11 | 12 | function myFunction(a, b, c) { 13 | expect(a).toBe('a') 14 | expect(b).toBe('b') 15 | expect(c).toBe('c') 16 | calls++ 17 | } 18 | }) 19 | 20 | test(`should be easier to concatenate arrays`, () => { 21 | const array1 = [1, 2, 3] 22 | // FINAL_START 23 | const result = [...array1, 4, 5, 6] 24 | // FINAL_END 25 | // WORKSHOP_START 26 | // create a result array that uses the spread operator to concatenate array1 with [4, 5, 6] 27 | // WORKSHOP_END 28 | expect(result).toEqual([1, 2, 3, 4, 5, 6]) 29 | }) 30 | 31 | test(`should be able to merge properties from objects`, () => { 32 | const obj1 = { 33 | foo: 'bar', 34 | baz: 'foobar', 35 | } 36 | // FINAL_START 37 | const result = { 38 | ...obj1, 39 | eggs: 'spam', 40 | } 41 | // FINAL_END 42 | // WORKSHOP_START 43 | // create a result object that uses the spread operator to add `eggs: 'spam'` to what exists in obj1 44 | // WORKSHOP_END 45 | expect(result).toEqual({ 46 | foo: 'bar', 47 | baz: 'foobar', 48 | eggs: 'spam', 49 | }) 50 | }) 51 | 52 | //////// Elaboration & Feedback ///////// 53 | // WORKSHOP_START 54 | /* 55 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Spread&em= 56 | */ 57 | // WORKSHOP_END 58 | test('I submitted my elaboration and feedback', () => { 59 | // WORKSHOP_START 60 | const submitted = false // change this when you've submitted! 61 | // WORKSHOP_END 62 | // FINAL_START 63 | const submitted = true 64 | // FINAL_END 65 | expect(true).toBe(submitted) 66 | }) 67 | //////////////////////////////// 68 | 69 | //////// EXTRA CREDIT //////// 70 | 71 | // If you get this far, try adding a few more tests, 72 | // then file a pull request to add them to the extra credit! 73 | // Learn more here: http://kcd.im/es6-workshop-contributing 74 | -------------------------------------------------------------------------------- /templates/09_arrow.test.js: -------------------------------------------------------------------------------- 1 | test('can replace traditional functions', () => { 2 | let fnMultiply, arrowMultiply 3 | 4 | // FINAL_START 5 | fnMultiply = function(a, b) { 6 | return a * b 7 | } 8 | 9 | arrowMultiply = (a, b) => a * b 10 | // FINAL_END 11 | // WORKSHOP_START 12 | // Write two functions that take two params and return their product 13 | // For 'fnMultiply', set it equal to a regular function 14 | // For 'arrowMultiply', set it equal to an arrow function 15 | // WORKSHOP_END 16 | 17 | expect(fnMultiply(5, 5)).toBe(arrowMultiply(5, 5)) 18 | }) 19 | 20 | test('can replace traditional functions #2', () => { 21 | const nums = [2, 5, 10] 22 | // FINAL_START 23 | const squares = nums.map(num => num * num) 24 | // FINAL_END 25 | // WORKSHOP_START 26 | // Replace the 'function' in this 'map' call with an arrow function. 27 | // Hint: you shouldn't have any braces or 'return' after you are done 28 | const squares = nums.map(function(num) { 29 | return num * num 30 | }) 31 | // WORKSHOP_END 32 | 33 | expect(squares.shift()).toBe(4) 34 | expect(squares.shift()).toBe(25) 35 | expect(squares.shift()).toBe(100) 36 | }) 37 | 38 | test('binds `this` to the eval scope, not the runtime scope', () => { 39 | // FINAL_START 40 | const person = { 41 | name: 'Aaron', 42 | greetFriends: function(friends) { 43 | return friends.map(friend => this.name + ' greets to ' + friend) 44 | }, 45 | } 46 | // FINAL_END 47 | // WORKSHOP_START 48 | // Change the person object. One of the functions should become an arrow to 49 | // allow for 'this' to retain context correctly 50 | const person = { 51 | name: 'Aaron', 52 | greetFriends: function(friends) { 53 | return friends.map(function(friend) { 54 | return this.name + ' greets to ' + friend 55 | }) 56 | }, 57 | } 58 | // WORKSHOP_END 59 | 60 | const friendsArray = ['Naomi', 'Jojo', 'Ryan', 'Owen'] 61 | expect(() => person.greetFriends(friendsArray)).not.toThrow() 62 | }) 63 | 64 | test('can make array filter chains more manageable', () => { 65 | const data = [ 66 | {type: 'Widget', name: 'Sprocket', price: 10.0, qty: 3}, 67 | {type: 'Widget', name: 'Bracket', price: 1.0, qty: 5}, 68 | {type: 'Widget', name: 'Brace', price: 2.5, qty: 1}, 69 | {type: 'Widget', name: 'Sprocket', price: 4.0, qty: 2}, 70 | {type: 'Food', name: 'Gouda', price: 8.75, qty: 4}, 71 | {type: 'Food', name: 'Bacon', price: 3.5, qty: 3}, 72 | {type: 'CD', name: 'Queen Best Hits', price: 5.5, qty: 5}, 73 | {type: 'CD', name: 'Brittney Best Hits', price: 6.25, qty: 3}, 74 | {type: 'CD', name: 'JT Best Hits', price: 2.25, qty: 6}, 75 | ] 76 | 77 | // FINAL_START 78 | const shoppingList = data 79 | .filter(d => d.type != 'Widget') // Remove Widgets 80 | .filter(d => d.price < 5) // Find only remaining items with price < 5 81 | .sort((a, b) => a.qty - b.qty) // Sort by quantity, desc 82 | .map(d => d.name) // Pull just the name from each item 83 | // FINAL_END 84 | // WORKSHOP_START 85 | // REPLACE ALL REGULAR FUNCTIONS WITH ARROW FUNCTIONS 86 | const shoppingList = data 87 | .filter(function(d) { 88 | return d.type != 'Widget' 89 | }) // Remove Widgets 90 | .filter(function(d) { 91 | return d.price < 5 92 | }) // Find only remaining items with price < 5 93 | .sort(function(a, b) { 94 | return a.qty - b.qty 95 | }) // Sort by quantity, desc 96 | .map(function(d) { 97 | return d.name 98 | }) // Pull just the name from each item 99 | // WORKSHOP_END 100 | 101 | expect(shoppingList.shift()).toBe('Bacon') 102 | expect(shoppingList.shift()).toBe('JT Best Hits') 103 | }) 104 | 105 | //////// Elaboration & Feedback ///////// 106 | // WORKSHOP_START 107 | /* 108 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Arrow+Functions&em= 109 | */ 110 | // WORKSHOP_END 111 | test('I submitted my elaboration and feedback', () => { 112 | // WORKSHOP_START 113 | const submitted = false // change this when you've submitted! 114 | // WORKSHOP_END 115 | // FINAL_START 116 | const submitted = true 117 | // FINAL_END 118 | expect(true).toBe(submitted) 119 | }) 120 | //////////////////////////////// 121 | 122 | //////// EXTRA CREDIT //////// 123 | 124 | // If you get this far, try adding a few more tests, 125 | // then file a pull request to add them to the extra credit! 126 | // Learn more here: http://kcd.im/es6-workshop-contributing 127 | -------------------------------------------------------------------------------- /templates/10_class.test.js: -------------------------------------------------------------------------------- 1 | test('has a constructor for initialization', () => { 2 | // FINAL_START 3 | class Animal { 4 | constructor(name) { 5 | this.name = name 6 | } 7 | } 8 | // FINAL_END 9 | // WORKSHOP_START 10 | // Create an Animal class 11 | // Add a constructor that takes one param, the name. 12 | // Set this.name to the name passed in 13 | // WORKSHOP_END 14 | 15 | const animal = new Animal() 16 | const dog = new Animal('Dog') 17 | 18 | expect(animal.name).toBeUndefined() 19 | expect(dog.name).toBe('Dog') 20 | }) 21 | 22 | test('constructor can have default param values', () => { 23 | // FINAL_START 24 | class Animal { 25 | constructor(name = 'Honey Badger') { 26 | this.name = name 27 | } 28 | } 29 | // FINAL_END 30 | // WORKSHOP_START 31 | // Create an Animal class with a constructor 32 | // Make your class default (using default params) the name to 'Honey Badger' 33 | // WORKSHOP_END 34 | 35 | const animal = new Animal() 36 | const dog = new Animal('Dog') 37 | 38 | expect(animal.name).toBe('Honey Badger') 39 | expect(dog.name).toBe('Dog') 40 | }) 41 | 42 | test('can have instance methods', () => { 43 | // FINAL_START 44 | class Animal { 45 | constructor(name = 'Honey Badger') { 46 | this.name = name 47 | } 48 | 49 | sayName() { 50 | return `My name is: ${this.name}` 51 | } 52 | } 53 | // FINAL_END 54 | // WORKSHOP_START 55 | // Create an Animal class, pass in the name to the constructor, and add a sayName function to the class definition 56 | // WORKSHOP_END 57 | 58 | const animal = new Animal() 59 | 60 | expect(animal.sayName).toBeDefined() 61 | expect(Animal.sayName).toBeUndefined() 62 | expect(animal.sayName()).toBe('My name is: Honey Badger') 63 | }) 64 | 65 | test('can have static methods', () => { 66 | // FINAL_START 67 | class Animal { 68 | constructor(name = 'Honey Badger') { 69 | this.name = name 70 | } 71 | 72 | sayName() { 73 | return `My name is: ${this.name}` 74 | } 75 | 76 | static create(name) { 77 | return new Animal(name) 78 | } 79 | } 80 | // FINAL_END 81 | // WORKSHOP_START 82 | // Create an Animal class, pass in the name to the constructor, 83 | // and add a create method that takes a name and returns an instance 84 | // WORKSHOP_END 85 | 86 | const animal = new Animal() 87 | 88 | expect(animal.create).toBeUndefined() 89 | expect(Animal.create).toBeDefined() 90 | }) 91 | 92 | test('can extend another class', () => { 93 | // FINAL_START 94 | class Animal { 95 | constructor(name = 'Honey Badger') { 96 | this.name = name 97 | } 98 | } 99 | 100 | class Dog extends Animal { 101 | sayName() { 102 | return `My name is: ${this.name}` 103 | } 104 | } 105 | // FINAL_END 106 | // WORKSHOP_START 107 | // Create an Animal class 108 | // Create a Dog class that extends Animal 109 | // Add sayName to Dog 110 | // WORKSHOP_END 111 | 112 | const dog = new Dog('Fido') 113 | 114 | expect(dog instanceof Dog).toBe(true) 115 | expect(dog instanceof Animal).toBe(true) 116 | expect(Animal.prototype.sayName).toBeUndefined() 117 | expect(Dog.prototype.sayName).toBeDefined() 118 | }) 119 | 120 | test('can use property setters and getters', () => { 121 | // FINAL_START 122 | class Animal { 123 | set name(name) { 124 | this._name = name 125 | } 126 | get name() { 127 | return `${this._name} type of animal` 128 | } 129 | } 130 | // FINAL_END 131 | // WORKSHOP_START 132 | // Create an Animal class (don't pass name into constructor) 133 | // Add property setter for name 134 | // Add property getter for name 135 | // WORKSHOP_END 136 | 137 | const animal = new Animal() 138 | animal.name = 'Dog' 139 | expect(animal.name).toBe('Dog type of animal') 140 | animal.name = 'Cat' 141 | expect(animal.name).toBe('Cat type of animal') 142 | }) 143 | 144 | //////// Elaboration & Feedback ///////// 145 | // WORKSHOP_START 146 | /* 147 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Classes&em= 148 | */ 149 | // WORKSHOP_END 150 | test('I submitted my elaboration and feedback', () => { 151 | // WORKSHOP_START 152 | const submitted = false // change this when you've submitted! 153 | // WORKSHOP_END 154 | // FINAL_START 155 | const submitted = true 156 | // FINAL_END 157 | expect(true).toBe(submitted) 158 | }) 159 | //////////////////////////////// 160 | 161 | //////// EXTRA CREDIT //////// 162 | 163 | // If you get this far, try adding a few more tests, 164 | // then file a pull request to add them to the extra credit! 165 | // Learn more here: http://kcd.im/es6-workshop-contributing 166 | -------------------------------------------------------------------------------- /templates/11_set.test.js: -------------------------------------------------------------------------------- 1 | test('has an add method and a has method', () => { 2 | // FINAL_START 3 | const mySet = new Set() 4 | mySet.add(1) 5 | mySet.add(2) 6 | mySet.add(3) 7 | // FINAL_END 8 | // WORKSHOP_START 9 | // Create a new Set called 'mySet' 10 | // add the numbers 1, 2, and 3 to the set 11 | // WORKSHOP_END 12 | 13 | expect(mySet.has(1)).toBe(true) 14 | expect(mySet.has(2)).toBe(true) 15 | expect(mySet.has(3)).toBe(true) 16 | expect(mySet.has(4)).toBe(false) 17 | }) 18 | 19 | test('doesn`t allow duplicates', () => { 20 | // FINAL_START 21 | const mySet = new Set() 22 | mySet.add(1) 23 | mySet.add(1) 24 | mySet.add(1) 25 | // FINAL_END 26 | // WORKSHOP_START 27 | // Create a new Set 28 | // add the following numbers to it, using set.add(num): 1, 1, 1 29 | // WORKSHOP_END 30 | 31 | expect(mySet.has(1)).toBe(true) 32 | expect(mySet.has(2)).toBe(false) 33 | expect(mySet.has(3)).toBe(false) 34 | expect(mySet.has(4)).toBe(false) 35 | }) 36 | 37 | //////// Elaboration & Feedback ///////// 38 | // WORKSHOP_START 39 | /* 40 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Sets&em= 41 | */ 42 | // WORKSHOP_END 43 | test('I submitted my elaboration and feedback', () => { 44 | // WORKSHOP_START 45 | const submitted = false // change this when you've submitted! 46 | // WORKSHOP_END 47 | // FINAL_START 48 | const submitted = true 49 | // FINAL_END 50 | expect(true).toBe(submitted) 51 | }) 52 | //////////////////////////////// 53 | 54 | //////// EXTRA CREDIT //////// 55 | 56 | // If you get this far, try adding a few more tests, 57 | // then file a pull request to add them to the extra credit! 58 | // Learn more here: http://kcd.im/es6-workshop-contributing 59 | -------------------------------------------------------------------------------- /templates/12_maps.test.js: -------------------------------------------------------------------------------- 1 | test('has a set method', () => { 2 | // FINAL_START 3 | const myMap = new Map() 4 | myMap.set('name', 'Aaron') 5 | // FINAL_END 6 | // WORKSHOP_START 7 | // Create a new map called 'myMap' 8 | // add a new entry. Use 'name' as the key and 'Aaron' as the value 9 | // WORKSHOP_END 10 | 11 | expect(myMap.get('name')).toBe('Aaron') 12 | }) 13 | 14 | test('can use objects as a key', () => { 15 | const user = {name: 'Aaron'} 16 | const value = {twitter: '@js_dev', gplus: '+AaronFrost'} 17 | 18 | // FINAL_START 19 | const myMap = new Map() 20 | myMap.set(user, value) 21 | // FINAL_END 22 | // WORKSHOP_START 23 | // Create a map called 'myMap' 24 | // add a new entry. Use user as the key, and value as the value 25 | // WORKSHOP_END 26 | 27 | expect(myMap.has(user)).toBe(true) 28 | expect(myMap.get(user)).toBe(value) 29 | }) 30 | 31 | test(`doesn't coerce keys`, () => { 32 | // FINAL_START 33 | const myMap = new Map() 34 | myMap.set(1, 'Aaron') 35 | expect(myMap.get('1')).toBe(undefined) 36 | myMap.set('1', 'Aaron') 37 | expect(myMap.get('1')).toBe('Aaron') 38 | // FINAL_END 39 | // WORKSHOP_START 40 | const myMap = new Map() 41 | myMap.set(1, 'Aaron') 42 | expect(myMap.get('1')).toBe(/*ENTER YOUR GUESS HERE*/) 43 | myMap.set('1', 'Aaron') 44 | expect(myMap.get('1')).toBe(/*ENTER YOUR GUESS HERE*/) 45 | // WORKSHOP_END 46 | }) 47 | 48 | //////// Elaboration & Feedback ///////// 49 | // WORKSHOP_START 50 | /* 51 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Maps&em= 52 | */ 53 | // WORKSHOP_END 54 | test('I submitted my elaboration and feedback', () => { 55 | // WORKSHOP_START 56 | const submitted = false // change this when you've submitted! 57 | // WORKSHOP_END 58 | // FINAL_START 59 | const submitted = true 60 | // FINAL_END 61 | expect(true).toBe(submitted) 62 | }) 63 | //////////////////////////////// 64 | 65 | //////// EXTRA CREDIT //////// 66 | 67 | // If you get this far, try adding a few more tests, 68 | // then file a pull request to add them to the extra credit! 69 | // Learn more here: http://kcd.im/es6-workshop-contributing 70 | -------------------------------------------------------------------------------- /templates/13_weakmap.test.js: -------------------------------------------------------------------------------- 1 | test('has a set method', () => { 2 | const key = {name: 'Aaron'} 3 | const value = {twitter: '@js_dev', gplus: '+AaronFrost'} 4 | // FINAL_START 5 | const myMap = new WeakMap() 6 | myMap.set(key, value) 7 | // FINAL_END 8 | // WORKSHOP_START 9 | // Create a new WeakMap called 'myMap' 10 | // Add a new entry. Use key as the key and values as the value 11 | // WORKSHOP_END 12 | expect(myMap.has(key)).toBe(true) 13 | }) 14 | 15 | test(`should enable private members in classes`, () => { 16 | // COMMENT_START eslint trickery 17 | /* eslint no-unreachable:0 */ 18 | // COMMENT_END 19 | // FINAL_START 20 | const privateData = new WeakMap() 21 | // FINAL_END 22 | // WORKSHOP_START 23 | // If you make it this far, write a class with private member variables, using WeakMaps 24 | // WORKSHOP_END 25 | class Person { 26 | constructor(name, age) { 27 | // FINAL_START 28 | privateData.set(this, {name, age}) 29 | // FINAL_END 30 | // WORKSHOP_START 31 | this._name = name 32 | this._age = age 33 | // WORKSHOP_END 34 | } 35 | 36 | getName() { 37 | // FINAL_START 38 | return privateData.get(this).name 39 | // FINAL_END 40 | // WORKSHOP_START 41 | return this._name 42 | // WORKSHOP_END 43 | } 44 | 45 | getAge() { 46 | // FINAL_START 47 | return privateData.get(this).age 48 | // FINAL_END 49 | // WORKSHOP_START 50 | return this._age 51 | // WORKSHOP_END 52 | } 53 | } 54 | 55 | const person = new Person('Kent C. Dodds', 26) 56 | expect(person._name).toBeUndefined() 57 | expect(person.getName()).toBe('Kent C. Dodds') 58 | expect(person._age).toBeUndefined() 59 | expect(person.getAge()).toBe(26) 60 | }) 61 | 62 | //////// Elaboration & Feedback ///////// 63 | // WORKSHOP_START 64 | /* 65 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=WeakMaps&em= 66 | */ 67 | // WORKSHOP_END 68 | test('I submitted my elaboration and feedback', () => { 69 | // WORKSHOP_START 70 | const submitted = false // change this when you've submitted! 71 | // WORKSHOP_END 72 | // FINAL_START 73 | const submitted = true 74 | // FINAL_END 75 | expect(true).toBe(submitted) 76 | }) 77 | //////////////////////////////// 78 | 79 | //////// EXTRA CREDIT //////// 80 | 81 | // If you get this far, try adding a few more tests, 82 | // then file a pull request to add them to the extra credit! 83 | // Learn more here: http://kcd.im/es6-workshop-contributing 84 | -------------------------------------------------------------------------------- /templates/14_promises.test.js: -------------------------------------------------------------------------------- 1 | // COMMENT_START eslint trickery :) 2 | /* eslint no-unreachable:0 */ 3 | // COMMENT_END 4 | // WORKSHOP_START 5 | // For each then() or catch() block, pick whether it should run or not 6 | // If it should run, tell which value it will receive in `result` or `error` 7 | // If it should not run, uncomment the error throwing statement 8 | // WORKSHOP_END 9 | test(`should resolve`, () => { 10 | return pickApple('ripe') 11 | .then( 12 | result => { 13 | // FINAL_START 14 | expect(result).toBe('ripe apple') 15 | // FINAL_END 16 | // WORKSHOP_START 17 | // throw new Error('this should not run') 18 | // expect(result).toBe(/*ENTER GUESS HERE*/) 19 | throw new Error('assert or throw here') 20 | // WORKSHOP_END 21 | }, 22 | error => { 23 | // FINAL_START 24 | throw new Error('this should not run') 25 | // FINAL_END 26 | // WORKSHOP_START 27 | // throw new Error('this should not run') 28 | // expect(error).toBe(/*ENTER GUESS HERE*/) 29 | throw new Error('assert or throw here') 30 | // WORKSHOP_END 31 | }, 32 | ) 33 | .catch(error => { 34 | // FINAL_START 35 | throw new Error('this should not run') 36 | // FINAL_END 37 | // WORKSHOP_START 38 | // throw new Error('this should not run') 39 | // expect(error).toBe(/*ENTER GUESS HERE*/) 40 | throw new Error('assert or throw here') 41 | // WORKSHOP_END 42 | }) 43 | }) 44 | 45 | test(`should reject`, () => { 46 | return pickApple('unripe') 47 | .then( 48 | result => { 49 | // FINAL_START 50 | throw new Error('this should not run') 51 | // FINAL_END 52 | // WORKSHOP_START 53 | // throw new Error('this should not run') 54 | // expect(result).toBe(/*ENTER GUESS HERE*/) 55 | throw new Error('assert or throw here') 56 | // WORKSHOP_END 57 | }, 58 | error => { 59 | // FINAL_START 60 | expect(error).toBe('unripe apple') 61 | // FINAL_END 62 | // WORKSHOP_START 63 | // throw new Error('this should not run') 64 | // expect(error).toBe(/*ENTER GUESS HERE*/) 65 | throw new Error('assert or throw here') 66 | // WORKSHOP_END 67 | }, 68 | ) 69 | .catch(error => { 70 | // FINAL_START 71 | throw new Error('this should not run') 72 | // FINAL_END 73 | // WORKSHOP_START 74 | // throw new Error('this should not run') 75 | // expect(error).toBe(/*ENTER GUESS HERE*/) 76 | throw new Error('assert or throw here') 77 | // WORKSHOP_END 78 | }) 79 | }) 80 | 81 | test(`errors can be caught`, () => { 82 | return pickApple() 83 | .then(result => { 84 | // FINAL_START 85 | throw new Error('this should not run') 86 | // FINAL_END 87 | // WORKSHOP_START 88 | // throw new Error('this should not run') 89 | // expect(result).toBe(/*ENTER GUESS HERE*/) 90 | throw new Error('assert or throw here') 91 | // WORKSHOP_END 92 | }) 93 | .catch(error => { 94 | // FINAL_START 95 | expect(error.message).toBe('out of apples') 96 | // FINAL_END 97 | // WORKSHOP_START 98 | // throw new Error('this should not run') 99 | // expect(error).toBe(/*ENTER GUESS HERE*/) 100 | throw new Error('assert or throw here') 101 | // WORKSHOP_END 102 | }) 103 | }) 104 | 105 | function pickApple(ripeness) { 106 | // Immediately return a promise which will eventually get resolved 107 | // or rejected by calling the corresponding function. 108 | return new Promise((resolve, reject) => { 109 | // Do something asynchronous. Could be AJAX, using a timeout here. 110 | setTimeout(() => { 111 | if (ripeness === 'ripe') { 112 | resolve('ripe apple') 113 | } else if (ripeness === 'unripe') { 114 | reject('unripe apple') 115 | } else { 116 | reject(new Error('out of apples')) 117 | } 118 | }) 119 | }) 120 | } 121 | 122 | //////// Elaboration & Feedback ///////// 123 | // WORKSHOP_START 124 | /* 125 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Promises&em= 126 | */ 127 | // WORKSHOP_END 128 | test('I submitted my elaboration and feedback', () => { 129 | // WORKSHOP_START 130 | const submitted = false // change this when you've submitted! 131 | // WORKSHOP_END 132 | // FINAL_START 133 | const submitted = true 134 | // FINAL_END 135 | expect(true).toBe(submitted) 136 | }) 137 | //////////////////////////////// 138 | 139 | //////// EXTRA CREDIT //////// 140 | 141 | // If you get this far, try adding a few more tests, 142 | // then file a pull request to add them to the extra credit! 143 | // Learn more here: http://kcd.im/es6-workshop-contributing 144 | -------------------------------------------------------------------------------- /templates/15_async-await.test.js: -------------------------------------------------------------------------------- 1 | // WORKSHOP_START 2 | // Rewrite all of these from promises to async/await 3 | // tip: you can turn the `it` callbacks to async functions by adding `async` to them :) 4 | // WORKSHOP_END 5 | test('should work with resolved promises', async () => { 6 | // FINAL_START 7 | const result = await doAsync() 8 | expect(result).toBe('resolved') 9 | // FINAL_END 10 | // WORKSHOP_START 11 | return doAsync().then(result => { 12 | expect(result).toBe('resolved') 13 | throw new Error( 14 | 'convert this to an async/await function and remove this error', 15 | ) 16 | }) 17 | // WORKSHOP_END 18 | }) 19 | 20 | test('should throw an error with a rejected promise', async () => { 21 | // FINAL_START 22 | try { 23 | await doAsync(true) 24 | throw new Error('this should not run') 25 | } catch (error) { 26 | expect(error).toBe('rejected') 27 | } 28 | // FINAL_END 29 | // WORKSHOP_START 30 | return doAsync(true).catch(error => { 31 | expect(error).toBe('rejected') 32 | throw new Error( 33 | 'convert this to an async/await function and remove this error', 34 | ) 35 | }) 36 | // WORKSHOP_END 37 | }) 38 | 39 | function doAsync(rejectPromise = false) { 40 | return new Promise((resolve, reject) => { 41 | setTimeout(() => { 42 | if (rejectPromise) { 43 | reject('rejected') 44 | } else { 45 | resolve('resolved') 46 | } 47 | }) 48 | }) 49 | } 50 | 51 | //////// Elaboration & Feedback ///////// 52 | // WORKSHOP_START 53 | /* 54 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Async/Await&em= 55 | */ 56 | // WORKSHOP_END 57 | test('I submitted my elaboration and feedback', () => { 58 | // WORKSHOP_START 59 | const submitted = false // change this when you've submitted! 60 | // WORKSHOP_END 61 | // FINAL_START 62 | const submitted = true 63 | // FINAL_END 64 | expect(true).toBe(submitted) 65 | }) 66 | //////////////////////////////// 67 | 68 | //////// EXTRA CREDIT //////// 69 | 70 | // If you get this far, try adding a few more tests, 71 | // then file a pull request to add them to the extra credit! 72 | // Learn more here: http://kcd.im/es6-workshop-contributing 73 | -------------------------------------------------------------------------------- /templates/16_es2016.test.js: -------------------------------------------------------------------------------- 1 | test('the exponentiation operation can be used to raise a number to a power of another number', () => { 2 | // WORKSHOP_START 3 | // refactor this to use the exponentiation operator (**) 4 | const result = Math.pow(3, 2) 5 | // WORKSHOP_END 6 | // FINAL_START 7 | const result = 3 ** 2 8 | // FINAL_END 9 | expect(result).toBe(9) 10 | }) 11 | 12 | test('array.includes can be used to determine whether an item exists in an array', () => { 13 | const bestFriend = {name: 'Sindre Sorhus'} 14 | const greatFriends = [ 15 | bestFriend, 16 | {name: 'Dustan Kasten'}, 17 | {name: 'Sam Saccone'}, 18 | {name: 'Ingvar Stepanyan'}, 19 | ] 20 | // WORKSHOP_START 21 | // refactor this to use `includes` instead 22 | const result = greatFriends.indexOf(bestFriend) !== -1 23 | // WORKSHOP_END 24 | // FINAL_START 25 | const result = greatFriends.includes(bestFriend) 26 | // FINAL_END 27 | expect(result).toBe(true) 28 | }) 29 | 30 | //////// Elaboration & Feedback ///////// 31 | // WORKSHOP_START 32 | /* 33 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=ES2016&em= 34 | */ 35 | // WORKSHOP_END 36 | test('I submitted my elaboration and feedback', () => { 37 | // WORKSHOP_START 38 | const submitted = false // change this when you've submitted! 39 | // WORKSHOP_END 40 | // FINAL_START 41 | const submitted = true 42 | // FINAL_END 43 | expect(true).toBe(submitted) 44 | }) 45 | //////////////////////////////// 46 | 47 | //////// EXTRA CREDIT //////// 48 | 49 | // If you get this far, try adding a few more tests, 50 | // then file a pull request to add them to the extra credit! 51 | // Learn more here: http://kcd.im/es6-workshop-contributing 52 | -------------------------------------------------------------------------------- /templates/18_public-class-fields.test.js: -------------------------------------------------------------------------------- 1 | test('public class fields help us avoid .bind-ing everything', () => { 2 | class FakeReactComponent { 3 | constructor(props) { 4 | this.props = props 5 | this.setState = () => {} // just for fun 6 | } 7 | } 8 | 9 | class MyComponent extends FakeReactComponent { 10 | // WORKSHOP_START 11 | constructor(...args) { 12 | super(...args) 13 | // we don't want to have to do this... 14 | this.handleClick = this.handleClick.bind(this) // sad :-( 15 | } 16 | // convert this to a public class field so it's autobound 17 | handleClick({target: {value}}) { 18 | this.props.onClick(value) 19 | } 20 | // WORKSHOP_END 21 | // FINAL_START 22 | handleClick = ({target: {value}}) => { 23 | this.props.onClick(value) 24 | } 25 | // FINAL_END 26 | render() { 27 | // weird JSX stuff here 28 | } 29 | // this is just so we can test things out 30 | testClick(value) { 31 | const fakeEvent = {target: {value}} 32 | this.handleClick(fakeEvent) 33 | } 34 | } 35 | 36 | const onClick = jest.fn() 37 | const myComponent = new MyComponent({onClick}) 38 | myComponent.testClick('hello world') 39 | expect(onClick).toHaveBeenCalledTimes(1) 40 | expect(onClick).toHaveBeenCalledWith('hello world') 41 | }) 42 | 43 | //////// Elaboration & Feedback ///////// 44 | // WORKSHOP_START 45 | /* 46 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Public+Class+Fields&em= 47 | */ 48 | // WORKSHOP_END 49 | test('I submitted my elaboration and feedback', () => { 50 | // WORKSHOP_START 51 | const submitted = false // change this when you've submitted! 52 | // WORKSHOP_END 53 | // FINAL_START 54 | const submitted = true 55 | // FINAL_END 56 | expect(true).toBe(submitted) 57 | }) 58 | //////////////////////////////// 59 | 60 | //////// EXTRA CREDIT //////// 61 | 62 | // If you get this far, try adding a few more tests, 63 | // then file a pull request to add them to the extra credit! 64 | // Learn more here: http://kcd.im/es6-workshop-contributing 65 | -------------------------------------------------------------------------------- /templates/19_symbols.test.js: -------------------------------------------------------------------------------- 1 | test('creating symbols', () => { 2 | // WORKSHOP_START 3 | const symbol = 'I wanna be a symbol one day' 4 | // WORKSHOP_END 5 | // FINAL_START 6 | const symbol = Symbol() 7 | // FINAL_END 8 | expect(typeof symbol).toBe('symbol') 9 | }) 10 | 11 | test('giving a symbol a description', () => { 12 | // WORKSHOP_START 13 | const symbol = Symbol() // give me a label 14 | // WORKSHOP_END 15 | // FINAL_START 16 | const symbol = Symbol('use the force') 17 | // FINAL_END 18 | expect(String(symbol)).toBe('Symbol(use the force)') 19 | }) 20 | 21 | test('symbols are unique', () => { 22 | const s1 = Symbol() 23 | const s2 = Symbol() 24 | // WORKSHOP_START 25 | expect(s1 === s2).toBe(/* enter your guess here */) 26 | // WORKSHOP_END 27 | // FINAL_START 28 | expect(s1 === s2).toBe(false) 29 | // FINAL_END 30 | 31 | const s3 = Symbol('I am a symbol') 32 | const s4 = Symbol('I am a symbol') 33 | // WORKSHOP_START 34 | expect(s3 === s4).toBe(/* enter your guess here */) 35 | // WORKSHOP_END 36 | // FINAL_START 37 | expect(s3 === s4).toBe(false) 38 | // FINAL_END 39 | }) 40 | 41 | test('symbols on objects', () => { 42 | const symbol = Symbol('metadata') 43 | // WORKSHOP_START 44 | // make an object called `game` that 45 | // makes this test pass 46 | // WORKSHOP_END 47 | // FINAL_START 48 | const game = { 49 | name: 'The Legend of Zelda', 50 | releaseDate: 'February 21, 1986', 51 | [symbol]: { 52 | fans: 'about a billion', 53 | }, 54 | } 55 | // FINAL_END 56 | 57 | expect(JSON.parse(JSON.stringify(game))).toEqual({ 58 | name: 'The Legend of Zelda', 59 | releaseDate: 'February 21, 1986', 60 | }) 61 | expect(game[symbol]).toEqual({ 62 | fans: 'about a billion', 63 | }) 64 | }) 65 | 66 | //////// Elaboration & Feedback ///////// 67 | // WORKSHOP_START 68 | /* 69 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Symbols&em= 70 | */ 71 | // WORKSHOP_END 72 | test('I submitted my elaboration and feedback', () => { 73 | // WORKSHOP_START 74 | const submitted = false // change this when you've submitted! 75 | // WORKSHOP_END 76 | // FINAL_START 77 | const submitted = true 78 | // FINAL_END 79 | expect(true).toBe(submitted) 80 | }) 81 | //////////////////////////////// 82 | 83 | //////// EXTRA CREDIT //////// 84 | 85 | // If you get this far, try adding a few more tests, 86 | // then file a pull request to add them to the extra credit! 87 | // Learn more here: http://kcd.im/es6-workshop-contributing 88 | -------------------------------------------------------------------------------- /templates/21_generators.test.js: -------------------------------------------------------------------------------- 1 | test(`should yield objects with value and done properties`, () => { 2 | const odds = giveMeOneOddNumber() 3 | 4 | // FINAL_START 5 | expect(typeof odds).toBe('object') 6 | expect(odds.next).toBeDefined() 7 | expect(odds.next().value).toBe(1) 8 | expect(odds.next().value).toBe(3) 9 | expect(odds.next().done).toBe(false) 10 | odds.next() 11 | expect(odds.next().value).toBe(9) 12 | expect(odds.next().done).toBe(true) 13 | // FINAL_END 14 | // WORKSHOP_START 15 | expect(odds.next().value).toBe(/* ENTER YOUR GUESS */) 16 | expect(odds.next().value).toBe(/* ENTER YOUR GUESS */) 17 | expect(odds.next().done).toBe(/* ENTER YOUR GUESS */) 18 | odds.next() 19 | expect(odds.next().value).toBe(/* ENTER YOUR GUESS */) 20 | expect(odds.next().done).toBe(/* ENTER YOUR GUESS */) 21 | // WORKSHOP_END 22 | 23 | function* giveMeOneOddNumber() { 24 | yield 1 25 | yield 3 26 | yield 5 27 | yield 7 28 | yield 9 29 | } 30 | }) 31 | 32 | test(`can be iterated over`, () => { 33 | function* giveMeOneEvenNumber() { 34 | yield 2 35 | yield 4 36 | yield 6 37 | yield 8 38 | } 39 | 40 | let sum = 0 41 | 42 | // BEWARE, THIS IS BLOCKING/SYNCHRONOUS! 43 | // Generators are not async/await, those may be in ES2016 44 | for (let even of giveMeOneEvenNumber()) { 45 | sum = sum + even 46 | } 47 | 48 | // FINAL_START 49 | expect(sum).toBe(20) 50 | // FINAL_END 51 | // WORKSHOP_START 52 | expect(sum).toBe(/* ENTER YOUR GUESS */) 53 | // WORKSHOP_END 54 | }) 55 | 56 | //////// Elaboration & Feedback ///////// 57 | // WORKSHOP_START 58 | /* 59 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Generators&em= 60 | */ 61 | // WORKSHOP_END 62 | test('I submitted my elaboration and feedback', () => { 63 | // WORKSHOP_START 64 | const submitted = false // change this when you've submitted! 65 | // WORKSHOP_END 66 | // FINAL_START 67 | const submitted = true 68 | // FINAL_END 69 | expect(true).toBe(submitted) 70 | }) 71 | //////////////////////////////// 72 | 73 | //////// EXTRA CREDIT //////// 74 | 75 | // If you get this far, try adding a few more tests, 76 | // then file a pull request to add them to the extra credit! 77 | // Learn more here: http://kcd.im/es6-workshop-contributing 78 | -------------------------------------------------------------------------------- /templates/22_reflect.test.js: -------------------------------------------------------------------------------- 1 | test('Reflect.apply can be used to call a function', () => { 2 | const person = { 3 | name: 'Fred', 4 | sayHi(greeting, noun) { 5 | return `${greeting} ${noun}! My name is ${this.name}` 6 | }, 7 | } 8 | 9 | // FINAL_START 10 | const result = Reflect.apply(person.sayHi, person, ['Hey there', 'Jaimee']) 11 | // FINAL_END 12 | // WORKSHOP_START 13 | const result = null // use Reflect.apply to invoke person.sayHi 14 | // WORKSHOP_END 15 | expect(result).toBe('Hey there Jaimee! My name is Fred') 16 | }) 17 | 18 | test('Reflect.deleteProperty can be used instead of the `delete` keyword', () => { 19 | const person = {name: 'Joan', age: 56} 20 | Reflect.defineProperty(person, 'protected', { 21 | configurable: false, 22 | value: 'YOU CANNOT GET RID OF ME!', 23 | }) 24 | // FINAL_START 25 | const ageDeleted = Reflect.deleteProperty(person, 'age') 26 | const protectedDeleted = Reflect.deleteProperty(person, 'protected') 27 | // FINAL_END 28 | // WORKSHOP_START 29 | // use Reflect.deleteProperty to delete the age property from the person object 30 | const ageDeleted = delete person.age 31 | const protectedDeleted = delete person.protected 32 | // WORKSHOP_END 33 | expect(person.age).not.toBeDefined() 34 | expect(ageDeleted).toBe(true) 35 | expect(person.protected).toBe('YOU CANNOT GET RID OF ME!') 36 | expect(protectedDeleted).toBe(false) 37 | }) 38 | 39 | test(`Reflect.ownKeys returns the object's own (not inherited) keys (including symbols)`, () => { 40 | const exists = Symbol('existance') 41 | const person = {human: true, [exists]: true} 42 | const favoriteFeature = Symbol('Fav Feat') 43 | const kyle = { 44 | __proto__: person, 45 | awesome: true, 46 | [favoriteFeature]: 'destructuring', 47 | } 48 | Reflect.defineProperty(kyle, 'favoriteLanguage', { 49 | value: 'JS', 50 | configurable: false, 51 | enumerable: false, 52 | }) 53 | // WORKSHOP_START 54 | // hint, the keys will be in the order that they're added to the object 55 | // this will be the case for most environments, though it's generally not 56 | // a good idea to rely on this fact as it's not specified in the spec. 57 | expect(Object.keys(kyle)).toEqual([ 58 | /* ENTER YOUR GUESS */ 59 | ]) 60 | expect(Object.getOwnPropertyNames(kyle)).toEqual([ 61 | /* ENTER YOUR GUESS */ 62 | ]) 63 | expect(Object.getOwnPropertySymbols(kyle)).toEqual([ 64 | /* ENTER YOUR GUESS */ 65 | ]) 66 | expect(Reflect.ownKeys(kyle)).toEqual([ 67 | /* ENTER YOUR GUESS */ 68 | ]) 69 | // WORKSHOP_END 70 | // FINAL_START 71 | expect(Object.keys(kyle)).toEqual(['awesome']) 72 | expect(Object.getOwnPropertyNames(kyle)).toEqual([ 73 | 'awesome', 74 | 'favoriteLanguage', 75 | ]) 76 | expect(Object.getOwnPropertySymbols(kyle)).toEqual([favoriteFeature]) 77 | expect(Reflect.ownKeys(kyle)).toEqual([ 78 | 'awesome', 79 | 'favoriteLanguage', 80 | favoriteFeature, 81 | ]) 82 | // FINAL_END 83 | }) 84 | 85 | //////// Elaboration & Feedback ///////// 86 | // WORKSHOP_START 87 | /* 88 | http://ws.kcd.im/?ws=ES6+and+Beyond&e=Reflect&em= 89 | */ 90 | // WORKSHOP_END 91 | test('I submitted my elaboration and feedback', () => { 92 | // WORKSHOP_START 93 | const submitted = false // change this when you've submitted! 94 | // WORKSHOP_END 95 | // FINAL_START 96 | const submitted = true 97 | // FINAL_END 98 | expect(true).toBe(submitted) 99 | }) 100 | //////////////////////////////// 101 | 102 | //////// EXTRA CREDIT //////// 103 | 104 | // If you get this far, try adding a few more tests, 105 | // then file a pull request to add them to the extra credit! 106 | // Learn more here: http://kcd.im/es6-workshop-contributing 107 | -------------------------------------------------------------------------------- /templates/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | setupTestFrameworkScriptFile: require.resolve( 3 | '../common/helpers/expect-to-be-valid-syntax.js', 4 | ), 5 | testURL: 'http://localhost/', 6 | } 7 | --------------------------------------------------------------------------------