diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..dfe077042 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.travis.yml b/.travis.yml index 008cbc167..afd0508c9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: node_js node_js: - - "5.10.0" + - "14.17.0" diff --git a/README.md b/README.md index a81b5c08a..917524b39 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -[![Brest Rolling Scopes](http://brest.rollingscopes.com/images/logo_rs_text.svg)](http://brest.rollingscopes.com/) -#Brest Rolling Scopes School -## Javascript Assignments [![Build Status](https://travis-ci.org/rolling-scopes-school/js-assignments.svg?branch=master)](https://travis-ci.org/rolling-scopes-school/js-assignments) +[![Brest Shagoysai](http://brest.rollingscopes.com/images/logo_rs_text.svg)](http://brest.rollingscopes.com/) +#Brest Shagoysai School +## Javascript Assignments [![Build Status](https://travis-ci.org/Shagoysai/js-assignments.svg?branch=master)](https://travis-ci.org/Shagoysai/js-assignments) Yet another javascript assignments. There are a lot of interactive javascript resources for beginners, but most of them are online and do not cover the modern programming workflow. There are some excellent training resources on github (https://github.com/rmurphey/js-assessment, https://github.com/mrdavidlaing/javascript-koans, https://github.com/vasanthk/js-bits etc) but they are not exactly simulate the everyday programming process. So the motivation of this project is to show TDD process in the wild to the beginners. Assingment tests are implemented in various ways to feel a difference and gain the experience what manner is good, what is bad and what is ugly. @@ -22,14 +22,14 @@ To start javascript assignments please follow the next steps: ### How to setup travis-ci * Open [https://travis-ci.org/](https://travis-ci.org/) and sign in with your github account. * Activate your forked repo **js-assignments**. -* Edit local README.md file and update all links (just replace all occurrences of `'rolling-scopes-school'` with your account name). +* Edit local README.md file and update all links (just replace all occurrences of `'Shagoysai'` with your account name). * Commit and push updated README.md to github: ```bash git add README.md git commit -m "Update the links" git push origin master ``` -* Open https://github.com/rolling-scopes-school/js-assignments and test the build icon. Now it will run all tests and update status once you push changes to github. Keep this icon green! +* Open https://github.com/Shagoysai/js-assignments and test the build icon. Now it will run all tests and update status once you push changes to github. Keep this icon green! ### How to setup work environment @@ -73,7 +73,7 @@ and run the unit tests again. Find one test failed (red). Now it's time to fix i * Implement the function by any way and verify your solution by running tests until the failed test become passed (green). * Your solution work, but now time to refactor it. Try to make your code as pretty and simple as possible keeping up the test green. * Once you can't improve your code and tests are passed you can commit your solution. -* Push your updates to github server and check if tests passed on [travis-ci](https://travis-ci.org/rolling-scopes-school/js-assignments/builds). +* Push your updates to github server and check if tests passed on [travis-ci](https://travis-ci.org/Shagoysai/js-assignments/builds). * If everything is OK you can try to resolve the next task. ### How to debug tasks diff --git a/task/01-strings-tasks.js b/task/01-strings-tasks.js index e28054657..3bd6e0c0a 100644 --- a/task/01-strings-tasks.js +++ b/task/01-strings-tasks.js @@ -22,7 +22,7 @@ * '', 'bb' => 'bb' */ function concatenateStrings(value1, value2) { - throw new Error('Not implemented'); + return value1.concat(value2); } @@ -38,7 +38,7 @@ function concatenateStrings(value1, value2) { * '' => 0 */ function getStringLength(value) { - throw new Error('Not implemented'); + return value.length; } /** @@ -55,7 +55,7 @@ function getStringLength(value) { * 'Chuck','Norris' => 'Hello, Chuck Norris!' */ function getStringFromTemplate(firstName, lastName) { - throw new Error('Not implemented'); + return `Hello, ${firstName} ${lastName}!`; } /** @@ -69,7 +69,7 @@ function getStringFromTemplate(firstName, lastName) { * 'Hello, Chuck Norris!' => 'Chuck Norris' */ function extractNameFromTemplate(value) { - throw new Error('Not implemented'); + return new RegExp("Hello, (.+)!").exec(value)[1]; } @@ -84,7 +84,7 @@ function extractNameFromTemplate(value) { * 'cat' => 'c' */ function getFirstChar(value) { - throw new Error('Not implemented'); + return value.substring(0,1); } /** @@ -99,7 +99,7 @@ function getFirstChar(value) { * '\tHello, World! ' => 'Hello, World!' */ function removeLeadingAndTrailingWhitespaces(value) { - throw new Error('Not implemented'); + return value.trim(); } /** @@ -114,7 +114,7 @@ function removeLeadingAndTrailingWhitespaces(value) { * 'cat', 3 => 'catcatcat' */ function repeatString(value, count) { - throw new Error('Not implemented'); + return value.repeat(count); } /** @@ -130,7 +130,7 @@ function repeatString(value, count) { * 'ABABAB','BA' => 'ABAB' */ function removeFirstOccurrences(str, value) { - throw new Error('Not implemented'); + return str.replace(value, ''); } /** @@ -145,7 +145,7 @@ function removeFirstOccurrences(str, value) { * '' => 'a' */ function unbracketTag(str) { - throw new Error('Not implemented'); + return str.replace('<', '').replace('>', ''); } @@ -160,7 +160,7 @@ function unbracketTag(str) { * 'abcdefghijklmnopqrstuvwxyz' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' */ function convertToUpperCase(str) { - throw new Error('Not implemented'); + return str.toUpperCase(); } /** @@ -174,7 +174,7 @@ function convertToUpperCase(str) { * 'info@gmail.com' => ['info@gmail.com'] */ function extractEmails(str) { - throw new Error('Not implemented'); + return str.split(';'); } /** @@ -201,7 +201,10 @@ function extractEmails(str) { * */ function getRectangleString(width, height) { - throw new Error('Not implemented'); + const top = `┌${'─'.repeat(width - 2)}┐\n`; + const mid = `│${' '.repeat(width - 2)}│\n`; + const bottom = `└${'─'.repeat(width - 2)}┘\n`; + return top + mid.repeat(height - 2) + bottom; } @@ -221,7 +224,10 @@ function getRectangleString(width, height) { * */ function encodeToRot13(str) { - throw new Error('Not implemented'); + const input = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); + const output = 'NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm'.split(''); + const lookup = input.reduce((m, k, i) => Object.assign(m, { [k]: output[i] }), {}); + return str.split('').map((x) => lookup[x] || x).join(''); } /** @@ -238,7 +244,7 @@ function encodeToRot13(str) { * isString(new String('test')) => true */ function isString(value) { - throw new Error('Not implemented'); + return Object.prototype.toString.call(value) === "[object String]"; } @@ -267,7 +273,14 @@ function isString(value) { * 'K♠' => 51 */ function getCardId(value) { - throw new Error('Not implemented'); + const cards = [ + 'A♣', '2♣', '3♣', '4♣', '5♣', '6♣', '7♣', '8♣', '9♣', '10♣', 'J♣', 'Q♣', 'K♣', + 'A♦', '2♦', '3♦', '4♦', '5♦', '6♦', '7♦', '8♦', '9♦', '10♦', 'J♦', 'Q♦', 'K♦', + 'A♥', '2♥', '3♥', '4♥', '5♥', '6♥', '7♥', '8♥', '9♥', '10♥', 'J♥', 'Q♥', 'K♥', + 'A♠', '2♠', '3♠', '4♠', '5♠', '6♠', '7♠', '8♠', '9♠', '10♠', 'J♠', 'Q♠', 'K♠', + ]; + + return cards.indexOf(value); } diff --git a/task/02-numbers-tasks.js b/task/02-numbers-tasks.js index c9ed20208..15af81652 100644 --- a/task/02-numbers-tasks.js +++ b/task/02-numbers-tasks.js @@ -22,7 +22,7 @@ * 5, 5 => 25 */ function getRectangleArea(width, height) { - throw new Error('Not implemented'); + return width * height; } @@ -38,7 +38,7 @@ function getRectangleArea(width, height) { * 0 => 0 */ function getCicleCircumference(radius) { - throw new Error('Not implemented'); + return Math.PI * radius * 2; } /** @@ -54,7 +54,7 @@ function getCicleCircumference(radius) { * -3, 3 => 0 */ function getAverage(value1, value2) { - throw new Error('Not implemented'); + return value1/2 + value2/2; } /** @@ -73,7 +73,9 @@ function getAverage(value1, value2) { * (-5,0) (10,-10) => 18.027756377319946 */ function getDistanceBetweenPoints(x1, y1, x2, y2) { - throw new Error('Not implemented'); + var a = x1 - x2; + var b = y1 - y2; + return Math.sqrt(a*a + b*b); } /** @@ -89,7 +91,7 @@ function getDistanceBetweenPoints(x1, y1, x2, y2) { * 5*x = 0 => 0 */ function getLinearEquationRoot(a, b) { - throw new Error('Not implemented'); + return -1 * b/a; } @@ -111,7 +113,11 @@ function getLinearEquationRoot(a, b) { * (0,1) (1,2) => 0 */ function getAngleBetweenVectors(x1, y1, x2, y2) { - throw new Error('Not implemented'); + var angle = Math.atan2(y2, x2) - Math.atan2(y1, x1) + if (angle < 0) { + angle += 2 * Math.PI; + } + return angle } /** @@ -127,7 +133,7 @@ function getAngleBetweenVectors(x1, y1, x2, y2) { * 0 => 0 */ function getLastDigit(value) { - throw new Error('Not implemented'); + return Math.abs(value) % 10; } @@ -143,7 +149,7 @@ function getLastDigit(value) { * '-525.5' => -525.5 */ function parseNumberFromString(value) { - throw new Error('Not implemented'); + return Number(value); } /** @@ -160,7 +166,7 @@ function parseNumberFromString(value) { * 1,2,3 => 3.741657386773941 */ function getParallelipidedDiagonal(a,b,c) { - throw new Error('Not implemented'); + return Math.sqrt(a*a + b*b + c*c); } /** @@ -181,7 +187,9 @@ function getParallelipidedDiagonal(a,b,c) { * 1678, 3 => 2000 */ function roundToPowerOfTen(num, pow) { - throw new Error('Not implemented'); + let a = Math.pow(10, pow); + + return (a === 0) ? num : Math.round(num / a) * a; } /** @@ -202,7 +210,11 @@ function roundToPowerOfTen(num, pow) { * 17 => true */ function isPrime(n) { - throw new Error('Not implemented'); + for (let i = 2; i < n / 2 + 1; i++) { + if (n % i == 0) + return false; + } + return true; } /** @@ -221,7 +233,7 @@ function isPrime(n) { * toNumber(new Number(42), 0) => 42 */ function toNumber(value, def) { - throw new Error('Not implemented'); + return typeof +value == 'number' && !isNaN(+value) ? +value : def; } module.exports = { diff --git a/task/03-date-tasks.js b/task/03-date-tasks.js index 83c6266bc..29b4400f1 100644 --- a/task/03-date-tasks.js +++ b/task/03-date-tasks.js @@ -22,7 +22,7 @@ * 'Sun, 17 May 1998 03:00:00 GMT+01' => Date() */ function parseDataFromRfc2822(value) { - throw new Error('Not implemented'); + return Date.parse(value); } /** @@ -37,7 +37,7 @@ function parseDataFromRfc2822(value) { * '2016-01-19T08:07:37Z' => Date() */ function parseDataFromIso8601(value) { - throw new Error('Not implemented'); + return Date.parse(value); } @@ -56,7 +56,8 @@ function parseDataFromIso8601(value) { * Date(2015,1,1) => false */ function isLeapYear(date) { - throw new Error('Not implemented'); + const year = date.getFullYear(); + return ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0); } @@ -76,7 +77,7 @@ function isLeapYear(date) { * Date(2000,1,1,10,0,0), Date(2000,1,1,15,20,10,453) => "05:20:10.453" */ function timeSpanToString(startDate, endDate) { - throw new Error('Not implemented'); + return (new Date(endDate - startDate)).toISOString().slice(11, -1); } @@ -94,7 +95,13 @@ function timeSpanToString(startDate, endDate) { * Date.UTC(2016,3,5,21, 0) => Math.PI/2 */ function angleBetweenClockHands(date) { - throw new Error('Not implemented'); + const hoursArrow = 0.5 * (60 * (date.getUTCHours() % 12) + date.getUTCMinutes()); + const minutesArrow = 6 * date.getUTCMinutes(); + let angle = Math.abs(hoursArrow - minutesArrow); + if (angle > 180) { + angle = 360 - angle; + } + return (angle * Math.PI / 180); } diff --git a/task/04-arrays-tasks.js b/task/04-arrays-tasks.js index ff3a4c019..6d404f859 100644 --- a/task/04-arrays-tasks.js +++ b/task/04-arrays-tasks.js @@ -23,22 +23,7 @@ * [0, 1, 2, 3, 4, 5], 5 => 5 */ function findElement(arr, value) { - throw new Error('Not implemented'); -} - -/** - * Generates an array of odd numbers of the specified length - * - * @param {number} len - * @return {array} - * - * @example - * 1 => [ 1 ] - * 2 => [ 1, 3 ] - * 5 => [ 1, 3, 5, 7, 9 ] - */ -function generateOdds(len) { - throw new Error('Not implemented'); + return arr.indexOf(value); } @@ -53,8 +38,22 @@ function generateOdds(len) { * [0, 1, 2, 3, 4, 5] => [0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5] * [] => [] */ + +function generateOdds(len) { + let arr = new Array(len); + arr.fill(1); + return arr.map((elem, index) => + { + return index*2 + 1; + }); +} + function doubleArray(arr) { - throw new Error('Not implemented'); + arr.map(elem => + { + return arr.push(elem); + }); + return arr; } @@ -70,7 +69,9 @@ function doubleArray(arr) { * [] => [] */ function getArrayOfPositives(arr) { - throw new Error('Not implemented'); + return arr.filter(function(value) { + return value > 0; + }); } /** @@ -85,7 +86,7 @@ function getArrayOfPositives(arr) { * [ 'cat, 'dog', 'raccon' ] => [ 'cat', 'dog', 'racoon' ] */ function getArrayOfStrings(arr) { - throw new Error('Not implemented'); + return arr.filter((x) => typeof (x) === 'string'); } /** @@ -102,7 +103,9 @@ function getArrayOfStrings(arr) { * [ false, 0, NaN, '', undefined ] => [ ] */ function removeFalsyValues(arr) { - throw new Error('Not implemented'); + return arr.filter(function(value) { + return Boolean(value); + }); } /** @@ -116,7 +119,7 @@ function removeFalsyValues(arr) { * [ 'a', 'b', 'c', 'd', 'e', 'f', 'g' ] => [ 'A', 'B', 'C', 'D', 'E', 'F', 'G' ] */ function getUpperCaseStrings(arr) { - throw new Error('Not implemented'); + return arr.map(value => value.toUpperCase()); } @@ -131,7 +134,7 @@ function getUpperCaseStrings(arr) { * [ 'angular', 'react', 'ember' ] => [ 7, 5, 5 ] */ function getStringsLength(arr) { - throw new Error('Not implemented'); + return arr.map(value => value.length); } /** @@ -146,7 +149,7 @@ function getStringsLength(arr) { * [ 1, 'b', 'c'], 0, 'x' => [ 'x', 1, 'b', 'c' ] */ function insertItem(arr, item, index) { - throw new Error('Not implemented'); + return arr.splice(index, 0, item); } /** @@ -160,9 +163,16 @@ function insertItem(arr, item, index) { * [ 'a', 'b', 'c', 'd'], 3 => [ 'a', 'b', 'c' ] */ function getHead(arr, n) { - throw new Error('Not implemented'); + return arr.slice(0, n); } +function sortDigitNamesByNumericOrder(arr) { + let alph = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']; + return arr.sort((a, b) => + { + return alph.indexOf(a)-alph.indexOf(b); + }); +} /** * Returns the n last items of the specified array @@ -175,7 +185,7 @@ function getHead(arr, n) { * [ 'a', 'b', 'c', 'd'], 3 => [ 'b', 'c', 'd' ] */ function getTail(arr, n) { - throw new Error('Not implemented'); + return arr.slice(Math.max(arr.length - n, 1)); } @@ -200,7 +210,7 @@ function getTail(arr, n) { * +'30,31,32,33,34' */ function toCsvText(arr) { - throw new Error('Not implemented'); + return arr.join('\n'); } /** @@ -215,7 +225,7 @@ function toCsvText(arr) { * [ 10, 100, -1 ] => [ 100, 10000, 1 ] */ function toArrayOfSquares(arr) { - throw new Error('Not implemented'); + return arr.map(value => value*value); } @@ -234,7 +244,7 @@ function toArrayOfSquares(arr) { * [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] => [ 1, 3, 6, 10, 15, 21, 28, 36, 45, 55 ] */ function getMovingSum(arr) { - throw new Error('Not implemented'); + return arr.map((x, y, arr) => arr.slice(0, y + 1).reduce((fs, val) => fs + val)); } /** @@ -249,7 +259,7 @@ function getMovingSum(arr) { * [ "a" ] => [] */ function getSecondItems(arr) { - throw new Error('Not implemented'); + return arr.filter((x, y) => y % 2 === 1); } @@ -268,7 +278,12 @@ function getSecondItems(arr) { * [ 1,2,3,4,5 ] => [ 1, 2,2, 3,3,3, 4,4,4,4, 5,5,5,5,5 ] */ function propagateItemsByPositionIndex(arr) { - throw new Error('Not implemented'); + let copy = []; + arr.map((x, y) => { + copy = copy.concat(new Array(y + 1).fill(x)); + return x; + }); + return copy; } @@ -286,7 +301,8 @@ function propagateItemsByPositionIndex(arr) { * [ 10, 10, 10, 10 ] => [ 10, 10, 10 ] */ function get3TopItems(arr) { - throw new Error('Not implemented'); + arr.sort(function(a, b){return b-a}); + return arr.slice(0,3); } @@ -304,25 +320,9 @@ function get3TopItems(arr) { * [ 1, '2' ] => 1 */ function getPositivesCount(arr) { - throw new Error('Not implemented'); + return arr.filter((x) => (x > 0) && (Number(x) === x)).length; } -/** - * Sorts digit names - * - * @param {array} arr - * @return {array} - * - * @example - * [] => [] - * [ 'nine','one' ] => [ 'one', 'nine' ] - * [ 'one','two','three' ] => [ 'one','two', 'three' ] - * [ 'nine','eight','nine','eight'] => [ 'eight','eight','nine','nine'] - * [ 'one','one','one','zero' ] => [ 'zero','one','one','one' ] - */ -function sortDigitNamesByNumericOrder(arr) { - throw new Error('Not implemented'); -} /** * Returns the sum of all items in the specified array of numbers @@ -337,7 +337,7 @@ function sortDigitNamesByNumericOrder(arr) { * [ 1, 10, 100, 1000 ] => 1111 */ function getItemsSum(arr) { - throw new Error('Not implemented'); + return arr.reduce((a, b) => a + b, 0); } /** @@ -353,7 +353,7 @@ function getItemsSum(arr) { * [ null, undefined, NaN, false, 0, '' ] => 6 */ function getFalsyValuesCount(arr) { - throw new Error('Not implemented'); + return arr.filter((x) => !x).length; } /** @@ -371,7 +371,7 @@ function getFalsyValuesCount(arr) { * [ true, 0, 1, 'true' ], true => 1 */ function findAllOccurences(arr, item) { - throw new Error('Not implemented'); + return arr.reduce(function(n, value) {return n + (value === item);}, 0); } /** @@ -386,7 +386,7 @@ function findAllOccurences(arr, item) { * ['rock', 'paper', 'scissors'] => 'rock,paper,scissors' */ function toStringList(arr) { - throw new Error('Not implemented'); + return arr.join(','); } @@ -415,7 +415,18 @@ function toStringList(arr) { * { country: 'Russia', city: 'Saint Petersburg' } */ function sortCitiesArray(arr) { - throw new Error('Not implemented'); + return arr.sort((a, b) => { + if (a.country > b.country) { + return 1; + } + if (a.country < b.country) { + return -1; + } + if (a.city > b.city) { + return 1; + } + return -1; + }); } /** @@ -437,7 +448,12 @@ function sortCitiesArray(arr) { * [0,0,0,0,1]] */ function getIdentityMatrix(n) { - throw new Error('Not implemented'); + return new Array(n).fill(0).map((x, y) => new Array(n).fill(0).map((x, y_) => { + if (y === y_) { + return 1; + } + return 0; + })); } /** @@ -454,7 +470,7 @@ function getIdentityMatrix(n) { * 3, 3 => [ 3 ] */ function getIntervalArray(start, end) { - throw new Error('Not implemented'); + return Array(end - start + 1).fill().map((x, y) => start + y); } /** @@ -469,7 +485,7 @@ function getIntervalArray(start, end) { * [ 1, 1, 2, 2, 3, 3, 4, 4] => [ 1, 2, 3, 4] */ function distinct(arr) { - throw new Error('Not implemented'); + return arr.filter((x, y) => arr.indexOf(x) === y); } /** @@ -503,7 +519,18 @@ function distinct(arr) { * } */ function group(array, keySelector, valueSelector) { - throw new Error('Not implemented'); + const map = new Map(); + array.map((x) => [keySelector(x), valueSelector(x)]).map((x) => { + if (map.has(x[0])) { + map.set(x[0], map.get(x[0]).concat(x[1])); + } else { + const arr = Array(1); + arr[0] = x[1]; + map.set(x[0], arr); + } + return x; + }); + return map; } @@ -519,7 +546,9 @@ function group(array, keySelector, valueSelector) { * ['one','two','three'], x=>x.split('') => ['o','n','e','t','w','o','t','h','r','e','e'] */ function selectMany(arr, childrenSelector) { - throw new Error('Not implemented'); + let retArr = []; + arr.map(childrenSelector).map((x) => retArr = retArr.concat(x)); + return retArr; } @@ -536,33 +565,17 @@ function selectMany(arr, childrenSelector) { * [[[ 1, 2, 3]]], [ 0, 0, 1 ] => 2 (arr[0][0][1]) */ function getElementByIndexes(arr, indexes) { - throw new Error('Not implemented'); + return arr.join().split(',')[indexes.reduce((accumulator, value) => accumulator + value)]; } - -/** - * Swaps the head and tail of the specified array: - * the head (first half) of array move to the end, the tail (last half) move to the start. - * The middle element (if exists) leave on the same position. - * - * - * @param {array} arr - * @return {array} - * - * @example - * [ 1, 2, 3, 4, 5 ] => [ 4, 5, 3, 1, 2 ] - * \----/ \----/ - * head tail - * - * [ 1, 2 ] => [ 2, 1 ] - * [ 1, 2, 3, 4, 5, 6, 7, 8 ] => [ 5, 6, 7, 8, 1, 2, 3, 4 ] - * - */ function swapHeadAndTail(arr) { - throw new Error('Not implemented'); + let hlen = Math.floor(arr.length/2); + let headArr = arr.slice(0, hlen); + let middleArr = arr.slice(hlen, hlen + (arr.length & 1)); + let tailArr = arr.slice(arr.length - hlen, arr.length); + return tailArr.concat(middleArr).concat(headArr); } - module.exports = { findElement: findElement, generateOdds: generateOdds, diff --git a/task/05-regex-tasks.js b/task/05-regex-tasks.js index b1c60f2d4..ff4986ed7 100644 --- a/task/05-regex-tasks.js +++ b/task/05-regex-tasks.js @@ -31,7 +31,8 @@ * @return {RegExp} */ function getRegexForGuid() { - throw new Error('Not implemented'); + let reg = '[0-9a-f]'; + return new RegExp('^{'+reg+'{8}-'+reg+'{4}-'+reg+'{4}-'+reg+'{4}-'+reg+'{12}}$', 'i'); } @@ -53,7 +54,7 @@ function getRegexForGuid() { * */ function getRegexForPitSpot() { - throw new Error('Not implemented'); + return new RegExp(/.*p.t.*/); } @@ -72,7 +73,7 @@ function getRegexForPitSpot() { * @return {RegExp} */ function getRegexForIPv4() { - throw new Error('Not implemented'); + return /^(([0-2][0-9]{2}|[0-9]{1,2})\.){3}([0-2][0-9]{2}|[0-9]{1,2})$/; } @@ -91,35 +92,13 @@ function getRegexForIPv4() { * @return {RegExp} */ function getRegexForSSN() { - throw new Error('Not implemented'); + return /\d{2}[1-9]-\d{1}[1-9]-\d{3}[1-9]/; } - -/** - * Returns the password validator regex. - * Regex will validate a password to make sure it meets the follwing criteria: - * - At least specified characters long (argument minLength) - * - Contains a lowercase letter - * - Contains an uppercase letter - * - Contains a number - * - Valid passwords will only be alphanumeric characters. - * - * @param {number} minLength - * @return {Regex} - * - * @example - * let validator = getPasswordValidator(6); - * 'password'.match(validator) => false - * 'Pa55Word'.match(validator) => true - * 'PASSw0rd'.match(validator) => true - * 'PASSW0RD'.match(validator) => false - * 'Pa55'.match(validator) => false - */ function getPasswordValidator(minLength) { - throw new Error('Not implemented'); + return new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[A-Za-z\\d]{'+minLength+',}'); } - module.exports = { getRegexForGuid: getRegexForGuid, getRegexForPitSpot: getRegexForPitSpot, diff --git a/task/06-conditions-n-loops-tasks.js b/task/06-conditions-n-loops-tasks.js index 249194c34..06dd524ac 100644 --- a/task/06-conditions-n-loops-tasks.js +++ b/task/06-conditions-n-loops-tasks.js @@ -9,63 +9,6 @@ **************************************************************************************************/ -/** - * Returns the 'Fizz','Buzz' or an original number using the following rules: - * 1) return original number - * 2) but if number multiples of three return 'Fizz' - * 3) for the multiples of five return 'Buzz' - * 4) for numbers which are multiples of both three and five return 'FizzBuzz' - * - * @param {number} num - * @return {any} - * - * @example - * 2 => 2 - * 3 => 'Fizz' - * 5 => 'Buzz' - * 4 => 4 - * 15 => 'FizzBuzz' - * 20 => 'Buzz' - * 21 => 'Fizz' - * - */ -function getFizzBuzz(num) { - throw new Error('Not implemented'); -} - - -/** - * Returns the factorial of the specified integer n. - * - * @param {number} n - * @return {number} - * - * @example: - * 1 => 1 - * 5 => 120 - * 10 => 3628800 - */ -function getFactorial(n) { - throw new Error('Not implemented'); -} - - -/** - * Returns the sum of integer numbers between n1 and n2 (inclusive). - * - * @param {number} n1 - * @param {number} n2 - * @return {number} - * - * @example: - * 1,2 => 3 ( = 1+2 ) - * 5,10 => 45 ( = 5+6+7+8+9+10 ) - * -1,1 => 0 ( = -1 + 0 + 1 ) - */ -function getSumBetweenNumbers(n1, n2) { - throw new Error('Not implemented'); -} - /** * Returns true, if a triangle can be built with the specified sides a,b,c and false in any other ways. @@ -81,45 +24,66 @@ function getSumBetweenNumbers(n1, n2) { * 10,1,1 => false * 10,10,10 => true */ -function isTriangle(a,b,c) { - throw new Error('Not implemented'); +function getFizzBuzz(num) { + if((num % 3 === 0) && (num % 5 === 0)) { + return 'FizzBuzz' + } else if(num % 5 === 0) { + return'Buzz' + } else if (num % 3 === 0) { + return 'Fizz' + } + return num } +function getFactorial(n) { + if (n < 0) + return -1; + else if (n === 0) + return 1; + else { + return (n * getFactorial(n - 1)); + } +} + +function getSumBetweenNumbers(n1, n2) { + let count = 0; + for(let i = n1; i <=n2; i++ ) { + count +=i + } + return count; +} -/** - * Returns true, if two specified axis-aligned rectangles overlap, otherwise false. - * Each rectangle representing by object - * { - * top: 5, - * left: 5, - * width: 20, - * height: 10 - * } - * - * (5;5) - * ------------- - * | | - * | | height = 10 - * ------------- - * width=20 - * - * NOTE: Please use canvas coordinate space (https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes#The_grid), - * it differs from Cartesian coordinate system. - * - * @param {object} rect1 - * @param {object} rect2 - * @return {bool} - * - * @example: - * { top: 0, left: 0, width: 10, height: 10 }, - * { top: 5, left: 5, width: 20, height: 20 } => true - * - * { top: 0, left: 0, width: 10, height: 10 }, - * { top:20, left:20, width: 20, height: 20 } => false - * - */ function doRectanglesOverlap(rect1, rect2) { - throw new Error('Not implemented'); + return !( + rect1.left + rect1.width < rect2.left || + rect2.left + rect2.width < rect1.left || + rect1.top + rect1.height < rect2.top || + rect2.top + rect2.height < rect1.top + ); +} + +function findFirstSingleChar(str) { + for (let i = 0; i < str.length; i++) { + if (str.indexOf(str[i]) == str.lastIndexOf(str[i])) { + return str[i]; + } + } + + return null; +} + +function getIntervalString(a, b, isStartIncluded, isEndIncluded) { + let res = ""; + res += (isStartIncluded && res.length === 0) ? "[" : "("; + res += a <= b ? `${a}, ${b}` : `${b}, ${a}`; + res += isEndIncluded ? "]" : ")"; + return res; +} + + + +function isTriangle(a,b,c) { + return a < b + c && b < a + c && c < a + b; } @@ -150,50 +114,12 @@ function doRectanglesOverlap(rect1, rect2) { * */ function isInsideCircle(circle, point) { - throw new Error('Not implemented'); + let dx = point.x - circle.center.x; + let dy = point.y - circle.center.y; + return (dx*dx + dy*dy) < (circle.radius * circle.radius); } -/** - * Returns the first non repeated char in the specified strings otherwise returns null. - * - * @param {string} str - * @return {string} - * - * @example: - * 'The quick brown fox jumps over the lazy dog' => 'T' - * 'abracadabra' => 'c' - * 'entente' => null - */ -function findFirstSingleChar(str) { - throw new Error('Not implemented'); -} - - -/** - * Returns the string representation of math interval, specified by two points and include / exclude flags. - * See the details: https://en.wikipedia.org/wiki/Interval_(mathematics) - * - * Please take attention, that the smaller number should be the first in the notation - * - * @param {number} a - * @param {number} b - * @param {bool} isStartIncluded - * @param {bool} isEndIncluded - * @return {string} - * - * @example - * 0, 1, true, true => '[0, 1]' - * 0, 1, true, false => '[0, 1)' - * 0, 1, false, true => '(0, 1]' - * 0, 1, false, false => '(0, 1)' - * Smaller number has to be first : - * 5, 3, true, true => '[3, 5]' - * - */ -function getIntervalString(a, b, isStartIncluded, isEndIncluded) { - throw new Error('Not implemented'); -} /** @@ -209,7 +135,7 @@ function getIntervalString(a, b, isStartIncluded, isEndIncluded) { * 'noon' => 'noon' */ function reverseString(str) { - throw new Error('Not implemented'); + return [...str].reverse().join(''); } @@ -226,113 +152,9 @@ function reverseString(str) { * 34143 => 34143 */ function reverseInteger(num) { - throw new Error('Not implemented'); -} - - -/** - * Validates the CCN (credit card number) and return true if CCN is valid - * and false otherwise. - * - * See algorithm here : https://en.wikipedia.org/wiki/Luhn_algorithm - * - * @param {number} cnn - * @return {boolean} - * - * @example: - * 79927398713 => true - * 4012888888881881 => true - * 5123456789012346 => true - * 378282246310005 => true - * 371449635398431 => true - * - * 4571234567890111 => false - * 5436468789016589 => false - * 4916123456789012 => false - */ -function isCreditCardNumber(ccn) { - throw new Error('Not implemented'); -} - - -/** - * Returns the digital root of integer: - * step1 : find sum of all digits - * step2 : if sum > 9 then goto step1 otherwise return the sum - * - * @param {number} n - * @return {number} - * - * @example: - * 12345 ( 1+2+3+4+5 = 15, 1+5 = 6) => 6 - * 23456 ( 2+3+4+5+6 = 20, 2+0 = 2) => 2 - * 10000 ( 1+0+0+0+0 = 1 ) => 1 - * 165536 (1+6+5+5+3+6 = 26, 2+6 = 8) => 8 - */ -function getDigitalRoot(num) { - throw new Error('Not implemented'); -} - - -/** - * Returns true if the specified string has the balanced brackets and false otherwise. - * Balanced means that is, whether it consists entirely of pairs of opening/closing brackets - * (in that order), none of which mis-nest. - * Brackets include [],(),{},<> - * - * @param {string} str - * @return {boolean} - * - * @example: - * '' => true - * '[]' => true - * '{}' => true - * '() => true - * '[[]' => false - * '][' => false - * '[[][][[]]]' => true - * '[[][]][' => false - * '{)' = false - * '{[(<{[]}>)]}' = true - */ -function isBracketsBalanced(str) { - throw new Error('Not implemented'); -} - - -/** - * Returns the human readable string of time period specified by the start and end time. - * The result string should be constrcuted using the folliwing rules: - * - * --------------------------------------------------------------------- - * Difference | Result - * --------------------------------------------------------------------- - * 0 to 45 seconds | a few seconds ago - * 45 to 90 seconds | a minute ago - * 90 seconds to 45 minutes | 2 minutes ago ... 45 minutes ago - * 45 to 90 minutes | an hour ago - * 90 minutes to 22 hours | 2 hours ago ... 22 hours ago - * 22 to 36 hours | a day ago - * 36 hours to 25 days | 2 days ago ... 25 days ago - * 25 to 45 days | a month ago - * 45 to 345 days | 2 months ago ... 11 months ago - * 345 to 545 days (1.5 years) | a year ago - * 546 days+ | 2 years ago ... 20 years ago - * --------------------------------------------------------------------- - * - * @param {Date} startDate - * @param {Date} endDate - * @return {string} - * - * @example - * Date('2000-01-01 01:00:00.100'), Date('2000-01-01 01:00:00.200') => 'a few seconds ago' - * Date('2000-01-01 01:00:00.100'), Date('2000-01-01 01:00:05.000') => '5 minutes ago' - * Date('2000-01-01 01:00:00.100'), Date('2000-01-02 03:00:05.000') => 'a day ago' - * Date('2000-01-01 01:00:00.100'), Date('2015-01-02 03:00:05.000') => '15 years ago' - * - */ -function timespanToHumanString(startDate, endDate) { - throw new Error('Not implemented'); + let str = ''; + str += num; + return [...str].reverse().join(''); } @@ -356,85 +178,119 @@ function timespanToHumanString(startDate, endDate) { * 365, 10 => '365' */ function toNaryString(num, n) { - throw new Error('Not implemented'); + return num.toString(n); } +function isCreditCardNumber(ccn) { + ccn = [...String(ccn)].reverse(); + ccn = ccn.reduce(function(sum, val, ind) + { + let dig = Number(val); + if(ind % 2) + dig *= 2; + sum += Math.floor(dig / 10); + sum += dig % 10; + return sum; + }, 0); + return (ccn * 3) % 10 == 0; +} -/** - * Returns the commom directory path for specified array of full filenames. - * - * @param {array} pathes - * @return {string} - * - * @example: - * ['/web/images/image1.png', '/web/images/image2.png'] => '/web/images/' - * ['/web/assets/style.css', '/web/scripts/app.js', 'home/setting.conf'] => '' - * ['/web/assets/style.css', '/.bin/mocha', '/read.me'] => '/' - * ['/web/favicon.ico', '/web-scripts/dump', '/webalizer/logs'] => '/' - */ -function getCommonDirectoryPath(pathes) { - throw new Error('Not implemented'); +function getDigitalRoot(num) { + do{ + let sum = 0; + while(num > 0){ + sum += num % 10; + num = Math.floor(num / 10); + } + num = sum; + }while(num > 9); + return num; } +function isBracketsBalanced(str) { + let pair = { + '>': '<', + ')': '(', + ']': '[', + '}': '{' + } + let res = [...str].reduce(function(acc, x, ind) + { + if (['(', '{', '[', '<'].indexOf(x) != -1) + acc.push(x); + else + { + if (acc.length > 0 && acc[acc.length - 1] == pair[x]) + acc.pop(); + else + acc.push(x); + } + return acc; + }, []); + return res.length == 0; +} -/** - * Returns the product of two specified matrixes. - * See details: https://en.wikipedia.org/wiki/Matrix_multiplication - * - * @param {array} m1 - * @param {array} m2 - * @return {array} - * - * @example: - * [[ 1, 0, 0 ], [[ 1, 2, 3 ], [[ 1, 2, 3 ], - * [ 0, 1, 0 ], X [ 4, 5, 6 ], => [ 4, 5, 6 ], - * [ 0, 0, 1 ]] [ 7, 8, 9 ]] [ 7, 8, 9 ]] - * - * [[ 4 ], - * [[ 1, 2, 3]] X [ 5 ], => [[ 32 ]] - * [ 6 ]] - * - */ -function getMatrixProduct(m1, m2) { - throw new Error('Not implemented'); + +function getCommonDirectoryPath(pathes) { + let answer = ''; + pathes = pathes.map(path => path.split('/')); + + while(pathes.length != 0) { + let tmp = pathes[0].shift(); + for (let i = 1; i < pathes.length; i++) { + if(pathes[i].shift() != tmp) { + return answer; + } + } + answer += tmp + '/'; + } + return answer; } +function getMatrixProduct(m1, m2) { + let n = m1.length, m = m2[0].length; + let answer = new Array(n); + for (let i=0; i < n; i++) + { + answer[i]=new Array(m); + } + for (let i = 0; i < m; i++) + for (let j = 0; j < n; j++) + answer[i][j]=0; + + for(let i = 0; i < n; i++) + for(let j = 0; j < m; j++) + for(let k = 0; k < m1[0].length; k++) + answer[i][j] += m1[i][k] * m2[k][j]; + return answer; +} -/** - * Returns the evaluation of the specified tic-tac-toe position. - * See the details: https://en.wikipedia.org/wiki/Tic-tac-toe - * - * Position is provides as 3x3 array with the following values: 'X','0', undefined - * Function should return who is winner in the current position according to the game rules. - * The result can be: 'X','0',undefined - * - * @param {array} position - * @return {string} - * - * @example - * - * [[ 'X', ,'0' ], - * [ ,'X','0' ], => 'X' - * [ , ,'X' ]] - * - * [[ '0','0','0' ], - * [ ,'X', ], => '0' - * [ 'X', ,'X' ]] - * - * [[ '0','X','0' ], - * [ ,'X', ], => undefined - * [ 'X','0','X' ]] - * - * [[ , , ], - * [ , , ], => undefined - * [ , , ]] - * - */ function evaluateTicTacToePosition(position) { - throw new Error('Not implemented'); + for (let i = 0; i < position.length; i++) + { + if ((position[i][0] === position[i][1]) && (position[i][0] === position[i][2]) && (typeof position[i][0] != 'undefined')) + return position[i][0]; + } + let fl1 = true, fl2 = true; + for (let x = 1; x < position.length; x++) + { + if (position[x][x] !== position[0][0]) + fl1 = false; + if (position[x][position.length - 1 - x] !== position[0][2]) + fl2 = false; + } + if (fl1 && typeof position[0][0] != undefined) + return position[0][0]; + if (fl2 && typeof position[0][2] != undefined) + return position[0][2]; + + for (let i = 0; i < position.length; i++) { + if ((position[0][i] === position[1][i]) && (position[0][i] === position[2][i]) && (typeof position[0][i] !== 'undefined')) + return position[0][i]; + } + return undefined; } - module.exports = { getFizzBuzz: getFizzBuzz, getFactorial: getFactorial, @@ -449,7 +305,6 @@ module.exports = { isCreditCardNumber: isCreditCardNumber, getDigitalRoot: getDigitalRoot, isBracketsBalanced: isBracketsBalanced, - timespanToHumanString : timespanToHumanString, toNaryString: toNaryString, getCommonDirectoryPath: getCommonDirectoryPath, getMatrixProduct: getMatrixProduct, diff --git a/task/07-yield-tasks.js b/task/07-yield-tasks.js index a2369790a..5a18b97f3 100644 --- a/task/07-yield-tasks.js +++ b/task/07-yield-tasks.js @@ -33,7 +33,19 @@ * */ function* get99BottlesOfBeer() { - throw new Error('Not implemented'); + let count = 99; + while (count >= 3) { + yield count + ' bottles of beer on the wall, ' + count + ' bottles of beer.'; + yield 'Take one down and pass it around, ' + --count + ' bottles of beer on the wall.'; + } + if (count = 2) { + yield count + ' bottles of beer on the wall, ' + count + ' bottles of beer.'; + yield 'Take one down and pass it around, ' + --count + ' bottle of beer on the wall.'; + } + yield '1 bottle of beer on the wall, 1 bottle of beer.'; + yield 'Take one down and pass it around, no more bottles of beer on the wall.'; + yield 'No more bottles of beer on the wall, no more bottles of beer.'; + yield 'Go to the store and buy some more, 99 bottles of beer on the wall.'; } @@ -47,7 +59,19 @@ function* get99BottlesOfBeer() { * */ function* getFibonacciSequence() { - throw new Error('Not implemented'); + yield 0; + yield 1; + + let last = 0; + let current = 1; + let new_val = 1; + + while (true){ + yield new_val; + last = current; + current = new_val; + new_val = last + current; + } } @@ -82,7 +106,14 @@ function* getFibonacciSequence() { * */ function* depthTraversalTree(root) { - throw new Error('Not implemented'); + let q1 = [root]; + while (q1.length) { + let node = q1.pop(); + yield node; + if (node.children) { + q1.push(...node.children.reverse()); + } + } } @@ -108,7 +139,15 @@ function* depthTraversalTree(root) { * */ function* breadthTraversalTree(root) { - throw new Error('Not implemented'); + let nodes = [root]; + for (let i = 0; i < nodes.length; i++) { + yield nodes[i]; + if ('children' in nodes[i]) { + for (let j = 0; j < nodes[i].children.length; j++) { + nodes.push(nodes[i].children[j]); + } + } + } } @@ -126,7 +165,25 @@ function* breadthTraversalTree(root) { * [ 1, 3, 5, ... ], [ -1 ] => [ -1, 1, 3, 5, ...] */ function* mergeSortedSequences(source1, source2) { - throw new Error('Not implemented'); + let src1 = source1(); + let src2 = source2(); + let i = src1.next().value; + let j = src2.next().value; + while (true) { + if ((i == undefined) && (j != undefined)) { + yield j; + j = src2.next().value; + } else if ((j == undefined) && (i != undefined)) { + yield i; + i = src1.next().value; + } else if ((i < j) && (i != undefined) && (j != undefined)) { + yield i; + i = src1.next().value; + } else if ((i >= j) && (i != undefined) && (j != undefined)) { + yield j; + j = src2.next().value; + } + } } diff --git a/task/08-objects-tasks.js b/task/08-objects-tasks.js index 610b1e7b2..0e6e3bc74 100644 --- a/task/08-objects-tasks.js +++ b/task/08-objects-tasks.js @@ -23,7 +23,12 @@ * console.log(r.getArea()); // => 200 */ function Rectangle(width, height) { - throw new Error('Not implemented'); + this.width = width; + this.height = height; + + this.getArea = () => { + return this.width * this.height + } } @@ -38,7 +43,7 @@ function Rectangle(width, height) { * { width: 10, height : 20 } => '{"height":10,"width":20}' */ function getJSON(obj) { - throw new Error('Not implemented'); + return JSON.stringify(obj); } diff --git a/task/09-functions-n-closures-tasks.js b/task/09-functions-n-closures-tasks.js index 6ba9dcedd..740553688 100644 --- a/task/09-functions-n-closures-tasks.js +++ b/task/09-functions-n-closures-tasks.js @@ -26,7 +26,9 @@ * */ function getComposition(f,g) { - throw new Error('Not implemented'); + return function (x) { + return f(g(x)); + } } @@ -47,7 +49,9 @@ function getComposition(f,g) { * */ function getPowerFunction(exponent) { - throw new Error('Not implemented'); + return function (number) { + return Math.pow(number, exponent); + }; } @@ -65,7 +69,10 @@ function getPowerFunction(exponent) { * getPolynom() => null */ function getPolynom() { - throw new Error('Not implemented'); + var tmp = Array.from(arguments).reverse(); + return (x) => { + return tmp.reduce((prev, curr, index) => prev+curr*Math.pow(x,index),0); + }; } @@ -84,7 +91,10 @@ function getPolynom() { * memoizer() => the same random number (next run, returns the previous cached result) */ function memoize(func) { - throw new Error('Not implemented'); + let cashed = func(); + return function() { + return cashed + } } @@ -104,7 +114,16 @@ function memoize(func) { * retryer() => 2 */ function retry(func, attempts) { - throw new Error('Not implemented'); + return()=>{ + for (var i = 0; i < attempts;){ + try{ + return func(); + } catch(err){ + i += 1; + } + } + return i; + }; } @@ -132,7 +151,13 @@ function retry(func, attempts) { * */ function logger(func, logFunc) { - throw new Error('Not implemented'); + return function(...props) { + const str = `${func.name}(${JSON.stringify(Array.from(props)).slice(1, -1)}) `; + logFunc(str + 'starts'); + const result = func.apply(null, props); + logFunc(str + 'ends'); + return result; + }; } @@ -150,7 +175,10 @@ function logger(func, logFunc) { * partialUsingArguments(fn, 'a','b','c','d')() => 'abcd' */ function partialUsingArguments(fn) { - throw new Error('Not implemented'); + let arg = Array.from(arguments).slice(1); + return function () { + return fn.apply(null, arg.concat(Array.from(arguments))); + } } @@ -171,7 +199,10 @@ function partialUsingArguments(fn) { * getId10() => 11 */ function getIdGeneratorFunction(startFrom) { - throw new Error('Not implemented'); + var curr = startFrom; + return function() { + return startFrom++; + } } diff --git a/test/06-conditions-n-loops-tests.js b/test/06-conditions-n-loops-tests.js index 93ffa3eee..ce22fce03 100644 --- a/test/06-conditions-n-loops-tests.js +++ b/test/06-conditions-n-loops-tests.js @@ -468,146 +468,7 @@ describe('06-conditions-n-loops-tasks', function() { }); - it.optional('timespanToHumanString should return the human string representation of datetime period', () => { - [ - { - startDate: '2000-01-01 01:00:00.100', - endDate: '2000-01-01 01:00:00.200', - expected: 'a few seconds ago' - }, { - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 01:00:30.000', - expected: 'a few seconds ago' - }, { - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 01:00:45.000', - expected: 'a few seconds ago' - }, { - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 01:00:45.001', - expected: 'a minute ago' - }, { - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 01:01:00.000', - expected: 'a minute ago' - }, { - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 01:01:30.000', - expected: 'a minute ago' - }, { - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 01:01:30.001', - expected: '2 minutes ago' - }, { - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 01:05:30.000', - expected: '5 minutes ago' - },{ - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 01:45:00.000', - expected: '45 minutes ago' - },{ - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 01:45:00.001', - expected: 'an hour ago' - },{ - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 02:00:00.000', - expected: 'an hour ago' - },{ - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 02:30:00.000', - expected: 'an hour ago' - },{ - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 02:30:00.001', - expected: '2 hours ago' - },{ - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 05:30:00.000', - expected: '4 hours ago' - },{ - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 05:30:00.001', - expected: '5 hours ago' - },{ - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 23:00:00.000', - expected: '22 hours ago' - },{ - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-01 23:00:00.001', - expected: 'a day ago' - },{ - startDate: '2000-01-01 01:00:00.000', - endDate: '2000-01-02 01:00:00.000', - expected: 'a day ago' - },{ - startDate: '2000-01-01 00:00:00.000', - endDate: '2000-01-02 12:00:00.000', - expected: 'a day ago' - },{ - startDate: '2000-01-01 00:00:00.000', - endDate: '2000-01-02 12:00:00.001', - expected: '2 days ago' - },{ - startDate: '2000-01-01 00:00:00.000', - endDate: '2000-01-05 12:00:00.000', - expected: '4 days ago' - },{ - startDate: '2000-01-01 00:00:00.000', - endDate: '2000-01-26 00:00:00.000', - expected: '25 days ago' - },{ - startDate: '2000-01-01 00:00:00.000', - endDate: '2000-01-26 00:00:00.001', - expected: 'a month ago' - },{ - startDate: '2000-01-01 00:00:00.000', - endDate: '2000-02-01 00:00:00.000', - expected: 'a month ago' - },{ - startDate: '2000-01-01 00:00:00.000', - endDate: '2000-02-15 00:00:00.000', - expected: 'a month ago' - },{ - startDate: '2000-01-01 00:00:00.000', - endDate: '2000-02-16 00:00:00.000', - expected: '2 months ago' - },{ - startDate: '2000-01-01 00:00:00.000', - endDate: '2000-05-20 00:00:00.000', - expected: '5 months ago' - },{ - startDate: '2000-01-01 00:00:00.000', - endDate: '2000-12-10 00:00:00.000', - expected: '11 months ago' - },{ - startDate: '2000-01-01 00:00:00.000', - endDate: '2000-12-12 00:00:00.000', - expected: 'a year ago' - },{ - startDate: '2000-01-01 00:00:00.000', - endDate: '2001-02-15 00:00:00.001', - expected: 'a year ago' - },{ - startDate: '2000-01-01 00:00:00.000', - endDate: '2001-06-01 00:00:00.001', - expected: 'a year ago' - },{ - startDate: '2000-01-01 00:00:00.000', - endDate: '2015-02-15 00:00:00.001', - expected: '15 years ago' - } - ].forEach(data => { - var actual = tasks.timespanToHumanString(new Date(data.startDate), new Date(data.endDate)); - assert.equal( - actual, - data.expected, - `timespanToHumanString('${data.startDate}', '${data.endDate}') shoud return '${data.expected}', but actually '${actual}'` - ) - }); - }); + it.optional('evaluateTicTacToePosition should return the winner if defined', () => {