Created
April 8, 2018 17:03
-
-
Save Tolomeo/39c6e905a0e2a75b71301d5797523818 to your computer and use it in GitHub Desktop.
Coding Challenge JS Developer Code Challenge // source http://jsbin.com/ruyoyip
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<script src="http://jashkenas.github.io/underscore/underscore-min.js"></script> | |
<meta name="description" content="JS Developer Code Challenge" /><html> | |
<head> | |
<title>Coding Challenge</title> | |
<style id="jsbin-css"> | |
body { | |
font-family: Arial; | |
font-size: 80%; | |
} | |
ul.expected { | |
font-family: Courier; | |
} | |
ul.output { | |
font-family: Courier; | |
background-color: white; | |
border: 1px solid #999999; | |
padding: 10px 30px 10px 30px; | |
} | |
ul.output li {; | |
margin-bottom: 10px; | |
} | |
ul.output li code { | |
font-size: 1.2em; | |
color: blue; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>Terms of the Exercise</h1> | |
<ul> | |
<li>Try and stay away from libraries if possible</li> | |
<li>You can take as long as you like to complete the exercise, but for an indicative timescale we expect a senior developer can accomplish this in an hour.</li> | |
<li>You may use online resources to assist you with specific techniques, syntax etc. but please do not just copy code.</li> | |
<li>Please don't share this exercise with any third party</li> | |
</ul> | |
<h1>The Challenge</h1> | |
<p> | |
The aim of the exercise is to demonstrate your problem solving and understanding of JavaScript by implementing something found in every unit testing tool - an "assertEquals" method.</p> | |
<ul> | |
<li>Fill in the "assertEquals" function such that it will correctly compare the passed "expected" vs "actual" parameters.</li> | |
<li>You may add more functions.</li> | |
<li>Credit will be given for approach, correctly identifying "failed" assertEquals, "clean" code and coding style.</li> | |
</ul> | |
<h1>Expected Result</h1> | |
The following tests should "fail": <strong>02, 03, 04, 07, 08 and 09</strong> - and the failures should be reported using the provided mechanism.<br/> | |
Ideally the failure messages should report further information: | |
<ul class="expected"> | |
<li>Test 02: Expected "abcdef" found "abc"</li> | |
<li>Test 03: Expected type Array but found Object</li> | |
<li>Test 04: Expected array length 2 but found 3</li> | |
<li>Test 07: Expected propB.propA[1].propB "b" but found "c"</li> | |
<li>Test 08: Expected propB.propC but was not found</li> | |
<li>Test 09: Expected type null but found type Object</li> | |
</ul> | |
<h1>Output</h1> | |
<ul class="output" id="messages"></ul> | |
<script id="jsbin-javascript"> | |
/** | |
* Shared utility fn used to throw a TypeError | |
* | |
* @param {String} errorMessage | |
* @throws {TypeError} | |
*/ | |
function throwError(errorMessage) { | |
throw new TypeError(errorMessage) | |
} | |
/** | |
* Returns the type of the passed argument | |
* | |
* @param {any} what - The object to return the type of | |
* @returns {String} | |
*/ | |
function getTypeOf (what) { | |
let type = typeof what | |
if (type === 'object') { | |
return getObjectTypeOf(what) | |
} | |
return type | |
} | |
/** | |
* Returns the object type of object passed | |
* | |
* @param {Object} what | |
* @returns {String} | |
*/ | |
function getObjectTypeOf (what) { | |
if (what === null) | |
return "null" | |
else if (Array.isArray(what)) | |
return "Array" | |
else | |
return "Object" | |
} | |
/** | |
* Asserts "expected" versus "actual" using strict equality comparison | |
* | |
* @param {*} expected The expected item | |
* @param {*} actual The actual item | |
* @throws {TypeError} | |
*/ | |
function assertStrictEquals(expected, actual, errorMessage) { | |
if(expected !== actual) { | |
throwError(errorMessage) | |
} | |
} | |
/** | |
* Asserts expected array and actual array have the same length | |
* | |
* @param {Array} expected | |
* @param {Array} actual | |
*/ | |
function assertSameLengthArray (expected, actual, errorMessage) { | |
if (expected.length !== actual.length) { | |
throwError(errorMessage) | |
} | |
} | |
/** | |
* Asserts recursively deep equality | |
* | |
* @param {Object|Array} expected | |
* @param {Object|Array} actual | |
* @param {String} message | |
*/ | |
function assertDeepEquals (expected, actual, message) { | |
const type = getObjectTypeOf(expected), | |
// local utilty fn | |
getFormattedMessageKey = key => (type === 'Array') ? `[${key}]` : `.${key}` | |
for (expectedKey in expected) { | |
if (!(expectedKey in actual)) | |
throwError(`${message}${getFormattedMessageKey(expectedKey)} expected but was not found`) | |
assertEquals(`${message}${getFormattedMessageKey(expectedKey)}`, expected[expectedKey], actual[expectedKey]) | |
} | |
} | |
/** | |
* Asserts "expected" versus "actual", | |
* 'failing' the assertion (via Error) if a difference is found. | |
* | |
* @param {String} message The comparison message passed by the user | |
* @param {*} expected The expected item | |
* @param {*} actual The actual item | |
*/ | |
function assertEquals(message, expected, actual) { | |
let expectedType = getTypeOf(expected), | |
actualType = getTypeOf(actual) | |
assertStrictEquals(expectedType, actualType, `${message} expected type ${expectedType} but found ${actualType}`) | |
switch (expectedType) { | |
case 'string': | |
case 'number': | |
assertStrictEquals(expected, actual, `${message} expected "${expected}" found "${actual}"`) | |
break | |
case 'Array': | |
assertSameLengthArray(expected, actual, `${message} expected array length "${expected.length}" but found "${actual.length}"`) | |
assertDeepEquals(expected, actual, message) | |
break | |
case 'Object': | |
assertDeepEquals(expected, actual, message) | |
break | |
} | |
} | |
/* -- Test running code: --- */ | |
/** | |
* Runs a "assertEquals" test. | |
* | |
* @param {String} message The initial message to pass | |
* @param {Array} assertionFailures List of messages that will be displayed on the UI for evaluation | |
* @param {*} expected Expected item | |
* @param {*} actual The actual item | |
*/ | |
function runTest(message, assertionFailures, expected, actual) { | |
try { | |
assertEquals(message, expected, actual); | |
} catch (failure) { | |
assertionFailures.push(failure.message); | |
} | |
} | |
function runAll() { | |
var complexObject1 = { | |
propA: 1, | |
propB: { | |
propA: [1, { propA: 'a', propB: 'b' }, 3], | |
propB: 1, | |
propC: 2 | |
} | |
}; | |
var complexObject1Copy = { | |
propA: 1, | |
propB: { | |
propA: [1, { propA: 'a', propB: 'b' }, 3], | |
propB: 1, | |
propC: 2 | |
} | |
}; | |
var complexObject2 = { | |
propA: 1, | |
propB: { | |
propB: 1, | |
propA: [1, { propA: 'a', propB: 'c' }, 3], | |
propC: 2 | |
} | |
}; | |
var complexObject3 = { | |
propA: 1, | |
propB: { | |
propA: [1, { propA: 'a', propB: 'b' }, 3], | |
propB: 1 | |
} | |
}; | |
// Run the tests | |
var assertionFailures = []; | |
runTest('Test 01: ', assertionFailures, 'abc', 'abc'); | |
runTest('Test 02: ', assertionFailures, 'abcdef', 'abc'); | |
runTest('Test 03: ', assertionFailures, ['a'], {0: 'a'}); | |
runTest('Test 04: ', assertionFailures, ['a', 'b'], ['a', 'b', 'c']); | |
runTest('Test 05: ', assertionFailures, ['a', 'b', 'c'], ['a', 'b', 'c']); | |
runTest('Test 06: ', assertionFailures, complexObject1, complexObject1Copy); | |
runTest('Test 07: ', assertionFailures, complexObject1, complexObject2); | |
runTest('Test 08: ', assertionFailures, complexObject1, complexObject3); | |
runTest('Test 09: ', assertionFailures, null, {}); | |
// Output the results | |
var messagesEl = document.getElementById('messages'); | |
var newListEl; | |
var i, ii; | |
for (i = 0, ii = assertionFailures.length; i < ii; i++) { | |
newListEl = document.createElement('li'); | |
newListEl.innerHTML = assertionFailures[i]; | |
messagesEl.appendChild(newListEl); | |
} | |
} | |
runAll(); | |
</script> | |
<script id="jsbin-source-html" type="text/html"><html> | |
<head> | |
<script src="//jashkenas.github.io/underscore/underscore-min.js"><\/script> | |
<meta name="description" content="JS Developer Code Challenge" /><html> | |
<head> | |
<title>Coding Challenge</title> | |
</head> | |
<body> | |
<h1>Terms of the Exercise</h1> | |
<ul> | |
<li>Try and stay away from libraries if possible</li> | |
<li>You can take as long as you like to complete the exercise, but for an indicative timescale we expect a senior developer can accomplish this in an hour.</li> | |
<li>You may use online resources to assist you with specific techniques, syntax etc. but please do not just copy code.</li> | |
<li>Please don't share this exercise with any third party</li> | |
</ul> | |
<h1>The Challenge</h1> | |
<p> | |
The aim of the exercise is to demonstrate your problem solving and understanding of JavaScript by implementing something found in every unit testing tool - an "assertEquals" method.</p> | |
<ul> | |
<li>Fill in the "assertEquals" function such that it will correctly compare the passed "expected" vs "actual" parameters.</li> | |
<li>You may add more functions.</li> | |
<li>Credit will be given for approach, correctly identifying "failed" assertEquals, "clean" code and coding style.</li> | |
</ul> | |
<h1>Expected Result</h1> | |
The following tests should "fail": <strong>02, 03, 04, 07, 08 and 09</strong> - and the failures should be reported using the provided mechanism.<br/> | |
Ideally the failure messages should report further information: | |
<ul class="expected"> | |
<li>Test 02: Expected "abcdef" found "abc"</li> | |
<li>Test 03: Expected type Array but found Object</li> | |
<li>Test 04: Expected array length 2 but found 3</li> | |
<li>Test 07: Expected propB.propA[1].propB "b" but found "c"</li> | |
<li>Test 08: Expected propB.propC but was not found</li> | |
<li>Test 09: Expected type null but found type Object</li> | |
</ul> | |
<h1>Output</h1> | |
<ul class="output" id="messages"></ul> | |
</body> | |
</html></script> | |
<script id="jsbin-source-css" type="text/css">body { | |
font-family: Arial; | |
font-size: 80%; | |
} | |
ul.expected { | |
font-family: Courier; | |
} | |
ul.output { | |
font-family: Courier; | |
background-color: white; | |
border: 1px solid #999999; | |
padding: 10px 30px 10px 30px; | |
} | |
ul.output li {; | |
margin-bottom: 10px; | |
} | |
ul.output li code { | |
font-size: 1.2em; | |
color: blue; | |
}</script> | |
<script id="jsbin-source-javascript" type="text/javascript">/** | |
* Shared utility fn used to throw a TypeError | |
* | |
* @param {String} errorMessage | |
* @throws {TypeError} | |
*/ | |
function throwError(errorMessage) { | |
throw new TypeError(errorMessage) | |
} | |
/** | |
* Returns the type of the passed argument | |
* | |
* @param {any} what - The object to return the type of | |
* @returns {String} | |
*/ | |
function getTypeOf (what) { | |
let type = typeof what | |
if (type === 'object') { | |
return getObjectTypeOf(what) | |
} | |
return type | |
} | |
/** | |
* Returns the object type of object passed | |
* | |
* @param {Object} what | |
* @returns {String} | |
*/ | |
function getObjectTypeOf (what) { | |
if (what === null) | |
return "null" | |
else if (Array.isArray(what)) | |
return "Array" | |
else | |
return "Object" | |
} | |
/** | |
* Asserts "expected" versus "actual" using strict equality comparison | |
* | |
* @param {*} expected The expected item | |
* @param {*} actual The actual item | |
* @throws {TypeError} | |
*/ | |
function assertStrictEquals(expected, actual, errorMessage) { | |
if(expected !== actual) { | |
throwError(errorMessage) | |
} | |
} | |
/** | |
* Asserts expected array and actual array have the same length | |
* | |
* @param {Array} expected | |
* @param {Array} actual | |
*/ | |
function assertSameLengthArray (expected, actual, errorMessage) { | |
if (expected.length !== actual.length) { | |
throwError(errorMessage) | |
} | |
} | |
/** | |
* Asserts recursively deep equality | |
* | |
* @param {Object|Array} expected | |
* @param {Object|Array} actual | |
* @param {String} message | |
*/ | |
function assertDeepEquals (expected, actual, message) { | |
const type = getObjectTypeOf(expected), | |
// local utilty fn | |
getFormattedMessageKey = key => (type === 'Array') ? `[${key}]` : `.${key}` | |
for (expectedKey in expected) { | |
if (!(expectedKey in actual)) | |
throwError(`${message}${getFormattedMessageKey(expectedKey)} expected but was not found`) | |
assertEquals(`${message}${getFormattedMessageKey(expectedKey)}`, expected[expectedKey], actual[expectedKey]) | |
} | |
} | |
/** | |
* Asserts "expected" versus "actual", | |
* 'failing' the assertion (via Error) if a difference is found. | |
* | |
* @param {String} message The comparison message passed by the user | |
* @param {*} expected The expected item | |
* @param {*} actual The actual item | |
*/ | |
function assertEquals(message, expected, actual) { | |
let expectedType = getTypeOf(expected), | |
actualType = getTypeOf(actual) | |
assertStrictEquals(expectedType, actualType, `${message} expected type ${expectedType} but found ${actualType}`) | |
switch (expectedType) { | |
case 'string': | |
case 'number': | |
assertStrictEquals(expected, actual, `${message} expected "${expected}" found "${actual}"`) | |
break | |
case 'Array': | |
assertSameLengthArray(expected, actual, `${message} expected array length "${expected.length}" but found "${actual.length}"`) | |
assertDeepEquals(expected, actual, message) | |
break | |
case 'Object': | |
assertDeepEquals(expected, actual, message) | |
break | |
} | |
} | |
/* -- Test running code: --- */ | |
/** | |
* Runs a "assertEquals" test. | |
* | |
* @param {String} message The initial message to pass | |
* @param {Array} assertionFailures List of messages that will be displayed on the UI for evaluation | |
* @param {*} expected Expected item | |
* @param {*} actual The actual item | |
*/ | |
function runTest(message, assertionFailures, expected, actual) { | |
try { | |
assertEquals(message, expected, actual); | |
} catch (failure) { | |
assertionFailures.push(failure.message); | |
} | |
} | |
function runAll() { | |
var complexObject1 = { | |
propA: 1, | |
propB: { | |
propA: [1, { propA: 'a', propB: 'b' }, 3], | |
propB: 1, | |
propC: 2 | |
} | |
}; | |
var complexObject1Copy = { | |
propA: 1, | |
propB: { | |
propA: [1, { propA: 'a', propB: 'b' }, 3], | |
propB: 1, | |
propC: 2 | |
} | |
}; | |
var complexObject2 = { | |
propA: 1, | |
propB: { | |
propB: 1, | |
propA: [1, { propA: 'a', propB: 'c' }, 3], | |
propC: 2 | |
} | |
}; | |
var complexObject3 = { | |
propA: 1, | |
propB: { | |
propA: [1, { propA: 'a', propB: 'b' }, 3], | |
propB: 1 | |
} | |
}; | |
// Run the tests | |
var assertionFailures = []; | |
runTest('Test 01: ', assertionFailures, 'abc', 'abc'); | |
runTest('Test 02: ', assertionFailures, 'abcdef', 'abc'); | |
runTest('Test 03: ', assertionFailures, ['a'], {0: 'a'}); | |
runTest('Test 04: ', assertionFailures, ['a', 'b'], ['a', 'b', 'c']); | |
runTest('Test 05: ', assertionFailures, ['a', 'b', 'c'], ['a', 'b', 'c']); | |
runTest('Test 06: ', assertionFailures, complexObject1, complexObject1Copy); | |
runTest('Test 07: ', assertionFailures, complexObject1, complexObject2); | |
runTest('Test 08: ', assertionFailures, complexObject1, complexObject3); | |
runTest('Test 09: ', assertionFailures, null, {}); | |
// Output the results | |
var messagesEl = document.getElementById('messages'); | |
var newListEl; | |
var i, ii; | |
for (i = 0, ii = assertionFailures.length; i < ii; i++) { | |
newListEl = document.createElement('li'); | |
newListEl.innerHTML = assertionFailures[i]; | |
messagesEl.appendChild(newListEl); | |
} | |
} | |
runAll(); | |
</script></body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
font-family: Arial; | |
font-size: 80%; | |
} | |
ul.expected { | |
font-family: Courier; | |
} | |
ul.output { | |
font-family: Courier; | |
background-color: white; | |
border: 1px solid #999999; | |
padding: 10px 30px 10px 30px; | |
} | |
ul.output li {; | |
margin-bottom: 10px; | |
} | |
ul.output li code { | |
font-size: 1.2em; | |
color: blue; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Shared utility fn used to throw a TypeError | |
* | |
* @param {String} errorMessage | |
* @throws {TypeError} | |
*/ | |
function throwError(errorMessage) { | |
throw new TypeError(errorMessage) | |
} | |
/** | |
* Returns the type of the passed argument | |
* | |
* @param {any} what - The object to return the type of | |
* @returns {String} | |
*/ | |
function getTypeOf (what) { | |
let type = typeof what | |
if (type === 'object') { | |
return getObjectTypeOf(what) | |
} | |
return type | |
} | |
/** | |
* Returns the object type of object passed | |
* | |
* @param {Object} what | |
* @returns {String} | |
*/ | |
function getObjectTypeOf (what) { | |
if (what === null) | |
return "null" | |
else if (Array.isArray(what)) | |
return "Array" | |
else | |
return "Object" | |
} | |
/** | |
* Asserts "expected" versus "actual" using strict equality comparison | |
* | |
* @param {*} expected The expected item | |
* @param {*} actual The actual item | |
* @throws {TypeError} | |
*/ | |
function assertStrictEquals(expected, actual, errorMessage) { | |
if(expected !== actual) { | |
throwError(errorMessage) | |
} | |
} | |
/** | |
* Asserts expected array and actual array have the same length | |
* | |
* @param {Array} expected | |
* @param {Array} actual | |
*/ | |
function assertSameLengthArray (expected, actual, errorMessage) { | |
if (expected.length !== actual.length) { | |
throwError(errorMessage) | |
} | |
} | |
/** | |
* Asserts recursively deep equality | |
* | |
* @param {Object|Array} expected | |
* @param {Object|Array} actual | |
* @param {String} message | |
*/ | |
function assertDeepEquals (expected, actual, message) { | |
const type = getObjectTypeOf(expected), | |
// local utilty fn | |
getFormattedMessageKey = key => (type === 'Array') ? `[${key}]` : `.${key}` | |
for (expectedKey in expected) { | |
if (!(expectedKey in actual)) | |
throwError(`${message}${getFormattedMessageKey(expectedKey)} expected but was not found`) | |
assertEquals(`${message}${getFormattedMessageKey(expectedKey)}`, expected[expectedKey], actual[expectedKey]) | |
} | |
} | |
/** | |
* Asserts "expected" versus "actual", | |
* 'failing' the assertion (via Error) if a difference is found. | |
* | |
* @param {String} message The comparison message passed by the user | |
* @param {*} expected The expected item | |
* @param {*} actual The actual item | |
*/ | |
function assertEquals(message, expected, actual) { | |
let expectedType = getTypeOf(expected), | |
actualType = getTypeOf(actual) | |
assertStrictEquals(expectedType, actualType, `${message} expected type ${expectedType} but found ${actualType}`) | |
switch (expectedType) { | |
case 'string': | |
case 'number': | |
assertStrictEquals(expected, actual, `${message} expected "${expected}" found "${actual}"`) | |
break | |
case 'Array': | |
assertSameLengthArray(expected, actual, `${message} expected array length "${expected.length}" but found "${actual.length}"`) | |
assertDeepEquals(expected, actual, message) | |
break | |
case 'Object': | |
assertDeepEquals(expected, actual, message) | |
break | |
} | |
} | |
/* -- Test running code: --- */ | |
/** | |
* Runs a "assertEquals" test. | |
* | |
* @param {String} message The initial message to pass | |
* @param {Array} assertionFailures List of messages that will be displayed on the UI for evaluation | |
* @param {*} expected Expected item | |
* @param {*} actual The actual item | |
*/ | |
function runTest(message, assertionFailures, expected, actual) { | |
try { | |
assertEquals(message, expected, actual); | |
} catch (failure) { | |
assertionFailures.push(failure.message); | |
} | |
} | |
function runAll() { | |
var complexObject1 = { | |
propA: 1, | |
propB: { | |
propA: [1, { propA: 'a', propB: 'b' }, 3], | |
propB: 1, | |
propC: 2 | |
} | |
}; | |
var complexObject1Copy = { | |
propA: 1, | |
propB: { | |
propA: [1, { propA: 'a', propB: 'b' }, 3], | |
propB: 1, | |
propC: 2 | |
} | |
}; | |
var complexObject2 = { | |
propA: 1, | |
propB: { | |
propB: 1, | |
propA: [1, { propA: 'a', propB: 'c' }, 3], | |
propC: 2 | |
} | |
}; | |
var complexObject3 = { | |
propA: 1, | |
propB: { | |
propA: [1, { propA: 'a', propB: 'b' }, 3], | |
propB: 1 | |
} | |
}; | |
// Run the tests | |
var assertionFailures = []; | |
runTest('Test 01: ', assertionFailures, 'abc', 'abc'); | |
runTest('Test 02: ', assertionFailures, 'abcdef', 'abc'); | |
runTest('Test 03: ', assertionFailures, ['a'], {0: 'a'}); | |
runTest('Test 04: ', assertionFailures, ['a', 'b'], ['a', 'b', 'c']); | |
runTest('Test 05: ', assertionFailures, ['a', 'b', 'c'], ['a', 'b', 'c']); | |
runTest('Test 06: ', assertionFailures, complexObject1, complexObject1Copy); | |
runTest('Test 07: ', assertionFailures, complexObject1, complexObject2); | |
runTest('Test 08: ', assertionFailures, complexObject1, complexObject3); | |
runTest('Test 09: ', assertionFailures, null, {}); | |
// Output the results | |
var messagesEl = document.getElementById('messages'); | |
var newListEl; | |
var i, ii; | |
for (i = 0, ii = assertionFailures.length; i < ii; i++) { | |
newListEl = document.createElement('li'); | |
newListEl.innerHTML = assertionFailures[i]; | |
messagesEl.appendChild(newListEl); | |
} | |
} | |
runAll(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment