Skip to content

Instantly share code, notes, and snippets.

@jaromb
Last active July 28, 2019 21:32
Show Gist options
  • Save jaromb/f858bca0ebf387df2861bd289fe00d7e to your computer and use it in GitHub Desktop.
Save jaromb/f858bca0ebf387df2861bd289fe00d7e to your computer and use it in GitHub Desktop.
ES6 Katas solutions (from es6katas.org)
// 52: Generator - Send value to a generator
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Pass a value to a generator', () => {
it('basics: get the values from a generator in two ways', function() {
function* generatorFunction() {
yield 1;
yield 2;
}
// way #1
var convertedToAnArray = Array.from(generatorFunction());
// way #2
var iterator = generatorFunction();
// var iteratedOver = [iterator.next().___, iterator.___];
var iteratedOver = [iterator.next().value, iterator.next().value];
assert.deepEqual(convertedToAnArray, iteratedOver);
});
it('pass a value to the iterator', function() {
function* generatorFunction() {
// yield 1;
// yield param;
var param = yield 1;
yield param;
}
var iterator = generatorFunction();
var iteratedOver = [iterator.next().value, iterator.next(2).value];
assert.deepEqual([1, 2], iteratedOver);
});
it('a value passed to the 1st `next()` call is ignored', function() {
function* generatorFunction() {
// yield 1;
// declare param, with the first yield equal to 1, then yield the
// given param in following
var param = yield 1;
yield param;
}
let iterator = generatorFunction();
const values = [
iterator.next('irrelevant').value,
iterator.next(2).value
];
assert.deepEqual(values, [1, 2]);
});
});
// 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 [firstValue, secondValue, lastValue] = [1, 2, 3];
assert.strictEqual(lastValue, 3);
});
it('swap two variables, in one operation', () => {
let [x, y] = ['ax', 'why'];
[y, x] = [x, y];
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 [[0, 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']);
// values inside array pair with values of the same index in the string
});
it('missing characters are undefined', () => {
const [a,, c] = 'ab';
assert.equal(c, void 0);
// undefined because there is no letter in the same index of the string as that index in the array
});
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 {magic: {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, y: 2};
assert.equal(z, void 0);
});
it('destructure from builtins (string)', () => {
const {substr} = '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`', () => {
// indicates the first value in the array should equal 1
const [a=1] = [];
assert.equal(a, 1);
});
it('for a missing value', () => {
// gives default for the 1st index value where no value exists
const [,b=2,] = [1,,3];
assert.equal(b, 2);
});
it('in an object', () => {
// can define defaults in objects like in arrays (this problem initially given with const [a,b=2])
const {a, b=2} = {a: 1};
assert.equal(b, 2);
});
it('if the value is undefined', () => {
// adding a value for b in the const sets the undefined b in object to the default given
const {a, b=2} = {a: 1, b: void 0};
assert.strictEqual(b, 2);
});
it('also a string works with defaults', () => {
// adding a to the array before b sets a equal to 1 as both are 0th index in array and string, respectively
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}) => {
// using params in curly braces brings in params matching those names from object
assert.equal(id, 42);
assert.equal(name, 'Wolfram');
};
const user = {name: 'Wolfram', id: 42, param3: 3, param4: 'four'};
fn(user);
});
it('multiple params from array/object', () => {
//indicate place in the array that you want to access value, skipping first value
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);
//param not stated in function call, so default for name is 'Bob'
});
it('for a missing array value', () => {
const defaultUser = {id: 23, name: 'Joe'};
const fn = ([user=defaultUser]) => {
// sets user equal to a given value in case value no stated in function call
assert.deepEqual(user, defaultUser);
};
fn([]);
})
it('mix of parameter types', () => {
const fn = (id=1, [arr=2], {obj=3}) => {
// sets values for default of each of these params in case there is no value
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, y: 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]: () => '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 = 'Key';
const obj = {['proper' + 'tyName']: null};
assert('propertyName' in obj);
});
it('accessor keys can be computed names too', () => {
const obj = {
get ['key']() {return 1},
};
assert.equal(obj.key, 1);
});
});
// 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 money() { return Infinity; }
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 = 42;
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 [getterName]() { return -Infinity; }
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; }
set [propertyName](amount) { this.amount = 23 }
}
const account = new MyAccount();
account.balance = 42;
// not sure why this call to account.balance doesn't change the balance
assert.equal(account.balance, 23);
});
});
});
// 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() {}
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;
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;
let values = '';
for (let value of iterable) {
values += value;
}
assert.equal(values, '');
});
it('has no `.length` property', function() {
const hasLengthProperty = !! iterable.length;
// !! to determine if length in this case is truthy or falsy
// returns false because there is no length property (undefined)
// which coerces to false
assert.equal(hasLengthProperty, false);
});
describe('can be converted to an array', function() {
it('using `Array.from()`', function() {
// const arr = iterable;
const arr = Array.from(iterable);
assert.equal(Array.isArray(arr), true);
});
it('where `.length` is still 0', function() {
// const arr = iterable;
const arr = Array.from(iterable);
const length = arr.length;
assert.equal(length, 0);
});
});
});
});
// 40: iterator - one example usage. Build an iterable and use it with some built-in ES6 constructs.
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
// The example is a consumable users ("consumable" is in the name just to make it
// more explicit that the generator will consume them):
// - `consumableUser` contains a consumable user,
// - `anyLeft` tells if there is any user left that can be consumed.
class ConsumableUsers {
constructor() {
this.users = ['Alice', 'Bob'];
}
get nextUser() {
if (this.users.length > 0) {
return `user: ${this.users.shift()}`;
}
return void 0;
}
get anyLeft() {
return this.users.length > 0;
}
}
describe('Iterator usages', () => {
let usersIterable;
beforeEach(function(){
const consumableUsers = new ConsumableUsers();
function iteratorFunction() {
return {
next: function() {
// unsure why the below is necessary? why does defining this
// constant in place of using consumableUsers.anyLeft in the
// object directly change the resultant value that goes into done?
const anyLeft = consumableUsers.anyLeft
return {value: consumableUsers.nextUser, done: !anyLeft}
}
}
}
// usersIterable = {}
usersIterable = {[Symbol.iterator]: iteratorFunction};
});
describe('create an iterator/iterable', function() {
it('the `usersIterable` should be iterable', function() {
const isIterable = Symbol.iterator in usersIterable;
assert.equal(isIterable, true);
});
it('the iterator of `usersIterable` should return an object', function() {
const iterator = usersIterable[Symbol.iterator]();
assert.equal(typeof iterator, 'object');
});
it('the iterator of `usersIterable` should have a next function', function() {
const iterator = usersIterable[Symbol.iterator]();
assert.equal(typeof iterator.next, 'function');
});
});
describe('fill the iterable with content using `ConsumableUsers`', function() {
describe('using the iterator', function() {
let iterator;
beforeEach(function(){
iterator = usersIterable[Symbol.iterator]();
});
it('should return `Alice` as first user', function() {
const firstItem = iterator.next();
assert.deepEqual(firstItem, {value: "user: Alice", done: false});
});
it('should return `Bob` as second user', function() {
iterator.next(); // drop the first item
const secondItem = iterator.next();
assert.deepEqual(secondItem, {value: "user: Bob", done: false});
});
it('should return `done:true`, which means there are no more items', function() {
iterator.next();
// iterator.____();
iterator.next();
const beyondLast = iterator.next();
assert.deepEqual(beyondLast, {value: void 0, done: true});
})
});
describe('using built-in constructs', function() {
it('use `Array.from()` to convert an iterable to an array', function() {
// const users = usersIterable;
const users = Array.from(usersIterable);
assert.deepEqual(users, ['user: Alice', 'user: Bob']);
});
it('use for-of to loop over an iterable', function() {
const users = [];
// for (let user in usersIterable) users.push(user);
for (let user of usersIterable) users.push(user);
// for-of is specifically used to access values of iterable objects,
// where for-in returns a list of keys on the iterated object
assert.deepEqual(users, ['user: Alice', 'user: Bob']);
});
it('use the spread-operator to convert/add iterable to an array', function() {
// const users = ['noname', usersIterable];
const users = ['noname', ...usersIterable];
assert.deepEqual(users, ['noname', 'user: Alice', 'user: Bob']);
});
it('destructure an iterable like an array', function() {
// const {firstUser, secondUser} = usersIterable;
const [firstUser, secondUser] = usersIterable;
assert.equal(firstUser, 'user: Alice');
assert.equal(secondUser, 'user: Bob');
})
});
});
});
This one is broken right now
// 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 = `like a string`;
// backticks can surround a string just like quotation marks would surround a string,
// and then within that template string you can now also employ variables
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}`;
// placing a ${} around a variable allows the variable in template string to be evaluated
assert.equal(evaluated, 'x=' + x);
});
it('multiple variables get evaluated too', function() {
var evaluated = `${x}+${y}`;
// multiple variables can each be evaluated by wrapping with ${}
assert.equal(evaluated, x + '+' + y);
});
});
describe('can evaluate any expression, wrapped inside "${...}"', function() {
it('all inside "${...}" gets evaluated', function() {
var evaluated = `${ x + y }`;
// no need for wrapping each individual variable in additional `` and ${}
// if you simply want to evaluate a whole expression
assert.equal(evaluated, x+y);
});
it('inside "${...}" can also be a function call', function() {
function getDomain(){
return document.domain;
}
var evaluated = `${ getDomain() }`;
// include function inside `${}` and specify function call with trailing parentheses
assert.equal(evaluated, 'tddbin.com');
});
});
});
// 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`;
// new lines are designated using backslash with n, e.g. "\n"
// above code creates 3 lines: line1 with text 'line1', line 2 with no text, and line3 with text
'line3'
assert.equal(normalString, 'line1\n\nline3');
});
it('even over more than two lines', function() {
var multiline = `\n\n\n`;
// above code creates 4 lines with all lines being blank
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}`;
// expressions can be included along with instances of new lines
assert.equal(multiline, 'line 1\n\n 42');
});
it('also here spaces matter', function() {
var multiline = `\n\n42`;
assert.equal(multiline, '\n\n42');
});
});
});
// 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 tagFunction(s) {
return s.toString();
}
var evaluated = tagFunction `template string`;
assert.equal(evaluated, 'template string');
});
//function calls performed with backticks where expressions are involved instead of calling with ()
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 = tagFunction`template string`;
assert.deepEqual(tagFunction`template string`, result);
});
// calls tagFunction with strings === 'template string'
it('expressions are NOT passed to it', function() {
var tagged = tagFunction`one${23}two`;
assert.deepEqual(tagged, ['one', 'two']);
});
// expression not passed, so it is skipped and next portion of the string taken
});
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);
// returning 'strings' would return the string components, 'firstValue' (the second param) returns the first string literal expression
});
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);
// secondValue, the third param, returns the second string literal expression
});
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]);
//using rest syntax ...allValues (name is interchangeable) indicates a return of all values from string literal expressions
});
});
});
});
// 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[0];
return lineBreak[0];
}
assert.equal(firstCharEntered`\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.raw`\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 = () => ({iAm: 'an object'});
// parentheses indicate the curly brackets inside enclose 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;
// instead of having getFunction return 'new LexicallyBound' which returns a new instance of the class, returning 'this' references the specific object which contains the return statement
}
}
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();
// bound.getFunction() returns "this", which is the 'new LexicallyBound()' object stored in the variable bound
assert.strictEqual(fn(), bound);
});
it('can NOT bind a different context', function() {
var bound = new LexicallyBound();
var fn = bound.getFunction();
var anotherObj = {};
var expected = bound;
assert.strictEqual(fn.call(anotherObj), expected);
/* fn.call(this) attempts to bind the object in anotherObj as 'this' in getFunction(), but this doesn't work in an arrow function as you can't bind to a different context. With a function() statement, you can bind and the test passes with var expected set to anotherObj */
});
it('`arguments` does NOT work inside arrow functions', function() {
var bound = new LexicallyBound();
var fn = bound.getArgumentsFunction();
assert.equal(fn(1, 2).length, 0);
/* with getArguments set to a function() call, the length comes out as 0, but since you can't access arguments in an arrow function the length would come out to 0 as arguments comes back undefined */
});
});
// 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; ***can't change a read-only variable
assert.equal(constNum, 0);
});
it('or a string', () => {
const constString = 'I am a const';
// constString = 'Cant change you?'; *** still can't change
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] = 0;
arr[0] = 42
assert.equal(arr[0], 42);
});
it('object`s can be modified', () => {
const obj = {x: 1};
obj.x = 2;
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};
// when the key value pair share the same value, it can be abbreviated to that value
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() {return 'I am inline'}
};
// function can be accessed using its name as the key rather than explicitly defining a key
assert.deepEqual(short.inlineFunc(), 'I am inline');
});
});
});
// 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;
const nuclear = '\u2622';
assert.equal(nuclear, '☢');
});
it('value is 4 bytes/digits', () => {
// const nuclear = '\u26222';
const nuclear = '\u2622';
assert.equal(`no more ${nuclear}`, 'no more ☢');
});
it('even "normal" character`s values can be written as hexadecimal unicode', () => {
// const nuclear = `\u006B\u006A more \u2622`;
// n = \u006E, o = \u006F
const nuclear = `\u006E\u006F more \u2622`;
assert.equal(nuclear, 'no more ☢');
});
it('curly braces may surround the value', () => {
// const nuclear = `\u{0000000006E}\u00006F more \u2622`;
const nuclear = `\u{0000000006E}\u{00006F} 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, veryLast) => {
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) => {
const fn = (firstParam, secondParam, ...rest) => {
assert.deepEqual([3,4], rest);
};
fn(null, 2, 3, 4);
});
it('makes `arguments` obsolete', () => {
// const fn = () => {
const fn = (...args) => {
assert.deepEqual([42, 'twenty three', 'win'], args);
};
fn(42, 'twenty three', 'win');
});
it('eliminate `arguments`!!!', () => {
// const fn = () => arguments;
// function defined to return all the args that are input,
// second const defines 'rest' as all args after the first arg
// so fn(1,2,3) gives 3 args, of which rest is defined as the last 2
const fn = (...args) => args;
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, last] = [1, 2, 3, 4];
const [...all] = [1, 2, 3, 4];
assert.deepEqual(all, [1, 2, 3, 4]);
});
it('assign rest of an array to a variable', () => {
// const [...all] = [1, 2, 3, 4];
const [first, ...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]];
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);
const date = new Date(...theDate);
// when 'new Date' takes in an array, the second value is the month
// number, but when taking 3 values the first arg is year, second
// is monthIndex (0 for Jan, 1 for Feb, ...), third is day. Arrays
// taken in are handled as strings, e.g. '2015,1,1' producing Jan 1 2015
// where handling as multiple args produced Feb 1 2015
assert.deepEqual(new Date(2015, 1, 1), date);
});
});
// 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];
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];
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]];
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];
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, 43]);
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];
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 [b, a] = [...'ab'];
const [b, a] = [...'ba'];
assert.equal(a, 'a');
assert.equal(b, 'b');
});
it('expands any kind of character', function() {
// const arr = [...'12'];
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'];
const letters = ['a', ...'bcd', 'e', 'f'];
assert.equal(letters.length, 6);
});
it('don`t confuse with the rest operator', function() {
// const [...rest] = ['1234', ...'5'];
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');
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() {
// let TestClass;
let TestClass = class XXX {};
const instance = new TestClass();
assert.equal(typeof instance, 'object');
});
it('a class is block scoped', () => {
// class Inside {} returns undefined now because class is defined within a
// local code block, not globally
{class Inside {}}
assert.equal(typeof Inside, 'undefined');
});
it('the `constructor` is a special method', function() {
class User {
// constructor(id) {}
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 {
// method below originally not defined in tests, method can be
// defined like so, but may more typically include reference to
// params in the constructor (as shown for constructor above)
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 {
constructor() {this.everWroteATest = false;}
// constructor has been added to define default value to everWroteATest
wroteATest() { this.everWroteATest = true; }
// isLazy() { }
isLazy() { return !this.everWroteATest }
// tester is lazy if never wrote a test, not lazy if it has
}
const tester = new User();
assert.equal(tester.isLazy(), true);
tester.wroteATest();
assert.equal(tester.isLazy(), false);
});
it('anonymous class', () => {
// const classType = typeof {};
const classType = typeof class {};
assert.equal(classType, 'function');
});
});
// 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 {
// makeTest() { return new UnitTest(); }
static makeTest() { return new UnitTest(); }
}
assert.ok(TestFactory.makeTest() instanceof UnitTest);
});
it('the method name can be dynamic/computed at runtime', () => {
// const methodName = 'makeTest';
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 {
// get testType() { return 'unit'; }
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 {
// get type() { return 'integration'; }
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`', () => {
// let A
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 {}
class A extends Object {}
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 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', () => {
// let A;
class A extends Object {}
class B extends A {}
assert.equal(new B() instanceof A, true);
});
it('extend over multiple levels', () => {
class A {}
// empty line, replaced by line below
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', () => {
// let A;
function 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(null);
const isIt = A.isPrototypeOf(B);
assert.equal(isIt, true);
});
it('A`s prototype is also B`s prototype', () => {
// const proto = B;
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', () => {
let A;
// class B extends (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 ? null : class {};
// class B extends returnParent() {}
class B extends returnParent(true) {}
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 super; }}
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 {hasSuper() { return super.hasSuper; }}
class B extends A {hasSuper() { return super.hasSuper(); }}
assert.equal(new B().hasSuper(), true);
});
it('when overridden a method does NOT automatically call its super method', () => {
class A {hasSuper() { return true; }}
// class B extends A {hasSuper() { return 'nothing'; }}
class B extends A {hasSuper() { return undefined; }}
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 {iAmSuper() { return iAmSuper(); }}
class C extends B {iAmSuper() { return super.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.constructor; }}
class B extends A {getMethod() { return super.constructor(); }}
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 {
// constructor() {
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();
super(42,2);
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 = 2;
super.inc();
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.konstructer;
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 = arrayLike;
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 = domNode.classList;
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 = document.createElement('span');
// const divs = nodeList.filter((node) => node.tagName === 'div');
const divs = Array.from(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);
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}=${value}`);
const arr = Array.from(arrayLike, (value, key) => `${key}=${value}`);
assert.deepEqual(arr, ['0=one', '1=two']);
});
});
});
// 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(10);
const arr = Array.of(10);
assert.deepEqual(arr, [10]);
});
it('puts all arguments into array elements', () => {
// const arr = Array.of();
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);
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();
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 = [undefined].fill(0);
const arr = [].fill(0);
assert.deepEqual(arr, []);
});
it('second parameter to `fill()` is the position where to start filling', function() {
// const fillPosition = 0;
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 = 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(true)
const found = [true].find(x => x= true);
assert.equal(found, true);
});
it('returns the first value found', function() {
// const found = [0, 1].find(item => item > 1);
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 === 2);
const found = [1, 2, 3].find(item => item === 4);
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);
const found = [bob, alice].find(({name: value}) => value === '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);
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);
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 > 1);
const foundAt = [1, 2, 3].findIndex(item => item > 3);
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(index, arr) {
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}}) => length > 3);
const foundAt = [bob, alice].findIndex(({name:value}) => value.length > 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 = someNameSpace.Symbol;
const expected = window.Symbol;
assert.equal(Symbol, expected);
});
it('every `Symbol()` is unique', function(){
const sym1 = Symbol();
// const sym2 = sym1;
const sym2 = Symbol();
assert.notEqual(sym1, sym2);
});
it('every `Symbol()` is unique, also with the same parameter', function(){
var sym1 = Symbol('foo');
// var sym1 = Symbol('foo');
var sym2 = Symbol('foo');
assert.notEqual(sym1, sym2);
});
it('`typeof Symbol()` returns "symbol"', function(){
// const theType = typeof Symbol;
const theType = typeof Symbol();
assert.equal(theType, 'symbol');
});
it('`new Symbol()` throws an exception, to prevent creation of Symbol wrapper objects', function(){
function fn() {
// Symbol();
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 = Symbol.for('symbol name');
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 symbol1');
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.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('').toString();
var localSymbol = Symbol('new symbol');
const description = localSymbol.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 1').toString();
// const symbol2AsString = Symbol.for('new symbol').toString();
const symbol2AsString = Symbol.for('new symbol 1').toString();
assert.equal(symbol1AsString, symbol2AsString);
});
it('but the symbols are not the same!', function() {
// const symbol1 = Symbol.for('new symbol');
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.____(Symbol.for('foo'));
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.for('foo');
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);
const key = Symbol.keyFor(Symbol.iterator);
assert.equal(key, void 0);
});
it('for non-Symbols throws an error', function() {
function fn() {
// Symbol.keyFor(Symbol.for('foo'));
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 = '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--;
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.___();
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;
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;
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 = s;
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.to____();
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.___();
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(){
const str = '';
const iterator = str[Symbol.iterator]();
iterator.next();
assert.equal(iterator.next().done, true);
});
});
});
// 41: array - entries
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`[].entries()` returns an iterator object with all entries', function() {
it('returns key+value for each element', function() {
const arr = ['a', 'b', 'c'];
const entriesAsArray = Array.from(arr.entries());
assert.deepEqual(entriesAsArray, [[0,"a"], [1,"b"], [2,"c"]]);
});
it('empty elements contain the value `undefined`', function() {
const arr = ['one'];
arr[2] = 'three';
// const secondValue = arr.entries();
const secondValue = Array.from(arr.entries())[1];
assert.deepEqual(secondValue, [1, void 0]);
});
describe('returns an iterable', function() {
it('has `next()` to iterate', function() {
const arr = ['one'];
// const value = arr;
const next = arr.entries().next()
// arr.entries().next() returns { value: [0, 'one'], done: false }
const value = next.value
assert.deepEqual(value, [0, 'one']);
});
});
});
// 42: array - `Array.prototype.keys`
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Array.prototype.keys` returns an iterator for all keys in the array', () => {
it('`keys()` returns an iterator', function() {
// const arr = ['a', 'b'];
const arr = ['a'];
const iterator = arr.keys();
assert.deepEqual(iterator.next(), {value: 0, done: false});
assert.deepEqual(iterator.next(), {value: void 0, done: true});
});
it('gets all keys', function() {
// const arr = ['a', 'b'];
const arr = ['a', 'b', 'c'];
const keys = Array.from(arr.keys());
assert.deepEqual(keys, [0, 1, 2]);
});
it('empty array contains no keys', function() {
// const arr = ['empty me'];
const arr = [];
const keys = [...arr.keys()];
assert.equal(keys.length, 0);
});
it('a sparse array without real values has keys though', function() {
const arr = [,,];
// const keys = [...arr.___()];
const keys = [...arr.keys()];
assert.deepEqual(keys, [0, 1]);
});
it('also includes holes in sparse arrays', function() {
const arr = ['a', , 'c'];
// const keys = arr.keys;
const keys = Array.from(arr.keys());
assert.deepEqual(keys, [0, 1, 2]);
});
});
// 43: array - `Array.prototype.values`
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Array.prototype.values` returns an iterator for all values in the array', () => {
it('`values()` returns an iterator', function() {
// const arr = ['k', 'e', 'y'];
const arr = [];
const iterator = arr.values();
assert.deepEqual(iterator.next(), {value: void 0, done: true});
});
it('use `iterator.next()` to drop first value', function() {
const arr = ['keys', 'values', 'entries'];
const iterator = arr.values();
// iterator.___();
iterator.next();
assert.deepEqual([...iterator], ['values', 'entries']);
});
it('empty array contains no values', function() {
// const arr = [...[...[...[...'1']]]];
const arr = [...[...[...[]]]];
const values = [...arr.values()];
assert.equal(values.length, 0);
});
it('a sparse array without real values has values though', function() {
// const arr = [, 0];
const arr = [, void 0];
const keys = [...arr.values()];
assert.deepEqual(keys, [void 0, void 0]);
});
it('also includes holes in sparse arrays', function() {
// const arr = ['a',];
const arr = ['a',, 'c'];
assert.deepEqual([...arr.values()], ['a', void 0, 'c']);
});
});
// 44: Map - basics
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Map` is a key/value map', function(){
it('`Map` is a new global constructor function', function() {
// const typeOfMap = '???';
const typeOfMap = 'function';
assert.equal(typeof Map, typeOfMap);
});
it('provides `new Map().set()` to add key+value pair, `get()` to read it by key', function() {
let map = new Map();
// map.set('key', null);
// const value = map.get();
map.set('key', 'value');
const value = map.get('key');
assert.equal(value, 'value');
});
it('`has()` tells if map has the given key', function() {
let map = new Map();
map.set('key', 'value');
// const hasIt = map.hazz;
const hasIt = map.has('key')
assert.equal(hasIt, true);
});
it('a map is iterable', function() {
let map = new Map();
map.set('1', 'one');
map.set('2', 'two');
// const mapAsArray = map; // hint: kata #29 http://tddbin.com/#?kata=es6/language/array-api/from
const mapAsArray = Array.from(map);
assert.deepEqual(mapAsArray, [['1', 'one'], ['2', 'two']]);
});
it('complex types can be keys', function() {
const obj = {x: 1};
const otherObj = {x: 1};
let map = new Map();
map.set(obj, '');
// map.set(otherObj, '');
assert.equal(map.has(otherObj), false);
});
});
// 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({}, 'object is key');
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(___);
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();
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();
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(function() {}, 'the real noop');
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', 'value3');
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')
// chain to add key 3, value 'three'
.set(3, 'three')
;
assert.deepEqual([...map.keys()], [1,2,3]);
assert.deepEqual([...map.values()], ['one', 'two', 'three']);
});
});
// 47: Set - basics
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Set` lets you store unique values of any type', function(){
it('`Set` is a new global constructor function', function() {
// const typeOfSet = 'function';
const typeOfSet = 'function';
assert.equal(typeof Set, typeOfSet);
});
it('every value in a set is unique', function() {
let set = new Set();
set.add(1);
// set.add(1); value must be unique
set.add(2);
const expectedSize = 2;
assert.equal(set.size, expectedSize);
});
it('the string "1" is different to the number 1', function() {
let set = new Set();
set.add(1);
// string '1' is of a different type than integer 1, so still unique
set.add('1')
assert.equal(set.size, 2);
});
it('even NaN is equal to NaN', function() {
let set = new Set();
set.add(NaN);
// set.add(Na);
set.add(NaN);
assert.equal(set.size, 1);
});
it('+0 and -0 are seen as equal', function() {
let set = new Set();
set.add(+0);
set.add(0);
// set.add('-0');
set.add(-0);
assert.deepEqual([...set.values()], [+0]);
});
});
// 48: Set - add
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`add()` appends a new element to the end of a Set object.', function(){
let set;
beforeEach(() => set = new Set());
it('adds every value, of any type, only once', function() {
const fn = () => {};
set.add(1);
set.add(1);
set.add(fn);
// set.add({fn});
set.add(fn);
assert.equal(set.size, 2);
});
it('is chainable', function() {
// set.add.add;
set.add(1).add(2);
assert.equal(set.has(2), true);
});
it('call without params adds undefined', function() {
// set.add
set.add()
assert.equal(set.has(void 0), true);
});
it('0, -0 and +0 are equal', function() {
// set.add();
// set.add();
set.add(0);
set.add(+0);
set.add(-0);
assert.equal(set.has(+0), true);
});
});
// 49: Generator - creation
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Generators can be created in multiple ways', function() {
it('the most common way is by adding `*` after `function`', function() {
// function g() {}
function * g() {}
assertIsGenerator(g());
});
it('as a function expression, by adding a `*` after `function`', function() {
// let g = function() {};
let g = function*() {};
assertIsGenerator(g());
});
it('inside an object by prefixing the function name with `*`', function() {
let obj = {
// g() {}
*g() {}
};
assertIsGenerator(obj.g());
});
it('computed generator names, are just prefixed with a `*`', function() {
const generatorName = 'g';
let obj = {
// [generatorName]() {}
*[generatorName]() {}
};
assertIsGenerator(obj.g());
});
it('inside a class the same way', function() {
const generatorName = 'g';
class Klazz {
// [generatorName]() {}
*[generatorName]() {}
}
assertIsGenerator(new Klazz().g());
});
function assertIsGenerator(gen) {
const toStringed = '' + gen;
assert.equal(toStringed, '[object Generator]');
}
});
// 50: Generator - iterator
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Generators returns an iterable object', function() {
function* generatorFunction(){
yield 1;
yield 2;
}
let generator;
beforeEach(() => {
generator = generatorFunction();
});
it('a generator returns an object', function() {
// const typeOfTheGenerator = '';
const typeOfTheGenerator = 'object';
assert.equal(typeof generator, typeOfTheGenerator);
});
it('a generator object has a key `Symbol.iterator`', function() {
// const key = '???';
const key = Symbol.iterator;
assert.equal(key in generator, true);
});
it('the `Symbol.iterator` is a function', function() {
// const theType = typeof generator.Symbol.iterator;
const theType = typeof generator[Symbol.iterator];
assert.equal(theType, 'function');
});
it('can be looped with `for-of`, which expects an iterable', function() {
function iterateForOf(){
// for (let value of {}) {
for (let value of generator) {
// no statements needed
}
}
assert.doesNotThrow(iterateForOf);
});
});
// 51: Generator - Yield Expressions
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Generator - `yield` is used to pause and resume a generator function', () => {
function* generatorFunction() {
yield 'hello';
yield 'world';
}
let generator;
beforeEach(function() {
generator = generatorFunction();
});
it('converting a generator to an array (using `Array.from`) resumes the generator until all values are received', () => {
// let values = Array.from(generatorFunction);
let values = Array.from(generatorFunction());
assert.deepEqual(values, ['hello', 'world']);
});
describe('after the first `generator.next()` call', function() {
it('the value is "hello"', function() {
// const {value} = generator.next;
const {value} = generator.next();
assert.equal(value, 'hello');
});
it('and `done` is false', function() {
// const {done} = generator;
const {done} = generator.next();
assert.equal(done, false);
});
});
describe('after the second `next()` call', function() {
let secondItem;
beforeEach(function() {
//
generator.next()
// needs next to pass first iteration, on to second
secondItem = generator.next();
});
it('`value` is "world"', function() {
let {value} = secondItem;
assert.equal(value, 'world');
});
it('and `done` is still false', function() {
const {done} = secondItem;
assert.equal(done, false);
});
});
describe('after stepping past the last element, calling `next()` that often', function() {
it('`done` property equals true, since there is nothing more to iterator over', function() {
generator.next();
generator.next();
// let done = generator.done;
let {done} = generator.next();
assert.equal(done, true);
});
});
});
// 53: Map - initialize
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('initialize a `Map`', function(){
it('a `new Map()` is empty, has size=0', function() {
// const mapSize = new Map()
const mapSize = new Map().size;
assert.equal(mapSize, 0);
});
it('init Map with `[[]]` has a size=1', function() {
// const mapSize = new Map().size;
const mapSize = new Map([[]]).size;
assert.equal(mapSize, 1);
});
it('init a Map with `[[1]]` is the same as `map.set(1, void 0)`', function() {
// let map1 = new Map();
let map1 = new Map([[1]]);
let map2 = new Map().set(1, void 0);
assertMapsEqual(map1, map2);
});
it('init Map with multiple key+value pairs', function() {
const pair1 = [1, 'one'];
const pair2 = [2, 'two'];
// const map = new Map();
const map = new Map([pair1, pair2]);
assertMapsEqual(map, new Map().set(...pair1).set(...pair2));
});
it('keys are unique, the last one is used', function() {
const pair1 = [1, 'one'];
const pair2 = [1, 'uno'];
const pair3 = [1, 'eins'];
const pair4 = [2, 'two'];
// const map = new Map([pair3, pair1, pair2, pair4]);
const map = new Map([pair2, pair1, pair3, pair4]);
assertMapsEqual(map, new Map().set(...pair3).set(...pair4));
});
it('init Map from an Object, is a bit of work', function() {
let map = new Map();
const obj = {x: 1, y: 2};
const keys = Object.keys(obj);
// keys.forEach(key => map.set(key));
keys.forEach(key => map.set(key, obj[key]));
const expectedEntries = [['x', 1], ['y', 2]];
assertMapsEqual(map, expectedEntries);
});
});
function mapToArray(map) {
return Array.from(map);
}
function assertMapsEqual(map1, map2) {
assert.deepEqual(mapToArray(map1), mapToArray(map2));
}
// ES6 - 54: Object - is
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Object.is()` determines whether two values are the same', function(){
describe('scalar values', function() {
it('1 is the same as 1', function() {
// const areSame = Object.is(1, '???');
const areSame = Object.is(1, 1);
assert(areSame);
});
it('int 1 is different to string "1"', function() {
// const areSame = Object.___(1, '1');
const areSame = Object.is(1, '1');
assert(areSame === false);
});
it('strings just have to match', function() {
// const areSame = Object.is('one', 'two');
const areSame = Object.is('one', 'one');
assert(areSame);
});
it('+0 is not the same as -0', function() {
// const areSame = -1;
const areSame = false;
assert.equal(Object.is(+0, -0), areSame);
});
it('NaN is the same as NaN', function() {
// const number = 0;
const number = 0/0; // 0/0 === NaN
assert.equal(Object.is(NaN, number), true);
});
});
describe('coercion, as in `==` and `===`, does NOT apply', function() {
it('+0 != -0', function() {
// const coerced = +0 === -0;
const coerced = +0 !== -0;
const isSame = Object.is(+0, -0);
assert.equal(isSame, coerced);
});
it('empty string and `false` are not the same', function() {
const emptyString = '';
// const isSame = Object.is(emptyString, false);
const isSame = !Object.is(emptyString, false);
assert.equal(isSame, emptyString == false);
});
it('NaN', function() {
const coerced = NaN !== NaN;
const isSame = Object.is(NaN, NaN);
assert.equal(isSame, coerced);
});
it('NaN 0/0', function() {
const isSame = Object.is(NaN, 0/0);
assert.equal(isSame, true);
});
});
describe('complex values', function() {
it('`{}` is just not the same as `{}`', function() {
const areSame = {} == {};
assert(Object.is({}, {}) === areSame);
});
it('Map', function() {
let map1 = new Map([[1, 'one']]);
let map2 = new Map([[1, 'one']]);
const areSame = Object.is(map1, map2);
assert.equal(areSame, false);
});
});
});
// 55: Number - isInteger
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Number.isInteger()` determines if a value is an integer', function(){
it('`isInteger` is a static function on `Number`', function() {
// const whatType = 'method';
const whatType = 'function';
assert.equal(typeof Number.isInteger, whatType);
});
describe('zero in different ways', function() {
it('0 is an integer', function() {
// const zero = null;
const zero = 0;
assert(Number.isInteger(zero));
});
it('0.000', function() {
// const veryZero = 0.000001;
const veryZero = 0.000000;
assert(Number.isInteger(veryZero));
});
it('the string "0" is NOT an integer', function() {
// const stringZero = 0;
const stringZero = '0';
assert(Number.isInteger(stringZero) === false);
});
});
describe('one in different ways', function() {
it('0.111 + 0.889', function() {
// const rest = 0.88;
const rest = 0.889;
assert(Number.isInteger(0.111 + rest));
});
it('0.5 + 0.2 + 0.2 + 0.1 = 1 ... isn`t it?', function() {
// const oneOrNot = 0.5 + 0.2 + 0.3; evaluates to 1
const oneOrNot = 0.5 + 0.2 + 0.2 + 0.1; // evaluates to 1.09999...
assert(Number.isInteger(oneOrNot) === false);
});
it('parseInt`ed "1" is an integer', function() {
// const convertedToInt = Number.parse('1.01');
const convertedToInt = Number.parseInt('1.01');
assert(Number.isInteger(convertedToInt));
});
});
describe('what is not an integer', function() {
it('`Number()` is an integer', function() {
// const numberOne = Number;
const numberOne = Number();
assert(Number.isInteger(numberOne));
});
it('`{}` is NOT an integer', function() {
// const isit = Number.isWhat({});
const isit = Number.isInteger({});
assert(isit === false);
});
it('`0.1` is not an integer', function() {
// const isit = Number.isInteger(0.1);
const isit = Number.isInteger(0.1);
assert(isit === false);
});
it('`Number.Infinity` is not an integer', function() {
// const isit = Number.isInteger(Number.MAX_VALUE);
const isit = Number.isInteger(Number.Infinity);
assert(isit === false);
});
it('`NaN` is not an integer', function() {
// const isit = Number.isFloat(NaN);
const isit = Number.isInteger(NaN);
assert(isit === false);
});
});
});
// 56: Generator - Send function to a generator
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Pass a function to a generator', () => {
it('the generator can receive a function as a value', function() {
let fn = function() {};
function* generatorFunction() {
// can yield a function
yield fn;
assert.equal(yield null, fn); // remember, don't touch this line
}
let iterator = generatorFunction();
iterator.next();
iterator.next();
});
it('pass a function to the iterator, which calls it', function() {
function* generatorFunction() {
// yield (yield 1)();
yield (yield 1)(yield 2);
}
var iterator = generatorFunction();
var iteratedOver = [iterator.next().value, iterator.next().value];
assert.deepEqual([1, 2], iteratedOver);
});
it('nesting yielded function calls', function() {
function* generatorFunction() {
// yield (yield (yield 1)());
// }
// var iteratedOver = [];
yield (yield (yield 1)(yield (yield 2)(yield 3)));
}
var iterator = generatorFunction();
var iteratedOver = [iterator.next().value, iterator.next().value, iterator.next().value];
assert.deepEqual([1, 2, 3], iteratedOver);
});
});
// 57: Default parameters - basics
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('Default parameters make function parameters more flexible', () => {
it('define it using an assignment to the parameter `function(param=1){}`', function() {
// let number = (int) => int;
let number = (int=0) => int;
assert.equal(number(), 0);
});
it('it is used when `undefined` is passed', function() {
let number = (int = 23) => int;
// const param = 42;
const param = undefined;
// or simply when declared without value, e.g. let param
assert.equal(number(param), 23);
});
it('it is not used when a value is given', function() {
// function xhr() {
// return method;
// }
function xhr(method = 'PUT') {
return method;
}
assert.equal(xhr('POST'), 'POST');
});
it('it is evaluated at run time', function() {
// let defaultValue;
let defaultValue = 42;
function xhr(method = `value: ${defaultValue}`) {
return method;
}
assert.equal(xhr(), 'value: 42');
});
it('it can also be a function', function() {
// const defaultValue = 0;
const defaultValue = () => {
return 'defaultValue'
}
function fn(value = defaultValue()) {
return value;
}
assert.equal(fn(), 'defaultValue');
});
});
// 58: Reflect - basics
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Reflect` basics', function() {
describe('Reflect is special, it is different to e.g. `Object`', function() {
it('it`s of type object', function() {
// const expectedType = 'not a function!';
const expectedType = 'object';
assert.equal(typeof Reflect, expectedType);
});
it('it can not be instantiated (`new Reflect()`)', function() {
// const tryToConstruct = () => { Reflect; };
const tryToConstruct = () => { new Reflect; };
assert.throws(tryToConstruct, TypeError);
});
it('has no `call` method (as opposed to e.g. Object)', function() {
// const expected = 'function';
const expected = 'undefined';
assert.equal(typeof Reflect.call, expected);
});
});
describe('some `Reflect` usages', function() {
it('`Reflect.construct()` is like `new ClassName`', function() {
// let Class
class Class {};
assert.equal(Reflect.construct(Class, []) instanceof Class, true);
});
it('`Reflect.get()` returns a property`s value', function() {
// let obj = {x: 42};
let obj = {x: 23};
assert.equal(Reflect.get(obj, 'x'), 23);
});
it('`Reflect.has()` is like `in` just as a function', function() {
// let obj = {};
let obj = {x: 'value'};
assert.equal(Reflect.has(obj, 'x'), true);
});
});
});
// 59: Reflect - apply
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Reflect.apply` calls a target function', function() {
it('it is a static method', function() {
const expectedType = 'function';
assert.equal(typeof Reflect.apply, expectedType)
});
describe('the 1st parameter', () => {
it('is a callable, e.g. a function', () => {
// let fn;
let fn = () => { return 42 };
assert.equal(Reflect.apply(fn, void 0, []), 42);
});
it('passing it a non-callable throws a TypeError', function() {
const applyOnUncallable = () =>
// Reflect.apply(() => {}, void 0, []);
Reflect.apply({}, void 0, []);
assert.throws(applyOnUncallable, TypeError);
});
});
describe('the 2nd parameter', () => {
it('is the scope (or the `this`)', function() {
class FourtyTwo {
constructor() { this.value = 42}
fn() {return this.value}
}
let instance = new FourtyTwo();
// const fourtyTwo = Reflect.apply(instance.fn, ___, []);
const fourtyTwo = Reflect.apply(instance.fn, instance, []);
assert.deepEqual(fourtyTwo, 42);
});
});
describe('the 3rd parameter', () => {
it('must be an array (or array-like)', () => {
// const thirdParam = ['should be array-like'];
const thirdParam = [];
assert.doesNotThrow(() => Reflect.apply(() => void 0, null, thirdParam));
});
it('is an array of parameters passed to the call', function() {
// let emptyArrayWithFiveElements = Reflect.apply(Array);
// params are: callable item, scope, array (here indicating number of paramaters to be passed)
let emptyArrayWithFiveElements = Reflect.apply(Array, null, [5]);
assert.deepEqual(emptyArrayWithFiveElements.fill(42), [42, 42, 42, 42, 42]);
});
});
describe('example usages', () => {
it('simple function call', () => {
// const fn = () => ':(';
const fn = () => 'the return value';
assert.equal(Reflect.apply(fn, void 0, []), 'the return value');
});
it('call a function on an array', () => {
// const fn = [].push;
const fn = [].splice;
assert.deepEqual(Reflect.apply(fn, [0, 23, 42], [1]), [23, 42]);
});
it('pass in the `this` that the function to call needs', () => {
class Bob {
constructor() { this._name = 'Bob'; }
name() { return this._name; }
}
const bob = new Bob();
// const scope = Bob;
const scope = bob;
assert.equal(Reflect.apply(bob.name, scope, []), 'Bob');
});
});
});
// 60: Reflect - getPrototypeOf
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Reflect.getPrototypeOf` returns the prototype', function() {
it('works like `Object.getPrototypeOf`', function() {
const viaObject = Object.getPrototypeOf({});
// const viaReflect = Reflect.getPrototypeOf();
const viaReflect = Reflect.getPrototypeOf({});
assert.strictEqual(viaObject, viaReflect);
});
it('throws a TypeError for a non-object', function() {
// let fn = () => { Reflect.getPrototypeOf({}) };
let fn = () => { Reflect.getPrototypeOf('non-object') };
assert.throws(fn, TypeError);
});
it('a `new Set()` has a prototype', function() {
// const aSet = Set;
const aSet = new Set();
assert.equal(Reflect.getPrototypeOf(aSet), Set.prototype);
});
it('for a class, it is `Klass.prototype`', function() {
class Klass {}
// const proto = new Klass();
const proto = Reflect.getPrototypeOf(new Klass);
assert.equal(proto, Klass.prototype);
});
it('works also for an old-style "class"', function() {
function Klass() {}
// const proto = Reflect.getPrototypeOf();
const proto = Reflect.getPrototypeOf(new Klass);
assert.equal(proto, Klass.prototype);
});
it('an array has a prototype too', function() {
let arr = [];
// const expectedProto = Array;
const expectedProto = Array.prototype;
assert.equal(Reflect.getPrototypeOf(arr), expectedProto);
});
// TODO
// it('getting the prototype of an "exotic namespace object" returns `null`', function() {
// http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-getprototypeof
// Don't know how to write a test for this yet, without creating a dep in tddbin hardcoded
// PRs welcome
// assert.equal(Reflect.getPrototypeOf(namespace exotic object), null);
// });
});
// 62: Map - `has()`
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`map.has()` indicates whether an element with a key exists', function() {
it('finds nothing in an empty map', function() {
let map = new Map();
// const hasKey = map.hazz(void 0);
const hasKey = map.has(void 0);
assert.equal(hasKey, false);
});
it('finds an element by it`s key', function() {
let map = new Map([['key', 'VALUE']]);
// const hasKey = map.has();
const hasKey = map.has('key');
assert.equal(hasKey, true);
});
it('finds `undefined` as key too', function() {
let map = new Map([[void 0, 'not defined key']]);
// const hasUndefinedAsKey = map;
const hasUndefinedAsKey = map.has(undefined);
assert.equal(hasUndefinedAsKey, true);
});
it('does not coerce keys', function() {
let map = new Map([[1, 'one']]);
// const findsStringOne = true;
const findsStringOne = false;
assert.equal(map.has('1'), findsStringOne);
});
it('after removal (using `map.delete(<key>)`) it doesnt find the element anymore', function() {
let map = new Map([[1, 'one']]);
//
map.delete(1);
assert.equal(map.has(1), false);
});
it('adding an item (using `map.set(key, value)`) later will make `has()` return true', function() {
let map = new Map();
//
map.set(undefined, 'aValue')
assert.equal(map.has(void 0), true);
});
});
// 63: String - `includes()`
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`string.includes()` determines if a string can be found inside another one', function() {
describe('finding a single character', function() {
it('can be done (a character is also a string, in JS)', function() {
// const searchString = 'a';
const searchString = 'x';
assert.equal('xyz'.includes(searchString), true);
});
it('reports false if character was not found', function() {
// const expected = '???';
const expected = false;
assert.equal('xyz'.includes('abc'), expected);
});
});
describe('find a string', function() {
it('that matches exactly', function() {
// const findSome = findMe => 'xyz'.includes;
const findSome = findMe => 'xyz'.includes(findMe);
assert.equal(findSome('xyz'), true);
});
});
describe('search for an empty string, is always true', function() {
it('in an empty string', function() {
// const emptyString = ' ';
const emptyString = '';
assert.equal(''.includes(emptyString), true);
});
it('in `abc`', function() {
// const actual = _.includes('');
const actual = 'abc'.includes('');
assert.equal(actual, true);
});
});
describe('special/corner cases', function() {
it('search for `undefined` in a string fails', function() {
// const findInAbc = (what) => 'abc'.includes;
const findInAbc = (what) => 'abc'.includes(what);
assert.equal(findInAbc(undefined), false);
});
it('searches are case-sensitive', function() {
// const findInAbc = (what) => 'abc'.inkludez(what);
const findInAbc = (what) => 'abc'.includes(what);
assert.equal(findInAbc('A'), false);
});
it('must NOT be a regular expression', function() {
// const regExp = '';
const regExp = /\w+/;
assert.throws(() => {''.includes(regExp)});
});
describe('coerces the searched "thing" into a string', function() {
it('e.g. from a number', function() {
// const actual = '123'.includes(4);
const actual = '123'.includes(3);
assert.equal(actual, true);
});
it('e.g. from an array', function() {
// const actual = '123'.includes([1,2,3]);
const actual = '123'.includes([123]);
assert.equal(actual, true);
});
it('e.g. from an object, with a `toString()` method', function() {
// const objWithToString = {toString: 1};
const objWithToString = {toString: () => 1};
// think of key-value pair as reading key = value,
// so the method is a function toString = () => 1 in arrow notation
assert.equal('123'.includes(objWithToString), true);
});
});
});
describe('takes a position from where to start searching', function() {
it('does not find `a` after position 1 in `abc`', function() {
// const position = 0;
const position = 1;
assert.equal('abc'.includes('a', position), false);
});
it('even the position gets coerced', function() {
// const findAtPosition = position => 'xyz'.includes('x', pos);
const findAtPosition = pos => 'xyz'.includes('x', pos);
assert.equal(findAtPosition('2'), false);
});
describe('invalid positions get converted to 0', function() {
it('e.g. `undefined`', function() {
// const findAtPosition = (pos=2) => 'xyz'.includes('x', pos);
const findAtPosition = (pos=undefined) => 'xyz'.includes('x', pos);
assert.equal(findAtPosition(undefined), true);
});
it('negative numbers', function() {
// const findAtPosition = (pos) => 'xyz'.includes('x', -pos);
const findAtPosition = (pos) => 'xyz'.includes('x', pos);
assert.equal(findAtPosition(-2), true);
});
it('NaN', function() {
// const findAtPosition = (pos) => 'xyz'.includes('x', 1);
const findAtPosition = (pos) => 'xyz'.includes('x', NaN);
assert.equal(findAtPosition(NaN), true);
});
});
});
});
// 64: Set - delete
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`set.delete()` deletes an element from a set', function(){
let set;
beforeEach(() => set = new Set());
describe('use `delete(<value>)` to delete an element', function() {
beforeEach(function() {
set.add('one').add('two').add('three');
});
it('`delete()` returns `true` when the element was found', function() {
// const returns = set.remove;
const returns = set.delete('one');
assert.strictEqual(returns, true);
});
it('and the size decreases', function() {
//
set.delete('one')
assert.equal(set.size, 2);
});
});
describe('if nothing was deleted (no element with the given value was found)', function() {
it('returns `false`', function() {
set.add('one');
// const returns = set.delete('one');
const returns = set.delete('two');
assert.equal(returns, false);
});
});
describe('`undefined` is a valid value in a set', function() {
it('deleting it, when it is not in the set, returns `false` too', function() {
// initializing without a value is the same as whatToDelete = undefined
let whatToDelete;
assert.equal(set.delete(whatToDelete), false);
});
it('`delete()` removes it, when its in the set', function() {
// performing set.add() without a value adds an undefined value to the set
set.add()
assert.equal(set.delete(), true);
});
});
describe('the value does NOT get casted', function() {
it('number 1 is different to string "1"', function() {
set.add(1);
// set.add('1');
assert.equal(set.delete('1'), false);
});
});
});
// 65: Set - API overview
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Set` API overview', function(){
const api = ['size', 'add', 'clear', 'delete', 'entries', 'forEach', 'has', 'keys', 'values'];
let set;
beforeEach(function() {
set = new Set(api);
});
it('a Set can be created from an array', function() {
// let set = new Set([]);
let set = new Set(api);
assert.deepEqual(Array.from(set), api);
});
it('`size` is the number of values', function() {
// const theSize = set.count;
const theSize = set.size;
assert.equal(theSize, api.length);
});
it('`add()` appends the given value', function() {
// Hint: To make the content of `api` and `set` consistent you can add the
// `Symbol.iterator` to the `set`. Strongly speaking it is missing in the API.
set.add(Symbol.iterator)
assert.equal(set.size, api.length + 1);
});
it('`clear()` removes all elements', function() {
//
set.clear()
assert.equal(set.size, 0);
});
it('`delete()` removes the given value', function() {
//
set.delete('add')
assert.equal(set.size, api.length - 1);
});
it('`entries()` returns an iterator for all values', function() {
const expectedEntries = api.map(entry => [entry, entry]);
// const actualEntries = set.entry;
const actualEntries = set.entries();
assert.deepEqual([...actualEntries], expectedEntries);
});
it('`forEach()` calls a callback for each value', function() {
let values = [];
// set.map(value => { values.push(value); });
set.forEach(value => { values.push(value); });
assert.deepEqual(values, api);
});
it('`has()` returns true if the given value is in the set', function() {
// const propertyName = '';
const propertyName = 'add';
assert.equal(set.has(propertyName), true);
});
describe('returns an iterator that contains all values', function() {
// In order to be alike to `Map`, `keys()` and `values()` are essentially the same thing for a `Set`.
it('`keys()`', function() {
// const allKeys = Object.keys(set);
const allKeys = set.keys()
assert.deepEqual([...allKeys], api);
});
it('`values()`', function() {
// const allValues = set.value();
const allValues = set.values();
assert.deepEqual([...allValues], api);
});
it('`[Symbol.iterator]()`', function() {
// const iteratorKey = '???';
const iteratorKey = Symbol.iterator;
assert.deepEqual([...set[iteratorKey]()], api);
});
});
});
// 66: object-literal - getter
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('An object literal can also contain getters', () => {
it('just prefix the property with `get` (and make it a function)', function() {
const obj = {
// x() { return 'ax'; }
get x() { return 'ax'; }
};
assert.equal(obj.x, 'ax');
});
it('must have NO parameters', function() {
const obj = {
// x(param) { return 'ax'; }
get x() { return 'ax'; }
};
assert.equal(obj.x, 'ax');
});
it('can be a computed property (an expression enclosed in `[]`)', function() {
const keyName = 'x';
const obj = {
// get keyName() { return 'ax'; }
get [keyName]() { return 'ax'; }
};
assert.equal(obj.x, 'ax');
});
it('can be removed using delete', function() {
const obj = {
get x() { return 'ax'; }
};
// delete obj.y;
delete obj.x;
assert.equal(obj.x, void 0);
});
// 67: object-literal - setter
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('An object literal can also contain setters', () => {
describe('defining: a setter', function() {
it('by prefixing the property with `set` (and make it a function)', function() {
let theX = null;
const obj = {
// x(newX) { theX = newX; }
set x(newX) { theX = newX; }
};
obj.x = 'the new X';
assert.equal(theX, 'the new X');
});
it('must have exactly one parameter', function() {
// let setterCalledWith = void 0;
// const obj = {
// x() { // <<<<=== it's not a setter yet!
// if (arguments.length === 1) {
// setterCalledWith = arguments[0];
let setterCalledWith = 'new value';
const obj = {
set x(SetterCalledWith) { // <<<<=== it's not a setter yet!
if (arguments.length === 1) {
setterCalledWith = arguments[0];
}
}
};
assert.equal(obj.x = 'new value', setterCalledWith);
});
it('can be a computed property (an expression enclosed in `[]`)', function() {
const publicPropertyName = 'x';
const privatePropertyName = '_' + publicPropertyName;
const obj = {
[privatePropertyName]: null,
//
set [publicPropertyName](param) {
obj[privatePropertyName] = param
}
// write the complete setter to make the assert below pass :)
};
obj.x = 'axe';
assert.equal(obj._x, 'axe');
});
});
describe('working with/on the setter', function() {
it('you can use `delete` to remove the property (including it`s setter)', function() {
let setterCalled = false;
const obj = {
set x(param) { setterCalled = true; }
};
// delete the property x here, to make the test pass
//
delete obj.x
obj.x = true;
assert.equal(setterCalled, false);
});
});
// 68: Reflect - construct
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Reflect.construct` is the `new` operator as a function', function() {
describe('the function itself', function() {
it('is static on the `Reflect` object', function() {
// const name = 'konstructor';
const name = 'constructor';
assert.equal(name in Reflect, true);
});
it('is of type `function`', function() {
// const expectedType = '???';
const expectedType = 'function';
assert.equal(typeof Reflect.construct, expectedType)
});
});
describe('the 1st parameter is the constructor to be invoked', function() {
it('fails when given a number as constructor', function() {
// let aNumber = class {}
let aNumber = 5;
assert.throws(() => { Reflect.construct(aNumber, []) }, TypeError);
});
it('works given a function that can be instanciated', function() {
// let aFunction;
let aFunction = function() {return 'this works'};
assert.doesNotThrow(() => { Reflect.construct(aFunction, []) });
});
it('works given a class', function() {
// const aClass = {};
const aClass = class {};
assert.doesNotThrow(() => { Reflect.construct(aClass, []) });
});
});
describe('the 2nd parameter is a list of arguments, that will be passed to the constructor', function() {
const aClass = class {};
it('fails when it`s not an array(-like), e.g. a number', function() {
// let aNumber = [];
let aNumber = 5;
assert.throws(() => { Reflect.construct(aClass, aNumber) }, TypeError);
});
it('works with an array-like object (the `length` property look up should not throw)', function() {
// let arrayLike = {get length() { throw new Error(); }};
let arrayLike = {get length() { return 'this is the length'; }};
assert.doesNotThrow(() => { Reflect.construct(aClass, arrayLike) });
});
it('works with a real array', function() {
// let realArray = '';
let realArray = [1,2,3,4,5];
assert.doesNotThrow(() => { Reflect.construct(aClass, realArray) });
});
});
describe('in use', function() {
it('giving it a class it returns an instance of this class', function() {
class Constructable {}
// let instance = Reflect.construct; // use Reflect.construct here!!!
let instance = Reflect.construct(Constructable, []);
// or
instance = Reflect.construct(Constructable, {});
assert.equal(instance instanceof Constructable, true);
});
describe('the list of arguments are passed to the constructor as given', function() {
class Constructable {
constructor(...args) { this.args = args; }
}
it('if none given, nothing is passed', function() {
// let instance = Reflect.construct(Constructable, [1]);
let instance = Reflect.construct(Constructable, []);
assert.deepEqual(instance.args, []);
});
it('passing an array, all args of any type are passed', function() {
const argumentsList = ['arg1', ['arg2.1', 'arg2.2'], {arg: 3}];
// let instance = Reflect.construct;
let instance = Reflect.construct(Constructable, argumentsList);
assert.deepEqual(instance.args, argumentsList);
});
});
});
describe('the length property', function() {
it('of `Reflect.construct` is 2', function() {
// let expected;
let expected = 2;
assert.equal(Reflect.construct.length, expected);
});
});
});
// 69: Reflect - defineProperty
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Reflect.defineProperty()` is like `Object.defineProperty()` but returns a Boolean.', function() {
describe('the function itself', function() {
it('is static on the `Reflect` object', function() {
// const name = 'what`s the functions name again? :)';
const name = 'defineProperty';
assert.equal(name in Reflect, true);
});
it('is of type `function`', function() {
// const expectedType = '';
const expectedType = 'function';
assert.equal(typeof Reflect.defineProperty, expectedType)
});
});
describe('the 1st parameter is the object on which to define a property', function() {
it('fails if it is not an object', function() {
// let noObj = {};
let noObj = '';
assert.throws(() => { Reflect.defineProperty(noObj, 'property', {value: 'value'}); });
});
it('accepts an object', function() {
// let obj = '';
let obj = {};
assert.doesNotThrow(() => { Reflect.defineProperty(obj, 'property', {value: 'value'}); });
});
it('accepts an instance (of a class)', function() {
// let instance;
let instance = class {};
assert.doesNotThrow(() => { Reflect.defineProperty(instance, 'property', {value: 'value'}); });
});
});
describe('2nd parameter is the name of the property to be defined on the object (normally a string)', function() {
it('works with a `normal` string', function() {
let obj = {};
// Reflect.defineProperty(obj, '', {});
Reflect.defineProperty(obj, 'prop', {});
assert.equal('prop' in obj, true);
});
it('a number gets converted into a string', function() {
let obj = {};
// Reflect.defineProperty(obj, 2, {});
Reflect.defineProperty(obj, 1, {});
assert.equal('1' in obj, true);
});
it('`undefined` also gets converted into a string (watch out!)', function() {
let obj = {};
// let undef = '';
let undef = undefined;
Reflect.defineProperty(obj, undef, {});
assert.equal('undefined' in obj, true);
});
it('it can be a symbol', function() {
let obj = {};
const sym = Symbol.for('prop');
// Reflect.defineProperty(obj, 'prop', {});
Reflect.defineProperty(obj, sym, {});
assert.equal(sym in obj, true);
});
});
describe('the `value` is part of the 3rd parameter, given as a property in an object `{value: ...}`', function() {
// The entire complexity of the 3rd parameter might be covered in a later kata.
it('contains the initial value of the property, as an object in the property `value`', function() {
let obj = {};
// Reflect.defineProperty(obj, 'prop');
Reflect.defineProperty(obj, 'prop', {value: 'property value'});
assert.equal(obj.prop, 'property value');
});
it('can be of any type (even itself)', function() {
let obj = {};
// Reflect.defineProperty(obj, 'prop');
Reflect.defineProperty(obj, 'prop', {value: obj});
assert.equal(obj.prop, obj);
});
});
describe('the return value of the function indicates whether the property was defined successfully', function() {
describe('returns true', function() {
it('when the property was created (which requires the 3rd parameter too!!!)', function() {
let instance = new class {};
// const wasPropertyDefined = Reflect.defineProperty();
const wasPropertyDefined = Reflect.defineProperty(instance, 'property', {value: 'value'});
assert.equal(wasPropertyDefined, true);
});
it('no matter what the value of the property is (just the 3rd param has to exist as `{}`)', function() {
let instance = new class {};
// const wasPropertyDefined = Reflect.defineProperty(instance);
const wasPropertyDefined = Reflect.defineProperty(instance, 'property', {value: 'value'});
assert.equal(wasPropertyDefined, true);
});
});
describe('returns false', function() {
it('when a non-configurable property wants to be changed to configurable=true', function() {
let obj = {};
Reflect.defineProperty(obj, 'x', {configurable: false});
// const wasPropertyDefined = Reflect.defineProperty;
const wasPropertyDefined = Reflect.defineProperty(obj, 'x', {configurable: true});
assert.equal(wasPropertyDefined, false);
});
it('when the object we want to add a property to is frozen', function() {
let instance = new class {};
Object.freeze(instance);
// const wasPropertyDefined = Reflect.defineProperty({}, 'prop', {value: 1});
const wasPropertyDefined = Reflect.defineProperty(instance, 'prop', {value: 1});
assert.equal(wasPropertyDefined, false);
});
});
});
});
// 70: Set - clear
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`clear()` removes all elements from a Set object.', function(){
let set;
beforeEach(() => set = new Set());
it('`set.size` becomes 0', function() {
set.add('one').add(2);
set.clear();
// var expectedSize;
var expectedSize = 0;
assert.equal(set.size, expectedSize);
});
it('the iterator `set.entries()` will not contain any items', function() {
set.add('one').add(2);
// set.clear;
set.clear();
const {done} = set.entries().next();
assert.equal(done, true);
});
it('any call to `set.has()` returns false', function() {
set.add('one').add(2);
//
set.clear();
set.has();
assert.deepEqual(set.has(2), false);
});
it('returns `undefined`', function() {
// var expectedReturn = true;
var expectedReturn = undefined;
assert.equal(set.clear(), expectedReturn);
});
});
// 71: String - `repeat()`
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`str.repeat(x)` concatenates `x` copies of `str` and returns it', function() {
describe('the 1st parameter the count', function() {
it('if missing, returns an empty string', function() {
// const what = 'one'.repeat(23);
const what = 'one'.repeat();
assert.equal(what, '');
});
it('when `1`, the string stays the same', function() {
// const what = 'one'.repeat();
const what = 'one'.repeat(1);
assert.equal(what, 'one');
});
it('for `3` the string `x` becomes `xxx`', function() {
// const actual = 'x'.REPEAT(1);
const actual = 'x'.repeat(3);
assert.equal(actual, 'xxx');
});
it('for `0` an empty string is returned', function() {
// const repeatCount = 1;
const repeatCount = 0;
assert.equal('shrink'.repeat(repeatCount), '');
});
describe('the count is not a number', () => {
it('such as a string "3", it gets converted to an int', function() {
// const repeated = 'three'.repeat('2');
const repeated = 'three'.repeat('3');
assert.equal(repeated, 'threethreethree');
});
it('a hex looking number as a string "0xA", it gets converted to an int', function() {
// const repeated = 'x'.repeat('0A');
const repeated = 'x'.repeat('0xA');
assert.equal('xxxxxxxxxx', repeated);
});
it('and does not look like a number, it behaves like 0', function() {
const repeated = 'x'.repeat('z');
assert.equal('', repeated);
});
});
});
describe('throws an error for', function() {
it('a count of <0', function() {
// const belowZero = 1;
const belowZero = -1;
assert.throws(() => { ''.repeat(belowZero); }, RangeError);
});
it('a count of +Infinty', function() {
// let infinity = 'infinity';
let infinity = +Infinity;
assert.throws(() => { ''.repeat(infinity); }, RangeError);
});
});
describe('accepts everything that can be coerced to a string', function() {
it('e.g. a boolean', function() {
// let aBool = true;
let aBool = false;
assert.equal(String.prototype.repeat.call(aBool, 2), 'falsefalse');
});
it('e.g. a number', function() {
// let aNumber;
let aNumber=1;
assert.equal(String.prototype.repeat.call(aNumber, 2), '11');
});
});
describe('for my own (string) class', function() {
it('calls `toString()` to make it a string', function() {
class MyString { toString() { return 'my string'; } }
// const expectedString = ''
const expectedString = new MyString();;
assert.equal(String(new MyString()).repeat(1), expectedString);
});
it('`toString()` is only called once', function() {
let counter = 1;
class X {
toString() {
return counter++;
}
}
let repeated = String(new X()).repeat(2);
assert.equal(repeated, '11');
});
});
});
// 72: String - `startsWith()`
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`str.startsWith(searchString)` determines whether `str` begins with `searchString`.', function() {
const s = 'the string s';
describe('the 1st parameter, the string to search for', function() {
it('can be just a character', function() {
// const actual = s.starts_with('t');
const actual = s.startsWith('t');
assert.equal(actual, true);
});
it('can be a string', function() {
// const expected = '???';
const expected = true;
assert.equal(s.startsWith('the'), expected);
});
it('can contain unicode characters', function() {
// const nuclear = 'NO ☢ NO';
const nuclear = '☢ NO';
assert.equal(nuclear.startsWith('☢'), true);
});
it('a regular expression throws a TypeError', function() {
// const aRegExp = 'the';
const aRegExp = /the/;
assert.throws(() => {''.startsWith(aRegExp)}, TypeError);
});
});
describe('the 2nd parameter, the position where to start searching from', function() {
it('e.g. find "str" at position 4', function() {
// const position = 3;
const position = 4;
assert.equal(s.startsWith('str', position), true);
});
it('for `undefined` is the same as 0', function() {
// const _undefined_ = '1';
const _undefined_ = undefined;
assert.equal(s.startsWith('the', _undefined_), true);
});
it('the parameter gets converted to an int', function() {
// const position = 'four';
const position = '4';
assert.equal(s.startsWith('str', position), true);
});
it('a value larger than the string`s length, returns false', function() {
// const expected = true;
const expected = false;
assert.equal(s.startsWith(' ', s.length + 1), expected);
});
});
describe('this functionality can be used on non-strings too', function() {
it('e.g. a boolean', function() {
// let aBool;
let aBool = false;
assert.equal(String.prototype.startsWith.call(aBool, 'false'), true);
});
it('e.g. a number', function() {
// let aNumber = 19;
let aNumber = '19';
assert.equal(String.prototype.startsWith.call(aNumber + 84, '1984'), true);
});
it('also using the position works', function() {
// const position = 0;
const position = 1;
assert.equal(String.prototype.startsWith.call(1994, '99', position), true);
});
});
});
// 73: Generator - `return` inside a generator is special
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`return` in a generator function is special', function() {
describe('the returned value is an IteratorResult (just like any value returned via `yield`)', function() {
it('returns an IteratorResult (an object with the properties `value` and `done`)', function() {
function* generatorFunction() { return 1; }
const returned = generatorFunction().next();
const propertyNames = ['value', 'done'];
assert.deepEqual(Object.keys(returned), propertyNames);
});
it('the property `value` is the returned value', function() {
// function* generatorFunction() { return; }
function* generatorFunction() { return 23; }
const {value} = generatorFunction().next();
assert.equal(value, 23);
});
it('the property `done` is true', function() {
// function* generatorFunction() { yield 42; }
function* generatorFunction() { return 42; }
const {done} = generatorFunction().next();
assert.equal(done, true);
});
it('NOTE: `yield` does not return `done=true` but `done=false`!', function() {
// function* generatorFunction() { return 1; }
function* generatorFunction() { yield 1; }
const returned = generatorFunction().next();
assert.deepEqual(returned, {value: 1, done: false});
});
it('a missing `return` returns `{value: undefined, done: true}`', function() {
// function* generatorFunction() { yield; }
function* generatorFunction() { return ; }
const returned = generatorFunction().next();
assert.deepEqual(returned, {value: void 0, done: true});
});
});
describe('mixing `return` and `yield`', function() {
function* generatorFunctionWithYieldAndReturn() {
yield 1;
// yield ;
//
return 2
}
it('is possible', function() {
const iterator = generatorFunctionWithYieldAndReturn();
const values = [
iterator.next(),
iterator.next()
];
assert.deepEqual(values, [{value: 1, done: false}, {value: 2, done: true}]);
});
it('the mix behaves different to two `yield`s', function() {
const iterator = generatorFunctionWithYieldAndReturn();
// const values = [1, 2];
const values = [1]
assert.deepEqual(Array.from(iterator), values);
});
it('two `yield`s returning values', function() {
function* generatorFunctionWithTwoYields() {
//
yield 1;
yield 2;
}
assert.deepEqual(Array.from(generatorFunctionWithTwoYields()), [1, 2]);
});
it('return a yielded value by "chaining" `return` and `yield`', function() {
function* generatorFunction() {
return yield 1
}
const iterator = generatorFunction();
const values = [
iterator.next().value,
iterator.next(2).value
];
assert.deepEqual(values, [1, 2]);
});
});
});
// 74: String - `endsWith()`
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`str.endsWith(searchString)` determines whether `str` ends with `searchString`', function() {
const s = 'el fin';
describe('the 1st parameter the string to search for', function() {
it('can be a character', function() {
// const doesEndWith = s.doesItReallyEndWith('n');
const doesEndWith = s.endsWith('n');
assert.equal(doesEndWith, true);
});
it('can be a string', function() {
// const expected = false;
const expected = true;
assert.equal(s.endsWith('fin'), expected);
});
it('can contain unicode characters', function() {
// const nuclear = 'NO ☢ Oh NO!';
const nuclear = 'NO ☢';
assert.equal(nuclear.endsWith('☢'), true);
});
it('a regular expression throws a TypeError', function() {
// const aRegExp = '/the/';
const aRegExp = /the/;
assert.throws(() => {''.endsWith(aRegExp)}, TypeError);
});
});
describe('the 2nd parameter, the position where the search ends (as if the string was only that long)', function() {
it('find "el" at a substring of the length 2', function() {
// const endPos = 0;
const endPos = 2;
assert.equal(s.endsWith('el', endPos), true);
});
it('`undefined` uses the entire string', function() {
// const _undefined_ = 'i would like to be undefined';
const _undefined_ = undefined;
assert.equal(s.endsWith('fin', _undefined_), true);
});
it('the parameter gets coerced to an integer (e.g. "5" becomes 5)', function() {
// const position = 'five';
const position = '5';
assert.equal(s.endsWith('fi', position), true);
});
describe('value less than 0', function() {
it('returns `true`, when searching for an empty string', function() {
// const emptyString = '??';
const emptyString = '';
assert.equal('1'.endsWith(emptyString, -1), true);
});
it('return `false`, when searching for a non-empty string', function() {
// const notEmpty = '';
const notEmpty = 'not empty';
assert.equal('1'.endsWith(notEmpty, -1), false);
});
});
});
describe('this functionality can be used on non-strings too', function() {
it('e.g. a boolean', function() {
let aBool = false;
assert.equal(String.prototype.endsWith.call(aBool, 'lse'), true);
});
it('e.g. a number', function() {
// let aNumber = 0;
let aNumber = 84;
assert.equal(String.prototype.endsWith.call(aNumber + 1900, 84), true);
});
it('also using the position works', function() {
// const position = '??';
const position = 3;
assert.equal(String.prototype.endsWith.call(1994, '99', position), true);
});
});
});
// 75: Promise - basics
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('a Promise represents an operation that hasn`t completed yet, but is expected in the future', function() {
it('`Promise` is a global function', function() {
// const expectedType = '???';
const expectedType = 'function';
assert.equal(typeof Promise, expectedType);
});
describe('the constructor', function() {
it('instantiating it without params throws', function() {
// const fn = () => { Promise }
const fn = () => { new Promise }
assert.throws(fn);
});
it('expects a function as parameter', function() {
// const param = null;
const param = () => {};
assert.doesNotThrow(() => { new Promise(param); });
});
});
describe('simplest promises', function() {
it('resolve a promise by calling the `resolve` function given as first parameter', function(done) {
let promise = new Promise((resolve) => {
//
done();
});
promise
.then(() => done())
.catch(() => done(new Error('The promise is expected to resolve.')));
});
it('the `resolve` function can return a value, that is consumed by the `promise.then()` callback', function(done) {
let promise = new Promise((resolve) => {
// resolve();
resolve(42);
});
promise
.then(value => {assert.equal(value, 42); done(); })
.catch(() => done(new Error('The promise is expected to resolve with 42!')));
});
it('rejecting a promise is done by calling the callback given as 2nd parameter', function(done) {
// let promise = new Promise((resolve, reject) => {
let promise = new Promise((resolve, reject) => {
reject();
});
promise
.then(() => done(new Error('The promise is expected to be rejected.')))
.catch(() => done());
});
});
describe('an asynchronous promise', function() {
it('can resolve later, also by calling the first callback', function(done) {
// let promise = new Promise(() => {
let promise = new Promise((resolve) => {
setTimeout(() => resolve(), 100);
});
promise
.then(() => done())
.catch(() => done(new Error('The promise is expected to resolve.')));
});
it('reject it at some later point in time, calling the 2nd callback', function(done) {
// let promise = new Promise((reject) => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(), 100);
});
promise
.then(() => done(new Error('The promise is expected to be rejected.')))
.catch(() => done());
});
});
describe('test library (mocha here) support for promises', function() {
it('just returning the promise makes the test library check that the promise resolves', function() {
// let promise = new Promise((reject, resolve) => {
let promise = new Promise((resolve, reject) => {
resolve();
});
// return the promise to mocha, it has the checking for promise resolving built in, when it receives a promise
return promise;
});
});
});
// 76: Promise - creation
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('A promise can be created in multiple ways', function() {
describe('creating a promise fails when', function() {
it('using `Promise` as a function', function() {
function callPromiseAsFunction() {
// Promise;
Promise();
}
assert.throws(callPromiseAsFunction);
});
it('no parameter is passed', function() {
function promiseWithoutParams() {
// new Promise(() => {});
new Promise();
}
assert.throws(promiseWithoutParams);
});
it('passing a non-callable throws too', function() {
// const notAFunction = () => {};
const notAFunction = null;
assert.throws(() => { new Promise(notAFunction); });
});
});
describe('most commonly Promises get created using the constructor', function() {
it('by passing a resolve function to it', function() {
// const promise = new Promise(() => resolve());
const promise = new Promise((resolve) => resolve());
return promise;
});
it('by passing a resolve and a reject function to it', function(done) {
// const promise = new Promise((resolve, reject) => resolve());
const promise = new Promise((resolve, reject) => reject());
promise
.then(() => done(new Error('Expected promise to be rejected.')))
.catch(done);
});
});
describe('extending a `Promise`', function() {
it('using `class X extends Promise{}` is possible', function(done) {
// class MyPromise {}
// const promise = MyPromise(resolve => resolve());
class MyPromise extends Promise{}
const promise = new MyPromise(resolve => resolve());
promise
.then(() => done())
.catch(e => done(new Error('Expected to resolve, but failed with: ' + e)));
});
it('must call `super()` in the constructor if it wants to inherit/specialize the behavior', function() {
class ResolvingPromise extends Promise {
//
constructor(resolve,reject) {
super(resolve)
}
}
return new ResolvingPromise(resolve => resolve());
});
});
describe('`Promise.all()` returns a promise that resolves when all given promises resolve', function() {
it('returns all results', function(done) {
const promise = Promise.all([
// new Promise(resolve => resolve(1)),new Promise(resolve => resolve(2)),new Promise(resolve => resolve(3))
new Promise(resolve => resolve(1)),new Promise(resolve => resolve(2))
]);
promise
.then(value => { assert.deepEqual(value, [1, 2]); done(); })
.catch(e => done(new Error(e)));
});
it('is rejected if one rejects', function(done) {
const promise = Promise.all([
// new Promise(resolve => resolve(1))
new Promise(resolve => resolve(1)), new Promise((resolve, reject) => reject(2))
]);
promise
.then(() => done(new NotRejectedError()))
.catch(() => done());
});
});
describe('`Promise.race()` returns the first settled promise', function() {
it('if it resolves first, the promises resolves', function(done) {
const lateRejectedPromise = new Promise((resolve, reject) => setTimeout(reject, 100));
const earlyResolvingPromise = new Promise(resolve => resolve('1st :)'));
// const promise = Promise.race([lateRejectedPromise]);
const promise = Promise.race([lateRejectedPromise, earlyResolvingPromise]);
promise
.then(value => { assert.deepEqual(value, '1st :)'); done(); })
.catch(e => done(new Error('Expected to resolve, but failed with: ' + e)));
});
it('if one of the given promises rejects first, the returned promise is rejected', function(done) {
// const earlyRejectedPromise = new Promise((resolve, reject) => reject('I am a REJECTOR'));
const earlyRejectedPromise = new Promise((resolve, reject) => reject('I am a rejector'));
const lateResolvingPromise = new Promise(resolve => setTimeout(resolve, 10));
const promise = Promise.race([earlyRejectedPromise, lateResolvingPromise]);
promise
.then(() => done(new NotRejectedError()))
.catch(value => { assert.equal(value, 'I am a rejector'); done(); })
.catch(done);
});
});
describe('`Promise.resolve()` returns a resolving promise', function() {
it('if no value given, it resolves with `undefined`', function(done) {
// const promise = Promise.resolve;
const promise = Promise.resolve();
promise
.then(value => { assert.deepEqual(value, void 0); done(); })
.catch(e => done(new Error('Expected to resolve, but failed with: ' + e)));
});
it('resolves with the given value', function(done) {
// const promise = Promise.resolve();
const promise = Promise.resolve('quick resolve');
promise
.then(value => { assert.equal(value, 'quick resolve'); done(); })
.catch(e => done(e));
});
});
describe('`Promise.reject()` returns a rejecting promise', function() {
it('if no value given, it rejects with `undefined`', function(done) {
// const promise = Promise.resolve();
const promise = Promise.reject();
promise
.then(() => done(new NotRejectedError()))
.catch(value => { assert.deepEqual(value, void 0); done(); })
.catch(done);
});
it('the parameter passed to `reject()` can be used in the `.catch()`', function(done) {
// const promise = Promise;
const promise = Promise.reject('quick reject');
promise
.then(() => done(new NotRejectedError()))
.catch(value => { assert.deepEqual(value, 'quick reject'); done(); })
.catch(done);
});
});
});
class NotRejectedError extends Error {
constructor() {
super();
this.message = 'Expected promise to be rejected.';
}
}
// 77: Promise - chaining
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('chaining multiple promises can enhance readability', () => {
describe('prerequisites for understanding', function() {
it('reminder: the test passes when a fulfilled promise is returned', function() {
// return Promise.reject('I should fulfill.');
return Promise.resolve('I should fulfill.');
});
it('a function given to `then()` fulfills (if it doesnt throw)', function() {
// const beNice = () => { throw new Error('I am nice') };
const beNice = () => { return 'I am nice' };
return Promise.resolve()
.then(beNice)
.then(niceMessage => assert.equal(niceMessage, 'I am nice'));
});
});
describe('chain promises', function() {
const removeMultipleSpaces = string => string.replace(/\s+/g, ' ');
it('`then()` receives the result of the promise it was called on', function() {
const wordsPromise = Promise.resolve('one space between each word');
return wordsPromise
// .then(string => removeMultipleSpaces())
.then(string => removeMultipleSpaces(string))
.then(actual => {assert.equal(actual, 'one space between each word')})
;
});
const appendPeriod = string => `${string}.`;
it('multiple `then()`s can be chained', function() {
const wordsPromise = Promise.resolve('Sentence without an end');
return wordsPromise
//
.then(string => string.concat('.'))
.then(removeMultipleSpaces)
.then(actual => {assert.equal(actual, 'Sentence without an end.')})
;
});
const trim = string => string.replace(/^\s+/, '').replace(/\s+$/, '');
it('order of the `then()`s matters', function() {
const wordsPromise = Promise.resolve('Sentence without an end ');
return wordsPromise
// .then(appendPeriod)
.then(trim)
.then(appendPeriod)
.then(removeMultipleSpaces)
.then(actual => {assert.equal(actual, 'Sentence without an end.')})
;
});
const asyncUpperCaseStart = (string, onDone) => {
const format = () => onDone(string[0].toUpperCase() + string.substr(1));
setTimeout(format, 100);
};
it('any of the things given to `then()` can resolve asynchronously (the real power of Promises)', function() {
const wordsPromise = Promise.resolve('sentence without an end');
return wordsPromise
// .then(string => new Promise(resolve => asyncUpperCaseStart))
.then(string => new Promise(resolve => asyncUpperCaseStart(string, resolve)))
.then(string => new Promise(resolve => setTimeout(() => resolve(appendPeriod(string)), 100)))
.then(actual => {assert.equal(actual, 'Sentence without an end.')})
;
});
it('also asynchronously, the order still matters, promises wait, but don`t block', function() {
const wordsPromise = Promise.resolve('trailing space ');
return wordsPromise
.then(string => new Promise(resolve => asyncUpperCaseStart(string, resolve)))
// .then(string => new Promise(resolve => setTimeout(() => resolve(appendPeriod(string)), 100)))
.then(string => new Promise(resolve => setTimeout(() => resolve(trim(string)), 100)))
.then(string => new Promise(resolve => setTimeout(() => resolve(appendPeriod(string)), 100)))
.then(actual => {assert.equal(actual, 'Trailing space.')})
;
});
});
});
// 78: Promise - API overview
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Promise` API overview', function() {
it('`new Promise()` requires a function as param', () => {
// const param = null;
const param = () => {};
assert.doesNotThrow(() => { new Promise(param); });
});
describe('resolving a promise', () => {
// reminder: the test passes when a fulfilled promise is returned
it('via constructor parameter `new Promise((resolve) => { resolve(); })`', () => {
// const param = () => { resolve(); };
const param = (resolve) => { resolve(); };
return new Promise(param);
});
it('using `Promise.resolve()`', () => {
// return Promise.reject('all fine');
return Promise.resolve('all fine');
});
});
describe('a rejected promise', () => {
it('using the constructor parameter', (done) => {
// const promise = new Promise((reject) => { reject(); });
const promise = new Promise((resolve, reject) => { reject(); });
promise
.then(() => done(new Error('The promise is expected to be rejected.')))
.catch(() => done());
});
it('via `Promise.reject()`', (done) => {
// const promise = Promise.resolve();
const promise = Promise.reject();
promise
.then(() => done(new Error('The promise is expected to be rejected.')))
.catch(() => done());
});
});
describe('`Promise.all()`', () => {
it('`Promise.all([p1, p2])` resolves when all promises resolve', () => {
// return Promise.all([Promise.resolve(), Promise.reject(), Promise.resolve()])
return Promise.all([Promise.resolve(), Promise.resolve(), Promise.resolve()])
});
it('`Promise.all([p1, p2])` rejects when a promise is rejected', (done) => {
// Promise.all([Promise.resolve()])
Promise.all([Promise.reject()])
.then(() => done(new Error('The promise is expected to be rejected.')))
.catch(() => done())
});
});
describe('`Promise.race()`', () => {
it('`Promise.race([p1, p2])` resolves/reject when one of the promises resolves/rejects', () => {
// return Promise.race([Promise.reject(), Promise.reject()])
return Promise.race([Promise.resolve(), Promise.reject()])
});
it('`Promise.race([p1, p2])` rejects when one of the promises rejects', (done) => {
// Promise.race([Promise.resolve()])
Promise.race([Promise.reject()])
.then(() => done(new Error('The promise is expected to be rejected.')))
.catch(() => done())
});
it('`Promise.race([p1, p2])` order matters (and timing)', () => {
// return Promise.race([Promise.reject(), Promise.resolve()])
return Promise.race([Promise.resolve(), Promise.reject()])
});
});
});
// 79: Promise - catch
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
// Here we use promises to trigger, don't modify the block with the
// returning promise!
describe('`catch()` returns a Promise and deals with rejected cases only', () => {
describe('prerequisites for understanding', () => {
it('*return* a fulfilled promise, to pass a test', () => {
// Promise.resolve();
return Promise.resolve();
assert(false); // Don't touch! Make the test pass in the line above!
});
it('reminder: the test passes when a fulfilled promise is returned', () => {
// return Promise.reject('I should fulfill.');
return Promise.resolve('I should fulfill.');
});
});
describe('`catch` method basics', () => {
it('is an instance method', () => {
// const p = Promise;
const p = new Promise(resolve => {});
assert.equal(typeof p.catch, 'function');
});
it('catches only promise rejections', (done) => {
// const promise = Promise.resolve();
const promise = Promise.reject();
promise
.then(() => { done('Should not be called!'); })
.catch(done);
});
it('returns a new promise', () => {
// const whatToReturn = () => Promise.reject();
// needs to provide a new function to run when there is a catch
const whatToReturn = () => Promise.resolve();
const promise = Promise.reject();
return promise.catch(() =>
whatToReturn()
);
});
it('converts it`s return value into a promise', () => {
const p = Promise.reject();
// const p1 = p.catch(() => void 0);
const p1 = p.catch(() => 'promise?');
return p1.then(result => assert.equal('promise?', result));
});
it('the first parameter is the rejection reason', () => {
// const p = Promise.reject('rejection');
const p = Promise.reject('oops');
return p.catch(reason => {
assert.equal(reason, 'oops');
});
});
});
describe('multiple `catch`es', () => {
it('only the first `catch` is called', () => {
const p = Promise.reject('1');
const p1 = p
// .catch(reason => 'void 0')
.catch(reason => reason + ' AND 2')
.catch(reason => `${reason} AND 3`)
;
return p1.then(result =>
assert.equal(result, '1 AND 2')
);
});
it('if a `catch` throws, the next `catch` catches it', () => {
const p = Promise.reject('1');
const p1 = p
.catch(reason => { throw Error(`${reason} AND 2`) })
// .catch(err => { throw Error(`${err.message} AND 3`) })
.catch(err => { return `${err.message} AND 3` })
.catch(err => `${err} but NOT THIS`)
;
return p1.then(result =>
assert.equal(result, '1 AND 2 AND 3')
);
});
});
});
// 80: Number - isNaN
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('`Number.isNaN()` determines if a value is `NaN`', function(){
it('it is a static function on `Number`', function() {
// const whatType = 'method';
const whatType = 'function';
assert.equal(typeof Number.isNaN, whatType);
});
describe('returns false', () => {
describe('for any not-Number type', () => {
it('like null', () => {
// const justNull = NaN;
const justNull = null;
assert.equal(Number.isNaN(justNull), false);
});
it('like a string', () => {
// const aString = NaN;
const aString = 'string';
assert.equal(Number.isNaN(aString), false);
});
it('like an object', () => {
// const anObject = NaN;
const anObject = {};
assert.equal(Number.isNaN(anObject), false);
});
describe('different to the global `isNaN` function (specified way before ES6)', () => {
it('an object gets converted to a Number before the check, and returns true therefore', () => {
// const fn = Number.isNaN;
const fn = object => Number.isNaN(Number(object));
assert.equal(fn({}), true);
});
it('a string gets converted to a Number first, and returns true therefore (even though its not `NaN`)', () => {
// const fn = Number.isNaN
const fn = (string) => Number.isNaN(Number(string))
assert.equal(fn('just a string'), true);
});
});
});
describe('for Numbers', () => {
it('like 0', () => {
// const zero = NaN;
const zero = 0;
assert.equal(Number.isNaN(zero), false);
});
it('or Infinity (+∞)', () => {
// const infinity = Nummmmber.POSITIVE_INFINITY;
const infinity = Number.POSITIVE_INFINITY;
assert.equal(Number.isNaN(infinity), false);
});
it('or the biggest Number (9007199254740991 (2^53−1))', () => {
// const max = N_u_m_b_e_r.MAX_SAFE_INTEGER;
const max = Number.MAX_SAFE_INTEGER;
assert.equal(Number.isNaN(max), false);
});
it('or a decimal number', () => {
// const pie = 3.1415926535897932;
const pi = 3.1415926535897932;
assert.equal(Number.isNaN(pi), false);
});
});
});
describe('returns true for', () => {
it('exactly `NaN`', () => {
// const notANumber = 1;
const notANumber = NaN;
assert.equal(Number.isNaN(notANumber), true);
});
it('zero divided by zero', () => {
// const zeroDividedByZero = 0 / 1;
const zeroDividedByZero = 0 / 0;
assert.equal(Number.isNaN(zeroDividedByZero), true);
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment