Skip to content

Instantly share code, notes, and snippets.

@Tolomeo
Created April 8, 2018 17:03
Show Gist options
  • Save Tolomeo/39c6e905a0e2a75b71301d5797523818 to your computer and use it in GitHub Desktop.
Save Tolomeo/39c6e905a0e2a75b71301d5797523818 to your computer and use it in GitHub Desktop.
Coding Challenge JS Developer Code Challenge // source http://jsbin.com/ruyoyip
<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>
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;
}
/**
* 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