Skip to content

Instantly share code, notes, and snippets.

@mischah
Last active February 20, 2022 02:13
Show Gist options
  • Save mischah/3dfc9d1923f7f72e955189cfb0197fa2 to your computer and use it in GitHub Desktop.
Save mischah/3dfc9d1923f7f72e955189cfb0197fa2 to your computer and use it in GitHub Desktop.

One kata a day keeps ES5 away ✌️

// http://tddbin.com/?787#?kata=es6/language/template-strings/basics
// 1: template strings - basics
// To do: make all tests pass, leave the asserts unchanged!
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`;
assert.equal(str, 'like a string');
});
});
var x = 42;
var y = 23;
describe('can evaluate variables, which are wrapped in "${" and "}"', function() {
it('e.g. a simple variable "${x}" just gets evaluated', function() {
var evaluated = `x=${x}`;
assert.equal(evaluated, 'x=' + x);
});
it('multiple variables get evaluated too', function() {
var evaluated = `${x}+${y}`;
assert.equal(evaluated, x + '+' + y);
});
});
describe('can evaluate any expression, wrapped inside "${...}"', function() {
it('all inside "${...}" gets evaluated', function() {
var evaluated = `${x+y}`;
assert.equal(evaluated, x+y);
});
it('inside "${...}" can also be a function call', function() {
function getDomain(){
return document.domain;
}
var evaluated = `${getDomain()}`;
assert.equal(evaluated, 'tddbin.com');
});
});
});
// http://tddbin.com/?270#?kata=es6/language/template-strings/multiline
// 2: template strings - multiline
// To do: make all tests pass, leave the asserts unchanged!
describe('template string, can contain multiline content', function() {
it('a normal string can`t span across multiple lines', function() {
var normalString = 'line1' +
'\n' +
'line2';
assert.equal(normalString, 'line1\nline2');
});
it('wrapped in backticks it can span over multiple lines', function() {
var templateString = `line1
line2`;
assert.equal(templateString, 'line1\nline2');
});
it('even over more than two lines', function() {
var multiline = `line 1
line 2
line 3
line 4`;
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
${x}`;
assert.equal(multiline, 'line 1\n 42');
});
it('also here spaces matter', function() {
var multiline = `
${x}`;
assert.equal(multiline, '\n42');
});
});
});
// http://tddbin.com/?622#?kata=es6/language/template-strings/tagged
// 3: template strings - tagged
// To do: make all tests pass, leave the asserts unchanged!
describe('tagged template strings, are an advanced form of template strings', function() {
it('syntax: prefix the 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');
});
describe('the function can access each part of the template', function() {
describe('the 1st parameter - receives only the pure strings of the template string', function() {
function tagFunction(strings) {
return strings;
}
it('the strings are an array', function() {
var result = ['template string'];
assert.deepEqual(tagFunction `template string`, result);
});
it('expressions are NOT passed to it', function() {
var tagged = tagFunction `one${23}two`;
assert.deepEqual(tagged, ['one', 'two']);
});
});
describe('the 2nd and following parameters - contain the values of the processed substitution', function() {
var one = 1;
var two = 2;
var three = 3;
it('the 2nd parameter contains the first expression`s value', function() {
function firstValueOnly(strings, firstValue) {
return firstValue;
}
assert.equal(firstValueOnly `uno ${one}, dos ${two}`, 1);
});
it('the 3rd parameter contains the second expression`s value', function() {
function firstValueOnly(strings, firstValue, secondValue) {
return secondValue;
}
assert.equal(firstValueOnly`uno ${one}, dos ${two}`, 2);
});
it('using ES6 rest syntax, all values can be accessed via one variable', function() {
function valuesOnly(stringsArray, ...allValues) { // using the new ES6 rest syntax
return allValues;
}
assert.deepEqual(valuesOnly`uno=${one}, dos=${two}, tres=${three}`, [1, 2, 3]);
});
});
});
});
// http://tddbin.com/?243#?kata=es6/language/template-strings/raw
// 4: template strings - String.raw
// To do: make all tests pass, leave the asserts unchanged!
describe('on tagged template strings you can use the `raw` property 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.toString()[0];
return lineBreak;
}
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);
});
});
});
// http://tddbin.com/?938#?kata=es6/language/arrow-functions/basics
// 5: arrow functions - basics
// To do: make all tests pass, leave the asserts unchanged!
describe('arrow functions', function() {
it('are shorter to write', function() {
var func = () => {
return 'I am func';
};
assert.equal(func(), 'I am func');
});
it('a single expression, without curly braces returns too', 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(23), 24);
});
it('many params require parens', () => {
var func = (param, param1) => param + param1;
assert.equal(func(23, 42), 23+42);
});
it('body needs parens to return an object', () => {
var func = () => ({iAm: 'an object'});
assert.deepEqual(func(), {iAm: 'an object'});
});
});
// http://tddbin.com/?355#?kata=es6/language/arrow-functions/binding
// 6: arrow functions - binding
// To do: make all tests pass, leave the asserts unchanged!
class LexicallyBound {
getFunction() {
return () => this;
}
getArgumentsFunction() {
return () => arguments;
}
}
describe('arrow functions have lexical `this`, no dynamic `this`', () => {
it('bound at definition time, use `=>` ', function() {
var bound = new LexicallyBound();
var fn = bound.getFunction();
assert.strictEqual(fn(), bound);
});
it('can NOT bind a different context', function() {
var bound = new LexicallyBound();
var fn = bound.getFunction();
var anotherObj = {};
var expected = bound;
assert.strictEqual(fn.call(anotherObj), expected);
});
it('`arguments` doesnt work inside arrow functions', function() {
var bound = new LexicallyBound();
var fn = bound.getArgumentsFunction();
assert.equal(fn(1, 2).length, 0);
});
});
// http://tddbin.com/?319#?kata=es6/language/block-scoping/let
// 7: block scope - let
// To do: make all tests pass, leave the asserts unchanged!
describe('`let` restricts the scope of the variable to the current block', () => {
describe('`let` vs. `var`', () => {
it('`var` works as usual', () => {
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));
});
});
});
// http://tddbin.com/?934#?kata=es6/language/block-scoping/const
// 8: block scope - const
// To do: make all tests pass, leave the asserts unchanged!
describe('`const` is like `let` plus read-only', () => {
describe('scalar values are read-only', () => {
it('number', () => {
const constNum = 0;
assert.equal(constNum, 0);
});
it('string', () => {
const constString = 'I am a const';
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', () => {
const arr = [42, 23];
assert.equal(arr[0], 42);
});
it('object', () => {
const obj = {x: 1};
obj.x = 3;
assert.equal(obj.x, 3);
});
});
});
// http://tddbin.com/?161#?kata=es6/language/object-literal/basics
// 9: object-literals - basics
// To do: make all tests pass, leave the assert lines unchanged!
describe('The object literal allows for new shorthands', () => {
const x = 1;
const y = 2;
describe('with variables', () => {
it('the short version for `{x: x}` is {x}', () => {
const short = {y};
assert.deepEqual(short, {y: y});
});
it('works with multiple variables too', () => {
const short = {x, y};
assert.deepEqual(short, {x: x, y: y});
});
});
describe('with methods', () => {
const func = () => func;
it('using the name only uses it as key', () => {
const short = {func};
assert.deepEqual(short, {func: func});
});
it('a different key must be given explicitly, just like before ES6', () => {
const short = {otherKey: func};
assert.deepEqual(short, {otherKey: func});
});
it('inline functions, can written as `obj={func(){}}` instead of `obj={func:function(){}}`', () => {
const short = {
inlineFunc() { return 'I am inline'}
};
assert.deepEqual(short.inlineFunc(), 'I am inline');
});
});
});
// http://tddbin.com/?451#?kata=es6/language/destructuring/array
// 10: destructuring - array
// To do: make all tests pass, leave the assert lines unchanged!
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('swap two variables, in one operation', () => {
let [y, x] = ['ax', 'why'];
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]);
});
});
// http://tddbin.com/?808#?kata=es6/language/destructuring/string
// 11: destructuring - string
// To do: make all tests pass, leave the assert lines unchanged!
describe('destructuring also works on strings', () => {
it('destructure every character', () => {
let [a, b, c] = 'abc';
assert.deepEqual([a, b, c], ['a', 'b', 'c']);
});
it('missing characters are undefined', () => {
const [a, b, c] = 'ab';
assert.equal(c, void 0);
});
it('unicode character work too', () => {
const [, , coffee] = 'a ☕';
assert.equal(coffee, '\u{2615}');
});
});
// http://tddbin.com/#?kata=es6/language/destructuring/object
// 12: destructuring - object
// To do: make all tests pass, leave the assert lines unchanged!
describe('destructuring objects', () => {
it('is simple', () => {
const object = {x: 1};
const {x} = object;
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} = String.prototype;
assert.equal(substr, String.prototype.substr);
});
});
});
// http://tddbin.com/#?kata=es6/language/destructuring/defaults
// 13: destructuring - defaults
// To do: make all tests pass, leave the assert lines unchanged!
describe('destructuring can also have default values', () => {
it('for an empty array', () => {
const [a=1] = [];
assert.equal(a, 1);
});
it('for a missing value', () => {
const [a,b=2,c] = [1,,3];
assert.equal(b, 2);
});
it('in an object', () => {
const {a, b=2} = {a: 1};
assert.equal(b, 2);
});
it('if the value is undefined', () => {
const {a, b=2} = {a: 1, b: void 0};
assert.strictEqual(b, 2);
});
it('also a string works with defaults', () => {
const [a, b=2] = '1';
assert.equal(a, '1');
assert.equal(b, 2);
});
});
// http://tddbin.com/#?kata=es6/language/destructuring/parameters
// 14: destructuring - parameters
// To do: make all tests pass, leave the assert lines unchanged!
describe('destructuring function parameters', () => {
describe('destruct parameters', () => {
it('multiple params from object', () => {
const fn = ({name, id}) => {
assert.equal(id, 42);
assert.equal(name, 'Wolfram');
};
const user = {name: 'Wolfram', id: 42};
fn(user);
});
it('multiple params from array/object', () => {
const fn = ([,{name}]) => {
assert.equal(name, 'Alice');
};
const users = [{name: 'nobody'}, {name: 'Alice', id: 42}];
fn(users);
});
});
describe('default values', () => {
it('for simple values', () => {
const fn = ({id, name='Bob'}) => {
assert.strictEqual(id, 23);
assert.strictEqual(name, 'Bob');
};
fn({id:23});
});
it('for a missing array value', () => {
const defaultUser = {id: 23, name: 'Joe'};
const fn = ([user=defaultUser]) => {
assert.deepEqual(user, defaultUser);
};
fn([]);
});
it('mix of parameter types', () => {
const fn = (id=1, [arr=2], {obj=3}) => {
assert.equal(id, 1);
assert.equal(arr, 2);
assert.equal(obj, 3);
};
fn(void 0, [], {});
});
});
});
// http://tddbin.com/#?kata=es6/language/destructuring/rename
// 15: destructuring - assign
// To do: make all tests pass, leave the assert lines unchanged!
describe('assign object property values to new variables while destructuring', () => {
describe('for simple objects', function() {
it('use a colon after the property name, like so `propertyName: newName`', () => {
const {x: y} = {x: 1};
assert.equal(y, 1);
});
it('assign a new name and give it a default value using `= <default value>`', () => {
const {x: y=42} = {y: 23};
assert.equal(y, 42);
});
});
describe('for function parameter names', function() {
it('do it the same way, with a colon behind it', () => {
const fn = ({x:y}) => {
assert.equal(y, 1);
};
fn({x: 1});
});
it('giving it a default value is possible too, like above', () => {
const fn = ({x: y=3}) => {
assert.equal(y, 3);
};
fn({});
});
});
});
// http://tddbin.com/#?kata=es6/language/object-literal/computed-properties
// 16: object-literal - computed properties
// To do: make all tests pass, leave the assert lines unchanged!
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 = 'Name';
const obj = {['property' + what]: null};
assert.equal('propertyName' in obj, true);
});
it('accessor keys can be computed names too', () => {
const obj = {
get ['key']() {return 1}
};
assert.equal(obj.key, 1);
});
});
// http://tddbin.com/?793#?kata=es6/language/unicode/in-strings
// 17: unicode - in strings
// To do: make all tests pass, leave the assert lines unchanged!
describe('unicode strings', () => {
it('are \\u prefixed', () => {
const nuclear = '\u2622';
assert.equal(nuclear, '☢');
});
it('value is 4 bytes/digits', () => {
const nuclear = '\u2622';
assert.equal(`no more ${nuclear}`, 'no more ☢');
});
it('value is hexadecimal', () => {
const nuclear = `\u006E\u006F more \u2622`;
assert.equal(nuclear, 'no more ☢');
});
it('curly braces may surround the value', () => {
const nuclear = `\u{006E}\u{006F} more \u2622`;
assert.equal(nuclear, 'no more ☢');
});
});
// http://tddbin.com/?389#?kata=es6/language/rest/as-parameter
// 18: rest - as-parameter
// To do: make all tests pass, leave the assert lines unchanged!
describe('rest in function params', () => {
it('must be the last parameter', () => {
const fn = (first, ...rest) => {
assert.deepEqual([1, 2], rest);
};
fn(0, 1, 2);
});
it('can be used to get all other parameters', () => {
const fn = (firstParam, secondParam, ...rest) => {
assert.deepEqual([3,4], rest);
};
fn(null, 2, 3, 4);
});
it('makes `arguments` obsolete', () => {
const fn = (...args) => {
assert.deepEqual([42, 'twenty three', 'win'], args);
};
fn(42, 'twenty three', 'win');
});
it('eliminate `arguments`!!!', () => {
const fn = (first, ...rest) => rest;
const rest = fn(1, 2, 3);
assert.deepEqual([2, 3], rest);
});
});
// http://tddbin.com/?375#?kata=es6/language/rest/with-destructuring
// 19: rest - with-destructuring
// To do: make all tests pass, leave the assert lines unchanged!
describe('rest with destructuring', () => {
it('rest parameter must be last', () => {
const [...all] = [1, 2, 3, 4];
assert.deepEqual(all, [1, 2, 3, 4]);
});
it('assign rest of an array to a variable', () => {
const [first, ...all] = [1, 2, 3, 4];
assert.deepEqual(all, [2, 3, 4]);
});
// the following are actually using `spread` ... oops, to be fixed
it('concat differently', () => {
const theEnd = [3, 4];
const allInOne = [1, 2, ...theEnd];
assert.deepEqual(allInOne, [1, 2, 3, 4]);
});
it('`apply` made simple, even for constructors', () => {
const theDate = [2015, 1, 1];
const date = new Date(...theDate);
assert.deepEqual(new Date(2015, 1, 1), date);
});
});
// http://tddbin.com/#?kata=es6/language/spread/with-arrays
// 20: spread - with-arrays
// To do: make all tests pass, leave the assert lines unchanged!
describe('spread with arrays', () => {
it('extracts each array item', function() {
const [a, b] = [...[1, 2]];
assert.equal(a, 1);
assert.equal(b, 2);
});
it('in combination with rest', function() {
const [a, b, ...rest] = [...[1, 2, 3, 4, 5]];
assert.equal(a, 1);
assert.equal(b, 2);
assert.deepEqual(rest, [3, 4, 5]);
});
it('spreading into the rest', function() {
const [...rest] = [...[1, 2, 3, 4, 5]];
assert.deepEqual(rest, [1, 2, 3, 4, 5]);
});
describe('used as function parameter', () => {
it('prefix with `...` to spread as function params', function() {
const magicNumbers = [1, 2];
const fn = (magicA, magicB) => {
assert.deepEqual(magicNumbers[0], magicA);
assert.deepEqual(magicNumbers[1], magicB);
};
fn(...magicNumbers);
});
it('pass an array of numbers to Math.max()', function() {
const max = Math.max(...[23, 0, 42]);
assert.equal(max, 42);
});
});
});
// http://tddbin.com/#?kata=es6/language/spread/with-strings
// 21: spread - with-strings
// To do: make all tests pass, leave the assert lines unchanged!
describe('spread with strings', () => {
it('simply spread each char of a string', function() {
const [a, b] = [...'ab'];
assert.equal(a, 'a');
assert.equal(b, 'b');
});
it('extracts each array item', function() {
const [,a,b] = ['a', ...'12'];
assert.equal(a, 1);
assert.equal(b, 2);
});
it('works anywhere inside an array (must not be last)', function() {
const letters = [...'a', ...'bcd', ...'e', ...'f'];
assert.equal(letters.length, 6);
});
it('dont confuse with the rest operator', function() {
const rest = [...'1234', ...'5'];
assert.deepEqual(rest, [1, 2, 3, 4, 5]);
});
it('passed as function parameter', function() {
const max = Math.max(...[1,2,3,4,5]);
assert.deepEqual(max, 5);
});
});
// http://tddbin.com/?953#?kata=es6/language/class/creation
// 22: class - creation
// To do: make all tests pass, leave the assert lines unchanged!
describe('class creation', () => {
it('is as simple as `class XXX {}`', function() {
class TestClass {};
const instance = new TestClass();
assert.equal(typeof instance, 'object');
});
it('class is block scoped', () => {
class Outside {}
{ class Inside {} }
assert.equal(typeof Inside, 'undefined');
});
it('special method is `constructor`', function() {
class User {
constructor(id) {
this.id = id;
}
}
const user = new User(42);
assert.equal(user.id, 42);
});
it('defining a method is simple', function() {
class User {
writesTests() { return false; }
}
const notATester = new User();
assert.equal(notATester.writesTests(), false);
});
it('multiple methods need no commas (opposed to object notation)', function() {
class User {
wroteATest() { this.everWroteATest = true; }
isLazy() {
if (this.everWroteATest === true) {
return false;
}
return true;
}
}
const tester = new User();
assert.equal(tester.isLazy(), true);
tester.wroteATest();
assert.equal(tester.isLazy(), false);
});
it('anonymous class', () => {
const classType = typeof class {};
assert.equal(classType, 'function');
});
});
// http://tddbin.com/#?kata=es6/language/class/accessors
// 23: class - accessors
// To do: make all tests pass, leave the assert lines unchanged!
describe('class accessors (getter and setter)', () => {
it('only a getter is defined like a method prefixed with `get`', () => {
class MyAccount {
get balance() { return Infinity; }
}
assert.equal(new MyAccount().balance, Infinity);
});
it('a setter has the prefix `set`', () => {
class MyAccount {
get balance() { return this.amount; }
set balance(amount) { this.amount = amount; }
}
const account = new MyAccount();
account.balance = 23;
assert.equal(account.balance, 23);
});
describe('dynamic accessors', () => {
it('a dynamic getter name is enclosed in [ and ]', function() {
const balance = 'yourMoney';
class YourAccount {
get [balance]() { return -Infinity; }
}
assert.equal(new YourAccount().yourMoney, -Infinity);
});
it('a dynamic setter name as well', function() {
const propertyName = 'balance';
class MyAccount {
get [propertyName]() { return this.amount; }
set [propertyName](amount) { this.amount = 23; }
}
const account = new MyAccount();
account.balance = 23;
assert.equal(account.balance, 23);
});
});
});
// http://tddbin.com/#?kata=es6/language/class/static
// 24: class - static keyword
// To do: make all tests pass, leave the assert lines unchanged!
describe('inside a class you can use the `static` keyword', () => {
describe('for methods', () => {
class IntegrationTest {}
class UnitTest {}
it('a static method just has the prefix `static`', () => {
class TestFactory {
static makeTest() { return new UnitTest(); }
}
assert.ok(TestFactory.makeTest() instanceof UnitTest);
});
it('the method name can be dynamic/computed at runtime', () => {
const methodName = 'createTest';
class TestFactory {
static [methodName]() { return new UnitTest(); }
}
assert.ok(TestFactory.createTest() instanceof UnitTest);
});
});
describe('for accessors', () => {
it('a getter name can be static, just prefix it with `static`', () => {
class UnitTest {
static get testType() { return 'unit'; }
}
assert.equal(UnitTest.testType, 'unit');
});
it('even a static getter name can be dynamic/computed at runtime', () => {
const type = 'test' + 'Type';
class IntegrationTest {
static get [type]() { return 'integration'; }
}
assert.ok('testType' in IntegrationTest);
assert.equal(IntegrationTest.testType, 'integration');
});
});
});
// http://tddbin.com/#?kata=es6/language/class/extends
// 25: class - extends
// To do: make all tests pass, leave the assert lines unchanged!
describe('classes can inherit from another', () => {
describe('the default super class is Object', () => {
it('class A is an instance of Object', () => {
class A {}
assert.equal(new A() instanceof Object, true);
});
it('B extends A, B is also instance of Object', () => {
class A {}
class B extends A {}
assert.equal(new B() instanceof A, true);
assert.equal(new B() instanceof Object, true);
});
it('class can extend `null`, not an instance of Object', () => {
class NullClass extends null {}
let nullInstance = new NullClass();
assert.equal(nullInstance instanceof Object, false);
});
});
describe('instance of', () => {
it('when B inherits from A, `new B()` is also an instance of A', () => {
class A {};
class B extends A {}
assert.equal(new B() instanceof A, true);
});
it('extend over multiple levels', () => {
class A {}
class B extends A {}
class C extends B {}
let instance = new C();
assert.equal(instance instanceof A, true);
});
});
});
// http://tddbin.com/#?kata=es6/language/class/more-extends
// 26: class - more-extends
// To do: make all tests pass, leave the assert lines unchanged!
describe('class can inherit from another', () => {
it('extend an `old style` "class", a function, still works', () => {
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(B);
assert.equal(isIt, true);
});
it('A`s prototype is also B`s prototype', () => {
const proto = B.prototype;
// Remember: don't touch the assert!!! :)
assert.equal(A.prototype.isPrototypeOf(proto), true);
});
});
describe('`extends` using an expression', () => {
it('eg the inline assignment of the parent class', () => {
let A;
class B extends (A = class {}) {}
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(true)) {}
assert.equal(Object.getPrototypeOf(B.prototype), null);
});
});
});
// http://tddbin.com/#?kata=es6/language/class/super-in-method
// 27: class - super inside a method
// To do: make all tests pass, leave the assert lines unchanged!
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; }}
// 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(); } }
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() {}}
assert.equal(new B().hasSuper(), void 0);
});
it('`super` works across any number of levels of inheritance', () => {
class A { iAmSuper() { return this.youAreSuper; } }
class B extends A { constructor() { super(); this.youAreSuper = true; } }
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(); } }
assert.equal(new B().getMethod(), void 0);
});
});
// http://tddbin.com/#?kata=es6/language/class/super-in-constructor
// 28: class - super in constructor
// To do: make all tests pass, leave the assert lines unchanged!
describe('class', () => {
it('if you `extend` a class, use `super()` to call the parent constructor', () => {
class A {constructor() { this.levels = 1; }}
class B extends A {
constructor() {
super();
this.levels++;
}
}
assert.equal(new B().levels, 2);
});
it('`super()` may also take params', () => {
class A {constructor(startValue=1, addTo=1) { this.counter = startValue + addTo; }}
class B extends A {
constructor(...args) {
super(43);
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 A extends null {
constructor() {
super();
this.isTop = !super.constructor;
}
}
assert.equal(new A().isTop, false);
});
});
// http://tddbin.com/#?kata=es6/language/array-api/from
// 29: array - `Array.from` static method
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Array.from` converts an array-like object or list into an Array', () => {
const arrayLike = {0: 'one', 1: 'two', length: 2};
it('call `Array.from` with an array-like object', function() {
const arr = Array.from(arrayLike);
assert.deepEqual(arr, ['one', 'two']);
});
it('a DOM node`s classList object can be converted', function() {
document.body.classList.add('some');
document.body.classList.add('other');
const classList = Array.from(document.body.classList);
assert.equal(''+classList, ''+['some', 'other']);
});
it('convert a NodeList to an Array and `filter()` works on it', function() {
const nodeList = document.querySelectorAll('body');
const bodies = Array.from(nodeList).filter((node) => node === document.body);
assert.deepEqual(bodies, [document.body]);
});
describe('custom conversion using a map function as second param', () => {
it('we can modify the value before putting it in the array', function() {
const arr = Array.from(arrayLike, (value) => value.toUpperCase());
assert.deepEqual(arr, ['ONE', 'TWO']);
});
it('and we also get the object`s key as second parameter', function() {
const arr = Array.from(arrayLike, (value, key) => `${key}=${value}`);
assert.deepEqual(arr, ['0=one', '1=two']);
});
});
});
// http://tddbin.com/#?kata=es6/language/array-api/of
// 30: array - `Array.of` static method
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Array.of` creates an array with the given arguments as elements', () => {
it('dont mix it up with `Array(10)`, where the argument is the array length', () => {
const arr = Array.of(10);
assert.deepEqual(arr, [10]);
});
it('puts all arguments into array elements', () => {
const arr = Array.of(1,2);
assert.deepEqual(arr, [1, 2]);
});
it('takes any kind and number of arguments', () => {
const starter = [1, 2];
const end = [3, '4'];
const arr = Array.of([...starter], ...end);
assert.deepEqual(arr, [[1, 2], 3, '4']);
});
});
// http://tddbin.com/#?kata=es6/language/array-api/fill
// 31: array - `Array.prototype.fill` method
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Array.prototype.fill` can fill up an array with one value', () => {
it('`fill(0)` will populate `0` into each array element', function() {
const arr = new Array(3).fill(0);
assert.deepEqual(arr, [0, 0, 0]);
});
it('fill only changes content, adds no new elements', function() {
const arr = [].fill();
assert.deepEqual(arr, []);
});
it('second parameter to `fill()` is the position where to start filling', function() {
const fillPosition = 2;
const arr = [1,2,3].fill(42, fillPosition);
assert.deepEqual(arr, [1, 2, 42]);
});
it('third parameter is the position where filling stops', function() {
const fillStartAt = 1;
const fillEndAt = 2;
const arr = [1,2,3].fill(42, fillStartAt, fillEndAt);
assert.deepEqual(arr, [1, 42, 3]);
});
});
// http://tddbin.com/#?kata=es6/language/array-api/find
// 32: array - `Array.prototype.find`
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Array.prototype.find` makes finding items in arrays easier', () => {
it('takes a compare function', function() {
const found = [false, true].find(value => value === true);
assert.equal(found, true);
});
it('returns the first value found', function() {
const found = [2, 3].find((item, index) => index === 0);
assert.equal(found, 2);
});
it('returns `undefined` when nothing was found', function() {
const found = [].find(item => item === 2);
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(person => person.name === 'Alice');
assert.equal(found, alice);
});
});
// http://tddbin.com/#?kata=es6/language/array-api/findIndex
// 33: array - `Array.prototype.findIndex`
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Array.prototype.findIndex` makes finding items in arrays easier', () => {
it('takes a compare function, returns the index where it returned true', function() {
const foundAt = [false, true].findIndex(item => item === true);
assert.equal(foundAt, 1);
});
it('returns the first position it was found at', function() {
const foundAt = [0, 1, 1, 1].findIndex(item => item === 1);
assert.equal(foundAt, 1);
});
it('returns `-1` when nothing was found', function() {
const foundAt = [1, 2, 3].findIndex(item => item > 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(element, 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}) => name === 'Alice');
assert.equal(foundAt, 1);
});
});
// http://tddbin.com/#?kata=es6/language/symbol/basics
// 34: symbol
// 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!
describe('Symbol', function() {
it('`Symbol` lives in the global scope', function(){
const expected = window.Symbol;
assert.equal(Symbol, expected);
});
it('every `Symbol()` is unique', function(){
const sym1 = Symbol();
const sym2 = Symbol();
assert.notEqual(sym1, sym2);
});
it('every `Symbol()` is unique, also with the same parameter', function(){
var sym1 = Symbol('foo');
var sym2 = Symbol('foo');
assert.notEqual(sym1, sym2);
});
it('`typeof Symbol()` returns "symbol"', function(){
const theType = typeof Symbol();
assert.equal(theType, 'symbol');
});
it('`new Symbol()` throws an exception, to prevent creation of Symbol wrapper objects', function(){
function fn() {
new Symbol();
}
assert.throws(fn);
});
});
// http://tddbin.com/#?kata=es6/language/symbol/for
// 35: Symbol.for - retrieves or creates a runtime-wide symbol
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Symbol.for` for registering Symbols globally', function() {
it('creates a new symbol (check via `typeof`)', function() {
const symbolType = typeof Symbol.for('symbol name');
assert.equal(symbolType, 'symbol');
});
it('stores the symbol in a runtime-wide registry and retrieves it from it', function() {
const sym = Symbol.for('new symbol');
const sym1 = Symbol.for('new symbol');
assert.equal(sym, sym1);
});
it('is different to `Symbol()` which creates a symbol every time and does not store it', function() {
var localSymbol = Symbol.for('new symbol');
var globalSymbol = Symbol('new symbol');
assert.notEqual(globalSymbol, localSymbol);
});
describe('`.toString()` on a Symbol', function() {
const localSymbol = Symbol('new symbol');
const symbolFromRegistry = Symbol.for('new symbol');
it('also contains the key given to `Symbol.for()`', function() {
const description = symbolFromRegistry.toString();
assert.equal(description, 'Symbol(new symbol)');
});
describe('NOTE: the description of two different symbols', function() {
it('might be the same', function() {
const localDescription = localSymbol.toString();
const fromRegistryDescription = symbolFromRegistry.toString();
assert.equal(localDescription, fromRegistryDescription);
});
it('but the symbols are not the same!', function() {
assert.notEqual(localSymbol, symbolFromRegistry);
});
});
});
});
// http://tddbin.com/#?kata=es6/language/symbol/keyFor
// 36: Symbol.keyFor - retrieves a shared symbol key from the global symbol registry
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Symbol.keyFor()` gets the symbol key for a given symbol', function() {
const sym = Symbol.for('foo');
it('pass the symbol to `keyFor()` and you get its key', function() {
const key = Symbol.keyFor(sym);
assert.equal(key, 'foo');
});
it('local symbols are not in the runtime-wide registry', function() {
// hint: `Symbol()` creates a local symbol!
const localSymbol = Symbol('foo');
const key = Symbol.keyFor(localSymbol);
assert.equal(key, void 0);
});
it('well-known symbols are not in the runtime-wide registry either', function() {
const key = Symbol.keyFor(Symbol.iterator);
assert.equal(key, void 0);
});
it('for non-Symbols throws an error', function() {
function fn() {
Symbol.keyFor(unknown);
}
assert.throws(fn);
});
});
// http://tddbin.com/#?kata=es6/language/iterator/array
// 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!
describe('array is a built-in iterable object', function() {
const arr = ['a', 'B', 'see'];
describe('the iterator', function() {
it('an array has an iterator, which is a function', function() {
const iterator = arr[Symbol.iterator];
const theType = typeof iterator;
const expected = 'function';
assert.equal(theType, expected);
});
it('can be looped with `for-of`, which expects an iterable', function() {
let count = 0;
for (let value of arr) {
count++;
}
assert.equal(count, arr.length);
});
});
describe('the iterator protocol', function() {
it('calling `next()` on an iterator returns an object according to the iterator protocol', function() {
const iterator = arr[Symbol.iterator]();
const firstItem = iterator.next();
assert.deepEqual(firstItem, {done: false, value: 'a'});
});
it('the after-last element has done=true', function() {
const arr = [];
const iterator = arr[Symbol.iterator]();
const afterLast = iterator.next();
assert.deepEqual(afterLast, {done: true, value: void 0});
});
});
});
// http://tddbin.com/#?kata=es6/language/iterator/string
// 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!
describe('string is a built-in iterable object', function() {
const s = 'abc';
describe('string is iterable', function() {
it('the string`s object key `Symbol.iterator` is a function', function() {
const isA = typeof s[Symbol.iterator];
assert.equal(isA, 'function');
});
it('use `Array.from()` to make an array out of any iterable', function(){
const arr = Array.from(s);
assert.deepEqual(arr, ['a', 'b', 'c']);
});
});
describe('a string`s iterator', function() {
let iterator;
beforeEach(function() {
iterator = s[Symbol.iterator]();
});
it('has a special string representation', function(){
const description = iterator.toString();
assert.equal(description, '[object String Iterator]');
});
it('`iterator.next()` returns an object according to the iterator protocol', function(){
const value = iterator.next();
assert.deepEqual(value, {done: false, value: 'a'});
});
it('the after-last call to `iterator.next()` says done=true, no more elements', function(){
iterator.next();
iterator.next();
iterator.next();
assert.equal(iterator.next().done, true);
});
});
});
// http://tddbin.com/#?kata=es6/language/iterator/protocol
// 39: iterator - custom. Iterable is a protocol, when implemented allows objects
// to customize their iteration behavior, such as what values are looped over in a for..of construct.
// read more at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
// To do: make all tests pass, leave the assert lines unchanged!
// Follow the hints of the failure messages!
describe('A simple iterable without items inside, implementing the right protocol', () => {
function iteratorFunction() {
return {
next: function() {
return {done: true};
}
};
}
describe('the `iteratorFunction` needs to comply to the iterator protocol', function() {
it('must return an object', function() {
assert.equal(typeof iteratorFunction(), 'object');
});
it('the object must have a function assigned to a key `next`', function() {
assert.equal(typeof iteratorFunction().next, 'function');
});
it('calling `next()` must return an object with `{done: true}`', function() {
assert.deepEqual(iteratorFunction().next(), {done: true});
});
});
let iterable = {};
iterable[Symbol.iterator] = iteratorFunction;
beforeEach(function() {
iterable;
});
describe('the iterable', function() {
it('must be an object', function() {
assert.equal(typeof iterable, 'object');
});
it('must have the iterator function assigned to the key `Symbol.iterator`', function() {
assert.equal(iterable[Symbol.iterator], iteratorFunction);
});
});
describe('using the iterable', function() {
it('it contains no values', function() {
let values = '';
for (let value of iterable) {
values += value;
}
assert.equal(values, '');
});
it('has no `.length` property', function() {
const hasLengthProperty = iterable.length || false;
assert.equal(hasLengthProperty, false);
});
describe('can be converted to an array', function() {
it('using `Array.from()`', function() {
const arr = Array.from(iterable);
assert.equal(Array.isArray(arr), true);
});
it('where `.length` is still 0', function() {
const arr = Array.from(iterable);
const length = arr.length;
assert.equal(length, 0);
});
});
});
});
// http://tddbin.com/#?kata=es6/language/iterator/usages
// 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!
// Consumable users:
// - `consumableUser` contains a consumable user,
// - `anyLeft` tells if there is any user left that can be consumed.
class ConsumableUsers {
constructor() {
this.users = ['Alice', 'Bob'];
this.isEmpty = false;
}
get nextUser() {
if (this.users.length > 0) {
return `user: ${this.users.shift()}`;
}
this.isEmpty = true;
return undefined;
}
get anyLeft() {
return this.isEmpty === true;
}
}
describe('Iterator usages', () => {
let usersIterable;
beforeEach(function(){
const consumableUsers = new ConsumableUsers();
function iteratorFunction() {
return {
next: function() {
return {value: consumableUsers.nextUser, done: consumableUsers.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.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 = 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 of usersIterable) users.push(user);
assert.deepEqual(users, ['user: Alice', 'user: Bob']);
});
it('use the spread-operator to convert/add iterable to an array', function() {
const users = ['noname', ...usersIterable];
assert.deepEqual(users, ['noname', 'user: Alice', 'user: Bob']);
});
it('destructure an iterable like an array', function() {
const [firstUser, secondUser] = usersIterable;
assert.equal(firstUser, 'user: Alice');
assert.equal(secondUser, 'user: Bob');
})
});
});
});
// http://tddbin.com/#?kata=es6/language/array-api/entries
// 41: array - entries
// To do: make all tests pass, leave the assert lines unchanged!
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 = 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.entries().next().value;
assert.deepEqual(value, [0, 'one']);
});
});
});
// http://tddbin.com/#?kata=es6/language/array-api/keys
// 42: array - `Array.prototype.keys`
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Array.prototype.keys` returns an iterator for all keys in the array', () => {
it('`keys()` returns an iterator', function() {
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 = [1, 2, 3];
const keys = Array.from(arr.keys());
assert.deepEqual(keys, [0, 1, 2]);
});
it('empty array contains no keys', function() {
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.keys()];
assert.deepEqual(keys, [0, 1]);
});
it('also includes holes in sparse arrays', function() {
const arr = ['a', , 'c'];
const keys = [...arr.keys()];
assert.deepEqual(keys, [0, 1, 2]);
});
});
// http://tddbin.com/#?kata=es6/language/array-api/values
// 43: array - `Array.prototype.values`
// To do: make all tests pass, leave the assert lines unchanged!
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 iterator = arr.values();
iterator.next();
iterator.next();
iterator.next();
assert.deepEqual(iterator.next(), {value: void 0, done: true});
});
it('use iterator to drop first key', function() {
const arr = ['keys', 'values', 'entries'];
const iterator = arr.values();
iterator.next();
assert.deepEqual([...iterator], ['values', 'entries']);
});
it('empty array contains no values', function() {
const arr = [];
const values = [...arr.values()];
assert.equal(values.length, 0);
});
it('a sparse array without real values has values though', function() {
const arr = [,,];
const keys = [...arr.values()];
assert.deepEqual(keys, [void 0, void 0]);
});
it('also includes holes in sparse arrays', function() {
const arr = ['a',,'c'];
assert.deepEqual([...arr.values()], ['a', void 0, 'c']);
});
});
// http://tddbin.com/#?kata=es6/language/map/basics
// 44: Map - basics
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Map` is a key/value map', function(){
it('`Map` is a new global constructor function', function() {
assert.equal(typeof Map, 'function');
});
it('provides `new Map().set()` to add key+value pair, `get()` to read it by key', function() {
let map = new 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().set('key', 'value');
const hasIt = map.has('key');
assert.equal(hasIt, true);
});
it('a map is iterable', function() {
let map = new Map()
.set('1', 'one')
.set('2', 'two');
const mapAsArray = Array.from(map); // hint: kata #29 http://tddbin.com/#?kata=es6/language/array-api/from
assert.deepEqual(mapAsArray, [['1', 'one'], ['2', 'two']]);
});
it('complex types can be keys', function() {
const otherObj = {x: 1};
let map = new Map().set(otherObj, '');
map.delete(otherObj);
assert.equal(map.has(otherObj), false);
});
});
// http://tddbin.com/#?kata=es6/language/map/get
// 45: Map.prototype.get()
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Map.prototype.get` returns the element from the map for a key', function(){
it('`get(key)` returns the value stored for this key', function() {
let map = new Map();
map.set('key', 'value');
const value = map.get('key');
assert.equal(value, 'value');
});
it('multiple calls still return the same value', function() {
let map = new Map();
map.set('value', 'value');
var value = map.get(map.get(map.get('value')));
assert.equal(value, 'value');
});
it('requires exactly the value as passed to `set()`', function() {
let map = new Map();
const obj = {};
map.set(obj, 'object is key');
assert.equal(map.get(obj), 'object is key');
});
it('leave out the key, and you get the value set for the key `undefined` (void 0)', function() {
let map = new Map();
map.set(void 0, 'yo');
const value = map.get();
assert.equal(value, 'yo');
});
it('returns undefined for an unknown key', function() {
let map = new Map();
map.set(void 0, 1);
const value = map.get('unknown');
assert.equal(value, void 0);
});
});
// http://tddbin.com/#?kata=es6/language/map/set
// 46: Map.prototype.set()
// To do: make all tests pass, leave the assert lines unchanged!
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().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().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()
.set('key', 'value')
.set('key', 'value1');
assert.equal(map.get('key'), 'value1');
});
it('`set()` returns the map object, it`s chainable', function() {
let map = new Map()
.set(1, 'one')
.set(2, 'two')
.set(3, 'three');
assert.deepEqual([...map.keys()], [1,2,3]);
assert.deepEqual([...map.values()], ['one', 'two', 'three']);
});
});
// http://tddbin.com/#?kata=es6/language/set/basics
// 47: Set - basics
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Set` lets you store unique values of any type', function(){
it('`Set` is a new global constructor function', function() {
assert.equal(typeof Set, 'function');
});
it('every value in a set is unique', function() {
let set = new Set();
set.add(1);
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).add('1');
assert.equal(set.size, 2);
});
it('even NaN is equal to NaN', function() {
let set = new Set();
set.add(NaN);
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);
assert.deepEqual([...set.values()], [+0]);
});
});
// http://tddbin.com/#?kata=es6/language/set/add
// 48: Set - add
// To do: make all tests pass, leave the assert lines unchanged!
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 ones', function() {
const fn = () => {};
set.add(1);
set.add(1);
set.add(fn);
set.add(fn);
assert.equal(set.size, 2);
});
it('is chainable', function() {
set.add(1).add(2);
assert.equal(set.has(2), true);
});
it('call without params adds undefined', function() {
set.add()
assert.equal(set.has(void 0), true);
});
it('0, -0 and +0 are equal', function() {
set.add(0).add(-0).add(+0);
assert.equal(set.has(+0), true);
});
});
// http://tddbin.com/#?kata=es6/language/generator/creation
// 49: Generator - creation
// To do: make all tests pass, leave the assert lines unchanged!
describe('generator can be created in multiple ways', function() {
it('the most common way is by adding `*` after `function`', function() {
function* g() {}
assertIsGenerator(g());
});
it('as a function expression, by adding a `*` after `function`', function() {
let g = function*() {};
assertIsGenerator(g());
});
it('inside an object by prefixing the function name with `*`', function() {
let obj = {
*g() {}
};
assertIsGenerator(obj.g());
});
it('computed generator names, are just prefixed with a `*`', function() {
const generatorName = 'g';
let obj = {
*[generatorName]() {}
};
assertIsGenerator(obj.g());
});
it('inside a class the same way', function() {
const generatorName = 'g';
class Klazz {
*[generatorName]() {}
}
assertIsGenerator(new Klazz().g());
});
function assertIsGenerator(gen) {
const toStringed = '' + gen;
assert.equal(toStringed, '[object Generator]');
}
});
// http://tddbin.com/#?kata=es6/language/generator/iterator
// 50: Generator - iterator
// To do: make all tests pass, leave the assert lines unchanged!
describe('a generator 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 = 'object';
assert.equal(typeof generator, typeOfTheGenerator);
});
it('a generator object has a key `Symbol.iterator`', function() {
const key = Symbol.iterator;
assert.equal(key in generator, true);
});
it('the `Symbol.iterator` is a function', function() {
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 generator) {
// no statements needed
}
}
assert.doesNotThrow(iterateForOf);
});
});
// http://tddbin.com/#?kata=es6/language/generator/yield
// 51: Generator - Yield Expressions
// To do: make all tests pass, leave the assert lines unchanged!
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 resumes the generator until all values are received', () => {
let values = Array.from(generator);
assert.deepEqual(values, ['hello', 'world']);
});
describe('after the first `generator.next()` call', function() {
it('the value is "hello"', function() {
const {value} = generator.next();
assert.equal(value, 'hello');
});
it('and `done` is false', function() {
const {done} = generator.next();
assert.equal(done, false);
});
});
describe('after the second `next()` call', function() {
let secondItem;
let firstItem;
beforeEach(function() {
firstItem = generator.next();
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.next();
assert.equal(done, true);
});
});
});
// http://tddbin.com/#?kata=es6/language/generator/send-value
// 52: Generator - Send value to a generator
// To do: make all tests pass, leave the assert lines unchanged!
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().value, iterator.next().value];
assert.deepEqual(convertedToAnArray, iteratedOver);
});
it('pass a value to the iterator', function() {
function* generatorFunction() {
const 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() {
const param = yield 1;
yield param;
}
let iterator = generatorFunction();
const values = [
iterator.next('irrelevant').value,
iterator.next(2).value
];
assert.deepEqual(values, [1, 2]);
});
});
// http://tddbin.com/#?kata=es6/language/map/initialize
// 53: Map - initialize
// To do: make all tests pass, leave the assert lines unchanged!
describe('initialize a `Map`', function(){
it('a `new Map()` is empty, has size=0', function() {
const mapSize = new Map().size;
assert.equal(mapSize, 0);
});
it('init Map with `[[]]` has a size=1', function() {
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([[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([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([pair1, pair2, 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, 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));
}
// http://tddbin.com/#?kata=es6/language/object-api/is
// 54: Object - is
// To do: make all tests pass, leave the assert lines unchanged!
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, 1);
assert.equal(areSame, true);
});
it('int 1 is different to string "1"', function() {
const areSame = Object.is(1, '1');
assert.equal(areSame, false);
});
it('strings just have to match', function() {
const areSame = Object.is('one', 'one');
assert.equal(areSame, true);
});
it('+0 is not the same as -0', function() {
const areSame = false;
assert.equal(Object.is(+0, -0), areSame);
});
it('NaN is the same as NaN', function() {
const number = 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 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, emptyString);
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.equal(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);
});
});
});
// http://tddbin.com/#?kata=es6/language/number-api/isinteger
// 55: Number - isInteger
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Number.isInteger()` determines if a value is an integer', function(){
const isTrue = (what) => assert.equal(what, true);
const isFalse = (what) => assert.equal(what, false);
it('`isInteger` is a static function on `Number`', function() {
const whatType = 'function';
assert.equal(typeof Number.isInteger, whatType);
});
describe('zero in different ways', function() {
it('0 is an integer', function() {
const zero = 0;
isTrue(Number.isInteger(zero));
});
it('0.000', function() {
isTrue(Number.isInteger(0.000));
});
it('the string "0" is NOT an integer', function() {
const stringZero = '0';
isFalse(Number.isInteger(stringZero));
});
});
describe('one in different ways', function() {
it('0.111 + 0.889', function() {
const rest = 0.889;
isTrue(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.2 + 0.1;
isFalse(Number.isInteger(oneOrNot));
});
it('parseInt`ed "1" is an integer', function() {
const convertedToInt = Number.parseInt('1.01');
isTrue(Number.isInteger(convertedToInt));
});
});
describe('what is not an integer', function() {
it('`Number()` is an integer', function() {
const numberOne = Number(1);
isTrue(Number.isInteger(numberOne));
});
it('`{}` is NOT an integer', function() {
const isit = Number.isInteger({});
isFalse(isit);
});
it('`0.1` is not an integer', function() {
const isit = Number.isInteger(0.1);
isFalse(isit);
});
it('`Number.Infinity` is not an integer', function() {
const isit = Number.isInteger(Number.Infinity);
isFalse(isit);
});
it('`NaN` is not an integer', function() {
const isit = Number.isInteger(NaN);
isFalse(isit);
});
});
});
// http://tddbin.com/#?kata=es6/language/generator/send-function
// 56: Generator - Send function to a generator
// To do: make all tests pass, leave the assert lines unchanged!
describe('pass a function to a generator', () => {
it('the generator can receive a function as a value', function() {
const fn = function() {};
function* generatorFunction(fn) {
yield;
assert.equal(yield null, fn); // remember, don't touch this line
}
const iterator = generatorFunction(fn);
iterator.next();
iterator.next();
});
it('pass a function to the iterator, which calls it', function() {
const fn = value => value;
function* generatorFunction(fn) {
yield fn(1)
yield fn(2);
}
const iterator = generatorFunction(fn);
const iteratedOver = [iterator.next().value, iterator.next().value];
assert.deepEqual([1, 2], iteratedOver);
});
it('nesting yielded function calls', function() {
const fn = function () {};
function* generatorFunction(fn) {
yield 1, fn(yield 2, fn(yield 3, fn()));
}
const iterator = generatorFunction(fn);
const iteratedOver = [iterator.next().value, iterator.next().value, iterator.next().value];
assert.deepEqual([1, 2, 3], iteratedOver);
});
});
// http://tddbin.com/#?kata=es6/language/default-parameters/basics
// 57: Default parameters - basics
// To do: make all tests pass, leave the assert lines unchanged!
describe('default parameters make function parameters more flexible', () => {
it('define it using an assignment to the parameter `function(param=1){}`', function() {
let number = (int=0) => int;
assert.equal(number(), 0);
});
it('it is used when undefined is passed', function() {
const number = (int = 23) => int;
let param;
assert.equal(number(param), 23);
});
it('it is not used when a value is given', function() {
const xhr = (method = 'GET') => method;
assert.equal(xhr('POST'), 'POST');
});
it('it is evaluated at run time', function() {
let defaultValue = 42;
function xhr(method = `value: ${defaultValue}`) {
return method;
}
assert.equal(xhr(), 'value: 42');
});
it('it can also be a function', function() {
let defaultValue = () => {};
function fn(value = defaultValue()) {
return value;
}
assert.equal(fn(), defaultValue());
});
});
// http://tddbin.com/#?kata=es6/language/reflect/basics
// 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('is not a function', function() {
const expectedType = 'object';
assert.equal(typeof Reflect, expectedType);
});
it('it can not be instantiated', function() {
const tryToConstruct = () => { new Reflect(); };
assert.throws(tryToConstruct, TypeError);
});
it('has no `call` method (as opposed to e.g. Object)', function() {
const expected = 'undefined';
assert.equal(typeof Reflect.call, expected);
});
});
describe('some `Reflect` usages', function() {
it('`Reflect.construct()` is like new', function() {
let Class = function() {};
assert.equal(Reflect.construct(Class, []) instanceof Class, true);
});
it('`Reflect.get()` returns a property`s value', function() {
let obj = {x: 23};
assert.equal(Reflect.get(obj, 'x'), 23);
});
it('`Reflect.has()` is like `in` just as a function', function() {
let obj = {x: 23};
assert.equal(Reflect.has(obj, 'x'), true);
});
});
});
// http://tddbin.com/#?kata=es6/language/reflect/apply
// 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)
});
it('it calls a callable, e.g. a function', function() {
let fn = function() { return 42; };
assert.equal(Reflect.apply(fn, undefined, []), 42);
});
it('passing it a non-callable throws a TypeError', function() {
let applyOnUncallable = () => {
Reflect.apply(Array);
};
assert.throws(applyOnUncallable, TypeError);
});
it('the second argument 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, instance, instance);
assert.deepEqual(fourtyTwo, 42);
});
it('the 3rd argument is an array of parameters passed to the call', function() {
let emptyArrayWithFiveElements = Reflect.apply(Array, undefined, new Array(5));
assert.deepEqual(emptyArrayWithFiveElements.fill(42), [42, 42, 42, 42, 42]);
});
});
// http://tddbin.com/#?kata=es6/language/reflect/getprototypeof
// 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({});
assert.strictEqual(viaObject, viaReflect);
});
it('throws a TypeError for a non-object', function() {
let fn = () => { Reflect.getPrototypeOf(42) };
assert.throws(fn, TypeError);
});
it('a `new Set()` has a prototype', function() {
const aSet = new Set();
assert.equal(Reflect.getPrototypeOf(aSet), Set.prototype);
});
it('for a class, it is `Klass.prototype`', function() {
class Klass {}
const proto = Reflect.getPrototypeOf(new Klass());
assert.equal(proto, Klass.prototype);
});
it('for a old-style class, works too', function() {
function Klass() { }
const proto = Reflect.getPrototypeOf(new Klass());
assert.equal(proto, Klass.prototype);
});
it('an array has a prototype too', function() {
let arr = [];
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);
// });
});
// http://tddbin.com/#?kata=es6/language/modules/import
// 61: modules - import
// To do: make all tests pass, leave the assert lines unchanged!
import assert from 'assert'; // is only here for completeness, `assert` is always imported by default
import {equal, deepEqual, notEqual} from 'assert';
import {equal as myEqual} from 'assert';
import {default as myAssert} from 'assert';
describe('use `import` to import functions that have been exported (somewhere else)', function() {
describe('the import statement', function() {
it('is only allowed on the root level', function() {
// try to comment this out, it will yell at you :)
// import assert from 'assert';
});
it('import an entire module using `import <name> from "<moduleName>"`', function() {
// this can't fail, since `assert` is imported by default
assert.equal(typeof assert, 'function');
});
});
describe('import members', function() {
it('import a single member, using `import {<memberName>} from "module"`', function() {
assert.strictEqual(equal, assert.equal);
});
describe('separate multiple members with a comma', function() {
it('`deepEqual` from the assert module', () => {
assert.strictEqual(deepEqual, assert.deepEqual);
});
it('`notEqual` from the assert module', () => {
assert.strictEqual(notEqual, assert.notEqual);
});
});
});
describe('alias imports', function() {
it('using `member as alias` as memberName', function() {
assert.strictEqual(myEqual, assert.equal);
});
it('rename the default export of a module, using `default as alias` as memberName', function() {
assert.strictEqual(myAssert, assert);
});
});
});
// http://tddbin.com/#?kata=es6/language/map/has
// 62: Map - `has()`
// To do: make all tests pass, leave the assert lines unchanged!
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.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('key');
assert.equal(hasKey, true);
});
it('finds `undefined` as key too', function() {
let map = new Map([[void 0, 'not defined key']]);
const hasUndefinedAsKey = map.has(undefined);
assert.equal(hasUndefinedAsKey, true);
});
it('does not coerce keys', function() {
let map = new Map([['1', 'one']]);
const findsStringOne = true;
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, 'not defined');
assert.equal(map.has(void 0), true);
});
});
// http://tddbin.com/#?kata=es6/language/string-api/includes
// 63: String - `includes()`
// To do: make all tests pass, leave the assert lines unchanged!
describe('`string.includes()` finds string within another string', function() {
describe('find a single character', function() {
it('in a three char string', function() {
const searchString = 'x';
assert.equal('xyz'.includes(searchString), true);
});
it('reports false if character was not found', function() {
const expected = false;
assert.equal('xyz'.includes('abc'), expected);
});
});
describe('find a string', function() {
it('that matches exactly', function() {
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 = '';
assert.equal(''.includes(emptyString), true);
});
it('in `abc`', function() {
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(what);
assert.equal(findInAbc(void 0), false);
});
it('searches case-sensitive', function() {
const findInAbc = (what) => 'abc'.includes(what);
assert.equal(findInAbc('A'), false);
});
it('must NOT be a regular expression', function() {
const regExp = /a/;
assert.throws(() => {''.includes(regExp)});
});
describe('coerces the searched "thing" into a string', function() {
it('e.g. from a number', function() {
const actual = '123'.includes(3);
assert.equal(actual, true);
});
it('e.g. from an array', function() {
const actual = '123'.includes([1]);
assert.equal(actual, true);
});
it('e.g. from an object, with a `toString()` method', function() {
const objWithToString = {toString: function() {return 1}};
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 = 1;
assert.equal('abc'.includes('a', position), false);
});
it('even the position gets coerced', function() {
const findAtPosition = (pos) => 'xyz'.includes('z', pos);
assert.equal(findAtPosition('2'), true);
});
describe('invalid positions get converted to 0', function() {
it('e.g. `undefined`', function() {
const findAtPosition = (pos) => 'xyz'.includes('x', pos);
assert.equal(findAtPosition(void 0), true);
});
it('negative numbers', function() {
const findAtPosition = (pos) => 'xyz'.includes('x', pos);
assert.equal(findAtPosition(-2), true);
});
it('NaN', function() {
const findAtPosition = (pos) => 'xyz'.includes('x', pos);
assert.equal(findAtPosition(NaN), true);
});
});
});
});
// http://tddbin.com/#?kata=es6/language/set/delete
// 64: Set - delete
// To do: make all tests pass, leave the assert lines unchanged!
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.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('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() {
let whatToDelete = undefined;
assert.equal(set.delete(whatToDelete), false);
});
it('`delete()` removes it, when its in the set', function() {
set.add(undefined);
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);
assert.equal(set.delete('1'), false);
});
});
});
// http://tddbin.com/#?kata=es6/language/set/api
// 65: Set - API overview
// To do: make all tests pass, leave the assert lines unchanged!
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(api);
assert.deepEqual(Array.from(set), api);
});
it('`size` is the number of values', function() {
const theSize = set.size;
assert.equal(theSize, api.length);
});
it('`add()` appends the given value', function() {
// hint: to make the example consistent you can add the `Symbol.iterator` to `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('size');
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.entries();
assert.deepEqual([...actualEntries], expectedEntries);
});
it('`forEach()` calls a callback for each value', function() {
let values = [];
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 = 'has';
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 = set.keys();
assert.deepEqual([...allKeys], api);
});
it('`values()`', function() {
const allValues = set.values();
assert.deepEqual([...allValues], api);
});
it('`[Symbol.iterator]()`', function() {
const iteratorKey = Symbol.iterator;
assert.deepEqual([...set[iteratorKey]()], api);
});
});
});
// http://tddbin.com/#?kata=es6/language/object-literal/getter
// 66: object-literal - getter
// To do: make all tests pass, leave the assert lines unchanged!
describe('An object literal can also contain getters', () => {
it('just prefix the property with `get` (and make it a function)', function() {
const obj = {
get x() { return 'ax'; }
};
assert.equal(obj.x, 'ax');
});
it('must have NO parameters', function() {
const obj = {
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'; }
};
assert.equal(obj.x, 'ax');
});
it('can be removed using delete', function() {
const obj = {
get x() { return 'ax'; }
};
delete obj.x;
assert.equal(obj.x, void 0);
});
// The following dont seem to work in the current transpiler version
// but should be correct, as stated here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
// It might be corrected later, new knowledge welcome.
//it('must not overlap with a pure property', function() {
// const obj = {
// x: 1,
// get x() { return 'ax'; }
// };
//
// assert.equal(obj.x, 'ax');
//});
//
//it('multiple `get` for the same property are not allowed', function() {
// const obj = {
// x: 1,
// get x() { return 'ax'; },
// get x() { return 'ax1'; }
// };
//
// assert.equal(obj.x, 'ax');
//});
});
// http://tddbin.com/#?kata=es6/language/object-literal/setter
// 67: object-literal - setter
// To do: make all tests pass, leave the assert lines unchanged!
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 = {
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 = {
set x(foo) {
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';
let privatePropertyName = '_' + publicPropertyName;
const obj = {
[privatePropertyName]: null,
set [publicPropertyName](foo) {
this[privatePropertyName] = foo;
}
};
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);
});
});
// TODO
// The following dont seem to work in the current transpiler version
// but should be correct, as stated here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set
// It might be corrected later, new knowledge welcome.
// it('must not overlap with a pure property', function() {
// const obj = {
// x: 1,
// set x(val) { return 'ax'; }
// };
// assert.equal(obj.x, 'ax');
// });
// it('multiple `set` for the same property are not allowed', function() {
// const obj = {
// x: 1,
// set x(v) { return 'ax'; },
// set x(v) { return 'ax1'; }
// };
// assert.equal(obj.x, 'ax');
// });
});
// http://tddbin.com/#?kata=es6/language/reflect/construct
// 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 = 'constructor';
assert.equal(name in Reflect, true);
});
it('is of type `function`', function() {
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 = 42;
assert.throws(() => { Reflect.construct(aNumber, []) }, TypeError);
});
it('works giving a function', function() {
let aFunction = () => {};
assert.doesNotThrow(() => { Reflect.construct(aFunction, []) });
});
it('works giving a class', function() {
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 = 42;
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() { [].length; }};
assert.doesNotThrow(() => { Reflect.construct(aClass, arrayLike) });
});
it('works with a real array', function() {
let realArray = ['foo', 'bar'];
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(Constructable, []); // use Reflect.construct here!!!
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, []);
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(Constructable, argumentsList);
assert.deepEqual(instance.args, argumentsList);
});
});
});
describe('the length property', function() {
it('of `Reflect.construct` is 2', function() {
let expected = 2;
assert.equal(Reflect.construct.length, expected);
});
});
});
// http://tddbin.com/#?kata=es6/language/reflect/defineproperty
// 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 = 'defineProperty';
assert.equal(name in Reflect, true);
});
it('is of type `function`', function() {
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 = '';
assert.throws(() => { Reflect.defineProperty(noObj, 'x', {value: 42}); }, TypeError);
});
it('accepts an object', function() {
let obj = {};
assert.doesNotThrow(() => { Reflect.defineProperty(obj, 'x', {value: 42}); });
});
it('accepts an instance (of a class)', function() {
class Foo {}
let instance = new Foo();
assert.doesNotThrow(() => { Reflect.defineProperty(instance, 'x', {value: 42}); });
});
});
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, 'prop', {});
assert.equal('prop' in obj, true);
});
it('a number gets converted into a string', function() {
let obj = {};
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;
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, 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', {value: 'property value'});
assert.equal(obj.prop, 'property value');
});
it('can be of any type (even itself)', function() {
let obj = {};
Reflect.defineProperty(obj, 'prop', {value: obj});
assert.equal(obj.prop, obj);
});
});
describe('the return value of the function indicates wether 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(instance, 'prop', {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, 'prop', {});
assert.equal(wasPropertyDefined, true);
});
});
describe('returns false', function() {
it('when no property name is given (since no property has been added)', function() {
let instance = new class {};
const wasPropertyDefined = Reflect.defineProperty(instance, null, {});
assert.equal(wasPropertyDefined, false);
});
it('when no 3rd parameter, the descriptor is given', function() {
let instance = new class {};
const wasPropertyDefined = Reflect.defineProperty(instance, 'prop');
assert.equal(wasPropertyDefined, false);
});
});
});
});
// http://tddbin.com/#?kata=es6/language/set/clear
// 70: Set - clear
// To do: make all tests pass, leave the assert lines unchanged!
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 = 0;
assert.equal(set.size, expectedSize);
});
it('the iterator `set.entries()` will not contain any items', function() {
set.add('one').add(2);
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();
assert.deepEqual(set.has(2), false);
});
it('returns `undefined`', function() {
var expectedReturn;
assert.equal(set.clear(), expectedReturn);
});
});
// http://tddbin.com/#?kata=es6/language/string-api/repeat
// 71: String - `repeat()`
// To do: make all tests pass, leave the assert lines unchanged!
describe('`str.repeat(x)` appends `x` copies of `str` to each other and returns it', function() {
describe('pass the count to `str.repeat(count)`', function() {
it('for `1` the string stays the same', function() {
const what = 'one'.repeat(1);
assert.equal(what, 'one');
});
it('for `3` the string `x` becomes `xxx`', function() {
const actual = 'x'.repeat(3);
assert.equal(actual, 'xxx');
});
it('for `0` an empty string is returned', function() {
const dontRepeat = '1'.repeat(0);
assert.equal('shrink'.repeat(dontRepeat), '');
});
it('the count is not an int, such as "3", it gets coerced to an int', function() {
const repeated = 'three'.repeat('3');
assert.equal(repeated, 'threethreethree');
});
});
describe('throws an error for', function() {
it('a count of <0', function() {
const belowZero = -1;
assert.throws(() => { ''.repeat(belowZero); }, RangeError);
});
it('a count of +Infinty', function() {
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 = false;
assert.equal(String.prototype.repeat.call(aBool, 2), 'falsefalse');
});
it('e.g. a number', function() {
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 = 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');
});
});
});
// http://tddbin.com/#?kata=es6/language/string-api/startswith
// 72: String - `startsWith()`
// To do: make all tests pass, leave the assert lines unchanged!
describe('`str.startsWith(searchString)` determines whether `str` begins with `searchString`.', function() {
const s = 'the string s';
describe('1st parameter, the string to search for', function() {
it('works with just a character', function() {
const actual = s.startsWith('t');
assert.equal(actual, true);
});
it('works with a string', function() {
const expected = true;
assert.equal(s.startsWith('the'), expected);
});
it('works with unicode characters', function() {
const nuclear = '☢ NO';
assert.equal(nuclear.startsWith('☢'), true);
});
it('a regular expression throws a TypeError', function() {
const aRegExp = /the/;
assert.throws(() => {''.startsWith(aRegExp)}, TypeError);
});
});
describe('2nd parameter, the position where to start searching from', function() {
it('find "str" at position 4', function() {
const position = 4;
assert.equal(s.startsWith('str', position), true);
});
it('`undefined` is the same as 0', function() {
let _undefined_;
assert.equal(s.startsWith('the', _undefined_), true);
});
it('the parameter gets coerced to an int', function() {
const position = '4';
assert.equal(s.startsWith('str', position), true);
});
it('a value larger than the string`s length, returns false', function() {
const expected = false;
assert.equal(s.startsWith(' ', s.length + 1), expected);
});
});
describe('transfer the functionality to other objects', function() {
const startsWith = (...args) => String.prototype.startsWith.call(...args);
it('e.g. a boolean', function() {
let aBool = true;
assert.equal(startsWith(!aBool, 'false'), true);
});
it('e.g. a number', function() {
let aNumber = 1900;
assert.equal(startsWith(aNumber + 84, '1984'), true);
});
it('also using the position works', function() {
const position = 1;
assert.equal(startsWith(1994, '99', position), true);
});
});
});
// http://tddbin.com/#?kata=es6/language/generator/return
// 73: Generator - `return` inside a generator is special
// To do: make all tests pass, leave the assert lines unchanged!
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 value given after the `return` statement', function() {
function* generatorFunction() { return 23; }
const {value} = generatorFunction().next();
assert.equal(value, 23);
});
it('the property `done` is true', function() {
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() { 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() { }
const returned = generatorFunction().next();
assert.deepEqual(returned, {value: void 0, done: true});
});
});
describe('mixing `return` and `yield`', function() {
function* generatorFunctionWithYieldAndReturn() {
yield 1;
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];
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('returning a yielded value', function() {
function* generatorFunction() {
yield 1;
yield 2;
}
const iterator = generatorFunction();
const values = [
iterator.next().value,
iterator.next(2).value
];
assert.deepEqual(values, [1, 2]);
});
});
});
// http://tddbin.com/#?kata=es6/language/string-api/endswith
// 74: String - `endsWith()`
// To do: make all tests pass, leave the assert lines unchanged!
describe('`str.endsWith(searchString)` determines whether `str` ends with `searchString`.', function() {
const s = 'el fin';
describe('1st parameter, the string to search for', function() {
it('works with just a character', function() {
const doesEndWith = s.endsWith('n');
assert.equal(doesEndWith, true);
});
it('works with a string', function() {
const expected = true;
assert.equal(s.endsWith('fin'), expected);
});
it('works with unicode characters', function() {
const nuclear = 'NO ☢';
assert.equal(nuclear.endsWith('☢'), true);
});
it('a regular expression throws a TypeError', function() {
const aRegExp = /the/;
assert.throws(() => {''.endsWith(aRegExp)}, TypeError);
});
});
describe('2nd parameter, searches within this string as if this string were only this long', function() {
it('find "el" at a substring of the length 2', function() {
const endPos = 2;
assert.equal(s.endsWith('el', endPos), true);
});
it('`undefined` uses the entire string', function() {
let _undefined_;
assert.equal(s.endsWith('fin', _undefined_), true);
});
it('the parameter gets coerced to an int', function() {
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 = '';
assert.equal('1'.endsWith(emptyString, -1), true);
});
it('return `false`, when searching for a non-empty string', function() {
const notEmpty = 'huh';
assert.equal('1'.endsWith(notEmpty, -1), false);
});
});
});
describe('transfer the functionality to other objects', function() {
const endsWith = (...args) => String.prototype.endsWith.call(...args);
it('e.g. a boolean', function() {
let aBool = true;
assert.equal(endsWith(!aBool, 'lse'), true);
});
it('e.g. a number', function() {
let aNumber = 84;
assert.equal(endsWith(aNumber + 1900, 84), true);
});
it('also using the position works', function() {
const position = 3;
assert.equal(endsWith(1994, '99', position), true);
});
});
});
// http://tddbin.com/#?kata=es6/language/promise/basics
// 75: Promise - basics
// To do: make all tests pass, leave the assert lines unchanged!
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 = 'function';
assert.equal(typeof Promise, expectedType);
});
describe('the constructor', function() {
it('instantiating it without params throws', function() {
const fn = () => { new Promise(); }
assert.throws(fn);
});
it('expects a function as parameter', function() {
const param = function () {};
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) => {
resolve();
});
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(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) => {
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((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((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((resolve, reject) => {
resolve();
});
// return the promise to mocha, it has the checking for promise resolving built in, when it receives a promise
return promise;
});
});
});
// http://tddbin.com/#?kata=es6/language/promise/creation
// 76: Promise - creation
// To do: make all tests pass, leave the assert lines unchanged!
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() {
new Promise();
}
assert.throws(callPromiseAsFunction);
});
it('no parameter is passed', function() {
function promiseWithoutParams() {
new Promise();
}
assert.throws(promiseWithoutParams);
});
it('passing a non-callable throws too', function() {
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) => resolve());
return promise;
});
it('by passing a resolve and a reject function to it', function(done) {
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() {
class MyPromise extends Promise {}
const curpromise = new MyPromise(resolve => resolve());
curpromise
.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() {
super();
}
}
return new ResolvingPromise();
});
});
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))
]);
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 => reject(1)),
new Promise(resolve => resolve(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([earlyResolvingPromise, lateRejectedPromise]);
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 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();
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('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.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.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.';
}
}
// http://tddbin.com/#?kata=es6/language/promise/chaining-then
// 77: Promise - chaining
// To do: make all tests pass, leave the assert lines unchanged!
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.resolve('I should fulfill.');
});
it('a function given to `then()` fulfills (if it doesnt throw)', function() {
const beNice = () => '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');
wordsPromise
.then(string => removeMultipleSpaces())
.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');
wordsPromise
.then(removeMultipleSpaces)
.then(appendPeriod)
.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(removeMultipleSpaces)
.then(trim)
.then(appendPeriod)
.then(actual => {assert.equal(actual, 'Sentence without an end.')})
;
});
const asyncUpperCaseStart = (string, onDone) => {
const format = onDone(string[0].toUpperCase() + string.substr(1));
setTimeout(format, 10);
};
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');
wordsPromise
.then(string => new Promise(resolve => asyncUpperCaseStart))
.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 ');
wordsPromise
.then(string => new Promise(resolve => setTimeout(() => resolve(trim(string)), 100)))
.then(string => new Promise(resolve => asyncUpperCaseStart(string, resolve)))
.then(string => new Promise(resolve => setTimeout(() => resolve(appendPeriod(string)), 100)))
.then(actual => {assert.equal(actual, 'Trailing space.')})
;
});
});
});
// http://tddbin.com/#?kata=es6/language/promise/api
// 78: Promise - API overview
// To do: make all tests pass, leave the assert lines unchanged!
describe('`Promise` API overview', function() {
it('`new Promise()` requires a function as param', () => {
const param = (resolve) => resolve();
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) => { resolve(); };
return new Promise(param);
});
it('using `Promise.resolve()`', () => {
return Promise.resolve('all fine');
});
});
describe('a rejected promise', () => {
it('using the constructor parameter', (done) => {
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.reject();
promise
.then(() => done(new Error('The promise is expected to be rejected.')))
.catch(() => done());
});
});
const resolvingPromise = Promise.resolve();
const rejectingPromise = Promise.reject();
describe('`Promise.all()`', () => {
it('`Promise.all([p1, p2])` resolves when all promises resolve', () =>
Promise.all([resolvingPromise, resolvingPromise])
);
it('`Promise.all([p1, p2])` rejects when a promise is rejected', (done) => {
Promise.all([resolvingPromise, rejectingPromise])
.then(() => done(new Error('The promise is expected to be rejected.')))
.catch(() => done())
});
});
describe('`Promise.race()`', () => {
it('`Promise.race([p1, p2])` resolves when one of the promises resolves', () =>
Promise.race([resolvingPromise, rejectingPromise])
);
it('`Promise.race([p1, p2])` rejects when one of the promises rejects', (done) => {
Promise.race(rejectingPromise, [resolvingPromise])
.then(() => done(new Error('The promise is expected to be rejected.')))
.catch(() => done())
});
it('`Promise.race([p1, p2])` order matters (and timing)', () =>
Promise.race([resolvingPromise, rejectingPromise])
);
});
});
// http://tddbin.com/#?kata=es6/language/promise/catch
// 79: Promise - catch
// To do: make all tests pass, leave the assert lines unchanged!
// 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', () => {
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.resolve('I should fulfill.');
});
});
describe('`catch` method basics', () => {
it('is an instance method', () => {
const p = Promise.reject();
assert.equal(typeof p.catch, 'function');
});
it('catches only promise rejections', (done) => {
const promise = Promise.reject();
promise
.then(() => { done('Should not be called!'); })
.catch(done);
});
it('returns a new promise', () => {
const whatToReturn = () => Promise.reject();
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(() => 'promise?');
return p1.then(result => assert.equal('promise?', result));
});
it('the first parameter is the rejection reason', () => {
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 => `${reason} AND 2`)
.catch(reason => `${reason} AND FOO`)
;
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 => err.message)
;
return p1.then(result =>
assert.equal(result, '1 AND 2 AND 3')
);
});
});
});
@Tiriel
Copy link

Tiriel commented Aug 31, 2017

Hi!

Just so you know, your file is good, but you could improve it by saying why you chose a solution over an other. For newbs like me, it helps understanding the internals of JS/ES

Anyway, for #56, last use case, you could do like this. It also works, and is less verbose (using destructuring+rest operator):

  it('nesting yielded function calls', function() {
    let fn = () => {};
    function* generatorFunction(fn) {
      yield 1, fn(yield 2, fn(yield 3, fn()));
    }
    var iteratedOver = [...generatorFunction(fn)];
    assert.deepEqual([1, 2, 3], iteratedOver);
  });

Hope this helps, and thanks again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment