kokopu
Version:
A JavaScript/TypeScript library implementing the chess game rules and providing tools to read/write the standard chess file formats.
800 lines (643 loc) • 27.7 kB
JavaScript
/*!
* -------------------------------------------------------------------------- *
* *
* Kokopu - A JavaScript/TypeScript chess library. *
* <https://www.npmjs.com/package/kokopu> *
* Copyright (C) 2018-2026 Yoann Le Montagner <yo35 -at- melix.net> *
* *
* Kokopu is free software: you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public License *
* as published by the Free Software Foundation, either version 3 of *
* the License, or (at your option) any later version. *
* *
* Kokopu is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General *
* Public License along with this program. If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* -------------------------------------------------------------------------- */
const { exception, DateValue, Game, Position, Variation } = require('../dist/lib/index');
const assert = require('node:assert/strict');
describe('General game header', () => {
it('Initial state', () => {
const game = new Game();
assert.deepEqual(game.event(), undefined);
});
it('Set & get', () => {
const game = new Game();
game.event('The event');
assert.deepEqual(game.event(), 'The event');
});
it('Set empty string', () => {
const game = new Game();
game.event('');
assert.deepEqual(game.event(), '');
});
it('Set blank string', () => {
const game = new Game();
game.event(' ');
assert.deepEqual(game.event(), ' ');
});
it('Set non-string (number)', () => {
const game = new Game();
game.event(42);
assert.deepEqual(game.event(), '42');
});
it('Set non-string (boolean)', () => {
const game = new Game();
game.event(false);
assert.deepEqual(game.event(), 'false');
});
it('Erase with undefined', () => {
const game = new Game();
game.event('The event');
game.event(undefined);
assert.deepEqual(game.event(), undefined);
});
it('Erase with null', () => {
const game = new Game();
game.event('The event');
game.event(null);
assert.deepEqual(game.event(), undefined);
});
});
describe('Result header', () => {
it('Default value', () => {
const game = new Game();
assert.deepEqual(game.result(), '*');
});
it('Set & get', () => {
const game = new Game();
game.result('1-0');
assert.deepEqual(game.result(), '1-0');
});
function itInvalidValue(label, value) {
it(label, () => {
const game = new Game();
assert.throws(() => game.result(value), exception.IllegalArgument);
});
}
itInvalidValue('Set dummy value', 'dummy');
itInvalidValue('Set empty string', '');
itInvalidValue('Set undefined', undefined);
itInvalidValue('Set null', null);
});
describe('ECO header', () => {
function itInvalidValue(label, value) {
it(label, () => {
const game = new Game();
assert.throws(() => game.eco(value), exception.IllegalArgument);
});
}
itInvalidValue('Set dummy value', 'dummy');
itInvalidValue('Set empty string', '');
itInvalidValue('Set out-of-range code', 'F00');
itInvalidValue('Set untrimmed code 1', ' A18');
itInvalidValue('Set untrimmed code 2', 'A18 ');
});
describe('Color-dependant header', () => {
function itInvalidColor(label, action) {
it(label, () => {
const game = new Game();
assert.throws(() => action(game), exception.IllegalArgument);
});
}
itInvalidColor('Dummy color 1', game => game.playerName('dummy', 'TheName'));
itInvalidColor('Dummy color 2', game => game.playerTitle('ww', 'GM'));
itInvalidColor('Dummy color 3', game => game.playerTitle('B', 'IM'));
itInvalidColor('Empty color', game => game.playerElo('', '1234'));
});
describe('Elo header', () => {
it('Set number 1', () => {
const game = new Game();
game.playerElo('w', 899);
assert.deepEqual(game.playerElo('w'), 899);
});
it('Set number 2', () => {
const game = new Game();
game.playerElo('w', 0);
assert.deepEqual(game.playerElo('w'), 0);
});
it('Set number as string', () => {
const game = new Game();
game.playerElo('b', '2000');
assert.deepEqual(game.playerElo('b'), 2000);
});
function itInvalidElo(label, action) {
it(label, () => {
const game = new Game();
assert.throws(() => action(game), exception.IllegalArgument);
});
}
itInvalidElo('Non-convertible string', game => game.playerElo('w', 'two thousand'));
itInvalidElo('Invalid elo value', game => game.playerElo('b', -42));
});
describe('Round / sub-round / sub-sub-round headers', () => {
it('Set number 1', () => {
const game = new Game();
game.round(3);
assert.deepEqual(game.round(), 3);
});
it('Set number 2', () => {
const game = new Game();
game.subRound(0);
assert.deepEqual(game.subRound(), 0);
});
it('Set number 3', () => {
const game = new Game();
game.subSubRound(9999);
assert.deepEqual(game.subSubRound(), 9999);
});
it('Set number as string', () => {
const game = new Game();
game.round('2000');
assert.deepEqual(game.round(), 2000);
});
function itInvalidRound(label, action) {
it(label, () => {
const game = new Game();
assert.throws(() => action(game), exception.IllegalArgument);
});
}
itInvalidRound('Non-convertible string', game => game.round('two'));
itInvalidRound('Negative value', game => game.subRound(-42));
itInvalidRound('Non-integer value', game => game.subSubRound(1.2));
});
describe('Date header', () => {
function testDateIsUndefined(game) {
assert.deepEqual(game.date(), undefined);
assert.deepEqual(game.dateAsDate(), undefined);
}
function testDateIs(game, value, dateValue) {
assert(game.date() instanceof DateValue);
assert.deepEqual(game.date().toString(), value);
assert.deepEqual(game.dateAsDate(), dateValue);
}
it('Initial state', () => {
const game = new Game();
testDateIsUndefined(game);
});
it('Set JS Date & get', () => {
const game = new Game();
game.date(new Date(2021, 8, 12));
testDateIs(game, '2021-09-12', new Date(2021, 8, 12));
});
it('Set full date & get', () => {
const game = new Game();
game.date(2021, 9, 12);
testDateIs(game, '2021-09-12', new Date(2021, 8, 12));
});
it('Set month+year date 1 & get', () => {
const game = new Game();
game.date(2021, 12);
testDateIs(game, '2021-12-**', new Date(2021, 11, 1));
});
it('Set month+year date 2 & get', () => {
const game = new Game();
game.date(2021, 2, undefined);
testDateIs(game, '2021-02-**', new Date(2021, 1, 1));
});
it('Set month+year date 3 & get', () => {
const game = new Game();
game.date(2021, 2, null);
testDateIs(game, '2021-02-**', new Date(2021, 1, 1));
});
it('Set year-only date 1 & get', () => {
const game = new Game();
game.date(2021);
testDateIs(game, '2021-**-**', new Date(2021, 0, 1));
});
it('Set year-only date 2 & get', () => {
const game = new Game();
game.date(1, undefined);
testDateIs(game, '0001-**-**', new Date(1, 0, 1));
});
it('Set year-only date 3 & get', () => {
const game = new Game();
game.date(99, null);
testDateIs(game, '0099-**-**', new Date(99, 0, 1));
});
it('Set year-only date 4 & get', () => {
const game = new Game();
game.date(1921, undefined, undefined);
testDateIs(game, '1921-**-**', new Date(1921, 0, 1));
});
it('Erase with undefined', () => {
const game = new Game();
game.date(2021, 9, 12);
game.date(undefined);
testDateIsUndefined(game);
});
it('Erase with null', () => {
const game = new Game();
game.date(2021, 9, 12);
game.date(null);
testDateIsUndefined(game);
});
it('Get as string (full date)', () => {
const game = new Game();
game.date(2021, 9, 12);
assert.deepEqual(game.dateAsString('en-us'), 'September 12, 2021');
assert.deepEqual(game.dateAsString('fr'), '12 septembre 2021');
});
it('Get as string (month+year)', () => {
const game = new Game();
game.date(2021, 12);
assert.deepEqual(game.dateAsString('en-us'), 'December 2021');
assert.deepEqual(game.dateAsString('fr'), 'décembre 2021');
});
it('Get as string (year only)', () => {
const game = new Game();
game.date(2021);
assert.deepEqual(game.dateAsString('en-us'), '2021');
assert.deepEqual(game.dateAsString('fr'), '2021');
});
function itInvalidValue(label, value) {
it(label, () => {
const game = new Game();
assert.throws(() => game.date(value), exception.IllegalArgument);
});
}
itInvalidValue('Set string', 'dummy');
itInvalidValue('Set boolean', false);
itInvalidValue('Set empty object', {});
itInvalidValue('Set invalid year 1', -5);
itInvalidValue('Set invalid year 2', 1989.3);
function itInvalidYMD(label, year, month, day) {
it(label, () => {
const game = new Game();
assert.throws(() => game.date(year, month, day), exception.IllegalArgument);
});
}
itInvalidYMD('Set invalid month 1', 2021, 13, undefined);
itInvalidYMD('Set invalid month 2', 2021, 0, undefined);
itInvalidYMD('Set invalid month 3', 2021, 5.7, undefined);
itInvalidYMD('Set invalid day 1', 2021, 8, 0);
itInvalidYMD('Set invalid day 2', 2021, 6, 31);
itInvalidYMD('Set invalid day 3', 2021, 3, 3.1);
itInvalidYMD('Set day without month', 2021, undefined, 4);
itInvalidYMD('Set month without year', undefined, 8, undefined);
itInvalidYMD('Set all undefined', undefined, undefined, undefined);
});
describe('NAGs', () => {
function itOnNodeAndVariation(label, action) {
it(label + ' (node)', () => {
const game = new Game();
game.mainVariation().play('e4');
action(() => game.mainVariation().first());
});
it(label + ' (variation)', () => {
const game = new Game();
action(() => game.mainVariation());
});
}
itOnNodeAndVariation('Set & test', nodeGetter => {
nodeGetter().addNag(34);
assert.deepEqual(nodeGetter().nags(), [ 34 ]);
assert.deepEqual(nodeGetter().hasNag(34), true);
assert.deepEqual(nodeGetter().hasNag(42), false);
});
itOnNodeAndVariation('Erase', nodeGetter => {
nodeGetter().addNag(18);
nodeGetter().addNag(21);
nodeGetter().addNag(5);
assert.deepEqual(nodeGetter().nags(), [ 5, 18, 21 ]);
nodeGetter().removeNag(18);
assert.deepEqual(nodeGetter().nags(), [ 5, 21 ]);
assert.deepEqual(nodeGetter().hasNag(18), false);
assert.deepEqual(nodeGetter().hasNag(21), true);
nodeGetter().removeNag(5);
assert.deepEqual(nodeGetter().nags(), [ 21 ]);
nodeGetter().removeNag(16);
assert.deepEqual(nodeGetter().nags(), [ 21 ]);
nodeGetter().removeNag(21);
assert.deepEqual(nodeGetter().nags(), []);
});
itOnNodeAndVariation('Sorted NAGs', nodeGetter => {
nodeGetter().addNag(18);
nodeGetter().addNag(11);
nodeGetter().addNag(34);
nodeGetter().addNag(1234);
nodeGetter().addNag(2);
nodeGetter().addNag(1);
assert.deepEqual(nodeGetter().nags(), [ 1, 2, 11, 18, 34, 1234 ]);
});
itOnNodeAndVariation('Clear NAGs', nodeGetter => {
nodeGetter().addNag(52);
nodeGetter().addNag(3);
nodeGetter().addNag(14);
assert.deepEqual(nodeGetter().nags(), [ 3, 14, 52 ]);
nodeGetter().clearNags();
assert.deepEqual(nodeGetter().nags(), []);
});
itOnNodeAndVariation('Filter NAGs', nodeGetter => {
nodeGetter().addNag(18);
nodeGetter().addNag(1);
nodeGetter().addNag(14);
nodeGetter().addNag(24);
nodeGetter().addNag(31);
assert.deepEqual(nodeGetter().nags(), [ 1, 14, 18, 24, 31 ]);
nodeGetter().filterNags(nag => nag % 2 === 1);
assert.deepEqual(nodeGetter().nags(), [ 1, 31 ]);
});
function itInvalidNag(label, value) {
function doIt(nodeFactory) {
assert.throws(() => nodeFactory().addNag(value), exception.IllegalArgument);
assert.throws(() => nodeFactory().removeNag(value), exception.IllegalArgument);
assert.throws(() => nodeFactory().hasNag(value), exception.IllegalArgument);
}
it(label + ' (node)', () => {
doIt(() => {
const game = new Game();
return game.mainVariation().play('e4');
});
});
it(label + ' (variation)', () => {
doIt(() => {
const game = new Game();
return game.mainVariation();
});
});
}
itInvalidNag('Non-numeric NAG 1', 'dummy');
itInvalidNag('Non-numeric NAG 2', '42');
itInvalidNag('Negative NAG', -1);
itInvalidNag('NaN NAG', NaN);
});
describe('Tags', () => {
function itOnNodeAndVariation(label, action) {
it(label + ' (node)', () => {
const game = new Game();
game.mainVariation().play('e4');
action(() => game.mainVariation().first());
});
it(label + ' (variation)', () => {
const game = new Game();
action(() => game.mainVariation());
});
}
itOnNodeAndVariation('Set & get', nodeGetter => {
nodeGetter().tag('TheKey', 'TheValue');
assert.deepEqual(nodeGetter().tags(), [ 'TheKey' ]);
assert.deepEqual(nodeGetter().tag('TheKey'), 'TheValue');
assert.deepEqual(nodeGetter().tag('AnotherKey'), undefined);
});
itOnNodeAndVariation('Set empty string', nodeGetter => {
nodeGetter().tag('TheKey1', '');
assert.deepEqual(nodeGetter().tags(), [ 'TheKey1' ]);
assert.deepEqual(nodeGetter().tag('TheKey1'), '');
});
itOnNodeAndVariation('Set blank string', nodeGetter => {
nodeGetter().tag('__TheKey__', ' ');
assert.deepEqual(nodeGetter().tags(), [ '__TheKey__' ]);
assert.deepEqual(nodeGetter().tag('__TheKey__'), ' ');
});
itOnNodeAndVariation('Set non-string (number)', nodeGetter => {
nodeGetter().tag('_', 42);
assert.deepEqual(nodeGetter().tags(), [ '_' ]);
assert.deepEqual(nodeGetter().tag('_'), '42');
});
itOnNodeAndVariation('Set non-string (boolean)', nodeGetter => {
nodeGetter().tag('123', false);
assert.deepEqual(nodeGetter().tags(), [ '123' ]);
assert.deepEqual(nodeGetter().tag('123'), 'false');
});
itOnNodeAndVariation('Erase with undefined', nodeGetter => {
nodeGetter().tag('TheKey', 'TheValue');
nodeGetter().tag('TheKey', undefined);
assert.deepEqual(nodeGetter().tags(), []);
assert.deepEqual(nodeGetter().tag('TheKey'), undefined);
});
itOnNodeAndVariation('Erase with null', nodeGetter => {
nodeGetter().tag('TheKey', 'TheValue');
nodeGetter().tag('TheKey', null);
assert.deepEqual(nodeGetter().tags(), []);
assert.deepEqual(nodeGetter().tag('TheKey'), undefined);
});
itOnNodeAndVariation('Sorted keys', nodeGetter => {
nodeGetter().tag('TheKey', 'TheValue');
nodeGetter().tag('ABCD', 'Another value');
nodeGetter().tag('1234', 'Some number');
nodeGetter().tag('_a', '');
nodeGetter().tag('32', 'blah');
nodeGetter().tag('xyz', 0);
nodeGetter().tag('Blah', 33);
assert.deepEqual(nodeGetter().tags(), [ '1234', '32', 'ABCD', 'Blah', 'TheKey', '_a', 'xyz' ]);
});
itOnNodeAndVariation('Clear tags', nodeGetter => {
nodeGetter().tag('TheKey1', 'TheValue');
nodeGetter().tag('TheKey2', 'TheOtherValue');
assert.deepEqual(nodeGetter().tags(), [ 'TheKey1', 'TheKey2' ]);
nodeGetter().clearTags();
assert.deepEqual(nodeGetter().tags(), []);
});
itOnNodeAndVariation('Filter tags', nodeGetter => {
nodeGetter().tag('ab', 'a');
nodeGetter().tag('cd', 'b');
nodeGetter().tag('ef', 'c');
nodeGetter().tag('gh', 'd');
assert.deepEqual(nodeGetter().tags(), [ 'ab', 'cd', 'ef', 'gh' ]);
nodeGetter().filterTags((tagKey, tagValue) => tagKey.includes('b') || tagValue.includes('d'));
assert.deepEqual(nodeGetter().tags(), [ 'ab', 'gh' ]);
});
function itInvalidKey(label, action) {
it(label + ' (node)', () => {
const game = new Game();
game.mainVariation().play('e4');
assert.throws(() => action(game.mainVariation().first()), exception.IllegalArgument);
});
it(label + ' (variation)', () => {
const game = new Game();
assert.throws(() => action(game.mainVariation()), exception.IllegalArgument);
});
}
itInvalidKey('Dummy key 1', node => node.tag('.', 'TheValue'));
itInvalidKey('Dummy key 2', node => node.tag('-', undefined));
itInvalidKey('Empty key', node => node.tag('', 'Whatever'));
itInvalidKey('Blank key', node => node.tag(' ', 'The value'));
itInvalidKey('Undefined key', node => node.tag(undefined, 'Another value'));
});
describe('ToString', () => {
it('Node', () => {
const game = new Game();
const node = game.mainVariation().play('e4').play('e5').play('Nf3').play('Nc6');
assert.deepEqual(node.toString(), '2b[Nc6]');
});
it('Main variation', () => {
const game = new Game();
const variation = game.mainVariation();
assert.deepEqual(variation.toString(), 'start');
});
it('Sub-variation', () => {
const game = new Game();
const variation = game.mainVariation().play('e4').addVariation();
assert.deepEqual(variation.toString(), '1w-v0-start');
});
});
describe('Invalid findById', () => {
function itInvalidId(label, id) {
it(label, () => {
const game = new Game();
const current = game.mainVariation().play('e4').play('e5');
const alternative1 = current.addVariation();
alternative1.play('c5').play('Nf3');
const alternative2 = current.addVariation();
alternative2.play('e6').play('d4');
current.play('Bc4').play('Nc6').play('Qh5').play('Nf6').play('Qxf7#');
game.result('1-0');
assert.deepEqual(game.findById(id), undefined);
});
}
itInvalidId('Empty', '');
itInvalidId('Missing start', '1b-v0');
itInvalidId('Invalid variation index', '1b-vOne-start');
itInvalidId('Out of bound variation index', '1b-v2-start');
itInvalidId('Out of bound node 1', '5w');
itInvalidId('Out of bound node 2', '1b-v0-2b');
itInvalidId('Out of bound node 3', '5w-v0-start');
});
describe('FindById with aliases', () => {
function buildGame() {
const game = new Game();
const current = game.mainVariation().play('e4').play('e5');
current.addVariation();
const alternative2 = current.addVariation();
alternative2.play('e6').play('d4');
current.play('Bc4').play('Nc6').play('Qh5');
return game;
}
function itFindIdAlias(label, gameBuilder, idAlias, expectedId, expectedFEN) {
it(label, () => {
const game = gameBuilder();
const result = game.findById(idAlias);
assert.deepEqual(result.id(), expectedId);
assert.deepEqual(result instanceof Variation ? result.initialPosition().fen() : result.position().fen(), expectedFEN);
const result2 = game.findById(idAlias, true);
assert.deepEqual(result2.id(), expectedId);
assert.deepEqual(result2 instanceof Variation ? result2.initialPosition().fen() : result2.position().fen(), expectedFEN);
assert.deepEqual(game.findById(idAlias, false), undefined);
});
}
itFindIdAlias('End of main line', buildGame, 'end', '3w', 'r1bqkbnr/pppp1ppp/2n5/4p2Q/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq - 0 1');
itFindIdAlias('End of sub-variation', buildGame, '1b-v1-end', '1b-v1-2w', 'rnbqkbnr/pppp1ppp/4p3/8/3PP3/8/PPP2PPP/RNBQKBNR b KQkq - 0 1');
itFindIdAlias('End of empty sub-variation', buildGame, '1b-v0-end', '1b-v0-start', 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1');
itFindIdAlias('End of empty main line', () => new Game(), 'end', 'start', 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');
});
describe('Following ID', () => {
function checkVariation(variation) {
// Check the method on the variation itself.
assert.deepEqual(variation.followingId(0), variation.id());
// Check the method on each node of the variation.
const previousNodesOrVariation = [ variation ];
let currentNode = variation.first();
while (currentNode) {
// Check current node.
let index = 0;
for (const previousNodeOrVariation of previousNodesOrVariation) {
assert.deepEqual(previousNodeOrVariation.followingId(previousNodesOrVariation.length - index), currentNode.id());
++index;
}
assert.deepEqual(currentNode.followingId(0), currentNode.id());
// Check the variations starting at the current node, if any.
for (const subVariation of currentNode.variations()) {
checkVariation(subVariation);
}
// Go to the next node.
previousNodesOrVariation.push(currentNode);
currentNode = currentNode.next();
}
}
function itFollowingID(label, gameBuilder) {
it(label, () => {
const game = gameBuilder();
checkVariation(game.mainVariation());
});
}
itFollowingID('Linear game', () => {
const game = new Game();
game.mainVariation().play('e4').play('e5').play('Nf3').play('Nc6').play('Bc4').play('Nf6');
return game;
});
itFollowingID('Game with sub-variations', () => {
const game = new Game();
const current = game.mainVariation().play('e4').play('e5');
const alternative1 = current.addVariation();
alternative1.play('c5').play('Nf3').play('d6');
const alternative2 = current.addVariation();
alternative2.play('e6').play('d4');
current.play('Bc4').play('Nc6').play('Qh5').play('Nf6').play('Qxf7#');
return game;
});
});
describe('Invalid followingId', () => {
function itInvalidFollowingId(label, distance) {
it(label, () => {
const game = new Game();
const mainVariation = game.mainVariation();
const firstNode = mainVariation.play('e4');
const secondNode = firstNode.play('e5');
assert.throws(() => mainVariation.followingId(distance), exception.IllegalArgument);
assert.throws(() => firstNode.followingId(distance), exception.IllegalArgument);
assert.throws(() => secondNode.followingId(distance), exception.IllegalArgument);
});
}
itInvalidFollowingId('String', '1');
itInvalidFollowingId('Negative integer', -1);
itInvalidFollowingId('Non-integer', 1.2);
itInvalidFollowingId('Infinite value', Number.POSITIVE_INFINITY);
itInvalidFollowingId('NaN', Number.NaN);
});
describe('Invalid initial position', () => {
function itInvalidInitialPosition(label, action) {
it(label, () => {
const game = new Game();
assert.throws(() => action(game), exception.IllegalArgument);
});
}
itInvalidInitialPosition('Not a position 1', game => game.initialPosition(42));
itInvalidInitialPosition('Not a position 2', game => game.initialPosition('whatever'));
itInvalidInitialPosition('Invalid full-move number', game => game.initialPosition(new Position(), 'not-a-number'));
});
describe('Invalid variation index', () => {
function itInvalidVariationIndex(label, action) {
it(label, () => {
const game = new Game();
const node = game.mainVariation().play('e4').play('e5');
node.addVariation().play('c5').play('Nf3');
node.addVariation().play('d5').play('exd5').play('Qxd5');
node.addVariation().play('c6').play('d4');
node.addVariation().play('e6').play('d4').play('d5');
node.play('Nf3').play('Nc6').play('Bc4');
assert.throws(() => action(node), exception.IllegalArgument);
});
}
itInvalidVariationIndex('Not a number (remove)', node => node.removeVariation('1'));
itInvalidVariationIndex('Not a number (promote)', node => node.promoteVariation('2'));
itInvalidVariationIndex('Not a number (swap 1)', node => node.swapVariations('0', 1));
itInvalidVariationIndex('Not a number (swap 2)', node => node.swapVariations(2, '3'));
itInvalidVariationIndex('Out of range (remove)', node => node.removeVariation(4));
itInvalidVariationIndex('Out of range (promote)', node => node.promoteVariation(4));
itInvalidVariationIndex('Out of range (swap 1)', node => node.swapVariations(4, 1));
itInvalidVariationIndex('Out of range (swap 2)', node => node.swapVariations(2, 4));
});
describe('Figurine notation', () => {
it('White piece', () => {
const game = new Game();
const node = game.mainVariation().play('Nf3');
assert.deepEqual(node.figurineNotation(), '\u2658f3');
});
it('Black piece', () => {
const game = new Game();
const node = game.mainVariation().play('e4').play('e5').play('Nc3').play('Bc5');
assert.deepEqual(node.figurineNotation(), '\u265dc5');
});
it('Null move', () => {
const game = new Game();
const node = game.mainVariation().play('e4').play('--');
assert.deepEqual(node.figurineNotation(), '--');
});
});