Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

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);
});
});
});