diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..9efd82a --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,271 @@ +{ + "env": { + "browser": true, + "es6": true, + "node": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 2018 + }, + "rules": { + "indent": [ + "error", + 2 + ], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "single" + ], + "semi": [ + "error", + "always" + ], + "no-console": "off", + "no-loop-func": [ + "error" + ], + "block-spacing": [ + "error", + "always" + ], + "camelcase": [ + "error" + ], + "eqeqeq": [ + "error", + "always" + ], + "strict": [ + "error", + "global" + ], + "brace-style": [ + "error", + "1tbs", + { + "allowSingleLine": true + } + ], + "comma-style": [ + "error", + "last" + ], + "comma-spacing": [ + "error", + { + "before": false, + "after": true + } + ], + "eol-last": [ + "error" + ], + "func-call-spacing": [ + "error", + "never" + ], + "key-spacing": [ + "error", + { + "beforeColon": false, + "afterColon": true, + "mode": "minimum" + } + ], + "keyword-spacing": [ + "error", + { + "before": true, + "after": true, + "overrides": { + "function": { + "after": false + } + } + } + ], + "max-len": [ + "error", + { + "code": 80, + "ignoreUrls": true + } + ], + "max-nested-callbacks": [ + "error", + { + "max": 7 + } + ], + "new-cap": [ + "error", + { + "newIsCap": true, + "capIsNew": false, + "properties": true + } + ], + "new-parens": [ + "error" + ], + "no-trailing-spaces": [ + "error" + ], + "no-unneeded-ternary": [ + "error" + ], + "no-whitespace-before-property": [ + "error" + ], + "object-curly-spacing": [ + "error", + "always" + ], + "operator-assignment": [ + "error", + "always" + ], + "operator-linebreak": [ + "error", + "after" + ], + "semi-spacing": [ + "error", + { + "before": false, + "after": true + } + ], + "space-before-blocks": [ + "error", + "always" + ], + "space-before-function-paren": [ + "error", + { + "anonymous": "always", + "named": "never", + "asyncArrow": "always" + } + ], + "space-in-parens": [ + "error", + "never" + ], + "space-infix-ops": [ + "error" + ], + "space-unary-ops": [ + "error", + { + "words": true, + "nonwords": false, + "overrides": { + "typeof": false + } + } + ], + "no-unreachable": [ + "error" + ], + "no-global-assign": [ + "error" + ], + "no-self-compare": [ + "error" + ], + "no-unmodified-loop-condition": [ + "error" + ], + "no-constant-condition": [ + "error", + { + "checkLoops": false + } + ], + "no-console": [ + "off" + ], + "no-useless-concat": [ + "error" + ], + "no-useless-escape": [ + "error" + ], + "no-shadow-restricted-names": [ + "error" + ], + "no-use-before-define": [ + "error", + { + "functions": false + } + ], + "arrow-parens": [ + "error", + "as-needed" + ], + "arrow-body-style": [ + "error", + "as-needed" + ], + "arrow-spacing": [ + "error" + ], + "no-confusing-arrow": [ + "error", + { + "allowParens": true + } + ], + "no-useless-computed-key": [ + "error" + ], + "no-useless-rename": [ + "error" + ], + "no-var": [ + "error" + ], + "object-shorthand": [ + "error", + "always" + ], + "prefer-arrow-callback": [ + "error" + ], + "prefer-const": [ + "error" + ], + "prefer-numeric-literals": [ + "error" + ], + "prefer-rest-params": [ + "error" + ], + "prefer-spread": [ + "error" + ], + "rest-spread-spacing": [ + "error", + "never" + ], + "template-curly-spacing": [ + "error", + "never" + ] + }, + "overrides": [ + { + "files": ["1-let.js"], + "rules": { + "prefer-const": "off" + } + } + ] +} \ No newline at end of file diff --git a/Exercises.en.md b/Exercises.en.md new file mode 100644 index 0000000..aeea5a9 --- /dev/null +++ b/Exercises.en.md @@ -0,0 +1,93 @@ +# Exercises + +## №1 Palindrome + +Implement the `isPalindrome (str: string)` function, which allows you to return true, +if the string is a palindrome, and false if not. + +In this case, spaces and punctuation marks must be considered. + +For example: +```js +isPalindrome('racecar') === true +isPalindrome('table') === false +``` + +## №2 Format number to a human-readable string + +Implement the function `humanizeFormat (num: number)`, which allows you to format a number +into a human-readable string with the correct suffix, such as `1st, 2nd, 3rd, 4th`. + +For example: +```js +humanizeFormat() === 'undefined' +humanizeFormat(1) === '1st' +humanizeFormat(3) === '3rd' +humanizeFormat(13) === '13th' +humanizeFormat(302) === '302nd' +``` + +## №3 Emoji + +Implement the function `getEmojisFromText (textWithEmoji: string)`, which takes a string, +which consists of unicode emoji `'0x9749, 0x9752, 0x9917, 0x9925, 0x9935'` +and returns a string of sorted emojis. + +For example: +```js +getEmojisFromText('0x9749, 0x9752, 0x9917, 0x9925, 0x9935') --> '⛏ ⛅ ⚽ ☘ ☕' +getEmojisFromText('0x9975, 0x9977, 0x9917, 0x9968, 0x9935, 0x9978') --> '⛺ ⛏ ⛰ ⚽ ⛹ ⛷' +``` + +## №4 Anagram + +Anagrams are words that are obtained when rearranging letters or sounds in places in the original word. +For example, finder and Friend. + +Implement the `isAnagram (first: string, second: string)` function, which checks +whether two lines are anagrams, and the case of letters does not matter. +Only characters are taken into account, spaces or any other signs `^,., *,! ... `are not taken into account. + +For example: +```js +isAnagram('Eleven plus Two', 'Twelve plus one'); --> true +isAnagram('finder', 'Friend') --> true +isAnagram('finder', 'Fri---end') --> true +isAnagram('fi^nd&e^r', 'Fri---end') --> true +isAnagram('hello', 'bye') --> false +``` + +## №5 Acronym + +An acronym is a word or name formed from the initial components of a longer name or phrase, +usually using individual initial letters, as in NATO (North Atlantic Treaty Organization) or +EU (European Union), but sometimes using syllables, as in Benelux (Belgium, Netherlands and Luxembourg), +or a mixture of the two, as in radar (RAdio Detection And Ranging). + +Implement the function `convertToAcronim (words: string)`, +which translates the phrase into an abbreviation. + +For example: +```js +convertToAcronim('GNU Image Manipulation Program'); --> 'GIMP' +convertToAcronim('Kyiv Polytechnic Institute'); --> 'KPI' +convertToAcronim('First In, First Out'); --> 'FIFO' +convertToAcronim('Complementary metal-oxide semiconductor'); --> 'CMOS' +``` + +## №6 Weirdcase in Ruby + +Write a function `toWeirdCase(text: string)` that accepts a string, and returns the same string with all even indexed +characters in each word upper cased, and all odd indexed characters in each word lower cased. +The indexing just explained is zero based, so the zero-ith index is even, +therefore that character should be upper cased. + +The passed in string will only consist of alphabetical characters and spaces `(' ')`. +Spaces will only be present if there are multiple words. +Words will be separated by a single space `(' ')`. + +For example: +```js +toWeirdCase('String'); --> 'StRiNg' +toWeirdCase('Weird string case'); --> 'WeIrD StRiNg CaSe' +``` \ No newline at end of file diff --git a/Exercises.ru.md b/Exercises.ru.md new file mode 100644 index 0000000..34f2918 --- /dev/null +++ b/Exercises.ru.md @@ -0,0 +1,94 @@ +# Упражнения + +## №1 Палиндром + +Реализуйте функцию `isPalindrome(str: string)`, которая позволяет вернуть значение true, +если строка является палиндромом, и false — если нет. + +При этом нужно учитывать пробелы и знаки препинания. + +Например: +```js +isPalindrome('racecar') === true +isPalindrome('table') === false +``` + +## №2 Форматировать число в удобочитаемую строку + +Реализуйте функцию `humanizeFormat(num: number)`, которая позволяет отформатировать число +в удобочитаемую строку с правильным суффиксом, таким как `1st, 2nd, 3rd, 4th`. + +Например: +```js +humanizeFormat() === 'undefined' +humanizeFormat(1) === '1st' +humanizeFormat(3) === '3rd' +humanizeFormat(13) === '13th' +humanizeFormat(302) === '302nd' +``` + +## №3 Эмодзи + +Реализуйте функцию `getEmojisFromText(textWithEmoji: string)`, которая принимает строку, +что состоит из юникодов эмодзи `'0x9749, 0x9752, 0x9917, 0x9925, 0x9935'` +и возвращает строку отсортированных эмодзи. + +Например: +```js +getEmojisFromText('0x9749, 0x9752, 0x9917, 0x9925, 0x9935') --> '⛏ ⛅ ⚽ ☘ ☕' +getEmojisFromText('0x9975, 0x9977, 0x9917, 0x9968, 0x9935, 0x9978') --> '⛺ ⛏ ⛰ ⚽ ⛹ ⛷' +``` + +## №4 Анаграмма + +Анаграммы — слова, которые получаются при перестановке букв или звуков местами в исходном слове. +Например, апельсин и спаниель, старорежимность и нерасторжимость, равновесие и своенравие. + +Реализуйте функцию `isAnagram(first: string, second: string)`, которая проверяет, +являются ли две строки анаграммами, причем регистр букв не имеет значения. +Учитываются лишь символы, пробелы или любые другие знаки `^, ., *, ! ...` в расчет не берутся. + +Например: +```js +isAnagram('Eleven plus Two', 'Twelve plus one'); --> true +isAnagram('finder', 'Friend') --> true +isAnagram('finder', 'Fri---end') --> true +isAnagram('fi^nd&e^r', 'Fri---end') --> true +isAnagram('hello', 'bye') --> false +``` + +## №5 Акроним + +Акро́ним — вид аббревиатуры. Акронимы образуются начальными звуками (например ВУЗ, ТАСС, БАМ). +Фактически, акроним представляет собой слово, являющееся сокращением, +которое можно произнести слитно, в отличие от других видов аббревиатур, +которые произносят «по буквам», например: КПИ, НАУ. + +Реализуйте функцию `convertToAcronim(words: string)`, +которая переводит словосочетание в аббревиатуру. + +Например: +```js +convertToAcronim('GNU Image Manipulation Program'); --> 'GIMP' +convertToAcronim('Kyiv Polytechnic Institute'); --> 'KPI' +convertToAcronim('First In, First Out'); --> 'FIFO' +convertToAcronim('Complementary metal-oxide semiconductor'); --> 'CMOS' +``` + +## №6 Странный регистр + +Реализуйте функцию `toWeirdCase(text: string)`, которая принимает строку и возвращает +ту же строку со всеми четными индексированными символами в каждом слове в верхнем регистре +и всеми нечетными индексированными символами в каждом слове в нижнем регистре. +Только что объясненное индексирование основано на нуле, поэтому индекс с нулевым значением +является четным, поэтому этот символ должен быть в верхнем регистре. + +Переданная строка будет состоять только из букв и пробелов `(' ')`. +Пробелы будут присутствовать только если есть несколько слов. +Слова будут разделены одним пробелом `(' ')`. + +Например: +```js +toWeirdCase('String'); --> 'StRiNg' +toWeirdCase('Weird string case'); --> 'WeIrD StRiNg CaSe' +``` \ No newline at end of file diff --git a/Exercises/1-palindrome.js b/Exercises/1-palindrome.js new file mode 100644 index 0000000..a8b0173 --- /dev/null +++ b/Exercises/1-palindrome.js @@ -0,0 +1,8 @@ +'use strict'; + +const isPalindrome = str => { + + +}; + +module.exports = { isPalindrome }; diff --git a/Exercises/1-palindrome.test b/Exercises/1-palindrome.test new file mode 100644 index 0000000..2a30bbc --- /dev/null +++ b/Exercises/1-palindrome.test @@ -0,0 +1,17 @@ +({ + name: 'isPalindrome', + length: [80, 120], + test: isPalindrome => { + const x = isPalindrome('racecar'); + if (typeof x !== 'boolean') throw new Error('Expected boolean result'); + if (x !== true) { + throw new Error('Expected true'); + } + + const y = isPalindrome('hello'); + if (y === true) throw new Error('Expected false'); + + const z = isPalindrome('aBbA'); + if (z !== true) throw new Error('Expected true'); + } +}) \ No newline at end of file diff --git a/Exercises/2-human-readable.js b/Exercises/2-human-readable.js new file mode 100644 index 0000000..92f77f0 --- /dev/null +++ b/Exercises/2-human-readable.js @@ -0,0 +1,6 @@ +'use strict'; +const humanizeFormat = num => { + +}; + +module.exports = { humanizeFormat }; diff --git a/Exercises/2-human-readable.test b/Exercises/2-human-readable.test new file mode 100644 index 0000000..0fbd631 --- /dev/null +++ b/Exercises/2-human-readable.test @@ -0,0 +1,24 @@ +({ + name: 'humanizeFormat', + length: [180, 320], + test: humanizeFormat => { + const x = humanizeFormat(); + if (typeof x !== 'undefined') throw new Error('Expected undefined result'); + + const y = humanizeFormat(1); + if (y !== '1st') throw new Error('Expected 1st'); + if (typeof y !== 'string') throw new Error('Type of result must be string'); + + const z = humanizeFormat(11); + if (z !== '11th') throw new Error('Expected 11th'); + + const a = humanizeFormat(13); + if (a !== '13th') throw new Error('Expected 13th'); + + const b = humanizeFormat(103); + if (b !== '103rd') throw new Error('Expected 103rd'); + + const c = humanizeFormat(402); + if (c !== '402nd') throw new Error('Expected 402nd'); + } +}) \ No newline at end of file diff --git a/Exercises/3-emoji.js b/Exercises/3-emoji.js new file mode 100644 index 0000000..542762e --- /dev/null +++ b/Exercises/3-emoji.js @@ -0,0 +1,7 @@ +'use strict'; + +const getEmojisFromText = textWithEmoji => { + +} + +module.exports = { getEmojisFromText }; diff --git a/Exercises/3-emoji.test b/Exercises/3-emoji.test new file mode 100644 index 0000000..dfaa8df --- /dev/null +++ b/Exercises/3-emoji.test @@ -0,0 +1,13 @@ +({ + name: 'getEmojisFromText', + length: [120, 200], + test: getEmojisFromText => { + + const y = getEmojisFromText('0x9749, 0x9752, 0x9917, 0x9925, 0x9935'); + if (y !== '⛏ ⛅ ⚽ ☘ ☕') throw new Error('Expected ⛏ ⛅ ⚽ ☘ ☕'); + + const z = getEmojisFromText('0x9975, 0x9977, 0x9917, 0x9968, 0x9935, 0x9978'); + if (z !== '⛺ ⛏ ⛰ ⚽ ⛹ ⛷') throw new Error('Expected ⛺ ⛏ ⛰ ⚽ ⛹ ⛷'); + + } +}) \ No newline at end of file diff --git a/Exercises/4-anagram.js b/Exercises/4-anagram.js new file mode 100644 index 0000000..d501bad --- /dev/null +++ b/Exercises/4-anagram.js @@ -0,0 +1,10 @@ +'use strict'; + +//Can be used an auxiliary function + +const isAnagram = (first, second) => { + +}; + + +module.exports = { isAnagram }; diff --git a/Exercises/4-anagram.test b/Exercises/4-anagram.test new file mode 100644 index 0000000..c61e082 --- /dev/null +++ b/Exercises/4-anagram.test @@ -0,0 +1,19 @@ +({ + name: 'isAnagram', + length: [40, 120], + test: isAnagram => { + + const x = isAnagram('hello', 'bye'); + if (x !== false) throw new Error('Expected false when args are (hello, bye)'); + + const y = isAnagram('fi^nd&e^r', 'Fri---end'); + if (y !== true) throw new Error('Expected true when args are (fi^nd&e^r, Fri---end)'); + + const z = isAnagram('Eleven plus Two', 'Twelve plus one'); + if (z !== true) throw new Error('Expected true when args are (Eleven plus Two, Twelve plus one)'); + + const a = isAnagram('finder', 'Friend'); + if (a !== true) throw new Error('Expected true when args are (finder, Friend)'); + + } +}) \ No newline at end of file diff --git a/Exercises/5-acronym.js b/Exercises/5-acronym.js new file mode 100644 index 0000000..a268640 --- /dev/null +++ b/Exercises/5-acronym.js @@ -0,0 +1,7 @@ +'use strict'; + +const convertToAcronim = words => { + +}; + +module.exports = { convertToAcronim }; diff --git a/Exercises/5-acronym.test b/Exercises/5-acronym.test new file mode 100644 index 0000000..b5202b7 --- /dev/null +++ b/Exercises/5-acronym.test @@ -0,0 +1,19 @@ +({ + name: 'convertToAcronim', + length: [70, 140], + test: convertToAcronim => { + + const x = convertToAcronim('GNU Image Manipulation Program'); + if (x !== 'GIMP') throw new Error('Expected GIMP result'); + + const y = convertToAcronim('Kyiv Polytechnic Institute'); + if (y !== 'KPI') throw new Error('Expected KPI result'); + + const z = convertToAcronim('First In, First Out'); + if (z !== 'FIFO') throw new Error('Expected FIFO result'); + + const a = convertToAcronim('Complementary metal-oxide semiconductor'); + if (a !== 'CMOS') throw new Error('Expected CMOS result'); + + } +}) \ No newline at end of file diff --git a/Exercises/6-weird-text.js b/Exercises/6-weird-text.js new file mode 100644 index 0000000..2ef3932 --- /dev/null +++ b/Exercises/6-weird-text.js @@ -0,0 +1,8 @@ +'use strict'; + +const toWeirdCase = string => { + +}; + +module.exports = { toWeirdCase }; + diff --git a/Exercises/6-weird-text.test b/Exercises/6-weird-text.test new file mode 100644 index 0000000..c4e4448 --- /dev/null +++ b/Exercises/6-weird-text.test @@ -0,0 +1,19 @@ +({ + name: 'toWeirdCase', + length: [120, 160], + test: toWeirdCase => { + + const x = toWeirdCase('This is a test'); + if (x !== 'ThIs Is A TeSt') throw new Error('Expected ThIs Is A TeSt result'); + + const y = toWeirdCase('String'); + if (y !== 'StRiNg') throw new Error('Expected StRiNg result'); + + const z = toWeirdCase('is'); + if (z !== 'Is') throw new Error('Expected Is result'); + + const a = toWeirdCase('Weird string case'); + if (a !== 'WeIrD StRiNg CaSe') throw new Error('Expected WeIrD StRiNg CaSe result'); + + } +}) \ No newline at end of file diff --git a/Solutions/1-palindrome.js b/Solutions/1-palindrome.js new file mode 100644 index 0000000..55f2ba5 --- /dev/null +++ b/Solutions/1-palindrome.js @@ -0,0 +1,8 @@ +'use strict'; + +const isPalindrome = str => { + str = str.toLowerCase(); + return str === [...str].reverse().join(''); +}; + +module.exports = { isPalindrome }; diff --git a/Solutions/2-human-readable.js b/Solutions/2-human-readable.js new file mode 100644 index 0000000..6089f85 --- /dev/null +++ b/Solutions/2-human-readable.js @@ -0,0 +1,15 @@ +'use strict'; +const humanizeFormat = num => { + if (typeof(num) === 'undefined') return; + if (num % 100 >= 11 && num % 100 <= 13) + return num + 'th'; + + return num + ({ + 1: 'st', + 2: 'nd', + 3: 'rd', + }[num % 10] || 'th'); +}; + + +module.exports = { humanizeFormat }; diff --git a/Solutions/3-emoji.js b/Solutions/3-emoji.js new file mode 100644 index 0000000..c2c2935 --- /dev/null +++ b/Solutions/3-emoji.js @@ -0,0 +1,9 @@ +'use strict'; +const getEmojisFromText = textWithEmoji => + textWithEmoji.split`, ` + .map(u => parseInt(u.substr(2))) + .reverse() + .map(u => String.fromCodePoint(u)) + .join` `; + +module.exports = { getEmojisFromText }; diff --git a/Solutions/4-anagram.js b/Solutions/4-anagram.js new file mode 100644 index 0000000..ec91ed8 --- /dev/null +++ b/Solutions/4-anagram.js @@ -0,0 +1,11 @@ +'use strict'; + +const sort = str => + [...str.replace(/[^\w]/g, '').toLowerCase()] + .sort() + .join``; + +const isAnagram = (first, second) => sort(first) === sort(second); + + +module.exports = { isAnagram }; diff --git a/Solutions/5-acronym.js b/Solutions/5-acronym.js new file mode 100644 index 0000000..f45c921 --- /dev/null +++ b/Solutions/5-acronym.js @@ -0,0 +1,10 @@ +'use strict'; + +const convertToAcronim = words => + words.replace(/[ \-_]/ig, ' ') + .split` ` + .map(w => w[0].toUpperCase()) + .join``; + + +module.exports = { convertToAcronim }; diff --git a/Solutions/6-weird-text.js b/Solutions/6-weird-text.js new file mode 100644 index 0000000..68744fd --- /dev/null +++ b/Solutions/6-weird-text.js @@ -0,0 +1,7 @@ +'use strict'; +const toWeirdCase = string => string.split` ` + .map(w => [...w] + .map((l, i) => ((i % 2 == 0) ? l.toUpperCase() : l.toLowerCase())).join``) + .join` `; + +module.exports = { toWeirdCase }; diff --git a/bin/hpw.js b/bin/hpw.js new file mode 100644 index 0000000..3dcbcf6 --- /dev/null +++ b/bin/hpw.js @@ -0,0 +1,136 @@ +#!/usr/bin/env node +'use strict'; + +const PARSING_TIMEOUT = 1000; +const EXECUTION_TIMEOUT = 5000; + +const vNode = parseInt(process.versions.node.split('.')[0], 10); +if (vNode < 10) { + console.log('You need node.js v10.0 or greater to run tests'); + process.exit(1); +} + +const vm = require('vm'); +const fs = require('fs').promises; +const concolor = require('concolor'); + +const curDir = process.cwd(); +const dir = curDir + (curDir.includes('/Exercises') ? '' : '/Exercises'); + +const prepareSandbox = () => { + const context = { module: {}, console }; + context.global = context; + const sandbox = vm.createContext(context); + return sandbox; +}; + +const loadFile = async file => { + const fileName = dir + '/' + file; + const data = await fs.readFile(fileName, 'utf8'); + const isTest = file.includes('.test'); + const src = isTest ? `() => ( ${data} );` : `() => { ${data} };`; + let script; + try { + const options = { timeout: PARSING_TIMEOUT }; + script = new vm.Script(src, options); + } catch (e) { + console.dir(e); + console.log('Parsing error'); + process.exit(1); + } + const sandbox = prepareSandbox(); + let exported, result; + try { + const options = { timeout: EXECUTION_TIMEOUT }; + const f = script.runInNewContext(sandbox, options); + result = f(); + exported = sandbox.module.exports; + } catch (e) { + console.dir(e); + console.log('Execution timeout'); + process.exit(1); + } + return exported ? exported : result; +}; + +const countLines = s => { + let count = 1; + for (let i = 0; i < s.length; i++) { + if (s[i] === '\n') count++; + } + return count; +}; + +const checkTarget = async (name, target, test) => { + if (typeof target === 'function') { + if (target.name !== test.name) { + throw new Error(`Function ${test.name} is not found`); + } + } + const targetLength = target.toString().length; + const lines = countLines(target.toString()); + const msgTarget = concolor` Target: ${name}(b,white), `; + const msgLength = concolor`Length: ${targetLength}(b,white), `; + const msgLines = concolor`Lines: ${lines}(b,white)`; + console.log(msgTarget + msgLength + msgLines); + const [minLength, maxLength] = test.length || []; + if (targetLength > maxLength) throw new Error('Solution is too long'); + if (targetLength < minLength) throw new Error('Solution is too short'); + let casesResult = 'No test cases'; + if (test.cases) { + for (const callCase of test.cases) { + const expected = JSON.stringify(callCase.pop()); + const targetResult = target(...callCase); + const result = JSON.stringify(targetResult); + const callArgs = callCase.map(el => JSON.stringify(el)).join(', '); + const args = `arguments: (${callArgs})`; + if (result !== expected) { + throw new Error( + `Case failed: ${args}, expected: ${expected}, result: ${result}` + ); + } + } + casesResult = concolor`Passed cases: ${test.cases.length}(b,white)`; + } + if (test.test) { + test.test(target); + } + console.log(concolor` Status: ${'Passed'}(b,green), ${casesResult}(green)`); +}; + +const executeTest = async file => { + const jsFile = `./${file}.js`; + const js = await loadFile(jsFile); + const testFile = `./${file}.test`; + const test = await loadFile(testFile); + const tests = Array.isArray(test) ? test : [test]; + for (const currentTest of tests) { + const { name } = currentTest; + const target = js[name]; + if (!target) throw new Error(`No implementation detected: ${name}`); + await checkTarget(name, target, currentTest); + } +}; + +(async () => { + console.log(concolor.white('How Programming Works')); + console.log(concolor.info('Labs Auto Checker\n')); + const files = await fs.readdir(dir); + const tests = files + .filter(file => file.endsWith('.test')) + .map(file => file.substring(0, file.length - '.test'.length)); + for (const test of tests) { + console.log(concolor`\nTest ${test}(b,white)`); + try { + await executeTest(test); + } catch (e) { + const lines = e.stack.split('\n'); + if (lines[1].includes('at Object.test')) { + console.log(concolor` Error: ${e.message}(b,red)`); + } else { + const stack = lines.filter(s => !s.includes('hpw.js')).join('\n '); + console.log(concolor` Error: ${stack}(b,red)`); + } + } + } +})(); diff --git a/bin/install.js b/bin/install.js new file mode 100644 index 0000000..3052623 --- /dev/null +++ b/bin/install.js @@ -0,0 +1,9 @@ +'use strict'; + +const concolor = require('concolor'); +const isWin = !!process.platform.match(/^win/); + +console.log(concolor.info('Installing...')); +if (isWin) console.log(concolor.error('Please install Linux')); +console.log('\nUsage:'); +console.log(concolor.white(' npm test')); diff --git a/package.json b/package.json new file mode 100644 index 0000000..c543fc4 --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "hpw", + "version": "0.1.8", + "author": "Timur Shemsedinov ", + "description": "Labs Auto Checker", + "license": "MIT", + "keywords": [ + "javascript", + "programming", + "node.js" + ], + "readmeFilename": "README.md", + "repository": { + "type": "git", + "url": "https://github.com/HowProgrammingWorks/Labs" + }, + "bugs": { + "url": "https://github.com/HowProgrammingWorks/Labs/issues", + "email": "timur.shemsedinov@gmail.com" + }, + "main": "./bin/hpw.js", + "bin": { + "hpw": "./bin/hpw.js" + }, + "scripts": { + "install": "node ./bin/install.js", + "test": "node ./bin/hpw.js", + "lint": "eslint ." + }, + "engines": { + "node": ">=10.0.0" + }, + "dependencies": { + "concolor": "^0.1.12", + "metasync": "^0.3.31" + }, + "devDependencies": { + "eslint": "^6.4.0" + } +}