Skip to content

Instantly share code, notes, and snippets.

@Chett23
Last active October 1, 2019 00:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Chett23/3f4c30da15a1056e97478062c0441b53 to your computer and use it in GitHub Desktop.
Save Chett23/3f4c30da15a1056e97478062c0441b53 to your computer and use it in GitHub Desktop.
ES6 Katas
This gist is to save my work on the ES6 Katas.
// 10: destructuring - array
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Destructuring arrays makes shorter code', () => {
it('extract value from array, e.g. extract 0 into x like so `let [x] = [0];`', () => {
let [firstValue] = [1];
assert.strictEqual(firstValue, 1);
});
it('get the last item from array', () => {
let [,,lastValue] = [1, 2, 3];
assert.strictEqual(lastValue, 3);
});
it('swap two variables, in one operation', () => {
let [x, y] = ['ax', 'why'];
[x, y] = [y, x];
assert.deepEqual([x, y], ['why', 'ax']);
});
it('leading commas', () => {
const all = ['ax', 'why', 'zet'];
const [,,z] = all;
assert.equal(z, 'zet');
});
it('extract from nested arrays', () => {
const user = [['Some', 'One'], 23];
const [[firstName, surname], age] = user;
const expected = 'Some One = 23 years';
assert.equal(`${firstName} ${surname} = ${age} years`, expected);
});
it('chained assignments', () => {
let c, d;
let [a, b] = [c, d] = [1, 2];
assert.deepEqual([a, b, c, d], [1, 2, 1, 2]);
});
it('in for-of loop', () => {
for (var [a, b] of [[1, 2]]) {}
assert.deepEqual([a, b], [1, 2]);
});
});
// 11: destructuring - string
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Destructuring also works on strings', () => {
it('destructure every character, just as if the string was an array', () => {
let [a, b, c] = 'abc';
assert.deepEqual([a, b, c], ['a', 'b', 'c']);
});
it('missing characters are undefined', () => {
const [a, ,c] = 'ab';
assert.equal(c, void 0);
});
it('unicode character work too', () => {
const [,space, coffee] = 'a ☕';
assert.equal(coffee, '\u{2615}');
});
});
// 12: destructuring - object
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Destructure objects', () => {
it('by surrounding the left-hand variable with `{}`', () => {
const {x} = {x: 1};
assert.equal(x, 1);
});
describe('nested', () => {
it('multiple objects', () => {
const magic = {first: 23, second: 42};
const {second} = magic;
assert.equal(second, 42);
});
it('object and array', () => {
const {z:[,x]} = {z: [23, 42]};
assert.equal(x, 42);
});
it('array and object', () => {
const [,[{lang}]] = [null, [{env: 'browser', lang: 'ES6'}]];
assert.equal(lang, 'ES6');
});
});
describe('interesting', () => {
it('missing refs become undefined', () => {
const {z} = {x: 1};
assert.equal(z, void 0);
});
it('destructure from builtins (string)', () => {
const {substr} = String(1);
assert.equal(substr, String.prototype.substr);
});
});
});
// 13: destructuring - defaults
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('When destructuring you can also provide default values', () => {
it('just assign a default value, like so `a=1`', () => {
const [a=1] = [];
assert.equal(a, 1);
});
it('for a missing value', () => {
const [,b=2] = [1,,3];
assert.equal(b, 2);
});
it('in an object', () => {
const {a, b=2} = {a: 1};
assert.equal(b, 2);
});
it('if the value is undefined', () => {
const {a, b=2} = {a: 1, b: void 0};
assert.strictEqual(b, 2);
});
it('also a string works with defaults', () => {
const [a,b=2] = '1';
assert.equal(a, '1');
assert.equal(b, 2);
});
});
// 14: destructuring - parameters
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Destructuring function parameters', () => {
describe('destruct parameters', () => {
it('multiple params from object', () => {
const fn = ({id, name}) => {
assert.equal(id, 42);
assert.equal(name, 'Wolfram');
};
const user = {name: 'Wolfram', id: 42};
fn(user);
});
it('multiple params from array/object', () => {
const fn = ([,{name}]) => {
assert.equal(name, 'Alice');
};
const users = [{name: 'nobody'}, {name: 'Alice', id: 42}];
fn(users);
});
});
describe('default values', () => {
it('for simple values', () => {
const fn = (id, name='Bob') => {
assert.strictEqual(id, 23);
assert.strictEqual(name, 'Bob');
};
fn(23);
});
it('for a missing array value', () => {
const defaultUser = {id: 23, name: 'Joe'};
const fn = ([user={id:23,name:'Joe'}]) => {
assert.deepEqual(user, defaultUser);
};
fn([]);
});
it('mix of parameter types', () => {
const fn = (id=1, [arr=2], {obj=3}) => {
assert.equal(id, 1);
assert.equal(arr, 2);
assert.equal(obj, 3);
};
fn(void 0, [], {});
});
});
});
// 15: destructuring - assign
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Assign object property values to new variables while destructuring', () => {
describe('for simple objects', function() {
it('use a colon after the property name, like so `propertyName: newName`', () => {
const {x: y} = {x: 1};
assert.equal(y, 1);
});
it('assign a new name and give it a default value using `= <default value>`', () => {
const {x: y=42} = {y: 23};
assert.equal(y, 42);
});
});
describe('for function parameter names', function() {
it('do it the same way, with a colon behind it', () => {
const fn = ({x: y}) => {
assert.equal(y, 1);
};
fn({x: 1});
});
it('giving it a default value is possible too, like above', () => {
const fn = ({x: y=3}) => {
assert.equal(y, 3);
};
fn({});
});
});
});
// 16: object-literal - computed properties
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Object literal properties may be computed values', () => {
it('a computed property `x` needs to be surrounded by `[]`', () => {
const propertyName = 'x';
const obj = {[propertyName]: 1};
assert.equal(obj.x, 1);
});
it('can also get a function assigned', () => {
const key = 'func';
const obj = {[key](){return 'seven'}};
assert.equal(obj.func(), 'seven');
});
it('the key may also be the result of a function call', () => {
const getName = () => 'propertyName';
const obj = {[getName()]() {return 'seven'}};
assert.equal(obj.propertyName(), 'seven');
});
it('the key can also be constructed by an expression', () => {
const what = 'tyName';
const obj = {['proper' + what]: null};
assert('propertyName' in obj);
});
it('accessor keys can be computed names too', () => {
const obj = {
get ['key']() {return 1},
};
assert.equal(obj.key, 1);
});
});
// 17: unicode - in strings
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Unicode in strings', () => {
it('are prefixed with `\\u` (one backslash and u)', () => {
const nuclear = '\u2622';
assert.equal(nuclear, '☢');
});
it('value is 4 bytes/digits', () => {
const nuclear = '\u2622';
assert.equal(`no more ${nuclear}`, 'no more ☢');
});
it('even "normal" character`s values can be written as hexadecimal unicode', () => {
const nuclear = `\u006E\u006F more \u2622`;
assert.equal(nuclear, 'no more ☢');
});
it('curly braces may surround the value', () => {
const nuclear = `\u{006E}\u006F more \u2622`;
assert.equal(nuclear, 'no more ☢');
});
});
// 18: rest - as-parameter
// To do: make all tests pass, leave the assert lines unchanged!
describe('Rest parameters in functions', () => {
it('must be the last parameter', () => {
const fn = (...rest) => {
assert.deepEqual([1, 2], rest);
};
fn(1, 2);
});
it('can be used to get all other parameters', () => {
const fn = (firstParam, secondParam, ...rest) => {
assert.deepEqual([3,4], rest);
};
fn(null, 2, 3, 4);
});
it('makes `arguments` obsolete', () => {
const fn = (...args) => {
assert.deepEqual([42, 'twenty three', 'win'], args);
};
fn(42, 'twenty three', 'win');
});
it('eliminate `arguments`!!!', () => {
const fn = (...rest) => rest;
const [firstArg, ...rest] = fn(1, 2, 3);
assert.deepEqual([2, 3], rest);
});
});
// 19: rest - with-destructuring
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Rest parameters with destructuring', () => {
it('must be last', () => {
const [...all] = [1, 2, 3, 4];
assert.deepEqual(all, [1, 2, 3, 4]);
});
it('assign rest of an array to a variable', () => {
const [last, ...all] = [1, 2, 3, 4];
assert.deepEqual(all, [2, 3, 4]);
});
// the following are actually using `spread` ... oops, to be fixed #TODO
it('concat differently', () => {
const theEnd = [3, 4];
const allInOne = [1, 2, ...theEnd];
assert.deepEqual(allInOne, [1, 2, 3, 4]);
});
it('`apply` made simple, even for constructors', () => {
const theDate = [2015, 1, 1];
const date = new Date(...theDate);
assert.deepEqual(new Date(2015, 1, 1), date);
});
});
// 1: template strings - basics
// To do: make all tests pass, leave the asserts unchanged!
// Follow the hints of the failure messages!
describe('A template string, is wrapped in ` (backticks) instead of \' or "', function() {
describe('by default, behaves like a normal string', function() {
it('just surrounded by backticks', function() {
// var str had nothing within the backticks
var str = `like a string`;
assert.equal(str, `like a string`);
});
});
var x = 42;
var y = 23;
describe('can evaluate variables, which are wrapped in "${" and "}"', function() {
it('e.g. a simple variable "${x}" just gets evaluated', function() {
var evaluated = `x=${x}`;
assert.equal(evaluated, 'x=' + x);
});
it('multiple variables get evaluated too', function() {
var evaluated = `${ x }+${ y }`;
assert.equal(evaluated, x + '+' + y);
});
});
describe('can evaluate any expression, wrapped inside "${...}"', function() {
it('all inside "${...}" gets evaluated', function() {
var evaluated = `${ x + y }`;
assert.equal(evaluated, x+y);
});
it('inside "${...}" can also be a function call', function() {
function getDomain(){
return document.domain;
}
var evaluated = `${ getDomain() }`;
assert.equal(evaluated, 'tddbin.com');
});
});
});
// 20: spread - with-arrays
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Spread syntax with arrays', () => {
describe('basically', () => {
it('expands the items of an array by prefixing it with `...`', function() {
const middle = [1, 2, 3];
const arr = [0, ...middle, 4];
assert.deepEqual(arr, [0, 1, 2, 3, 4]);
});
it('an empty array expanded is no item', function() {
const arr = [0, ...[], 1];
assert.deepEqual(arr, [0, 1]);
});
});
describe('is (in a way) the opposite to the rest syntax', function() {
it('both use `...` to either expand all items and collect them', function() {
const [...rest] = [...[1, 2, 3, 4, 5]];
assert.deepEqual(rest, [1, 2, 3, 4, 5]);
});
it('rest syntax must be last in an array, spread can be used in any place', function() {
const [a, b, ...rest] = [1, ...[2, 3], 4, 5];
assert.equal(a, 1);
assert.equal(b, 2);
assert.deepEqual(rest, [3, 4, 5]);
});
});
describe('used as function parameter', () => {
it('prefix with `...` to spread as function params', function() {
const magicNumbers = [1, 2];
const fn = ([magicA, magicB]) => {
assert.deepEqual(magicNumbers[0], magicA);
assert.deepEqual(magicNumbers[1], magicB);
};
fn(magicNumbers);
});
it('pass an array of numbers to Math.max()', function() {
const max = Math.max(...[23, 0, 42]);
assert.equal(max, 42);
});
});
describe('used as constructor parameter', () => {
it('just like in a function call (is not possible using `apply`)', () => {
class X {
constructor(a, b, c) { return [a, b, c]; }
}
const args = [1,2,3];
assert.deepEqual(new X(...args), [1, 2, 3]);
});
});
});
// 21: spread - with-strings
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Spread syntax with strings', () => {
it('expands each character of a string by prefixing it with `...`', function() {
const [a,b] = [...'ab'];
assert.equal(a, 'a');
assert.equal(b, 'b');
});
it('expands any kind of character', function() {
const arr = [...'1', ' ', '☢', ' ', '2'];
assert.deepEqual(arr, ['1', ' ', '☢', ' ', '2']);
});
it('works anywhere inside an array (must not be last)', function() {
const letters = ['a', 'bcd', 'e', 'f','',''];
assert.equal(letters.length, 6);
});
it('don`t confuse with the rest operator', function() {
const [...rest] = [...'1234', ...'5'];
assert.deepEqual(rest, [1, 2, 3, 4, 5]);
});
it('can also be used as function parameter', function() {
const max = Math.max(...'12345');
assert.deepEqual(max, 5);
});
});
// 22: class - creation
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Class creation', () => {
it('is as simple as `class XXX {}`', function() {
class TestClass {};
const instance = new TestClass();
assert.equal(typeof instance, 'object');
});
it('a class is block scoped', () => {
{class Inside {}}
assert.equal(typeof Inside, 'undefined');
});
it('the `constructor` is a special method', function() {
class User {
constructor(id) {
this.id = id
}
}
const user = new User(42);
assert.equal(user.id, 42);
});
it('defining a method by writing it inside the class body', function() {
class User {
writesTests(){
return false
}
}
const notATester = new User();
assert.equal(notATester.writesTests(), false);
});
it('multiple methods need no commas (opposed to object notation)', function() {
class User {
wroteATest() { this.everWroteATest = true; }
isLazy() { return true }
}
const tester = new User();
assert.equal(tester.isLazy(), true);
tester.wroteATest();
assert.equal(tester.isLazy(), true);
});
it('anonymous class', () => {
const classType = typeof class test {};
assert.equal(classType, 'function');
});
});
// 23: class - accessors
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Class accessors (getter and setter)', () => {
it('a getter is defined like a method prefixed with `get`', () => {
class MyAccount {
get balance() { return Infinity; }
}
assert.equal(new MyAccount().balance, Infinity);
});
it('a setter has the prefix `set`', () => {
class MyAccount {
get balance() { return this.amount; }
set balance(amount) { this.amount = amount; }
}
const account = new MyAccount();
account.balance = 23;
assert.equal(account.balance, 23);
});
describe('dynamic accessors', () => {
it('a dynamic getter name is enclosed in `[]`', function() {
const balance = 'yourMoney';
class YourAccount {
get [balance]() { return -Infinity; }
}
assert.equal(new YourAccount().yourMoney, -Infinity);
});
it('a dynamic setter name as well', function() {
const propertyName = 'balance';
class MyAccount {
get [propertyName]() { return this.amount; }
set [propertyName](amount) { this.amount = 23; }
}
const account = new MyAccount();
account.balance = 42;
assert.equal(account.balance, 23);
});
});
});
// 24: class - static keyword
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Inside a class you can use the `static` keyword', () => {
describe('for methods', () => {
class UnitTest {}
it('a static method just has the prefix `static`', () => {
class TestFactory {
static makeTest() { return new UnitTest(); }
}
assert.ok(TestFactory.makeTest() instanceof UnitTest);
});
it('the method name can be dynamic/computed at runtime', () => {
const methodName = 'createTest';
class TestFactory {
static [methodName]() { return new UnitTest(); }
}
assert.ok(TestFactory.createTest() instanceof UnitTest);
});
});
describe('for accessors', () => {
it('a getter name can be static, just prefix it with `static`', () => {
class UnitTest {
static get testType() { return 'unit'; }
}
assert.equal(UnitTest.testType, 'unit');
});
it('even a static getter name can be dynamic/computed at runtime', () => {
const type = 'test' + 'Type';
class IntegrationTest {
static get [type]() { return 'integration'; }
}
assert.ok('testType' in IntegrationTest);
assert.equal(IntegrationTest.testType, 'integration');
});
});
});
// 25: class - extends
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Classes can inherit from another using `extends`', () => {
describe('the default super class is `Object`', () => {
it('a `class A` is an instance of `Object`', () => {
class A extends Object {}
assert.equal(new A() instanceof Object, true);
});
it('when B extends A, B is also instance of `Object`', () => {
class A {}
class B extends A {}
assert.equal(new B() instanceof A, true);
assert.equal(new B() instanceof Object, true);
});
it('a class can extend `null`, and is not an instance of Object', () => {
class NullClass extends null {}
let nullInstance = new NullClass();
assert.equal(nullInstance instanceof Object, false);
});
});
describe('instance of', () => {
it('when B inherits from A, `new B()` is also an instance of A', () => {
class A {};
class B extends A {}
assert.equal(new B() instanceof A, true);
});
it('extend over multiple levels', () => {
class A {}
class B extends A {}
class C extends B {}
assert.equal(new C instanceof A, true);
});
});
});
// 26: class - more-extends
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Classes can inherit from another', () => {
it('extend an `old style` "class", a function, still works', () => {
class A {};
class B extends A {}
assert.equal(new B() instanceof A, true);
});
describe('prototypes are as you know them', () => {
class A {}
class B extends A {}
it('A is the prototype of B', () => {
const isIt = A.isPrototypeOf(B);
assert.equal(isIt, true);
});
it('A`s prototype is also B`s prototype', () => {
const proto = new B ();
// Remember: don't touch the assert!!! :)
assert.equal(A.prototype.isPrototypeOf(proto), true);
});
});
describe('`extends` using an expression', () => {
it('e.g. the inline assignment of the parent class', () => {
class A {};
class B extends A {}
assert.equal(new B() instanceof A, true);
});
it('or calling a function that returns the parent class', () => {
const returnParent = (beNull) => beNull ? class {} : null;
class B extends returnParent() {}
assert.equal(Object.getPrototypeOf(B.prototype), null);
});
});
});
// 27: class - super inside a method
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Inside a class use `super` to access parent methods', () => {
it('use of `super` without `extends` fails (already when transpiling)', () => {
class A {hasSuper() { return false; }}
assert.equal(new A().hasSuper(), false);
});
it('`super` with `extends` calls the method of the given name of the parent class', () => {
class A {hasSuper() { return true; }}
class B extends A {super() { return this.hasSuper; }}
assert.equal(new B().hasSuper(), true);
});
it('when overridden a method does NOT automatically call its super method', () => {
class A {hasSuper() { return void 0; }}
class B extends A {super() { return this.hasSuper; }}
assert.equal(new B().hasSuper(), void 0);
});
it('`super` works across any number of levels of inheritance', () => {
class A {iAmSuper() { return true; }}
class B extends A {}
class C extends B {super() { return iAmSuper(); }}
assert.equal(new C().iAmSuper(), true);
});
it('accessing an undefined member of the parent class returns `undefined`', () => {
class A {}
class B extends A {getMethod() { return super.undefined; }}
assert.equal(new B().getMethod(), void 0);
});
});
// 28: class - super in constructor
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Inside a class`s constructor `super()` can be used', () => {
it('`extend` a class and use `super()` to call the parent constructor', () => {
class A {constructor() { this.levels = 1; }}
class B extends A {
constructor() {
super();
this.levels++;
}
}
assert.equal(new B().levels, 2);
});
it('`super()` may also take params', () => {
class A {constructor(startValue=1, addTo=1) { this.counter = startValue + addTo; }}
class B extends A {
constructor(...args) {
super(...args);
this.counter++;
}
}
assert.equal(new B(42, 2).counter, 45);
});
it('it is important where you place your `super()` call!', () => {
class A {inc() { this.countUp = 1; }}
class B extends A {
inc() {
super.inc();
this.countUp = 1;
return this.countUp;
}
}
assert.equal(new B().inc(), 1);
});
it('use `super.constructor` to find out if there is a parent constructor', () => {
class ParentClassA {constructor() {"parent"}}
class B extends ParentClassA {
constructor() {
super();
this.isTop = '' + super.constructor;
}
}
assert(new B().isTop.includes('ParentClassA'), new B().isTop);
});
});
// 29: array - `Array.from` static method
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Array.from` converts an array-like object or list into an Array', () => {
const arrayLike = {0: 'one', 1: 'two', length: 2};
it('call `Array.from` with an array-like object', function() {
const arr = Array.from(arrayLike);
assert.deepEqual(arr, ['one', 'two']);
});
it('a DOM node`s classList object can be converted', function() {
const domNode = document.createElement('span');
domNode.classList.add('some');
domNode.classList.add('other');
const classList = Array.from(domNode.classList);
assert.equal(''+classList, ''+['some', 'other']);
});
it('convert a NodeList to an Array and `filter()` works on it', function() {
const nodeList = Array.from(document.createElement('span'));
const divs = nodeList.filter((node) => node.tagName === 'div');
assert.deepEqual(divs.length, 0);
});
describe('custom conversion using a map function as second param', () => {
it('we can modify the value before putting it in the array', function() {
const arr = Array.from(arrayLike, (value) => value.toUpperCase());
assert.deepEqual(arr, ['ONE', 'TWO']);
});
it('and we also get the object`s key as second parameter', function() {
const arr = Array.from(arrayLike, (value, key) => `${key}=${value}`);
assert.deepEqual(arr, ['0=one', '1=two']);
});
});
});
// 2: template strings - multiline
// To do: make all tests pass, leave the asserts unchanged!
// Follow the hints of the failure messages!
describe('Template string, can contain multiline content', function() {
it('wrap it in backticks (`) and add a newline, to span across two lines', function() {
var normalString = `line1\n\nline3`;
assert.equal(normalString, 'line1\n\nline3');
});
it('even over more than two lines', function() {
var multiline = `\n\n\n`;
assert.equal(multiline.split('\n').length, 4);
});
describe('and expressions inside work too', function() {
var x = 42;
it('like simple variables', function() {
var multiline = `line 1\n\n ${x}`;
assert.equal(multiline, 'line 1\n\n 42');
});
it('also here spaces matter', function() {
var multiline = `\n\n${x}`;
assert.equal(multiline, '\n\n42');
});
});
});
// 30: array - `Array.of` static method
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Array.of` creates an array with the given arguments as elements', () => {
it('dont mix it up with `Array(10)`, where the argument is the array length', () => {
const arr = Array.of(10);
assert.deepEqual(arr, [10]);
});
it('puts all arguments into array elements', () => {
const arr = Array.of(1,2);
assert.deepEqual(arr, [1, 2]);
});
it('takes any kind and number of arguments', () => {
const starter = [1, 2];
const end = [3, '4'];
const arr = Array.of([...starter], ...end);
assert.deepEqual(arr, [[1, 2], 3, '4']);
});
});
// 31: array - `Array.prototype.fill` method
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Array.prototype.fill` can fill up an array with one value', () => {
it('`fill(0)` will populate `0` into each array element', function() {
const arr = new Array(3).fill(0);
assert.deepEqual(arr, [0, 0, 0]);
});
it('fill only changes content, adds no new elements', function() {
const arr = [].fill();
assert.deepEqual(arr, []);
});
it('second parameter to `fill()` is the position where to start filling', function() {
const fillPosition = 2;
const arr = [1,2,3].fill(42, fillPosition);
assert.deepEqual(arr, [1, 2, 42]);
});
it('third parameter is the position where filling stops', function() {
const fillStartAt = 1;
const fillEndAt = 2;
const arr = [1,2,3].fill(42, fillStartAt, fillEndAt);
assert.deepEqual(arr, [1, 42, 3]);
});
});
// 32: array - `Array.prototype.find`
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Array.prototype.find` makes finding items in arrays easier', () => {
it('takes a compare function', function() {
const found = [true].find(el => el === true);
assert.equal(found, true);
});
it('returns the first value found', function() {
const found = [0, 1, 2].find(item => item > 1);
assert.equal(found, 2);
});
it('returns `undefined` when nothing was found', function() {
const found = [1, 2, 3].find(item => item === 23);
assert.equal(found, void 0);
});
it('combined with destructuring complex compares become short', function() {
const bob = {name: 'Bob'};
const alice = {name: 'Alice'};
const found = [bob, alice].find(({name}) => name === 'Alice');
assert.equal(found, alice);
});
});
// 33: array - `Array.prototype.findIndex`
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Array.prototype.findIndex` makes finding items in arrays easier', () => {
it('takes a compare function, returns the index where it returned true', function() {
const foundAt = [false, true].findIndex(item => item === true);
assert.equal(foundAt, 1);
});
it('returns the first position it was found at', function() {
const foundAt = [0, 1, 1, 1].findIndex(item => item === 1);
assert.equal(foundAt, 1);
});
it('returns `-1` when nothing was found', function() {
const foundAt = [1, 2, 3].findIndex(item => item > 5);
assert.equal(foundAt, -1);
});
it('the findIndex callback gets the item, index and array as arguments', function() {
const three = 3;
const containsThree = arr => arr.indexOf(three) > -1;
function theSecondThree(item, index, arr) {
const preceedingItems = arr.slice(0, index);
return containsThree(preceedingItems);
}
const foundAt = [1, 1, 2, 3, 3, 3].findIndex(theSecondThree);
assert.equal(foundAt, 4);
});
it('combined with destructuring complex compares become short', function() {
const bob = {name: 'Bob'};
const alice = {name: 'Alice'};
const foundAt = [bob, alice].findIndex(({name: {length: l}}) => l > 3);
assert.equal(foundAt, 1);
});
});
// 34: symbol - basics
// A symbol is a unique and immutable data type and may be used as an identifier for object properties
// read more at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Symbol', function() {
it('`Symbol` lives in the global scope', function(){
const expected = window.Symbol;
assert.equal(Symbol, expected);
});
it('every `Symbol()` is unique', function(){
const sym1 = Symbol();
const sym2 = Symbol();
assert.notEqual(sym1, sym2);
});
it('every `Symbol()` is unique, even with the same parameter', function(){
var sym1 = Symbol('foo');
var sym2 = Symbol('foo');
assert.notEqual(sym1, sym2);
});
it('`typeof Symbol()` returns "symbol"', function(){
const theType = typeof(Symbol());
assert.equal(theType, 'symbol');
});
it('`new Symbol()` throws an exception, to prevent creation of Symbol wrapper objects', function(){
function fn() {
new Symbol();
}
assert.throws(fn);
});
});
// 35: Symbol.for - retrieves or creates a runtime-wide symbol
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Symbol.for` for registering Symbols globally', function() {
it('creates a new symbol (check via `typeof`)', function() {
const symbolType = typeof Symbol.for('symbol name');
assert.equal(symbolType, 'symbol');
});
it('stores the symbol in a runtime-wide registry and retrieves it from there', function() {
const sym = Symbol.for('new symbol');
const sym1 = Symbol.for('new symbol');
assert.equal(sym, sym1);
});
it('is different to `Symbol()` which creates a symbol every time and does not store it', function() {
var globalSymbol = Symbol.for('new symbol');
var localSymbol = Symbol('new symbol');
assert.notEqual(globalSymbol, localSymbol);
});
describe('`.toString()` on a Symbol', function() {
it('also contains the key given to `Symbol.for()`', function() {
const description = Symbol('new symbol').toString();
assert.equal(description, 'Symbol(new symbol)');
});
describe('NOTE: the description of two different symbols', function() {
it('might be the same', function() {
const symbol1AsString = Symbol('new symbol').toString();
const symbol2AsString = Symbol.for('new symbol').toString();
assert.equal(symbol1AsString, symbol2AsString);
});
it('but the symbols are not the same!', function() {
const symbol1 = Symbol('new symbol');
const symbol2 = Symbol.for('new symbol');
assert.notEqual(symbol1, symbol2);
});
});
});
});
// 36: Symbol.keyFor - retrieves a shared symbol key from the global symbol registry
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Symbol.keyFor()` gets the symbol key for a given symbol', function() {
it('pass the symbol to `keyFor()` and you get its key', function() {
const key = Symbol.keyFor(Symbol.for('foo'));
assert.equal(key, 'foo');
});
it('local symbols are not in the runtime-wide registry', function() {
// Hint: `Symbol()` creates a local symbol!
const localSymbol = Symbol('foo');
const key = Symbol.keyFor(localSymbol);
assert.equal(key, void 0);
});
it('predefined symbols are not in the runtime-wide registry either', function() {
const key = Symbol.keyFor(Symbol.iterator);
assert.equal(key, void 0);
});
it('for non-Symbols throws an error', function() {
function fn() {
Symbol.keyFor('foo');
}
assert.throws(fn);
});
});
// 37: iterator/iterable - array.
// The iterator protocol defines a standard way to produce a sequence of values (either finite or infinite).
// read more at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('The native array is a built-in iterable object', function() {
const arr = ['a', 'B', 'see'];
describe('the iterator', function() {
it('an array has an iterator, which is a function', function() {
const iterator = arr[Symbol.iterator];
const theType = typeof iterator;
const expected = 'function';
assert.equal(theType, expected);
});
it('can be looped with `for-of`, which expects an iterable', function() {
let count = 0;
for (let value of arr) {
count++;
}
assert.equal(count, arr.length);
});
});
describe('the iterator protocol', function() {
it('calling `next()` on an iterator returns an object according to the iterator protocol', function() {
const iterator = arr[Symbol.iterator]();
const firstItem = iterator.next();
assert.deepEqual(firstItem, {done: false, value: 'a'});
});
it('the after-last element has done=true', function() {
const arr = [];
const iterator = arr[Symbol.iterator]();
const afterLast = iterator.next();
assert.deepEqual(afterLast, {done: true, value: void 0});
});
});
});
// 38: iterator/iterable - string.
// The iterator protocol defines a standard way to produce a sequence of values (either finite or infinite).
// read more at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('The native string is a built-in iterable object', function() {
const s = 'abc';
describe('string is iterable', function() {
it('the string`s object key `Symbol.iterator` is a function', function() {
const isA = typeof s[Symbol.iterator];
assert.equal(isA, 'function');
});
it('use `Array.from()` to make an array out of any iterable', function(){
const arr = Array.from(s);
assert.deepEqual(arr, ['a', 'b', 'c']);
});
});
describe('a string`s iterator', function() {
let iterator;
beforeEach(function() {
iterator = s[Symbol.iterator]();
});
it('has a special string representation', function(){
const description = iterator.toString();
assert.equal(description, '[object String Iterator]');
});
it('`iterator.next()` returns an object according to the iterator protocol', function(){
const value = iterator.next();
assert.deepEqual(value, {done: false, value: 'a'});
});
it('the after-last call to `iterator.next()` says done=true, no more elements', function(){
iterator.next();
iterator.next();
iterator.next();
assert.equal(iterator.next().done, true);
});
});
});
// 39: iterator - custom. Iterable is a protocol, when implemented allows objects
// to customize their iteration behavior, such as what values are looped over in a for..of construct.
// read more at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('A simple iterable without items inside, implementing the right protocol', () => {
function iteratorFunction() {
return {
next: function(){ return { done: true }}
}
}
describe('the `iteratorFunction` needs to comply to the iterator protocol', function() {
it('must return an object', function() {
assert.equal(typeof iteratorFunction(), 'object');
});
it('the object must have a function assigned to a key `next`', function() {
assert.equal(typeof iteratorFunction().next, 'function');
});
it('calling `next()` must return an object with `{done: true}`', function() {
assert.deepEqual(iteratorFunction().next(), {done: true});
});
});
let iterable = {
[Symbol.iterator]: iteratorFunction
};
beforeEach(function() {
iterable;
});
describe('the iterable', function() {
it('must be an object', function() {
assert.equal(typeof iterable, 'object');
});
it('must have the iterator function assigned to the key `Symbol.iterator`', function() {
assert.equal(iterable[Symbol.iterator], iteratorFunction);
});
});
describe('using the iterable', function() {
it('it contains no values', function() {
let values = '';
for (let value of iterable) {
values += value;
}
assert.equal(values, '');
});
it('has no `.length` property', function() {
const hasLengthProperty = iterable.hasOwnProperty('length');
assert.equal(hasLengthProperty, false);
});
describe('can be converted to an array', function() {
it('using `Array.from()`', function() {
const arr = Array.from(iterable);
assert.equal(Array.isArray(arr), true);
});
it('where `.length` is still 0', function() {
const arr = Array.from(iterable);
const length = arr.length;
assert.equal(length, 0);
});
});
});
});
// 3: template strings - tagged
// To do: make all tests pass, leave the asserts unchanged!
// Follow the hints of the failure messages!
describe('Tagged template strings, are an advanced form of template strings', function() {
it('syntax: prefix a template string with a function to call (without "()" around it)', function() {
function tagFunc(s) {
return s.toString();
}
var evaluated = tagFunc `template string`;
assert.equal(evaluated, 'template string');
});
describe('the tag function can access each part of the template', function() {
describe('the 1st parameter receives only the pure strings of the template string', function() {
function tagFunction(strings) {
return strings;
}
it('the strings are an array', function() {
var result = ['template string'];
assert.deepEqual(tagFunction`template string`, result);
});
it('expressions are NOT passed to it', function() {
var tagged = tagFunction`one${23}two`;
assert.deepEqual(tagged, ['one', 'two']);
});
});
describe('the 2nd and following parameters contain the values of the processed substitution', function() {
var one = 1;
var two = 2;
var three = 3;
it('the 2nd parameter contains the first expression`s value', function() {
function firstValueOnly(strings, firstValue) {
return firstValue;
}
assert.equal(firstValueOnly`uno ${one}, dos ${two}`, 1);
});
it('the 3rd parameter contains the second expression`s value', function() {
function firstValueOnly(strings, firstValue, secondValue) {
return secondValue;
}
assert.equal(firstValueOnly`uno ${one}, dos ${two}`, 2);
});
it('using ES6 rest syntax, all values can be accessed via one variable', function() {
function valuesOnly(stringsArray, ...allValues) { // using the new ES6 rest syntax
return allValues;
}
assert.deepEqual(valuesOnly`uno=${one}, dos=${two}, tres=${three}`, [1, 2, 3]);
});
});
});
});
// 45: Map.prototype.get()
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Map.prototype.get` returns the element from the map for a key', function(){
it('`get(key)` returns the value stored for this key', function() {
let map = new Map();
map.set('key', 'value');
const value = map.get('key');
assert.equal(value, 'value');
});
it('multiple calls still return the same value', function() {
let map = new Map();
map.set('value', 'value');
var value = map.get(map.get(map.get('value')));
assert.equal(value, 'value');
});
it('requires exactly the value as passed to `set()`', function() {
let map = new Map();
const obj = {};
map.set(obj, 'object is key');
assert.equal(map.get(obj), 'object is key');
});
it('leave out the key, and you get the value set for the key `undefined` (void 0)', function() {
let map = new Map();
map.set(void 0, 'yo');
const value = map.get();
assert.equal(value, 'yo');
});
it('returns undefined for an unknown key', function() {
let map = new Map();
map.set(void 0, 1);
const value = map.get('unknown');
assert.equal(value, void 0);
});
});
// 46: Map.prototype.set()
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Map.prototype.set` adds a new element with key and value to a Map', function(){
it('simplest use case is `set(key, value)` and `get(key)`', function() {
let map = new Map();
map.set('key','value');
assert.equal(map.get('key'), 'value');
});
it('the key can be a complex type too', function() {
const noop = function() {};
let map = new Map();
map.set(noop, 'the real noop');
assert.equal(map.get(noop), 'the real noop');
});
it('calling `set()` again with the same key replaces the value', function() {
let map = new Map();
map.set('key', 'value');
map.set('key', 'value1');
assert.equal(map.get('key'), 'value1');
});
it('`set()` returns the map object, it`s chainable', function() {
let map = new Map();
map.set(1, 'one')
.set(2, 'two')
.set(3, 'three')
;
assert.deepEqual([...map.keys()], [1,2,3]);
assert.deepEqual([...map.values()], ['one', 'two', 'three']);
});
});
// 4: template strings - String.raw
// To do: make all tests pass, leave the asserts unchanged!
// Follow the hints of the failure messages!
describe('Use the `raw` property of tagged template strings like so `s.raw`', function() {
it('the `raw` property accesses the string as it was entered', function() {
function firstChar(strings) {
return strings.raw;
}
assert.equal(firstChar`\n`, '\\n');
});
it('`raw` can access the backslash of a line-break', function() {
function firstCharEntered(strings) {
var lineBreak = strings.raw;
return lineBreak;
}
assert.equal(firstCharEntered`\n`, '\\n');
});
describe('`String.raw` as a static function', function(){
it('concats the raw strings', function() {
var expected = '\\n';
assert.equal(String.raw`\n`, expected);
});
it('two raw-templates-string-backslashes equal two escaped backslashes', function() {
const TWO_BACKSLASHES = '\\\\';
assert.equal(String.raw`\\`, TWO_BACKSLASHES);
});
it('works on unicodes too', function() {
var smilie = '\u{1F600}';
var actual = String`\u{1F600}`;
assert.equal(actual, smilie);
});
});
});
// 5: arrow functions - basics
// To do: make all tests pass, leave the asserts unchanged!
// Follow the hints of the failure messages!
describe('Arrow functions', function() {
it('are shorter to write, instead of `function(){}` write `() => {}`', function() {
var func = () => '() => {}';
assert.equal('' + func(), '() => {}');
});
it('instead `{}` use an expression, as return value', function() {
var func = () => 'I return too';
assert.equal(func(), 'I return too');
});
it('one parameter can be written without parens', () => {
var func = p => p - 1;
assert.equal(func(25), 24);
});
it('many params require parens', () => {
var func = (param, param1) => param + param1;
assert.equal(func(23, 42), 23+42);
});
it('the function body needs parens to return an object', () => {
var func = () => {return{iAm: 'an object'}};
assert.deepEqual(func(), {iAm: 'an object'});
});
});
// 6: arrow functions - binding
// To do: make all tests pass, leave the asserts unchanged!
// Follow the hints of the failure messages!
class LexicallyBound {
getFunction() {
return () => {
// return this returns that object from memory instead of a new LexicallyBound class obj
return this;
}
}
getArgumentsFunction() {
return () => {return arguments}
}
}
describe('Arrow functions have lexical `this`, no dynamic `this`', () => {
it('bound at definition time, use `=>`', function() {
var bound = new LexicallyBound();
var fn = bound.getFunction();
assert.strictEqual(fn(), bound);
});
it('can NOT bind a different context', function() {
var bound = new LexicallyBound();
var fn = bound.getFunction();
var anotherObj = {};
// expected = bound or a new LexicallyBound obj and fn.call(anotherObj) returns the same
var expected = bound;
assert.strictEqual(fn.call(anotherObj), expected);
});
it('`arguments` does NOT work inside arrow functions', function() {
var bound = new LexicallyBound();
var fn = bound.getArgumentsFunction();
assert.equal(fn(1, 2).length, 0);
});
});
// 7: block scope - let
// To do: make all tests pass, leave the asserts unchanged!
// Follow the hints of the failure messages!
describe('`let` restricts the scope of the variable to the current block', () => {
describe('`let` vs. `var`', () => {
it('`var` works as usual, it`s scope is the function', () => {
if (true) {
var varX = true;
}
assert.equal(varX, true);
});
it('`let` restricts scope to inside the block', () => {
if (true) {
let letX = true;
}
assert.throws(() => console.log(letX));
});
});
describe('`let` usage', () => {
it('`let` use in `for` loops', () => {
let obj = {x: 1};
for (let key in obj) {}
assert.throws(() => console.log(key));
});
it('create artifical scope, using curly braces', () => {
{
let letX = true;
}
assert.throws(() => console.log(letX));
});
});
});
// 8: block scope - const
// To do: make all tests pass, leave the asserts unchanged!
// Follow the hints of the failure messages!
describe('`const` is like `let` plus read-only', () => {
describe('scalar values are read-only', () => {
it('e.g. a number', () => {
const constNum = 0;
// constNum = 1;
assert.equal(constNum, 0);
});
it('or a string', () => {
const constString = 'I am a const';
// constString = 'Cant change you?';
assert.equal(constString, 'I am a const');
});
});
const notChangeable = 23;
it('const scope leaks too', () => {
assert.equal(notChangeable, 23);
});
describe('complex types are NOT fully read-only', () => {
it('array`s items can be changed', () => {
const arr = [];
arr[0] = 42;
assert.equal(arr[0], 42);
});
it('object`s can be modified', () => {
const obj = {x: 1};
obj.x = 3;
assert.equal(obj.x, 3);
});
});
});
// 9: object-literals - basics
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('The object literal allows for new shorthands', () => {
const x = 1;
const y = 2;
describe('with variables', () => {
it('the short version for `{x: x}` is {x}', () => {
const short = {y};
assert.deepEqual(short, {y: y});
});
it('works with multiple variables too', () => {
const short = {x, y};
assert.deepEqual(short, {x: x, y: y});
});
});
describe('with methods', () => {
const func = () => func;
it('using the name only uses it as key', () => {
const short = {func};
assert.deepEqual(short, {func: func});
});
it('a different key must be given explicitly, just like before ES6', () => {
const short = {otherKey: func};
assert.deepEqual(short, {otherKey: func});
});
it('inline functions, can written as `obj={func(){}}` instead of `obj={func:function(){}}`', () => {
const short = {
inlineFunc: () => 'I am inline'
};
assert.deepEqual(short.inlineFunc(), 'I am inline');
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment