Skip to content

Instantly share code, notes, and snippets.

@joshycube
Created March 23, 2018 11:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joshycube/83a9e62e884997c474cf40ba28fb3306 to your computer and use it in GitHub Desktop.
Save joshycube/83a9e62e884997c474cf40ba28fb3306 to your computer and use it in GitHub Desktop.
Coding Challenge JS Developer Code Challenge // source https://jsbin.com/pafafek
<html>
<head>
<meta name="description" content="JS Developer Code Challenge" /><html>
<head>
<script src="https://cdn.rawgit.com/lodash/lodash/4.5.1/dist/lodash.min.js"></script>
<script src="https://cdn.rawgit.com/lodash/lodash/4.5.1/dist/lodash.fp.min.js"></script>
<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>
<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 src="https://cdn.jsdelivr.net/lodash/4/lodash.min.js"></script>
<script id="jsbin-javascript">
/**
* @param {String} message The message that will be thrown
*/
"use strict";
function AssertFailEx(message) {
this.message = message;
this.name = "AssertFailEx";
}
/**
* @param {*} subject The item needs to be type checked
*/
function getType(subject) {
return Object.prototype.toString.call(subject).slice(8, -1);
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function deepEqualObject(expected, actual) {
var path = [];
var expectedVal = [];
function changes(expected, actual) {
return _.transform(expected, function (result, value, key) {
if (!_.isEqual(value, actual[key])) {
path.push(key);
result[key] = _.isObject(value) && _.isObject(actual[key]) ? changes(value, actual[key]) : expectedVal.push(value) && value;
}
});
}
changes(expected, actual);
return { path: path, expectedVal: expectedVal };
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function shallowEqualArray(expected, actual) {
for (var _item in expected) {
if (!actual[_item]) {
return _item;
}
}
return true;
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function checkArray(expected, actual) {
var expectedType = getType(expected);
var expectedLength = expected.length;
var actualType = getType(actual);
var actualLength = actual.length;
if (expectedType !== actualType) {
return "Expected type " + expectedType + " but found " + actualType;
} else if (expectedLength !== actualLength) {
return "Expected length " + expectedLength + " but found " + actualLength;
} else {
var shallow = shallowEqualArray(expected, actual);
return shallow === true ? shallow : "Expected item in array " + item + " but found undefined";
}
return true;
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function checkObject(expected, actual) {
var expectedType = getType(expected);
var actualType = getType(actual);
if (expectedType !== actualType) {
return "Expected type " + expectedType + " but found type " + actualType;
} else {
var deep = deepEqualObject(expected, actual);
return deep.path.length === 0 ? true : "Expected " + deep.path.join('.') + " \"" + deep.expectedVal + "\"\n but found \"" + _.get(actual, deep.path) + "\"";
}
return true;
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function checkOtherTypes(expected, actual) {
var expectedType = getType(expected);
var actualType = getType(actual);
if (expectedType !== actualType) {
return "Expected type " + expectedType + " but found type " + actualType;
} else if (expected !== actual) {
return "Expected \"" + expected + "\" but found \"" + actual + "\"";
}
return true;
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
* @param {String} type The returned type Array, Null, Object, String, Undefined, Number
*/
function assertTypes(expected, actual, type) {
switch (type) {
case "Object":
return checkObject(expected, actual);
break;
case "Array":
return checkArray(expected, actual);
break;
default:
return checkOtherTypes(expected, actual);
}
}
/**
* 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(expected, actual) {
var type = getType(expected);
var asserted = assertTypes(expected, actual, type);
if (asserted !== true) {
throw new AssertFailEx(asserted);
}
}
/* -- 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(expected, actual);
} catch (failure) {
assertionFailures.push(message + " " + 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-javascript" type="text/javascript">/**
* @param {String} message The message that will be thrown
*/
function AssertFailEx(message) {
this.message = message;
this.name = "AssertFailEx";
}
/**
* @param {*} subject The item needs to be type checked
*/
function getType(subject) {
return Object.prototype.toString.call(subject).slice(8, -1);
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function deepEqualObject(expected, actual) {
const path = [];
const expectedVal = [];
function changes(expected, actual) {
return _.transform(expected, (result, value, key) => {
if (!_.isEqual(value, actual[key])) {
path.push(key);
result[key] =
_.isObject(value) && _.isObject(actual[key])
? changes(value, actual[key])
: expectedVal.push(value) && value;
}
});
}
changes(expected, actual);
return { path, expectedVal };
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function shallowEqualArray(expected, actual) {
for (let item in expected) {
if (!actual[item]) {
return item;
}
}
return true;
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function checkArray(expected, actual) {
const expectedType = getType(expected);
const expectedLength = expected.length;
const actualType = getType(actual);
const actualLength = actual.length;
if (expectedType !== actualType) {
return `Expected type ${expectedType} but found ${actualType}`;
} else if (expectedLength !== actualLength) {
return `Expected length ${expectedLength} but found ${actualLength}`;
} else {
const shallow = shallowEqualArray(expected, actual);
return shallow === true
? shallow
: `Expected item in array ${item} but found undefined`;
}
return true;
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function checkObject(expected, actual) {
const expectedType = getType(expected);
const actualType = getType(actual);
if (expectedType !== actualType) {
return `Expected type ${expectedType} but found type ${actualType}`;
} else {
const deep = deepEqualObject(expected, actual);
return deep.path.length === 0
? true
: `Expected ${deep.path.join('.')} "${deep.expectedVal}"
but found "${_.get(actual, deep.path)}"`;
}
return true;
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function checkOtherTypes(expected, actual) {
const expectedType = getType(expected);
const actualType = getType(actual);
if (expectedType !== actualType) {
return `Expected type ${expectedType} but found type ${actualType}`;
} else if (expected !== actual) {
return `Expected "${expected}" but found "${actual}"`;
}
return true;
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
* @param {String} type The returned type Array, Null, Object, String, Undefined, Number
*/
function assertTypes(expected, actual, type) {
switch (type) {
case "Object":
return checkObject(expected, actual);
break;
case "Array":
return checkArray(expected, actual);
break;
default:
return checkOtherTypes(expected, actual);
}
}
/**
* 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(expected, actual) {
const type = getType(expected);
const asserted = assertTypes(expected, actual, type);
if (asserted !== true) {
throw new AssertFailEx(asserted);
}
}
/* -- 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(expected, actual);
} catch (failure) {
assertionFailures.push(`${message} ${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>
/**
* @param {String} message The message that will be thrown
*/
"use strict";
function AssertFailEx(message) {
this.message = message;
this.name = "AssertFailEx";
}
/**
* @param {*} subject The item needs to be type checked
*/
function getType(subject) {
return Object.prototype.toString.call(subject).slice(8, -1);
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function deepEqualObject(expected, actual) {
var path = [];
var expectedVal = [];
function changes(expected, actual) {
return _.transform(expected, function (result, value, key) {
if (!_.isEqual(value, actual[key])) {
path.push(key);
result[key] = _.isObject(value) && _.isObject(actual[key]) ? changes(value, actual[key]) : expectedVal.push(value) && value;
}
});
}
changes(expected, actual);
return { path: path, expectedVal: expectedVal };
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function shallowEqualArray(expected, actual) {
for (var _item in expected) {
if (!actual[_item]) {
return _item;
}
}
return true;
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function checkArray(expected, actual) {
var expectedType = getType(expected);
var expectedLength = expected.length;
var actualType = getType(actual);
var actualLength = actual.length;
if (expectedType !== actualType) {
return "Expected type " + expectedType + " but found " + actualType;
} else if (expectedLength !== actualLength) {
return "Expected length " + expectedLength + " but found " + actualLength;
} else {
var shallow = shallowEqualArray(expected, actual);
return shallow === true ? shallow : "Expected item in array " + item + " but found undefined";
}
return true;
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function checkObject(expected, actual) {
var expectedType = getType(expected);
var actualType = getType(actual);
if (expectedType !== actualType) {
return "Expected type " + expectedType + " but found type " + actualType;
} else {
var deep = deepEqualObject(expected, actual);
return deep.path.length === 0 ? true : "Expected " + deep.path.join('.') + " \"" + deep.expectedVal + "\"\n but found \"" + _.get(actual, deep.path) + "\"";
}
return true;
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
*/
function checkOtherTypes(expected, actual) {
var expectedType = getType(expected);
var actualType = getType(actual);
if (expectedType !== actualType) {
return "Expected type " + expectedType + " but found type " + actualType;
} else if (expected !== actual) {
return "Expected \"" + expected + "\" but found \"" + actual + "\"";
}
return true;
}
/**
* @param {*} expected The expected item
* @param {*} actual The actual item
* @param {String} type The returned type Array, Null, Object, String, Undefined, Number
*/
function assertTypes(expected, actual, type) {
switch (type) {
case "Object":
return checkObject(expected, actual);
break;
case "Array":
return checkArray(expected, actual);
break;
default:
return checkOtherTypes(expected, actual);
}
}
/**
* 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(expected, actual) {
var type = getType(expected);
var asserted = assertTypes(expected, actual, type);
if (asserted !== true) {
throw new AssertFailEx(asserted);
}
}
/* -- 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(expected, actual);
} catch (failure) {
assertionFailures.push(message + " " + 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